--- /dev/null
+An appendix to Chapter 6 of the book: some extra explanation on pid's
+and on temporal claims. Updated for Spin Version 2.0 - January 1995.
+
+PROCESS IDs
+
+In Spin 2.0 and later the never claim can refer to the control state
+of any process, but not to their local variables.
+This functionality is meant to be used for building correctness assertions
+with never claims. It should never be used for anything else.
+An example is
+ Receiver[pid]@place
+where `place' the name of a label within `proctype Receiver,' and
+`pid' is the value returned by the run statement that instantiated the
+copy of the Receiver proctype that we are interested in.
+
+There is a misleading suggestion in the book that says that you can
+usually guess the `pid's. Wiser is to always use the explicit value
+returned by the `run()' statement that instantiated the proces.
+Processes started with the `active' prefix obtain instantiation
+numbers starting at value 1, in the order in which they appear in the
+specification. Each process also has a local variable _pid that
+holds its own instantiation number.
+
+SPECIFYING TEMPORAL CLAIMS
+
+The body of a temporal claim is defined just like PROMELA proctype bodies.
+This means that all control flow structures, such as if-fi selections,
+do-od repetitions, and goto jumps, are allowed.
+There is, however, one important difference:
+
+ Every statement inside a temporal claim is (interpreted as) a condition.
+ A never claim should therefore never contain statements that can
+ have side-effects (assignments, communications, run-statements, etc.)
+
+Temporal claims are used to express behaviors that are considered undesirable
+or illegal. We say that a temporal claim is `matched' if the undesirable
+behavior can be realized, and thus the claim violated.
+
+The recommended use of a temporal claim is in combination with acceptance labels.
+There are two ways to `match' a temporal claim, depending on whether the
+undesirable behavior defines a terminating or a cyclic execution sequence.
+
+o A temporal claim is matched when it terminates (reaches its closing curly brace).
+ That is, the claim can be violated if the closing curly brace of the PROMELA
+ body of the claim is reachable for at least one execution sequence.
+
+o For a cyclic execution sequence, the claim is matched only when an explicit
+ acceptance cycle exists. The acceptance labels within temporal claims
+ are user defined, there are no defaults. This means that in the absence of
+ acceptance labels no cyclic behavior can be matched by a temporal claim.
+ It also means that to check a cyclic temporal claim, acceptance labels should
+ only occur within the claim and not elsewhere in the PROMELA code.
+
+
+SEMANTICS
+
+The normal system behavior of a PROMELA system is defined as the
+`asynchronous product' of the behaviors of the individual processes.
+Given an arbitrary system state, its successor states are obtained
+in two steps. In the first step all the executable (atomic) statements in the
+individual processes are identified. In the second step, each one of these
+statements is executed.
+Each single execution produces a successor state in the asynchronous product.
+The complete system behavior is thus defined recursively and
+represents all possible interleavings of the individual process behaviors.
+Call this asynchronous product machine the `global machine'.
+
+The addition of a temporal claim defines an additional `synchronous product'
+of this global machine with the state machine that defines the temporal
+claim. Call the latter machine the `claim machine', and call the new
+synchronous product the `labeled machine'.
+
+Every state in the labeled machine is a pair (p,q) with p a state in the global
+machine and q a state in the claim machine. Every transition in the labeled
+machine is similarly defined by a pair (r,s) with r a transition in the global
+machine and s a transition in the claim machine.
+In other words, every transition in the `synchronous' product is a joint move
+of the global machine and the claim machine.
+(By contrast, every transition in an `asynchronous' product would correspond
+to a single transition in either the global machine or the claim machine, thus
+interleaving transitions instead of combining them.)
+
+Since all statements in the claim machine are boolean propositions, the second
+half of the transition pair (r,s) is either true or false.
+Call all transitions where this proposition is true `matching transitions'.
+In a matching transition proposition s evaluates to true in state system state r.
+Notice that the claim machine has at least one stop state E, the state
+at the closing curly brace of the claim body.
+
+The semantics of temporal claims can now be summed up as follows.
+
+o If the labeled machine contains any sequence of matching transitions only,
+ that connects its initial state with a state (p,E) for any p, the temporal
+ claim can be matched by a terminating sequence (a correctness violation).
+
+o If the labeled machine contains any cycle of matching transitions only, that
+ passes through an acceptance state, the temporal claim can be matched by a
+ cyclic sequence.
+
+
+EXAMPLES
+
+Listed below are the equivalent PROMELA definitions for the three basic
+temporal properties defined by Zohar Manna & Amir Pnueli in
+``Tools and Rules for the Practicing Verifier'' Stanford University,
+Report STAN-CS-90-1321, July 1990, 34 pgs.
+
+The following descriptions are quoted from Manna & Pnueli:
+
+ ``There are three classes of properties we [...] believe to cover
+ the majority of properties one would ever wish to verify.''
+
+ 1. Invariance
+ ``An invariance property refers to an assertion p, and requires that p
+ is an invariant over all the computations of a program P, i.e. all
+ the states arising in a computation of P satisfy p. In temporal
+ logic notation, such properties are expressed by [] p, for a state
+ formula p.''
+
+ Corresponding Temporal Claim in PROMELA:
+ never {
+ do
+ :: p
+ :: !p -> break
+ od
+ }
+
+ 2. Response
+ ``A response property refers to two assertions p and q, and
+ requires that every p-state (a state satisfying p) arising in
+ a computation is eventually followed by a q-state.
+ In temporal logic notation this is written as p -> <> q.''
+
+ Corresponding Temporal Claim in PROMELA:
+ never {
+ do
+ :: true
+ :: p && !q -> break
+ od;
+ accept:
+ do
+ :: !q
+ od
+ }
+
+ Note that using (!p || q) instead of `skip' would check only the
+ first occurrence of p becoming true while q is false.
+ The above formalization checks for all occurrences, also future ones.
+ Strictly seen, therefore, the claim above uses a common interpretation
+ of the formula, requiring it to hold always, or: [] { p -> <> q }
+
+ 3. Precedence
+ ``A simple precedence property refers to three assertions p, q, and r.
+ It requires that any p-state initiates a q-interval (i.e. an interval
+ all of whose states satisfy q) which, either runs to the end of the
+ computation, or is terminated by an r-state.
+ Such a property is useful to express the restriction that, following
+ a certain condition, one future event will always be preceded by
+ another future event.
+ For example, it may express the property that, from the time a certain
+ input has arrived, there will be an output before the next input.
+ Note that this does not guarantee [require] that the output will actually
+ be produced. It only guarantees [requires] that the next input (if any)
+ will be preceded by an output. In temporal logic, this property is
+ expressed by p -> (q U r), using the unless operator (weak until) U.
+
+ Corresponding Temporal Claim in PROMELA:
+
+ never {
+ do
+ :: skip /* to match any occurrence */
+ :: p && q && !r -> break
+ :: p && !q && !r -> goto error
+ od;
+ do
+ :: q && !r
+ :: !q && !r -> break
+ od;
+ error: skip
+ }
+
+ Strictly again, this encodes: [] { p -> (q U r) }
+ To match just the first occurence, replace skip with (!p || r).
--- /dev/null
+Errata for `Design and Validation of Computer Protocols'
+[trivial typos not listed]
+
+CHAPTER 2, page 26 - Example of a Shorter Error Scenario
+============================================================
+
+A duplicate message can be accepted after even a single
+transmission error occurs. E.g.:
+
+ (A) (B)
+ ~ ~
+ | |
+ | ack 'z' /-----------<---------+
+ +-----------/ |
+accept(z) | |
+ +-----------\ ack 'a' -> err |
+ | \----------->--------+
+ | |
+ | nak 'z' /-----------<--------+
+ +------------/ |
+accept(z) | |
+
+
+CHAPTER 3, page 61/62 - Revised CRC-Algorithm
+(Bits renumbered in more standard right to left order.)
+============================================================
+
+The following C program, by Don Mitchell of AT&T Bell
+Laboratories, generates a lookup table for an arbitrary
+checksum polynomial. Input for the routine is an octal
+number, specified as an argument, that encodes the generator
+polynomial.
+In the version of the program shown here, compliments of Ned
+W. Rhodes, Software Systems Group, bits are numbered from
+zero to r-1, with bit zero corresponding to the right-most
+bit, and r the degree of the generator polynomial. (In
+Mitchell's original algorithm the bits in the message and
+generator polynomial were reversed.) The r-th bit itself is
+omitted from the code word, since it is implicit in the
+length. Using this program takes two separate steps.
+First, the program is compiled and run to generate the
+lookup tables. Then the checksum generation routine can be
+compiled, with the precalculated lookup tables in place. On
+a UNIX system, the program is compiled as
+
+ $ cc -o crc_init crc_init.c
+
+Lookup tables for the two most popular CRC-polynomials can
+now be produced as follows:
+
+ $ crc_init 0100005 > crc_16.h
+ $ crc_init 010041 > crc_ccitt.h
+
+This is the text of crc_init.c:
+
+
+ main(argc, argv)
+ int argc; char *argv[];
+ {
+ unsigned long crc, poly;
+ int n, i;
+
+ sscanf(argv[1], "%lo", &poly);
+ if (poly & 0xffff0000)
+ { fprintf(stderr, "polynomial is too large\n");
+ exit(1);
+ }
+
+ printf("/*\n * CRC 0%o\n */\n", poly);
+ printf("static unsigned short crc_table[256] = {\n");
+ for (n = 0; n < 256; n++)
+ { if (n % 8 == 0) printf(" ");
+ crc = n << 8;
+ for (i = 0; i < 8; i++)
+ { if (crc & 0x8000)
+ crc = (crc << 1) ^ poly;
+ else
+ crc <<= 1;
+ crc &= 0xFFFF;
+ }
+ if (n == 255) printf("0x%04X ", crc);
+ else printf("0x%04X, ", crc);
+ if (n % 8 == 7) printf("\n");
+ }
+ exit(0);
+ }
+
+The table can now be used to generate checksums:
+
+ unsigned short
+ cksum(s, n)
+ register unsigned char *s;
+ register int n;
+ {
+ register unsigned short crc=0;
+
+ while (n-- > 0)
+ crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8);
+
+ return crc;
+ }
+
+
+CHAPTER 4, page 81 - Typo
+============================================================
+
+old< Taking the modulo M effect into account, this becomes:
+ valid(m) = ( 0 < p - m <= W ) || ( 0 < p - M - m <= W )
+
+new> Taking the modulo M effect into account (p is always
+ smaller than M), this becomes:
+ valid(m) = ( 0 < p - m <= W ) || ( 0 < p + M - m <= W )
+
+ERROR, Page 83, Figure 4.14
+===========================
+
+should not "accept:i" if (a==e) is false
+
+
+CHAPTER 5, error/typos
+===========================
+
+Page 96, bottom
+
+The mutual exclusion algorithm attributed to Dekker is
+really a simplication of Dekker's algorithm that is known
+as Peterson's algorithm.
+Dekker's original solution is modeled in Promela like this:
+
+#define true 1
+#define false 0
+#define Aturn 1
+#define Bturn 0
+
+bool x, y, t;
+
+proctype A()
+{
+ do
+ :: x = true;
+ if
+ :: y == true && t == Bturn ->
+ x = false;
+ (t == Aturn)
+ :: y == false ->
+ break
+ fi
+ od;
+
+ /* critical section */
+
+ t = Bturn;
+ x = false
+}
+
+proctype B()
+{
+ do
+ :: y = true;
+ if
+ :: x == true && t == Aturn ->
+ y = false;
+ (t == Bturn)
+ :: x == false ->
+ break
+ fi
+ od;
+
+ /* critical section */
+
+ t = Aturn;
+ y = false
+}
+
+init { run A(); run B() }
+
+===========================
+
+Page 98, last paragraph
+
+old> "If the receive operation tries to retrieve more parameters
+ than are available, the value of the extra parameters is undefined;
+ if it receives fewer than the number of parameters that was sent,
+ the extra information is lost."
+new> "It is always an error if the receive operation tries to retrieve
+ a different number of parameters than the corresponding channel
+ declaration specifies."
+
+===========================
+
+Page 99, last line of "init", middle of page:
+
+old< qname!qforb
+
+new> qname[0]!qforb
+
+Page 100, delete last line on page:
+
+old< byte name; /* delete */
+
+Page 103, in the Dijkstra example:
+
+old< chan sema = [0] of { bit };
+
+new> chan sema = [0] of { mtype };
+
+Page 108, "init" section, top of page:
+
+old< chan Ain = [2] of { byte };
+ chan Bin = [2] of { byte };
+ chan Aout = [2] of { byte };
+ chan Bout = [2] of { byte };
+
+new> chan Ain = [2] of { byte, byte };
+ chan Bin = [2] of { byte, byte };
+ chan Aout = [2] of { byte, byte };
+ chan Bout = [2] of { byte, byte };
+
+===========================
+
+Page 107, last sentence of first paragraph Section 5.12:
+
+old< discussed in Section 2.4.
+new> discussed in Section 2.3.
+
+===========================
+
+Page 110, exercise 5-3:
+
+old< Revise the two programs from Section 5.6
+new> Revise the two programs from Section 5.8
+
+
+CHAPTER 6
+
+
+TYPO, page 117
+=======================
+old< chan sema[0] of {bit};
+new> chan sema = [0] of {bit};
+
+SERIOUS OMISSION, Section 6.4, page 116-117:
+=================================================
+The treatment of formalizing system invariants in a 1-statement
+monitor process is correct only if the model does not contain
+any timeout statements.
+If it does, the statements in the model that would be executed
+after a timeout expires are not checked (since assert is always
+executable, it would always be executed before the timeout expires
+under default timeout heuristics used in spin).
+there are two possible solutions:
+
+- disable the default timeout heuristics for a fully exhaustive
+ search for all possible choices of timeouts (brute force)
+ to do this, include a single line
+ #define timeout skip
+ as the first line of your model - and nothing else has to change
+
+- use a safer formalization of the system invariant, using a never claim.
+ the simples formalization is:
+ never { do :: assert(invariant) od }
+ which checks the truth of the invariant for every reachable state,
+ independent of timeouts.
+ another way would be to use the implicit matching behavior of a never
+ claim, without an explicit assertion:
+ never
+ { do
+ :: (invariant) /* things are fine, the invariant holds */
+ :: !(invariant) -> break /* invariant fails - match */
+ od
+ }
+
+CLARIFICATION, page 118, Section 6.5
+====================================
+The validator SPIN does not enforce the second criterion
+for a proper endstate, i.e., the emptiness of all channels.
+It does enforce the revised first criterion from the bottom
+of page 118.
+
+TYPO, page 121 middle:
+=================================================
+
+old< never { do :: skip od -> P -> !Q }
+
+new> never { do :: skip :: break od -> P -> !Q }
+
+ADDED EXPLANATIONS (throughout page 121 and onw.):
+=================================================
+
+A terminating claim is matched, and the corresponding correctness
+property thereby violated, if and when the claim body terminates.
+
+A non-terminating claim is matched, and the corresponding
+correctness property violated, if and when an acceptance cycle
+is detected.
+
+SPECIFYING TEMPORAL CLAIMS
+
+The body of a temporal claim is defined just like PROMELA
+proctype bodies. This means that all control flow struc-
+tures, such as if-fi selections, do-od repetitions, and
+goto jumps, are allowed. There is, however, one important
+difference:
+
+ Every statement inside a temporal claim is (interpreted
+ as) a boolean condition.
+
+Specifically, this means that the statements inside temporal
+claims should be free of side-effects. For reference, the
+PROMELA statements with side-effects are: assignments,
+assertions, sends, receives, and printf statements.
+
+Temporal claims are used to express system
+behaviors that are considered undesirable or illegal. We
+say that a temporal claim is matched if the undesirable
+behavior can be realized, and thus our correctness claim can
+be violated. The most useful application of temporal claims
+is in combination with acceptance labels. There are then
+two ways to match a temporal claim, depending on whether the
+undesirable behavior defines terminating or cyclic execution
+sequences.
+
+ For a terminating execution sequence, a temporal claim
+ is matched only when it can terminate (reaches the
+ closing curly brace) That is, the claim can be violated
+ if the closing curly brace of the PROMELA body of the
+ claim is reachable.
+
+ For a cyclic execution sequence, the claim is matched
+ only when an explicit acceptance cycle exists. The
+ acceptance labels within temporal claims are user
+ defined, there are no defaults. This means that in the
+ absence of acceptance labels no cyclic behavior can be
+ matched by a temporal claim. It also means that to
+ check a cyclic temporal claim, acceptance labels should
+ only occur within the claim and not elsewhere in the
+ PROMELA code.
+
+ERROR, page 124, top
+=======================
+old< :: len(receiver) == 0
+
+new> :: skip /* allow any time delay */
+
+ERROR, page 125, top
+=======================
+the claim can of course always be violated (== matched),
+whether timeout is redefined or not.
+
+CHAPTER 7
+
+ERROR, page 139, bottom
+=======================
+old< Pr(Burst >= 17) = 0.08 . e ^ { -0.08 . 17 } = 0.007
+
+new> Pr(Burst >= 17) = 0.009 . e ^ { -0.009 . 17 } = 0.007
+
+ERROR, page 156, middle
+=======================
+old< flow_to_dll[n]!sync_ack,0
+new> flow_to_dll[n]!sync_ack,m
+ (and move the new line up to precede: "m=0;")
+
+old< flow_to_ses[n]!sync_ack,0
+new> flow_to_ses[n]!sync_ack,m
+
+old< To avoid circularity, the synchronization messages
+ do not carry sequence numbers.
+new> The sync_ack message echos the session number of the
+ sync message.
+
+ERROR, page 156, bottom
+=======================
+old< || (0<p-m-M && p-m-M<=W));
+new> || (0<p-m+M && p-m+M<=W));
+
+
+CHAPTER 11
+
+ERROR, page 224, algorithm "analyze()"
+======================================
+old< q = element from W;
+new> q = last element from W;
+
+further down:
+=============
+old< If states are stored in set W in first-in first-out order,
+ the algorithm performs a breadth-first search of the state space tree.
+new> If states are stored in set W in first-in last-out (i.e., stack)
+ order, the algorithm performs a depth-first search of the state space tree.
+
+further down:
+=============
+old< If states are stored in first-in last-out (i.e., stack)
+ < order, this changes into a depth-first search.
+
+new> If states are stored and removed in first-in first-out
+ order, this changes into a breadth-first search
+ (element q must be deleted upon retrieval from set W in
+ this type of algorithm).
+
+Page 227, top
+=============
+old< q = element from W;
+new> q = last element from W;
+
+Page 237, bottom
+================
+old< after removing states 4, 3, and 2 from the stack...
+new> after removing states 4, and 3 from the stack...
+
+CHAPTER 13
+
+Page 315, 2nd para in 13.9
+==========================
+The first two sentences of this paragraph are incorrect.
+At the low end, just 1 state would be stored in the hash-array,
+taking up 2 bits of storage out of N available bits; at the
+high end, all N bits would be set at the end of the run, and
+(allowing overlaps) we cannot have seen more than N states.
+This leads to a possible range of values for the hash factor
+of N/2 >= hf >= 1
+For full state space storage the hash factor is meaningless.
+
+CHAPTER 14
+
+Page 331, lines 86, 88, and 94
+==============================
+See the corrections described for CHAPTER 7, page 156.
+
+APPENDIX C
+==============================
+
+Page 387-388
+The syntax of remote referencing has changed in SPIN Version 2.0.
+Remote referencing to local variables is no longer allowed
+(page 387, 5th line from below).
+The syntax for referencing the state of another process has changed
+from (page 388, 3rd line):
+ same[2]:here
+to:
+ same[2]@here
+
+=end errata=
--- /dev/null
+
+
+Answers to selected exercises from
+'Design and Validation of Computer Protocols'
+=============================================
+
+1.1
+Assuming that torches in the two groups would be
+raised and lowered simultaneously,
+the code for the first character in the first group
+could have clashed with the pre-defined start of text code.
+
+If torches are not raised simultaneously it is conceivable
+that group numbers could be paired with the wrong character numbers.
+
+A well-trained transmitter might also overestimate the receiver's
+ability to translate the codes on the fly.
+as is still true today: receiving is a more time consuming task
+than transmitting.
+
+1.2
+Assuming that a torch code is displayed for a minimum of 30 seconds,
+the torch telegraph transmits a choice of 1 out of 25 (between 4
+and 5 bits of information), giving a speed of roughly 0.15 bits/sec.
+Chappe's telegraph transmitted a choice of 1 out of 128 every 15 to 20
+seconds, giving a transmission speed of roughly 0.5 bits/sec.
+On Polybius' telegraph the 15 characters of the message
+``protocol failure'' would take 15x30 seconds or 7.5 minutes to transmit...
+(Note that a code for the space was not available.)
+On Chappe's system the 16 characters (space included) would be
+transmitted in 4 minutes, assuming that no predefined code
+was assigned to either the word `protocol' or the word `failure.'
+(As far as we know, there wasn't.)
+
+1.3
+Removing the redundancy in messages increases the chance that a
+single transmission error would make a large part of a message
+inrecognizable. It could cause a lot of extra traffic from receiver
+back to sender, asking for retransmissions, and additional transmissions
+of messages. The same tradeoff is still valid on today's communication
+channels (see Chapter 3).
+
+1.4
+The signalman at A had to make sure that not one but two
+trains would leave the tunnel, before he admitted the third.
+The two trains could reach signalman B in approximately 2 minutes.
+At 25 symbols per minute, that would allow the two signalmen
+to exchange roughly 50 characters of text.
+A could have signaled: "two trains now in tunnel - how many left?"
+for a total of 42 characters.
+Assuming that B would have answered eventually "one train left,"
+that would still leave A puzzling if B had really understood his
+message, and if so, where the second train could possibly be.
+Considering also that signalman A had been on duty for almost
+18 hours when the accident occured, it is not entirely certain
+that he could have succesfully resolved the protocol problem.
+Note that he still would have had to `invent' part of the protocol
+for resolving the problem in real-time.
+
+1.5
+Replace the message `train in tunnel' with `increment the number of
+trains in the tunnel by one.' Similarly, replace the message `tunnel
+is clear' by `decrement the number of trains in the tunnel by one.'
+The message `is tunnel clear' becomes `how many trains are in the
+tunnel?' with the possible responses spelled out with numerals 0 to 9.
+Either signalman can increment or decrement the number.
+The rule of the tunnel is invariantly that the number of trains in
+the tunnel is either zero or one, and only one signalman may transmit
+at a time. (To resolve conflicts on access to the transmission line,
+one could simply give one of the signalmen a fixed priority.)
+
+1.6
+A livelock would result. Assuming that the semaphore operators would
+quickly enough recognize the livelock, it is still an open question
+what they would (should) do to recover properly from such an occurence.
+
+1.7
+One possible scenario, observed by Jon Bentley in real life, is that
+two connections are made, and both parties are charged for the call.
+Clearly, a dream come true for the telephone companies.
+
+2.1
+Service - the simplex transfer of arbitrary messages from a designated
+sender to a designated receiver.
+
+Assumptions about the environment - sufficient visibility and small enough
+distance to make and accurate detection (and counting) of torches possible
+for both sender and receiver. There seems to be an implicit assumption of
+an error-free channel. There is also the implicit assumption that the
+receiver will always be able to keep up with the sender and will not get
+out of sync with the symbols that have to be decoded.
+
+Vocabulary - 24 characters from the Greek alphabet, plus two control messages
+(the start of text message and its acknowledgement).
+
+Encoding - each character, and each control message, is encoded into two
+numbers, both between 1 and 5.
+Since there are 26 distinct messages and only 5x5=25 distinct codes, some
+ambiguity is unavoidable.
+
+Procedure rules - minimal. Apparently there was only a single handshake
+at the start of the transmission. All other interactions (end of transmission,
+error recovery, flow control) were undefined and would have had to be
+improvised in the field. There is also no resolution of a potential conflict
+at the start of transmission (assuming that both parties could decide to
+start sending at the same time).
+
+2.2
+The procedure rules can include a poll message once per
+complete page - interrogating the printer about it's status
+(confirming that it is online and confirming that it
+is not out of paper - both conditions that can change from
+one page to the next). Note that the procedure rules must
+also guarantee that no more than one user can use the printer
+at a time.
+
+2.3
+Service - establishing a voice connection between two subscribers
+of a phone system. (Let's conveniently ignore multi-party connections,
+or faxes and modems.)
+
+Assumptions about environment - the phone system is infinitely
+available and error-free (sic).
+
+Vocabulary - off-hook, on-hook, dial 0 ... dial 9 (ignore star and sharp).
+Dial-tone, busy-tone, ring-tone. All messages listed here are control
+messages - the `data' of the protocol is encoded in the voice message.
+(For completeness then we could list `voice' as a separate message in the
+vocabulary, without attempting to formalize it's `encoding.')
+
+Encoding - lifting the receiver, lowering the receiver,
+pushing one of 10 labeled buttons.
+
+Informal procedure rules - Go off-hook, if no dial-tone is returned
+go on-hook and repeat a random amount of time later.
+If there is a dial-tone, push the sequence of buttons that identify the
+required destination to the phone system.
+If a busy-tone is returned in this interval, go on-hook, wait a random
+amount of time, and repeat from the start.
+If a ring-tone is returned - the call has been established - wait a
+random amount of time, go on-hook.
+
+Note that the random wait period after a busy signal makes it less likely
+that a `Lovers' Paradox' can be created (cf. Exercise 1.7).
+To be complete, the phone systems behavior should also be listed.
+Be warned that the latter is not a trivial exercise....
+
+2.4
+The revised version is the famous alternating bit protocol, see Chapter 4.
+
+2.5
+The receiver can then determine where a message (should) end by
+counting the number of bytes it receives, following the header.
+It does not have to scan for a pre-defined message terminator.
+
+2.6
+For isntance, a character stuffed protocol always transmits an integral
+number of bytes. A bit stuffed protocol carries slightly less overhead.
+
+2.7
+See Bertsekas and Gallager, [1987, p. 78-79].
+
+2.8
+More detailed sample assignments for software projects such as
+this one are available from the author (email to gerard@research.att.com).
+
+3.1
+The code rate is 0.2. Protection against bursts is limited
+to errors affecting maximally 2 out of the 5 bytes.
+At 56 Kbits/sec that means bursts smaller than 0.28 msec.
+
+3.3
+Does the crc need to be protected by a crc?
+
+3.4
+In many cases English sentences are redundant enough that
+forward error correction is possible. Any real conversation,
+though, contains many examples of feedback error correction
+to resolve ambiguities.
+To stick with the example - if the sentence ``the dog run'' is
+received, the original version (i.e., one or more dogs) cannot be
+determined without feedback error control.
+
+3.5
+(a) - the checksum is 6 bits wide.
+(b) - the original data is equal to the first n-6 bits of the code word
+(6 bits in this case).
+(c) - there were no transmission errors other than possible multiples
+of the generator polynomial itself.
+
+3.6
+The standard example is that of the voyager space craft near
+the planet Jupiter receiving a course adjustment from earth.
+A retransmission of the message would mean hours delay and invalidate
+the original commands.
+If the return channel has a high probability of error (e.g., a low
+power transmitter on board the spacecraft, sending out a very weak
+signal back to earth), the chances that a retransmission request would
+reach the earth intact may also be unacceptably low.
+
+3.8
+It is impossible to reduce a non-zero error rate to zero.
+The probability of error can be brought arbitrarily close to zero,
+at the expense of transmission speed, but it cannot reach zero.
+The scheme suggested would violate this principle and therefore
+should be placed in the same category as perpetuum mobiles and time-travel.
+
+3.9
+Fletcher's algorithm can be classified as a systematic block code.
+
+4.2
+The alternating bit protocol does not protect against
+duplication errors or reordering errors.
+Duplication errors persist (duplicate messages do not dissapear
+but generate duplicate acks etc, for the duration of the session.)
+Reordering can cause erroneous data to be accepted.
+
+4.5
+Too short a timeout creates duplicate messages.
+The duplicates lower the throughput for the remainder of the session.
+Too long a timeout increases idle-time and lowers the
+throughput.
+
+4.6
+Ignore duplicate acks.
+
+4.8
+See Bertsekas and Gallager [1987, pp. 28-29].
+
+4.9
+In at least one case (when the receiver is one full window of
+messages behind the sender) there is a confusion case where
+the receiver cannot tell if an incoming message is a repeat
+from W messages back, or a new message.
+
+4.10
+Store the data in buffer[n%W] where W is window size.
+
+4.11
+Use time-stamps and restrict the maximum life time of
+a message. Note however that time-stamps are just another
+flavor of sequence numbers and they would have to be selected
+from a sufficiently large window.
+For 32 bit sequence numbers one message transmission
+per micro-second would recycle the number in 71 minutes.
+For 64 bit sequence numbers, the number recycles
+at the same transmission speed in 500,000 years.
+
+4.12
+Alpha controls the rate of adaption to changes in
+network performance.
+Beta controls the allowed variance in response time.
+(It estimates the load variance of the remote host.)
+
+4.13
+Most importantly, all assumptions about the environment,
+specifically of the tranmission channel used, are missing completely.
+
+4.14
+The message could be received again and cause a duplicate acceptance.
+
+5.1
+An assignment is always executable. The variable b would be set
+to the value 0.
+
+5.2
+If the receive is executable on the first attempt to execute
+the statement, the message would be received, and the condition
+would be false (since the `executability' value of the receive is
+non-zero). The statement would block, and would be repeated.
+If the receive is (finally) non-executable, the receive fails,
+but the condition becomes true and executable.
+For all clarity: this is not valid Promela syntax. In Promela
+the rule is that the evaluation of a condition must always be
+completely side-effect free.
+
+5.3
+
+/***** Factorial - without channels *****/
+
+int par[16];
+int done[16];
+int depth;
+
+proctype fact()
+{ int r, n, m;
+
+ m = depth;
+ n = par[m];
+ if
+ :: (n <= 1) -> r = 1
+ :: (n >= 2) ->
+ depth = m + 1;
+ par[m+1] = n-1;
+ run fact();
+ done[m+1];
+ r = par[m+1];
+ depth = m;
+ r = r*n
+ fi;
+ par[m] = r;
+ printf("Value: %d\n", par[m]);
+ done[m] = 1
+}
+
+init
+{ depth = 0;
+ par[0] = 12;
+ run fact();
+ done[0];
+ printf("value: %d\n", par[0])
+ /* factorial of 12: 12*11*10....*2*1 = 479001600 */
+}
+
+/***** Ackermann's function *****/
+
+short ans[100];
+short done[100]; /* synchronization */
+
+proctype ack(short a, b, c)
+{
+ if
+ :: (a == 0) ->
+ ans[c] = b+1
+ :: (a != 0) ->
+ done[c+1] = 0;
+ if
+ :: (b == 0) ->
+ run ack(a-1, 1, c+1)
+ :: (b != 0) ->
+ run ack(a, b-1, c+1);
+ done[c+1]; /* wait */
+ done[c+1] = 0;
+ run ack(a-1, ans[c+1], c+1)
+ fi;
+ done[c+1]; /* wait */
+ ans[c] = ans[c+1]
+ fi;
+ done[c] = 1
+}
+init
+{
+ run ack(3, 3, 0);
+ done[0]; /* wait */
+ printf("ack(3,3) = %d\n", ans[0])
+}
+
+5.10
+
+Here, as an inspiration, is another sort program, performing
+a tree-sort.
+
+/**** Tree sorter ****/
+
+mtype = { data, eof };
+
+proctype seed(chan in)
+{ byte num, nr;
+
+ do
+ :: (num < 250) -> num = num + 5
+ :: (num > 3) -> num = num - 3
+ :: in!data,num
+ :: (num > 200) -> in!eof,0 -> break
+ od
+}
+
+init {
+ chan in[1] of { byte, byte };
+ chan rgt [1] of { byte, byte };
+ byte n;
+
+ run seed(in);
+ in?data,n;
+ run node(n, rgt, in);
+ do
+ :: rgt?eof,0 -> printf("\n"); break
+ :: rgt?data,n -> printf("%d, ", n)
+ od
+
+}
+
+proctype node(byte hold; chan up, down)
+{ chan lft [1] of { byte, byte };
+ chan rgt [1] of { byte, byte };
+ chan ret [1] of { byte, byte };
+ byte n; bit havergt, havelft;
+
+ do
+ :: down?data,n ->
+ if
+ :: (n > hold) ->
+ if
+ :: ( havelft) -> lft!data,n
+ :: (!havelft) -> havelft=1;
+ run node(n, ret, lft)
+ fi
+ :: (n <= hold) ->
+ if
+ :: ( havergt) -> rgt!data,n
+ :: (!havergt) -> havergt=1;
+ run node(n, ret, rgt)
+ fi
+ fi
+ :: down?eof,0 -> break
+ od;
+ if
+ :: (!havergt) -> skip
+ :: ( havergt) -> rgt!eof,0;
+ do
+ :: ret?data,n -> up!data,n
+ :: ret?eof,0 -> break
+ od
+ fi;
+ up!data,hold;
+ if
+ :: (!havelft) -> skip
+ :: ( havelft) -> lft!eof,0;
+ do
+ :: ret?data,n -> up!data,n
+ :: ret?eof,0 -> break
+ od
+ fi;
+ up!eof,0
+}
+
+5.13
+Promela is a validation modeling language, not an implementation language.
+Why does a civil engineer not use steel beams in wooden bridge models?
+
+6.1
+The assertion can be placed inside the critical section.
+The simplest way is as follows (rewritten with some features
+from the more recent versions of Spin):
+
+ mtype = { p, v }
+
+ chan sema[0] of { mtype };
+ byte cnt;
+
+ active proctype dijkstra() /* 1 semaphore process */
+ { do
+ :: sema!p -> sema?v
+ od
+ }
+ active [3] proctype user() /* 3 user processes */
+ {
+ sema?p;
+ cnt++;
+ assert (cnt == 0 || cnt == 1); /* critical section */
+ cnt--;
+ sem!v
+ skip /* non-critical section */
+ }
+
+6.2
+To check the truth of the invariant
+for every reachable state, one can write simply:
+
+ never { do :: assert(invariant) od }
+
+Or to match an invalid behavior by reaching the
+end of the never claim, without assertions:
+
+ never
+ { do
+ :: (invariant) /* things are fine, the invariant holds */
+ :: !(invariant) -> break /* invariant fails - match */
+ od
+ }
+
+Note that semi-colons (or arrows) in never claims match system transitions,
+(i.e., each transition in the system must be matched by a move in the
+never claim; the claim does not move independently).
+
+6.5
+Using accept labels, for instance:
+
+ proctype A()
+ { do
+ :: x = true;
+ t = Bturn;
+ (y == false || t == Aturn);
+ ain = true;
+ CS: skip; /* the critical section */
+ ain = false;
+ x = false
+ od
+ }
+ ... and simularly for proctype B()
+
+ never {
+ do
+ :: skip /* allow arbitrary initial execution */
+ :: !A[1]@CS -> goto accept1
+ :: !B[2]@CS -> goto accept2
+ od;
+ accept1:
+ do :: !A[1]@CS od /* process 1 denied access forever */
+ accept2:
+ do :: !B[2]@CS od /* process 2 denied access forever */
+ }
+
+
+6.6.a
+ never {
+ do
+ :: skip /* after an arbitrary number of steps */
+ :: p -> break
+ od;
+ accept:
+ do
+ :: p /* can only match if p remains true forever */
+ od
+ }
+
+6.6.b
+For instance:
+ never {
+ do
+ :: assert(q || p) /* "!q implies p" */
+ od
+ }
+
+6.6.c
+ never { /* <> (p U q) */
+ do
+ :: skip /* after an arbitrary number of steps */
+ :: p -> break /* eventually */
+ od;
+ do
+ :: p /* p remains true */
+ :: q -> break /* at least until q becomes true */
+ od
+ /* invalid behavior is matched if we get here */
+ }
+
+The translation has been automated, and is standard in Spin version 2.7
+and later (Spin option -f).
+
+6.7
+A research problem -- there are no easy answers.
+
+6.8
+ assert(0)
+is an immediate violation in both finite or infinite execution sequences,
+and is a safety property.
+
+ accept: do :: skip od
+
+is only a violation for an infinite execution sequence, and is a liveness
+property (i.e., requires a nested depth-first search for acceptance
+cycles). The safety property can be proven more effieciently.
+Other than this, the two properties are equivalent;
+
+7.1
+Layers 3 to 5 and layer 7.
+
+7.2
+At the sender: first checksumming, then byte stuffing and framing.
+At the receiver: first unstuffing and de-framing, then checksum
+verification.
+
+7.3
+Rate control is placed at the layer that handles the units it
+refers too (for bit rates, it is the physical layer - for packets
+it would be the layer that produces packets, etc.).
+Dynamic flow control belongs in the flow control module.
+
+7.13
+The one-bit solution will no longer work.
+
+8.1
+The dash is used as a don't care symbol - any valid symbol
+could replace it without changing the validity of the specification.
+The epsilon is a null-element, i.e. it represents the absence
+of a symbol (the empty set).
+
+8.7
+No, the run and chan operators are defined to be unexecutable
+when an (implementation dependent) bound is reached.
+
+9.2
+No.
+
+9.5
+More states, up to a predefined bound only. Fewer states, yes.
+
+9.6
+No, the IUT could be arbitrarily large.
+
+9.8
+It can no longer detect transfer errors.
+
+10.2
+The computational complexity will make an interactive
+solution impossible for all but the simplest
+applications.
+
+11.3
+Missing from the program text is that variable j is
+initialized to 1 minus the value of i.
+
+12.1
+Note that the number of states to be searched by a validator on
+such a protocol would multiply by the range of the time count...
+
+13.1
+If done right, the changes will be minimal (say 10 to 20 lines
+of source).
+The memory requirements will very quickly make validations
+effectively impossible.
--- /dev/null
+# To unbundle, sh this file
+echo README 1>&2
+sed 's/.//' >README <<'//GO.SYSIN DD README'
+-Readme
+-------
+-The files in this set contain the text of examples
+-used in the Design and\16 Validation of Computer
+-Protocols book. The name of each file corresponds to the
+-page number in the book where the example appears in its
+-most useful version. The overview below gives a short
+-descriptive phrase for each file.
+-
+-Description Page Nr = Filename
+------------ ------------------
+-hello world = p95.1
+-tiny examples = p94 p95.2 p96.1 p97.1 p97.2 p101 p102 p104.1
+-useful demos = p99 p104.2 p105.2 p116 p248
+-mutual excl. = p96.2 p105.1 p117 p320
+-Lynch's prot. = p107 p312
+-alternatin bit = p123
+-chappe's prot. = p319
+-
+-Large runs
+-----------
+-ackerman's fct = p108 # read info at start of the file
+-
+-Pftp Protocol
+--------------
+-upper tester = p325.test # not runnable
+-flow control l. = p329 p330
+-session layer = p337.pftp.ses p342.pftp.ses1 p347.pftp.ses5
+-all pftp = App.F.pftp - plus 8 include files
+-
+-See also the single file version of pftp in: Test/pftp
+-
+-General
+--------
+-Use these examples for inspiration, and to get quickly
+-acquainted with the language and the Spin software.
+-All examples - except p123 - can be used with both version
+-1 and version 2 of SPIN. (p123 was modifed for versoin 2.0
+-to use the new syntax for remote referencing).
+-If you repeat the runs that are listed in the book for
+-these examples, you should expect to get roughly the same
+-numbers in the result - although in some cases there may
+-be small differences that are due to changes in bookkeeping.
+-
+-For instance, for p329, the book (Spin V1.0) says
+-on pg. 332, using a BITSTATE run, that there are:
+- 90845 + 317134 + 182425 states (stored + linked + matched)
+-Spin V2.0 reports the numbers:
+- 90837 + 317122 + 182421 states (stored + atomic + matched)
+-and when compiled for partial order reduction (-DREDUCE):
+- 74016 + 203616 + 104008 states (stored + atomic + matched)
+-
+-If you repeat a BITSTATE run, of course, by the nature of the
+-machine dependent effect of hashing, you may get different
+-coverage and hash-factors for larger runs. The implementation
+-of the hash functions has also been improved in version 2.0,
+-so the numbers you see will likely differ. The numbers, though,
+-should still be in the same range as those reported in the book.
+-
+-The last set of file (prefixed App.F) is included for completeness.
+-As explained in the book: don't expect to be able to do an
+-exhaustive verification for this specification as listed.
+-In chapter 14 it is illustrated how the spec can be broken up
+-into smaller portions that are more easily verified.
+-
+-Some Small Experiments
+------------------------
+-Try:
+- spin p95.1 # small simulation run
+-
+- spin -s p108 # bigger simulation run, track send stmnts
+-
+- spin -a p312 # lynch's protocol - generate verifier
+- cc -o pan pan.c # compile it for exhaustive verification
+- pan # prove correctness of assertions etc.
+- spin -t -r -s p312 # display the error trace
+-
+-now edit p312 to change all three channel declarations in init
+-to look like: ``chan AtoB = [1] of { mtype byte }''
+-and repeat the above four steps.
+-note the improvement in the trace.
+-
+- spin -a p123 # alternating bit protocol - generate verifier
+- cc -o pan pan.c # compile it for exhaustive verification
+- pan -a # check violations of the never claim
+- spin -t -r -s p123 # display the error trace
+-
+-for more intuitive use of all the above options: try using the
+-graphical interface xspin, and repeat the experiments.
+//GO.SYSIN DD README
+echo App.F.datalink 1>&2
+sed 's/.//' >App.F.datalink <<'//GO.SYSIN DD App.F.datalink'
+-/*
+- * Datalink Layer Validation Model
+- */
+-
+-proctype data_link()
+-{ byte type, seq;
+-
+-end: do
+- :: flow_to_dll[0]?type,seq ->
+- if
+- :: dll_to_flow[1]!type,seq
+- :: skip /* lose message */
+- fi
+- :: flow_to_dll[1]?type,seq ->
+- if
+- :: dll_to_flow[0]!type,seq
+- :: skip /* lose message */
+- fi
+- od
+-}
+//GO.SYSIN DD App.F.datalink
+echo App.F.defines 1>&2
+sed 's/.//' >App.F.defines <<'//GO.SYSIN DD App.F.defines'
+-/*
+- * Global Definitions
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan use_to_pres[2] = [QSZ] of { byte };
+-chan pres_to_use[2] = [QSZ] of { byte };
+-chan pres_to_ses[2] = [QSZ] of { byte };
+-chan ses_to_pres[2] = [QSZ] of { byte, byte };
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2] = [QSZ] of { byte, byte };
+-chan ses_to_fsrv[2] = [0] of { byte };
+-chan fsrv_to_ses[2] = [0] of { byte };
+//GO.SYSIN DD App.F.defines
+echo App.F.flow_cl 1>&2
+sed 's/.//' >App.F.flow_cl <<'//GO.SYSIN DD App.F.flow_cl'
+-/*
+- * Flow Control Layer Validation Model
+- */
+-
+-#define true 1
+-#define false 0
+-
+-#define M 4 /* range sequence numbers */
+-#define W 2 /* window size: M/2 */
+-
+-proctype fc(bit n)
+-{ bool busy[M]; /* outstanding messages */
+- byte q; /* seq# oldest unacked msg */
+- byte m; /* seq# last msg received */
+- byte s; /* seq# next msg to send */
+- byte window; /* nr of outstanding msgs */
+- byte type; /* msg type */
+- bit received[M]; /* receiver housekeeping */
+- bit x; /* scratch variable */
+- byte p; /* seq# of last msg acked */
+- byte I_buf[M], O_buf[M]; /* message buffers */
+-
+- /* sender part */
+-end: do
+- :: atomic {
+- (window < W && len(ses_to_flow[n]) > 0
+- && len(flow_to_dll[n]) < QSZ) ->
+- ses_to_flow[n]?type,x;
+- window = window + 1;
+- busy[s] = true;
+- O_buf[s] = type;
+- flow_to_dll[n]!type,s;
+- if
+- :: (type != sync) ->
+- s = (s+1)%M
+- :: (type == sync) ->
+- window = 0;
+- s = M;
+- do
+- :: (s > 0) ->
+- s = s-1;
+- busy[s] = false
+- :: (s == 0) ->
+- break
+- od
+- fi
+- }
+- :: atomic {
+- (window > 0 && busy[q] == false) ->
+- window = window - 1;
+- q = (q+1)%M
+- }
+-#if DUPS
+- :: atomic {
+- (len(flow_to_dll[n]) < QSZ
+- && window > 0 && busy[q] == true) ->
+- flow_to_dll[n]! O_buf[q],q
+- }
+-#endif
+- :: atomic {
+- (timeout && len(flow_to_dll[n]) < QSZ
+- && window > 0 && busy[q] == true) ->
+- flow_to_dll[n]! O_buf[q],q
+- }
+-
+- /* receiver part */
+-#if LOSS
+- :: dll_to_flow[n]?type,m /* lose any message */
+-#endif
+- :: dll_to_flow[n]?type,m ->
+- if
+- :: atomic {
+- (type == ack) ->
+- busy[m] = false
+- }
+- :: atomic {
+- (type == sync) ->
+- flow_to_dll[n]!sync_ack,m;
+- m = 0;
+- do
+- :: (m < M) ->
+- received[m] = 0;
+- m = m+1
+- :: (m == M) ->
+- break
+- od
+- }
+- :: (type == sync_ack) ->
+- flow_to_ses[n]!sync_ack,m
+- :: (type != ack && type != sync && type != sync_ack)->
+- if
+- :: atomic {
+- (received[m] == true) ->
+- x = ((0<p-m && p-m<=W)
+- || (0<p-m+M && p-m+M<=W)) };
+- if
+- :: (x) -> flow_to_dll[n]!ack,m
+- :: (!x) /* else skip */
+- fi
+- :: atomic {
+- (received[m] == false) ->
+- I_buf[m] = type;
+- received[m] = true;
+- received[(m-W+M)%M] = false
+- }
+- fi
+- fi
+- :: (received[p] == true && len(flow_to_ses[n])<QSZ
+- && len(flow_to_dll[n])<QSZ) ->
+- flow_to_ses[n]!I_buf[p],0;
+- flow_to_dll[n]!ack,p;
+- p = (p+1)%M
+- od
+-}
+//GO.SYSIN DD App.F.flow_cl
+echo App.F.fserver 1>&2
+sed 's/.//' >App.F.fserver <<'//GO.SYSIN DD App.F.fserver'
+-/*
+- * File Server Validation Model
+- */
+-
+-proctype fserver(bit n)
+-{
+-end: do
+- :: ses_to_fsrv[n]?create -> /* incoming */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: ses_to_fsrv[n]?data
+- :: ses_to_fsrv[n]?eof -> break
+- :: ses_to_fsrv[n]?close -> break
+- od
+- fi
+- :: ses_to_fsrv[n]?open -> /* outgoing */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: fsrv_to_ses[n]!data -> progress: skip
+- :: ses_to_fsrv[n]?close -> break
+- :: fsrv_to_ses[n]!eof -> break
+- od
+- fi
+- od
+-}
+//GO.SYSIN DD App.F.fserver
+echo App.F.pftp 1>&2
+sed 's/.//' >App.F.pftp <<'//GO.SYSIN DD App.F.pftp'
+-/*
+- * PROMELA Validation Model - startup script
+- */
+-
+-#include "App.F.defines"
+-#include "App.F.user"
+-#include "App.F.present"
+-#include "App.F.session"
+-#include "App.F.fserver"
+-#include "App.F.flow_cl"
+-#include "App.F.datalink"
+-
+-init
+-{ atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- run fc(0); run fc(1);
+- run data_link()
+- }
+-}
+//GO.SYSIN DD App.F.pftp
+echo App.F.present 1>&2
+sed 's/.//' >App.F.present <<'//GO.SYSIN DD App.F.present'
+-/*
+- * Presentation Layer Validation Model
+- */
+-
+-proctype present(bit n)
+-{ byte status, uabort;
+-
+-endIDLE:
+- do
+- :: use_to_pres[n]?transfer ->
+- uabort = 0;
+- break
+- :: use_to_pres[n]?abort ->
+- skip
+- od;
+-
+-TRANSFER:
+- pres_to_ses[n]!transfer;
+- do
+- :: use_to_pres[n]?abort ->
+- if
+- :: (!uabort) ->
+- uabort = 1;
+- pres_to_ses[n]!abort
+- :: (uabort) ->
+- assert(1+1!=2)
+- fi
+- :: ses_to_pres[n]?accept,0 ->
+- goto DONE
+- :: ses_to_pres[n]?reject(status) ->
+- if
+- :: (status == FATAL || uabort) ->
+- goto FAIL
+- :: (status == NON_FATAL && !uabort) ->
+-progress: goto TRANSFER
+- fi
+- od;
+-DONE:
+- pres_to_use[n]!accept;
+- goto endIDLE;
+-FAIL:
+- pres_to_use[n]!reject;
+- goto endIDLE
+-}
+//GO.SYSIN DD App.F.present
+echo App.F.session 1>&2
+sed 's/.//' >App.F.session <<'//GO.SYSIN DD App.F.session'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto DATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto DATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-DATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+- ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-DATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control */
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD App.F.session
+echo App.F.user 1>&2
+sed 's/.//' >App.F.user <<'//GO.SYSIN DD App.F.user'
+-/*
+- * User Layer Validation Model
+- */
+-
+-proctype userprc(bit n)
+-{
+- use_to_pres[n]!transfer;
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- :: use_to_pres[n]!abort -> goto Aborted
+- fi;
+-Aborted:
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- fi;
+-Done:
+- skip
+-}
+//GO.SYSIN DD App.F.user
+echo p101 1>&2
+sed 's/.//' >p101 <<'//GO.SYSIN DD p101'
+-#define msgtype 33
+-
+-chan name = [0] of { byte, byte };
+-
+-/* byte name; typo - this line shouldn't have been here */
+-
+-proctype A()
+-{ name!msgtype(124);
+- name!msgtype(121)
+-}
+-proctype B()
+-{ byte state;
+- name?msgtype(state)
+-}
+-init
+-{ atomic { run A(); run B() }
+-}
+//GO.SYSIN DD p101
+echo p102 1>&2
+sed 's/.//' >p102 <<'//GO.SYSIN DD p102'
+-#define a 1
+-#define b 2
+-
+-chan ch = [1] of { byte };
+-
+-proctype A() { ch!a }
+-proctype B() { ch!b }
+-proctype C()
+-{ if
+- :: ch?a
+- :: ch?b
+- fi
+-}
+-init { atomic { run A(); run B(); run C() } }
+//GO.SYSIN DD p102
+echo p104.1 1>&2
+sed 's/.//' >p104.1 <<'//GO.SYSIN DD p104.1'
+-#define N 128
+-#define size 16
+-
+-chan in = [size] of { short };
+-chan large = [size] of { short };
+-chan small = [size] of { short };
+-
+-proctype split()
+-{ short cargo;
+-
+- do
+- :: in?cargo ->
+- if
+- :: (cargo >= N) -> large!cargo
+- :: (cargo < N) -> small!cargo
+- fi
+- od
+-}
+-init { run split() }
+//GO.SYSIN DD p104.1
+echo p104.2 1>&2
+sed 's/.//' >p104.2 <<'//GO.SYSIN DD p104.2'
+-#define N 128
+-#define size 16
+-
+-chan in = [size] of { short };
+-chan large = [size] of { short };
+-chan small = [size] of { short };
+-
+-proctype split()
+-{ short cargo;
+-
+- do
+- :: in?cargo ->
+- if
+- :: (cargo >= N) -> large!cargo
+- :: (cargo < N) -> small!cargo
+- fi
+- od
+-}
+-proctype merge()
+-{ short cargo;
+-
+- do
+- :: if
+- :: large?cargo
+- :: small?cargo
+- fi;
+- in!cargo
+- od
+-}
+-init
+-{ in!345; in!12; in!6777; in!32; in!0;
+- run split(); run merge()
+-}
+//GO.SYSIN DD p104.2
+echo p105.1 1>&2
+sed 's/.//' >p105.1 <<'//GO.SYSIN DD p105.1'
+-#define p 0
+-#define v 1
+-
+-chan sema = [0] of { bit };
+-
+-proctype dijkstra()
+-{ do
+- :: sema!p -> sema?v
+- od
+-}
+-proctype user()
+-{ sema?p;
+- /* critical section */
+- sema!v
+- /* non-critical section */
+-}
+-init
+-{ atomic {
+- run dijkstra();
+- run user(); run user(); run user()
+- }
+-}
+//GO.SYSIN DD p105.1
+echo p105.2 1>&2
+sed 's/.//' >p105.2 <<'//GO.SYSIN DD p105.2'
+-proctype fact(int n; chan p)
+-{ int result;
+-
+- if
+- :: (n <= 1) -> p!1
+- :: (n >= 2) ->
+- chan child = [1] of { int };
+- run fact(n-1, child);
+- child?result;
+- p!n*result
+- fi
+-}
+-init
+-{ int result;
+- chan child = [1] of { int };
+-
+- run fact(7, child);
+- child?result;
+- printf("result: %d\n", result)
+-}
+//GO.SYSIN DD p105.2
+echo p107 1>&2
+sed 's/.//' >p107 <<'//GO.SYSIN DD p107'
+-mtype = { ack, nak, err, next, accept }
+-
+-proctype transfer(chan in, out, chin, chout)
+-{ byte o, i;
+-
+- in?next(o);
+- do
+- :: chin?nak(i) -> out!accept(i); chout!ack(o)
+- :: chin?ack(i) -> out!accept(i); in?next(o); chout!ack(o)
+- :: chin?err(i) -> chout!nak(o)
+- od
+-}
+-init
+-{ chan AtoB = [1] of { byte, byte };
+- chan BtoA = [1] of { byte, byte };
+- chan Ain = [2] of { byte, byte };
+- chan Bin = [2] of { byte, byte };
+- chan Aout = [2] of { byte, byte };
+- chan Bout = [2] of { byte, byte };
+-
+- atomic {
+- run transfer(Ain, Aout, AtoB, BtoA);
+- run transfer(Bin, Bout, BtoA, AtoB)
+- };
+- AtoB!err(0)
+-}
+//GO.SYSIN DD p107
+echo p108 1>&2
+sed 's/.//' >p108 <<'//GO.SYSIN DD p108'
+-/***** Ackermann's function *****/
+-
+-/* a good example where a simulation run is the
+- better choice - and verification is overkill.
+-
+- 1. simulation
+- -> straight simulation (spin p108) takes
+- -> approx. 6.4 sec on an SGI R3000
+- -> prints the answer: ack(3,3) = 61
+- -> after creating 2433 processes
+-
+- note: all process invocations can, at least in one
+- feasible execution scenario, overlap - if each
+- process chooses to hang around indefinitely in
+- its dying state, at the closing curly brace.
+- this means that the maximum state vector `could' grow
+- to hold all 2433 processes or about 2433*12 bytes of data.
+- the assert(0) at the end makes sure though that the run
+- stops the first time we complete an execution sequence
+- that computes the answer, so the following suffices:
+-
+- 2. verification
+- -> spin -a p108
+- -> cc -DVECTORSZ=2048 -o pan pan.c
+- -> pan -m15000
+- -> which completes in about 5 sec
+- */
+-
+-proctype ack(short a, b; chan ch1)
+-{ chan ch2 = [1] of { short };
+- short ans;
+-
+- if
+- :: (a == 0) ->
+- ans = b+1
+- :: (a != 0) ->
+- if
+- :: (b == 0) ->
+- run ack(a-1, 1, ch2)
+- :: (b != 0) ->
+- run ack(a, b-1, ch2);
+- ch2?ans;
+- run ack(a-1, ans, ch2)
+- fi;
+- ch2?ans
+- fi;
+- ch1!ans
+-}
+-init
+-{ chan ch = [1] of { short };
+- short ans;
+-
+- run ack(3, 3, ch);
+- ch?ans;
+- printf("ack(3,3) = %d\n", ans);
+- assert(0) /* a forced stop, (Chapter 6) */
+-}
+//GO.SYSIN DD p108
+echo p116 1>&2
+sed 's/.//' >p116 <<'//GO.SYSIN DD p116'
+-byte state = 1;
+-
+-proctype A()
+-{ (state == 1) -> state = state + 1;
+- assert(state == 2)
+-}
+-proctype B()
+-{ (state == 1) -> state = state - 1;
+- assert(state == 0)
+-}
+-init { run A(); run B() }
+//GO.SYSIN DD p116
+echo p117 1>&2
+sed 's/.//' >p117 <<'//GO.SYSIN DD p117'
+-#define p 0
+-#define v 1
+-
+-chan sema = [0] of { bit }; /* typo in original `=' was missing */
+-
+-proctype dijkstra()
+-{ do
+- :: sema!p -> sema?v
+- od
+-}
+-byte count;
+-
+-proctype user()
+-{ sema?p;
+- count = count+1;
+- skip; /* critical section */
+- count = count-1;
+- sema!v;
+- skip /* non-critical section */
+-}
+-proctype monitor() { assert(count == 0 || count == 1) }
+-init
+-{ atomic {
+- run dijkstra(); run monitor();
+- run user(); run user(); run user()
+- }
+-}
+//GO.SYSIN DD p117
+echo p123 1>&2
+sed 's/.//' >p123 <<'//GO.SYSIN DD p123'
+-/* alternating bit - version with message loss */
+-
+-#define MAX 3
+-
+-mtype = { msg0, msg1, ack0, ack1 };
+-
+-chan sender =[1] of { byte };
+-chan receiver=[1] of { byte };
+-
+-proctype Sender()
+-{ byte any;
+-again:
+- do
+- :: receiver!msg1;
+- if
+- :: sender?ack1 -> break
+- :: sender?any /* lost */
+- :: timeout /* retransmit */
+- fi
+- od;
+- do
+- :: receiver!msg0;
+- if
+- :: sender?ack0 -> break
+- :: sender?any /* lost */
+- :: timeout /* retransmit */
+- fi
+- od;
+- goto again
+-}
+-
+-proctype Receiver()
+-{ byte any;
+-again:
+- do
+- :: receiver?msg1 -> sender!ack1; break
+- :: receiver?msg0 -> sender!ack0
+- :: receiver?any /* lost */
+- od;
+-P0:
+- do
+- :: receiver?msg0 -> sender!ack0; break
+- :: receiver?msg1 -> sender!ack1
+- :: receiver?any /* lost */
+- od;
+-P1:
+- goto again
+-}
+-
+-init { atomic { run Sender(); run Receiver() } }
+-
+-never {
+- do
+- :: skip /* allow any time delay */
+- :: receiver?[msg0] -> goto accept0
+- :: receiver?[msg1] -> goto accept1
+- od;
+-accept0:
+- do
+- :: !Receiver[2]@P0 /* n.b. new syntax of remote reference */
+- od;
+-accept1:
+- do
+- :: !Receiver[2]@P1
+- od
+-}
+//GO.SYSIN DD p123
+echo p248 1>&2
+sed 's/.//' >p248 <<'//GO.SYSIN DD p248'
+-proctype fact(int n; chan p)
+-{ int result;
+-
+- if
+- :: (n <= 1) -> p!1
+- :: (n >= 2) ->
+- chan child = [1] of { int };
+- run fact(n-1, child);
+- child?result;
+- p!n*result
+- fi
+-}
+-init
+-{ int result;
+- chan child = [1] of { int };
+-
+- run fact(12, child);
+- child?result;
+- printf("result: %d\n", result)
+-}
+//GO.SYSIN DD p248
+echo p312 1>&2
+sed 's/.//' >p312 <<'//GO.SYSIN DD p312'
+-#define MIN 9 /* first data message to send */
+-#define MAX 12 /* last data message to send */
+-#define FILL 99 /* filler message */
+-
+-mtype = { ack, nak, err }
+-
+-proctype transfer(chan chin, chout)
+-{ byte o, i, last_i=MIN;
+-
+- o = MIN+1;
+- do
+- :: chin?nak(i) ->
+- assert(i == last_i+1);
+- chout!ack(o)
+- :: chin?ack(i) ->
+- if
+- :: (o < MAX) -> o = o+1 /* next */
+- :: (o >= MAX) -> o = FILL /* done */
+- fi;
+- chout!ack(o)
+- :: chin?err(i) ->
+- chout!nak(o)
+- od
+-}
+-
+-proctype channel(chan in, out)
+-{ byte md, mt;
+- do
+- :: in?mt,md ->
+- if
+- :: out!mt,md
+- :: out!err,0
+- fi
+- od
+-}
+-
+-init
+-{ chan AtoB = [1] of { byte, byte };
+- chan BtoC = [1] of { byte, byte };
+- chan CtoA = [1] of { byte, byte };
+- atomic {
+- run transfer(AtoB, BtoC);
+- run channel(BtoC, CtoA);
+- run transfer(CtoA, AtoB)
+- };
+- AtoB!err,0 /* start */
+-}
+//GO.SYSIN DD p312
+echo p319 1>&2
+sed 's/.//' >p319 <<'//GO.SYSIN DD p319'
+-#define true 1
+-#define false 0
+-
+-bool busy[3];
+-
+-chan up[3] = [1] of { byte };
+-chan down[3] = [1] of { byte };
+-
+-mtype = { start, attention, data, stop }
+-
+-proctype station(byte id; chan in, out)
+-{ do
+- :: in?start ->
+- atomic { !busy[id] -> busy[id] = true };
+- out!attention;
+- do
+- :: in?data -> out!data
+- :: in?stop -> break
+- od;
+- out!stop;
+- busy[id] = false
+- :: atomic { !busy[id] -> busy[id] = true };
+- out!start;
+- in?attention;
+- do
+- :: out!data -> in?data
+- :: out!stop -> break
+- od;
+- in?stop;
+- busy[id] = false
+- od
+-}
+-
+-init {
+- atomic {
+- run station(0, up[2], down[2]);
+- run station(1, up[0], down[0]);
+- run station(2, up[1], down[1]);
+-
+- run station(0, down[0], up[0]);
+- run station(1, down[1], up[1]);
+- run station(2, down[2], up[2])
+- }
+-}
+//GO.SYSIN DD p319
+echo p320 1>&2
+sed 's/.//' >p320 <<'//GO.SYSIN DD p320'
+-#define true 1
+-#define false 0
+-#define Aturn false
+-#define Bturn true
+-
+-bool x, y, t;
+-bool ain, bin;
+-
+-proctype A()
+-{ x = true;
+- t = Bturn;
+- (y == false || t == Aturn);
+- ain = true;
+- assert(bin == false); /* critical section */
+- ain = false;
+- x = false
+-}
+-
+-proctype B()
+-{ y = true;
+- t = Aturn;
+- (x == false || t == Bturn);
+- bin = true;
+- assert(ain == false); /* critical section */
+- bin = false;
+- y = false
+-}
+-
+-init
+-{ run A(); run B()
+-}
+//GO.SYSIN DD p320
+echo p325.test 1>&2
+sed 's/.//' >p325.test <<'//GO.SYSIN DD p325.test'
+-proctype test_sender(bit n)
+-{ byte type, toggle;
+-
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- :: timeout ->
+- ses_to_flow[n]!sync,toggle
+- od;
+- toggle = 1 - toggle;
+-
+- do
+- :: ses_to_flow[n]!data,white
+- :: ses_to_flow[n]!data,red -> break
+- od;
+- do
+- :: ses_to_flow[n]!data,white
+- :: ses_to_flow[n]!data,blue -> break
+- od;
+- do
+- :: ses_to_flow[n]!data,white
+- :: break
+- od
+-}
+-proctype test_receiver(bit n)
+-{
+- do
+- :: flow_to_ses[n]?data,white
+- :: flow_to_ses[n]?data,red -> break
+- od;
+- do
+- :: flow_to_ses[n]?data,white
+- :: flow_to_ses[n]?data,blue -> break
+- od;
+-end: do
+- :: flow_to_ses[n]?data,white
+- od
+-}
+//GO.SYSIN DD p325.test
+echo p327.upper 1>&2
+sed 's/.//' >p327.upper <<'//GO.SYSIN DD p327.upper'
+-proctype upper()
+-{ byte s_state, r_state;
+- byte type, toggle;
+-
+- ses_to_flow[0]!sync,toggle;
+- do
+- :: flow_to_ses[0]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- :: timeout ->
+- ses_to_flow[0]!sync,toggle
+- od;
+- toggle = 1 - toggle;
+-
+- do
+- /* sender */
+- :: ses_to_flow[0]!white,0
+- :: atomic {
+- (s_state == 0 && len (ses_to_flow[0]) < QSZ) ->
+- ses_to_flow[0]!red,0 ->
+- s_state = 1
+- }
+- :: atomic {
+- (s_state == 1 && len (ses_to_flow[0]) < QSZ) ->
+- ses_to_flow[0]!blue,0 ->
+- s_state = 2
+- }
+- /* receiver */
+- :: flow_to_ses[1]?white,0
+- :: atomic {
+- (r_state == 0 && flow_to_ses[1]?[red]) ->
+- flow_to_ses[1]?red,0 ->
+- r_state = 1
+- }
+- :: atomic {
+- (r_state == 0 && flow_to_ses[1]?[blue]) ->
+- assert(0)
+- }
+- :: atomic {
+- (r_state == 1 && flow_to_ses[1]?[blue]) ->
+- flow_to_ses[1]?blue,0;
+- break
+- }
+- :: atomic {
+- (r_state == 1 && flow_to_ses[1]?[red]) ->
+- assert(0)
+- }
+- od;
+-end:
+- do
+- :: flow_to_ses[1]?white,0
+- :: flow_to_ses[1]?red,0 -> assert(0)
+- :: flow_to_ses[1]?blue,0 -> assert(0)
+- od
+-}
+//GO.SYSIN DD p327.upper
+echo p329 1>&2
+sed 's/.//' >p329 <<'//GO.SYSIN DD p329'
+-/*
+- * PROMELA Validation Model
+- * FLOW CONTROL LAYER VALIDATION
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2];
+-
+-#include "App.F.flow_cl"
+-#include "p327.upper"
+-
+-init
+-{
+- atomic {
+- flow_to_dll[0] = dll_to_flow[1];
+- flow_to_dll[1] = dll_to_flow[0];
+- run fc(0); run fc(1);
+- run upper()
+- }
+-}
+//GO.SYSIN DD p329
+echo p330 1>&2
+sed 's/.//' >p330 <<'//GO.SYSIN DD p330'
+-/*
+- * PROMELA Validation Model
+- * FLOW CONTROL LAYER VALIDATION
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2];
+-
+-#include "App.F.flow_cl"
+-#include "p327.upper"
+-
+-init
+-{
+- atomic {
+- flow_to_dll[0] = dll_to_flow[1];
+- flow_to_dll[1] = dll_to_flow[0];
+- run fc(0); run fc(1);
+- run upper()
+- }
+-}
+//GO.SYSIN DD p330
+echo p337.defines2 1>&2
+sed 's/.//' >p337.defines2 <<'//GO.SYSIN DD p337.defines2'
+-/*
+- * PROMELA Validation Model
+- * global definitions
+- */
+-
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan use_to_pres[2] = [QSZ] of { mtype };
+-chan pres_to_use[2] = [QSZ] of { mtype };
+-chan pres_to_ses[2] = [QSZ] of { mtype };
+-chan ses_to_pres[2] = [QSZ] of { mtype, byte };
+-chan ses_to_flow[2] = [QSZ] of { mtype, byte };
+-chan ses_to_fsrv[2] = [0] of { mtype };
+-chan fsrv_to_ses[2] = [0] of { mtype };
+-chan flow_to_ses[2];
+//GO.SYSIN DD p337.defines2
+echo p337.fserver 1>&2
+sed 's/.//' >p337.fserver <<'//GO.SYSIN DD p337.fserver'
+-/*
+- * File Server Validation Model
+- */
+-
+-proctype fserver(bit n)
+-{
+-end: do
+- :: ses_to_fsrv[n]?create -> /* incoming */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: ses_to_fsrv[n]?data
+- :: ses_to_fsrv[n]?eof -> break
+- :: ses_to_fsrv[n]?close -> break
+- od
+- fi
+- :: ses_to_fsrv[n]?open -> /* outgoing */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: fsrv_to_ses[n]!data -> progress: skip
+- :: ses_to_fsrv[n]?close -> break
+- :: fsrv_to_ses[n]!eof -> break
+- od
+- fi
+- od
+-}
+//GO.SYSIN DD p337.fserver
+echo p337.pftp.ses 1>&2
+sed 's/.//' >p337.pftp.ses <<'//GO.SYSIN DD p337.pftp.ses'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p337.user"
+-#include "App.F.present"
+-#include "p337.session"
+-#include "p337.fserver"
+-
+-init
+-{ atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- }
+-}
+//GO.SYSIN DD p337.pftp.ses
+echo p337.session 1>&2
+sed 's/.//' >p337.session <<'//GO.SYSIN DD p337.session'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto DATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto DATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-DATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+- ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-DATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control *** disabled
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD p337.session
+echo p337.user 1>&2
+sed 's/.//' >p337.user <<'//GO.SYSIN DD p337.user'
+-/*
+- * User Layer Validation Model
+- */
+-
+-proctype userprc(bit n)
+-{
+- use_to_pres[n]!transfer;
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- :: use_to_pres[n]!abort -> goto Aborted
+- fi;
+-Aborted:
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- fi;
+-Done:
+- skip
+-}
+//GO.SYSIN DD p337.user
+echo p342.pftp.ses1 1>&2
+sed 's/.//' >p342.pftp.ses1 <<'//GO.SYSIN DD p342.pftp.ses1'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p337.user"
+-#include "App.F.present"
+-#include "p337.session"
+-#include "p337.fserver"
+-
+-init
+-{
+- atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- };
+- atomic {
+- byte any;
+- chan foo = [1] of { byte, byte };
+- ses_to_flow[0] = foo;
+- ses_to_flow[1] = foo
+- };
+-end: do
+- :: foo?any,any
+- od
+-}
+//GO.SYSIN DD p342.pftp.ses1
+echo p343.claim 1>&2
+sed 's/.//' >p343.claim <<'//GO.SYSIN DD p343.claim'
+-never {
+- skip; /* match first step of init (spin version 2.0) */
+- do
+- :: !pres_to_ses[0]?[transfer]
+- && !flow_to_ses[0]?[connect]
+- :: pres_to_ses[0]?[transfer] ->
+- goto accept0
+- :: flow_to_ses[0]?[connect] ->
+- goto accept1
+- od;
+-accept0:
+- do
+- :: !ses_to_pres[0]?[accept]
+- && !ses_to_pres[0]?[reject]
+- od;
+-accept1:
+- do
+- :: !ses_to_pres[1]?[accept]
+- && !ses_to_pres[1]?[reject]
+- od
+-}
+//GO.SYSIN DD p343.claim
+echo p347.pftp.ses5 1>&2
+sed 's/.//' >p347.pftp.ses5 <<'//GO.SYSIN DD p347.pftp.ses5'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p347.pres.sim"
+-#include "p347.session.prog"
+-#include "p337.fserver"
+-
+-init
+-{ atomic {
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- }
+-}
+//GO.SYSIN DD p347.pftp.ses5
+echo p347.pres.sim 1>&2
+sed 's/.//' >p347.pres.sim <<'//GO.SYSIN DD p347.pres.sim'
+-/*
+- * PROMELA Validation Model
+- * Presentation & User Layer - combined and reduced
+- */
+-
+-proctype present(bit n)
+-{ byte status;
+-progress0:
+- pres_to_ses[n]!transfer ->
+- do
+- :: pres_to_ses[n]!abort;
+-progress1: skip
+- :: ses_to_pres[n]?accept,status ->
+- break
+- :: ses_to_pres[n]?reject,status ->
+- if
+- :: (status == NON_FATAL) ->
+- goto progress0
+- :: (status != NON_FATAL) ->
+- break
+- fi
+- od
+-}
+//GO.SYSIN DD p347.pres.sim
+echo p347.session.prog 1>&2
+sed 's/.//' >p347.session.prog <<'//GO.SYSIN DD p347.session.prog'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto progressDATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto progressDATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-progressDATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+-progress: ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-progressDATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control *** disabled
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,status ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD p347.session.prog
+echo p94 1>&2
+sed 's/.//' >p94 <<'//GO.SYSIN DD p94'
+-byte state = 2;
+-
+-proctype A() { (state == 1) -> state = 3 }
+-
+-proctype B() { state = state - 1 }
+-
+-/* added: */
+-init { run A(); run B() }
+//GO.SYSIN DD p94
+echo p95.1 1>&2
+sed 's/.//' >p95.1 <<'//GO.SYSIN DD p95.1'
+-init { printf("hello world\n") }
+//GO.SYSIN DD p95.1
+echo p95.2 1>&2
+sed 's/.//' >p95.2 <<'//GO.SYSIN DD p95.2'
+-proctype A(byte state; short set)
+-{ (state == 1) -> state = set
+-}
+-
+-init { run A(1, 3) }
+//GO.SYSIN DD p95.2
+echo p96.1 1>&2
+sed 's/.//' >p96.1 <<'//GO.SYSIN DD p96.1'
+-byte state = 1;
+-
+-proctype A() { (state == 1) -> state = state + 1 }
+-
+-proctype B() { (state == 1) -> state = state - 1 }
+-
+-init { run A(); run B() }
+//GO.SYSIN DD p96.1
+echo p96.2 1>&2
+sed 's/.//' >p96.2 <<'//GO.SYSIN DD p96.2'
+-#define true 1
+-#define false 0
+-#define Aturn 1
+-#define Bturn 0
+-
+-bool x, y, t;
+-
+-proctype A()
+-{ x = true;
+- t = Bturn;
+- (y == false || t == Aturn);
+- /* critical section */
+- x = false
+-}
+-proctype B()
+-{ y = true;
+- t = Aturn;
+- (x == false || t == Bturn);
+- /* critical section */
+- y = false
+-}
+-init { run A(); run B() }
+//GO.SYSIN DD p96.2
+echo p97.1 1>&2
+sed 's/.//' >p97.1 <<'//GO.SYSIN DD p97.1'
+-byte state = 1;
+-proctype A() { atomic { (state == 1) -> state = state + 1 } }
+-proctype B() { atomic { (state == 1) -> state = state - 1 } }
+-init { run A(); run B() }
+//GO.SYSIN DD p97.1
+echo p97.2 1>&2
+sed 's/.//' >p97.2 <<'//GO.SYSIN DD p97.2'
+-proctype nr(short pid, a, b)
+-{ int res;
+-
+-atomic { res = (a*a+b)/2*a;
+- printf("result %d: %d\n", pid, res)
+- }
+-}
+-init { run nr(1,1,1); run nr(1,2,2); run nr(1,3,2) }
+//GO.SYSIN DD p97.2
+echo p99 1>&2
+sed 's/.//' >p99 <<'//GO.SYSIN DD p99'
+-proctype A(chan q1)
+-{ chan q2;
+-
+- q1?q2;
+- q2!123
+-}
+-
+-proctype B(chan qforb)
+-{ int x;
+-
+- qforb?x;
+- printf("x = %d\n", x)
+-}
+-
+-init
+-{ chan qname[2] = [1] of { chan };
+- chan qforb = [1] of { int };
+-
+- run A(qname[0]);
+- run B(qforb);
+-
+- qname[0]!qforb
+-}
+//GO.SYSIN DD p99
--- /dev/null
+<html>\r
+<head>\r
+<title>Book Errata - The Spin Model Checker</title>\r
+</head>\r
+<h3>Typos found in the first printing (August 2003)</h3>\r
+<font face=helvetica,arial>\r
+<ul>\r
+<li>p. vi chapter 8 topic listings, Breath-First -> Breadth-First</li>\r
+<li>p. 2 line 16 "always explicitly" -> "usually"</li>\r
+<li>p. 3 figure 1.1 is mirror reversed</li>\r
+<li>p. 4 the website crashdatabas.com no longer seems to exist</li>\r
+<li>p. 20 See note (*) below, provided by Heikki Tauriainen (Feb 1, 2006).\r
+<li>p. 22 6th line from the bottom: "if" -> "of"</li>\r
+<li>p. 25 4th line from the bottom: "variable in" -> "variable cnt"</li>\r
+<li>p. 26 10th line from bottom: "set to false" -> "set to true"</li>\r
+<li>p. 27 an error slipped into Figure 2.6. The fragment\r
+<pre>\r
+ M?data -> /* receive data */\r
+ do\r
+ :: W!data /* send data */\r
+ :: W!shutup; /* or shutdown */\r
+ break\r
+ od\r
+</pre>\r
+is an unfortunate last-minute rewrite of the originally intended version:\r
+<pre>\r
+ do\r
+ :: M?data -> W!data\r
+ :: M?data -> W!shutup;\r
+ break\r
+ od\r
+</pre>\r
+The behavior is of course not equivalent.\r
+In particular, the version in the book cannot create the error scenario\r
+given on page 29, but the intended version can.</li>\r
+<li>p. 33 8th line from bottom: "the specification" -> "the specification of"</li>\r
+<li>p. 33 3rd line from bottom: "functions pointers" -> "function pointers</li>\r
+<li>p. 41 "1 <= n <= 32" -> "1 <= n < 32".</li>\r
+<li>p. 43 appel -> apple</li>\r
+<li>p. 52 11th line from the top: "p. 39." -> "p. 38."</li>\r
+<li>p. 69 bottom line: "exclusive read and exclusive write" -> "exclusive receive and exclusive send"</li>\r
+<li>p. 75 and 548 Goldstein -> Goldstine</li>\r
+<li>p. 81 and 271 pan.trail -> fair.pml.trail</li>\r
+<li>p. 81 3rd line from the bottom: "trace fpr" -> "trace for"</li>\r
+<li>p. 82 18th line from the bottom: "process than" -> "process that"</li>\r
+<li>p. 92 4th line from bottom: "Even traces" -> "Event traces"</li>\r
+<li>p. 96 middle of page identify -> identity</li>\r
+<li>p. 96 l. -6, for -> by</li>\r
+<li>p. 111 below figure 5.4: "f==free" -> "f=free"</li>\r
+<li>p. 119 12th line from the bottom; "xDm" -> "Dm"</li>\r
+<li>p. 121 figure 5.8, in captions on bottom two figures: "p" -> "q"</li>\r
+<li>p. 137 14th line from bottom (first rule in list): first 3 chars in wrong font</li>\r
+<li>p. 139 last line; italic P -> roman P</li>\r
+<li>p. 142 3rd line from bottom: "reach" -> "reached"</li>\r
+<li>p. 142 5th line from bottom: omit comma</li>\r
+<li>p. 148 middle of the page: "can be express" -> "can be expressed"</li>\r
+<li>p. 149 5th line from bottom: "eventually always" -> "always eventually"</li>\r
+<li>p. 150 replace "it is impossible for p to hold only in even steps in a run, but never at odd steps"\r
+with "it is possible for p to hold in even steps in a run, but it is not possible for p to hold in odd steps"</li>\r
+<li>p. 150 Omega-Regular Properties, line 1: "that" -> "than"</li>\r
+<li>p. 158 middle of page: redundant space after "("</li>\r
+<li>p. 168 the list of properties given for < and > is not exhaustive</li>\r
+<li>p. 174 11th line from bottom: "but" -> "by"</li>\r
+<li>p. 177 Procedure Search() in Figure 8.6 is incorrect. A corrected version is:\r
+<pre>\r
+Search()\r
+{\r
+ while (Empty_Queue(D) == false)\r
+ { s = Del_Queue(D)\r
+ for each (s,1,s') member A.T\r
+ if In_Statespace(V, s') == false\r
+ { Add_Statespace(V, s')\r
+ Add_Queue(D, s')\r
+ }\r
+ }\r
+}\r
+</pre>\r
+</li>\r
+<li>p. 178 2nd line from top: "at state" -> "at each state", "of each state" -> "of that state"</li>\r
+<li>p. 179 lead -> led</li>\r
+<li>p. 180 before first 'if' stmnt inside for loop add: if (toggle == true)</li>\r
+<li>p. 185 Fig. 8.10, circle at s^{1}_{2} should be dotted</li>\r
+<li>p. 187 line -11: "interative" -> "iterative"</li>\r
+<li>p. 188 replace "(RxB)+(k+2)" with "Rx(B+k+2)"</li>\r
+<li>p. 193 Fig. 9.2, the two circles labeled 0,1,0 should be dashed</li>\r
+<li>p. 193 7th line from bottom: "g=g*2," -> "g=g*2."</li>\r
+<li>p. 196 line -9: "control control" -> "control"</li>\r
+<li>p. 204 last line, "to 133 seconds" -> "to 53 seconds"</li>\r
+<li>p. 208 a goof: m changes from bits to bytes between 2nd and 3rd paragraph</li>\r
+<li>p. 209 in first two formulas: (1-1/m) sup {kr}.</li>\r
+<li>p. 211 3rd line from below: probabilitie -> probabilities</li>\r
+<li>p. 212 line -2: "ration" -> "ratio"</li>\r
+<li>p. 214 A formal -> Formal</li>\r
+<li>p. 216 1st-2nd line: 'collissions' -> 'collisions'</li>\r
+<li>p. 219 last paragraph: missing right parenthesis</li>\r
+<li>p. 228 Celcius -> Celsius</li>\r
+<li>p. 228 in the list at the bottom: there are just 6 entries with 'keep' as a target</li>\r
+<li>p. 237 12th line from below: "postive" -> "zero".</li>\r
+<li>p. 237 4th line from below: "unsound" -> "incomplete".</li>\r
+<li>p. 238 knifes -> knives</li>\r
+<li>p. 241 7th line from bottom: "world0" -> "world\n"</li>\r
+<li>p. 243 Pressburger -> Presburger</li>\r
+<li>p. 251 Selet -> Select</li>\r
+<li>p. 262 10th line from bottom: "do to" -> "due to"</li>\r
+<li>p. 272 an basic -> a basic</li>\r
+<li>p. 272 "As a special feature [...], if the statement" omit "if"</li>\r
+<li>p. 279 "#define q" -> "#define r"\r
+<!--\r
+<li>p. 280 (strong) -> (weak)</li>\r
+-->\r
+<li>p. 281 Automata View -> Automaton View</li>\r
+<li>p. 283 The correct wording of the quote from Willem L. van der Poel, as corrected by its author:\r
+ <pre>"There are no wrong programs, it simply is another program."</pre>\r
+ (email from the author, Feb 1, 2006).\r
+<li>p. 284 8th line from bottom: omit "blue"</li>\r
+<li>p. 287 6th line from top: omit "blue"</li>\r
+<li>p. 307 top of page: "ringtone" -> "ring tone" </li>\r
+<li>p. 307 top of page: "dialtone" -> "dial tone"</li>\r
+<li>p. 307 top of page: "notone" -> "no tone"</li>\r
+<!-- <li>p. 332 3rd line from bottom: UTS without a trademark (see also next page, 1x)</li> -->\r
+<li>p. 333 11th line from top: "and early version" -> "an early version"</li>\r
+<li>p. 338 the line numbered [19] is actually from the FIX</li>\r
+<li>p. 339 6th line from below: pid 1 -> pid 0</li>\r
+<li>p. 393 2nd line from top: "innermostn" -> "innermost"</li>\r
+<li>p. 341 10th line from top: "body ," -> "body,"</li>\r
+<li>p. 346 identificatio -> identification</li>\r
+<li>p. 346 middle of the page: "tranaction" -> "transaction"</li>\r
+<li>p. 349 55 is not the integer square root of either 1024 or 3601.</li>\r
+<li>p. 356 n=1<<30 does not fail on all systems</li>\r
+<li>p. 359 Fig. 15.8: what looks like commas are really single quotes</li>\r
+<li>p. 359 Fig. 15.8: the automaton fails to detect strings that start inside a comment;</li>\r
+unfortunate given the example that also appears on this page...</li>\r
+<li>p. 365 the grammar listing misses productions for inlines</li>\r
+<li>p. 365 [active] PROCTYPE -> [active ['[' const ']']] PROCTYPE</li>\r
+<li>p. 367 "PRINT" -> "PRINTF"</li>\r
+<li>p. 369 in PREDEFINED: "373last" -> "374"</li>\r
+<li>p. 369 in PREDEFINED: "373nr_pr" -> "376"</li>\r
+<li>p. 369 5th line from bottom: "special case" -> "special cases"</li>\r
+<li>p. 370 8th line from bottom: "p.272" -> "p. 272"</li>\r
+<li>p. 370 2nd line from bottom: "(434)" -> "(p. 434)"</li>\r
+<li>p. 370 2nd line from bottom: "(p, 483)" -> "(p. 483)"</li>\r
+<li>p. 371 2nd line from top: "Two" -> "Three"</li>\r
+<li>p. 371 9th line from top: "or both of the above two" -> "of the above" </li>\r
+<li>p. 374 11th line from bottom: "from into" -> "to"</li>\r
+<li>p. 376 5th line from bottom: "at 256" -> "at 255"</li>\r
+<li>p. 377 5th line in DESCRIPTION: "process" -> "processes"</li>\r
+<li>p. 381 4th line from bottom: "four process" -> "four processes"</li>\r
+<li>p. 381 3rd line from bottom: "to three" -> "to four"</li>\r
+<li>p. 390 9th line from bottom: "recepient" -> "recipient"</li>\r
+<li>p. 393 10th line from bottom: "label L1" -> "label L2"\r
+<li>p. 395 6th line from top: "multiple field" -> "multiple fields"</li>\r
+<li>p. 397 4th line from top: "the the" -> "the"</li>\r
+<li>p. 398 11th line from bottom: redundant space after "("</li>\r
+<li>p. 402 7th line from top, "accidentily" -> "accidentally"</li>\r
+<li>p. 404 mixed fonts in Table</li>\r
+<li>p. 404 5th line from bottom: "the fact the" -> "the fact that the"</li>\r
+<li>p. 407 in Notes, 2nd line: "tha" -> "that"</li>\r
+<li>p. 408 "(x < 0)" -> "(x <= 0)"</li>\r
+<li>p. 411 last line: "ltl len" -> "ltl, len"</li>\r
+<li>p. 425 11th line from top: "followin" -> "following"</li>\r
+<li>p. 440 11th line from top: "equivalents" -> "equivalent"</li>\r
+<li>p. 441 middle of page: "LTL formula" -> "LTL formulae"</li>\r
+<li>p. 446 10th line from top: "equivalents" -> "equivalent"</li>\r
+<li>p. 450 last example in notes should be: atomic { P && qname?[ack,var] -> qname?ack,var }</li>\r
+<li>p. 452 15th line from bottom: "will included" -> "will be included"</li>\r
+<li>p. 455 5th line from top: "restrction" -> "restriction"</li>\r
+<li>p. 456 middle of page: "type main" -> "type fact"</li>\r
+<li>p. 456 12th line from bottom: "2,147,483,648" ->"2,147,483,647"</li>\r
+<li>p. 456 10th line from bottom: 13! = 6,227,020,800 (and so even 13! > 2^31-1)</li>\r
+<li>p. 464 9th line from bottom: "just and safe" -> "justified and safe" (2x)</li>\r
+<li>p. 466 1st line in EFFECT: "to the" -> "of the" </li>\r
+<li>p. 476 in EXAMPLES (2x): "b = a" -> "b = tmp"</li>\r
+<li>p. 479 7th line from top: "can are" -> "are"</li>\r
+<li>p. 496 6th line: "in in" -> "in"</li>\r
+<li>p. 498 2nd line from bottom: "coord.trail" -> "example.trail"</li>\r
+<li>p. 509 13th line from bottom: "known the" -> "known. The"</li>\r
+<li>p. 512 middle of page: "an pointer" -> "a pointer"</li>\r
+<li>p. 518 l -8, most -> must</li>\r
+<li>p. 519 l -10, -rthis -> -r, this</li>\r
+<li>p. 521 5th line from bottom: "substitions" -> "substitutions"</li>\r
+<li>p. 528 under basic options -DBFS, "reducting" -> "reducing"</li>\r
+<li>p. 532 under -DSDUMP, replace "-DCHECK" with: "-DVERBOSE or -DDEBUG"</li>\r
+<li>p. 532 under -DSVDUMP, replace "a file named svdump" with "a file with extension .svd"</li>\r
+<li>p. 541 11th line from bottom: "-a" in wrong font</li>\r
+<li>p. 543 middle of page: "two for processes" -> "three for processes"</li>\r
+<li>p. 547 Americans would put "Dijkstra" above "Dillon" in alphabetical order. Dutchmen, though, recognize the "ij" as a single letter, and place "Dijkstra" below "Doran" as shown. Dijkstra was, of course, a Dutchman...</li>\r
+<li>p. 547 Entry for Emerson: "model logic" -> "modal logic"</li>\r
+<li>p. 553 13th line from bottom: "to represents" -> "to represent"</li>\r
+<li>p. 554 10th line from top: "product" -> "products"</li>\r
+<li>p. 561 DEADLOCK DETECTION, 1st line: "is system" -> "is a system"</li>\r
+<li>p. 561 10th line from bottom: replace "invalid endstate" with "valid endstate", and replace the subsentence after the comma with: "from which we can derive the definition of an invalid endstate, matching Spin's formalization of a system deadlock. In an invalid endstate at least one process has not reached its closing curly brace or a state marked with an endstate label."</li>\r
+<li>p. 565 4th line from top: "andq, r" -> "q and r"</li>\r
+<li>p. 566 define BDD (Binary Decision Diagram) and NP (Non-deterministic Polynomial)</li>\r
+<li>p. 572 l 8, wil -> will</li>\r
+<li>p. 575 10th line from bottom should be: spin -a -m ex2</li>\r
+<li>p. 575 9th line from bottom should be: cc -DPC -DBITSTATE -DSAFETY -o pan pan.c</li>\r
+<li>p. 577 C.9., 1st line: "an little" -> "a little"</li>\r
+<li>p. 579 5th line from top: "these tool" -> "these tools"</li>\r
+</ul>\r
+</font>\r
+<hr>\r
+Statistics:\r
+The list above contains\r
+roughly 128 reported typos and goofs in the first printing of the book.\r
+There are approximately 340K words in the book, giving 1 reported defect\r
+per 2,650 words written. At and average of 10 words per sentence, this is\r
+about 4 reported defects per 1,000 sentences in the book, which is roughly\r
+on par with a reasonably good software development process of 1-10 residual\r
+defects (<em>after</em> testing) per 1,000 lines of non-comment source code written.\r
+As in software, the number of reported defects depends both on the number of\r
+latent defects <em>and</em> on the number of users/readers\r
+(i.e., unread books will have no reported typos...).\r
+<hr>\r
+Note (*) on the example used on p. 20, provided by Heikki Tauriainen.\r
+<pre>\r
+Date: Wed, 01 Feb 2006 21:10:54 +0200 (EET) \r
+From: heikki.tauriainen [atsign] tkk [dot] fi \r
+Subject: Spin book: Doran & Thomas's mutual exclusion algorithm \r
+\r
+Dear Dr. Holzmann,\r
+\r
+Keijo Heljanko and I are giving at Helsinki University of Technology\r
+a basic course on parallel and distributed systems, using Spin for\r
+examples on model checking. To demonstrate using the tool, we\r
+considered Dekker's mutual exclusion algorithm found in your Spin\r
+book (p. 20) and the variant of the algorithm by Doran and Thomas\r
+mentioned on p. 22.\r
+\r
+According to the Spin book, Doran and Thomas's algorithm can be\r
+obtained from Dekker's algorithm by simply changing the outer do-loop\r
+of the algorithm into an if-selection, and this change is claimed to\r
+preserve the correctness of the algorithm. This doesn't, however,\r
+seem to be the case, as the verification results using the Promela\r
+models distributed in the package\r
+<http://spinroot.com/spin/Doc/Book_extras/examples.tar.gz> were\r
+somewhat unexpected (unless, of course, the models in the package are\r
+deliberately faulty). I'm referring to the file CH2/mutex.pml in the\r
+package.\r
+\r
+The Promela model uses a preprocessor directive (DORAN) to choose\r
+between the algorithm with the do-loop and the algorithm with the\r
+if-selection. Verifying the model with the do-loop indeed gives the\r
+expected result (no assertion violations). Firstly, however, Spin\r
+doesn't directly accept the model of the variant of the algorithm:\r
+\r
+$ spin -DDORAN -a mutex.pml\r
+spin: line 30 "mutex.pml", Error: misplaced break statement saw '-2'' near 'break'\r
+$\r
+\r
+After the obvious change of making the 'break' keyword at line 30\r
+apply only to the variant with the do-loop, that is, changing lines\r
+29--35 to read\r
+\r
+ :: else ->\r
+#ifdef DORAN\r
+ fi;\r
+#else\r
+ break\r
+ od;\r
+#endif\r
+\r
+and then verifying the mutual exclusion algorithm gives, however,\r
+the following (unexpected) result:\r
+\r
+$ spin -DDORAN -a mutex.pml\r
+$ gcc -o -DBFS -o pan pan.c\r
+$ ./pan\r
+pan: assertion violated (cnt==1) (at depth 9)\r
+pan: wrote mutex.pml.trail\r
+(Spin Version 4.2.6 -- 27 October 2005)\r
+Warning: Search not completed\r
+ + Using Breadth-First Search\r
+ + Partial Order Reduction\r
+\r
+Full statespace search for:\r
+ never claim - (none specified)\r
+ assertion violations +\r
+ cycle checks - (disabled by -DSAFETY)\r
+ invalid end states +\r
+\r
+State-vector 20 byte, depth reached 9, errors: 1\r
+ 56 states, stored\r
+ 56 nominal states (stored-atomic)\r
+ 32 states, matched\r
+ 88 transitions (= stored+matched)\r
+ 0 atomic steps\r
+hash conflicts: 0 (resolved)\r
+\r
+2.302 memory usage (Mbyte)\r
+\r
+$ spin -DDORAN -p -t mutex.pml\r
+Starting mutex with pid 0\r
+Starting mutex with pid 1\r
+ 1: proc 1 (mutex) line 11 "mutex.pml" (state 1) [i = _pid]\r
+ 1: proc 1 (mutex) line 12 "mutex.pml" (state 2) [j = (1-_pid)]\r
+ 2: proc 0 (mutex) line 11 "mutex.pml" (state 1) [i = _pid]\r
+ 2: proc 0 (mutex) line 12 "mutex.pml" (state 2) [j = (1-_pid)]\r
+ 3: proc 1 (mutex) line 14 "mutex.pml" (state 3) [flag[i] = 1]\r
+ 4: proc 1 (mutex) line 29 "mutex.pml" (state 12) [else]\r
+ 5: proc 1 (mutex) line 37 "mutex.pml" (state 15) [cnt = (cnt+1)]\r
+ 6: proc 0 (mutex) line 14 "mutex.pml" (state 3) [flag[i] = 1]\r
+ 7: proc 0 (mutex) line 21 "mutex.pml" (state 4) [(flag[j])]\r
+ 8: proc 0 (mutex) line 27 "mutex.pml" (state 9) [else]\r
+ 9: proc 0 (mutex) line 37 "mutex.pml" (state 15) [cnt = (cnt+1)]\r
+spin: trail ends after 9 steps\r
+#processes: 2\r
+ turn = 0\r
+ flag[0] = 1\r
+ flag[1] = 1\r
+ cnt = 2\r
+ 9: proc 1 (mutex) line 38 "mutex.pml" (state 16)\r
+ 9: proc 0 (mutex) line 38 "mutex.pml" (state 16)\r
+2 processes created\r
+$\r
+\r
+Trying to find a reason for this unexpected result, I compared the\r
+model with the algorithm in Doran and Thomas's original article [1].\r
+It appears that the model in fact differs from that algorithm\r
+(repeated below from [1], Fig. 1)\r
+\r
+Process A Process B\r
+ 1. A_needs := true; B_needs := true;\r
+ 2. if B_needs then begin if A_needs then begin\r
+ 3. if turn = 'B' then begin if turn = 'A' then begin\r
+ 4. A_needs := false; B_needs := false;\r
+ 5. wait until turn = 'A'; wait until turn = 'B';\r
+ 6. A_needs := true; B_needs := true;\r
+ 7. end; end;\r
+ 8. wait until !B_needs; wait until !A_needs;\r
+ 9. end; end;\r
+ 10. CRITICAL SECTION CRITICAL SECTION\r
+ 11. turn := 'B'; turn := 'A';\r
+ 12. A_needs := false; B_needs := false;\r
+ 13. NON-CRITICAL SECTION NON-CRITICAL SECTION\r
+\r
+In particular, the Promela model has no corresponding construct for\r
+line 8 of this algorithm, which appears to be critical to its\r
+correctness: changing the outer if-selection to read\r
+\r
+ if\r
+ :: flag[j] ->\r
+ if\r
+ :: turn == j ->\r
+ flag[i] = false;\r
+ !(turn == j);\r
+ flag[i] = true\r
+ :: else\r
+ fi;\r
+ (!flag[j]); /* needed for correctness */\r
+ :: else ->\r
+ fi;\r
+\r
+fixes the error. However, it is not sufficient to simply\r
+replace the do-loop with an if-selection, although the wording\r
+on page 22 of the Spin book can be interpreteted to suggest\r
+otherwise (at least both I and Keijo were surprised, that's why\r
+we decided to write this report).\r
+\r
+(The example file suggests that the model is taken from the book\r
+[2] instead of directly from Doran and Thomas's original article [1].\r
+As a matter of fact, this book---at least its English translation---contains the same error. This is probably also the\r
+reason why the model is faulty.)\r
+\r
+Best regards,\r
+Heikki Tauriainen\r
+\r
+\r
+References:\r
+\r
+[1] R. W. Doran and L. K. Thomas. Variants of the software solution to\r
+ mutual exclusion. Information Processing Letters 10(4--5):206--208,\r
+ 1980.\r
+\r
+[2] M. Raynal. Algorithms for mutual exclusion. North Oxford Academic\r
+ Publishers Ltd., 1986.\r
+</pre>\r
+<hr>\r
+<a href="http://spinroot.com/spin/Doc/Book_extras/index.html">book home page</a>\r
+<br>\r
+<a href="http://spinroot.com/spin/">Spin home page</a>\r
+<hr>\r
+<font size=2>Last updated: 1 February 2006</font>\r
+</html>\r
--- /dev/null
+An appendix to Chapter 6 of the book: some extra explanation on pid's
+and on temporal claims. Updated for Spin Version 2.0 - January 1995.
+
+PROCESS IDs
+
+In Spin 2.0 and later the never claim can refer to the control state
+of any process, but not to their local variables.
+This functionality is meant to be used for building correctness assertions
+with never claims. It should never be used for anything else.
+An example is
+ Receiver[pid]@place
+where `place' the name of a label within `proctype Receiver,' and
+`pid' is the value returned by the run statement that instantiated the
+copy of the Receiver proctype that we are interested in.
+
+There is a misleading suggestion in the book that says that you can
+usually guess the `pid's. Wiser is to always use the explicit value
+returned by the `run()' statement that instantiated the proces.
+Processes started with the `active' prefix obtain instantiation
+numbers starting at value 1, in the order in which they appear in the
+specification. Each process also has a local variable _pid that
+holds its own instantiation number.
+
+SPECIFYING TEMPORAL CLAIMS
+
+The body of a temporal claim is defined just like PROMELA proctype bodies.
+This means that all control flow structures, such as if-fi selections,
+do-od repetitions, and goto jumps, are allowed.
+There is, however, one important difference:
+
+ Every statement inside a temporal claim is (interpreted as) a condition.
+ A never claim should therefore never contain statements that can
+ have side-effects (assignments, communications, run-statements, etc.)
+
+Temporal claims are used to express behaviors that are considered undesirable
+or illegal. We say that a temporal claim is `matched' if the undesirable
+behavior can be realized, and thus the claim violated.
+
+The recommended use of a temporal claim is in combination with acceptance labels.
+There are two ways to `match' a temporal claim, depending on whether the
+undesirable behavior defines a terminating or a cyclic execution sequence.
+
+o A temporal claim is matched when it terminates (reaches its closing curly brace).
+ That is, the claim can be violated if the closing curly brace of the PROMELA
+ body of the claim is reachable for at least one execution sequence.
+
+o For a cyclic execution sequence, the claim is matched only when an explicit
+ acceptance cycle exists. The acceptance labels within temporal claims
+ are user defined, there are no defaults. This means that in the absence of
+ acceptance labels no cyclic behavior can be matched by a temporal claim.
+ It also means that to check a cyclic temporal claim, acceptance labels should
+ only occur within the claim and not elsewhere in the PROMELA code.
+
+
+SEMANTICS
+
+The normal system behavior of a PROMELA system is defined as the
+`asynchronous product' of the behaviors of the individual processes.
+Given an arbitrary system state, its successor states are obtained
+in two steps. In the first step all the executable (atomic) statements in the
+individual processes are identified. In the second step, each one of these
+statements is executed.
+Each single execution produces a successor state in the asynchronous product.
+The complete system behavior is thus defined recursively and
+represents all possible interleavings of the individual process behaviors.
+Call this asynchronous product machine the `global machine'.
+
+The addition of a temporal claim defines an additional `synchronous product'
+of this global machine with the state machine that defines the temporal
+claim. Call the latter machine the `claim machine', and call the new
+synchronous product the `labeled machine'.
+
+Every state in the labeled machine is a pair (p,q) with p a state in the global
+machine and q a state in the claim machine. Every transition in the labeled
+machine is similarly defined by a pair (r,s) with r a transition in the global
+machine and s a transition in the claim machine.
+In other words, every transition in the `synchronous' product is a joint move
+of the global machine and the claim machine.
+(By contrast, every transition in an `asynchronous' product would correspond
+to a single transition in either the global machine or the claim machine, thus
+interleaving transitions instead of combining them.)
+
+Since all statements in the claim machine are boolean propositions, the second
+half of the transition pair (r,s) is either true or false.
+Call all transitions where this proposition is true `matching transitions'.
+In a matching transition proposition s evaluates to true in state system state r.
+Notice that the claim machine has at least one stop state E, the state
+at the closing curly brace of the claim body.
+
+The semantics of temporal claims can now be summed up as follows.
+
+o If the labeled machine contains any sequence of matching transitions only,
+ that connects its initial state with a state (p,E) for any p, the temporal
+ claim can be matched by a terminating sequence (a correctness violation).
+
+o If the labeled machine contains any cycle of matching transitions only, that
+ passes through an acceptance state, the temporal claim can be matched by a
+ cyclic sequence.
+
+
+EXAMPLES
+
+Listed below are the equivalent PROMELA definitions for the three basic
+temporal properties defined by Zohar Manna & Amir Pnueli in
+``Tools and Rules for the Practicing Verifier'' Stanford University,
+Report STAN-CS-90-1321, July 1990, 34 pgs.
+
+The following descriptions are quoted from Manna & Pnueli:
+
+ ``There are three classes of properties we [...] believe to cover
+ the majority of properties one would ever wish to verify.''
+
+ 1. Invariance
+ ``An invariance property refers to an assertion p, and requires that p
+ is an invariant over all the computations of a program P, i.e. all
+ the states arising in a computation of P satisfy p. In temporal
+ logic notation, such properties are expressed by [] p, for a state
+ formula p.''
+
+ Corresponding Temporal Claim in PROMELA:
+ never {
+ do
+ :: p
+ :: !p -> break
+ od
+ }
+
+ 2. Response
+ ``A response property refers to two assertions p and q, and
+ requires that every p-state (a state satisfying p) arising in
+ a computation is eventually followed by a q-state.
+ In temporal logic notation this is written as p -> <> q.''
+
+ Corresponding Temporal Claim in PROMELA:
+ never {
+ do
+ :: true
+ :: p && !q -> break
+ od;
+ accept:
+ do
+ :: !q
+ od
+ }
+
+ Note that using (!p || q) instead of `skip' would check only the
+ first occurrence of p becoming true while q is false.
+ The above formalization checks for all occurrences, also future ones.
+ Strictly seen, therefore, the claim above uses a common interpretation
+ of the formula, requiring it to hold always, or: [] { p -> <> q }
+
+ 3. Precedence
+ ``A simple precedence property refers to three assertions p, q, and r.
+ It requires that any p-state initiates a q-interval (i.e. an interval
+ all of whose states satisfy q) which, either runs to the end of the
+ computation, or is terminated by an r-state.
+ Such a property is useful to express the restriction that, following
+ a certain condition, one future event will always be preceded by
+ another future event.
+ For example, it may express the property that, from the time a certain
+ input has arrived, there will be an output before the next input.
+ Note that this does not guarantee [require] that the output will actually
+ be produced. It only guarantees [requires] that the next input (if any)
+ will be preceded by an output. In temporal logic, this property is
+ expressed by p -> (q U r), using the unless operator (weak until) U.
+
+ Corresponding Temporal Claim in PROMELA:
+
+ never {
+ do
+ :: true /* to match any occurrence */
+ :: p && q && !r -> break
+ :: p && !q && !r -> goto error
+ od;
+ do
+ :: q && !r
+ :: !q && !r -> break
+ od;
+ error: skip
+ }
+
+ Strictly again, this encodes: [] { p -> (q U r) }
+ To match just the first occurence, replace 'true' with (!p || r).
--- /dev/null
+Errata for `Design and Validation of Computer Protocols'
+[trivial typos not listed]
+
+CHAPTER 2, page 26 - Example of a Shorter Error Scenario
+============================================================
+
+A duplicate message can be accepted after even a single
+transmission error occurs. E.g.:
+
+ (A) (B)
+ ~ ~
+ | |
+ | ack 'z' /-----------<---------+
+ +-----------/ |
+accept(z) | |
+ +-----------\ ack 'a' -> err |
+ | \----------->--------+
+ | |
+ | nak 'z' /-----------<--------+
+ +------------/ |
+accept(z) | |
+
+
+CHAPTER 3, page 61/62 - Revised CRC-Algorithm
+(Bits renumbered in more standard right to left order.)
+============================================================
+
+The following C program, by Don Mitchell of AT&T Bell
+Laboratories, generates a lookup table for an arbitrary
+checksum polynomial. Input for the routine is an octal
+number, specified as an argument, that encodes the generator
+polynomial.
+In the version of the program shown here, compliments of Ned
+W. Rhodes, Software Systems Group, bits are numbered from
+zero to r-1, with bit zero corresponding to the right-most
+bit, and r the degree of the generator polynomial. (In
+Mitchell's original algorithm the bits in the message and
+generator polynomial were reversed.) The r-th bit itself is
+omitted from the code word, since it is implicit in the
+length. Using this program takes two separate steps.
+First, the program is compiled and run to generate the
+lookup tables. Then the checksum generation routine can be
+compiled, with the precalculated lookup tables in place. On
+a UNIX system, the program is compiled as
+
+ $ cc -o crc_init crc_init.c
+
+Lookup tables for the two most popular CRC-polynomials can
+now be produced as follows:
+
+ $ crc_init 0100005 > crc_16.h
+ $ crc_init 010041 > crc_ccitt.h
+
+This is the text of crc_init.c:
+
+
+ main(argc, argv)
+ int argc; char *argv[];
+ {
+ unsigned long crc, poly;
+ int n, i;
+
+ sscanf(argv[1], "%lo", &poly);
+ if (poly & 0xffff0000)
+ { fprintf(stderr, "polynomial is too large\n");
+ exit(1);
+ }
+
+ printf("/*\n * CRC 0%o\n */\n", poly);
+ printf("static unsigned short crc_table[256] = {\n");
+ for (n = 0; n < 256; n++)
+ { if (n % 8 == 0) printf(" ");
+ crc = n << 8;
+ for (i = 0; i < 8; i++)
+ { if (crc & 0x8000)
+ crc = (crc << 1) ^ poly;
+ else
+ crc <<= 1;
+ crc &= 0xFFFF;
+ }
+ if (n == 255) printf("0x%04X ", crc);
+ else printf("0x%04X, ", crc);
+ if (n % 8 == 7) printf("\n");
+ }
+ exit(0);
+ }
+
+The table can now be used to generate checksums:
+
+ unsigned short
+ cksum(s, n)
+ register unsigned char *s;
+ register int n;
+ {
+ register unsigned short crc=0;
+
+ while (n-- > 0)
+ crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8);
+
+ return crc;
+ }
+
+
+CHAPTER 4, page 81 - Typo
+============================================================
+
+old< Taking the modulo M effect into account, this becomes:
+ valid(m) = ( 0 < p - m <= W ) || ( 0 < p - M - m <= W )
+
+new> Taking the modulo M effect into account (p is always
+ smaller than M), this becomes:
+ valid(m) = ( 0 < p - m <= W ) || ( 0 < p + M - m <= W )
+
+ERROR, Page 83, Figure 4.14
+===========================
+
+should not "accept:i" if (a==e) is false
+
+
+CHAPTER 5, error/typos
+===========================
+
+Page 96, bottom
+
+The mutual exclusion algorithm attributed to Dekker is
+really a simplication of Dekker's algorithm that is known
+as Peterson's algorithm.
+Dekker's original solution is modeled in Promela like this:
+
+#define true 1
+#define false 0
+#define Aturn 1
+#define Bturn 0
+
+bool x, y, t;
+
+proctype A()
+{
+ do
+ :: x = true;
+ if
+ :: y == true && t == Bturn ->
+ x = false;
+ (t == Aturn)
+ :: y == false ->
+ break
+ fi
+ od;
+
+ /* critical section */
+
+ t = Bturn;
+ x = false
+}
+
+proctype B()
+{
+ do
+ :: y = true;
+ if
+ :: x == true && t == Aturn ->
+ y = false;
+ (t == Bturn)
+ :: x == false ->
+ break
+ fi
+ od;
+
+ /* critical section */
+
+ t = Aturn;
+ y = false
+}
+
+init { run A(); run B() }
+
+===========================
+
+Page 98, last paragraph
+
+old> "If the receive operation tries to retrieve more parameters
+ than are available, the value of the extra parameters is undefined;
+ if it receives fewer than the number of parameters that was sent,
+ the extra information is lost."
+new> "It is always an error if the receive operation tries to retrieve
+ a different number of parameters than the corresponding channel
+ declaration specifies."
+
+===========================
+
+Page 99, last line of "init", middle of page:
+
+old< qname!qforb
+
+new> qname[0]!qforb
+
+Page 100, delete last line on page:
+
+old< byte name; /* delete */
+
+Page 103, in the Dijkstra example:
+
+old< chan sema = [0] of { bit };
+
+new> chan sema = [0] of { mtype };
+
+Page 108, "init" section, top of page:
+
+old< chan Ain = [2] of { byte };
+ chan Bin = [2] of { byte };
+ chan Aout = [2] of { byte };
+ chan Bout = [2] of { byte };
+
+new> chan Ain = [2] of { byte, byte };
+ chan Bin = [2] of { byte, byte };
+ chan Aout = [2] of { byte, byte };
+ chan Bout = [2] of { byte, byte };
+
+===========================
+
+Page 107, last sentence of first paragraph Section 5.12:
+
+old< discussed in Section 2.4.
+new> discussed in Section 2.3.
+
+===========================
+
+Page 110, exercise 5-3:
+
+old< Revise the two programs from Section 5.6
+new> Revise the two programs from Section 5.8
+
+
+CHAPTER 6
+
+
+TYPO, page 117
+=======================
+old< chan sema[0] of {bit};
+new> chan sema = [0] of {bit};
+
+SERIOUS OMISSION, Section 6.4, page 116-117:
+=================================================
+The treatment of formalizing system invariants in a 1-statement
+monitor process is correct only if the model does not contain
+any timeout statements.
+If it does, the statements in the model that would be executed
+after a timeout expires are not checked (since assert is always
+executable, it would always be executed before the timeout expires
+under default timeout heuristics used in spin).
+there are two possible solutions:
+
+- disable the default timeout heuristics for a fully exhaustive
+ search for all possible choices of timeouts (brute force)
+ to do this, include a single line
+ #define timeout skip
+ as the first line of your model - and nothing else has to change
+
+- use a safer formalization of the system invariant, using a never claim.
+ the simples formalization is:
+ never { do :: assert(invariant) od }
+ which checks the truth of the invariant for every reachable state,
+ independent of timeouts.
+ another way would be to use the implicit matching behavior of a never
+ claim, without an explicit assertion:
+ never
+ { do
+ :: (invariant) /* things are fine, the invariant holds */
+ :: !(invariant) -> break /* invariant fails - match */
+ od
+ }
+
+CLARIFICATION, page 118, Section 6.5
+====================================
+The validator SPIN does not enforce the second criterion
+for a proper endstate, i.e., the emptiness of all channels.
+It does enforce the revised first criterion from the bottom
+of page 118.
+
+TYPO, page 121 middle:
+=================================================
+
+old< never { do :: skip od -> P -> !Q }
+
+new> never { do :: skip :: break od -> P -> !Q }
+
+ADDED EXPLANATIONS (throughout page 121 and onw.):
+=================================================
+
+A terminating claim is matched, and the corresponding correctness
+property thereby violated, if and when the claim body terminates.
+
+A non-terminating claim is matched, and the corresponding
+correctness property violated, if and when an acceptance cycle
+is detected.
+
+SPECIFYING TEMPORAL CLAIMS
+
+The body of a temporal claim is defined just like PROMELA
+proctype bodies. This means that all control flow struc-
+tures, such as if-fi selections, do-od repetitions, and
+goto jumps, are allowed. There is, however, one important
+difference:
+
+ Every statement inside a temporal claim is (interpreted
+ as) a boolean condition.
+
+Specifically, this means that the statements inside temporal
+claims should be free of side-effects. For reference, the
+PROMELA statements with side-effects are: assignments,
+assertions, sends, receives, and printf statements.
+
+Temporal claims are used to express system
+behaviors that are considered undesirable or illegal. We
+say that a temporal claim is matched if the undesirable
+behavior can be realized, and thus our correctness claim can
+be violated. The most useful application of temporal claims
+is in combination with acceptance labels. There are then
+two ways to match a temporal claim, depending on whether the
+undesirable behavior defines terminating or cyclic execution
+sequences.
+
+ For a terminating execution sequence, a temporal claim
+ is matched only when it can terminate (reaches the
+ closing curly brace) That is, the claim can be violated
+ if the closing curly brace of the PROMELA body of the
+ claim is reachable.
+
+ For a cyclic execution sequence, the claim is matched
+ only when an explicit acceptance cycle exists. The
+ acceptance labels within temporal claims are user
+ defined, there are no defaults. This means that in the
+ absence of acceptance labels no cyclic behavior can be
+ matched by a temporal claim. It also means that to
+ check a cyclic temporal claim, acceptance labels should
+ only occur within the claim and not elsewhere in the
+ PROMELA code.
+
+ERROR, page 124, top
+=======================
+old< :: len(receiver) == 0
+
+new> :: skip /* allow any time delay */
+
+ERROR, page 125, top
+=======================
+the claim can of course always be violated (== matched),
+whether timeout is redefined or not.
+
+CHAPTER 7
+
+ERROR, page 139, bottom
+=======================
+old< Pr(Burst >= 17) = 0.08 . e ^ { -0.08 . 17 } = 0.007
+
+new> Pr(Burst >= 17) = 0.009 . e ^ { -0.009 . 17 } = 0.007
+
+ERROR, page 156, middle
+=======================
+old< flow_to_dll[n]!sync_ack,0
+new> flow_to_dll[n]!sync_ack,m
+ (and move the new line up to precede: "m=0;")
+
+old< flow_to_ses[n]!sync_ack,0
+new> flow_to_ses[n]!sync_ack,m
+
+old< To avoid circularity, the synchronization messages
+ do not carry sequence numbers.
+new> The sync_ack message echos the session number of the
+ sync message.
+
+ERROR, page 156, bottom
+=======================
+old< || (0<p-m-M && p-m-M<=W));
+new> || (0<p-m+M && p-m+M<=W));
+
+
+CHAPTER 11
+
+ERROR, page 224, algorithm "analyze()"
+======================================
+old< q = element from W;
+new> q = last element from W;
+
+further down:
+=============
+old< If states are stored in set W in first-in first-out order,
+ the algorithm performs a breadth-first search of the state space tree.
+new> If states are stored in set W in first-in last-out (i.e., stack)
+ order, the algorithm performs a depth-first search of the state space tree.
+
+further down:
+=============
+old< If states are stored in first-in last-out (i.e., stack)
+ < order, this changes into a depth-first search.
+
+new> If states are stored and removed in first-in first-out
+ order, this changes into a breadth-first search
+ (element q must be deleted upon retrieval from set W in
+ this type of algorithm).
+
+Page 227, top
+=============
+old< q = element from W;
+new> q = last element from W;
+
+Page 237, bottom
+================
+old< after removing states 4, 3, and 2 from the stack...
+new> after removing states 4, and 3 from the stack...
+
+CHAPTER 13
+
+Page 315, 2nd para in 13.9
+==========================
+The first two sentences of this paragraph are incorrect.
+At the low end, just 1 state would be stored in the hash-array,
+taking up 2 bits of storage out of N available bits; at the
+high end, all N bits would be set at the end of the run, and
+(allowing overlaps) we cannot have seen more than N states.
+This leads to a possible range of values for the hash factor
+of N/2 >= hf >= 1
+For full state space storage the hash factor is meaningless.
+
+CHAPTER 14
+
+Page 331, lines 86, 88, and 94
+==============================
+See the corrections described for CHAPTER 7, page 156.
+
+APPENDIX C
+==============================
+
+Page 387-388
+The syntax of remote referencing has changed in SPIN Version 2.0.
+Remote referencing to local variables is no longer allowed
+(page 387, 5th line from below).
+The syntax for referencing the state of another process has changed
+from (page 388, 3rd line):
+ same[2]:here
+to:
+ same[2]@here
+
+/===================================================================\
+| Final Erratum: |
+| |
+| This book is now replaced with the new, up to date description of |
+| the current version of Spin (per 9/2003): |
+| http://spinroot.com/spin/Doc/Book_extras/index.html |
+\===================================================================/
+
+=end errata=
--- /dev/null
+
+
+Answers to selected exercises from
+'Design and Validation of Computer Protocols'
+=============================================
+
+1.1
+Assuming that torches in the two groups would be
+raised and lowered simultaneously,
+the code for the first character in the first group
+could have clashed with the pre-defined start of text code.
+
+If torches are not raised simultaneously it is conceivable
+that group numbers could be paired with the wrong character numbers.
+
+A well-trained transmitter might also overestimate the receiver's
+ability to translate the codes on the fly.
+as is still true today: receiving is a more time consuming task
+than transmitting.
+
+1.2
+Assuming that a torch code is displayed for a minimum of 30 seconds,
+the torch telegraph transmits a choice of 1 out of 25 (between 4
+and 5 bits of information), giving a speed of roughly 0.15 bits/sec.
+Chappe's telegraph transmitted a choice of 1 out of 128 every 15 to 20
+seconds, giving a transmission speed of roughly 0.5 bits/sec.
+On Polybius' telegraph the 15 characters of the message
+``protocol failure'' would take 15x30 seconds or 7.5 minutes to transmit...
+(Note that a code for the space was not available.)
+On Chappe's system the 16 characters (space included) would be
+transmitted in 4 minutes, assuming that no predefined code
+was assigned to either the word `protocol' or the word `failure.'
+(As far as we know, there wasn't.)
+
+1.3
+Removing the redundancy in messages increases the chance that a
+single transmission error would make a large part of a message
+inrecognizable. It could cause a lot of extra traffic from receiver
+back to sender, asking for retransmissions, and additional transmissions
+of messages. The same tradeoff is still valid on today's communication
+channels (see Chapter 3).
+
+1.4
+The signalman at A had to make sure that not one but two
+trains would leave the tunnel, before he admitted the third.
+The two trains could reach signalman B in approximately 2 minutes.
+At 25 symbols per minute, that would allow the two signalmen
+to exchange roughly 50 characters of text.
+A could have signaled: "two trains now in tunnel - how many left?"
+for a total of 42 characters.
+Assuming that B would have answered eventually "one train left,"
+that would still leave A puzzling if B had really understood his
+message, and if so, where the second train could possibly be.
+Considering also that signalman A had been on duty for almost
+18 hours when the accident occured, it is not entirely certain
+that he could have succesfully resolved the protocol problem.
+Note that he still would have had to `invent' part of the protocol
+for resolving the problem in real-time.
+
+1.5
+Replace the message `train in tunnel' with `increment the number of
+trains in the tunnel by one.' Similarly, replace the message `tunnel
+is clear' by `decrement the number of trains in the tunnel by one.'
+The message `is tunnel clear' becomes `how many trains are in the
+tunnel?' with the possible responses spelled out with numerals 0 to 9.
+Either signalman can increment or decrement the number.
+The rule of the tunnel is invariantly that the number of trains in
+the tunnel is either zero or one, and only one signalman may transmit
+at a time. (To resolve conflicts on access to the transmission line,
+one could simply give one of the signalmen a fixed priority.)
+
+1.6
+A livelock would result. Assuming that the semaphore operators would
+quickly enough recognize the livelock, it is still an open question
+what they would (should) do to recover properly from such an occurence.
+
+1.7
+One possible scenario, observed by Jon Bentley in real life, is that
+two connections are made, and both parties are charged for the call.
+Clearly, a dream come true for the telephone companies.
+
+2.1
+Service - the simplex transfer of arbitrary messages from a designated
+sender to a designated receiver.
+
+Assumptions about the environment - sufficient visibility and small enough
+distance to make and accurate detection (and counting) of torches possible
+for both sender and receiver. There seems to be an implicit assumption of
+an error-free channel. There is also the implicit assumption that the
+receiver will always be able to keep up with the sender and will not get
+out of sync with the symbols that have to be decoded.
+
+Vocabulary - 24 characters from the Greek alphabet, plus two control messages
+(the start of text message and its acknowledgement).
+
+Encoding - each character, and each control message, is encoded into two
+numbers, both between 1 and 5.
+Since there are 26 distinct messages and only 5x5=25 distinct codes, some
+ambiguity is unavoidable.
+
+Procedure rules - minimal. Apparently there was only a single handshake
+at the start of the transmission. All other interactions (end of transmission,
+error recovery, flow control) were undefined and would have had to be
+improvised in the field. There is also no resolution of a potential conflict
+at the start of transmission (assuming that both parties could decide to
+start sending at the same time).
+
+2.2
+The procedure rules can include a poll message once per
+complete page - interrogating the printer about it's status
+(confirming that it is online and confirming that it
+is not out of paper - both conditions that can change from
+one page to the next). Note that the procedure rules must
+also guarantee that no more than one user can use the printer
+at a time.
+
+2.3
+Service - establishing a voice connection between two subscribers
+of a phone system. (Let's conveniently ignore multi-party connections,
+or faxes and modems.)
+
+Assumptions about environment - the phone system is infinitely
+available and error-free (sic).
+
+Vocabulary - off-hook, on-hook, dial 0 ... dial 9 (ignore star and sharp).
+Dial-tone, busy-tone, ring-tone. All messages listed here are control
+messages - the `data' of the protocol is encoded in the voice message.
+(For completeness then we could list `voice' as a separate message in the
+vocabulary, without attempting to formalize it's `encoding.')
+
+Encoding - lifting the receiver, lowering the receiver,
+pushing one of 10 labeled buttons.
+
+Informal procedure rules - Go off-hook, if no dial-tone is returned
+go on-hook and repeat a random amount of time later.
+If there is a dial-tone, push the sequence of buttons that identify the
+required destination to the phone system.
+If a busy-tone is returned in this interval, go on-hook, wait a random
+amount of time, and repeat from the start.
+If a ring-tone is returned - the call has been established - wait a
+random amount of time, go on-hook.
+
+Note that the random wait period after a busy signal makes it less likely
+that a `Lovers' Paradox' can be created (cf. Exercise 1.7).
+To be complete, the phone systems behavior should also be listed.
+Be warned that the latter is not a trivial exercise....
+
+2.4
+The revised version is the famous alternating bit protocol, see Chapter 4.
+
+2.5
+The receiver can then determine where a message (should) end by
+counting the number of bytes it receives, following the header.
+It does not have to scan for a pre-defined message terminator.
+
+2.6
+For isntance, a character stuffed protocol always transmits an integral
+number of bytes. A bit stuffed protocol carries slightly less overhead.
+
+2.7
+See Bertsekas and Gallager, [1987, p. 78-79].
+
+2.8
+More detailed sample assignments for software projects such as
+this one are available from the author (email to gerard@research.att.com).
+
+3.1
+The code rate is 0.2. Protection against bursts is limited
+to errors affecting maximally 2 out of the 5 bytes.
+At 56 Kbits/sec that means bursts smaller than 0.28 msec.
+
+3.3
+Does the crc need to be protected by a crc?
+
+3.4
+In many cases English sentences are redundant enough that
+forward error correction is possible. Any real conversation,
+though, contains many examples of feedback error correction
+to resolve ambiguities.
+To stick with the example - if the sentence ``the dog run'' is
+received, the original version (i.e., one or more dogs) cannot be
+determined without feedback error control.
+
+3.5
+(a) - the checksum is 6 bits wide.
+(b) - the original data is equal to the first n-6 bits of the code word
+(6 bits in this case).
+(c) - there were no transmission errors other than possible multiples
+of the generator polynomial itself.
+
+3.6
+The standard example is that of the voyager space craft near
+the planet Jupiter receiving a course adjustment from earth.
+A retransmission of the message would mean hours delay and invalidate
+the original commands.
+If the return channel has a high probability of error (e.g., a low
+power transmitter on board the spacecraft, sending out a very weak
+signal back to earth), the chances that a retransmission request would
+reach the earth intact may also be unacceptably low.
+
+3.8
+It is impossible to reduce a non-zero error rate to zero.
+The probability of error can be brought arbitrarily close to zero,
+at the expense of transmission speed, but it cannot reach zero.
+The scheme suggested would violate this principle and therefore
+should be placed in the same category as perpetuum mobiles and time-travel.
+
+3.9
+Fletcher's algorithm can be classified as a systematic block code.
+
+4.2
+The alternating bit protocol does not protect against
+duplication errors or reordering errors.
+Duplication errors persist (duplicate messages do not dissapear
+but generate duplicate acks etc, for the duration of the session.)
+Reordering can cause erroneous data to be accepted.
+
+4.5
+Too short a timeout creates duplicate messages.
+The duplicates lower the throughput for the remainder of the session.
+Too long a timeout increases idle-time and lowers the
+throughput.
+
+4.6
+Ignore duplicate acks.
+
+4.8
+See Bertsekas and Gallager [1987, pp. 28-29].
+
+4.9
+In at least one case (when the receiver is one full window of
+messages behind the sender) there is a confusion case where
+the receiver cannot tell if an incoming message is a repeat
+from W messages back, or a new message.
+
+4.10
+Store the data in buffer[n%W] where W is window size.
+
+4.11
+Use time-stamps and restrict the maximum life time of
+a message. Note however that time-stamps are just another
+flavor of sequence numbers and they would have to be selected
+from a sufficiently large window.
+For 32 bit sequence numbers one message transmission
+per micro-second would recycle the number in 71 minutes.
+For 64 bit sequence numbers, the number recycles
+at the same transmission speed in 500,000 years.
+
+4.12
+Alpha controls the rate of adaption to changes in
+network performance.
+Beta controls the allowed variance in response time.
+(It estimates the load variance of the remote host.)
+
+4.13
+Most importantly, all assumptions about the environment,
+specifically of the tranmission channel used, are missing completely.
+
+4.14
+The message could be received again and cause a duplicate acceptance.
+
+5.1
+An assignment is always executable. The variable b would be set
+to the value 0.
+
+5.2
+If the receive is executable on the first attempt to execute
+the statement, the message would be received, and the condition
+would be false (since the `executability' value of the receive is
+non-zero). The statement would block, and would be repeated.
+If the receive is (finally) non-executable, the receive fails,
+but the condition becomes true and executable.
+For all clarity: this is not valid Promela syntax. In Promela
+the rule is that the evaluation of a condition must always be
+completely side-effect free.
+
+5.3
+
+/***** Factorial - without channels *****/
+
+int par[16];
+int done[16];
+int depth;
+
+proctype fact()
+{ int r, n, m;
+
+ m = depth;
+ n = par[m];
+ if
+ :: (n <= 1) -> r = 1
+ :: (n >= 2) ->
+ depth = m + 1;
+ par[m+1] = n-1;
+ run fact();
+ done[m+1];
+ r = par[m+1];
+ depth = m;
+ r = r*n
+ fi;
+ par[m] = r;
+ printf("Value: %d\n", par[m]);
+ done[m] = 1
+}
+
+init
+{ depth = 0;
+ par[0] = 12;
+ run fact();
+ done[0];
+ printf("value: %d\n", par[0])
+ /* factorial of 12: 12*11*10....*2*1 = 479001600 */
+}
+
+/***** Ackermann's function *****/
+
+short ans[100];
+short done[100]; /* synchronization */
+
+proctype ack(short a, b, c)
+{
+ if
+ :: (a == 0) ->
+ ans[c] = b+1
+ :: (a != 0) ->
+ done[c+1] = 0;
+ if
+ :: (b == 0) ->
+ run ack(a-1, 1, c+1)
+ :: (b != 0) ->
+ run ack(a, b-1, c+1);
+ done[c+1]; /* wait */
+ done[c+1] = 0;
+ run ack(a-1, ans[c+1], c+1)
+ fi;
+ done[c+1]; /* wait */
+ ans[c] = ans[c+1]
+ fi;
+ done[c] = 1
+}
+init
+{
+ run ack(3, 3, 0);
+ done[0]; /* wait */
+ printf("ack(3,3) = %d\n", ans[0])
+}
+
+5.10
+
+Here, as an inspiration, is another sort program, performing
+a tree-sort.
+
+/**** Tree sorter ****/
+
+mtype = { data, eof };
+
+proctype seed(chan in)
+{ byte num, nr;
+
+ do
+ :: (num < 250) -> num = num + 5
+ :: (num > 3) -> num = num - 3
+ :: in!data,num
+ :: (num > 200) -> in!eof,0 -> break
+ od
+}
+
+init {
+ chan in[1] of { byte, byte };
+ chan rgt [1] of { byte, byte };
+ byte n;
+
+ run seed(in);
+ in?data,n;
+ run node(n, rgt, in);
+ do
+ :: rgt?eof,0 -> printf("\n"); break
+ :: rgt?data,n -> printf("%d, ", n)
+ od
+
+}
+
+proctype node(byte hold; chan up, down)
+{ chan lft [1] of { byte, byte };
+ chan rgt [1] of { byte, byte };
+ chan ret [1] of { byte, byte };
+ byte n; bit havergt, havelft;
+
+ do
+ :: down?data,n ->
+ if
+ :: (n > hold) ->
+ if
+ :: ( havelft) -> lft!data,n
+ :: (!havelft) -> havelft=1;
+ run node(n, ret, lft)
+ fi
+ :: (n <= hold) ->
+ if
+ :: ( havergt) -> rgt!data,n
+ :: (!havergt) -> havergt=1;
+ run node(n, ret, rgt)
+ fi
+ fi
+ :: down?eof,0 -> break
+ od;
+ if
+ :: (!havergt) -> skip
+ :: ( havergt) -> rgt!eof,0;
+ do
+ :: ret?data,n -> up!data,n
+ :: ret?eof,0 -> break
+ od
+ fi;
+ up!data,hold;
+ if
+ :: (!havelft) -> skip
+ :: ( havelft) -> lft!eof,0;
+ do
+ :: ret?data,n -> up!data,n
+ :: ret?eof,0 -> break
+ od
+ fi;
+ up!eof,0
+}
+
+5.13
+Promela is a validation modeling language, not an implementation language.
+Why does a civil engineer not use steel beams in wooden bridge models?
+
+6.1
+The assertion can be placed inside the critical section.
+The simplest way is as follows (rewritten with some features
+from the more recent versions of Spin):
+
+ mtype = { p, v }
+
+ chan sema[0] of { mtype };
+ byte cnt;
+
+ active proctype dijkstra() /* 1 semaphore process */
+ { do
+ :: sema!p -> sema?v
+ od
+ }
+ active [3] proctype user() /* 3 user processes */
+ {
+ sema?p;
+ cnt++;
+ assert (cnt == 0 || cnt == 1); /* critical section */
+ cnt--;
+ sem!v
+ skip /* non-critical section */
+ }
+
+6.2
+To check the truth of the invariant
+for every reachable state, one can write simply:
+
+ never { do :: assert(invariant) od }
+
+Or to match an invalid behavior by reaching the
+end of the never claim, without assertions:
+
+ never
+ { do
+ :: (invariant) /* things are fine, the invariant holds */
+ :: !(invariant) -> break /* invariant fails - match */
+ od
+ }
+
+Note that semi-colons (or arrows) in never claims match system transitions,
+(i.e., each transition in the system must be matched by a move in the
+never claim; the claim does not move independently).
+
+6.5
+Using accept labels, for instance:
+
+ proctype A()
+ { do
+ :: x = true;
+ t = Bturn;
+ (y == false || t == Aturn);
+ ain = true;
+ CS: skip; /* the critical section */
+ ain = false;
+ x = false
+ od
+ }
+ ... and simularly for proctype B()
+
+ never {
+ do
+ :: skip /* allow arbitrary initial execution */
+ :: !A[1]@CS -> goto accept1
+ :: !B[2]@CS -> goto accept2
+ od;
+ accept1:
+ do :: !A[1]@CS od /* process 1 denied access forever */
+ accept2:
+ do :: !B[2]@CS od /* process 2 denied access forever */
+ }
+
+
+6.6.a
+ never {
+ do
+ :: skip /* after an arbitrary number of steps */
+ :: p -> break
+ od;
+ accept:
+ do
+ :: p /* can only match if p remains true forever */
+ od
+ }
+
+6.6.b
+For instance:
+ never {
+ do
+ :: assert(q || p) /* "!q implies p" */
+ od
+ }
+
+6.6.c
+ never { /* <> (p U q) */
+ do
+ :: skip /* after an arbitrary number of steps */
+ :: p -> break /* eventually */
+ od;
+ do
+ :: p /* p remains true */
+ :: q -> break /* at least until q becomes true */
+ od
+ /* invalid behavior is matched if we get here */
+ }
+
+The translation has been automated, and is standard in Spin version 2.7
+and later (Spin option -f).
+
+6.7
+A research problem -- there are no easy answers.
+
+6.8
+ assert(0)
+is an immediate violation in both finite or infinite execution sequences,
+and is a safety property.
+
+ accept: do :: skip od
+
+is only a violation for an infinite execution sequence, and is a liveness
+property (i.e., requires a nested depth-first search for acceptance
+cycles). The safety property can be proven more effieciently.
+Other than this, the two properties are equivalent;
+
+7.1
+Layers 3 to 5 and layer 7.
+
+7.2
+At the sender: first checksumming, then byte stuffing and framing.
+At the receiver: first unstuffing and de-framing, then checksum
+verification.
+
+7.3
+Rate control is placed at the layer that handles the units it
+refers too (for bit rates, it is the physical layer - for packets
+it would be the layer that produces packets, etc.).
+Dynamic flow control belongs in the flow control module.
+
+7.13
+The one-bit solution will no longer work.
+
+8.1
+The dash is used as a don't care symbol - any valid symbol
+could replace it without changing the validity of the specification.
+The epsilon is a null-element, i.e. it represents the absence
+of a symbol (the empty set).
+
+8.7
+No, the run and chan operators are defined to be unexecutable
+when an (implementation dependent) bound is reached.
+
+9.2
+No.
+
+9.5
+More states, up to a predefined bound only. Fewer states, yes.
+
+9.6
+No, the IUT could be arbitrarily large.
+
+9.8
+It can no longer detect transfer errors.
+
+10.2
+The computational complexity will make an interactive
+solution impossible for all but the simplest
+applications.
+
+11.3
+Missing from the program text is that variable j is
+initialized to 1 minus the value of i.
+
+12.1
+Note that the number of states to be searched by a validator on
+such a protocol would multiply by the range of the time count...
+
+13.1
+If done right, the changes will be minimal (say 10 to 20 lines
+of source).
+The memory requirements will very quickly make validations
+effectively impossible.
--- /dev/null
+# To unbundle, sh this file
+echo README 1>&2
+sed 's/.//' >README <<'//GO.SYSIN DD README'
+-Readme
+-------
+-The files in this set contain the text of examples
+-used in the Design and Validation of Computer
+-Protocols book. The name of each file corresponds to the
+-page number in the book where the example appears in its
+-most useful version. The overview below gives a short
+-descriptive phrase for each file.
+-
+-Description Page Nr = Filename
+------------ ------------------
+-hello world = p95.1
+-tiny examples = p94 p95.2 p96.1 p97.1 p97.2 p101 p102 p104.1
+-useful demos = p99 p104.2 p105.2 p116 p248
+-mutual excl. = p96.2 p105.1 p117 p320
+-Lynch's prot. = p107 p312
+-alternatin bit = p123
+-chappe's prot. = p319
+-
+-Large runs
+-----------
+-ackerman's fct = p108 # read info at start of the file
+-
+-Pftp Protocol
+--------------
+-upper tester = p325.test # not runnable
+-flow control l. = p329 p330
+-session layer = p337.pftp.ses p342.pftp.ses1 p347.pftp.ses5
+-all pftp = App.F.pftp - plus 8 include files
+-
+-See also the single file version of pftp in: Test/pftp
+-
+-General
+--------
+-Use these examples for inspiration, and to get quickly
+-acquainted with the language and the Spin software.
+-All examples - except p123 - can be used with both version
+-1 and version 2 of SPIN. (p123 was modifed for versoin 2.0
+-to use the new syntax for remote referencing).
+-If you repeat the runs that are listed in the book for
+-these examples, you should expect to get roughly the same
+-numbers in the result - although in some cases there may
+-be small differences that are due to changes in bookkeeping.
+-
+-For instance, for p329, the book (Spin V1.0) says
+-on pg. 332, using a BITSTATE run, that there are:
+- 90845 + 317134 + 182425 states (stored + linked + matched)
+-Spin V2.0 reports the numbers:
+- 90837 + 317122 + 182421 states (stored + atomic + matched)
+-and when compiled for partial order reduction (-DREDUCE):
+- 74016 + 203616 + 104008 states (stored + atomic + matched)
+-
+-If you repeat a BITSTATE run, of course, by the nature of the
+-machine dependent effect of hashing, you may get different
+-coverage and hash-factors for larger runs. The implementation
+-of the hash functions has also been improved in version 2.0,
+-so the numbers you see will likely differ. The numbers, though,
+-should still be in the same range as those reported in the book.
+-
+-The last set of file (prefixed App.F) is included for completeness.
+-As explained in the book: don't expect to be able to do an
+-exhaustive verification for this specification as listed.
+-In chapter 14 it is illustrated how the spec can be broken up
+-into smaller portions that are more easily verified.
+-
+-Some Small Experiments
+------------------------
+-Try:
+- spin p95.1 # small simulation run
+-
+- spin -s p108 # bigger simulation run, track send stmnts
+-
+- spin -a p312 # lynch's protocol - generate verifier
+- cc -o pan pan.c # compile it for exhaustive verification
+- pan # prove correctness of assertions etc.
+- spin -t -r -s p312 # display the error trace
+-
+-now edit p312 to change all three channel declarations in init
+-to look like: ``chan AtoB = [1] of { mtype byte }''
+-and repeat the above four steps.
+-note the improvement in the trace.
+-
+- spin -a p123 # alternating bit protocol - generate verifier
+- cc -o pan pan.c # compile it for exhaustive verification
+- pan -a # check violations of the never claim
+- spin -t -r -s p123 # display the error trace
+-
+-for more intuitive use of all the above options: try using the
+-graphical interface xspin, and repeat the experiments.
+//GO.SYSIN DD README
+echo App.F.datalink 1>&2
+sed 's/.//' >App.F.datalink <<'//GO.SYSIN DD App.F.datalink'
+-/*
+- * Datalink Layer Validation Model
+- */
+-
+-proctype data_link()
+-{ byte type, seq;
+-
+-end: do
+- :: flow_to_dll[0]?type,seq ->
+- if
+- :: dll_to_flow[1]!type,seq
+- :: skip /* lose message */
+- fi
+- :: flow_to_dll[1]?type,seq ->
+- if
+- :: dll_to_flow[0]!type,seq
+- :: skip /* lose message */
+- fi
+- od
+-}
+//GO.SYSIN DD App.F.datalink
+echo App.F.defines 1>&2
+sed 's/.//' >App.F.defines <<'//GO.SYSIN DD App.F.defines'
+-/*
+- * Global Definitions
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan use_to_pres[2] = [QSZ] of { byte };
+-chan pres_to_use[2] = [QSZ] of { byte };
+-chan pres_to_ses[2] = [QSZ] of { byte };
+-chan ses_to_pres[2] = [QSZ] of { byte, byte };
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2] = [QSZ] of { byte, byte };
+-chan ses_to_fsrv[2] = [0] of { byte };
+-chan fsrv_to_ses[2] = [0] of { byte };
+//GO.SYSIN DD App.F.defines
+echo App.F.flow_cl 1>&2
+sed 's/.//' >App.F.flow_cl <<'//GO.SYSIN DD App.F.flow_cl'
+-/*
+- * Flow Control Layer Validation Model
+- */
+-
+-#define true 1
+-#define false 0
+-
+-#define M 4 /* range sequence numbers */
+-#define W 2 /* window size: M/2 */
+-
+-proctype fc(bit n)
+-{ bool busy[M]; /* outstanding messages */
+- byte q; /* seq# oldest unacked msg */
+- byte m; /* seq# last msg received */
+- byte s; /* seq# next msg to send */
+- byte window; /* nr of outstanding msgs */
+- byte type; /* msg type */
+- bit received[M]; /* receiver housekeeping */
+- bit x; /* scratch variable */
+- byte p; /* seq# of last msg acked */
+- byte I_buf[M], O_buf[M]; /* message buffers */
+-
+- /* sender part */
+-end: do
+- :: atomic {
+- (window < W && len(ses_to_flow[n]) > 0
+- && len(flow_to_dll[n]) < QSZ) ->
+- ses_to_flow[n]?type,x;
+- window = window + 1;
+- busy[s] = true;
+- O_buf[s] = type;
+- flow_to_dll[n]!type,s;
+- if
+- :: (type != sync) ->
+- s = (s+1)%M
+- :: (type == sync) ->
+- window = 0;
+- s = M;
+- do
+- :: (s > 0) ->
+- s = s-1;
+- busy[s] = false
+- :: (s == 0) ->
+- break
+- od
+- fi
+- }
+- :: atomic {
+- (window > 0 && busy[q] == false) ->
+- window = window - 1;
+- q = (q+1)%M
+- }
+-#if DUPS
+- :: atomic {
+- (len(flow_to_dll[n]) < QSZ
+- && window > 0 && busy[q] == true) ->
+- flow_to_dll[n]! O_buf[q],q
+- }
+-#endif
+- :: atomic {
+- (timeout && len(flow_to_dll[n]) < QSZ
+- && window > 0 && busy[q] == true) ->
+- flow_to_dll[n]! O_buf[q],q
+- }
+-
+- /* receiver part */
+-#if LOSS
+- :: dll_to_flow[n]?type,m /* lose any message */
+-#endif
+- :: dll_to_flow[n]?type,m ->
+- if
+- :: atomic {
+- (type == ack) ->
+- busy[m] = false
+- }
+- :: atomic {
+- (type == sync) ->
+- flow_to_dll[n]!sync_ack,m;
+- m = 0;
+- do
+- :: (m < M) ->
+- received[m] = 0;
+- m = m+1
+- :: (m == M) ->
+- break
+- od
+- }
+- :: (type == sync_ack) ->
+- flow_to_ses[n]!sync_ack,m
+- :: (type != ack && type != sync && type != sync_ack)->
+- if
+- :: atomic {
+- (received[m] == true) ->
+- x = ((0<p-m && p-m<=W)
+- || (0<p-m+M && p-m+M<=W)) };
+- if
+- :: (x) -> flow_to_dll[n]!ack,m
+- :: (!x) /* else skip */
+- fi
+- :: atomic {
+- (received[m] == false) ->
+- I_buf[m] = type;
+- received[m] = true;
+- received[(m-W+M)%M] = false
+- }
+- fi
+- fi
+- :: (received[p] == true && len(flow_to_ses[n])<QSZ
+- && len(flow_to_dll[n])<QSZ) ->
+- flow_to_ses[n]!I_buf[p],0;
+- flow_to_dll[n]!ack,p;
+- p = (p+1)%M
+- od
+-}
+//GO.SYSIN DD App.F.flow_cl
+echo App.F.fserver 1>&2
+sed 's/.//' >App.F.fserver <<'//GO.SYSIN DD App.F.fserver'
+-/*
+- * File Server Validation Model
+- */
+-
+-proctype fserver(bit n)
+-{
+-end: do
+- :: ses_to_fsrv[n]?create -> /* incoming */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: ses_to_fsrv[n]?data
+- :: ses_to_fsrv[n]?eof -> break
+- :: ses_to_fsrv[n]?close -> break
+- od
+- fi
+- :: ses_to_fsrv[n]?open -> /* outgoing */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: fsrv_to_ses[n]!data -> progress: skip
+- :: ses_to_fsrv[n]?close -> break
+- :: fsrv_to_ses[n]!eof -> break
+- od
+- fi
+- od
+-}
+//GO.SYSIN DD App.F.fserver
+echo App.F.pftp 1>&2
+sed 's/.//' >App.F.pftp <<'//GO.SYSIN DD App.F.pftp'
+-/*
+- * PROMELA Validation Model - startup script
+- */
+-
+-#include "App.F.defines"
+-#include "App.F.user"
+-#include "App.F.present"
+-#include "App.F.session"
+-#include "App.F.fserver"
+-#include "App.F.flow_cl"
+-#include "App.F.datalink"
+-
+-init
+-{ atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- run fc(0); run fc(1);
+- run data_link()
+- }
+-}
+//GO.SYSIN DD App.F.pftp
+echo App.F.present 1>&2
+sed 's/.//' >App.F.present <<'//GO.SYSIN DD App.F.present'
+-/*
+- * Presentation Layer Validation Model
+- */
+-
+-proctype present(bit n)
+-{ byte status, uabort;
+-
+-endIDLE:
+- do
+- :: use_to_pres[n]?transfer ->
+- uabort = 0;
+- break
+- :: use_to_pres[n]?abort ->
+- skip
+- od;
+-
+-TRANSFER:
+- pres_to_ses[n]!transfer;
+- do
+- :: use_to_pres[n]?abort ->
+- if
+- :: (!uabort) ->
+- uabort = 1;
+- pres_to_ses[n]!abort
+- :: (uabort) ->
+- assert(1+1!=2)
+- fi
+- :: ses_to_pres[n]?accept,0 ->
+- goto DONE
+- :: ses_to_pres[n]?reject(status) ->
+- if
+- :: (status == FATAL || uabort) ->
+- goto FAIL
+- :: (status == NON_FATAL && !uabort) ->
+-progress: goto TRANSFER
+- fi
+- od;
+-DONE:
+- pres_to_use[n]!accept;
+- goto endIDLE;
+-FAIL:
+- pres_to_use[n]!reject;
+- goto endIDLE
+-}
+//GO.SYSIN DD App.F.present
+echo App.F.session 1>&2
+sed 's/.//' >App.F.session <<'//GO.SYSIN DD App.F.session'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto DATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto DATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-DATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+- ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-DATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control */
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD App.F.session
+echo App.F.user 1>&2
+sed 's/.//' >App.F.user <<'//GO.SYSIN DD App.F.user'
+-/*
+- * User Layer Validation Model
+- */
+-
+-proctype userprc(bit n)
+-{
+- use_to_pres[n]!transfer;
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- :: use_to_pres[n]!abort -> goto Aborted
+- fi;
+-Aborted:
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- fi;
+-Done:
+- skip
+-}
+//GO.SYSIN DD App.F.user
+echo p101 1>&2
+sed 's/.//' >p101 <<'//GO.SYSIN DD p101'
+-#define msgtype 33
+-
+-chan name = [0] of { byte, byte };
+-
+-/* byte name; typo - this line shouldn't have been here */
+-
+-proctype A()
+-{ name!msgtype(124);
+- name!msgtype(121)
+-}
+-proctype B()
+-{ byte state;
+- name?msgtype(state)
+-}
+-init
+-{ atomic { run A(); run B() }
+-}
+//GO.SYSIN DD p101
+echo p102 1>&2
+sed 's/.//' >p102 <<'//GO.SYSIN DD p102'
+-#define a 1
+-#define b 2
+-
+-chan ch = [1] of { byte };
+-
+-proctype A() { ch!a }
+-proctype B() { ch!b }
+-proctype C()
+-{ if
+- :: ch?a
+- :: ch?b
+- fi
+-}
+-init { atomic { run A(); run B(); run C() } }
+//GO.SYSIN DD p102
+echo p104.1 1>&2
+sed 's/.//' >p104.1 <<'//GO.SYSIN DD p104.1'
+-#define N 128
+-#define size 16
+-
+-chan in = [size] of { short };
+-chan large = [size] of { short };
+-chan small = [size] of { short };
+-
+-proctype split()
+-{ short cargo;
+-
+- do
+- :: in?cargo ->
+- if
+- :: (cargo >= N) -> large!cargo
+- :: (cargo < N) -> small!cargo
+- fi
+- od
+-}
+-init { run split() }
+//GO.SYSIN DD p104.1
+echo p104.2 1>&2
+sed 's/.//' >p104.2 <<'//GO.SYSIN DD p104.2'
+-#define N 128
+-#define size 16
+-
+-chan in = [size] of { short };
+-chan large = [size] of { short };
+-chan small = [size] of { short };
+-
+-proctype split()
+-{ short cargo;
+-
+- do
+- :: in?cargo ->
+- if
+- :: (cargo >= N) -> large!cargo
+- :: (cargo < N) -> small!cargo
+- fi
+- od
+-}
+-proctype merge()
+-{ short cargo;
+-
+- do
+- :: if
+- :: large?cargo
+- :: small?cargo
+- fi;
+- in!cargo
+- od
+-}
+-init
+-{ in!345; in!12; in!6777; in!32; in!0;
+- run split(); run merge()
+-}
+//GO.SYSIN DD p104.2
+echo p105.1 1>&2
+sed 's/.//' >p105.1 <<'//GO.SYSIN DD p105.1'
+-#define p 0
+-#define v 1
+-
+-chan sema = [0] of { bit };
+-
+-proctype dijkstra()
+-{ do
+- :: sema!p -> sema?v
+- od
+-}
+-proctype user()
+-{ sema?p;
+- /* critical section */
+- sema!v
+- /* non-critical section */
+-}
+-init
+-{ atomic {
+- run dijkstra();
+- run user(); run user(); run user()
+- }
+-}
+//GO.SYSIN DD p105.1
+echo p105.2 1>&2
+sed 's/.//' >p105.2 <<'//GO.SYSIN DD p105.2'
+-proctype fact(int n; chan p)
+-{ int result;
+-
+- if
+- :: (n <= 1) -> p!1
+- :: (n >= 2) ->
+- chan child = [1] of { int };
+- run fact(n-1, child);
+- child?result;
+- p!n*result
+- fi
+-}
+-init
+-{ int result;
+- chan child = [1] of { int };
+-
+- run fact(7, child);
+- child?result;
+- printf("result: %d\n", result)
+-}
+//GO.SYSIN DD p105.2
+echo p107 1>&2
+sed 's/.//' >p107 <<'//GO.SYSIN DD p107'
+-mtype = { ack, nak, err, next, accept }
+-
+-proctype transfer(chan in, out, chin, chout)
+-{ byte o, i;
+-
+- in?next(o);
+- do
+- :: chin?nak(i) -> out!accept(i); chout!ack(o)
+- :: chin?ack(i) -> out!accept(i); in?next(o); chout!ack(o)
+- :: chin?err(i) -> chout!nak(o)
+- od
+-}
+-init
+-{ chan AtoB = [1] of { byte, byte };
+- chan BtoA = [1] of { byte, byte };
+- chan Ain = [2] of { byte, byte };
+- chan Bin = [2] of { byte, byte };
+- chan Aout = [2] of { byte, byte };
+- chan Bout = [2] of { byte, byte };
+-
+- atomic {
+- run transfer(Ain, Aout, AtoB, BtoA);
+- run transfer(Bin, Bout, BtoA, AtoB)
+- };
+- AtoB!err(0)
+-}
+//GO.SYSIN DD p107
+echo p108 1>&2
+sed 's/.//' >p108 <<'//GO.SYSIN DD p108'
+-/***** Ackermann's function *****/
+-
+-/* a good example where a simulation run is the
+- better choice - and verification is overkill.
+-
+- 1. simulation
+- -> straight simulation (spin p108) takes
+- -> approx. 6.4 sec on an SGI R3000
+- -> prints the answer: ack(3,3) = 61
+- -> after creating 2433 processes
+-
+- note: all process invocations can, at least in one
+- feasible execution scenario, overlap - if each
+- process chooses to hang around indefinitely in
+- its dying state, at the closing curly brace.
+- this means that the maximum state vector `could' grow
+- to hold all 2433 processes or about 2433*12 bytes of data.
+- the assert(0) at the end makes sure though that the run
+- stops the first time we complete an execution sequence
+- that computes the answer, so the following suffices:
+-
+- 2. verification
+- -> spin -a p108
+- -> cc -DVECTORSZ=2048 -o pan pan.c
+- -> pan -m15000
+- -> which completes in about 5 sec
+- */
+-
+-proctype ack(short a, b; chan ch1)
+-{ chan ch2 = [1] of { short };
+- short ans;
+-
+- if
+- :: (a == 0) ->
+- ans = b+1
+- :: (a != 0) ->
+- if
+- :: (b == 0) ->
+- run ack(a-1, 1, ch2)
+- :: (b != 0) ->
+- run ack(a, b-1, ch2);
+- ch2?ans;
+- run ack(a-1, ans, ch2)
+- fi;
+- ch2?ans
+- fi;
+- ch1!ans
+-}
+-init
+-{ chan ch = [1] of { short };
+- short ans;
+-
+- run ack(3, 3, ch);
+- ch?ans;
+- printf("ack(3,3) = %d\n", ans);
+- assert(0) /* a forced stop, (Chapter 6) */
+-}
+//GO.SYSIN DD p108
+echo p116 1>&2
+sed 's/.//' >p116 <<'//GO.SYSIN DD p116'
+-byte state = 1;
+-
+-proctype A()
+-{ (state == 1) -> state = state + 1;
+- assert(state == 2)
+-}
+-proctype B()
+-{ (state == 1) -> state = state - 1;
+- assert(state == 0)
+-}
+-init { run A(); run B() }
+//GO.SYSIN DD p116
+echo p117 1>&2
+sed 's/.//' >p117 <<'//GO.SYSIN DD p117'
+-#define p 0
+-#define v 1
+-
+-chan sema = [0] of { bit }; /* typo in original `=' was missing */
+-
+-proctype dijkstra()
+-{ do
+- :: sema!p -> sema?v
+- od
+-}
+-byte count;
+-
+-proctype user()
+-{ sema?p;
+- count = count+1;
+- skip; /* critical section */
+- count = count-1;
+- sema!v;
+- skip /* non-critical section */
+-}
+-proctype monitor() { assert(count == 0 || count == 1) }
+-init
+-{ atomic {
+- run dijkstra(); run monitor();
+- run user(); run user(); run user()
+- }
+-}
+//GO.SYSIN DD p117
+echo p123 1>&2
+sed 's/.//' >p123 <<'//GO.SYSIN DD p123'
+-/* alternating bit - version with message loss */
+-
+-#define MAX 3
+-
+-mtype = { msg0, msg1, ack0, ack1 };
+-
+-chan sender =[1] of { byte };
+-chan receiver=[1] of { byte };
+-
+-proctype Sender()
+-{ byte any;
+-again:
+- do
+- :: receiver!msg1;
+- if
+- :: sender?ack1 -> break
+- :: sender?any /* lost */
+- :: timeout /* retransmit */
+- fi
+- od;
+- do
+- :: receiver!msg0;
+- if
+- :: sender?ack0 -> break
+- :: sender?any /* lost */
+- :: timeout /* retransmit */
+- fi
+- od;
+- goto again
+-}
+-
+-proctype Receiver()
+-{ byte any;
+-again:
+- do
+- :: receiver?msg1 -> sender!ack1; break
+- :: receiver?msg0 -> sender!ack0
+- :: receiver?any /* lost */
+- od;
+-P0:
+- do
+- :: receiver?msg0 -> sender!ack0; break
+- :: receiver?msg1 -> sender!ack1
+- :: receiver?any /* lost */
+- od;
+-P1:
+- goto again
+-}
+-
+-init { atomic { run Sender(); run Receiver() } }
+-
+-never {
+- do
+- :: skip /* allow any time delay */
+- :: receiver?[msg0] -> goto accept0
+- :: receiver?[msg1] -> goto accept1
+- od;
+-accept0:
+- do
+- :: !Receiver[2]@P0 /* n.b. new syntax of remote reference */
+- od;
+-accept1:
+- do
+- :: !Receiver[2]@P1
+- od
+-}
+//GO.SYSIN DD p123
+echo p248 1>&2
+sed 's/.//' >p248 <<'//GO.SYSIN DD p248'
+-proctype fact(int n; chan p)
+-{ int result;
+-
+- if
+- :: (n <= 1) -> p!1
+- :: (n >= 2) ->
+- chan child = [1] of { int };
+- run fact(n-1, child);
+- child?result;
+- p!n*result
+- fi
+-}
+-init
+-{ int result;
+- chan child = [1] of { int };
+-
+- run fact(12, child);
+- child?result;
+- printf("result: %d\n", result)
+-}
+//GO.SYSIN DD p248
+echo p312 1>&2
+sed 's/.//' >p312 <<'//GO.SYSIN DD p312'
+-#define MIN 9 /* first data message to send */
+-#define MAX 12 /* last data message to send */
+-#define FILL 99 /* filler message */
+-
+-mtype = { ack, nak, err }
+-
+-proctype transfer(chan chin, chout)
+-{ byte o, i, last_i=MIN;
+-
+- o = MIN+1;
+- do
+- :: chin?nak(i) ->
+- assert(i == last_i+1);
+- chout!ack(o)
+- :: chin?ack(i) ->
+- if
+- :: (o < MAX) -> o = o+1 /* next */
+- :: (o >= MAX) -> o = FILL /* done */
+- fi;
+- chout!ack(o)
+- :: chin?err(i) ->
+- chout!nak(o)
+- od
+-}
+-
+-proctype channel(chan in, out)
+-{ byte md, mt;
+- do
+- :: in?mt,md ->
+- if
+- :: out!mt,md
+- :: out!err,0
+- fi
+- od
+-}
+-
+-init
+-{ chan AtoB = [1] of { byte, byte };
+- chan BtoC = [1] of { byte, byte };
+- chan CtoA = [1] of { byte, byte };
+- atomic {
+- run transfer(AtoB, BtoC);
+- run channel(BtoC, CtoA);
+- run transfer(CtoA, AtoB)
+- };
+- AtoB!err,0 /* start */
+-}
+//GO.SYSIN DD p312
+echo p319 1>&2
+sed 's/.//' >p319 <<'//GO.SYSIN DD p319'
+-#define true 1
+-#define false 0
+-
+-bool busy[3];
+-
+-chan up[3] = [1] of { byte };
+-chan down[3] = [1] of { byte };
+-
+-mtype = { start, attention, data, stop }
+-
+-proctype station(byte id; chan in, out)
+-{ do
+- :: in?start ->
+- atomic { !busy[id] -> busy[id] = true };
+- out!attention;
+- do
+- :: in?data -> out!data
+- :: in?stop -> break
+- od;
+- out!stop;
+- busy[id] = false
+- :: atomic { !busy[id] -> busy[id] = true };
+- out!start;
+- in?attention;
+- do
+- :: out!data -> in?data
+- :: out!stop -> break
+- od;
+- in?stop;
+- busy[id] = false
+- od
+-}
+-
+-init {
+- atomic {
+- run station(0, up[2], down[2]);
+- run station(1, up[0], down[0]);
+- run station(2, up[1], down[1]);
+-
+- run station(0, down[0], up[0]);
+- run station(1, down[1], up[1]);
+- run station(2, down[2], up[2])
+- }
+-}
+//GO.SYSIN DD p319
+echo p320 1>&2
+sed 's/.//' >p320 <<'//GO.SYSIN DD p320'
+-#define true 1
+-#define false 0
+-#define Aturn false
+-#define Bturn true
+-
+-bool x, y, t;
+-bool ain, bin;
+-
+-proctype A()
+-{ x = true;
+- t = Bturn;
+- (y == false || t == Aturn);
+- ain = true;
+- assert(bin == false); /* critical section */
+- ain = false;
+- x = false
+-}
+-
+-proctype B()
+-{ y = true;
+- t = Aturn;
+- (x == false || t == Bturn);
+- bin = true;
+- assert(ain == false); /* critical section */
+- bin = false;
+- y = false
+-}
+-
+-init
+-{ run A(); run B()
+-}
+//GO.SYSIN DD p320
+echo p325.test 1>&2
+sed 's/.//' >p325.test <<'//GO.SYSIN DD p325.test'
+-proctype test_sender(bit n)
+-{ byte type, toggle;
+-
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- :: timeout ->
+- ses_to_flow[n]!sync,toggle
+- od;
+- toggle = 1 - toggle;
+-
+- do
+- :: ses_to_flow[n]!data,white
+- :: ses_to_flow[n]!data,red -> break
+- od;
+- do
+- :: ses_to_flow[n]!data,white
+- :: ses_to_flow[n]!data,blue -> break
+- od;
+- do
+- :: ses_to_flow[n]!data,white
+- :: break
+- od
+-}
+-proctype test_receiver(bit n)
+-{
+- do
+- :: flow_to_ses[n]?data,white
+- :: flow_to_ses[n]?data,red -> break
+- od;
+- do
+- :: flow_to_ses[n]?data,white
+- :: flow_to_ses[n]?data,blue -> break
+- od;
+-end: do
+- :: flow_to_ses[n]?data,white
+- od
+-}
+//GO.SYSIN DD p325.test
+echo p327.upper 1>&2
+sed 's/.//' >p327.upper <<'//GO.SYSIN DD p327.upper'
+-proctype upper()
+-{ byte s_state, r_state;
+- byte type, toggle;
+-
+- ses_to_flow[0]!sync,toggle;
+- do
+- :: flow_to_ses[0]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- :: timeout ->
+- ses_to_flow[0]!sync,toggle
+- od;
+- toggle = 1 - toggle;
+-
+- do
+- /* sender */
+- :: ses_to_flow[0]!white,0
+- :: atomic {
+- (s_state == 0 && len (ses_to_flow[0]) < QSZ) ->
+- ses_to_flow[0]!red,0 ->
+- s_state = 1
+- }
+- :: atomic {
+- (s_state == 1 && len (ses_to_flow[0]) < QSZ) ->
+- ses_to_flow[0]!blue,0 ->
+- s_state = 2
+- }
+- /* receiver */
+- :: flow_to_ses[1]?white,0
+- :: atomic {
+- (r_state == 0 && flow_to_ses[1]?[red]) ->
+- flow_to_ses[1]?red,0 ->
+- r_state = 1
+- }
+- :: atomic {
+- (r_state == 0 && flow_to_ses[1]?[blue]) ->
+- assert(0)
+- }
+- :: atomic {
+- (r_state == 1 && flow_to_ses[1]?[blue]) ->
+- flow_to_ses[1]?blue,0;
+- break
+- }
+- :: atomic {
+- (r_state == 1 && flow_to_ses[1]?[red]) ->
+- assert(0)
+- }
+- od;
+-end:
+- do
+- :: flow_to_ses[1]?white,0
+- :: flow_to_ses[1]?red,0 -> assert(0)
+- :: flow_to_ses[1]?blue,0 -> assert(0)
+- od
+-}
+//GO.SYSIN DD p327.upper
+echo p329 1>&2
+sed 's/.//' >p329 <<'//GO.SYSIN DD p329'
+-/*
+- * PROMELA Validation Model
+- * FLOW CONTROL LAYER VALIDATION
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2];
+-
+-#include "App.F.flow_cl"
+-#include "p327.upper"
+-
+-init
+-{
+- atomic {
+- flow_to_dll[0] = dll_to_flow[1];
+- flow_to_dll[1] = dll_to_flow[0];
+- run fc(0); run fc(1);
+- run upper()
+- }
+-}
+//GO.SYSIN DD p329
+echo p330 1>&2
+sed 's/.//' >p330 <<'//GO.SYSIN DD p330'
+-/*
+- * PROMELA Validation Model
+- * FLOW CONTROL LAYER VALIDATION
+- */
+-
+-#define LOSS 0 /* message loss */
+-#define DUPS 0 /* duplicate msgs */
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan ses_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_ses[2] = [QSZ] of { byte, byte };
+-chan dll_to_flow[2] = [QSZ] of { byte, byte };
+-chan flow_to_dll[2];
+-
+-#include "App.F.flow_cl"
+-#include "p327.upper"
+-
+-init
+-{
+- atomic {
+- flow_to_dll[0] = dll_to_flow[1];
+- flow_to_dll[1] = dll_to_flow[0];
+- run fc(0); run fc(1);
+- run upper()
+- }
+-}
+//GO.SYSIN DD p330
+echo p337.defines2 1>&2
+sed 's/.//' >p337.defines2 <<'//GO.SYSIN DD p337.defines2'
+-/*
+- * PROMELA Validation Model
+- * global definitions
+- */
+-
+-#define QSZ 2 /* queue size */
+-
+-mtype = {
+- red, white, blue,
+- abort, accept, ack, sync_ack, close, connect,
+- create, data, eof, open, reject, sync, transfer,
+- FATAL, NON_FATAL, COMPLETE
+- }
+-
+-chan use_to_pres[2] = [QSZ] of { mtype };
+-chan pres_to_use[2] = [QSZ] of { mtype };
+-chan pres_to_ses[2] = [QSZ] of { mtype };
+-chan ses_to_pres[2] = [QSZ] of { mtype, byte };
+-chan ses_to_flow[2] = [QSZ] of { mtype, byte };
+-chan ses_to_fsrv[2] = [0] of { mtype };
+-chan fsrv_to_ses[2] = [0] of { mtype };
+-chan flow_to_ses[2];
+//GO.SYSIN DD p337.defines2
+echo p337.fserver 1>&2
+sed 's/.//' >p337.fserver <<'//GO.SYSIN DD p337.fserver'
+-/*
+- * File Server Validation Model
+- */
+-
+-proctype fserver(bit n)
+-{
+-end: do
+- :: ses_to_fsrv[n]?create -> /* incoming */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: ses_to_fsrv[n]?data
+- :: ses_to_fsrv[n]?eof -> break
+- :: ses_to_fsrv[n]?close -> break
+- od
+- fi
+- :: ses_to_fsrv[n]?open -> /* outgoing */
+- if
+- :: fsrv_to_ses[n]!reject
+- :: fsrv_to_ses[n]!accept ->
+- do
+- :: fsrv_to_ses[n]!data -> progress: skip
+- :: ses_to_fsrv[n]?close -> break
+- :: fsrv_to_ses[n]!eof -> break
+- od
+- fi
+- od
+-}
+//GO.SYSIN DD p337.fserver
+echo p337.pftp.ses 1>&2
+sed 's/.//' >p337.pftp.ses <<'//GO.SYSIN DD p337.pftp.ses'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p337.user"
+-#include "App.F.present"
+-#include "p337.session"
+-#include "p337.fserver"
+-
+-init
+-{ atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- }
+-}
+//GO.SYSIN DD p337.pftp.ses
+echo p337.session 1>&2
+sed 's/.//' >p337.session <<'//GO.SYSIN DD p337.session'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto DATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto DATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-DATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+- ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-DATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control *** disabled
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD p337.session
+echo p337.user 1>&2
+sed 's/.//' >p337.user <<'//GO.SYSIN DD p337.user'
+-/*
+- * User Layer Validation Model
+- */
+-
+-proctype userprc(bit n)
+-{
+- use_to_pres[n]!transfer;
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- :: use_to_pres[n]!abort -> goto Aborted
+- fi;
+-Aborted:
+- if
+- :: pres_to_use[n]?accept -> goto Done
+- :: pres_to_use[n]?reject -> goto Done
+- fi;
+-Done:
+- skip
+-}
+//GO.SYSIN DD p337.user
+echo p342.pftp.ses1 1>&2
+sed 's/.//' >p342.pftp.ses1 <<'//GO.SYSIN DD p342.pftp.ses1'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p337.user"
+-#include "App.F.present"
+-#include "p337.session"
+-#include "p337.fserver"
+-
+-init
+-{
+- atomic {
+- run userprc(0); run userprc(1);
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- };
+- atomic {
+- byte any;
+- chan foo = [1] of { byte, byte };
+- ses_to_flow[0] = foo;
+- ses_to_flow[1] = foo
+- };
+-end: do
+- :: foo?any,any
+- od
+-}
+//GO.SYSIN DD p342.pftp.ses1
+echo p343.claim 1>&2
+sed 's/.//' >p343.claim <<'//GO.SYSIN DD p343.claim'
+-never {
+- skip; /* match first step of init (spin version 2.0) */
+- do
+- :: !pres_to_ses[0]?[transfer]
+- && !flow_to_ses[0]?[connect]
+- :: pres_to_ses[0]?[transfer] ->
+- goto accept0
+- :: flow_to_ses[0]?[connect] ->
+- goto accept1
+- od;
+-accept0:
+- do
+- :: !ses_to_pres[0]?[accept]
+- && !ses_to_pres[0]?[reject]
+- od;
+-accept1:
+- do
+- :: !ses_to_pres[1]?[accept]
+- && !ses_to_pres[1]?[reject]
+- od
+-}
+//GO.SYSIN DD p343.claim
+echo p347.pftp.ses5 1>&2
+sed 's/.//' >p347.pftp.ses5 <<'//GO.SYSIN DD p347.pftp.ses5'
+-/*
+- * PROMELA Validation Model
+- * Session Layer
+- */
+-
+-#include "p337.defines2"
+-#include "p347.pres.sim"
+-#include "p347.session.prog"
+-#include "p337.fserver"
+-
+-init
+-{ atomic {
+- run present(0); run present(1);
+- run session(0); run session(1);
+- run fserver(0); run fserver(1);
+- flow_to_ses[0] = ses_to_flow[1];
+- flow_to_ses[1] = ses_to_flow[0]
+- }
+-}
+//GO.SYSIN DD p347.pftp.ses5
+echo p347.pres.sim 1>&2
+sed 's/.//' >p347.pres.sim <<'//GO.SYSIN DD p347.pres.sim'
+-/*
+- * PROMELA Validation Model
+- * Presentation & User Layer - combined and reduced
+- */
+-
+-proctype present(bit n)
+-{ byte status;
+-progress0:
+- pres_to_ses[n]!transfer ->
+- do
+- :: pres_to_ses[n]!abort;
+-progress1: skip
+- :: ses_to_pres[n]?accept,status ->
+- break
+- :: ses_to_pres[n]?reject,status ->
+- if
+- :: (status == NON_FATAL) ->
+- goto progress0
+- :: (status != NON_FATAL) ->
+- break
+- fi
+- od
+-}
+//GO.SYSIN DD p347.pres.sim
+echo p347.session.prog 1>&2
+sed 's/.//' >p347.session.prog <<'//GO.SYSIN DD p347.session.prog'
+-/*
+- * Session Layer Validation Model
+- */
+-
+-proctype session(bit n)
+-{ bit toggle;
+- byte type, status;
+-
+-endIDLE:
+- do
+- :: pres_to_ses[n]?type ->
+- if
+- :: (type == transfer) ->
+- goto progressDATA_OUT
+- :: (type != transfer) /* ignore */
+- fi
+- :: flow_to_ses[n]?type,0 ->
+- if
+- :: (type == connect) ->
+- goto progressDATA_IN
+- :: (type != connect) /* ignore */
+- fi
+- od;
+-
+-progressDATA_IN: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!create;
+- do
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_flow[n]!reject,0;
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- ses_to_flow[n]!accept,0;
+- break
+- od;
+- /* 2. Receive the data, upto eof */
+- do
+- :: flow_to_ses[n]?data,0 ->
+-progress: ses_to_fsrv[n]!data
+- :: flow_to_ses[n]?eof,0 ->
+- ses_to_fsrv[n]!eof;
+- break
+- :: pres_to_ses[n]?transfer ->
+- ses_to_pres[n]!reject(NON_FATAL)
+- :: flow_to_ses[n]?close,0 -> /* remote user aborted */
+- ses_to_fsrv[n]!close;
+- break
+- :: timeout -> /* got disconnected */
+- ses_to_fsrv[n]!close;
+- goto endIDLE
+- od;
+- /* 3. Close the connection */
+- ses_to_flow[n]!close,0;
+- goto endIDLE;
+-
+-progressDATA_OUT: /* 1. prepare local file fsrver */
+- ses_to_fsrv[n]!open;
+- if
+- :: fsrv_to_ses[n]?reject ->
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: fsrv_to_ses[n]?accept ->
+- skip
+- fi;
+- /* 2. initialize flow control *** disabled
+- ses_to_flow[n]!sync,toggle;
+- do
+- :: atomic {
+- flow_to_ses[n]?sync_ack,type ->
+- if
+- :: (type != toggle)
+- :: (type == toggle) -> break
+- fi
+- }
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- od;
+- toggle = 1 - toggle;
+- /* 3. prepare remote file fsrver */
+- ses_to_flow[n]!connect,0;
+- if
+- :: flow_to_ses[n]?reject,status ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?connect,0 ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(NON_FATAL);
+- goto endIDLE
+- :: flow_to_ses[n]?accept,0 ->
+- skip
+- :: timeout ->
+- ses_to_fsrv[n]!close;
+- ses_to_pres[n]!reject(FATAL);
+- goto endIDLE
+- fi;
+- /* 4. Transmit the data, upto eof */
+- do
+- :: fsrv_to_ses[n]?data ->
+- ses_to_flow[n]!data,0
+- :: fsrv_to_ses[n]?eof ->
+- ses_to_flow[n]!eof,0;
+- status = COMPLETE;
+- break
+- :: pres_to_ses[n]?abort -> /* local user aborted */
+- ses_to_fsrv[n]!close;
+- ses_to_flow[n]!close,0;
+- status = FATAL;
+- break
+- od;
+- /* 5. Close the connection */
+- do
+- :: pres_to_ses[n]?abort /* ignore */
+- :: flow_to_ses[n]?close,0 ->
+- if
+- :: (status == COMPLETE) ->
+- ses_to_pres[n]!accept,0
+- :: (status != COMPLETE) ->
+- ses_to_pres[n]!reject(status)
+- fi;
+- break
+- :: timeout ->
+- ses_to_pres[n]!reject(FATAL);
+- break
+- od;
+- goto endIDLE
+-}
+//GO.SYSIN DD p347.session.prog
+echo p94 1>&2
+sed 's/.//' >p94 <<'//GO.SYSIN DD p94'
+-byte state = 2;
+-
+-proctype A() { (state == 1) -> state = 3 }
+-
+-proctype B() { state = state - 1 }
+-
+-/* added: */
+-init { run A(); run B() }
+//GO.SYSIN DD p94
+echo p95.1 1>&2
+sed 's/.//' >p95.1 <<'//GO.SYSIN DD p95.1'
+-init { printf("hello world\n") }
+//GO.SYSIN DD p95.1
+echo p95.2 1>&2
+sed 's/.//' >p95.2 <<'//GO.SYSIN DD p95.2'
+-proctype A(byte state; short set)
+-{ (state == 1) -> state = set
+-}
+-
+-init { run A(1, 3) }
+//GO.SYSIN DD p95.2
+echo p96.1 1>&2
+sed 's/.//' >p96.1 <<'//GO.SYSIN DD p96.1'
+-byte state = 1;
+-
+-proctype A() { (state == 1) -> state = state + 1 }
+-
+-proctype B() { (state == 1) -> state = state - 1 }
+-
+-init { run A(); run B() }
+//GO.SYSIN DD p96.1
+echo p96.2 1>&2
+sed 's/.//' >p96.2 <<'//GO.SYSIN DD p96.2'
+-#define true 1
+-#define false 0
+-#define Aturn 1
+-#define Bturn 0
+-
+-bool x, y, t;
+-
+-proctype A()
+-{ x = true;
+- t = Bturn;
+- (y == false || t == Aturn);
+- /* critical section */
+- x = false
+-}
+-proctype B()
+-{ y = true;
+- t = Aturn;
+- (x == false || t == Bturn);
+- /* critical section */
+- y = false
+-}
+-init { run A(); run B() }
+//GO.SYSIN DD p96.2
+echo p97.1 1>&2
+sed 's/.//' >p97.1 <<'//GO.SYSIN DD p97.1'
+-byte state = 1;
+-proctype A() { atomic { (state == 1) -> state = state + 1 } }
+-proctype B() { atomic { (state == 1) -> state = state - 1 } }
+-init { run A(); run B() }
+//GO.SYSIN DD p97.1
+echo p97.2 1>&2
+sed 's/.//' >p97.2 <<'//GO.SYSIN DD p97.2'
+-proctype nr(short pid, a, b)
+-{ int res;
+-
+-atomic { res = (a*a+b)/2*a;
+- printf("result %d: %d\n", pid, res)
+- }
+-}
+-init { run nr(1,1,1); run nr(1,2,2); run nr(1,3,2) }
+//GO.SYSIN DD p97.2
+echo p99 1>&2
+sed 's/.//' >p99 <<'//GO.SYSIN DD p99'
+-proctype A(chan q1)
+-{ chan q2;
+-
+- q1?q2;
+- q2!123
+-}
+-
+-proctype B(chan qforb)
+-{ int x;
+-
+- qforb?x;
+- printf("x = %d\n", x)
+-}
+-
+-init
+-{ chan qname[2] = [1] of { chan };
+- chan qforb = [1] of { int };
+-
+- run A(qname[0]);
+- run B(qforb);
+-
+- qname[0]!qforb
+-}
+//GO.SYSIN DD p99
--- /dev/null
+Update History of the SPIN Version 1.0 sources
+==============================================
+
+==== Version 1.0 - January 1991 ====
+
+The version published in the first printing of
+``Design and Validation of Computer Protocols''
+is nominally SPIN Version 1.0.
+The notes below describe all modifications to that
+source that were made between January 1991 and
+December 1994 (when the distribution switched to
+Spin Version 2.0, see V2.Updates).
+
+==== Version 1.1 - April 1991 ====
+
+1. (4/6/91)
+a queue with a single field of type bool is now mapped onto
+an unsigned char to avoid compilers mapping it onto unsigned int.
+2. (4/7/91)
+variables are now sorted by type in the statevector (more compact
+and thus improves hashing)
+3. (4/7/91)
+improved handling of atomic move (removes a statespace-intercept
+bug for initial process with id=0)
+4. (5/22/91)
+allow multiple labels per statement, without needing skip-fillers
+5. (6/11/91)
+fixed bug in reassigning labels in pan.t
+6. (7/30/91)
+- added proc_skip[] and q_skip[] arrays to pan sources
+ the creation of processes and queues was not always
+ reversible due to word-alignment errors... [caused sv_restor() error]
+- removed Have_claim from sched.c; the adjustment of pids was incorrect
+- a remote ref to a non-existing pid is now always 0, and does not
+ cause a dummy global variable to be created by default with -t...
+ (chages in vars.c and sched.c)
+7. (8/1/91)
+- fixed a potentially infinite cycle in walk_sub()
+8. (8/7/91)
+- fixed bug: couldn't complete rendez-vous inside atomic sections
+9. (8/22/91)
+- lookup(s) failed to check a match on globals when
+ performing a local lookup of names; caused guided simulations
+ to report type clashes.
+
+==== Version 1.2 - August 1991 ====
+
+1. (9/11/91)
+- added traps to check for uninitialized channels in pan.c
+- added descriptive statement strings into transition matrix
+ which are now reported with unreached code etc.
+2. (1/7/92)
+- removed bug: no state should be stored in mid-rv.
+ note that a rv need not complete and matching
+ on the mid state of an unsuccessful rv may cause
+ missing errors (a rv may legally not complete in
+ some cases, without causing deadlock, and not in others).
+ the change also reduces the number of states stored.
+3. (1/11/92)
+- made printout for R different from r in mesg.c
+
+==== Version 1.3 - January 1992 ==== [current USL Toolchest version]
+
+1. 3/6/92
+- bug fixed in usage of -l with rendez-vous (forgot "if (boq==-1)") pangen1.h
+2. 4/10/92
+- converted to new hstore with sorted linked lists (roughly 10% faster)
+3. 6/3/92
+- remote variables were not promoted to (int) before referral in expressions
+ updated Errata with some warnings (e.g., about modeling system invariants
+ in the presence of timeouts, and using the wrong number of parameters in
+ receives)
+- updated makefile with hint for yacc-flags
+4. 6/10/92
+- spin now returns the number of parse errors found upon exit from main
+- yyin is now declared extern in main
+- srand is now declared void in spin.h
+5. 7/4/92
+- added pan options -d and -d -d to printout the internal state tables
+ pan will no longer report structurally unreachable states as
+ dynamically unreachable
+- added warning when spec is modified since pan.trail written
+- solved a trail problem when user guess pids which are offset by claim
+- print proper process numbers for spin -t when a claim is running
+- fixed error in lookup of _p values (it's a local var not global)
+- improved claim checking with geoffrey browns claim stuttering semantics
+
+==== Version 1.4 - July 1992 ====
+
+1. 7/8/92
+- replaced emalloc with a dedicated emalloc/malloc/free package; this
+ is six times faster on pftp.ses1 example compared to sysV library malloc
+2. 7/12/92
+- added default array bounds checking (except for remote references)
+ makes validations a little slower (say 5% - 10%), but is safer, and
+ the user can override it by compiling: cc -DNOBOUNDCHECK pan.c
+3. 8/26/92
+- improved the acceptance cycle detection method with a new algorithm
+ due to Patrice Godefroid, that works in both bitstate and exhaustive
+ search mode (the old version worked only in exhaustive mode)
+ time and space complexity of the new algorithm is the same as that for
+ non-progress cycle detection algorithm (at worst twice that of a straight search)
+ the method is functionally the same as the earlier method due to Courcoubetis,
+ Vardi, Wolper, and Yannakakis (CAV'90), but uses the 2-bit demon trick from
+ the non-progress cycle detector to distinguish between 1st and 2nd search.
+- fixed a buglet in lex.l that catenated strings printed on a single line
+ (thanks Alan Wenban for catching it)
+4. 12/11/92
+- intermediate states in atomic sequences were not stored in standard search
+ mode, but stored when a never claim was defined - that was unnecessary, and
+ has now been avoided. behavior doesn't change, but memory consumption in
+ exhaustive mode is now reduced somewhat.
+- the acceptance cycle detection algorithm would initiate its second search
+ from an accepting state within a never claim, even when the system was
+ inside an atomic sequence - it could therefore produce non-existing cycles.
+ has been fixed (both fixes thanks to Patrice Godefroid and Didier Pirrotin)
+
+==== Version 1.5 - January 1993 ====
+
+1.5.0
+- an option -V was added both to spin itself and to the analyzers
+ generated by spin to print the source version number.
+- a compiler directive VERBOSE was added to the generated analyzers
+ to assist the user in understanding how the depth-first searches
+ are performed. to invoke the extra printouts compile
+ the source as cc -DVERBOSE pan.c (plus any other directives you may
+ be using, such as -DBITSTATE or -DMEMCNT=23)
+- a small bug-fix was added to avoid a misplaced compiler directive
+ (in BITSTATE mode, in the absence of accept labels in the model, there
+ could be a compiler error that is now avoided)
+- somewhat improved error reporting.
+- several more important runtime options for the generated analyzers
+ were added:
+ 1 an explicit runtime option -a to invoke the search for
+ acceptance cycles. until now this used to happen by default
+ unless you specified an explicit -l option for a search for
+ non-progress cycles. from now on a search for cycles
+ always has to be specified explicitly as either -a or -l.
+ 2 a runtime option -f to modify the search for cycles under
+ `weak fairness.' it works for both -a (acceptance cycles)
+ and for -l (non-progress cycles), and is independent of the
+ search mode (full state storage or bitstate hashing)
+ using -f may increase the time-complexity of a search, but
+ it does not alter the space requirements
+- specifying -f without either -a or -l would have no effect, so it
+ is considered an error.
+- you cannot combine options -a and -l
+- you can always combine -a with a never claim, but
+- you cannot combine -l with a never claim.
+- a harmless glitch in the setting of tau values for atomic moves
+ was fixed - it should not alter the behavior of the analyzers
+- another small fix in the reporting of unreachable code (previous versions
+ of spin could forget to report some of the states)
+- remember: to search for acceptance cycles, you always must
+ specify option -a now (or -f -a if restricted to fair cycles)
+
+=
+1.5.1 - 2/23/93
+- the acceptance cycle detector now starts the search for an acceptance
+ cycle after any move, whether in the claim or in the system
+ (until now, it only did so after a system move - which did not cover
+ all cases correctly, specifically for cases that are covered by the
+ claim stutter semantics, and for cases where acceptance cycles are only
+ defined inside the claim, and not in the system)
+1.5.2 - 3/1/93
+- the change from 1.5.1 was incorrect - a search from acceptence cycles
+ starts after system moves, or after stutter-claim moves, but not
+ for other claim moves (a stutter claim move is used to cycle the claim
+ in valid and invalid endstates - it triggers an error report if the claim
+ can cycle through an accept state in this case - it should not trigger
+ error reports in any other case)
+1.5.3 - 3/19/93
+- spin now catches SIGPIPE signals, and exits when it sees one.
+ added an option -X to use stdout instead of stderr for all error messages
+ (these upgrades are in preparation for an X-interface to spin)
+1.5.4 - 4/15/93
+- in simulation mode spin didn't always flag it as an error if an array-name
+ was used as a formal parameter in a proctype declaration (spin -a always
+ reports it correctly) - the error report is now given
+- added Xspin to the distribution as bundle4 - an X-interface to spin based
+ on the (public domain) toolkit Tcl/Tk from the university of berkeley.
+1.5.5 - 4/21/93
+- fixed an error in fair_cycle(); the original algorithm omitted to set
+ the correct value in a pointer to the current process during the fairness
+ checks - the result was that fairness calls were not always accurate.
+- some small improvements in the Xspin interface (call it XSPIN version 1.0.1)
+- improvement in sched.c - to match the assignemnts of pids to those of the validator
+1.5.6 - 5/21/93
+- another error in fair_cycle; code inserted for the detection of self-loops
+ was incorrect; it has been omitted.
+ non-fair cycles that can become fair *only* by the inclusion of a dummy
+ "do :: skip od"
+ loop in one of the processes may be missed in a verification using the -f flag.
+ since such busy-looping constructs are not (or should not be) used in Promela
+ anyway, this should not create problems
+- changed the data-types used in the hash-functions - to secure portability
+ of SPIN to 64 bit machines.
+1.5.7 - 7/1/93
+- fixed a subtle error that happens when a remote variable is used deeply nested inside
+ the index of another remote variable (array)
+- also fixed a spurious error report on array bound checking in such cases
+- both cases should be rare enough that it didn't bite anyone - they affected only
+ simulation mode
+1.5.8 - 10/1/93
+- made it an error to place a label at the first statement of a sub-sequence.
+ spin's optimization strategy (to speed up searches) can defeat such an
+ unconventional label placement easily, which can cause problems in remote references.
+ the rule is (and has actually always been) that constructs such as
+ do if atomic {
+ :: L: a = 1 :: L: a = 1 L: a = 1
+ od fi }
+ should be written as:
+ L: do L: if L: atomic {
+ :: a = 1 :: a = 1 a = 1
+ od fi }
+ the rule is now enforced. to make it easier, the above message is printed if the
+ label is accidentily misplaced, in the heat of design...
+ note that the first state of a subsequence equals the state of the enclosing
+ construct (e.g., the start state of each option in a do-structure is the very
+ same state as the start state of the do-structure itself)
+1.5.9 - 11/17/93
+ A small error in the management of rendez-vous status during the search had
+ slipped in on 9/11/91. finally caught and removed.
+1.5.10 - 11/19/93
+-spin attempts to optimize goto and break statements by removing them from
+ the transition matrix wherever possible. this has no visible effect, other
+ then achieving an extra speedup of the validation.
+ in a small number of cases, though, the labels must be preserved
+ one such case is when a goto or break carries a progress, end, or accept label.
+ in that case the jump is preserved (that case was always treated correctly).
+ another case, that was overlooked so far, is when the label in front of a goto
+ is used in a remote reference, such as P[pid]:label. the use is dubious, but
+ cannot be excluded. in 1.5.10 this case has been added to the exceptions - where
+ the gotos are not removed from the matrix.
+-also fixed: the never claim no longer steps in the middle of a rendez-vous handshake
+ (it was correctly observed by a user that it shoudln't - since its really atomic)
+-also fixed: the initial state of a newly started process in the simulator
+ now always matches that of the validator (same optimization steps are done)
+ the avoids some cases of lost trails in guided simulations
+1.5.11 - 2/1/94
+-the fix from 1.5.10 works by inserting a dummy skip statement in between
+ a label and the statement that follows it (a goto in this case)
+ that calls for an extra statement, with a unique state number
+ the extra state numbers weren't counted in the allocation of memory for the
+ transition matrix - which could cause some peculiar behavior in the (luckily)
+ few cases where this improvement kicked in. it's fixed in this release.
+-another improvement, that had been waiting in the wings for a chance to make it
+ into the released version - is that the timeout variable is now testable inside
+ never claims (that wasn't true before).
+1.5.12 - 1/20/94
+-added a random number generator - compliments of Thierry Cattel.
+ as an alternative to the badly performing standard routines provided
+ on most systems. should improve simulations - affects nothing else.
+1.5.13 - 3/27/94
+-small improvement in handling of syntax errors in parser.
+-added clarifications to the file `roadmap' in bundle3
+-added manual.ps to the distribution
+1.5.14 - 4/22/94
+-until now you had to turn on message loss (-m) explicitly when following
+ a backtrace (using spin -t) from a system that was generated with message
+ loss enabled (i.e., with spin -a -m). that is easy to forget. spin -t no
+ longer explicitly requires the -m flag in these cases, to avoid confusion.
+ it is still valid to use -m in combination with -t, but not required.
+1.5.15 - 5/20/94
+-removed small bug from sched.c (the simulator) that could prevent a process
+ from being deleted correctly from the run queue when the last two processes die.
+1.5.16 - 6/3/94
+-if a goto jump inside an atomic sequence jumped to the last statement of the
+ sequence, spin would get confused and mark that jump as non-atomic
+ version 1.5.16 handles this case correctly
+-added an error production to the grammar - to improve syntax error reporting
+1.5.17 - 7/15/94
+-a remote reference to a non-existing variable could result in a core-dump
+ during guided simulations; fixed in this version.
+1.5.18 - 8/1/94
+-during simulation a remote reference to a previously unused local variable
+ from another process would return the default 0 value - instead of the initial
+ value of such a variable. this caused the behavior of validator and simulator
+ to differ in such cases (in the validator all variables are always created and
+ initialized upon process creation - in the simulator variables are created and
+ initialized in a `lazy' fashion: upon the first reference.
+ this is now fixed so that the simulator's behavior is now no different from
+ the validator: refering to a previously unused variable of a running process
+ returns its initial value - as it should.
+
+==== Version 1.6 - August 1994 ====
+
+1.6.0 - 8/5/94
+-Important improvement - Please Read!
+-it was always a problem to get ``mtype'' names used inside messages to
+ be distinguished properly from other integers in (guided) simulations.
+ the rule used so far was that if a message field held a ``constant''
+ it was interpreted and printed as an mtype name, and else as a value.
+
+ starting with version 1.6 this is handled better as follows:
+ if you declare a message field to have type ``mtype'' it is ALWAYS printed
+ as a symbolic name, never as a value.
+ for example, you can now declare a channel as:
+ chan sender = [12] of { mtype, byte, short, chan, mtype };
+ so that the first and last field are always printed symbolically as a name,
+ never as a value. only bits, bytes, shorts, and ints, are now printed
+ as values.
+ make good note of the change: you will almost always want to use mtype
+ declarations for at least some of the fields in any channel declaration.
+ the new functionality greatly increases the clarity of tracebacks with spin -t
+
+-new compile time option cc -DPEG pan.c - to force the runtime analyzer to
+ gather statistics about how often each transition (the bracketed number in
+ the pan -d output) is executed.
+
+1.6.1 - 8/16/94
+-added a declaration of procedure putpeg, to avoid a compiler warning
+-made sure that booleans such as q?[msg] can be used in any combination
+ in assert statements (until now spin could insert newlines that spoiled
+ printfs on debugging output)
+
+1.6.2 - 8/20/94
+-tightened the parser to reject expressions inside receive statements.
+ so far these were silently accepted (incorrectly) - and badly translated
+ into pan.[mb] files. the fields in a receive statement can legally only
+ contain variable-names or constants (mtypes). variables are set, and
+ constant fields are matched in the receive.
+-cleaned up the enforcement of the MEMCNT parameter (the compile time parameter
+ that is used to set a physical memory limit at 2**MEMCNT bytes)
+ we now check *before* allocating a new chunk of memory whether this will
+ exceed the limit - instead of *after* having done so - as was the case so far.
+ gives a better report on which memory request caused memory to run out.
+
+1.6.3 - 8/27/94
+-the simulator failed to recognize remote label references properly.
+ has been fixed in sched.c.
+-the validator failed to report blocking statements inside atomic sequences
+ when a never claim was present - it defaulted to claim stutter instead.
+ it now correctly reports the error.
+
+1.6.4 - 9/18/94
+-in rare cases, an accept cycle could be missed if it can only be closed
+ by multiple claim stutter moves through a sequence of distinct claim states
+ this now works as it should.
+-added some helpful printfs that are included when the -DVERBOSE and -DDEBUG
+ compile time flags are used on pan.c's
+
+1.6.5 - 10/19/94
+-the mtype field of message queues wasn't interpreted correctly in
+ in the printout of verbose simulation runs (the field was printed
+ numerically instead of symbolically).
+
+1.6.6 - 11/15/94
+ minor fix in procedure call of new_state - to avoid compiler complaints
+ about use of an ANSI-isms in an otherwise pre-ANSI-C source.
+ (version 2.0 - see below) is completely ANSI/POSIX standard and will not
+ compile with pre-ANSI compilers. version 1.6.6 is the last
+ version of SPIN that does not make this requirement.
+
+12/24/94
+ Version 1.6.6 is the last update of Spin Version 1.
+ The distribution will switch to Spin Version 2.0, and
+ all further updates will be documented in: Doc/V2.Updates.
--- /dev/null
+Distribution Update History of the SPIN sources
+===============================================
+
+==== Version 2.0 - 1 January 1995 ====
+
+The version published in the first printing of
+``Design and Validation of Computer Protocols''
+is nominally SPIN Version 1.0.
+
+SPIN version 2.0 includes a partial order reduction
+mode and many extensions and revisions that are
+documented in the file Doc/WhatsNew.ps of the
+distribution. This file lists all updates
+made to these sources after the first release.
+
+===== 2.0.1 - 7 January 1995 ====
+
+An automatic shell script `upgrades' in the main
+SPIN directory will be maintained from now on.
+For all future updates it will suffice to retrieve
+just the upgrades file, and execute it in the Src
+directory to apply all changes. The tar file will
+of course be kept up to date at all times as well.
+
+Changes in this update:
+1. MEMCNT can now grow larger than 2^31 - for those
+ happy users that have a machine with more than
+ a Gigabyte of main memory.
+ (N.B. this fix was made and distributed before the
+ upgrades file was started - so this one change isn't
+ available in that way)
+2. Change in the lexical analyzer to accept redundant
+ text at the end of preprocessor directives, that
+ some versions of cpp may produce.
+
+===== 2.0.2 - 10 January 1995 ====
+
+Two small updates to pangen1.h to avoid problems on
+some systems when (1) compiling pan.c with profiling
+enabled, or (2) when using the new predefined function
+enabled() in combination with partial order reduction.
+
+===== 2.0.3 - 12 January 1995 ====
+
+At request, added a printout of the mapping from label
+names as used in the Promela source to state numbers as
+used in the verifier to spin option -d.
+to see just this new part of the listing, say:
+ spin -d spec | grep "^label"
+first column is the keyword "label"
+second column is the name of the label
+third column is the state number assigned
+last column is the name of the proctype in which the
+label is used.
+the same state numbers and transitions are
+also used by the verifier, and can be printed as
+before with:
+ spin -a spec; cc -o pan pan.c; pan -d
+
+===== 2.0.4 - 14 January 1995 ====
+
+With the introduction of `else' a new opportunity for
+silly syntax mistakes is created. it is all too tempting
+to write:
+ if
+ :: condition ->
+ more
+ :: else
+ something
+ fi
+and forgot the statement separator that is required between
+the `else' and `something'
+this likely typo is easy to recognize, and is silently
+repaired and accepted by the new lexical analyzer.
+
+===== 2.0.5 - 17 January 1995 ====
+
+transition labels for send and receive operations
+now preserve all the information from the source text
+(i.e., the symbolic names for all message parameters used).
+
+===== 2.0.6 - 27 January 1995 ====
+
+two fixes, both caught by Roberto Manione and Paola:
+- deeply nested structures (more than two levels)
+ could give syntax errors, and
+- the transition label of an `else' statement wasn't
+ always printed correctly in the traces.
+
+===== 2.0.7 - 2 February 1995 ====
+
+- another fix of the implementation of data structures
+ to make references of deeply nested structure work
+ the way they should
+- removed suboptimal working of safety marking of
+ atomic sequences. reductions are now slightly
+ larger in some cases.
+- improved boundary checking of array indexing also
+ slightly (made it more efficient for some cases)
+
+===== 2.0.8 - 7 February 1995 ====
+
+- adjusted line number counting slightly in spinlex.c
+ to avoid annoying off-by-one errors.
+
+===== 2.0.9 - 9 February 1995 ====
+
+- removed a bug in the transition structures that are
+ generated for d_steps. (small fix in pangen2.h)
+
+===== 2.1 - 15 February 1995 ====
+
+- removed two errors in the implementation of d_steps:
+ one was related to the treatment of `else' during verification
+ another related to the execution of d_steps in guided simulations
+- improved the treatment of rendez-vous in SPIN verbose printings
+ improves match with xspin; avoids misinterpretation of pids
+- made mtype fields be interpreted uniformly in all SPIN modes
+- added message sequence charts in xspin
+- removed stutter-closedness checks from pangen2.h (no change
+ in functionality, just replaced a dubious check with a more
+ precise descriptive warning)
+
+===== 2.1.1 - 17 February 1995 ====
+
+- instantiated channels declared inside typedefs weren't
+ properly initialized
+
+===== 2.2 - 19 February 1995 ====
+
+- main extension of functionality:
+ added an interactive simulation mode (both in xspin and in spin itself)
+ that will be described at greater length in Newsletter #4.
+ also improved some of the printouts for verbose simulation runs.
+
+- added a precompiler directive -DREACH to force exploration of
+ all states reachable within N execution steps from the initial
+ system state, when the search is deliberately truncated with -mN.
+ normally such a truncated search does not give that guarantee.
+ (note that if a state at level N-1 is also reachable from the
+ initial state within 1 execution step, but it is reached via the
+ longer path first in the DFS - then all successors via the shorter
+ path would normally not be explored, because they succeed a
+ previously visited state. with -DREACH we remember the depth at
+ which a state was visited, and consider it unvisited when encountered
+ on a shorter path. not compatible with BITSTATE - for the obvious
+ reason (no room to store the depth counts).
+
+- fixed a bug in pangen[24].c that could cause an internal consistency check
+ in the runtime verifiers to fail, and cause the run to terminate with
+ the error `sv_save failed'
+
+===== 2.2.1 - 23 February 1995 ====
+
+- small refinements to interactive simulation mode (xspin and spin)
+
+===== 2.2.2 - 24 February 1995 ====
+
+- added missing prototype in 2.2.1, and fix parameter mistake in a
+ function that was added in 2.2.1
+
+===== 2.2.3 - 3 March 1995 ====
+
+- bug fix in implementation of d_step (dstep.c)
+ and some mild improvements in error reporting
+
+===== 2.2.4 - 9 March 1995 ====
+
+- made sure process numbers assigned by simulator
+ are always the same as those assigned by the verifier
+- by request: added a binary exclusive-or operator ('^')
+
+===== 2.2.5 - 14 March 1995 ====
+
+- removed error in treatment of `else' during random simulation.
+ `else' was incorrectly considered to be executable also
+ in the middle of a rendez-vous handshake. no more.
+
+===== 2.2.6 - 21 March 1995 ====
+
+- made sure that variable declarations are reproduced in
+ the pan.c sources in the same order as they are given
+ in the original promela specification. this matters
+ in cases where new variables are initialized with each
+ others values.
+- better error handling when a reference is made erroneously
+ to an assumed element of an undeclared structure
+
+===== 2.2.7 - 23 March 1995 ====
+
+- catches more cases of blocking executions inside d_step
+ sequences
+- better handling of timeout, when using both blocking
+ atomic sequences and never claims. the behavior of the
+ simulator and the verifier didn't always match in these
+ cases. it does now.
+
+===== 2.2.8 - 30 March 1995 ====
+
+- inside dstep sequences `else' wasn't translated correctly
+
+===== 2.2.9 - 12 April 1995 ====
+
+- removed a mismatch between spin and xspin in dataflow output
+- clarified the scope of dataflow checks spin's -? response
+- fixed a typo that could confuse pid assignments when -DNOCLAIM is used
+- improved some of spin's outputs a little for xspin's benefit
+
+===== 2.2.10 - 18 April 1995 ====
+
+- removed a redundancy in the creation of channel templates
+ during the generation of a verifier with spin option -a
+
+===== 2.3.0 - 19 April 1995 ====
+
+- an extension of functionality. until now the simulator would execute
+ a receive test (for instance: q?[var1,var2] ) erroneously with
+ side-effects, possibly altering the values of the variables.
+ any boolean condition in Promela, however, must be side-effect free
+ and therefore the verifier had the correct implementation (no value
+ transfers in a test of executability of this type)
+ michael griffioen noted that the poll of a message in a channel was
+ nonetheless usefull. this leads to a new type of operation in Promela.
+ pending more thorough documentation, here's an example of
+ each of the existing ones:
+
+ A. q?var1,const,var2 - fifo receive (constants must be matched)
+ if successful, the message is removed
+ from the channel
+ B. q??var1,const,var - random receive (as A., but from any slot
+ that has a matching message, checked in
+ fifo order) if successful, the message is
+ removed from the channel
+ C. q?[var1,const,var2] - boolean test of executability of type A receive.
+ D. q??[var1,const,var2] - boolean test of executability of type B receive.
+ E. q?<var1,const,var2> - fifo poll, exactly as A, but the message is
+ not removed from the channel
+ F. q??<var1,const,var2> - random receive poll, exactly as B, but message
+ not removed from the channel
+
+ there are still only two different ways to test the executability of a
+ receive operation without side-effects (be it a normal receive or a poll
+ operation)
+ the two new operations of type E and F allow one to retrieve the contents
+ of a message from a channel, without altering the state of the channel.
+ all constant fields must of course still be matched, and if no variables
+ are present, an operation of type E has the same effect as one of type C.
+ (and similarly for types F and D)
+
+===== 2.3.1 - 20 April 1995 ====
+
+- slightly more conservative treatment for the change from 2.2.10
+ some redundancy is caused by the spec, and now flagged as warnings.
+ using a \ 5typedef structure that contains an initialized channel in
+ a formal parameter list, for instance, is always reduncant and uses
+ up channel templates in the verifier - without otherwise doing harm,
+ but there is a limit (255) to the number of templates that can exist.
+ the limit is now also checked and a warning is given when it is exceeded.
+
+===== 2.3.2 - 25 April 1995 ====
+
+- adjustment of output format of guided simulation to match a recent
+ change in Xspin (better tracking of printfs in MSC's)
+
+===== 2.3.3 - 29 April 1995 ====
+
+- bit or bool variables cannot be hidden - this is now reported as a
+ syntax error if attempted
+- the reporting of the options during interactive simulations is
+ improved - mysterious options, such as [ATOMIC] are now avoided better
+
+===== 2.3.4 - 1 May 1995 ====
+
+- added the keyword D_proctype as an alternative to proctype to
+ indicate that all the corresponding processes are expected to
+ be deterministic. the determinism is not enforced, but it is
+ checked by the verifier. no other difference exists. a simulation
+ will not pick up violations of this requirement.
+
+===== 2.3.5 - 13 May 1995 ====
+
+- the check for enforcing determinism (2.3.4) was not placed
+ correctly. it is now.
+
+===== 2.3.6 - 18 May 1995 ====
+
+- removed bug in code generation for arrays of bools
+- moved a debug-printf statement
+
+===== 2.3.7 - 18 May 1995 ====
+
+- removed bug in testing enabledness of run statements
+ during interactive simulations
+
+===== 2.3.8 - 19 May 1995 ====
+
+- small change in bitstate mode. the verifier checks if a
+ new state is previously visited, or appears on the dfs stack.
+ (information about presence in the stack is only needed to
+ enforce the partial order reduction rules)
+ in both cases (in bitstate mode) the check is done via hashing
+ into a bitarray; with the array for the statespace much larger
+ than the one for the stack.
+ so far, if either match succeeded, the search was truncated.
+ any match in the stack-array that is combined with no-match in
+ the statespace array, however, is necessarily caused by a
+ hash-collision (i.e., is a false match) and can be discarded.
+ the coverage of bitstate runs should improve slightly by this
+ change. nothing else will be affected.
+
+===== 2.4.0 - 22 May 1995 ====
+
+- new functionality: there is a new option to SPIN that
+ can be used in guided simulations (that is: in combination
+ with the option -t, when following a error trail produced
+ by one of the verifiers generated by SPIN)
+ for very lengthy trails, it can take a while to reach an
+ interesting point in the sequence. by adding the option
+ -jN one can now skip over the first N steps in the trail
+ (more precisely: supress the printing during those steps)
+ and quickly get to the interesting parts.
+ for instance:
+ spin -t -p -g -l -v -j1000 spec
+ skips the first 1000 steps and prints the rest.
+ caveat: if there are fewer than 1000 steps in the trail,
+ only the last state of the trail gets printed.
+- avoiding some more redundant printouts during guided simulations
+ will also help to speed up the browsing of error trails with XSPIN
+- the extension is supported in the new version of XSPIN as well
+- the new version of XSPIN also supports BREAKPOINTS during
+ all types of simulation
+ breakpoints are supported via the MSC printf statements
+ * including a printf that starts with the prefix "MSC: "
+ in a Promela Model will cause the remainder of the line
+ printed to be included as a box inside the MSC charts
+ maintined by XSPIN.
+ * if the remainder of such a line contains the capitalized word
+ BREAK, the simulator will pause at that line and wait for
+ the user to reactivate the run (single step or run)
+
+===== 2.4.1 - 4 June 1995 ====
+
+- added runtime option -e to verifiers produced by SPIN.
+ with this option all errors encountered during the verification
+ are saved in a trail file - up to the limit imposed by -cN
+ (the default is one trail). the first trail has the extension .trail,
+ as before (which is also the only extension expected right now under
+ the guided simulation option -t of SPIN). subsequent trails have
+ extensions .trail1, .trail2, ... etc.
+ use this option in combination with -cN
+ using -c5 -e will produce the first 5 trails,
+ using -c0 -e will produce all trails.
+- modified Xspin to work also with the new Tcl7.4/Tk4.0, which is now
+ in beta release. the changes should not affect the working under
+ the current version Tcl7.3/Tk3.6
+- there is now a file called `version_nr' in the directory /netlib/spin
+ on the distribution server. the file contains just the version
+ number from the currently distributed SPIN sources (avoids having to
+ download larger files to find out if anything changed)
+- added runtime option -hN to choose another than the default hash-function
+ (useful in bitstate mode. N must be an integer between 1 and 32)
+- improved the implementation of the new runtime option -s (for selecting
+ single-bit hashing instead of the default double-bit hashing)
+ especially useful in combination with -hN for sequential multihashing
+ techniques (iterative approximation of exhaustive searches for very large
+ problem sizes)
+- starting with this version of Xspin there will also be a separate upgrades
+ script to keep the Tcl/Tk files up to date.
+
+===== 2.4.2 - 11 June 1995 ====
+
+- so far, variables were created and initialized in the simulator
+ as late as possible: upon the first reference. a variable that
+ is never referenced was therefore never created - which is some
+ sense of economy. however, if the local is initialized with an
+ expression that includes references to other variables (local or
+ global) then those other variables might well change value before
+ the first reference to the initialized local is made - and thus
+ the local might obtain an unexpected initial value.
+ the error was caught by Thierry Cattel - and lazy variable creation
+ is now abandoned. the verifier is unaffected by this fix -- it
+ already id the right thing (instant creation of all locals on process
+ initialization).
+- channels deeply hidden inside structures were not properly logged
+ in the partial order reduction tables. it could cause an abort of
+ a verification run with the mysterious error "unknown q_id, q_cond."
+ it works properly now. error caught by Greg Duval.
+- made a small change in output format (comments only) for remote
+ references
+- made the new runtim option -e (from version 2.4.1) also accessible
+ from within XSPIN
+- changed the MSC canvas in the simulation mode of XSPIn to no longer
+ be self-scrolling (it defeated any attempt from the user to select
+ other portions of the MSC to ponder, other than the tail, during a
+ run). also made the message transfer arrows wider - and added an
+ option to replace the symbolic step numbers in the MSC's with source
+ text.
+
+===== 2.5 - 7 July 1995 ====
+
+Fixes
+- values of rendez-vous receives were printed incorrectly in printouts
+ of spin simulation runs - this could confuse xspin; the symptom was that
+ some rendez-vous send-recv arrows were not drawn in the MSC displays.
+- rendez-vous operations that guarded escapes did not always execute
+ properly in random simulations.
+- in xspin, the boxes produced by an MSC printf could lose its text
+ after the cursor would pass over it - that's no longer the case
+- the use of a remote references used to disable the partial order
+ reduction algorithm. instead, such references now automatically label
+ the transition that enters or exits from the corresponding process state
+ as unsafe. this suffices to preserve the use of the partial order
+ reduction. (proposed by Ratan Nalumasu.)
+
+Small Changes
+- added a print line in verbose output for process creations - as well
+ as process terminations. it will be used in a future version of xspin.
+- made all xspin verification run timed - the user+system time info
+ appears in the xspin logs
+- on aborted verification runs, xspin will now also print the partial
+ information gathered up to the point of the abort
+- slightly changed the way aborts are handled in xspin
+- never claims containing assignments (i.e., side-effects) can be useful
+ in some cases. spin will still generate warnings, but allows the user
+ to ignore them (i.e., the exit status of spin is no longer affected)
+- in simulation mode - xspin now pays attention to filenames and will not
+ try to hilight lines in files that are not visible in the main text window
+- added compile-time directive -DNOFAIR to allow compilation of a verifier
+ if it is known that the weak-fairness option will not be used -- this
+ avoids some memory overhead, and runs slightly faster.
+ the fastest run can be obtained by compiling -DNOCOMP -DNOFAIR (turning
+ off the state compression and the weak fairness related code)
+ the most memory frugal run (apart from the effects of -DREDUCE) can be
+ obtained by compiling with just -DNOFAIR (and leaving compression turned
+ on by default) note that memory consumed also goes up with the value
+ provided for the -m option (the maximum depth of the stack)
+- added a file Doc/Pan.Directives with an overview of all user definable
+ compile-time directives of the above type.
+
+Extension
+- added a mechanism to define process priorities for use during random
+ simulations - to make high priority processes more likely to execute
+ than low priority processes.
+ as alternatives to the standard:
+ run pname(...)
+ active proctype pname() { ... }
+ you may now add a numeric execution priority:
+ run pname(...) priority N
+ and/or
+ active proctype pname() priority N
+ (with N a number >= 1)
+ the default execution priority is 1; a higher number indicates a
+ higher priority. for instance, a priority 10 process is 10x more
+ likely to execute than a priority 1 process, etc.
+ the priority specified at the proctype declaration only affects the
+ processes that are initiated through the `active' prefix
+ a process instantiated with a run statement always gets the priority
+ that is explicitly or implicitly specified there (the default is 1).
+ the execution priorities have no effect during verification, guided,
+ or interactive simulation.
+- added an example specification file (Test/priorities) to verify the
+ correct operation of the execution priorities
+
+===== 2.5.1 - 12 July 1995 ====
+
+- some tweaks to correct the last update (a simulation run could
+ fail to terminate - and xspin could fail to detect the zero exit
+ status of spin after warning messages are printed)
+
+===== 2.6 - 16 July 1995 ====
+
+- added a new option for executable verifiers - to compute the effective
+ value range of all variables during executions.
+ the option is enabled by compiling pan.c with the directive -DVAR_RANGES
+ values are tracked in the range 0..255 only - to keep the memory
+ and runtime overhead introduced by this feature reasonable.
+ (the overhead is now restricted to roughly 40 bytes per variable -
+ it would increase to over 8k per variable if extended to the full
+ range of 16-bit integers)
+- a new option in the validation panel to support the above extension
+ was added
+- xspin now supports compile-time option -DNOFAIR to produce faster
+ verifiers when the weak fairness option is not selected
+- added an example in directory Test to illustrate dynamic creation
+ of processes (Test/erathostenes)
+- removed an error message that attempted to warn when a data structure
+ that contained an initialized channel was passed to a process as a
+ formal parameter. it leads to the creation of a redundant channel,
+ but is otherwise harmless.
+- changed the data types of some option flags from int to short
+
+===== 2.6.1 - 18 July 1995 ====
+
+- option -jN (introduced in version 2.4.0) -- to skip the verbose
+ printouts for the first N execution steps -- now also works in
+ random simulation mode.
+ only process creations and terminations are still always printed
+- channel names are no longer included in the -DVAR_RANGES output
+ at least not by default (see version 2.6 - above)
+ to still include them, generate the verifier in verbose mode
+ as ``spin -a -v spec'' instead of the default ``spin -a spec''
+- predefined variable _last was not quite implemented correctly
+ in the verifier (the error was harmless, but if you tried to write
+ specifications that included this construct, you may have had
+ more trouble than necessary getting it to work properly)
+
+===== 2.7.Beta - 24 July 1995 ====
+
+Fixes
+- when an atomic sequence blocks, the process executing that sequence
+ loses control, and another process can run. when all processes block,
+ the predefined variable 'timeout' becomes true, and all processes again
+ get a chance to execute. if all processes still block - we have reached
+ an end-state (valid or invalid, depending on how states are labeled).
+ until now, timeout was allowed to become true before an atomically
+ executing process could lose control - this is not strictly conform the
+ semantics given above, and therefore no longer possible.
+- it is now caught as a runtime error (in verification) if an attempt
+ is made to perform a rendez-vous operation within a d_step sequence.
+- it is also caught as an error if a 'timeout' statement is used inside
+ a d_step sequence (it may be used as the first statement only)
+- an else statement that appears jointly with i/o statements in
+ a selection structure violates the rules of a partial order
+ reduction. a warning is now included if this combination is seen, both
+ at compile-time and at run-time. the combination should probably be
+ prevented alltogether since its semantics are also not always clear.
+- one more correction to the implementation of 'else' inside dsteps
+ to avoid a possibly unjustified error report during verification
+- a previously missed case of the appearance of multiple 'else' statements
+ in selection structures is now caught and reported
+- fixed a missing case were process numbers (_pid's) did not match
+ between a verification run and a guided simulation run. (if a never
+ claim was used, there could be an off-by-one difference.)
+- rendez-vous operations are now offered as a choice in interactive
+ simulation mode only when they are executable - as they should be
+- the introduction of active proctypes in 2.5 introduced an obscure
+ line-number problem (after the first active proctype in a model
+ that contains initialized local vars, the line numbers could be
+ off). it's fixed in this version.
+
+Extensions
+- The main extension added in this release is the inclusion of an
+ algorithm, due to Gerth, Peled, Wolper and Vardi, for the translation
+ from LTL formulae into Promela never claims. The new options are
+ supported both in SPIN directly, and from XSPIN's interface.
+
+- added the option to specify an enabling condition for each
+ proctype. the syntax is as follows:
+ proctype name(...) provided ( expression )
+ {
+ ...
+ }
+ where the expression is a general side-effect free expression
+ that may contain constants, global variables, and it may contain
+ the predefined variables timeout and _pid, but not other local
+ variables or parameters, and no remote references.
+
+ the proctype may be declared active or passive. no enabling
+ conditions can be specified on run statements unfortunately.
+ the provided clauses take effect both during simulation runs
+ and during verification runs.
+
+- extended XSPIN with a barchart that can give a dynamic display
+ of the relative fraction of the system execution that is used by
+ each process. modified the layout of the Simulation option panel,
+ to make some of the runtime display panels optional.
+
+- added a <Clear> button to most text windows (to clear the contents
+ of the display)
+
+- added <Larger> and <Smaller> scaling buttons to canvas displays
+ (that is: the FSM view displays and the MSC display). the buttons
+ show up when the image is complete (e.g., when the message sequence
+ chart has been completed) - so that it can be scaled to size as a
+ whole.
+
+- added new <Help> buttons, with matching explanations, to most display
+ panels - and updated and extended the help menus.
+
+===== 2.7 - 3 August 1995 ====
+
+- fixed possible memory allignment problem on sun sytems
+- several fine tunings of xspin
+- made it possible to let the vectorsize get larger than 2^15 bytes
+ to support very large models
+- a provided clause has no effect inside d_step sequences, but it
+ is looked at during atomic sequences. this rule is now enforced
+ equally in simulation and validation.
+- the use of enabled() and pc_value() outside never claims now triggers
+ a warning instead of an error message (i.e., it is now accepted).
+ in models with synchronous channels, the use of enabled() still makes
+ verification impossible (both random and interactive simulations will
+ work though)
+- added an option to `preserve' a message sequence chart across
+ simulation runs (by default, all remnants of an old run are removed)
+
+===== 2.7.1 - 9 August 1995 ====
+
+- removed bug in the code that implements the compile time directive
+ to verifier source -DNOFAIR, which was introduced in version 2.5,
+ (and picked up starting with version 2.6 also via xspin).
+
+===== 2.7.2 - 14 August 1995 ====
+
+- the LTL formula parser accidentily swapped the arguments of U and V
+ operators. it does it correctly now. the associativity of U and
+ V now defaults to left-associative.
+- arithmetic on channel id's was so far silently condoned.
+ it is now flagged as an error. it is still valid to say: c = d,
+ if both c and d are declared as chan's, but d may no longer be
+ part of an expression.
+- unless operators now distribute properly to (only) the guard of
+ d_step sequences (but not into the body of the d_step).
+
+===== 2.7.3 - 15 August 1995 ====
+
+- tweek in implementation of provided clauses - to make it possible
+ to use `enabled()' inside them.
+
+===== 2.7.4 - 25 September 1995 ====
+
+- fixed a small omission in the implementation of dsteps
+- allowed `else' to be combined with boolean channel references
+ such as len and receive-poll
+- added some compiler directives for agressively collapsing
+ the size of state vectors during exhaustive explorations.
+ this option is still experimental
+- made some cosmetic changes in the look and feel of xspin
+
+===== 2.7.5 - 7 October 1995 ====
+
+- the value returned by a run statement triggered and
+ unwarranted error report from spin, if assigned to a variable
+- xspin didn't offer all possible choices in the menus, when
+ used in interactive simulation mode
+- somewhat more careful in closing file descriptors once they
+ are no longer needed (to avoid running out on some systems)
+- some small cosmetic changes in xspin (smaller arrows in the
+ message sequence chart - to improve readability)
+
+===== 2.7.6 - 8 December 1995 ====
+
+- added a compiler directive PC to allow for compiling the
+ SPIN sources for a PC. (this is only needed in the makefile
+ for spin itself - not for the pan.? files.)
+ if you generate a y.tab.c file with yacc on a standard Unix
+ machine, you must replace every occurrence of y.tab.[ch]
+ with y_tab.[ch] in all the spin sources as well before compiling.
+ some of the source file names also may need to be shortened.
+- some syntax errors reported by spin fell between the cracks
+ in xspin simulations. they are now correctly reported in the
+ simulation window and the log window.
+- in interactive simulation mode - typing `q' is now a recognized
+ way to terminate the session
+- option -jN (skip output for the first N steps) now also works
+ for interactive simulations. the first N steps are then as in
+ a random simulation.
+- FSMview in xspin is updated to offer also the statemachine
+ view for never claims - and to suppress the one for an init
+ segment, if none is used
+- fixed a bug in implementation of hidden structures - some of
+ the references came out incorrectly in the pan.c code
+
+===== 2.7.7 - 1 February 1996 ====
+
+- made it possible to combine the search for acceptance cycles
+ with one for non-progress cycles, to find cycles with at least
+ one accepting state and no progress states.
+ the combined search is compatible with -f (weak fairness).
+ [note added at 2.8.0 -- this wasn't completely robust, and
+ therefore undone in 2.8.0]
+- added the explicit choice for the specification of a positive
+ or a negative property in LTL interface to xspin
+- fixed a bug in the ltl translator (it failed to produce the correct
+ automaton for the right-hand side of an until-clause, if that clause
+ contained boolean and-operators)
+- in non-xspin mode, process states are now identified as <endstates>
+ (where applicable) in the simulation trails
+- it is now flagged as an error if a `run' statement is used
+ inside a d_step sequence
+- added two new options ( -i and -I ) for the generated verifiers
+ (pan -i / pan -I). recommended compilation of pan.c is with -DREACH
+ for exhaustive mode (-DREACH has no effect on BITSTATE mode).
+ option -i will search for the shortest path to an error.
+ the search starts as before - but when an error is found, the
+ search depth (-m) is automatically truncated to the length of that
+ error sequence - so that only shorter sequences can now be
+ found. the last sequence generated will be the shortest one possible.
+ option -I is more aggressive: it halves the search depth
+ whenever an error is found, to try to generate one that is at most
+ half the length of the last generated one.
+ if no errors are found at all, the use of -i or -I has no effect on
+ the coverage of search performed (but the effect of using -DREACH
+ is an increase in memory and time used, see the notes at version 2.2).
+
+===== 2.7.8 - 25 February 1996 ====
+
+Extensions
+- a new runtime option on the verifiers produced by Spin is -q
+ it enforces stricter conformance to what is promised in the book.
+ by default, the verifiers produced by Spin require each process to have
+ either terminated or be in a state labeled with an endstate-label, when
+ the system as a whole terminates. the book says that for such a state
+ to be valid also all channels must be empty. option -q enforces that
+ stricter check (which is not always necessary). the option was suggested
+ by Pim Kars of Twente Univ., The Netherlands.
+- `mtype' is now a real data-type, that can be used everywhere a `bit'
+ `byte' `short' or `int' data-type is valid.
+ variables of type `mtype' can be assigned symbolic values from the range
+ declared in mtype range definitions (i.e.: mtype = { ... }; ).
+ the value range of an mtype variable (global or local) remains equal
+ to that of a `byte' variable (i.e., 0..255).
+ for instance:
+ mtype = { full, empty, half_full };
+ mtype glass = empty;
+ the extension was suggested by Alessandro Cimatti, Italy.
+- the formfeed character (^L or in C-terms: \f) is now an acceptable
+ white space character -- this makes it easier to produce printable
+ promela documents straight from the source (also suggested by Cimatti).
+- a new predefined and write-only (scratch) variable is introduced: _
+ the variable can be assigned to in any context, but it is an error to
+ try to read its value. for instance:
+ q?_,_,_; /* to remove a message from q */
+ _ = 12; /* assign a value to _ (not very useful) */
+ the value of _ is not stored in the state-vector
+ it may replace the need for the keyword `hidden' completely
+ the syntax was suggested by Norman Ramsey (inspired by Standard ML)
+
+Fixes
+- the FSM-view mode in xspin wasn't very robust, especially when moving
+ nodes, or changing scale. it should be much better now. button-1 or
+ button-2 can move nodes around. click button-1 at any edge to see
+ its edge label (it no longer comes up when you hover over the edge -
+ the magic point was too hard to find in many cases).
+- fixed bug in processing of structure names, introduced in 2.7.5, caught
+ by Thierry Cattel, France.
+- trying to pass an array hidden inside a structure as a parameter
+ to a process (in a run statement) was not caught as a syntax error
+- trying to pass too many parameters in same also wasn't caught
+- in interactive mode, the menus provided by xspin were sometimes
+ incorrect for interactions in a rendez-vous system, caught by Pim Kars.
+
+===== 2.8.0 - 19 March 1996 ====
+
+- new version of the Spin sources that can be compiled, installed, and
+ used successfully on PC systems running Windows95.
+ (Because of remaining flaws in the latest Tcl/Tk 7.5/4.1 beta 3
+ release, Xspin will not run on Windows 3.1 systems. Spin itself
+ however will work correctly on any platform.)
+ to compile, you'll need the public domain version of gcc and yacc for PCs,
+ (see README.spin under Related Software, for pointers on where to
+ get these if you don't already have them)
+
+ to use Spin on a PC, compile the Spin sources with directive -DPC
+ (if you have no `make' utitility on the PC, simply execute the
+ following three commands to obtain a spin.exe
+ byacc -v -d spin.y
+ gcc -DPC *.c -o spin
+ coff2exe spin
+ alternatively, use the precompiled spin.exe from the distribution
+ (contained in the pc_spin280.zip file)
+
+- small extension of xspin - lines with printf("MSC: ...\n") show up
+ in xspin's graphical message sequence chart panel. the colors can now
+ also be changed from the default yellow to red green or blue,
+ respectively, as follows;
+ printf("MSC: ~R ...\n")
+ printf("MSC: ~G ...\n")
+ printf("MSC: ~B ...\n")
+ (suggested by Michael Griffioen, The Netherlands)
+- small changes to improve portability and ANSI compliance of the C code
+- compile-time option -DREDUCE (partial order reduction) is now the
+ default type of verification. both spin and xspin now use this default.
+ to compile a verifier without using reduction, compile -DNOREDUCE
+- missed case of syntax check for use of _ as lvalue corrected
+- missed case of printing mtype values in symbolic form also corrected
+- printfs no longer generate output during verification runs
+ (this was rarely useful, since statements are executed in DFS search
+ order and maybe executed repeatedly in seemingly bewildering order)
+ a compile time directive -DPRINTF was added to suppress this change
+ (this may be useful in debugging, but in little else)
+ this relies on stdarg.h being available on your system - if not, compile
+ Spin itself with -DPRINTF, and the new code will not be added.
+
+===== 2.8.1 - 12 April 1996 ====
+
+- we found a case where the partial order reduction method
+ introduced in Spin version 2.0 was not compatible with
+ the nested depth-first search method that Spin uses for
+ cycle detection (i.e., runtime options -a and -l)
+ the essence of the problem is that reachability properties
+ are affected by the so-called `liveness proviso' from the
+ partial order reduction method. this `liveness proviso'
+ acted on different states in the 1st and in the 2nd dfs,
+ which could affect the basic reachability properties of states.
+ the problem is corrected in 2.8.1. as a side-effect of this
+ upgrade, a few other problems with the implementation of the
+ nested depth first searches were also corrected. in some cases
+ the changes do cause an increase of memory requirements.
+ a new compiler directive -DSAFETY is added to make sure that
+ more frugal verifiers can be produced if liveness is not required.
+- other fixes - one small fix of interactive simulation mode -
+ some small fixes to avoid compiler warnings in tl_parse.c -
+ a fix in pangen3.h to avoid a compile-time error for a few
+ cases of index bound-checking. small fixes in tl_buchi and
+ tl_trans to correct treatment of "[]true" and "[]false"
+- cosmetic changes to the xspin tcl/tk files
+
+===== 2.8.2 - 19 April 1996 ====
+
+- revised Doc/WhatsNew.ps to match the extensions in the
+ current version of the software
+- one more change to the algorithm for acceptance cycle detection
+- changed filenames for multiple error-trails (to ease use on PCs)
+ from spec.trail%d to spec%d.trail
+- some small changes to improve portability
+
+===== 2.8.3 - 23 April 1996 ====
+
+- corrected a missed optimization in the new acceptance cycle detection
+ algorithm from 2.8.[12]
+- corrected a problem with blocking atomic sequences in pangen1.h
+- added a predefined function predicate np_
+ the value of np_ is true if the system is in a non-progress state
+ and false otherwise. (claim stutter doesn't count as non-progress.)
+ np_ can only be used inside never claims, and if it is used all
+ transitions into and out of progress states become visible/global
+ under the partial order reduction
+- background:
+ the intended purpose of np_ is to support a more efficient check
+ for the existence of non-progress cycles. (the efficient check we
+ had was lost with the changes from 2.8.[12]) instead of the default
+ check with runtime option -l, a more efficient method (under partial
+ order reduction) is to use the never claim:
+ never {
+ /* non-progress: <>[] np_ */
+ do
+ :: skip
+ :: np_ -> break
+ od;
+ accept: do
+ :: np_
+ od
+ }
+ and to perform a standard check for acceptance cycles (runtime
+ option -a) -- the partial order reduction algorithm can optimize
+ a search for the existence of acceptance cycles much better than
+ one for non-progress cycles.
+ a related advantage of searching for non-progress cycles with an
+ LTL property is that the LTL formula (<>[] np_) can easily be
+ combined with extra LTL properties, to built more sophisticated
+ types of searches.
+
+===== 2.8.4 - 25 April 1996 ====
+
+- cycles are closed at a different point in the dfs with the change from
+ 2.8.[12], as a result, the cycle-point was no longer accurate - which
+ could be confusing. fixed
+- all moves through progress states and accepting states within the program
+ are visible under the partial order reduction rules. it is unlikely that
+ one would use extra accept labels in a program, if an LTL property or a
+ never claim with accept labels is used, but just in case, this is covered.
+
+===== 2.8.5 - 7 May 1996 ====
+
+- process creation and process deletion are global actions
+ they were incorrectly labeled as safe/local actions for the
+ purposes of partial order reduction. they are global, because
+ the execution of either one can change the executability of
+ similar actions in other processes
+- didn't catch as an error when too many message parameters are
+ specified in a receive test q?[...] (in verifications).
+ it was reported in all other cases, just not for queue tests
+ (the simulator reported it correctly, when flag -r is used)
+- peculiar nestings of array and non-array structure elements
+ could generate incorrect code
+- with fairness enabled (-f), cycles were sometimes closed at the
+ wrong place in the runtime verifiers.
+- a variable initialized with itself could cause spin to go into
+ an infinite loop. the error is now caught properly.
+
+===== 2.8.6 - 17 May 1996 ====
+
+- timeout's weren't always marked as global actions in partial
+ order reductions - they are now -- this can cause a small increase
+ in the number of reached states during reduced searches
+- fixed error that could cause a coredump on a remote reference
+ during guided simulations (reported by Joe Lin, Bellcore)
+- improved the efficiency of partial order search for acceptance
+ cycles in bitstate mode. (for non-progress cycles, though, we still
+ can't take much advantage of reduction during bitstate searches)
+- fixed the error that caused the extent of a cycle not to be
+ marked correctly in bitstate mode (the trails were always correct,
+ but the cycle point was sometimes placed incorrectly)
+- fixed error that could cause non-existent acceptance cycles to
+ be produced in bitstate mode (hopefully the last aftershock from
+ the correction of the cycle detection method in version 2.8.1)
+
+===== 2.9.0 - 14 July 1996 ====
+
+- Important Change:
+ Spin now contains a predefined never claim template that captures
+ non-progress as a standard LTL property (it is the template described
+ under the notes for 2.8.3 above)
+ this made it possible to unify the code for -a and -l; it brings
+ option -l (non-progress cycle detection) within the same automata
+ theoretic framework as -a; and it secures full compatibility of
+ both options -a and -l with partial order reduced searches.
+
+ compiled versions of pan.c now support *either* -a *or* -l, not both
+
+ by default, the verifiers check for safety properties and standard
+ buchi acceptance (option -a).
+ to search for non-progress cycles (i.e., to *replace* option -a with
+ option -l), compile the verifier with the new directive -DNP
+- Xspin 2.9.0 supports this change, and makes it invisible to the user.
+
+- the state vector length is now added explicitly into the state vector.
+ in virtually all cases this is redundant, but it is safer.
+ it can optionally be omitted from the state vector again (saving
+ 4 bytes of overhead per state) with the new compiler directive -DNOVSZ
+
+- the verifiers didn't allow a d_step sequence to begin with a
+ rendez-vous receive operation. that's now fixed.
+
+- change in the as-yet non-documented mode for extra agressive
+ state compressions (added in version 2.7.4, not enabled yet for
+ normal use - more information on this mode will come later)
+
+- assignments to channel variables can violate xr/xs assertions.
+ there is now a check to catch such violations
+
+- updated the PC executable of xspin for the newer current version of
+ gcc - updated the readme files to match the above changes
+
+- added the code for handling the Next Operator from LTL. the code is
+ not yet enabled (to enable it, compile the sources with -DNXT added).
+ note that when partial order reduction is used this operator cannot
+ be used. we'll figure out the appropriate warnings and then enable
+ the code (i.e., when you use it, you must compile pan.c with -DNOREDUCE).
+- in the process of this update, we also caught a bug in the translation
+ of LTL formulae into never claims (affecting how the initial state of
+ the claim was encoded). the implementation has meanwhile been subjected
+ to a more thorough check of the correctness of the translation -- using
+ another model checker (cospan) as a sanity check. (both model checkers
+ have passed the test)
+
+===== 2.9.1 - 16 September 1996 ====
+
+- no major changes - some purification and minor fixes
+- updated email address for bug reports to bell-labs.com in
+ all source files
+- disallowed remote references inside d_step sequences
+ (the verifier has no problem, but the simulator may
+ resolve these differently can cause strange guided
+ simulation results)
+- provided some missing arguments to a routine in pangen1.c
+ that could trigger compile time errors before
+- improved the COLLAPSE modes to be slightly more frugal
+- added explicit casts from shorts to ints to avoid warnings
+ of some compilers... also fixed a possible bad reference
+ to the stack if an error is found in the first execution step
+- fixed a case where the cycle-extent wasn't set correctly
+ (found by stavros tripakis)
+- write and rewrite just a single trail-file for options -[iI]
+ (instead of numbered trails)
+- fixed a bug in weak fairness cycle detection that had crept
+ in with the overhaul from version 2.8
+- fixed order of variable initialization in simulator (can
+ make a difference if a local variable is initialized with
+ the value of a parameter, which should now work correctly)
+- expanded the number of options accessible through Xspin
+
+===== 2.9.2 - 28 September 1996 ====
+
+- with a -c0 flag, the 2.9.1 verifiers would still stop at the
+ first error encountered, instead of ignoring all errors.
+ has been corrected (-c0 means: don't stop at errors)
+- corrected the instructions and the numbers in Test/README.tests
+ for the current version of spin (for leader and pftp there are
+ some small differences)
+
+===== 2.9.3 - 5 October 1996 ====
+
+- added a function eval() to allow casting a variable name into
+ a constant value inside receive arguments. this makes it possible
+ to match a message field with a variable value, as in
+ q?eval(_pid),par2,par3
+ until now, only constant values could be matched in this way.
+ note that in the above example the value of _pid does not change,
+ but it guarantees that the receive is unexecutable unless the first
+ message parameter is equal to the value of variable _pid
+ eval() can be used for any message field - the argument must be a
+ (local or global) variable reference (not be a general expression).
+- in the process, discovered that global references inside the parameter
+ list of send or receive statements would not be detected for the
+ marking of safe and unsafe statements for the partial order reduction.
+ this is now corrected - it may lead to a small increase in the number
+ of reachable states under partial order reduction
+
+===== 2.9.4 - 4 November 1996 ====
+
+- the order of initialization of parameters and local variables after
+ a process instantiation was incorrect in the verifier - this could
+ be noticed when a local var was instantiated with a formal parameter
+ inside the variable declaration itself (the verifier failed to do this).
+- added a missing case for interpreting eval() in run.c (see 2.9.3)
+- removed possible erroneous behavior during interactive simulations
+ when a choice is made between multiple rendez-vous handshakes
+- added SVDUMP compiler directive and some extra code to allow for the
+ creation of a statespace dump into a file called sv_dump
+- added an option in Xspin to redraw the layout of an FSM-view using
+ the program 'dot' -- the option automatically enables itself if xspin
+ notices that 'dot' is available on the host system (an extra button
+ is created, giving the redraw option)
+
+===== 2.9.5 - 18 February 1997 ====
+
+- thoroughly revised -DCOLLAPSE -- it can now be used without
+ further thought to reduce memory requirements of an exhaustive run
+ by up to 80-90% without loss of information. the price is an
+ increase in runtime by 2x to 3x.
+- added new compiler directives -DHYBRID_HASH and -DCOVEST
+ (both for experimental use, see the Pan.Directives file)
+- renamed file sv_dump (see 2.9.4) to 'spec'.svd, for compatibility
+ with PCs
+- removed spin's -D option (dataflow). it was inaccurate, and
+ took up more source code than warranted (300 lines in main.c and
+ another 60 or so in Xspin)
+
+===== 2.9.6 - 20 March 1997 ====
+
+- bug fix -- for vectorsizes larger than 1024 the generated
+ code from 2.9.5 contained an error in the backward execution
+ of the transition for send operations. (this did not
+ affect the verification unless a compiler directive -DVECTORSZ=N
+ with N>1024 was used -- which triggered an error-report)
+- sometimes one may try typing 'pan -h' instead of 'pan -?'
+ to get the options listing of the verifiers. this now gives
+ the expected response.
+- previous versions of the spin Windows95 executable in pc_spin*.zip
+ were compiled as 16-bit executable -- the current version is a
+ 32-bit executable. the newer versions of tcl/tk actually care
+ about the difference and will hang if you try to do a simulation
+ run with one of the older spin executables installed...
+- discrepancy in the stats on memory use reported at the end of a
+ bitstate run corrected.
+- new xspin295 file that corrects a problem when xspin is used on
+ unix systems with a file name argument (it reported an undeclared
+ function).
+
+===== 2.9.7 - 18 April 1997 ====
+
+- spin now recognizes a commandline option -P that can be used to
+ define a different preprocessor. the default behavior on Unix
+ systems corresponds to:
+ spin -P/lib/cpp [..other options..] model
+ and on solaris systems:
+ spin -P/usr/ccs/lib/cpp [..other options..] model
+ (note, these two are the defaults, so these are just examples)
+ use this option to define your own preprocessor for Promela++ variants
+- bitstate mode can be made to hash over compressed state-vectors
+ (using the byte-masking method). this can improve the coverage
+ in some cases. to enable, use -DBCOMP
+- -DSVDUMP (see 2.9.4) now also works in -DBITSTATE mode
+- added compiletime option -DRANDSTORE=33 to reduce the probability of
+ storing the bits in the hasharray in -DBITSTATE mode to 33%
+ give an integer value between 0 and 99 -- low values increase
+ the amount of work done (time complexity) roughly by the reverse
+ of the probability (i.e., by 5x for -DRANDSTORE=20), but they also
+ increase the effective coverage for oversized systems. this can be
+ useful in sequential bitstate hashing to improve accumulative coverage.
+- combined the 3 readme-files into a single comprehensive README.
+
+===== 3.0.0 - 29 April 1997 ====
+
+- new additions to Spin's functionality that motivates upping the
+ version number to 3.0:
+
+ 1. a new BDD-like state compression option based on
+ the storage of reachable states in an automaton,
+ combined with a checkpoint/recovery option for long runs.
+ for the automata based compression, compiled -DMA=N
+ with N the maximum length of the statevector in bytes
+ expected (spin will complain if you guess too low)
+ for checkpointing, compile -DW_XPT
+ to get a checkpoint file written out every multiple
+ of one million states stored
+ for restarting a run from a checkpoint file, compile -DR_XPT
+
+ 2. addition of "event trace" definitions. for a description
+ see Section 4 of the extended WhatsNew.ps
+
+ 3. addition of a columnated simulation output mode
+ for raw spin that mimicks the view one could so
+ far only obtain with through the intermediacy of
+ xspin. to use, say:
+ spin -c spec (or spin -t -c spec)
+ there is one column per running process. message send
+ or receive events that cannot be associated with a process
+ for any reason are printed in column zero.
+
+ 4. addition of a Postscript output option to spin.
+ this can be used to create a postscript file for a message
+ flow of a simulation, without needing the intervention of
+ xspin (which can be slow).
+ spin -M spec
+ generates the message flow in file spec.ps
+ also supported is:
+ spin -t -M spec
+ to convert an error trail into postscript form.
+
+ 5. addition of the ability in Xspin to automatically
+ track variable values -- by prefixing their declaration
+ in Promela with the keyword "show", e.g. "show byte cnt;"
+ also added: automatic tracking of the state changes in
+ the never claim, if present, during guided simulations
+ (i.e., when inspecting an error.trail produced by the
+ verifier)
+
+ 6. addition of an option to convert LTL formula stored in files.
+
+ 7. Xspin is now compatible with Windows95 and WindowsNT
+
+smaller, changes
+ - spin generates hints when the data-type of a variable is
+ over-declared (i.e., it will detect the use of integers for
+ storing booleans etc.)
+ - the spin -d output for structure variables now includes the
+ name of the structure declaration (as the 3rd field, which
+ was unused in this case) to make the listings unambiguous.
+ [change from Frank Weil]
+ - spin -t can now take an argument. without an argument
+ spin -t spec opens spec.trail
+ spin -t4 opens spec4.trail
+ (multiple trails are generated with runtime option
+ pan -c0 -e)
+ - bugfix: local channels were not always restored correctly to
+ their previous state on the reverse move of a process deletion
+ in the verification (i.e., the deletion of the process to which
+ those channels were local).
+ - bugfix: stutter moves were only done in the 2nd dfs, to close
+ cycles. this has to be done in both 1st and 2nd, to avoid missing
+ the valid stutter extension of some finite behaviors
+ - process death is now a conditionally safe action -- this partly
+ reverses a decision made in version 2.8.5.
+ the condition for safety is: this is the youngest process and
+ there are fewer than the max nr of processes running. this means
+ that the action cannot enable any blocked run statements, although
+ it may enable more process deaths (i.e., of processes that now
+ become youngest).
+ it does imply that a process dies as quickly as possible. allowing
+ them to also linger merely creates articifial execution scenarios
+ where the number of active processes can grow without bound.
+ compatibility with 2.8.5-2.9.7 on this issue can be forced by
+ compiling pan.c with -DGLOB_ALPHA
+ - atomics inside atomics or dsteps are now accepted by the parser,
+ and ignored.
+ - there is now a Syntax-Check option in Xspin
+ [suggested by Klaus Havelund]
+ - true and false are now predefined constants
+
+Subsequent updates will appear in a new file: V3.Updates
--- /dev/null
+Distribution Update History of the SPIN sources
+===============================================
+
+==== Version 3.0.0 - 12 August 1997 ====
+
+A new release, a new V3.Updates file. See the end
+of the V2.Updates file for the main changes between
+the last version 2.9.7 and the new version 3.0.0.
+
+Spin Version 1 lasted from Jan. 1990 - Jan. 1995.
+Spin Version 2 lasted from Jan. 1995 - August 1997.
+
+The shell script upgrade2 will take any version of
+Spin between version 2.7 and 2.9 to version 3.0.
+Upgrades from 3.0 forward will appear in a new shell
+script upgrade3, to keep the file small.
+
+==== Version 3.0.1 - 19 August 1997 ====
+
+- on older PC's the hashing behavior could be substandard.
+ on those systems an int is often interpreted as a 16 bit,
+ instead of a 32-bit quantity. the fix made is to declare
+ the relevant variables as long integers instead of plain
+ integers. there is no visible difference for other systems.
+- printf accidentily picked up a redundant newline in 3.0.0
+ it is gone again.
+- interactive use of spin models with rendez-vous statements
+ could get hung in some cases.
+
+==== Version 3.0.2 - 24 August 1997 ====
+
+- improved the fix for interactive use of rv's from 3.0.1
+- the parser now catches the use of 'run' to initialize
+ global variables as an error.
+- the parser now catches the use of any initializer on
+ a formal parameter in a proctype as an error.
+- addition of a new datatype to Promela: unsigned
+ usage:
+ unsigned name : 3;
+ declares 'name' to be a variable that can hold unsigned
+ values -- stored in 3 bits (i.e., values 0..7 inclusive).
+ values outside the declared range are truncated to the
+ range on assignments
+- d_step may now appear inside and atomic and vice versa
+- extra option -E to pass arguments to the C preprocessor
+ usage:
+ spin -E-Dfoo=faa filename
+ to redefined foo as faa in the filename
+ spin -Pmy_cpp -E-E filename
+ use my_cpp as the preprocessor (replacing cpp) and
+ pass it flag -E when it is called.
+
+==== Version 3.0.3 - 8 September 1997 ====
+
+- unsigned variables weren't cast correctly during
+ simulation runs --
+- warnings about variables being of too generous a type
+ are now only generated when the -v verbose option is set
+- extra warnings, on use of channels, are now also
+ generated with spin -v -- still more with spin -v -g
+- can now pass directives to the preprocessor with a simpler
+ spin option -D..., e.g., spin -DLOSS=1 spec
+ the earluer -E-D... also still works
+- a few more additions to xspin303.tcl
+
+==== Version 3.0.4 - 25 October 1997 ====
+
+- now accepts truncated extensions of pan.trail
+ (often visible only as pan.tra) on PCs
+- now recognizes compiler directive __FreeBSD__
+- redundant include file <malloc.h> deleted from main.c
+- now properly initializes all channels hidden in typedef
+ structures
+- made it possible to generate structural views of the
+ promela model, but tracking channel uses (more to come)
+- added pc_zpp.c to the sources - used only on the pc
+
+==== Version 3.0.5 - 5 November 1997 ====
+
+- corrected bug in the builtin macro preprocessor of the
+ PC-version (only) of spin. if the first literal match
+ of the macro source failed to be a valid replacement string,
+ no further matches were tried on that line
+- corrected bug in interactive simulation mode that could
+ cause a failure to return control to the user
+
+==== Version 3.0.6 - 16 December 1997 ====
+
+- a value that is truncated in an assignment to a variable
+ of a small type triggered an error message that sometimes
+ could cause xspin to miss a display update for the variable
+ values pannel.
+- on a -c flag spin was a little too talkative, assuming also
+ a -v verbose flag for the final detail printed at the end of
+ a simulation run.
+- fixed an error in the processing of logical OR in the presence
+ of the X operator in LTL formulae -- this only affected the
+ outcome of a translation if Spin was compiled with -DNXT
+ to enable the LTL next-time operator (this is not enabled by
+ default, because it jeopardizes compatibility with the partial
+ order reductions)
+- a check for non-progress, in combination with provided clauses
+ on proctypes, could fail. the omission was that the never claim
+ process searched for its own provided clause, which should in
+ this case default to true.
+- the restriction that the use of any provided clause voided the
+ partial order reduction was much too strict: it suffices to mark
+ all statements in only the proctype that is labeled with a
+ provided clause unsafe -- other processes are not affected.
+- added new Test/pathfinder example to the Test directory,
+ illustrating the use of provided clauses
+- the standard stutter extension on finite sequences is not
+ allowed to produce non-progress cycles, but in combination with
+ the weak-fairness option this erroneously could happen.
+ (stutter-extension on temporal claim matches is only applied
+ to standard acceptance properties, under runtime option -a)
+- there was an error in the implementation of weak fairness
+ that could cause the algorithm to miss matching acceptance or
+ non-progress cycles with weak-fairness enabled. a small change
+ in the implementation of this option (incrementing the Choueka
+ counter values by 1) repairs this flaw.
+
+==== Version 3.0.7 - 18 December 1997 ====
+
+- the check on a self-loop, added in 3.0.6, unintentionally also
+ ruled out self-loops in never claims, which are harmless (e.g.,
+ to allow for a finite prefix of 'true' propositions).
+
+==== Version 3.0.8 - 2 January 1998 ====
+
+- with fairness enabled, a process was considered to be temporarily
+ blocked while other processes performed a rv handshake. this
+ could cause a cycle to be reported as fair that normally would not
+ be considered as such. fairness rule 2 was updated to avoid this.
+- an assignment beginning a dstep sequence was incorrectly considered
+ to be executable in the middle of a rendezvous handshake in progress
+ elsewhere.
+
+==== Version 3.0.9 - 11 January 1998 ====
+
+- rendezvous communications lacked an arrow in the new postscript
+ output generated with spin option -M
+- new predefined channel name STDIN for reading a character from
+ the standard input as in:
+ chan STDIN;
+ short c;
+ do
+ :: STDIN?c ->
+ if
+ :: c == -1 -> /* EOF */
+ break
+ :: else ->
+ printf("%c", c)
+ fi
+ od
+- to match this, added support for recognizing character
+ symbols in Promela such as 'i', '\n', '\t', '\r', etc.
+- fixed the bug that prevented weak fairness from being
+ turned off during verifications.... (bug introduced in 3.0.8)
+- small improvements in error catching (mostly related to
+ illegal structure references)
+
+==== Version 3.1.0 - 27 January 1998 ====
+
+- all transitions from a local process state that is referenced
+ within the never claim (or ltl property) are now properly labeled
+ as unsafe in the partial order reduction
+- a d_step that appears at the last statement in a proctype no longer
+ generates an error in the simulator
+- the predefined variable _last is now updated correctly during the
+ verification process (thanks Pedro Merino for the examples)
+- weak fairness is now considered incompatible with partial order reduction
+ in models that contain rendezvous operations (thanks Dennis Dams for
+ the example that revealed this)
+
+==== Version 3.1.1 - 28 January 1998 ====
+
+- fixed a goof in pc_zpp.c -- only visible in the precompiled PC
+ version. symptom: it would fail to expand some macros with the
+ builtin version of cpp. in particular, it would fail on the
+ testcase: Test/leader from the distribution (thanks Mike Ferguson).
+
+==== Version 3.1.2 - 14 March 1998 ====
+
+- added a Cut/Copy/Paste meny to the text window of xspin version 3.1.2
+ (big convenience), plus a few smaller fixes
+- the verifiers generated by spin have two extra run-time options:
+ -E to ignore all invalid endstate errors
+ -A to ignore all assert() violations
+- added #include <strings.h> to pan.c
+- main in pan.c is now properly typed 'int' instead of 'void'
+- the following change, introduced in 2.9.0, was unnecessary
+ - assignments to channel variables can violate xr/xs assertions.
+ there is now a check to catch such violations
+ the check is removed:
+ when an xr channel variable is assigned to, it's old value is simply lost.
+ it was the old value (operations on the channel that the value pointed
+ to) that the xr/xs assertion applied to, not to the variable name as such.
+ operations on the new channel id that the variable now points to
+ are subject to the same xr/xs claims as the old one did.
+- new argument to spin:
+ spin -N claimfile ... promelaspec
+ reads the never claim from 'claimfile'
+ (the main filename 'promelaspec' is always the last argument)
+- new argument to spin
+ spin -C promelaspec
+ prints some channel access info on stdout, useful for producing
+ a structural view of the system
+ (used to be an added output in spin -v)
+- fixed bug in pan.c that caused some states created during claim stutter
+ from not being removed from the dfs stack. should rarely have occured.
+- all the above extensions are supported in Xspin 3.1.2
+- redesigned Xspin's LTL properties management dialogue panel
+- fixed problem with hanging of long simulations on pc's
+ (a buffer overflow problem internal to windows95/nt)
+
+==== Version 3.1.3 - 16 March 1998 ====
+
+- small bug fix in sym.c -- reported too many variables as
+ unused on a spin -a -v spec
+- small bug fix in xspin312.tcl -- replaced "else if" with "elseif"
+- both bugs reported by Theo Ruys within hours after the release of 3.1.2
+ thanks Theo!
+
+==== Version 3.2.0 - 7 April 1998 ====
+
+- a modest extension of the language:
+ there is now a procedure-like construct that should reduce the need
+ for macros. Promela 'inline' functions preserve linenumber
+ references in simulations (at least, that's the idea).
+ an inline definition look like this (appearing outside all proctypes)
+
+ inline functionname(x, y) {
+ ...a promela fragment...
+ }
+
+ a call looks like this -- and can appear wherever a statement can appear:
+
+ functionname(4, a);
+
+ the replacement text is inlined by the parser, with proper parameter
+ matching and replacement.
+ inlines can be recursive (one inline can call another), but not cyclic.
+
+ there is still no local scope for variables. this means that the scope
+ of any local variable declared is always the entire proctype body --
+ no matter where it is declared.
+ local variables may be declared at the start of an inline -- but such
+ variables have the same status as a local variable at the place of the call.
+
+- added an example to the Test directory, illustrating inlines (Test/abp)
+
+- timeout is no longer automatically enabled and available as a
+ user-selectable option during interactive simulation. it could cause
+ counter-intuitive behavior, e.g. when the timeout was used in an unless-
+ escape
+- 'else' is now flagged as unexecutable when appropriate during interactive
+ simulations -- where possible it is offered as a choice so that an
+ execution can be forced in a given direction.
+- small fixes and fiddles with xspin
+
+==== Version 3.2.1 - 4 July 1998 ====
+
+- added compile time directive HC, for a version of Wolper's hash-compact
+ algorithm. it can speed up the search, and reduce memory requirements,
+ with a relatively low probability of hash-collisions (or missed states).
+ this is a modification of exhaustive search where we store a 32-bit
+ hash-value in the hash-tables, as a compressed state vector.
+ the effective size of the compressed state-vector is the width of the
+ hash-table itself (controlled by the runtime -w parameter) plus 32 bits
+ (by default this is: 18+32 = 50 bits of information).
+ the hash-table entries have some additional overhead which pushes total
+ memory usage slightly higher -- but the memory reductions can be quite
+ substantial (depending, trivially, on the length of the state vector
+ without compression)
+ to enable: compile pan.c with -DHC (perferably combined with -DSAFETY)
+- fixed fgets problem discovered by Theo Ruys
+ (problem: newlines could accidentily be added to the input text)
+- fixed two bugs in dstep code generated in pan.c; improved error reporting
+- fixed bug in processing of include files, when an ltl claim is used
+
+==== Version 3.2.2 - 21 July 1998 ====
+
+- generalized the hash-compact implementation
+ by default (compiling pan.c with -DHC) 6 bytes are stored:
+ 4 bytes from the first hash and 2 bytes from a second hash
+ this gives 32+16 = 48 bits of information, which should secure
+ a very low collision probability
+ alternatives are -DHC0 (for 32 bits) -DHC1 (for 40 bits) -DHC2 (48 bits)
+ and -DHC3 (56 bits).
+- reversed the order in which the transitions in a never claim are
+ generated -- this tends to shorten the counter-examples generated
+ (by putting the 'true' self-loops that at the end of the list, rather
+ than at the beginning). Thanks to Dragan Bosnacki.
+- fixed a bug in xspin.tcl that could cause the application to hang
+ when used on a PC (e.g., any simulation of leader...).
+ (this synchronization bug was introduced in 3.1.4.)
+
+==== Version 3.2.3 - 1 August 1998 ====
+
+- an atomic that ends with a jump into another
+ atomic sequence, now connects correctly without
+ breaking atomicity
+- the choice of a rendezvous partner for send operations
+ wasn't random during simulations (when multiple targets
+ for the rendezvous are available). it is now.
+- fix in xspin to avoid confusion between proctype names
+ with a common prefix, in rendering an automaton view
+- fix in pc_zpp.c for occasional incorrect comment processing
+ and incorrect #define processing (affected the PC version only)
+
+==== Version 3.2.4 - 10 January 1999 ====
+
+modifications:
+- replaced type "unsigned" in parameter to Malloc and emalloc
+ with "unsigned long long" to support 64 bit word size machines
+ (thanks to Judi Romijn's experiments at CWI)
+ (may not be recognized by non-ansi standard c-compilers)
+extensions:
+- added a runtime flag -J for both spin (simulations) and
+ for pan (verification runs), to specify that nested unless
+ clauses are to be evaluated in reverse order from the default
+ (to match java semantics of catch clauses) at the request
+ of Klaus Havelund.
+- added runtime flags -qN and -B for spin (simulations)
+ -q4 suppresses printing output related to queue 4
+ -B suppresses printing the final wrapups at the end of a run
+- added runtime flag -v for pan (verification) to add filenames
+ to linenumbers in the listings of unreached states (xspin does
+ not support these extensions yet)
+bug-fixes:
+- a very long source statement could overflow an internal
+ buffer in pan.c, upon the generation of a trail-file.
+ (thanks for Klaus Havelund's experiments with a java->spin
+ translator)
+- compilation with a vectorsize greater than 1024 could cause
+ the model checker to behave incorrectly in cases when receive
+ statements were used that received data into global variables
+ (instead of locals). this now works correctly.
+- removed bug in the optimization code of the ltl-translation
+ algorithm -- it could remove untils in cases such as
+ p /\ (q U r) not only if p==r, but also if p appeared within r
+- line numbers could be inaccurate if #if 0 ... #endif directives
+ were used inside inline declarations. corrected.
+- fixed a bug in ltl translation due to a failure to recognize
+ 'true' to be part of any 'and' form -- should have been a rare
+ occurrence.
+- fixed a bug that affected the use of rendezvous statements in
+ the guard of an escape clause of an unless
+
+==== Version 3.3.0 - 1 June 1999 ====
+
+- rearranged code to share code for identical cases
+ in pan.m and pan.b -- this reduces the file sizes by up
+ to 60% and similarly reduces compilation times for pan.c
+- added pan.c compiler directive MEMLIM
+ compiling pan.c with -DMEMLIM=N will restrict memory use
+ to N Megabytes precisely; this is an alternative to the
+ existing limit -DMEMCNT=N which restricts to 2^N bytes
+ and gives less precise control.
+- added new data declaration tag 'local' which can be used
+ wherever currently 'show' or 'hidden' can be used.
+ it allows one to identify global variables that are
+ effectively local (used by only 1 process) as data objects
+ of which manipulation is safe for partial order reductions.
+ there's no check for the validity of the tag during verification.
+- automatically hide unused or write-only variables
+ option can be turned off with spin option -o2
+- eval() (used in receive statements to turn a variable into
+ a constant value) can now contain an arbitrary expression,
+ instead of just a name (request of Victor Bos).
+- it is no longer an error to have multiple mtype definitions
+ (they are catenated internally)
+- more verbose error-trails during guided simulations - in verbose
+ mode it now includes explicit mention of never claim moves, if
+ a never claim is involved
+- added also an experimental option to generate code separately
+ for the main system and for the never claim - this makes
+ separate compilation possible. the option will be finetuned
+ and documented once it has settled. for the time being, they
+ are not listed in the usage listings.
+- also added, but not enabled, fledgling support for a bisimulation
+ reduction algorithm that might be applied to never claims to
+ reduce their size (inspired by work of Kousha Etessami),
+
+- bugfixes (the first two found by Wenhui Zhang):
+ - in fairness option (could miss a fair cycle)
+ - in implementation of the -DMA option (could incorrectly
+ claim an intersection of the 1st dfs stack an declare a
+ cycle when none existed)
+ - in the conversion of ltl formulae to automata (could
+ occassionaly fail to match common subexpressions)
+ - bug fix in the runtime code for random receive, fixed
+ - fixed execution of atomics during interactive simulation
+ - fixed possibly erroneous marking as 'dead' variables used
+ to index a structure variable array
+
+- during interactive simulation, to avoid confusion, choices
+ between different *escapes* on a statement are no longer offered
+ in user menus, but are now always resolved by the simulator
+- removed all uses of "long long" and replace with "long."
+ the former (temporarily used in 3.2.4) is not in ansi c,
+ and the latter will be interpreted correctly on 64bit machines
+ by a 64bit compiler.
+- added better support for 64bit machines -- avoiding deteriorated
+ performance of the hashing algorithms (courtesy Doug McIlroy)
+- the pc version could get the linenumber references wrong after
+ multiline comments - now repaired (courtesy Mike Ferguson)
+- removed bug in xspin.tcl that prevented the selection of
+ bitstate hashing from the ltl properties manager panel
+ (courtesy Theo Ruys)
+- added an option in xspin to exclude specific channels from the
+ msc displays (you have to know the channel number though)
+- fixes in the interactive simulation model (courtesy Judi Romijn)
+ - d_steps and atomics now always run to completion without
+ prompting the user for intermediate choices that could break
+ the atomicity (and the semantics rules).
+ - unless escapes no longer reach inside d_steps (they do reach
+ inside atomics)
+- merges sequences of safe or atomic steps -- a considerable speedup
+ this behavior can be disabled with spin option -o3
+- computes precondition for feasibility of rv - this option can be
+ enabled with spin option -o4 -- it seems of little use in practice
+- dataflow analysis -- can be disabled with spin option -o1
+- partial evaluation to remove dead edges from verification model
+ (i.e., with a constant 'false' guard)
+- added pan compile time option -DSC to enable new stack cycling option.
+ this will swap parts of deep stacks to a diskfile with only low overhead.
+ it needs no further user action to work -- the runtime -m flag
+ remains, but now simply sets the size of the part of the stack
+ that is in core (i.e., you need not set it explicitly unless you want to)
+- added pan compile time option -DLC to optinally use hashcompacted stackstates
+ during Bitstate runs. it is slower by about 20-30%, but in cases
+ where -DSC is used (very deep stacks) it can safe a lot of extra memory.
+ for this reason -DSC always enables -DLC by default
+
+==== Version 3.3.1 - 12 July 1999 ====
+
+- fix in pangen2.h, to avoid a null-pointer reference
+ in the automata preparation routines. it can occur in some cases
+ where progress labels are used in combination with p.o. reduction
+- fix for weak-fairness in combination with p.o. reduction and
+ unless/rendez-vous (courtesy Dragan Bosnacki)
+- fix to prevent an infinite cycle during the weak-fairness based
+ verifications. (when both the 2nd and the 1st dfs stacks are
+ intersected with a non-zero choueka counter value, the search
+ used to continue - instead this should be treated as a regular
+ stack match)
+- better feedback on spin -a when parts of the automaton are pruned
+ due to constant false guards
+- added spin option -w (extra verbose) to force all variable
+ values to be printed at every execution step during simulations
+
+==== Version 3.3.2 - 16 July 1999 ====
+
+- correcting an initially erroneous fix in 3.3.1 that prevented
+ compilation alltogether for sources generated through xspin. (...)
+ (it left a reference to counters used in the weak fairness algorithm
+ in the code that had to be suppressed if weak fairness isn't used)
+
+==== Version 3.3.3 - 21 July 1999 ====
+
+- fix in the new code for dataflow analysis. in some cases a core-dump
+ could result if a particular control-flow structure was encountered
+ (courtesy Klaus Havelund)
+- updated Xspin to 3.3.3 to deal with the new policy in 3.3 that printfs
+ during simulations are always indented by a number of tab-stops that
+ corresponds to the process number of the process that executes the
+ printf - this makes printfs from the same process line up in columns,
+ but it confused xspin. (fix courtesy of Theo Ruys)
+
+==== Version 3.3.4 - 9 September 1999 ====
+
+- new pan option -T to prevent an existing trail file from being
+ overwritten (useful if you run multiple copies of pan with
+ bitstate hashing and different -w parameters, to optimize chances
+ of finding errors fast -- the first run to write the trail file
+ then wins)
+- small improvement in error reporting for use of special labels inside
+ atomic and d_step sequences
+- small portability change to avoid problems with some compilers (e.g.
+ the ones used on plan9)
+- increased some statically defined maxima (e.g. for the max length of
+ a single statement - now increased to 2K bytes to avoid problems with
+ machine generated Promela files)
+
+==== Version 3.3.5 - 28 September 1999 ====
+
+- two bug-fixes in the ltl->never claim conversion (with thanks to
+ Heikki Tauriainen for reporting them)
+- increase in some static buffer sizes to allow for long
+ (typically machine generated) variable names
+- removed some debugging printfs
+
+==== Version 3.3.6 - 23 November 1999 ====
+
+- two small extensions and 4 important bug fixes
+
+- added runtime option -t to pan; using pan -tsuf will
+ cause error trails to be written into spec.suf instead of
+ spec.trail (which remains the default)
+- added a verbose output to the verification runs, to write
+ a line of output each time a new state in the never claim
+ is reached. this helps keeping track of progress in long
+ running verifications -- and helps to avoid false positives
+ (i.e., when most states in the never claim are unreached,
+ which is a strong indication that the LTL formula that
+ produced the claim isn't related to real behavior of the
+ system)
+
+- bug fix in the fairness algorithm (-f flag during verification)
+ that could cause false error reports to be generated
+- bug fix in the stack cycling compile-time option to pan.c (-DSC)
+ which could cause erroneous behavior of the verifier
+ (both of these reported by Muffy Calder and Alice Miller)
+- bug fix in the generation of never claims from LTL -- missing
+ parentheses around subexpressions in a guard
+- fix to circumvent buggy behavior from gcc on Unix platforms
+ (its version of sbrk can return memory that is not properly
+ word aligned -- which causes memory faults in pan)
+
+==== Version 3.3.7 - 6 December 1999 ====
+
+- 3.3.6 introduced a bug that prevented the verifier code
+ from compiling unless fairness was enabled -- corrected in 3.3.7
+- fixed a minor problem with the as yet unadvertised separate
+ compilation option (compiling the program separately from
+ the claim to speed up verifications of multiple claims)
+- fixed a bug in the simulation code that could make the
+ simulator miss executing statements. it could lead to
+ misleading traces for errors. (thanks to an example by Pim Kars)
+
+==== Version 3.3.8 - 1 January 2000 ====
+
+- fixed a bug in the simulation code that caused no output
+ to appear, for instance, when the traceback is done with
+ a guided simulation for the Test/loops testfile -- fixed
+- fixed bug in the generation of ltl formula of the type:
+ <>[]p && []<>q && []<>r
+ traced to a mistake in the comparison of states in the
+ buchi automaton that could unjustly claim two states to
+ be identical even if they differed (reported by Hagiya)
+- added a cast to (double) for manipulation of MEMLIM to
+ avoid problems with some compilers
+- added a small optimization that rids the spec of repeated
+ sequential skip statements, or skips immediately following
+ printfs (these may be present in mechanically generated specs)
+
+==== Version 3.3.9 - 31 January 2000 ====
+
+- fixed several more bugs in the ltl -> buchi automata
+ conversion - found with a random testing method
+ described by Heikki Tauriainen. the method consists
+ of generating random ltl formula with a fixed number of
+ propositional symbols, with varying numbers of operators,
+ and generating random statespaces over the boolean
+ operands, up to preset maximum number of states.
+ we've done tests with three databases, consisting of:
+ - 27 handpicked standard ltl formulae with up to 4
+ operands
+ - 5356 random ltl formulae with up to 10 temporal
+ operators and up to 3 operands
+ - 20577 ltl formulae with up to 3 temporal operators
+ and up to 3 operands
+ each formula was tested for 25 randomly generated statespaces
+ with up to 50 global states.
+ we checked spin's automata generation method against an
+ independent implementation by kousha etessami, and verified
+ that each of the tests either failed with both tools or
+ passed with both tools -- any difference pointed to a bug
+ in one of the two tools.
+ the fixes in spin version 3.3.9 are mostly related
+ to the use of the X (next operator -- which is normally
+ disabled but can be enabled by compiling the spin sources
+ with the extra compiler directive -DNXT) and V (the dual
+ of U) in long formula.
+- used the opportunity to add in some more optimizations
+ that reduce the size of the automata that are produced
+ (which in many cases also speeds up the generation process).
+ the optimizations were inspired by kousha etessami's work.
+ (email: kousha@research.bell-labs.com)
+
+==== Version 3.3.10 - 15 March 2000 ====
+
+- this should be a final, stable release of spin
+ version 3.3, barring the unforeseen.
+ we'll move to 3.4.0 in a next round of extensions.
+
+- made the number of active processes a globally visible
+ read-only variable: _nr_pr
+ this makes it possible to start a process and then wait
+ for it to complete:
+ run A(); (_nr_pr == _pid+1);
+ useful for simulating function calls.
+- the appearance of a timeout in the guard of a d_step
+ sequence was treated as an error - it is not treated
+ as a warning. (in the guard a timeout is ok)
+- fixed rounding error in calculating the nr of bytes
+ to be stored in statevector with -DCOLLAPSE option.
+ in rare cases the roundoff error could result in
+ missed states when collapse was enabled. reported by
+ Dragan Bosnacki.
+- improved ltl->buchi automata conversion some more
+ to be described in an upcoming paper by kousha.
+- small update of xspin.tcl -- it failed to record spin
+ command line options in the advanced verification options
+ panel. reported by Theo Ruys.
+
+==== Version 3.4.0 - 14 August 2000 ====
+
+- fixed two remaining problems with the ltl conversion
+ algorithm, related to nested untils and the use of the next
+ operator (thanks again Heikki Tauriainen).
+- deals better with renaming files for preprocessing -- no
+ longer relies on temporary files residing on the same
+ filesystem as the working directory
+- added an alignment attribute for the State vector to force
+ gcc to align this structure on a wordboundary (on solaris
+ machines gcc apparently considers this optional).
+- fixed two problems in the use of trace-assertions (could
+ fail when tracking actions on rendezvous channels)
+- new xspin340.tcl that deals better with non-terminating
+ simulation runs on pcs.
+- added support for property-based slicing, to be documented.
+ one example in the Test directory illustrates its use: the
+ wordcount example.
+- added two examples (mobile[12]) that show how specifications
+ in the pi-calculus can be rendered in Promela (thanks Joachim
+ Parrow).
+
+==== Version 3.4.1 - 15 August 2000 ====
+
+- fixed problem with spin option -m -- it stopped working after
+ the upgrade to spin 3.3.0 a year ago. (Thanks Theo Ruys and Rui Hu).
+- minor twiddles to avoid some spurious warnings from gcc on pan_ast.c
+
+==== Version 3.4.2 - 28 October 2000 ====
+
+- release 3.4.1 had some windows carriage returns in some of the
+ source files, which unix compilers don't like - removed
+- two small fixes in the data dependency algorithm, e.g. to make sure
+ that an array index is never considered a def
+- made the allignment attribute on the State struct GCC specific
+ (which it is -- used only on Solaris)
+- the -o2 flag didn't work as advertised, fixed.
+- fix to prevent problems with too liberal use of sequence brackets
+ which could cause a coredump in some cases
+
+==== Version 3.4.3 - 22 December 2000 ====
+
+- small bug fixes, related to the use of {...} for plain sequences
+ (other than for atomic or d_step sequences), and the use of
+ enabled to refer to the running process in simulation mode
+
+==== Version 3.4.4 - 2 February 2001 ====
+
+- fix of the trace assertion code in pan.c (there was a problem
+ when trace assertions were used in combination with rv operations)
+- fix of marking of unreachable states (some reached states could
+ erroneously be reported as unreached in some cases)
+
+==== Version 3.4.5 - 8 March 2001 ====
+
+- one more bug found by Heikki Tauriainen - in the LTL -> Buchi
+ conversion algorithm. it was caused by an unjustified optimization
+ in tl_rewrt.c -- now commented out.
+
+==== Version 3.4.6 - 29 March 2001 ====
+
+- when using rendezvous channels, the compression mask was
+ not completely restored on backward moves during the search.
+ the correctness of the search was not affected, but the
+ number of reached states became larger than necessary
+ (up to twice as large as needed). bug fixed.
+ (found and reported by Vivek Shanbhag)
+
+==== Version 3.4.7 - 23 April 2001 ====
+
+- fixed a number of small bugs in xspin.tcl (now version 3.4.7)
+ (shaded out menu items were not enabled, errors on cancel of
+ simulation runs, etc.)
+- pruned the number of unreached states reported, by removing
+ reports for internal states (marked ".(goto)" or "goto :b3")
+- fixed bug in pid assignements on guided simulation for np-cycles
+
+==== Version 3.4.8 - 22 June 2001 ====
+
+- more small bug fixes
+ e.g., a problem with parameters on inline calls, if the name
+ of an actual parameter equals the name of another formal parameter
+ in the same inline; a typo in an 'attribute' annotation; some
+ missing parameters in rarely executed printf calls
+
+==== Version 3.4.9 - 1 October 2001 ====
+
+- two bug fixes:
+ - problem with xr/xs declarations for processes that can be
+ instatiated with multiple pids -- could lead to a coredump
+ - problem with treatment of merged statements in guided simulations.
+ could lead to a statement being printed twice when it only
+ occurred once.
+
+==== Version 3.4.10 - 30 October 2001 ====
+
+- two bug fixes:
+ - trace assertions were not working correctly, failing to
+ reliably generate matches for all channels within the scope
+ of an assertion. this was likely broken when statement merging
+ was first introduced in version 3.3
+ - added protection against the use of pids outside the valid
+ range in remote references (i.e., less than 0 or over 255)
+
+==== Version 3.4.11 - 17 December 2001 ====
+
+- a bevy of small bug fixes:
+- during verification, sorted send operations
+ (e.g., q!!m) were not reversed accurately, leading to
+ potentially inconsistent error trails
+- 'else' was not interpreted correctly when it appeared
+ as the first statement of a d_step
+- process death was not in all possible cases considered a safe
+ action, and thus could be deferred longer than necessary
+- collapse did not in all cases generate the best compression
+
+==== Version 3.4.12 - 18 December 2001 ====
+
+- correcting a dumn coding error in 3.4.11 that made the
+ pan.c source uncompilable..
+
+==== Version 3.4.13 - 31 January 2002 ====
+
+- new option -T, to suppress pid-dependent indenting of outputs
+- new datatype 'pid' for storing return values from run expressions
+
+- improved reporting of unreached states for models with inlines.
+- improved reporting of memory use for bitstate verification runs.
+- fewer unused vars in pan.c for common modes of compilation.
+- during simulation each line of output is now immediately flushed
+- new restrictions on the use of 'run': max 1 run operator per
+ expression, and run cannot be combined with other conditionals.
+ this secures that if a run expression fails, because the max nr
+ of procs would be exceeded, the expression as a whole will have
+ no side-effects.
+
+- corrected bug in treatment of parameters to inlines
+- corrected bug that showed up for some bizarre combinations
+ of constructs (d_step nested in atomic, embedded in loop)
+ sympton was that the spin parser would hang
+- the maximum number of processes during simulation is now
+ equal to that during verification (255) - to prevent
+ runaway simulations. the exact number can be redefined
+ when spin is compiled, by adding a directive, e.g. -DMAXP=512
+ similarly the max nr of message channels during simulation
+ can be set by compiling spin with a directive, e.g. -DMAXQ=512
+ the bounds used during verification (255) cannot be changed.
+
+==== Version 3.4.14 - 6 April 2002 ====
+
+- added new spin option -uN to truncate a simulation run after
+ precisely N steps were taken. in combination with option -jM
+ this can select step M to N from a very long simulation
+ (say guided or random); example: spin -j10 -u20 spec
+ prints step 10 up to 20, but nothing else
+
+- corrected important bug introduced in 3.4.13 that caused a
+ core dump during verification runs. the bug was caused by
+ a poor attempt to correct reporting of unreached states
+ due to statement merging effects.
+
+- corrected compilation error for an unusual combination of
+ compiler directives
+
+==== Version 3.4.15 - 1 June 2002 ====
+
+- much improved hashfunctions, at the suggestion of Jan Hajek
+ from The Netherlands (the original implementor of the Approver
+ tool from the seventies).
+ this makes for better performance in both exhaustive searches
+ (fewer hashcollisions on standard hashtable, therefore often
+ faster), in bitstate and hashcompact searches (more coverage).
+ the old hashfunctions are reenabled if pan.c is compiled
+ with the new directive -DOHASH. the new functions are the default.
+- improved reports of unreachable states, in the presence of
+ statement merging.
+- small change in the indenting of printf output -- it now lines
+ up better with process columns in -c simulation output
+- fewer compiler warnings
+- some small fiddles with xspin to fix small problems
+- giving up on maintaining the upgrade3 scripts -- they get too
+ long and they do not seem to be used much
+
+==== Version 3.4.16 - 2 June 2002 ====
+
+- a bug slipped in in 3.4.15, bitstate verification failed
+- also increased the default memory limit on PCs to 64 Mb
+
+==== Version 3.4.17 - 19 September 2002 ====
+
+- added a function printm(x) to print the symbolic name of
+ an mtype constant. this is equivalent to printf("%e", x),
+ but can be more convenient.
+- changed the structure of the never claim that is included
+ by default if pan.c is compiled for non-progress cycle
+ detection with directive -DNP
+ the change is to check first for a move to the accepting
+ state, rather than last. this reduces the length of
+ error trails that are generated, matching the earlier
+ change made in version 3.2.2, thanks again to Dragan Bosnacki
+ for pointing this out.
+- rearranged the code for pan_ast.c so that it can be compiled
+ separately, rather than as an include file of pangen5.c
+- a bug had been hiding in the -DCOLLAPSE memory compression
+ option that could in rare cases lead to states being missed
+ during a verification
+ the bug could be avoided with the optional -DJOINPROCS.
+ it is now permanently fixed by extending the nr of bytes
+ stored somewhat (the type of each process is now stored
+ explicitly in the compressed statevector, to avoid the
+ confusion that can result if two processes of the same
+ contents but with different types could be created with
+ the same pid, but as alternative options from the same
+ state -- a case found by Remco van Engelen.
+ the fix increases memory use slightly in some case (around
+ 10% in many test cases) but retains the greater part of
+ the memory compression benefit. if needed, the fix can
+ be disabled by compiling pan.c with -DNOFIX
+- pan_ast.c is now a separately compiled file, just like
+ all the others, instead of being #included into pangen5.c
+- more attempts to fix the accuracy of reachability reports
+
+==== Version 3.5.0 - 1 October 2002 ====
+
+- variable names starting with an underscore were mistreated
+ in the data flow analysis.
+- this is meant to be a stable release of spin version 3, with
+ minor changes in contact-information for the new spinroot.com
+ website for all documentation, workshop information and
+ newsletters.
+
+==== Version 3.5.1 - 11 November 2002 ====
+
+- bug in parsing of remote label references, could cause a
+ core-dump of spin -a
+- small additional improvements in reporting of unreachable
+ states - to more accurately take into account optimizations
+ made in the transition structure before verification starts
+- noted incompatability of combining -DREACH and -DMA
+
+==== Version 3.5.2 - 30 November 2002 ====
+
+- slightly improved line number references in reporting syntax
+ errors within d_steps
+- extension: remote references usually are written as:
+ proctypename[pid]@labelname
+ if there is only one instantiation of the proctype, then the
+ pid can more easily be figured out by Spin than by the user,
+ so it can, in these cases, now be omitted, making an anonymous
+ remote reference possible, as in:
+ proctypename@labelname
+ if there turn out to be multiple possible matches, Spin will
+ warn in simulation mode -- but not in verification mode.
+ (the additional check would probably be too consuming).
+- during the execution of a d_step, spin would by default
+ still print out every execution step in simulations (under
+ the -p option). now it will only do so in verbose mode
+ (with also -v).
+- if the last step in an atomic sequence was a rendezvous
+ send operation, atomicity would not reliably move with
+ the handshake to the receiver. this is fixed.
+- the simulator used a confused method to help the user out
+ if the pid of a process was guessed incorrectly in a remote
+ reference operation. this is now done more sanely: if a
+ variable is used for the pid, the simulator now trusts that
+ it was set correctly -- the remote ref will simply fail with
+ an error warning if this is not the case. if the user specified
+ the pid with a fixed constant, the simulator will now always
+ add 1 to the number if the presence of a never claim is detected.
+ (this is because behind the scenes the pid's will move up one
+ slot to accomodate the claim -- this is always hidden from the
+ user -- allowing the user to assume that pids always start at 0).
+
+==== Version 3.5.3 - 8 December 2002 ====
+
+- slightly better error reporting when the nr of pars in a send
+ or run statement differs from the nr declared
+- handling more cases of structure expansion (e.g., structure
+ reference inside other structure used as msg parameter)
+
+==== Version 4.0.0 - 1 January 2003 ====
+
+- Summary of the main changes that motivated the increase of the
+ main Spin version number from 3 to 4
+- added support for embedded C code, primarily to support
+ model extractors that can generate Spin models from C code
+ more easily now, but indirectly this extension also makes
+ all C data types and language elements available within
+ Spin models. a powerful extension - but with few safeguards
+ against erroneous use. read the documentation carefully.
+- added a Breadth-First search option (compile pan.c with -DBFS)
+ this option works only for safety properties. it often uses
+ more memory and more time than the standard Depth-First search
+ mode that Spin uses, but it can find the shortest possible
+ error-trails more easily than with the dfs.
+ cycle detection is hard with bfs, so it's not supported yet.
+ all state compression modes are supported (bitstate, collapse,
+ hash-compact, mininized automata, etc.)
+- a small number of bug fixes -- e.g., some unless constructs
+ gave compile-time errors in pan.c, some combinations of
+ compiler directives gave compiler errors, fewer unused vars
+ reported with some more rarely used combinations of compiler
+ directives.
+- slightly rearranged the makefiles -- there is now a separate
+ shell script (make_pc) for windows and a makefile for unix
+ (make_unix). there's also a script for compiling a debuggable
+ version of spin with gcc and gdb (make_gcc).
+ by default these scripts and makefiles now enable the LTL next
+ operator.
+- the call to sbrk() instead of malloc() on Unix is now no longer
+ the default -- it could cause large amounts of memory that on
+ Linux systems is pre-allocated to malloc, to be inaccessible.
+- on Windows PC's the compiler directive -DPC to compile pan.c
+ source is no longer needed (it is only needed to compiler spin
+ itself)
+
+All further updates will appear in the new file: V4.Updates
--- /dev/null
+Distribution Update History of the SPIN sources
+===============================================
+
+==== Version 4.0.0 - 1 January 2003 ====
+
+See the end of the V3.Updates file for the main changes
+between the last version 3.5.3 and version 4.0.0.
+A glimpse of the Spin update history since 1989:
+
+ Version 0: Jan. 1989 - Jan. 1990 5.6K lines: book version
+ Version 1: Jan. 1990 - Jan. 1995 7.9K lines: first version on netlib
+ Version 2: Jan. 1995 - Aug. 1997 6.1K lines: partial-order reduction
+ Version 3: Aug. 1997 - Jan. 2003 17.9K lines: bdd-like compression (MA)
+ Version 4: Jan. 2003 - 28.2K lines: embedded c-code, bfs
+
+==== Version 4.0.1 - 7 January 2003 ====
+
+- the rule that one cannot combine a run operator
+ in an expression with any other type of boolean
+ or arithmetic operator within the same expression
+ is now enforced.
+- bfs now produces the usual stats upon finding
+ and error; and it now supports the -e option.
+- extended bfs to deal also with safety properties
+ specified in never claims and trace assertions.
+ unlike the regular dfs search, the bfs search cannot
+ handle the use of atomic sequences inside never claims.
+ it will issue a warning, and will abort, if it sees this.
+ unless constructs, d_steps, etc. should all work also
+ within never claims
+ a warning is issued if accept labels are found inside
+ the never claim (to warn that the bfs search is restricted
+ to safety properties only).
+ the never claim does always work to restrict the search
+ space to the part that is covered by the claim.
+- fixed bug in simulation mode, where atomicity was not
+ always correctly preserved across rv actions from one
+ atomic chain to another (if the sender action was the
+ last statement in an atomic sequence) reported by Judi Romijn.
+- added the BFS option also in the advanced verification
+ options panel of xspin401.tcl
+
+==== Version 4.0.2 - 6 March 2003 ====
+
+- removed a long-standing bug in the marking of transitions
+ for partial order reduction:
+ the guard statement of an atomic or d_step sequence within
+ which a non-atomic,atomic,or d_step sequence is nested did
+ not always get the proper tag
+ if the tag assigned was local and it should have been global,
+ the p.o. reduction algorithm could make an invalid reduction.
+ such a case can indirectly be constructed if an atomic sequence
+ contains an call of an inline function as the first statement.
+ (this case was found by Bram de Wachter)
+- removed reliance on tmpnam() in main.c -- gcc complains about
+ it allowing a race condition on the use of the name returned.
+ we now use fixed local names for the temporary files.
+ there could be a problem now if two users run spin within the
+ same directory simultaneously -- but that problem already
+ exists with xspin as well (pan.tmp and pan.pre) and is
+ easily avoided. (if needed, we could add a locking mechanism
+ at some point, but this seems overkill for the time being.)
+- the -m option now works the same in breadth-first search as it
+ does in depth-first search. there's less of a strict reason
+ to cutoff the search at the -m depth with bfs, since the
+ stack is not pre-allocated in this case; it grows dynamically.
+ by setting -m to a very large number, one can therefore just
+ let the verifier proceed until it exhausts memory, or finishes
+ (that is to recreate the earlier behavior when needed).
+- increased the size of some internal arrays a bit to better
+ accomodate the use of very long identifier or structure names.
+- much improved rule for creating and locating error trail files:
+ if possible, the trail file is created by appending .trail
+ to the filename of the source model
+ some older operating systems don't like it if the filename
+ for the source model already contains a period, so if a
+ failure is detect, a second attempt is now made by stripping
+ the existing . extesion (e.g., .pml) and replacing it with
+ .trail (some os's also truncate this to .tra, which is also
+ accepted).
+
+==== Version 4.0.3 - 3 April 2003 ====
+
+- no verbose steps printed for never claim in guided simulations
+ unless -v is given as a commandline argument
+ state changes in the never claim are still printed, but with
+ the already existing separate output ("Never claim moves to...")
+- new spin command-line option -I, to reproduce a version of the
+ specification after preprocessing and inlining operations are
+ done. the reproduced code is not complete: declarations and
+ process parameters are skipped, some internally generated labels
+ and jumps (e.g., replacing break statements) also become visible.
+ the version is intended only to show what the effect of inlining
+ and preprocessing is.
+- change in sched.c to suppres printing of final value of variables
+ marked 'show' -- this looks like an assignment, which is confusing.
+- small fixes in xspin, which is now xspin402.tcl
+- in guided simulation mode, when a claim from an ltl property is
+ present, the simulator's pid nrs did not always agree with the
+ verifiers numbers -- this could lead to the wrong name for a
+ process being printed in the simulation trails.
+
+==== Version 4.0.4 - 12 April 2003 ====
+
+- there was a bug in 4.0.3 that prevented successful compilation
+ of pan.c (and unbalanced endif, caused by a missing newline
+ character in pangen1.h on line 3207)
+- there was a maximum of 128 variables that could be changed per
+ atomic sequence, this is now 512.
+
+==== Version 4.0.5 - 29 May 2003 ====
+
+- correction in the reporting of process id's during guided simulation.
+ the numbers could be off by one when never claims are used.
+ (just a reporting problem, the run itself was always correct)
+- increased bounds on the length of strings passed as preprocessor
+ commands
+- explicit cast of return value ot strlen to int, to keep compilers
+ happier
+- fixed subtle bug in the fairness option (reported by Dragan
+ Bosnacki). with fairness enabled, standard claim stutter could
+ in special cases cause a false acceptance cycle to be reported
+ (i.e., a cycle not containing an accepting state).
+ for compatibility, the old behavior can still be obtained by
+ compiling the pan.c verifiers with -DOLDFAIR. the default is
+ the fixed algorithm.
+- restricted the maximum length of a string in the lookup table
+ for c_code segments to 1024 characters. this table is only used
+ to print out the code segment in error traces -- so the truncation
+ is cosmetic, not functional. it avoids compiler complaints about
+ excessively long strings though (which could prevent compilation).
+- improved error reporting when a value outside the range of the
+ target data type is passed as an parameter in a run statement
+
+==== Version 4.0.6 - 29 May 2003 ====
+
+- the fix of the fairness option wasn't quite right.
+ directive -DOLDFAIR is gone again, and the real fix
+ is now in place.
+
+==== Version 4.0.7 - 1 August 2003 ====
+
+.------------------------------------------------------.
+| Version 4.0.7 is the version of Spin that is |
+| described in the Spin book (Addison-Wesley 2003) |
+| and that is used for all examples there |
+| http://spinroot.com/spin/Doc/Book_extras/index.html |
+.------------------------------------------------------.
+
+- added (really restored) code for allowing separate
+ compilation of pan.c for model and claim
+ two new (previously undisclosed) commandline
+ options -S1 and -S2 -- usage documented in the new book
+
+- if it is detected that a c_expr statement definitely has
+ side effects, this now triggers a fatal error.
+
+- complains about more than 255 active processes
+ being declared in active prefix
+
+- fix in -A option: removed bug in handling of eval()
+
+- cosmetic changes:
+ endstate and end-state are now spelled 'end state' as
+ preferred by Websters dictionary (...)
+ hash-array, hash-table, and never-claim similarly
+ are now spelled without hyphens
+
+- improved error replay for models with embedded c code
+
+- the -m option is no longer automatically set in guided
+ simulation runs.
+
+- change spin.h to allow it to be included twice without
+ ill effects (happens in y.tab.c, generated from spin.y)
+
+- updated the make_gcc file for newer versions if cygwin
+
+==== Version 4.1.0 - 4 December 2003 ====
+
+- new spin option -h, when used it will print out the
+ seed number that was used for the random number
+ generator at the end of a simulation run -- useful
+ when you have to reproduce a run precisely, but forgot
+ to set an explicit seed value with option -n
+
+- c_track now has an optional extra argument, to be
+ documented - the extra qualifier cannot be used with BFS
+ (spin.h, spin.y, spinlex.c, pangen4.c, ...)
+
+- the documentation (book p. 41) says that unsigned data
+ can use a width specifier up to 32 bits -- it actually
+ only worked up to 31 bits. it now works as advertised.
+ fix courtesy of Doug McIlroy. (vars.c)
+
+- when trying to compile a model without initialized
+ channels, a confusing compiler error would result.
+ now avoided (spin.y, pangen1.c)
+
+- there is no longer a default maximum memory arena
+ on verifications, that would apply in the absence of
+ an explicit -DMEMCNT or -DMEMLIM setting (the default
+ was 256 Mb).
+
+- some more fixes to account for 64bit machines, courtesy
+ of C. Scott Ananian.
+
+- from Dominik Brettnacher, some instructions on compiling Spin
+ on a Mac under OS X, added to the installation README.html
+ file.
+
+- so far you could not use a -w parameter larger than
+ 31 during bitstate search -- this effectively limited
+ the max bitarray to about 512Mb. the max is now -w32
+ which extends this to 1Gb (i.e., 4 10^9 bits).
+ (really should be allowed to go higher, but wordsize
+ gets in the way for now.)
+
+- suppressed a redundant 'transition failed' message
+ that could occur during guided simulations (guided.c)
+
+- fixed a long standing bug that could show up if the last
+ element of an atomic sequence was itself a sub-sequence
+ (e.g., defined as an inline or as an unless stmnt).
+ in those cases, atomicity could be lost before the
+ last statement (sequence) completed. (flow.c)
+
+- fixed two long standing bugs in parsing of
+ nested unless structures. the bug showed up in
+ a double nested unless that is itself embedded in a
+ non-atomic block. symptom was that some code became
+ unreachable (thanks to Judi Romijn for the example).
+ goto statements that survived state machine optimization
+ also did not properly get tagged with escape options.
+
+- also fixed a bug in handling excessively long assertion
+ strings (larger than 999 characters) during verification
+
+- revised the way that c_track is implemented (the points
+ where c_update and c_revert are called) to make it a
+ little easier to maintain
+
+- removed some no longer used code from pangen1.h
+
+- fixed bug in treatment of rendezvous statements in BFS mode
+
+- xspin408.tcl update: compiler errors are now printed in the
+ log window, as they should have been all along...
+ (courtesy Doug McIlroy)
+
+==== Version 4.1.1 - 2 January 2004 ====
+
+- extended bitstate hashing on 32-bit machines to work correctly
+ with -w arguments up to and including -w34 (courtesy Peter Dillinger)
+- reduced amount of memory allocated to dfs stack in bitstate
+ mode to something more reasonable (it's accessed through a
+ hash function -- now related to the maxdepth, not the -w
+ parameter
+- fixed bug that could cause problem with very long assertions
+ (more than 256 characters long)
+
+- in xspin411, verbose mode during verifications is now default
+ (it adds line numbers reached in the never claim to the output)
+- small fixes to the search capability in most text windows
+
+==== Version 4.1.2 - 21 February 2004 ====
+
+- fixed bug in support for notrace assertions (the pan.c would
+ not compile if a notrace assertion was defined)
+- fixed unintended feature interaction between bitstate search
+ and the -i or -I runtime flags for finding the shortest
+ counter-example
+- some cosmetic changes to ease the life of static analyzers
+- fixed implementation of Jenkins function to optionally
+ skip a semi-compression of the statevector -- to increase speed
+ (pointed out by Peter Dillinger)
+- fixed bug in resetting memory stack arena that could show up
+ in iterative verification runs with pan -R argument
+ (also found by Peter Dillinger)
+- new version of xspin 4.1.2, with a better layout of some
+ of the panels
+
+==== Version 4.1.3 - 24 April 2004 ====
+
+- changed from using "cpp" by default to using "gcc -E -x c"
+ given that most users/systems have gcc anyway to compile c programs
+ and not all systems have cpp in a fixed place.
+ there should be no visible effect of this change.
+
+- a rendezvous send operation inside an atomic sequence was
+ incorrectly accepted as a candidate for merging with subsequent
+ statements in the atomic sequence. it is the only type of statement
+ that can cause loss of atomicity (and a switch to another process)
+ when *executable* (rather than when blocking, as is the case for
+ all other types of statements, including asynchronous sends).
+ this is now fixed, such that if there is at least one rv channel
+ in the system, send operations inside atomic sequences cannot
+ be merged with any other statement
+ (in general, we cannot determine statically if a send operation
+ targets an rv channel or an asynchronous channel, so we can only
+ safely allow the merging if there are no rv channels at all).
+ this can cause a small increase in the number of stored states
+ for models with rendezvous cannels
+
+- counter-examples produced for liveness properties (non-progress or
+ acceptance cycles) often contained one step too many -- now fixed
+
+- added check for reuse of varnames in multiple message fields
+ i.e., q?x,x is not allowed (would cause trouble in the verifier)
+
+- added a warning against using a remote reference to a label
+ that is declared inside an atomic or d_step sequence -- such
+ labels are always invisible to the never claim (since the
+ executing of the sequence containing the label is meant to be
+ indivisible), which can cause confusion.
+
+- "StackOnly" can be used as an alternative to "UnMatched" when used
+ as the optional 3rd argument a c_track primitive (see Spin2004 paper)
+
+==== Version 4.2.0 - 27 June 2004 ====
+
+- main.c now recognizes __OpenBSD__ and treats it the same as __FreeBSD__
+
+- general cleanup of code (removing some ifdefs etc)
+
+- allow reuse of varnames in multiple message fields (see 4.1.3) if
+ var is an array variable (e.g., using different elements)
+
+- deleted support for directive -DCOVEST -- replaced with -DNOCOVEST
+
+- deleted support for directive -DHYBRID_HASH
+
+- deleted support for directive -DOHASH, -DJHASH, -DEXTENDED
+ added -DMM for an experimental/debugging mode (non-documented)
+
+- replaced Jenkins' original hashfunction with an extended version
+ contributed by Peter Dillinger.
+ it uses more of the information to generate multiple pseudo hash values
+ (multi-hashing with anywhere from 1,2,... hash-functions)
+
+- added runtime verifier flag -k to support multi-hashing in Bitstate mode.
+ pan -k2 reproduces the default behavior, with 2 bits set per state.
+ pan -k1 is the same as the old pan -s (which also still works).
+ (as also first suggested by Dillinger and Manolios from Georgia Tech.)
+
+- some more useful hints are generated at the end of each bitstate
+ run about possible improvements in coverage, based on the results
+ obtained in the last run.
+
+- updated xspin420.tcl to match the above changes in verification options.
+
+==== Version 4.2.1 - 8 October 2004 ====
+
+- improvement of BFS mode for partial order reduction, thanks to
+ Dragan Bosnacki
+- fewer redundant declarations and fewer complaints from static analyzers
+- a d_step could under some circumstances interfere with a rendezvous
+ in progress (e.g., when the d_step started with an if statement, or
+ when it started with a send or receive rather then a straight guard
+ condition (i.e., an expression). this now works as it should.
+- 4.2.0 attempted to make support for coverage estimates the default.
+ this, however, means that on some systems the pan.c source has to be
+ compiled with an additional -lm flag (for the math library)
+ coverage estimates had to be turned off explicitly by compiling with
+ -DNOCOVEST
+ in 4.2.1 the earlier default is restored, meaning that you have to
+ specify -DCOVEST to get the coverage estimates (and presumably you
+ will then know to compile also with -lm)
+- fixed bug that caused an internal name conflict on the verification
+ of the mobile1 model from the Test distribution
+- fixed a problem that prevented having more than 127 distinct proctypes
+ the max is now 255, the same as the max number of running processes.
+- fix to restore bitstate hashing to work on 64-bit machines
+ we still only compute a 32-bit hash; the largest usable bitstate
+ hash-array remains 2^35 bits (i.e., 2^32 bytes or 4 Gigabytes).
+ (the maximum on 32-bit machines remains -w34 or 2 Gigabytes)
+ for 64-bit mode, we will extend this soon to take advantage of
+ larger memory sizes available on those machines. [see 4.2.5]
+- the default number of hash-functions used in bitstate hashing
+ is now 3 (equivalent to a runtime option -k3), which gives slightly
+ better coverage in most cases
+
+==== Version 4.2.2 - 12 December 2004 ====
+
+- verifiers now can be compiled with -DRANDOMIZE (for dfs mode only)
+ to achieve that transitions within each process are explored in
+ random, rather than fixed, order. the other in which processes are
+ explored remains fixed, with most recently created process explored
+ first (if we can think of a good way of supporting random mode
+ for this, we may add this later). if there is an 'else' transition
+ among the option, no randomization is done (since 'else' currently
+ must be explored as the last option in a set, to work correctly).
+ this option can be useful to get more meaningful coverage of very
+ large states that cannot be explored exhaustively.
+ the idea for this option is Doron Peled's.
+- fixed a limitation in the pan.c verifiers that prevented the use
+ of channels with more than 256 slots. this should rarely be an
+ issue, since very large asynchronous channels are seldomly useful
+ in verifications, but it might as well work.
+- fix to avoid a compiler complaint about a missing prototype when
+ compiling pan.c with -DBFS
+- renamed error message about us of hidden arrays in parameter list
+ to the more accurate description 'array of structures'
+
+==== Version 4.2.3 - 5 February 2005 ====
+
+- _pid and _ are no longer considered global for partial order reduction
+- fixed bug that could lead to the error "confusing control structure"
+ during guided simulations (replay of error trails)
+- fixed problem where an error trail could be 1 step too long for
+ invalid endstate errors
+- added experimental 64-bit hash mode for 64-bit machines,
+ compile pan.c in bitstate mode with the additional directive -DHASH64
+ the code is by Bob Jenkins: http://burtleburtle.net/bob/c/lookup8.c
+ [placeholder for a later extension for 64 bit machines]
+
+==== Version 4.2.4 - 14 February 2005 ====
+
+- added missing newline after #ifdef HASH64 -- introduced in 4.2.3
+ this caused a compiler warning when compiling pan.c in -DBITSTATE mode
+- a terminating run ending in an accept state was not stutter extended
+ unless a never claim was defined. this now works also without a
+ never claim, provided that a search for acceptance cycles is performed.
+ in the absence of a never claim the corresponding error type is
+ called a 'accept stutter' sequence (to distinguish it from 'claim stutter')
+ (bug report from Alice Miller)
+ the extension is disabled if the compiler directive -DNOSTUTTER is used,
+ just like for the normal claim stutter extension rule
+- added support for using -DSC on file sizes larger than 2Gb (courtesy Hendrik Tews)
+- in simulation mode, the guard statement of a d_step sequence was not
+ subject to escape clauses from a possible unless statement, contrary to the
+ language spec. in verification mode this did work correctly; simulation mode fixed.
+ (courtesy Theo Ruys and David Guaspari)
+- added warning for use of an 'unless' construct inside a d_step sequence
+
+==== Version 4.2.5 - 2 April 2005 ====
+
+- the default bitstate space size is now 1 Mb (was 512K)
+- the default hashtable size in exhaustive mode is now 512K slots (was 256K)
+- fixed memory leak that can bite in very long simulation runs
+ (courtesy Hendrik Tews)
+- now turns off dataflow optimization (setting dead variables to 0)
+ when remote variable references are used. (this is a little bit of
+ overkill, since we'd only need to turn it off for the variables
+ that are being monitored from the never claim, but it is simple and safe)
+- you can now compile pan.c with -DHASH64 to invoke a 64bit Jenkins hash,
+ (enabled by default on 64bit machines) or disable it by compiling with -DHASH32
+ (which is the default on 32bit machines)
+ the 64-bit version of Spin (and of the pan.c files it generates) has now been
+ fully tested; this means that we can now use more than 4 Gbyte of memory, both
+ in full state and in bitstate mode.
+- added pan runtime options -M and -G (inspired by the work of Peter Dillinger
+ and Panagiotis Manolios on 3Spin), with a simple implementation.
+ (the code for pangen1.h actually got smaller in this update).
+
+ these two new options are available in bitstate mode only and allow the user to
+ define a bitstate hash array that is not necessarily equal to a power of two.
+ if you use -M or -G, then this overrides any other setting you may have
+ used for -w. for example:
+ pan -M5 will use a hash array of 5 Megabytes
+ pan -G7 will use a hash array of 7 Gigabytes.
+ use this instead of -w when the hash array cannot be a power of 2 bytes large.
+ pan -M4 is technically the same as pan -w25 in that it will allocate
+ a hash array of 4 Megabytes (2^(25-3) bytes), but it can be slower
+ because indices into the hash-array are now computed with a modulo operator
+ instead of with faster bit masks and bit shifts. similarly,
+ pan -G1 is technicall the same as pan -M1024 or pan -w33
+ whether the use of -M and -G is slower than -w depends on your compiler.
+ many modern compilers (e.g. gcc and microsoft visual studio) will automatically
+ optimize the hash array access anyway when it effectively is a power
+ of two large (i.e., independent of whether you use -w25 or -M4).
+ in a small set of tests on a 2.5 GHz machine, using both gcc and the MS
+ compilers, no meaningful difference in speed when using -M or -G could be
+ measured, compared with -w (not even for non powers of two hash array sizes).
+ (the difference in runtime was in the order of 3 to 4%).
+
+==== Version 4.2.6 - 27 October 2005 ====
+
+- mostly small fixes -- one bug fix for reading error trails on 64bit machines
+ (courtesy Ignacy Gawedzki)
+- the full tar file now creates all files into a single common directory named
+ Spin, which will simplify keep track of versions and updates
+- small update of xspin as well (now xspin4.2.6)
+ the main change in xspin is that on startup it will now look for a file with
+ the same name as the filename argument given (which is typically the name of
+ the file with the Promela model in it) with extension .xsp
+ so when executing "xspin model" the command will look for a file "model.xsp".
+ xspin will read this file (if present) for commands to execute upon startup.
+ (very useful for demos!)
+ commands must start with either "X:" or "L:"
+ an L: command must be followed by a number, which is used to set the number of
+ lines that should be visible in the command log window
+ an X: command can be followed by any shell command, that xspin will now execute
+ automatically, with the output appearing in the command log window
+ an example .xsp file:
+
+X: spin -a model
+L: 25
+X: nice gcc -DMEMLIM=1000 -DCOLLAPSE -DSAFETY -DREACH -o pan pan.c
+X: nice time -p ./pan -i -m150
+X: spin -t -c -q3 model
+X: cp model.trail pan_in.trail
+
+==== Version 4.2.7 - 23 June 2006 ====
+
+- change in pc_zpp.c, courtesy of Sasha Ivanov, to fix an incorrect order of
+ preprocessing directives -- scanning "if" before "ifdef" and "ifndef"
+
+- all 3 very dubious types of statements in the following model were erroneously
+ accepted by Spin version 4.2.6 and predecessors.
+ they no longer are -- courtesy of the class of 2006 @ Caltech CS
+ active proctype X() {
+ chan q = [2] of { int, int };
+
+ _nr_pr++; /* change the number of processes... */
+ _p = 3; /* change the state of process X.... */
+ q!2(run X()); /* something really devious with run */
+ }
+
+- added the compiler directive __NetBSD__
+
+- the vectorsize is now always stored in an unsigned long, to avoid
+ some obscure bugs when the size is chosen too small
+
+- fix in the parsing of LTL formulae with spin -f to make sure that
+ unbalanced braces are always detected
+
+- added warning against use of rendezvous in BFS mode -- which cannot
+ guarantee that all invalid endstates will be preserved
+
+- minor things: make_pc now defaults to gcc instead of cl (the microsoft
+ visual studio compiler)
+
+- xspin4.2.7 disables some bindings that seem to be failing
+ consistently now on all platforms, although the reason is unclear
+ (this occurs in the automata view and the msc views, which are supposed
+ to track states or execution steps to source lines in the main text
+ window -- instead these bindings, if enabled, now seem to hang the gui)
+
+==== Version 4.2.8 - 6 January 2007 ====
+
+- added optimizations in cycle search described by Schwoon & Esparza 2005,
+ in 'a note on on-the-fly verification algorithms' and in
+ Gastin, Moro, and Zeitoun 2004, 'Minimization of counter-examples in Spin'
+ to allow for early detection of acceptance cycles, if a state is found
+ on the stack that is accepting, while still in the 1st dfs. the version
+ also mentioned in Schwoon & Esparza -- for the case where the source state
+ of such a transition is accepting -- is also included.
+
+- eleminated many of the #ifdef PC directives that distinguished between
+ use of y.tab.h and y_tab.h which is no longer needed with the newer
+ versions if yacc on cygwin (e.g., bison yacc)
+
+- the use of a non-local x[rs] assertion is now fatal
+
+- fixed small problem where scheduler could lose track of a process during
+ simulations
+
+- small rewrites for problems spotted by static analyzers
+
+- restored correct working of separate compilation option (-S[12])
+
+- fixed initialization problem with local variables (making sure that
+ a local can be initialized with a parameter or with the value of a
+ previously declared and initialized local
+
+- emalloc now returns NULL when 0 bytes are requested (robert shelton 10/20/06)
+
+- using _stat instead of stat on WIN32 platforms for compatibility with cl.exe
+
+- avoided a problem with non-writable strings, in pan.c
+
+- renamed QPROVISO to Q_PROVISO in preparation for related updates in 4.3.0
+
+- fixed problem with the final transition of an error trail sometimes
+ not appearing in the trail file (alex groce)
+
+==== Version 4.2.9 - 8 February 2007 ====
+
+- the optimization for cycle search from 4.2.8 wasn't complete -- it could cause
+ annoying messages to show up, and the start of a cycle not being identified
+ in all cases (Moti Ben-Ari) -- it now works they way it was intended
+
+- made it possible to compile pan.c with visual studio, provided that -DWIN32 or
+ -DWIN64 are included in the compiler directives; see make_pc for an example.
+ without this, file creat calls would crash the application -- because the windows
+ implementation of this call uses its own set of flags...
+
+- the spin parser now flags all cases where the wrong number of parameters
+ is specified in a run statement (as suggested by Klaus Havelund)
+
+- it is now possible to use a c_expr inside an expression, e.g. as in
+ x[ c_expr { 4 } ] = 3 or x[ c_expr { f() } ] (Rajeev Joshi)
+
+- a new option for ./pan when embedded C code is used: -S to replay the
+ error trace without printing anything other than the user-defined printfs
+ from the model itself or from inside c_code fragments - (Rajeev Joshi)
+
+==== Version 4.3.0 - 22 June 2007 ====
+
+- bug fix (thank you Claus Traulsen) for goto jumps from one atomic
+ sequence into another. if the first was globally safe, but the second
+ was not, then an erroneous partial-order reduction was possible
+- small changes based on static analyzer output, to increase robustness
+- smaller pan.c files generated if huge arrays are part of the model
+- more accurate reporting of statecounts in bitstate liveness mode
+- better portability for compilation with visual studio
+- likely to be the last spin version 4 release -- the next should be 5.0
+ which supports safety and liveness verification on multi-core systems
+
+==== Version 5.0 - 26 October 2007 ====
+
+- lots of small changes to make the sources friendlier to static analyzers,
+ like coverity, klocwork, codesonar, and uno, so that they find fewer things
+ to warn about
+- improved check for a match of the number of operands specified to a run
+ operator with the number of formal parameters specified for the proctype
+ (courtesy an example by Klaus Havelund)
+- memory counts are now printed properly as MB quantities (divided by
+ 1024*1024 instead of 1,000,000)
+- more accurate treament of atomic sections that contain goto statements,
+ when they jump into a different atomic section (especially when the two
+ atomics have different properties under partial order reduction)
+ (courtesy and example by Claus Traulsen)
+- improvement treatment of run statements for processes that initialize
+ local variables with global expressions. in these cases the run
+ statement itself is now recognized as global -- otherwise it can still
+ be treated as local under partial order reduction rules
+- improved treatment of variable update printing when xspin is used.
+ before, large structures were always full printed on every step, which
+ could slow down xspin significantly -- this now happens only if there
+ was a change of at least one of the values printed.
+
+ Additions:
+- support for use of multi-core systems, for both safety and liveness
+ properties. see: http://www.spinroot.com/spin/multicore/
+- added the time of a run in seconds as part of all outputs, and in many
+ cases also the number of new states reached per second
+
+- new compile-time directives for pan.c supported in Version 5.0 include:
+ NCORE, SEP_STATE, USE_DISK, MAX_DSK_FILE, FULL_TRAIL, T_ALERT
+ and for more specialized use:
+ SET_SEG_SIZE, SET_WQ_SIZE, C_INIT, SHORT_T, ONESECOND
+ the following three can be left unspecified unless prompted by pan itself
+ on a first trial run:
+ VMAX, PMAX, QMAX,
+ the use of all the above directives is explained in
+ http://www.spinroot.com/spin/multicore/V5_Readme.html
+ for typical multi-core applications only the use of -DNCORE=N is
+ typically needed
+- a small number of other new directives is not related to the use of
+ multicore verifications - their use is also explained (at the very
+ bottom of) the V5_Readme.html file mentioned above. they are:
+ FREQ, NUSCC, BUDGET, THROTTLE, LOOPSTATE, NO_V_PROVISO
--- /dev/null
+Distribution Update History of the SPIN sources
+===============================================
+
+==== Version 5.0 - 26 October 2007 ====
+
+The update history since 1989:
+
+ Version 0: Jan. 1989 - Jan. 1990 5.6K lines: original book version
+ Version 1: Jan. 1990 - Jan. 1995 7.9K lines: first version on netlib
+ Version 2: Jan. 1995 - Aug. 1997 6.1K lines: partial-order reduction
+ Version 3: Aug. 1997 - Jan. 2003 17.9K lines: bdd-like compression (MA)
+ Version 4: Jan. 2003 - Oct. 2007 28.2K lines: embedded c-code, bfs
+ Version 5: Oct. 2007 - 32.8K lines: multi-core support
+
+See the end of the V4.Updates file for the main changes
+between the last Spin version 4.3.0 and Spin version 5.0.
+
+For more details on the use of the new options in 5.0, see also:
+ http://www.spinroot.com/spin/multicore/V5_Readme.html
+and
+ http://www.spinroot.com/spin/multicore/index.html
+which has additional details on the IEEE TSE paper on Spin V5.0.
+
+==== Version 5.1 - 3 November 2007 ====
+
+- fixed an endless loop in the parser for complex atomic sequences
+ (thanks to Mirek Filipow for the example)
+- noticed poor scaling for shared memory system with more than 8 cpus
+ in large rings the downstream cpus can fail to receive sufficient work
+ for some applications, which leads to poor performance.
+ modified the algorithm by adding a global queue that allows
+ cpus to also share some states independent of the ring structure.
+ (and modified the termination algorithm slightly to accomodate this)
+ this improves overall behavior, allows for deeper handoff depths, and
+ restores the scaling on mega-multicore systems
+ linear scalling sometimes stops past roughly 8 cpu's, but some speedup
+ was measured with the new algorithm up to 36 cpu-nodes
+- disabling the global queue is possible but not recommended
+- other smaller fixes, e.g. in issueing recompilation hints etc.
+
+==== Version 5.1.1 - 11 November 2007 ====
+
+- added a new directive -DSFH for fast safety verification
+ this uses a little more memory, but can give a significant speedup
+ it uses Hsieh's fast hash function, which isn't as good as Jenkins,
+ but can be faster, especially when compiling -O2 or -O3.
+ this option does not work in 64-bit mode (yet).
+ the speedup for safety properties the speedup can be up to 2x.
+- some more code cleanup -- more uses of #error and #warning to
+ give faster feedback on unsupported combinations of directives
+- reduced verbosity of outputs in multi-core mode somewhat
+- moved queue access pointers (free and full) into shared memory
+ to give more flexibility in defining handoff strategies
+ (i.e., all cores can now access all queues in principle)
+ added experimental handoff strategies -DPSTAT (with or without -DRROBIN)
+ another experimental handoff strategy is -DFRUGAL (when not using -DPSTAT)
+ [removed in 5.1.2 -- after more experiments showing limited benefit]
+- changed handoff heuristic for bitstate mode, to no longer drop
+ states silently if the target q is full, but instead to explore
+ such states locally -- this increases maxdepth requirements, but
+ is more faithful to the way non-bitstate searches work, and gives
+ better coverage overall
+- changed the way that the global queue is used for multi-core search
+ (the global queue was introduced in 5.1.0 to support scaling to larger
+ number of cores) it is now the second choice, not the first, for a
+ handoff -- the first choice is the normal handoff strategy (normally
+ a handoff to the right neighbor in the logical ring of cores)
+- removed the obsolete directive -DCOVEST
+
+==== Version 5.1.2 - 22 November 2007 ====
+
+- added an automatic resize option for the hashtable in non-bitstate mode
+ this is generally more efficient, although it will still remain faster to
+ choose the right -w parameter up front.
+ this option increases memory use somewhat (the hash now has to be stored
+ in the hashtable together with each state -- which adds about 4 bytes to
+ each state) the automatic resizing feature can be disabled with -DNO_RESIZE
+ (e.g., to reduce memory). not enabled in multi-core mode.
+- replaced the global heap in multicore mode with separate heaps for each
+ process (still using shared memory of course) -- this reduces the
+ amount of locking needed (suggested by Petr Rockai -- comparable to using hoard)
+- rewrote the compress function with some loop unwinding to try to speed
+ it up a bit (but no major improvement noticed)
+- increased the number of critical sections used for hashtable access in
+ multi-core mode 8x. this improves scaling for some problems
+ (e.g., for elevator2.3 from the BEEM database).
+- made it in principle possible to use more than 2 cores for liveness
+ verification, although more work would be needed to turn this into
+ a method that can speedup the verification of liveness properties further
+- reduced SFH to non-bitstate mode (it is slower than Jenkins if used for
+ double-bit hash computations)
+- changed the format of printfs a little to line up numbers better in output.
+ also improved the accuracy of the resource usage numbers reported
+ in multi-core mode
+- removed the experimentsl directives PSTAT, RROBIN, and FRUGAL from 5.1.1
+- also removed another stale directive R_H
+- updated the 64-bit version of Jenkins hash with the latest version
+ posted on his website (already a few years ago it seems).
+ no big difference in performance or accuracy could be noted though.
+- made liveness verification work with a global queue
+- changed the details of the state handoff mechanism, to rely more on
+ the global queue, to improve scaling behavior on larger numbers of cores
+- reduced the sizes of the handoff queues to the handoff-depth leaving
+ only the global queue at a fixed 128 MB -- in measurements this was a win
+- improved code for setting default values for MEMLIM
+- increased the value of VMAX to match that of the full VECTORSZ, so that
+ redefining it will be less frequently necessary -- leaving VMAX too high
+ reduces only the number of available slots in the queues
+- increased the value of PMAX and QMAX from 16 to 64, so that they
+ also should need adjusting much more rarely
+
+==== Version 5.1.3 - 8 December 2007 ====
+
+- fixed important bug that was introduced in 5.1.2 -- the automatic resize option
+ did not work correctly when -DCOLLAPSE was used. the result of a verification was
+ still correct, but the hashtable would become very slow after a single resizing,
+ and possibly duplicate work being done. corrected. (found by Alex Groce)
+- if the directive -DSPACE is defined, a more memory frugal (and slightly slower)
+ algorithm is used. no automatic resize of the hashtable and no suppression of
+ the default statevector compression mode (used by default in combination with SFH)
+- COLLAPSE compression didn't work with the new hash functions
+- if NGQ is defined (no global queue) in multi-core mode, the local workqueues
+ of the cpus is now a fixed size, rather than derived from the -z argument
+- preventing crash of the parser on the structure if :: false fi, reported
+ by Peter Schauss
+- on CYGWIN the max segment size for shared memory is now restricted to 512MB,
+ matching the max imposed by cywin itself
+- increased the max length of an input line to 1024 (from 512), to avoid preprocessing
+ problems for very long LTL formulae (reported by Peter Schauss)
+
+==== Version 5.1.4 - 27 January 2008 ====
+
+- fixed bug in enforcement of weak fairness -- introduced in 4.2.8 with the shortcut
+ based on Schwoon & Esparza 2005. the early stop after a match on the stack did
+ not take the fairness algorithm into account -- which means that it could generate
+ a counter-example that did not meet the fairness requirement.
+ reported by david farago.
+- added option to explore dfs in reverse with -DREVERSE (useful for very large searches
+ that run out of memory or time before completing the search)
+- added option to allow bfs to use disk, by compiling with -DBFS_DISK
+- can set limit to incore bfs queue with -DBFS_LIMIT=N (default N=100000 states)
+- can set limit to size of each file created with -DBFS_DISK_LIMIT=N (default N=1000000 states)
+- removed obsolete directive -DQLIST
+- made disk-use option for multi-core search work in more cases
+- new runtime option for pan.c to set a time limit to a verification run to a fixed
+ number of N minutes by saying ./pan -QN (single-core runs only)
+
+==== Version 5.1.5 - 26 April 2008 ====
+
+- added directives -DT_REVERSE to reverse order in which transitions are explored
+ (complementary to -DREVERSE from 5.1.4 and an alternative to -DRANDOMIZE)
+- added directive -DSCHED to enforce a context switch restriction (see pan -L)
+- added directive -DZAPH in bitstate mode, resets the hash array to empty each time it becomes half full
+- see online references for usage of all new directives
+ http://spinroot.com/spin/Man/Pan.html
+- directive -DRANDOMIZE can now take an optional random-seed value, as in -DRANDOMIZE=4347
+- added pan runtime option -x to prevent overwriting existing trail files
+- added pan runtime option -L to set a max for context switches (in combination with -DSCHED)
+- pan runtime option -r can take an argument, specifying the trailfile to use
+- pan runtime option -S replays a trail while printing only user-defined printfs
+- omitted references to obsolete directives OHASH, JHASH, HYBRIDHASH, COVEST, NOCOVEST, BCOMP
+- added directive -DPUTPID to include the process pid into each trailfile name
+- better check for inline parameter replacement, to prevent infinite recursion
+ when the formal parameter contains the replacement text
+- increased maximum size of a line for internal macro replacement to 2K
+- other small fixes, e.g., in verbose output, cleaned up multi-core usage detail
+
+==== Version 5.1.6 - 9 May 2008 ====
+
+- the bug fix from 5.1.4 for Schwoon & Esparza's shortcut in combination with fairness
+ did not go far enough. an example by Hirofumi Watanabe showed that the shortcut is
+ not compatible with the fairness algorithm at all. the result was the possible
+ generation of invalid accept cycles. the short-cut is no longer used when fairness
+ is enabled. no other changes in this version.
--- /dev/null
+.ds Z S\s-2PIN\s0
+.ds P P\s-2ROMELA\s0
+.\"
+.\" On CYGWIN move this page to c:/cygwin/usr/man/man1/spin.1
+.\"
+.TH SPIN 1
+.CT 1 comm_mach protocol
+.SH NAME
+spin \(mi verification tool for models of concurrent systems
+.SH SYNOPSIS
+.B spin
+.BI "-a [-m]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI "[-bglmprsv] [-n\f2N\f(BI]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI "-c [-t]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI -d
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI -f
+.I LTL
+.br
+.B spin
+.BI -F
+.I file
+.br
+.B spin
+.BI "-i [-bglmprsv] [-n\f2N\f(BI]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI "-M [-t]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI "-t[N] [-bglmprsv] [-j\f2N\f(BI]"
+[
+.BI -P cpp
+]
+.I file
+.br
+.B spin
+.BI -V
+.I file
+.SH DESCRIPTION
+\*Z
+is a tool for analyzing the logical consistency of
+asynchronous systems, specifically distributed software
+amd communication protocols.
+A verification model of the system is first specified
+in a guarded command language called Promela.
+This specification language, described in the reference,
+allows for the modeling of dynamic creation of
+asynchronous processes,
+nondeterministic case selection, loops, gotos, local and
+global variables.
+It also allows for a concise specification of logical
+correctness requirements, including, but not restricted
+to requirements expressed in linear temporal logic.
+.PP
+Given a Promela model
+stored in
+.I file ,
+\*Z can perform interactive, guided, or random simulations
+of the system's execution.
+It can also generate a C program that performs an exhaustive
+or approximate verification of the correctness requirements
+for the system.
+.\"----------------------a----------------
+.TP
+.B -a
+Generate a verifier (model checker) for the specification.
+The output is written into a set of C files, named
+.BR pan. [ cbhmt ],
+that can be compiled
+.RB ( "cc pan.c" )
+to produce an executable verifier.
+The online \*Z manuals (see below) contain
+the details on compilation and use of the verifiers.
+.\"--------------------------c------------
+.TP
+.B -c
+Produce an ASCII approximation of a message sequence
+chart for a random or guided \19(when combined with \f3-t\f1)
+simulation run. See also option \f3-M\f1.
+.\"--------------------------d------------
+.TP
+.BI -d
+Produce symbol table information for the model specified in
+.I file .
+For each Promela object this information includes the type, name and
+number of elements (if declared as an array), the initial
+value (if a data object) or size (if a message channel), the
+scope (global or local), and whether the object is declared as
+a variable or as a parameter. For message channels, the data types
+of the message fields are listed.
+For structure variables, the 3rd field defines the
+name of the structure declaration that contains the variable.
+.\"--------------------------f------------
+.TP
+.BI "-f \f2LTL\f1"
+Translate the LTL formula \f2LTL\f1 into a never claim.
+.br
+This option reads a formula in LTL syntax from the second argument
+and translates it into Promela syntax (a never claim, qhich is Promela's
+equivalent of a B\(u"chi Automaton).
+The LTL operators are written: [] (always), <> (eventually),
+and U (strong until). There is no X (next) operator, to secure
+compatibility with the partial order reduction rules that are
+applied during the verification process.
+If the formula contains spaces, it should be quoted to form a
+single argument to the \*Z command.
+.\"--------------------------F------------
+.TP
+.BI "-F \f2file\f1"
+Translate the LTL formula stored in
+.I file
+into a never claim.
+.br
+This behaves identical to option
+.B -f
+but will read the formula from the
+.I file
+instead of from the command line.
+The file should contain the formula as the first line. Any text
+that follows this first line is ignored, so it can be used to
+store comments or annotation on the formula.
+(On some systems the quoting conventions of the shell complicate
+the use of option
+.B -f .
+Option
+.B -F
+is meant to solve those problems.)
+.\"--------------------------i------------
+.TP
+.BI -i
+Perform an interactive simulation, prompting the user at
+every execution step that requires a nondeterministic choice
+to be made. The simulation proceeds without user intervention
+when execution is deterministic.
+.\"--------------------------M------------
+.TP
+.BI -M
+Produce a message sequence chart in Postscript form for a
+random simulation or a guided simulation
+(when combined with \f(BI-t\f1), for the model in
+.I file ,
+and write the result into
+.I file.ps .
+See also option \f3-c\f1.
+.\"--------------------------m------------
+.TP
+.BI -m
+Changes the semantics of send events.
+Ordinarily, a send action will be (blocked) if the
+target message buffer is full.
+With this option a message sent to a full buffer is lost.
+.\"--------------------------n------------
+.TP
+.BI "-n\f2N"
+Set the seed for a random simulation to the integer value
+.I N .
+There is no space between the \f(BI-n\f1 and the integer \f2N\f1.
+.\"--------------------------t------------
+.TP
+.BI -t
+Perform a guided simulation, following the error trail that
+was produces by an earlier verification run, see the online manuals
+for the details on verification.
+.\"--------------------------V------------
+.TP
+.BI -V
+Prints the \*Z version number and exits.
+.\"--------------------------.------------
+.PP
+With only a filename as an argument and no option flags,
+\*Z performs a random simulation of the model specified in
+the file (standard input is the default if the filename is omitted).
+This normally does not generate output, except what is generated
+explicitly by the user within the model with \f(CWprintf\f1
+statements, and some details about the final state that is
+reached after the simulation completes.
+The group of options
+.B -bglmprsv
+is used to set the desired level of information that the user wants
+about a random, guided, or interactive simulation run.
+Every line of output normally contains a reference to the source
+line in the specification that generated it.
+If option
+.B -i
+is added, the simulation is \f2interactive\f1, or if option
+.B -t
+is added, the simulation is \f2guided\f1.
+.\"--------------------------bglprsv------------
+.TP
+.BI -b
+Suppress the execution of \f(CWprintf\f1 statements within the model.
+.TP
+.BI -g
+Show at each time step the current value of global variables.
+.TP
+.BI -l
+In combination with option
+.BR -p ,
+show the current value of local variables of the process.
+.TP
+.BI -p
+Show at each simulation step which process changed state,
+and what source statement was executed.
+.TP
+.BI -r
+Show all message-receive events, giving
+the name and number of the receiving process
+and the corresponding the source line number.
+For each message parameter, show
+the message type and the message channel number and name.
+.TP
+.BI -s
+Show all message-send events.
+.TP
+.BI -v
+Verbose mode, add some more detail, and generat more
+hints and warnings about the model.
+.SH SEE ALSO
+Online manuals at spinroot.com:
+.br
+.in +4
+GettingStarted.pdf,
+Roadmap.pdf,
+Manual.pdf,
+WhatsNew.pdf,
+Exercises.pdf
+.in -4
+More background information on the system and the verification process,
+can be found in, for instance:
+.br
+.in +4
+G.J. Holzmann, \f2Design and Validation of Computer Protocols\f1,
+Prentice Hall, 1991.
+.br
+--, `Design and validation of protocols: a tutorial,'
+\f2Computer Networks and ISDN Systems\f1,
+Vol. 25, No. 9, 1993, pp. 981-1017.
+.br
+--, `The model checker \*Z,'
+\f2IEEE Trans. on SE\f1, Vol, 23, No. 5, May 1997.
+.in -4
+.br
\ No newline at end of file
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!-- saved from url=(0040)http://spinroot.com/spin/Man/README.html -->
+<HTML><HEAD><TITLE>Spin - Version 5.1 - December 2007</TITLE>
+<META http-equiv=Content-Type content="text/html; charset=windows-1252">
+<META content="MSHTML 6.00.2800.1276" name=GENERATOR></HEAD>
+<BODY bgColor=#ffffff>
+<p>
+<H1><TT><FONT color=#ff0000>SPIN README</FONT></TT></H1>
+<ul></ul>
+<H2><TT>Overview of this File</TT></H2>
+<OL>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S2">Downloading Spin</A>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S1">Installing Spin</A>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S3">Related software
+ (gcc, cpp, tcl/tk wish, yacc, dot, jspin, ltl2ba)</A> </LI></OL>
+<HR>
+
+<H2><TT>0. Overview</TT></H2>This readme file contains the guidelines for
+downloading and installing Spin and related software on Unix/Linux and Windows
+platforms. Refer to <A href="http://spinroot.com/spin/whatispin.html">Spin's
+homepage</A> for a general description of Spin, with pointers to <A
+href="http://spinroot.com/spin/Man/index.html">manual pages</A>, <A
+href="http://spinroot.com/spin/News/index.html">newsletters</A>.
+<p>Spin is distributed in source form to encourage research in formal
+verification, and to help a support friendly and open exchange of algorithms,
+ideas, and tools. The software itself has a copyright from Lucent Technologies
+and Bell Laboratories, and is distributed for research and educational purposes
+only (i.e., no guarantee of any kind is implied by the distribution of the code,
+and all rights are reserved by the copyright holder). For this general use of
+Spin, no license is required.
+<p>Commercial application of the Spin software is also allowed, but requires the
+acceptance of a basic license. Refer to the <A
+href="http://www.spinroot.com/spin/spin_license.html">Spin Public license</A> for details.
+<p>
+<HR>
+
+<H2><TT><A name=S2>1. Downloading Spin</A></TT></H2>Spin runs on Unix,
+Solaris, and Linux machines, on most flavors of Windows PCs, and on Macs.
+Precompiled binary executables for some popular types of machines are available
+in the
+<A href="http://spinroot.com/spin/Bin/index.html">Spin Binaries</A>.
+<p>
+All binaries have an extension that matches the Spin version number,
+such as <tt>spin427.exe</tt>. To install the binary, rename it to
+<tt>spin.exe</tt> and copy it into your bin directory.
+<p>
+If you have machine type that is not available there, or if you are
+installing Spin for the first time, then follow the more detailed instructions
+below.
+<ul>
+ <LI><B>Unix</B> systems: <BR>download the most recent .tar-file with sources,
+ the graphical interface Xspin, documentation and examples from the <A
+ href="http://spinroot.com/spin/Src/index.html">Spin Distribution</A>, and
+ continue at <A href="http://spinroot.com/spin/Man/README.html#S1a"><FONT
+ color=red><B><TT>Step 2a</TT></B></FONT></A>.</LI>
+ <p></p>
+ <LI><B>PCs</B> (Windows95/98/2000/NT/XP): <BR>download the most recent
+ pc_spin*.zip file, with a precompiled Spin executable, the graphical interface
+ Xspin, and some examples from the <A
+ href="http://spinroot.com/spin/Src/index.html">Spin Distribution</A>, and
+ continue at <A href="http://spinroot.com/spin/Man/README.html#S1b"><FONT
+ color=red><B><TT>Step 2b</TT></B></FONT></A>. </LI>
+ <p></p>
+ <LI><B>Macs</B> (Mac OS X): <BR>download the most recent .tar-file with sources,
+ from the <A href="http://spinroot.com/spin/Src/index.html">Spin Distribution</A>,
+ and continue at <A href="http://spinroot.com/spin/Man/README.html#S1c"><FONT
+ color=red><B><TT>Step 2c</TT></B></FONT></A>.
+ </LI></ul>
+<p>
+<HR>
+
+<H2><TT><A name=S1>2. Installing Spin</A></TT></H2>
+<ul>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S1a"><TT>Unix/Linux
+ systems</TT> (compiled from the sources)</A>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S1b"><TT>Windows
+ PC's</TT> (using the executable)</A> </LI>
+ <LI><A href="http://spinroot.com/spin/Man/README.html#S1c"><TT>Macs
+ </TT> (compiled from the sources, with some patches)</A> </LI></ul><p>
+<HR>
+
+<H2><TT><A name=S1a><FONT color=red>2a. Installing Spin on a Unix/Linux
+System</A></FONT></TT></H2>
+<ul>Place the *.tar.gz file from the <A
+ href="http://spinroot.com/spin/Src/index.html">Spin Source Distribution</A> in
+ clean directory, and cd to that directory. If you have a standard Unix/Linux system,
+ unpack the archive, and compile an executable, for instance as follows: <pre> gunzip *.tar.gz
+ tar -xf *.tar
+ cd Src*
+ make # or, on older distributions: make -f make_unix
+</pre>
+ <p>If you are on a SOLARIS system, edit the makefile and add
+ <TT>-DSOLARIS</TT> to the compiler directives in the makefile before you type
+ 'make'. Similarly, if you use a different C compiler than defined in the
+ makefile, edit the makefile first. You need to have at least a C compiler and
+ a copy of yacc.
+ <p>If all else fails, you can also compile everything with the following line:
+<pre> yacc -v -d spin.y; cc -o spin *.c
+</pre>
+ <p>Spin should compile without warnings. Install the executable version of
+ spin in a directory that is within your default search path (such as your home
+ bin directory, or /usr/local/bin etc.)
+ <p>
+ On Unix/Linux systems Spin assumes that the standard C preprocessor cpp is
+ stored in file "/lib/cpp". On some systems this is different: check the
+ comments in the makefile for details if you run into this problem.
+
+ <H3><TT>Testing</TT></H3>To test the basic sanity of the Spin executable, cd
+ to the Test directory that was created when you unpacked the source archive,
+ and follow the instructions in README.tests file that is included there.
+ <H3><TT>Adding Xspin (Unix/Linux)</TT></H3>Xspin is an optional, but highly
+ recommended, graphical user interface to Spin, written in Tcl/Tk. To obtain
+ Tcl/Tk, see <A href="http://spinroot.com/spin/Man/README.html#S3">Related
+ software</A>. The Xspin source can be found the Xspin4.? directory that will
+ also have been created when you unpacked the source tarfile.
+ <p>The current version of Xspin is compatible with
+<pre> Tk version 4.2 - Tcl version 7.6
+ Tk version 8.4 - Tcl version 8.4
+</pre>
+ <p>Xspin prints the version numbers of Spin, Xspin, and Tcl/Tk when it starts
+ up. You can also check separately which version of Tcl/Tk you have installed
+ by executing the following commands in `wish' (a Tcl/Tk command): <pre> info tclversion
+ puts $tk_version
+</pre>You can find out which version of Spin you have by typing, at the
+ command prompt: <pre> $ spin -V
+</pre>
+ <p>Xspin can also make use the graph layout program 'dot' if it is available
+ on your system (not required, but very nice if available -- xspin will
+ automatically recognize if it is installed.) For information on 'dot,' see <A
+ href="http://spinroot.com/spin/Man/README.html#S3">Related software</A>.
+ <p>To install Xspin on Unix/Linux:
+ <ul>
+ <LI>cd to directory Xspin... from the distribution,
+ <LI>Rename xspin*.tcl into a more convenient form (like xspin or xspin.tcl)
+ and follow the instructions at the top of this xspin.tcl file. Minimally:
+ you must change the first few lines of this file to point to the executable
+ `wish' command on your system that you want to use. If you use another
+ C-compiler than the default (gcc), you should update the global variable CC
+ inside xspin as well. Follow the instructions inside the xspin.tcl file to
+ do so.
+ <LI>copy the file into a directory within your search path, renamed to plain <tt>xspin</tt>
+and make it
+ executable, for instance:
+<pre> cp xspin510.tcl /usr/local/bin/xspin
+ chmod +x /usr/local/bin/xspin
+</pre>
+ <LI>On Unix/Linux, invoke the program by typing
+<pre> xspin # or xspin.tcl if you keep the extension...
+or
+ xspin promela_spec
+
+For example:
+ cd Test
+ xspin leader
+</pre></LI></ul>Check the online Help menus in xspin for more details on
+ routine use. </ul>
+<p>
+<HR>
+
+<H2><TT><A name=S1b><FONT color=red>2b. Installing Spin on a Windows
+PC</FONT></A></TT></H2>
+<ul>If you just need to update the Spin executable itself, download a new
+ version from <A
+ href="http://spinroot.com/spin/Bin/index.html">http://spinroot.com/spin/Bin/index.html</A>
+ If you need more files, e.g. a new copy of Xspin, download the latest
+ pc_spin*.zip file from <A
+ href="http://spinroot.com/spin/Src/index.html">http://spinroot.com/spin/Src/index.html</A>
+ Extract the files from pc_spin*.zip, and copy spin*.exe, renamed spin.exe, into the directory
+ where all your commands reside and that is within your default search path
+ (e.g., c:/cygwin/bin/, or c:\apps\spin\) You can find out what your search
+ path is set to by typing 'set' at an MS-DOS prompt -- this prints a list of
+ all defined variables in your environment, including the search path that is
+ used to find executable commands.
+ (Note that you may need to set the search path in the environment variables)
+ <p>If you use Spin from the command line (i.e., without Xspin), be warned that
+ some command shells, e.g., the MKS Korn-shell, have none-standard rules for
+ argument parsing (i.e., you can not reliably quote an argument that contains
+ spaces, such as an LTL formula). In most cases this will not be much of a
+ problem, except with the conversion of LTL formula with the Spin -f option.
+ You can circumvent this by using -F instead of -f, to read the formula from a
+ file instead of the command line.
+ <p>To run Spin, also with the precompiled version, you need a working
+ C-compiler and a C-preprocessor, because Spin generates its model checking
+ software as C-source files that require compilation before a verification can
+ be performed. This guarantees fast model checking, because each model checker
+ can be optimized to the specific model being checked. Check, for instance, if
+ you can compile and run a minimal C program succesfully, e.g.:
+<pre>
+ #include <stdio.h>
+ int main(void) { printf("hello\n"); }
+</pre>
+ <p>To find a public version of a C compiler and some instructions on how to
+ install it see <A href="http://spinroot.com/spin/Man/README.html#S3">Related
+ software</A>.
+ <H3><TT>Adding Xspin (PC)</TT></H3>To run Xspin on a PC, you need the PC
+ version of Tcl/Tk, which you can find under <A
+ href="http://spinroot.com/spin/Man/README.html#S3">Related software</A>.
+ <p>The xspin*.tcl source is contained in the .zip file of the <A
+ href="http://spinroot.com/spin/Src/index.html">distribution</A>. Copy the .tcl
+ file as is into a directory where you plan to work, or put a shortcut to this
+ file on the Desktop or in the Start Menu. If you keep the extension .tcl, make
+ sure it is recognized as a 'wish' file by the system, so that xspin starts
+ when you double click the xspin*.tcl script.
+ <p>Under cygwin, copy the xspin*.tcl file to /bin/xspin and make it executable
+ -- check the first few lines of xspin*.tcl to make sure the location of xspin
+ matches what you have on your system (it is currently setup for
+ c:/cygwin/bin/xspin). You can now use xspin as a normal Unix-style command,
+ and you can pass the name of a filename to it, for instance as: <pre> xspin leader
+</pre>
+ <p>An indirect way to force xspin to startup is to first start `wish' from the
+ Start Menu, under Programs, then select the larger window that comes up (the
+ command window), and cd to the directory where you've stored the xspin.tcl
+ file. Then you can then start it up by typing: <pre> source xspin.tcl # or whatever else you've named this
+</pre>and you should be up and running.
+ <p>The PC installation assumes that you have a command called "cpp.exe"
+ available (which comes with the gnu-c installation), which is the traditional
+ name of the C preprocessor. Alternatively, it can also use the Visual C++
+ compiler, which is named cl.exe for preprocessing. To complicate your life
+ somewhat, if you have a copy of the Borland C++ compiler installed, you'll
+ notice that this cplusplus compiler was also named cpp.exe -- that's not the
+ cpp you want. To avoid the name clash, you either have to edit the Spin source
+ code to give it the full path name of the 'real' cpp.exe and recompile, or use
+ Spin with the command-line option -Pxxxx where xxxx is the path for cpp.exe.
+ Nothing much in Spin will work without access to cpp.exe. You can do a
+ reasonable number of things without gcc.exe though (like simulations). The
+ C-compiler is required for all verifications and for the automata views in
+ Xspin. </p></ul>
+<p>
+<H2><TT><A name=S1c><FONT color=red>2c. Installing Spin on a Mac</FONT></A></TT></H2>
+ <ul>
+ Compile Spin from its sources, as described under 2a for Unix systems in general,
+ while following the suggestions below, which were provided by
+ Dominik Brettnacher, email: <tt>domi@saargate.de</tt>.
+<p>
+The C preprocessor on Mac OS X cannot be found in <tt>/lib</tt>.
+Change the path in the makefile for the
+proper location (<tt>/usr/bin/cpp</tt>), and in addition
+also tell the Mac preprocessor to handle its input as
+"assembler-with-cpp."
+This can be done by adding a flag to cpp, for instance in
+the makefile by adding the directive
+<ul>
+<pre>
+-DCPP="\"/usr/bin/cpp -xassembler-with-cpp\""
+</pre>
+</ul>
+to the definition of <tt>CFLAGS</tt>.
+</pre>
+<p>
+<H3><TT>Adding Xspin (Unix)</TT></H3>
+On the Mac, Xspin is known to work correctly with Tcl/Tk Aqua,
+which offers a self-contained binary distribution for the Mac.
+Use, for instance, "TclTkAquaStandalone", version 8.4.4 from
+<ul>
+<pre><a href="http://www.maths.mq.edu.au/~steffen/tcltk/TclTkAqua/">http://www.maths.mq.edu.au/~steffen/tcltk/TclTkAqua/</a>
+</pre>
+</ul>
+<p>
+Xspin by default places its temporary files into the root directory.
+This is nasty if you have admin privileges and probably leads to error
+messages if you don't.
+To prevent this, add a "cd" statement to xspin (no arguments, just cd by
+itself on a line), as the first command executed.
+Place it, for instance, directly after the opening comments in the file.
+This makes Xspin use the home directory for these files.
+<p>
+TclTk Aqua also provides the possibility to start a script when being run.
+For instance, to make Xspin start if you launch the TCL interpreter:
+move the xspin file into the "Wish Shell.app", as follows:
+<ul><pre>
+chmod -R u+w Wish\ Shell.app
+mkdir Wish\ Shell.app/Contents/Resources/Scripts
+mv xspin*.tcl Wish\ Shell.app/Contents/Resources/Scripts/AppMain.tcl
+</pre>
+</ul>
+</ul>
+
+<HR>
+
+<H2><TT><A name=S3>3. Related Software</A></TT></H2>
+<ul>Pointers to public domain versions of some related software packages are
+ discussed below:
+ <ul>
+ <LI>Gcc,</LI>
+ <LI>Cpp,</LI>
+ <LI>Yacc,</LI>
+ <LI>Tcl/Tk wish,</LI>
+ <LI>Dot,</LI>
+ <LI>JSpin, and</LI>
+ <LI>Ltl2Ba. </LI></ul>
+ <H3><TT>GCC</TT></H3>On Unix/Linux you probably have gcc, or an equivalent,
+ installed. On the PC you need either a copy of Visual Studio Express (for the cl
+ command), or an installation of gcc with minimally the executables: gcc.exe,
+ and cpp.exe in your search path, together with all the standard C library
+ routines and header files. You can get good free version of all these files
+ with the <TT>cygwin</TT> toolkit, which is mostly self-installing and
+ available from: <A href="http://www.cygwin.com/">http://www.cygwin.com/</A>
+ <BR>See also what's available in <A
+ href="http://spinroot.com/spin/Bin/index.html">http://spinroot.com/spin/Bin/index.html</A>.
+
+ <H3><TT>Tcl/Tk Wish</TT></H3>To run Xspin you'll need Tcl/Tk. Tcl/Tk was
+ written by John Ousterhout (john.ousterhout@eng.sun.com) and is public domain.
+ It can be obtained (for PCs or Unix) from cygwin, or from: <A
+ href="http://www.tcl.tk/">http://www.tcl.tk/</A> or also (a more recent extension):
+ <a href="http://www.activestate.com/Products/ActiveTcl/">
+ http://www.activestate.com/Products/ActiveTcl/</a><BR>More details can be found
+ in netnews-group: comp.lang.tcl
+
+ <H3><TT>Yacc (optional)</TT></H3>To compile Spin itself from its sources on a
+ PC, you'll need to have a copy of yacc installed. A public domain version for
+ a PC can most easily be obtained from <b>cygwin</b>, or also from:
+<pre> <A href="ftp://ftp.cs.berkeley.edu/ucb/4bsd/byacc.tar.Z">ftp://ftp.cs.berkeley.edu/ucb/4bsd/byacc.tar.Z</A>
+</pre>A copy of this file is also available in: <A
+ href="http://spinroot.com/spin/Bin/index.html">http://spinroot.com/spin/Bin/index.html</A>
+ (You don't need yacc on the PC's if you use the pre-compiled version of Spin
+ for the pc in the pc*.zip file from the distribution) Look at the file
+ make_it.bat for an example on how to perform the compilation.
+ <H3><TT>Dot (optional)</TT></H3>Dot is a graph layout tool developed by
+ Stephen North and colleagues at AT&T (email: north@research.att.com).
+ Xspin can make use of dot to beautify the layout of the state-machines in the
+ automata-view option (recommended!).
+To obtain Dot, see
+<pre>
+ <A href="http://www.graphviz.org/">http://www.graphviz.org/</A>
+</pre>
+The are both PC and Unix versions of dot available. For documentation of
+dot see, for instance:
+<pre><I> A technique for drawing directed graphs</I>,
+ by Gansner, Koutsofios, North and Vo,
+ IEEE-TSE, March, 1993.
+</pre>
+If you accept the default installation on a PC, you will need to define the
+location of dot.exe in the xspin source as follows:
+<pre>
+ set DOT "C:/Program\\ Files\ATT\Graphviz/bin/dot.exe"
+</pre>
+(the line that sets the location of DOT appears near the top of the xspin.tcl file).
+<H3><TT>JSpin (optional)</TT></H3>
+An alternative to the Xspin GUI, written in Java instead of Tcl/Tk.
+Written by Moti Ben-Ari (moti.ben-ari@weizmann.ac.il), see
+<pre>
+ <a href="http://stwww.weizmann.ac.il/g-cs/benari/jspin/">http://stwww.weizmann.ac.il/g-cs/benari/jspin/</a>
+</pre>
+The jSpin tool currently expects spin to be installed on Windows in c:/spin/spin.exe, and it assumes that you are using the
+mingw version of gcc.
+ <H3><TT>Ltl2Ba (optional)</TT></H3>A faster method to generate very small
+ never claims from LTL formulae, developed by Denis Oddoux and Paul Gastin is
+ available online in source form:
+<pre>
+ <A href="http://spinroot.com/spin/Src/ltl2ba.tar.gz">http://spinroot.com/spin/Src/ltl2ba.tar.gz</A>
+</pre>
+The latest version can be obtained from the authors website via:
+<pre>
+ <A href="http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/download.php">http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/download.php</A>
+See also
+ <A href="http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/index.php">http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/index.php</A>
+</pre>
+The C source code can be linked with Spin, or run as a standalone tool.
+<p>
+</ul>
+<HR>
+<TT>
+<TABLE cols=3 width="100%">
+<TBODY><TR>
+<TD align=left><A href="http://spinroot.com/spin/whatispin.html"><TT>Spin HomePage</TT></A></TD>
+<TD></TD>
+<TD align=right><FONT size=3><B><TT>
+(Page Last Updated: 26 April 2008)
+</TT></B></FONT></TD>
+</TR></TBODY>
+</TABLE>
+</TT>
+</BODY>
+</HTML>
--- /dev/null
+/***** spin: dstep.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+#define MAXDSTEP 1024 /* was 512 */
+
+char *NextLab[64];
+int Level=0, GenCode=0, IsGuard=0, TestOnly=0;
+
+static int Tj=0, Jt=0, LastGoto=0;
+static int Tojump[MAXDSTEP], Jumpto[MAXDSTEP], Special[MAXDSTEP];
+static void putCode(FILE *, Element *, Element *, Element *, int);
+
+extern int Pid, claimnr, separate, OkBreak;
+
+static void
+Sourced(int n, int special)
+{ int i;
+ for (i = 0; i < Tj; i++)
+ if (Tojump[i] == n)
+ return;
+ if (Tj >= MAXDSTEP)
+ fatal("d_step sequence too long", (char *)0);
+ Special[Tj] = special;
+ Tojump[Tj++] = n;
+}
+
+static void
+Dested(int n)
+{ int i;
+ for (i = 0; i < Tj; i++)
+ if (Tojump[i] == n)
+ return;
+ for (i = 0; i < Jt; i++)
+ if (Jumpto[i] == n)
+ return;
+ if (Jt >= MAXDSTEP)
+ fatal("d_step sequence too long", (char *)0);
+ Jumpto[Jt++] = n;
+ LastGoto = 1;
+}
+
+static void
+Mopup(FILE *fd)
+{ int i, j;
+
+ for (i = 0; i < Jt; i++)
+ { for (j = 0; j < Tj; j++)
+ if (Tojump[j] == Jumpto[i])
+ break;
+ if (j == Tj)
+ { char buf[16];
+ if (Jumpto[i] == OkBreak)
+ { if (!LastGoto)
+ fprintf(fd, "S_%.3d_0: /* break-dest */\n",
+ OkBreak);
+ } else {
+ sprintf(buf, "S_%.3d_0", Jumpto[i]);
+ non_fatal("goto %s breaks from d_step seq", buf);
+ } } }
+ for (j = 0; j < Tj; j++)
+ { for (i = 0; i < Jt; i++)
+ if (Tojump[j] == Jumpto[i])
+ break;
+#ifdef DEBUG
+ if (i == Jt && !Special[i])
+ fprintf(fd, "\t\t/* no goto's to S_%.3d_0 */\n",
+ Tojump[j]);
+#endif
+ }
+ for (j = i = 0; j < Tj; j++)
+ if (Special[j])
+ { Tojump[i] = Tojump[j];
+ Special[i] = 2;
+ if (i >= MAXDSTEP)
+ fatal("cannot happen (dstep.c)", (char *)0);
+ i++;
+ }
+ Tj = i; /* keep only the global exit-labels */
+ Jt = 0;
+}
+
+static int
+FirstTime(int n)
+{ int i;
+ for (i = 0; i < Tj; i++)
+ if (Tojump[i] == n)
+ return (Special[i] <= 1);
+ return 1;
+}
+
+static void
+illegal(Element *e, char *str)
+{
+ printf("illegal operator in 'd_step:' '");
+ comment(stdout, e->n, 0);
+ printf("'\n");
+ fatal("'%s'", str);
+}
+
+static void
+filterbad(Element *e)
+{
+ switch (e->n->ntyp) {
+ case ASSERT:
+ case PRINT:
+ case 'c':
+ /* run cannot be completely undone
+ * with sv_save-sv_restor
+ */
+ if (any_oper(e->n->lft, RUN))
+ illegal(e, "run operator in d_step");
+
+ /* remote refs inside d_step sequences
+ * would be okay, but they cannot always
+ * be interpreted by the simulator the
+ * same as by the verifier (e.g., for an
+ * error trail)
+ */
+ if (any_oper(e->n->lft, 'p'))
+ illegal(e, "remote reference in d_step");
+ break;
+ case '@':
+ illegal(e, "process termination");
+ break;
+ case D_STEP:
+ illegal(e, "nested d_step sequence");
+ break;
+ case ATOMIC:
+ illegal(e, "nested atomic sequence");
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+CollectGuards(FILE *fd, Element *e, int inh)
+{ SeqList *h; Element *ee;
+
+ for (h = e->sub; h; h = h->nxt)
+ { ee = huntstart(h->this->frst);
+ filterbad(ee);
+ switch (ee->n->ntyp) {
+ case NON_ATOMIC:
+ inh += CollectGuards(fd, ee->n->sl->this->frst, inh);
+ break;
+ case IF:
+ inh += CollectGuards(fd, ee, inh);
+ break;
+ case '.':
+ if (ee->nxt->n->ntyp == DO)
+ inh += CollectGuards(fd, ee->nxt, inh);
+ break;
+ case ELSE:
+ if (inh++ > 0) fprintf(fd, " || ");
+/* 4.2.5 */ if (Pid != claimnr)
+ fprintf(fd, "(boq == -1 /* else */)");
+ else
+ fprintf(fd, "(1 /* else */)");
+ break;
+ case 'R':
+ if (inh++ > 0) fprintf(fd, " || ");
+ fprintf(fd, "("); TestOnly=1;
+ putstmnt(fd, ee->n, ee->seqno);
+ fprintf(fd, ")"); TestOnly=0;
+ break;
+ case 'r':
+ if (inh++ > 0) fprintf(fd, " || ");
+ fprintf(fd, "("); TestOnly=1;
+ putstmnt(fd, ee->n, ee->seqno);
+ fprintf(fd, ")"); TestOnly=0;
+ break;
+ case 's':
+ if (inh++ > 0) fprintf(fd, " || ");
+ fprintf(fd, "("); TestOnly=1;
+/* 4.2.1 */ if (Pid != claimnr) fprintf(fd, "(boq == -1) && ");
+ putstmnt(fd, ee->n, ee->seqno);
+ fprintf(fd, ")"); TestOnly=0;
+ break;
+ case 'c':
+ if (inh++ > 0) fprintf(fd, " || ");
+ fprintf(fd, "("); TestOnly=1;
+ if (Pid != claimnr)
+ fprintf(fd, "(boq == -1 && ");
+ putstmnt(fd, ee->n->lft, e->seqno);
+ if (Pid != claimnr)
+ fprintf(fd, ")");
+ fprintf(fd, ")"); TestOnly=0;
+ break;
+ } }
+ return inh;
+}
+
+int
+putcode(FILE *fd, Sequence *s, Element *nxt, int justguards, int ln, int seqno)
+{ int isg=0; char buf[64];
+
+ NextLab[0] = "continue";
+ filterbad(s->frst);
+
+ switch (s->frst->n->ntyp) {
+ case UNLESS:
+ non_fatal("'unless' inside d_step - ignored", (char *) 0);
+ return putcode(fd, s->frst->n->sl->this, nxt, 0, ln, seqno);
+ case NON_ATOMIC:
+ (void) putcode(fd, s->frst->n->sl->this, ZE, 1, ln, seqno);
+ break;
+ case IF:
+ fprintf(fd, "if (!(");
+ if (!CollectGuards(fd, s->frst, 0)) /* what about boq? */
+ fprintf(fd, "1");
+ fprintf(fd, "))\n\t\t\tcontinue;");
+ isg = 1;
+ break;
+ case '.':
+ if (s->frst->nxt->n->ntyp == DO)
+ { fprintf(fd, "if (!(");
+ if (!CollectGuards(fd, s->frst->nxt, 0))
+ fprintf(fd, "1");
+ fprintf(fd, "))\n\t\t\tcontinue;");
+ isg = 1;
+ }
+ break;
+ case 'R': /* <- can't really happen (it's part of a 'c') */
+ fprintf(fd, "if (!("); TestOnly=1;
+ putstmnt(fd, s->frst->n, s->frst->seqno);
+ fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0;
+ break;
+ case 'r':
+ fprintf(fd, "if (!("); TestOnly=1;
+ putstmnt(fd, s->frst->n, s->frst->seqno);
+ fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0;
+ break;
+ case 's':
+ fprintf(fd, "if (");
+#if 1
+/* 4.2.1 */ if (Pid != claimnr) fprintf(fd, "(boq != -1) || ");
+#endif
+ fprintf(fd, "!("); TestOnly=1;
+ putstmnt(fd, s->frst->n, s->frst->seqno);
+ fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0;
+ break;
+ case 'c':
+ fprintf(fd, "if (!(");
+ if (Pid != claimnr) fprintf(fd, "boq == -1 && ");
+ TestOnly=1;
+ putstmnt(fd, s->frst->n->lft, s->frst->seqno);
+ fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0;
+ break;
+ case ELSE:
+ fprintf(fd, "if (boq != -1 || (");
+ if (separate != 2) fprintf(fd, "trpt->");
+ fprintf(fd, "o_pm&1))\n\t\t\tcontinue;");
+ break;
+ case ASGN: /* new 3.0.8 */
+ fprintf(fd, "IfNotBlocked");
+ break;
+ }
+ if (justguards) return 0;
+
+ fprintf(fd, "\n\t\tsv_save();\n\t\t");
+#if 1
+ fprintf(fd, "reached[%d][%d] = 1;\n\t\t", Pid, seqno);
+ fprintf(fd, "reached[%d][t->st] = 1;\n\t\t", Pid); /* true next state */
+ fprintf(fd, "reached[%d][tt] = 1;\n", Pid); /* true current state */
+#endif
+ sprintf(buf, "Uerror(\"block in d_step seq, line %d\")", ln);
+ NextLab[0] = buf;
+ putCode(fd, s->frst, s->extent, nxt, isg);
+
+ if (nxt)
+ { extern Symbol *Fname;
+ extern int lineno;
+
+ if (FirstTime(nxt->Seqno)
+ && (!(nxt->status & DONE2) || !(nxt->status & D_ATOM)))
+ { fprintf(fd, "S_%.3d_0: /* 1 */\n", nxt->Seqno);
+ nxt->status |= DONE2;
+ LastGoto = 0;
+ }
+ Sourced(nxt->Seqno, 1);
+ lineno = ln;
+ Fname = nxt->n->fn;
+ Mopup(fd);
+ }
+ unskip(s->frst->seqno);
+ return LastGoto;
+}
+
+static void
+putCode(FILE *fd, Element *f, Element *last, Element *next, int isguard)
+{ Element *e, *N;
+ SeqList *h; int i;
+ char NextOpt[64];
+ static int bno = 0;
+
+ for (e = f; e; e = e->nxt)
+ { if (e->status & DONE2)
+ continue;
+ e->status |= DONE2;
+
+ if (!(e->status & D_ATOM))
+ { if (!LastGoto)
+ { fprintf(fd, "\t\tgoto S_%.3d_0;\n",
+ e->Seqno);
+ Dested(e->Seqno);
+ }
+ break;
+ }
+ fprintf(fd, "S_%.3d_0: /* 2 */\n", e->Seqno);
+ LastGoto = 0;
+ Sourced(e->Seqno, 0);
+
+ if (!e->sub)
+ { filterbad(e);
+ switch (e->n->ntyp) {
+ case NON_ATOMIC:
+ h = e->n->sl;
+ putCode(fd, h->this->frst,
+ h->this->extent, e->nxt, 0);
+ break;
+ case BREAK:
+ if (LastGoto) break;
+ if (e->nxt)
+ { i = target( huntele(e->nxt,
+ e->status, -1))->Seqno;
+ fprintf(fd, "\t\tgoto S_%.3d_0; ", i);
+ fprintf(fd, "/* 'break' */\n");
+ Dested(i);
+ } else
+ { if (next)
+ { fprintf(fd, "\t\tgoto S_%.3d_0;",
+ next->Seqno);
+ fprintf(fd, " /* NEXT */\n");
+ Dested(next->Seqno);
+ } else
+ fatal("cannot interpret d_step", 0);
+ }
+ break;
+ case GOTO:
+ if (LastGoto) break;
+ i = huntele( get_lab(e->n,1),
+ e->status, -1)->Seqno;
+ fprintf(fd, "\t\tgoto S_%.3d_0; ", i);
+ fprintf(fd, "/* 'goto' */\n");
+ Dested(i);
+ break;
+ case '.':
+ if (LastGoto) break;
+ if (e->nxt && (e->nxt->status & DONE2))
+ { i = e->nxt?e->nxt->Seqno:0;
+ fprintf(fd, "\t\tgoto S_%.3d_0;", i);
+ fprintf(fd, " /* '.' */\n");
+ Dested(i);
+ }
+ break;
+ default:
+ putskip(e->seqno);
+ GenCode = 1; IsGuard = isguard;
+ fprintf(fd, "\t\t");
+ putstmnt(fd, e->n, e->seqno);
+ fprintf(fd, ";\n");
+ GenCode = IsGuard = isguard = LastGoto = 0;
+ break;
+ }
+ i = e->nxt?e->nxt->Seqno:0;
+ if (e->nxt && e->nxt->status & DONE2 && !LastGoto)
+ { fprintf(fd, "\t\tgoto S_%.3d_0; ", i);
+ fprintf(fd, "/* ';' */\n");
+ Dested(i);
+ break;
+ }
+ } else
+ { for (h = e->sub, i=1; h; h = h->nxt, i++)
+ { sprintf(NextOpt, "goto S_%.3d_%d",
+ e->Seqno, i);
+ NextLab[++Level] = NextOpt;
+ N = (e->n && e->n->ntyp == DO) ? e : e->nxt;
+ putCode(fd, h->this->frst,
+ h->this->extent, N, 1);
+ Level--;
+ fprintf(fd, "%s: /* 3 */\n", &NextOpt[5]);
+ LastGoto = 0;
+ }
+ if (!LastGoto)
+ { fprintf(fd, "\t\tUerror(\"blocking sel ");
+ fprintf(fd, "in d_step (nr.%d, near line %d)\");\n",
+ bno++, (e->n)?e->n->ln:0);
+ LastGoto = 0;
+ }
+ }
+ if (e == last)
+ { if (!LastGoto && next)
+ { fprintf(fd, "\t\tgoto S_%.3d_0;\n",
+ next->Seqno);
+ Dested(next->Seqno);
+ }
+ break;
+ } }
+}
--- /dev/null
+/***** spin: flow.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern Symbol *Fname;
+extern int nr_errs, lineno, verbose;
+extern short has_unless, has_badelse;
+
+Element *Al_El = ZE;
+Label *labtab = (Label *) 0;
+int Unique=0, Elcnt=0, DstepStart = -1;
+
+static Lbreak *breakstack = (Lbreak *) 0;
+static Lextok *innermost;
+static SeqList *cur_s = (SeqList *) 0;
+static int break_id=0;
+
+static Element *if_seq(Lextok *);
+static Element *new_el(Lextok *);
+static Element *unless_seq(Lextok *);
+static void add_el(Element *, Sequence *);
+static void attach_escape(Sequence *, Sequence *);
+static void mov_lab(Symbol *, Element *, Element *);
+static void walk_atomic(Element *, Element *, int);
+
+void
+open_seq(int top)
+{ SeqList *t;
+ Sequence *s = (Sequence *) emalloc(sizeof(Sequence));
+
+ t = seqlist(s, cur_s);
+ cur_s = t;
+ if (top) Elcnt = 1;
+}
+
+void
+rem_Seq(void)
+{
+ DstepStart = Unique;
+}
+
+void
+unrem_Seq(void)
+{
+ DstepStart = -1;
+}
+
+static int
+Rjumpslocal(Element *q, Element *stop)
+{ Element *lb, *f;
+ SeqList *h;
+
+ /* allow no jumps out of a d_step sequence */
+ for (f = q; f && f != stop; f = f->nxt)
+ { if (f && f->n && f->n->ntyp == GOTO)
+ { lb = get_lab(f->n, 0);
+ if (!lb || lb->Seqno < DstepStart)
+ { lineno = f->n->ln;
+ Fname = f->n->fn;
+ return 0;
+ } }
+ for (h = f->sub; h; h = h->nxt)
+ { if (!Rjumpslocal(h->this->frst, h->this->last))
+ return 0;
+
+ } }
+ return 1;
+}
+
+void
+cross_dsteps(Lextok *a, Lextok *b)
+{
+ if (a && b
+ && a->indstep != b->indstep)
+ { lineno = a->ln;
+ Fname = a->fn;
+ fatal("jump into d_step sequence", (char *) 0);
+ }
+}
+
+int
+is_skip(Lextok *n)
+{
+ return (n->ntyp == PRINT
+ || n->ntyp == PRINTM
+ || (n->ntyp == 'c'
+ && n->lft
+ && n->lft->ntyp == CONST
+ && n->lft->val == 1));
+}
+
+void
+check_sequence(Sequence *s)
+{ Element *e, *le = ZE;
+ Lextok *n;
+ int cnt = 0;
+
+ for (e = s->frst; e; le = e, e = e->nxt)
+ { n = e->n;
+ if (is_skip(n) && !has_lab(e, 0))
+ { cnt++;
+ if (cnt > 1
+ && n->ntyp != PRINT
+ && n->ntyp != PRINTM)
+ { if (verbose&32)
+ printf("spin: line %d %s, redundant skip\n",
+ n->ln, n->fn->name);
+ if (e != s->frst
+ && e != s->last
+ && e != s->extent)
+ { e->status |= DONE; /* not unreachable */
+ le->nxt = e->nxt; /* remove it */
+ e = le;
+ }
+ }
+ } else
+ cnt = 0;
+ }
+}
+
+void
+prune_opts(Lextok *n)
+{ SeqList *l;
+ extern Symbol *context;
+ extern char *claimproc;
+
+ if (!n
+ || (context && claimproc && strcmp(context->name, claimproc) == 0))
+ return;
+
+ for (l = n->sl; l; l = l->nxt) /* find sequences of unlabeled skips */
+ check_sequence(l->this);
+}
+
+Sequence *
+close_seq(int nottop)
+{ Sequence *s = cur_s->this;
+ Symbol *z;
+
+ if (nottop > 0 && (z = has_lab(s->frst, 0)))
+ { printf("error: (%s:%d) label %s placed incorrectly\n",
+ (s->frst->n)?s->frst->n->fn->name:"-",
+ (s->frst->n)?s->frst->n->ln:0,
+ z->name);
+ switch (nottop) {
+ case 1:
+ printf("=====> stmnt unless Label: stmnt\n");
+ printf("sorry, cannot jump to the guard of an\n");
+ printf("escape (it is not a unique state)\n");
+ break;
+ case 2:
+ printf("=====> instead of ");
+ printf("\"Label: stmnt unless stmnt\"\n");
+ printf("=====> always use ");
+ printf("\"Label: { stmnt unless stmnt }\"\n");
+ break;
+ case 3:
+ printf("=====> instead of ");
+ printf("\"atomic { Label: statement ... }\"\n");
+ printf("=====> always use ");
+ printf("\"Label: atomic { statement ... }\"\n");
+ break;
+ case 4:
+ printf("=====> instead of ");
+ printf("\"d_step { Label: statement ... }\"\n");
+ printf("=====> always use ");
+ printf("\"Label: d_step { statement ... }\"\n");
+ break;
+ case 5:
+ printf("=====> instead of ");
+ printf("\"{ Label: statement ... }\"\n");
+ printf("=====> always use ");
+ printf("\"Label: { statement ... }\"\n");
+ break;
+ case 6:
+ printf("=====>instead of\n");
+ printf(" do (or if)\n");
+ printf(" :: ...\n");
+ printf(" :: Label: statement\n");
+ printf(" od (of fi)\n");
+ printf("=====>always use\n");
+ printf("Label: do (or if)\n");
+ printf(" :: ...\n");
+ printf(" :: statement\n");
+ printf(" od (or fi)\n");
+ break;
+ case 7:
+ printf("cannot happen - labels\n");
+ break;
+ }
+ alldone(1);
+ }
+
+ if (nottop == 4
+ && !Rjumpslocal(s->frst, s->last))
+ fatal("non_local jump in d_step sequence", (char *) 0);
+
+ cur_s = cur_s->nxt;
+ s->maxel = Elcnt;
+ s->extent = s->last;
+ if (!s->last)
+ fatal("sequence must have at least one statement", (char *) 0);
+ return s;
+}
+
+Lextok *
+do_unless(Lextok *No, Lextok *Es)
+{ SeqList *Sl;
+ Lextok *Re = nn(ZN, UNLESS, ZN, ZN);
+ Re->ln = No->ln;
+ Re->fn = No->fn;
+
+ has_unless++;
+ if (Es->ntyp == NON_ATOMIC)
+ Sl = Es->sl;
+ else
+ { open_seq(0); add_seq(Es);
+ Sl = seqlist(close_seq(1), 0);
+ }
+
+ if (No->ntyp == NON_ATOMIC)
+ { No->sl->nxt = Sl;
+ Sl = No->sl;
+ } else if (No->ntyp == ':'
+ && (No->lft->ntyp == NON_ATOMIC
+ || No->lft->ntyp == ATOMIC
+ || No->lft->ntyp == D_STEP))
+ {
+ int tok = No->lft->ntyp;
+
+ No->lft->sl->nxt = Sl;
+ Re->sl = No->lft->sl;
+
+ open_seq(0); add_seq(Re);
+ Re = nn(ZN, tok, ZN, ZN);
+ Re->sl = seqlist(close_seq(7), 0);
+ Re->ln = No->ln;
+ Re->fn = No->fn;
+
+ Re = nn(No, ':', Re, ZN); /* lift label */
+ Re->ln = No->ln;
+ Re->fn = No->fn;
+ return Re;
+ } else
+ { open_seq(0); add_seq(No);
+ Sl = seqlist(close_seq(2), Sl);
+ }
+
+ Re->sl = Sl;
+ return Re;
+}
+
+SeqList *
+seqlist(Sequence *s, SeqList *r)
+{ SeqList *t = (SeqList *) emalloc(sizeof(SeqList));
+
+ t->this = s;
+ t->nxt = r;
+ return t;
+}
+
+static Element *
+new_el(Lextok *n)
+{ Element *m;
+
+ if (n)
+ { if (n->ntyp == IF || n->ntyp == DO)
+ return if_seq(n);
+ if (n->ntyp == UNLESS)
+ return unless_seq(n);
+ }
+ m = (Element *) emalloc(sizeof(Element));
+ m->n = n;
+ m->seqno = Elcnt++;
+ m->Seqno = Unique++;
+ m->Nxt = Al_El; Al_El = m;
+ return m;
+}
+
+static int
+has_chanref(Lextok *n)
+{
+ if (!n) return 0;
+
+ switch (n->ntyp) {
+ case 's': case 'r':
+#if 0
+ case 'R': case LEN:
+#endif
+ case FULL: case NFULL:
+ case EMPTY: case NEMPTY:
+ return 1;
+ default:
+ break;
+ }
+ if (has_chanref(n->lft))
+ return 1;
+
+ return has_chanref(n->rgt);
+}
+
+void
+loose_ends(void) /* properly tie-up ends of sub-sequences */
+{ Element *e, *f;
+
+ for (e = Al_El; e; e = e->Nxt)
+ { if (!e->n
+ || !e->nxt)
+ continue;
+ switch (e->n->ntyp) {
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ f = e->nxt;
+ while (f && f->n->ntyp == '.')
+ f = f->nxt;
+ if (0) printf("link %d, {%d .. %d} -> %d (ntyp=%d) was %d\n",
+ e->seqno,
+ e->n->sl->this->frst->seqno,
+ e->n->sl->this->last->seqno,
+ f?f->seqno:-1, f?f->n->ntyp:-1,
+ e->n->sl->this->last->nxt?e->n->sl->this->last->nxt->seqno:-1);
+ if (!e->n->sl->this->last->nxt)
+ e->n->sl->this->last->nxt = f;
+ else
+ { if (e->n->sl->this->last->nxt->n->ntyp != GOTO)
+ { if (!f || e->n->sl->this->last->nxt->seqno != f->seqno)
+ non_fatal("unexpected: loose ends", (char *)0);
+ } else
+ e->n->sl->this->last = e->n->sl->this->last->nxt;
+ /*
+ * fix_dest can push a goto into the nxt position
+ * in that case the goto wins and f is not needed
+ * but the last fields needs adjusting
+ */
+ }
+ break;
+ } }
+}
+
+static Element *
+if_seq(Lextok *n)
+{ int tok = n->ntyp;
+ SeqList *s = n->sl;
+ Element *e = new_el(ZN);
+ Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */
+ SeqList *z, *prev_z = (SeqList *) 0;
+ SeqList *move_else = (SeqList *) 0; /* to end of optionlist */
+ int ref_chans = 0;
+
+ for (z = s; z; z = z->nxt)
+ { if (!z->this->frst)
+ continue;
+ if (z->this->frst->n->ntyp == ELSE)
+ { if (move_else)
+ fatal("duplicate `else'", (char *) 0);
+ if (z->nxt) /* is not already at the end */
+ { move_else = z;
+ if (prev_z)
+ prev_z->nxt = z->nxt;
+ else
+ s = n->sl = z->nxt;
+ continue;
+ }
+ } else
+ ref_chans |= has_chanref(z->this->frst->n);
+ prev_z = z;
+ }
+ if (move_else)
+ { move_else->nxt = (SeqList *) 0;
+ /* if there is no prev, then else was at the end */
+ if (!prev_z) fatal("cannot happen - if_seq", (char *) 0);
+ prev_z->nxt = move_else;
+ prev_z = move_else;
+ }
+ if (prev_z
+ && ref_chans
+ && prev_z->this->frst->n->ntyp == ELSE)
+ { prev_z->this->frst->n->val = 1;
+ has_badelse++;
+ non_fatal("dubious use of 'else' combined with i/o,",
+ (char *)0);
+ nr_errs--;
+ }
+
+ e->n = nn(n, tok, ZN, ZN);
+ e->n->sl = s; /* preserve as info only */
+ e->sub = s;
+ for (z = s; z; prev_z = z, z = z->nxt)
+ add_el(t, z->this); /* append target */
+ if (tok == DO)
+ { add_el(t, cur_s->this); /* target upfront */
+ t = new_el(nn(n, BREAK, ZN, ZN)); /* break target */
+ set_lab(break_dest(), t); /* new exit */
+ breakstack = breakstack->nxt; /* pop stack */
+ }
+ add_el(e, cur_s->this);
+ add_el(t, cur_s->this);
+ return e; /* destination node for label */
+}
+
+static void
+escape_el(Element *f, Sequence *e)
+{ SeqList *z;
+
+ for (z = f->esc; z; z = z->nxt)
+ if (z->this == e)
+ return; /* already there */
+
+ /* cover the lower-level escapes of this state */
+ for (z = f->esc; z; z = z->nxt)
+ attach_escape(z->this, e);
+
+ /* now attach escape to the state itself */
+
+ f->esc = seqlist(e, f->esc); /* in lifo order... */
+#ifdef DEBUG
+ printf("attach %d (", e->frst->Seqno);
+ comment(stdout, e->frst->n, 0);
+ printf(") to %d (", f->Seqno);
+ comment(stdout, f->n, 0);
+ printf(")\n");
+#endif
+ switch (f->n->ntyp) {
+ case UNLESS:
+ attach_escape(f->sub->this, e);
+ break;
+ case IF:
+ case DO:
+ for (z = f->sub; z; z = z->nxt)
+ attach_escape(z->this, e);
+ break;
+ case D_STEP:
+ /* attach only to the guard stmnt */
+ escape_el(f->n->sl->this->frst, e);
+ break;
+ case ATOMIC:
+ case NON_ATOMIC:
+ /* attach to all stmnts */
+ attach_escape(f->n->sl->this, e);
+ break;
+ }
+}
+
+static void
+attach_escape(Sequence *n, Sequence *e)
+{ Element *f;
+
+ for (f = n->frst; f; f = f->nxt)
+ { escape_el(f, e);
+ if (f == n->extent)
+ break;
+ }
+}
+
+static Element *
+unless_seq(Lextok *n)
+{ SeqList *s = n->sl;
+ Element *e = new_el(ZN);
+ Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */
+ SeqList *z;
+
+ e->n = nn(n, UNLESS, ZN, ZN);
+ e->n->sl = s; /* info only */
+ e->sub = s;
+
+ /* need 2 sequences: normal execution and escape */
+ if (!s || !s->nxt || s->nxt->nxt)
+ fatal("unexpected unless structure", (char *)0);
+
+ /* append the target state to both */
+ for (z = s; z; z = z->nxt)
+ add_el(t, z->this);
+
+ /* attach escapes to all states in normal sequence */
+ attach_escape(s->this, s->nxt->this);
+
+ add_el(e, cur_s->this);
+ add_el(t, cur_s->this);
+#ifdef DEBUG
+ printf("unless element (%d,%d):\n", e->Seqno, t->Seqno);
+ for (z = s; z; z = z->nxt)
+ { Element *x; printf("\t%d,%d,%d :: ",
+ z->this->frst->Seqno,
+ z->this->extent->Seqno,
+ z->this->last->Seqno);
+ for (x = z->this->frst; x; x = x->nxt)
+ printf("(%d)", x->Seqno);
+ printf("\n");
+ }
+#endif
+ return e;
+}
+
+Element *
+mk_skip(void)
+{ Lextok *t = nn(ZN, CONST, ZN, ZN);
+ t->val = 1;
+ return new_el(nn(ZN, 'c', t, ZN));
+}
+
+static void
+add_el(Element *e, Sequence *s)
+{
+ if (e->n->ntyp == GOTO)
+ { Symbol *z = has_lab(e, (1|2|4));
+ if (z)
+ { Element *y; /* insert a skip */
+ y = mk_skip();
+ mov_lab(z, e, y); /* inherit label */
+ add_el(y, s);
+ } }
+#ifdef DEBUG
+ printf("add_el %d after %d -- ",
+ e->Seqno, (s->last)?s->last->Seqno:-1);
+ comment(stdout, e->n, 0);
+ printf("\n");
+#endif
+ if (!s->frst)
+ s->frst = e;
+ else
+ s->last->nxt = e;
+ s->last = e;
+}
+
+static Element *
+colons(Lextok *n)
+{
+ if (!n)
+ return ZE;
+ if (n->ntyp == ':')
+ { Element *e = colons(n->lft);
+ set_lab(n->sym, e);
+ return e;
+ }
+ innermost = n;
+ return new_el(n);
+}
+
+void
+add_seq(Lextok *n)
+{ Element *e;
+
+ if (!n) return;
+ innermost = n;
+ e = colons(n);
+ if (innermost->ntyp != IF
+ && innermost->ntyp != DO
+ && innermost->ntyp != UNLESS)
+ add_el(e, cur_s->this);
+}
+
+void
+set_lab(Symbol *s, Element *e)
+{ Label *l; extern Symbol *context;
+
+ if (!s) return;
+ for (l = labtab; l; l = l->nxt)
+ if (l->s == s && l->c == context)
+ { non_fatal("label %s redeclared", s->name);
+ break;
+ }
+ l = (Label *) emalloc(sizeof(Label));
+ l->s = s;
+ l->c = context;
+ l->e = e;
+ l->nxt = labtab;
+ labtab = l;
+}
+
+Element *
+get_lab(Lextok *n, int md)
+{ Label *l;
+ Symbol *s = n->sym;
+
+ for (l = labtab; l; l = l->nxt)
+ if (s == l->s)
+ return (l->e);
+
+ lineno = n->ln;
+ Fname = n->fn;
+ if (md) fatal("undefined label %s", s->name);
+ return ZE;
+}
+
+Symbol *
+has_lab(Element *e, int special)
+{ Label *l;
+
+ for (l = labtab; l; l = l->nxt)
+ { if (e != l->e)
+ continue;
+ if (special == 0
+ || ((special&1) && !strncmp(l->s->name, "accept", 6))
+ || ((special&2) && !strncmp(l->s->name, "end", 3))
+ || ((special&4) && !strncmp(l->s->name, "progress", 8)))
+ return (l->s);
+ }
+ return ZS;
+}
+
+static void
+mov_lab(Symbol *z, Element *e, Element *y)
+{ Label *l;
+
+ for (l = labtab; l; l = l->nxt)
+ if (e == l->e)
+ { l->e = y;
+ return;
+ }
+ if (e->n)
+ { lineno = e->n->ln;
+ Fname = e->n->fn;
+ }
+ fatal("cannot happen - mov_lab %s", z->name);
+}
+
+void
+fix_dest(Symbol *c, Symbol *a) /* c:label name, a:proctype name */
+{ Label *l; extern Symbol *context;
+
+#if 0
+ printf("ref to label '%s' in proctype '%s', search:\n",
+ c->name, a->name);
+ for (l = labtab; l; l = l->nxt)
+ printf(" %s in %s\n", l->s->name, l->c->name);
+#endif
+
+ for (l = labtab; l; l = l->nxt)
+ { if (strcmp(c->name, l->s->name) == 0
+ && strcmp(a->name, l->c->name) == 0) /* ? */
+ break;
+ }
+ if (!l)
+ { printf("spin: label '%s' (proctype %s)\n", c->name, a->name);
+ non_fatal("unknown label '%s'", c->name);
+ if (context == a)
+ printf("spin: cannot remote ref a label inside the same proctype\n");
+ return;
+ }
+ if (!l->e || !l->e->n)
+ fatal("fix_dest error (%s)", c->name);
+ if (l->e->n->ntyp == GOTO)
+ { Element *y = (Element *) emalloc(sizeof(Element));
+ int keep_ln = l->e->n->ln;
+ Symbol *keep_fn = l->e->n->fn;
+
+ /* insert skip - or target is optimized away */
+ y->n = l->e->n; /* copy of the goto */
+ y->seqno = find_maxel(a); /* unique seqno within proc */
+ y->nxt = l->e->nxt;
+ y->Seqno = Unique++; y->Nxt = Al_El; Al_El = y;
+
+ /* turn the original element+seqno into a skip */
+ l->e->n = nn(ZN, 'c', nn(ZN, CONST, ZN, ZN), ZN);
+ l->e->n->ln = l->e->n->lft->ln = keep_ln;
+ l->e->n->fn = l->e->n->lft->fn = keep_fn;
+ l->e->n->lft->val = 1;
+ l->e->nxt = y; /* append the goto */
+ }
+ l->e->status |= CHECK2; /* treat as if global */
+ if (l->e->status & (ATOM | L_ATOM | D_ATOM))
+ { non_fatal("cannot reference label inside atomic or d_step (%s)",
+ c->name);
+ }
+}
+
+int
+find_lab(Symbol *s, Symbol *c, int markit)
+{ Label *l;
+
+ for (l = labtab; l; l = l->nxt)
+ { if (strcmp(s->name, l->s->name) == 0
+ && strcmp(c->name, l->c->name) == 0)
+ { l->visible |= markit;
+ return (l->e->seqno);
+ } }
+ return 0;
+}
+
+void
+pushbreak(void)
+{ Lbreak *r = (Lbreak *) emalloc(sizeof(Lbreak));
+ Symbol *l;
+ char buf[64];
+
+ sprintf(buf, ":b%d", break_id++);
+ l = lookup(buf);
+ r->l = l;
+ r->nxt = breakstack;
+ breakstack = r;
+}
+
+Symbol *
+break_dest(void)
+{
+ if (!breakstack)
+ fatal("misplaced break statement", (char *)0);
+ return breakstack->l;
+}
+
+void
+make_atomic(Sequence *s, int added)
+{ Element *f;
+
+ walk_atomic(s->frst, s->last, added);
+
+ f = s->last;
+ switch (f->n->ntyp) { /* is last step basic stmnt or sequence ? */
+ case NON_ATOMIC:
+ case ATOMIC:
+ /* redo and search for the last step of that sequence */
+ make_atomic(f->n->sl->this, added);
+ break;
+
+ case UNLESS:
+ /* escapes are folded into main sequence */
+ make_atomic(f->sub->this, added);
+ break;
+
+ default:
+ f->status &= ~ATOM;
+ f->status |= L_ATOM;
+ break;
+ }
+}
+
+static void
+walk_atomic(Element *a, Element *b, int added)
+{ Element *f; Symbol *ofn; int oln;
+ SeqList *h;
+
+ ofn = Fname;
+ oln = lineno;
+ for (f = a; ; f = f->nxt)
+ { f->status |= (ATOM|added);
+ switch (f->n->ntyp) {
+ case ATOMIC:
+ if (verbose&32)
+ printf("spin: warning, line %3d %s, atomic inside %s (ignored)\n",
+ f->n->ln, f->n->fn->name, (added)?"d_step":"atomic");
+ goto mknonat;
+ case D_STEP:
+ if (!(verbose&32))
+ { if (added) goto mknonat;
+ break;
+ }
+ printf("spin: warning, line %3d %s, d_step inside ",
+ f->n->ln, f->n->fn->name);
+ if (added)
+ { printf("d_step (ignored)\n");
+ goto mknonat;
+ }
+ printf("atomic\n");
+ break;
+ case NON_ATOMIC:
+mknonat: f->n->ntyp = NON_ATOMIC; /* can jump here */
+ h = f->n->sl;
+ walk_atomic(h->this->frst, h->this->last, added);
+ break;
+ case UNLESS:
+ if (added)
+ { printf("spin: error, line %3d %s, unless in d_step (ignored)\n",
+ f->n->ln, f->n->fn->name);
+ }
+ }
+ for (h = f->sub; h; h = h->nxt)
+ walk_atomic(h->this->frst, h->this->last, added);
+ if (f == b)
+ break;
+ }
+ Fname = ofn;
+ lineno = oln;
+}
+
+void
+dumplabels(void)
+{ Label *l;
+
+ for (l = labtab; l; l = l->nxt)
+ if (l->c != 0 && l->s->name[0] != ':')
+ printf("label %s %d <%s>\n",
+ l->s->name, l->e->seqno, l->c->name);
+}
--- /dev/null
+/***** spin: guided.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "y.tab.h"
+
+extern RunList *run, *X;
+extern Element *Al_El;
+extern Symbol *Fname, *oFname;
+extern int verbose, lineno, xspin, jumpsteps, depth, merger, cutoff;
+extern int nproc, nstop, Tval, ntrail, columns;
+extern short Have_claim, Skip_claim;
+extern void ana_src(int, int);
+
+int TstOnly = 0, pno;
+
+static int lastclaim = -1;
+static FILE *fd;
+static void lost_trail(void);
+
+static void
+whichproc(int p)
+{ RunList *oX;
+
+ for (oX = run; oX; oX = oX->nxt)
+ if (oX->pid == p)
+ { printf("(%s) ", oX->n->name);
+ break;
+ }
+}
+
+static int
+newer(char *f1, char *f2)
+{
+#if defined(WIN32) || defined(WIN64)
+ struct _stat x, y;
+#else
+ struct stat x, y;
+#endif
+
+ if (stat(f1, (struct stat *)&x) < 0) return 0;
+ if (stat(f2, (struct stat *)&y) < 0) return 1;
+ if (x.st_mtime < y.st_mtime) return 0;
+
+ return 1;
+}
+
+void
+hookup(void)
+{ Element *e;
+
+ for (e = Al_El; e; e = e->Nxt)
+ if (e->n
+ && (e->n->ntyp == ATOMIC
+ || e->n->ntyp == NON_ATOMIC
+ || e->n->ntyp == D_STEP))
+ (void) huntstart(e);
+}
+
+int
+not_claim(void)
+{
+ return (!Have_claim || !X || X->pid != 0);
+}
+
+void
+match_trail(void)
+{ int i, a, nst;
+ Element *dothis;
+ char snap[512], *q;
+
+ /*
+ * if source model name is leader.pml
+ * look for the trail file under these names:
+ * leader.pml.trail
+ * leader.pml.tra
+ * leader.trail
+ * leader.tra
+ */
+
+ if (ntrail)
+ sprintf(snap, "%s%d.trail", oFname->name, ntrail);
+ else
+ sprintf(snap, "%s.trail", oFname->name);
+
+ if ((fd = fopen(snap, "r")) == NULL)
+ { snap[strlen(snap)-2] = '\0'; /* .tra */
+ if ((fd = fopen(snap, "r")) == NULL)
+ { if ((q = strchr(oFname->name, '.')) != NULL)
+ { *q = '\0';
+ if (ntrail)
+ sprintf(snap, "%s%d.trail",
+ oFname->name, ntrail);
+ else
+ sprintf(snap, "%s.trail",
+ oFname->name);
+ *q = '.';
+
+ if ((fd = fopen(snap, "r")) != NULL)
+ goto okay;
+
+ snap[strlen(snap)-2] = '\0'; /* last try */
+ if ((fd = fopen(snap, "r")) != NULL)
+ goto okay;
+ }
+ printf("spin: cannot find trail file\n");
+ alldone(1);
+ } }
+okay:
+ if (xspin == 0 && newer(oFname->name, snap))
+ printf("spin: warning, \"%s\" is newer than %s\n",
+ oFname->name, snap);
+
+ Tval = 1;
+
+ /*
+ * sets Tval because timeouts may be part of trail
+ * this used to also set m_loss to 1, but that is
+ * better handled with the runtime -m flag
+ */
+
+ hookup();
+
+ while (fscanf(fd, "%d:%d:%d\n", &depth, &pno, &nst) == 3)
+ { if (depth == -2) { start_claim(pno); continue; }
+ if (depth == -4) { merger = 1; ana_src(0, 1); continue; }
+ if (depth == -1)
+ { if (verbose)
+ { if (columns == 2)
+ dotag(stdout, " CYCLE>\n");
+ else
+ dotag(stdout, "<<<<<START OF CYCLE>>>>>\n");
+ }
+ continue;
+ }
+
+ if (cutoff > 0 && depth >= cutoff)
+ { printf("-------------\n");
+ printf("depth-limit (-u%d steps) reached\n", cutoff);
+ break;
+ }
+
+ if (Skip_claim && pno == 0) continue;
+
+ for (dothis = Al_El; dothis; dothis = dothis->Nxt)
+ { if (dothis->Seqno == nst)
+ break;
+ }
+ if (!dothis)
+ { printf("%3d: proc %d, no matching stmnt %d\n",
+ depth, pno - Have_claim, nst);
+ lost_trail();
+ }
+
+ i = nproc - nstop + Skip_claim;
+
+ if (dothis->n->ntyp == '@')
+ { if (pno == i-1)
+ { run = run->nxt;
+ nstop++;
+ if (verbose&4)
+ { if (columns == 2)
+ { dotag(stdout, "<end>\n");
+ continue;
+ }
+ if (Have_claim && pno == 0)
+ printf("%3d: claim terminates\n",
+ depth);
+ else
+ printf("%3d: proc %d terminates\n",
+ depth, pno - Have_claim);
+ }
+ continue;
+ }
+ if (pno <= 1) continue; /* init dies before never */
+ printf("%3d: stop error, ", depth);
+ printf("proc %d (i=%d) trans %d, %c\n",
+ pno - Have_claim, i, nst, dothis->n->ntyp);
+ lost_trail();
+ }
+
+ if (!xspin && (verbose&32))
+ { printf("i=%d pno %d\n", i, pno);
+ }
+
+ for (X = run; X; X = X->nxt)
+ { if (--i == pno)
+ break;
+ }
+
+ if (!X)
+ { if (verbose&32)
+ { printf("%3d: no process %d (step %d)\n", depth, pno - Have_claim, nst);
+ printf(" max %d (%d - %d + %d) claim %d",
+ nproc - nstop + Skip_claim,
+ nproc, nstop, Skip_claim, Have_claim);
+ printf("active processes:\n");
+ for (X = run; X; X = X->nxt)
+ { printf("\tpid %d\tproctype %s\n", X->pid, X->n->name);
+ }
+ printf("\n");
+ continue;
+ } else
+ { printf("%3d:\tproc %d (?) ", depth, pno);
+ lost_trail();
+ }
+ } else
+ { X->pc = dothis;
+ }
+
+ lineno = dothis->n->ln;
+ Fname = dothis->n->fn;
+
+ if (dothis->n->ntyp == D_STEP)
+ { Element *g, *og = dothis;
+ do {
+ g = eval_sub(og);
+ if (g && depth >= jumpsteps
+ && ((verbose&32) || ((verbose&4) && not_claim())))
+ { if (columns != 2)
+ { p_talk(og, 1);
+
+ if (og->n->ntyp == D_STEP)
+ og = og->n->sl->this->frst;
+
+ printf("\t[");
+ comment(stdout, og->n, 0);
+ printf("]\n");
+ }
+ if (verbose&1) dumpglobals();
+ if (verbose&2) dumplocal(X);
+ if (xspin) printf("\n");
+ }
+ og = g;
+ } while (g && g != dothis->nxt);
+ if (X != NULL)
+ { X->pc = g?huntele(g, 0, -1):g;
+ }
+ } else
+ {
+keepgoing: if (dothis->merge_start)
+ a = dothis->merge_start;
+ else
+ a = dothis->merge;
+
+ if (X != NULL)
+ { X->pc = eval_sub(dothis);
+ if (X->pc) X->pc = huntele(X->pc, 0, a);
+ }
+
+ if (depth >= jumpsteps
+ && ((verbose&32) || ((verbose&4) && not_claim()))) /* -v or -p */
+ { if (columns != 2)
+ { p_talk(dothis, 1);
+
+ if (dothis->n->ntyp == D_STEP)
+ dothis = dothis->n->sl->this->frst;
+
+ printf("\t[");
+ comment(stdout, dothis->n, 0);
+ printf("]");
+ if (a && (verbose&32))
+ printf("\t<merge %d now @%d>",
+ dothis->merge,
+ (X && X->pc)?X->pc->seqno:-1);
+ printf("\n");
+ }
+ if (verbose&1) dumpglobals();
+ if (verbose&2) dumplocal(X);
+ if (xspin) printf("\n");
+
+ if (X && !X->pc)
+ { X->pc = dothis;
+ printf("\ttransition failed\n");
+ a = 0; /* avoid inf loop */
+ }
+ }
+ if (a && X && X->pc && X->pc->seqno != a)
+ { dothis = X->pc;
+ goto keepgoing;
+ } }
+
+ if (Have_claim && X && X->pid == 0
+ && dothis->n
+ && lastclaim != dothis->n->ln)
+ { lastclaim = dothis->n->ln;
+ if (columns == 2)
+ { char t[128];
+ sprintf(t, "#%d", lastclaim);
+ pstext(0, t);
+ } else
+ {
+ printf("Never claim moves to line %d\t[", lastclaim);
+ comment(stdout, dothis->n, 0);
+ printf("]\n");
+ } } }
+ printf("spin: trail ends after %d steps\n", depth);
+ wrapup(0);
+}
+
+static void
+lost_trail(void)
+{ int d, p, n, l;
+
+ while (fscanf(fd, "%d:%d:%d:%d\n", &d, &p, &n, &l) == 4)
+ { printf("step %d: proc %d ", d, p); whichproc(p);
+ printf("(state %d) - d %d\n", n, l);
+ }
+ wrapup(1); /* no return */
+}
+
+int
+pc_value(Lextok *n)
+{ int i = nproc - nstop;
+ int pid = eval(n);
+ RunList *Y;
+
+ for (Y = run; Y; Y = Y->nxt)
+ { if (--i == pid)
+ return Y->pc->seqno;
+ }
+ return 0;
+}
--- /dev/null
+/***** spin: main.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include <stdlib.h>
+#include "spin.h"
+#include "version.h"
+#include <signal.h>
+/* #include <malloc.h> */
+#include <time.h>
+#ifdef PC
+#include <io.h>
+extern int unlink(const char *);
+#else
+#include <unistd.h>
+#endif
+#include "y.tab.h"
+
+extern int DstepStart, lineno, tl_terse;
+extern FILE *yyin, *yyout, *tl_out;
+extern Symbol *context;
+extern char *claimproc;
+extern void repro_src(void);
+extern void qhide(int);
+
+Symbol *Fname, *oFname;
+
+int Etimeouts; /* nr timeouts in program */
+int Ntimeouts; /* nr timeouts in never claim */
+int analyze, columns, dumptab, has_remote, has_remvar;
+int interactive, jumpsteps, m_loss, nr_errs, cutoff;
+int s_trail, ntrail, verbose, xspin, notabs, rvopt;
+int no_print, no_wrapup, Caccess, limited_vis, like_java;
+int separate; /* separate compilation */
+int export_ast; /* pangen5.c */
+
+int merger = 1, deadvar = 1, ccache = 1;
+
+static int preprocessonly, SeedUsed;
+static int seedy; /* be verbose about chosen seed */
+static int inlineonly; /* show inlined code */
+static int dataflow = 1;
+
+#if 0
+meaning of flags on verbose:
+ 1 -g global variable values
+ 2 -l local variable values
+ 4 -p all process actions
+ 8 -r receives
+ 16 -s sends
+ 32 -v verbose
+ 64 -w very verbose
+#endif
+
+static char Operator[] = "operator: ";
+static char Keyword[] = "keyword: ";
+static char Function[] = "function-name: ";
+static char **add_ltl = (char **)0;
+static char **ltl_file = (char **)0;
+static char **nvr_file = (char **)0;
+static char *PreArg[64];
+static int PreCnt = 0;
+static char out1[64];
+void explain(int);
+
+#ifndef CPP
+ /* OS2: "spin -Picc -E/Pd+ -E/Q+" */
+ /* Visual C++: "spin -PCL -E/E */
+#ifdef PC
+#define CPP "gcc -E -x c" /* most systems have gcc anyway */
+ /* else use "cpp" */
+#else
+#ifdef SOLARIS
+#define CPP "/usr/ccs/lib/cpp"
+#else
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#define CPP "cpp"
+#else
+#define CPP "/lib/cpp" /* classic Unix systems */
+#endif
+#endif
+#endif
+
+#endif
+static char *PreProc = CPP;
+extern int depth; /* at least some steps were made */
+
+void
+alldone(int estatus)
+{
+ if (preprocessonly == 0
+ && strlen(out1) > 0)
+ (void) unlink((const char *)out1);
+
+ if (seedy && !analyze && !export_ast
+ && !s_trail && !preprocessonly && depth > 0)
+ printf("seed used: %d\n", SeedUsed);
+
+ if (xspin && (analyze || s_trail))
+ { if (estatus)
+ printf("spin: %d error(s) - aborting\n",
+ estatus);
+ else
+ printf("Exit-Status 0\n");
+ }
+ exit(estatus);
+}
+
+void
+preprocess(char *a, char *b, int a_tmp)
+{ char precmd[1024], cmd[2048]; int i;
+#ifdef PC
+ extern int try_zpp(char *, char *);
+ if (PreCnt == 0 && try_zpp(a, b)) goto out;
+#endif
+ strcpy(precmd, PreProc);
+ for (i = 1; i <= PreCnt; i++)
+ { strcat(precmd, " ");
+ strcat(precmd, PreArg[i]);
+ }
+ if (strlen(precmd) > sizeof(precmd))
+ { fprintf(stdout, "spin: too many -D args, aborting\n");
+ alldone(1);
+ }
+ sprintf(cmd, "%s %s > %s", precmd, a, b);
+ if (system((const char *)cmd))
+ { (void) unlink((const char *) b);
+ if (a_tmp) (void) unlink((const char *) a);
+ fprintf(stdout, "spin: preprocessing failed\n"); /* 4.1.2 was stderr */
+ alldone(1); /* no return, error exit */
+ }
+#ifdef PC
+out:
+#endif
+ if (a_tmp) (void) unlink((const char *) a);
+}
+
+FILE *
+cpyfile(char *src, char *tgt)
+{ FILE *inp, *out;
+ char buf[1024];
+
+ inp = fopen(src, "r");
+ out = fopen(tgt, "w");
+ if (!inp || !out)
+ { printf("spin: cannot cp %s to %s\n", src, tgt);
+ alldone(1);
+ }
+ while (fgets(buf, 1024, inp))
+ fprintf(out, "%s", buf);
+ fclose(inp);
+ return out;
+}
+
+void
+usage(void)
+{
+ printf("use: spin [-option] ... [-option] file\n");
+ printf("\tNote: file must always be the last argument\n");
+ printf("\t-A apply slicing algorithm\n");
+ printf("\t-a generate a verifier in pan.c\n");
+ printf("\t-B no final state details in simulations\n");
+ printf("\t-b don't execute printfs in simulation\n");
+ printf("\t-C print channel access info (combine with -g etc.)\n");
+ printf("\t-c columnated -s -r simulation output\n");
+ printf("\t-d produce symbol-table information\n");
+ printf("\t-Dyyy pass -Dyyy to the preprocessor\n");
+ printf("\t-Eyyy pass yyy to the preprocessor\n");
+ printf("\t-f \"..formula..\" translate LTL ");
+ printf("into never claim\n");
+ printf("\t-F file like -f, but with the LTL ");
+ printf("formula stored in a 1-line file\n");
+ printf("\t-g print all global variables\n");
+ printf("\t-h at end of run, print value of seed for random nr generator used\n");
+ printf("\t-i interactive (random simulation)\n");
+ printf("\t-I show result of inlining and preprocessing\n");
+ printf("\t-J reverse eval order of nested unlesses\n");
+ printf("\t-jN skip the first N steps ");
+ printf("in simulation trail\n");
+ printf("\t-l print all local variables\n");
+ printf("\t-M print msc-flow in Postscript\n");
+ printf("\t-m lose msgs sent to full queues\n");
+ printf("\t-N file use never claim stored in file\n");
+ printf("\t-nN seed for random nr generator\n");
+ printf("\t-o1 turn off dataflow-optimizations in verifier\n");
+ printf("\t-o2 don't hide write-only variables in verifier\n");
+ printf("\t-o3 turn off statement merging in verifier\n");
+ printf("\t-Pxxx use xxx for preprocessing\n");
+ printf("\t-p print all statements\n");
+ printf("\t-qN suppress io for queue N in printouts\n");
+ printf("\t-r print receive events\n");
+ printf("\t-S1 and -S2 separate pan source for claim and model\n");
+ printf("\t-s print send events\n");
+ printf("\t-T do not indent printf output\n");
+ printf("\t-t[N] follow [Nth] simulation trail\n");
+ printf("\t-Uyyy pass -Uyyy to the preprocessor\n");
+ printf("\t-uN stop a simulation run after N steps\n");
+ printf("\t-v verbose, more warnings\n");
+ printf("\t-w very verbose (when combined with -l or -g)\n");
+ printf("\t-[XYZ] reserved for use by xspin interface\n");
+ printf("\t-V print version number and exit\n");
+ alldone(1);
+}
+
+void
+optimizations(int nr)
+{
+ switch (nr) {
+ case '1':
+ dataflow = 1 - dataflow; /* dataflow */
+ if (verbose&32)
+ printf("spin: dataflow optimizations turned %s\n",
+ dataflow?"on":"off");
+ break;
+ case '2':
+ /* dead variable elimination */
+ deadvar = 1 - deadvar;
+ if (verbose&32)
+ printf("spin: dead variable elimination turned %s\n",
+ deadvar?"on":"off");
+ break;
+ case '3':
+ /* statement merging */
+ merger = 1 - merger;
+ if (verbose&32)
+ printf("spin: statement merging turned %s\n",
+ merger?"on":"off");
+ break;
+
+ case '4':
+ /* rv optimization */
+ rvopt = 1 - rvopt;
+ if (verbose&32)
+ printf("spin: rendezvous optimization turned %s\n",
+ rvopt?"on":"off");
+ break;
+ case '5':
+ /* case caching */
+ ccache = 1 - ccache;
+ if (verbose&32)
+ printf("spin: case caching turned %s\n",
+ ccache?"on":"off");
+ break;
+ default:
+ printf("spin: bad or missing parameter on -o\n");
+ usage();
+ break;
+ }
+}
+
+#if 0
+static int
+Rename(const char *old, char *new)
+{ FILE *fo, *fn;
+ char buf[1024];
+
+ if ((fo = fopen(old, "r")) == NULL)
+ { printf("spin: cannot open %s\n", old);
+ return 1;
+ }
+ if ((fn = fopen(new, "w")) == NULL)
+ { printf("spin: cannot create %s\n", new);
+ fclose(fo);
+ return 2;
+ }
+ while (fgets(buf, 1024, fo))
+ fputs(buf, fn);
+
+ fclose(fo);
+ fclose(fn);
+
+ return 0; /* success */
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{ Symbol *s;
+ int T = (int) time((time_t *)0);
+ int usedopts = 0;
+ extern void ana_src(int, int);
+
+ yyin = stdin;
+ yyout = stdout;
+ tl_out = stdout;
+
+ /* unused flags: e, w, x, y, z, A, G, I, L, O, Q, R, S, T, W */
+ while (argc > 1 && argv[1][0] == '-')
+ { switch (argv[1][1]) {
+
+ /* generate code for separate compilation: S1 or S2 */
+ case 'S': separate = atoi(&argv[1][2]);
+ /* fall through */
+ case 'a': analyze = 1; break;
+
+ case 'A': export_ast = 1; break;
+ case 'B': no_wrapup = 1; break;
+ case 'b': no_print = 1; break;
+ case 'C': Caccess = 1; break;
+ case 'c': columns = 1; break;
+ case 'D': PreArg[++PreCnt] = (char *) &argv[1][0];
+ break; /* define */
+ case 'd': dumptab = 1; break;
+ case 'E': PreArg[++PreCnt] = (char *) &argv[1][2];
+ break;
+ case 'F': ltl_file = (char **) (argv+2);
+ argc--; argv++; break;
+ case 'f': add_ltl = (char **) argv;
+ argc--; argv++; break;
+ case 'g': verbose += 1; break;
+ case 'h': seedy = 1; break;
+ case 'i': interactive = 1; break;
+ case 'I': inlineonly = 1; break;
+ case 'J': like_java = 1; break;
+ case 'j': jumpsteps = atoi(&argv[1][2]); break;
+ case 'l': verbose += 2; break;
+ case 'M': columns = 2; break;
+ case 'm': m_loss = 1; break;
+ case 'N': nvr_file = (char **) (argv+2);
+ argc--; argv++; break;
+ case 'n': T = atoi(&argv[1][2]); tl_terse = 1; break;
+ case 'o': optimizations(argv[1][2]);
+ usedopts = 1; break;
+ case 'P': PreProc = (char *) &argv[1][2]; break;
+ case 'p': verbose += 4; break;
+ case 'q': if (isdigit(argv[1][2]))
+ qhide(atoi(&argv[1][2]));
+ break;
+ case 'r': verbose += 8; break;
+ case 's': verbose += 16; break;
+ case 'T': notabs = 1; break;
+ case 't': s_trail = 1;
+ if (isdigit(argv[1][2]))
+ ntrail = atoi(&argv[1][2]);
+ break;
+ case 'U': PreArg[++PreCnt] = (char *) &argv[1][0];
+ break; /* undefine */
+ case 'u': cutoff = atoi(&argv[1][2]); break; /* new 3.4.14 */
+ case 'v': verbose += 32; break;
+ case 'V': printf("%s\n", SpinVersion);
+ alldone(0);
+ break;
+ case 'w': verbose += 64; break;
+ case 'X': xspin = notabs = 1;
+#ifndef PC
+ signal(SIGPIPE, alldone); /* not posix... */
+#endif
+ break;
+ case 'Y': limited_vis = 1; break; /* used by xspin */
+ case 'Z': preprocessonly = 1; break; /* used by xspin */
+
+ default : usage(); break;
+ }
+ argc--; argv++;
+ }
+ if (usedopts && !analyze)
+ printf("spin: warning -o[123] option ignored in simulations\n");
+
+ if (ltl_file)
+ { char formula[4096];
+ add_ltl = ltl_file-2; add_ltl[1][1] = 'f';
+ if (!(tl_out = fopen(*ltl_file, "r")))
+ { printf("spin: cannot open %s\n", *ltl_file);
+ alldone(1);
+ }
+ fgets(formula, 4096, tl_out);
+ fclose(tl_out);
+ tl_out = stdout;
+ *ltl_file = (char *) formula;
+ }
+ if (argc > 1)
+ { char cmd[128], out2[64];
+
+ /* must remain in current dir */
+ strcpy(out1, "pan.pre");
+
+ if (add_ltl || nvr_file)
+ strcpy(out2, "pan.___");
+
+ if (add_ltl)
+ { tl_out = cpyfile(argv[1], out2);
+ nr_errs = tl_main(2, add_ltl); /* in tl_main.c */
+ fclose(tl_out);
+ preprocess(out2, out1, 1);
+ } else if (nvr_file)
+ { FILE *fd; char buf[1024];
+
+ if ((fd = fopen(*nvr_file, "r")) == NULL)
+ { printf("spin: cannot open %s\n",
+ *nvr_file);
+ alldone(1);
+ }
+ tl_out = cpyfile(argv[1], out2);
+ while (fgets(buf, 1024, fd))
+ fprintf(tl_out, "%s", buf);
+ fclose(tl_out);
+ fclose(fd);
+ preprocess(out2, out1, 1);
+ } else
+ preprocess(argv[1], out1, 0);
+
+ if (preprocessonly)
+ alldone(0);
+
+ if (!(yyin = fopen(out1, "r")))
+ { printf("spin: cannot open %s\n", out1);
+ alldone(1);
+ }
+
+ if (strncmp(argv[1], "progress", (size_t) 8) == 0
+ || strncmp(argv[1], "accept", (size_t) 6) == 0)
+ sprintf(cmd, "_%s", argv[1]);
+ else
+ strcpy(cmd, argv[1]);
+ oFname = Fname = lookup(cmd);
+ if (oFname->name[0] == '\"')
+ { int i = (int) strlen(oFname->name);
+ oFname->name[i-1] = '\0';
+ oFname = lookup(&oFname->name[1]);
+ }
+ } else
+ { oFname = Fname = lookup("<stdin>");
+ if (add_ltl)
+ { if (argc > 0)
+ exit(tl_main(2, add_ltl));
+ printf("spin: missing argument to -f\n");
+ alldone(1);
+ }
+ printf("%s\n", SpinVersion);
+ printf("reading input from stdin:\n");
+ fflush(stdout);
+ }
+ if (columns == 2)
+ { extern void putprelude(void);
+ if (xspin || verbose&(1|4|8|16|32))
+ { printf("spin: -c precludes all flags except -t\n");
+ alldone(1);
+ }
+ putprelude();
+ }
+ if (columns && !(verbose&8) && !(verbose&16))
+ verbose += (8+16);
+ if (columns == 2 && limited_vis)
+ verbose += (1+4);
+ Srand((unsigned int) T); /* defined in run.c */
+ SeedUsed = T;
+ s = lookup("_"); s->type = PREDEF; /* write-only global var */
+ s = lookup("_p"); s->type = PREDEF;
+ s = lookup("_pid"); s->type = PREDEF;
+ s = lookup("_last"); s->type = PREDEF;
+ s = lookup("_nr_pr"); s->type = PREDEF; /* new 3.3.10 */
+
+ yyparse();
+ fclose(yyin);
+ loose_ends();
+
+ if (inlineonly)
+ { repro_src();
+ return 0;
+ }
+
+ chanaccess();
+ if (!Caccess)
+ { if (!s_trail && (dataflow || merger))
+ ana_src(dataflow, merger);
+ sched();
+ alldone(nr_errs);
+ }
+ return 0;
+}
+
+int
+yywrap(void) /* dummy routine */
+{
+ return 1;
+}
+
+void
+non_fatal(char *s1, char *s2)
+{ extern int yychar; extern char yytext[];
+
+ printf("spin: line %3d %s, Error: ",
+ lineno, Fname?Fname->name:"nofilename");
+ if (s2)
+ printf(s1, s2);
+ else
+ printf(s1);
+ if (yychar != -1 && yychar != 0)
+ { printf(" saw '");
+ explain(yychar);
+ printf("'");
+ }
+ if (strlen(yytext)>1)
+ printf(" near '%s'", yytext);
+ printf("\n");
+ nr_errs++;
+}
+
+void
+fatal(char *s1, char *s2)
+{
+ non_fatal(s1, s2);
+ alldone(1);
+}
+
+char *
+emalloc(size_t n)
+{ char *tmp;
+
+ if (n == 0)
+ return NULL; /* robert shelton 10/20/06 */
+
+ if (!(tmp = (char *) malloc(n)))
+ fatal("not enough memory", (char *)0);
+ memset(tmp, 0, n);
+ return tmp;
+}
+
+void
+trapwonly(Lextok *n /* , char *unused */)
+{ extern int realread;
+ short i = (n->sym)?n->sym->type:0;
+
+ if (i != MTYPE
+ && i != BIT
+ && i != BYTE
+ && i != SHORT
+ && i != INT
+ && i != UNSIGNED)
+ return;
+
+ if (realread)
+ n->sym->hidden |= 128; /* var is read at least once */
+}
+
+void
+setaccess(Symbol *sp, Symbol *what, int cnt, int t)
+{ Access *a;
+
+ for (a = sp->access; a; a = a->lnk)
+ if (a->who == context
+ && a->what == what
+ && a->cnt == cnt
+ && a->typ == t)
+ return;
+
+ a = (Access *) emalloc(sizeof(Access));
+ a->who = context;
+ a->what = what;
+ a->cnt = cnt;
+ a->typ = t;
+ a->lnk = sp->access;
+ sp->access = a;
+}
+
+Lextok *
+nn(Lextok *s, int t, Lextok *ll, Lextok *rl)
+{ Lextok *n = (Lextok *) emalloc(sizeof(Lextok));
+ static int warn_nn = 0;
+
+ n->ntyp = (short) t;
+ if (s && s->fn)
+ { n->ln = s->ln;
+ n->fn = s->fn;
+ } else if (rl && rl->fn)
+ { n->ln = rl->ln;
+ n->fn = rl->fn;
+ } else if (ll && ll->fn)
+ { n->ln = ll->ln;
+ n->fn = ll->fn;
+ } else
+ { n->ln = lineno;
+ n->fn = Fname;
+ }
+ if (s) n->sym = s->sym;
+ n->lft = ll;
+ n->rgt = rl;
+ n->indstep = DstepStart;
+
+ if (t == TIMEOUT) Etimeouts++;
+
+ if (!context) return n;
+
+ if (t == 'r' || t == 's')
+ setaccess(n->sym, ZS, 0, t);
+ if (t == 'R')
+ setaccess(n->sym, ZS, 0, 'P');
+
+ if (context->name == claimproc)
+ { int forbidden = separate;
+ switch (t) {
+ case ASGN:
+ printf("spin: Warning, never claim has side-effect\n");
+ break;
+ case 'r': case 's':
+ non_fatal("never claim contains i/o stmnts",(char *)0);
+ break;
+ case TIMEOUT:
+ /* never claim polls timeout */
+ if (Ntimeouts && Etimeouts)
+ forbidden = 0;
+ Ntimeouts++; Etimeouts--;
+ break;
+ case LEN: case EMPTY: case FULL:
+ case 'R': case NFULL: case NEMPTY:
+ /* status becomes non-exclusive */
+ if (n->sym && !(n->sym->xu&XX))
+ { n->sym->xu |= XX;
+ if (separate == 2) {
+ printf("spin: warning, make sure that the S1 model\n");
+ printf(" also polls channel '%s' in its claim\n",
+ n->sym->name);
+ } }
+ forbidden = 0;
+ break;
+ case 'c':
+ AST_track(n, 0); /* register as a slice criterion */
+ /* fall thru */
+ default:
+ forbidden = 0;
+ break;
+ }
+ if (forbidden)
+ { printf("spin: never, saw "); explain(t); printf("\n");
+ fatal("incompatible with separate compilation",(char *)0);
+ }
+ } else if ((t == ENABLED || t == PC_VAL) && !(warn_nn&t))
+ { printf("spin: Warning, using %s outside never claim\n",
+ (t == ENABLED)?"enabled()":"pc_value()");
+ warn_nn |= t;
+ } else if (t == NONPROGRESS)
+ { fatal("spin: Error, using np_ outside never claim\n", (char *)0);
+ }
+ return n;
+}
+
+Lextok *
+rem_lab(Symbol *a, Lextok *b, Symbol *c) /* proctype name, pid, label name */
+{ Lextok *tmp1, *tmp2, *tmp3;
+
+ has_remote++;
+ c->type = LABEL; /* refered to in global context here */
+ fix_dest(c, a); /* in case target of rem_lab is jump */
+ tmp1 = nn(ZN, '?', b, ZN); tmp1->sym = a;
+ tmp1 = nn(ZN, 'p', tmp1, ZN);
+ tmp1->sym = lookup("_p");
+ tmp2 = nn(ZN, NAME, ZN, ZN); tmp2->sym = a;
+ tmp3 = nn(ZN, 'q', tmp2, ZN); tmp3->sym = c;
+ return nn(ZN, EQ, tmp1, tmp3);
+#if 0
+ .---------------EQ-------.
+ / \
+ 'p' -sym-> _p 'q' -sym-> c (label name)
+ / /
+ '?' -sym-> a (proctype) NAME -sym-> a (proctype name)
+ /
+ b (pid expr)
+#endif
+}
+
+Lextok *
+rem_var(Symbol *a, Lextok *b, Symbol *c, Lextok *ndx)
+{ Lextok *tmp1;
+
+ has_remote++;
+ has_remvar++;
+ dataflow = 0; /* turn off dead variable resets 4.2.5 */
+ tmp1 = nn(ZN, '?', b, ZN); tmp1->sym = a;
+ tmp1 = nn(ZN, 'p', tmp1, ndx);
+ tmp1->sym = c;
+ return tmp1;
+#if 0
+ cannot refer to struct elements
+ only to scalars and arrays
+
+ 'p' -sym-> c (variable name)
+ / \______ possible arrayindex on c
+ /
+ '?' -sym-> a (proctype)
+ /
+ b (pid expr)
+#endif
+}
+
+void
+explain(int n)
+{ FILE *fd = stdout;
+ switch (n) {
+ default: if (n > 0 && n < 256)
+ fprintf(fd, "'%c' = '", n);
+ fprintf(fd, "%d'", n);
+ break;
+ case '\b': fprintf(fd, "\\b"); break;
+ case '\t': fprintf(fd, "\\t"); break;
+ case '\f': fprintf(fd, "\\f"); break;
+ case '\n': fprintf(fd, "\\n"); break;
+ case '\r': fprintf(fd, "\\r"); break;
+ case 'c': fprintf(fd, "condition"); break;
+ case 's': fprintf(fd, "send"); break;
+ case 'r': fprintf(fd, "recv"); break;
+ case 'R': fprintf(fd, "recv poll %s", Operator); break;
+ case '@': fprintf(fd, "@"); break;
+ case '?': fprintf(fd, "(x->y:z)"); break;
+ case ACTIVE: fprintf(fd, "%sactive", Keyword); break;
+ case AND: fprintf(fd, "%s&&", Operator); break;
+ case ASGN: fprintf(fd, "%s=", Operator); break;
+ case ASSERT: fprintf(fd, "%sassert", Function); break;
+ case ATOMIC: fprintf(fd, "%satomic", Keyword); break;
+ case BREAK: fprintf(fd, "%sbreak", Keyword); break;
+ case C_CODE: fprintf(fd, "%sc_code", Keyword); break;
+ case C_DECL: fprintf(fd, "%sc_decl", Keyword); break;
+ case C_EXPR: fprintf(fd, "%sc_expr", Keyword); break;
+ case C_STATE: fprintf(fd, "%sc_state",Keyword); break;
+ case C_TRACK: fprintf(fd, "%sc_track",Keyword); break;
+ case CLAIM: fprintf(fd, "%snever", Keyword); break;
+ case CONST: fprintf(fd, "a constant"); break;
+ case DECR: fprintf(fd, "%s--", Operator); break;
+ case D_STEP: fprintf(fd, "%sd_step", Keyword); break;
+ case D_PROCTYPE: fprintf(fd, "%sd_proctype", Keyword); break;
+ case DO: fprintf(fd, "%sdo", Keyword); break;
+ case DOT: fprintf(fd, "."); break;
+ case ELSE: fprintf(fd, "%selse", Keyword); break;
+ case EMPTY: fprintf(fd, "%sempty", Function); break;
+ case ENABLED: fprintf(fd, "%senabled",Function); break;
+ case EQ: fprintf(fd, "%s==", Operator); break;
+ case EVAL: fprintf(fd, "%seval", Function); break;
+ case FI: fprintf(fd, "%sfi", Keyword); break;
+ case FULL: fprintf(fd, "%sfull", Function); break;
+ case GE: fprintf(fd, "%s>=", Operator); break;
+ case GOTO: fprintf(fd, "%sgoto", Keyword); break;
+ case GT: fprintf(fd, "%s>", Operator); break;
+ case HIDDEN: fprintf(fd, "%shidden", Keyword); break;
+ case IF: fprintf(fd, "%sif", Keyword); break;
+ case INCR: fprintf(fd, "%s++", Operator); break;
+ case INAME: fprintf(fd, "inline name"); break;
+ case INLINE: fprintf(fd, "%sinline", Keyword); break;
+ case INIT: fprintf(fd, "%sinit", Keyword); break;
+ case ISLOCAL: fprintf(fd, "%slocal", Keyword); break;
+ case LABEL: fprintf(fd, "a label-name"); break;
+ case LE: fprintf(fd, "%s<=", Operator); break;
+ case LEN: fprintf(fd, "%slen", Function); break;
+ case LSHIFT: fprintf(fd, "%s<<", Operator); break;
+ case LT: fprintf(fd, "%s<", Operator); break;
+ case MTYPE: fprintf(fd, "%smtype", Keyword); break;
+ case NAME: fprintf(fd, "an identifier"); break;
+ case NE: fprintf(fd, "%s!=", Operator); break;
+ case NEG: fprintf(fd, "%s! (not)",Operator); break;
+ case NEMPTY: fprintf(fd, "%snempty", Function); break;
+ case NFULL: fprintf(fd, "%snfull", Function); break;
+ case NON_ATOMIC: fprintf(fd, "sub-sequence"); break;
+ case NONPROGRESS: fprintf(fd, "%snp_", Function); break;
+ case OD: fprintf(fd, "%sod", Keyword); break;
+ case OF: fprintf(fd, "%sof", Keyword); break;
+ case OR: fprintf(fd, "%s||", Operator); break;
+ case O_SND: fprintf(fd, "%s!!", Operator); break;
+ case PC_VAL: fprintf(fd, "%spc_value",Function); break;
+ case PNAME: fprintf(fd, "process name"); break;
+ case PRINT: fprintf(fd, "%sprintf", Function); break;
+ case PRINTM: fprintf(fd, "%sprintm", Function); break;
+ case PRIORITY: fprintf(fd, "%spriority", Keyword); break;
+ case PROCTYPE: fprintf(fd, "%sproctype",Keyword); break;
+ case PROVIDED: fprintf(fd, "%sprovided",Keyword); break;
+ case RCV: fprintf(fd, "%s?", Operator); break;
+ case R_RCV: fprintf(fd, "%s??", Operator); break;
+ case RSHIFT: fprintf(fd, "%s>>", Operator); break;
+ case RUN: fprintf(fd, "%srun", Operator); break;
+ case SEP: fprintf(fd, "token: ::"); break;
+ case SEMI: fprintf(fd, ";"); break;
+ case SHOW: fprintf(fd, "%sshow", Keyword); break;
+ case SND: fprintf(fd, "%s!", Operator); break;
+ case STRING: fprintf(fd, "a string"); break;
+ case TRACE: fprintf(fd, "%strace", Keyword); break;
+ case TIMEOUT: fprintf(fd, "%stimeout",Keyword); break;
+ case TYPE: fprintf(fd, "data typename"); break;
+ case TYPEDEF: fprintf(fd, "%stypedef",Keyword); break;
+ case XU: fprintf(fd, "%sx[rs]", Keyword); break;
+ case UMIN: fprintf(fd, "%s- (unary minus)", Operator); break;
+ case UNAME: fprintf(fd, "a typename"); break;
+ case UNLESS: fprintf(fd, "%sunless", Keyword); break;
+ }
+}
--- /dev/null
+#!/bin/sh
+# SPIN - Verification Software - Version 5.1 - November 2007
+#
+# Tool documentation: http://spinroot.com/
+# bug-reports: bugs@spinroot.com
+
+# This script is for compiling Spin on a PC with a Unix shell
+# It requires 3 things to be installed on your system:
+# cygwin (for sh, bison yacc, echo, mv, and rm)
+# either gcc or the Visual C++ compiler (cl)
+# On a 2.5GHz system everything compiles in under 1 second.
+# See also makefile for compiling Spin on a standard Unix/Linux system.
+
+# CC="gcc"
+# CFLAGS="-DPC -DNXT -O1 -Wall -ansi -w -o spin.exe"
+
+CC="cl" # Visual Studio for a standalone compilation
+CFLAGS="-DPC -DNXT -DWIN32 -D_CONSOLE -O2 -Zp4 -nologo -wd4996 -Fespin.exe"
+
+yacc -v -d spin.y
+
+# compile for 32 or 64 bits:
+ $CC -DWIN32 $CFLAGS *.c
+# $CC -DWIN64 $CFLAGS *.c bufferoverflowu.lib
+
+rm -f *.o *.obj
+rm -f y?tab.? y.output
+
+# install in the usual place on cygwin:
+echo "mv spin.exe /usr/bin"
+mv spin.exe /usr/bin
--- /dev/null
+# SPIN - Verification Software - Version 5.1 - November 2007
+#
+# Copyright (c) 1989-2006 Lucent Technologies, Bell Labs
+# Extensions (c) 2006-2007 NASA/JPL California Institute of Technology
+# All Rights Reserved. For educational purposes only.
+# No guarantee whatsoever is expressed or implied by
+# the distribution of this code.
+#
+# Written by: Gerard J. Holzmann
+# Documentation: http://spinroot.com/
+# Bug-reports: bugs@spinroot.com
+
+CC=cc -DNXT # -DNXT enables the X operator in LTL
+# CC=cc -m32 -DNXT # for 32bit compilation on a 64bit system
+CFLAGS=-ansi -D_POSIX_SOURCE # on some systems add: -I/usr/include
+
+# for a more picky compilation:
+# CFLAGS=-std=c99 -Wstrict-prototypes -pedantic -fno-strength-reduce -fno-builtin -W -Wshadow -Wpointer-arith -Wcast-qual -Winline -Wall -g
+
+# on PC: add -DPC to CFLAGS above
+# on Solaris: add -DSOLARIS
+# on MAC: add -DMAC
+# on HP-UX: add -Aa
+# and add $(CFLAGS) to the spin.o line: $(CC) $(CFLAGS) -c y.tab.c
+# on __FreeBSD__: omit -D_POSIX_SOURCE
+# also recognized: __FreeBSD__ and __OpenBSD__ and __NetBSD__
+
+YACC=yacc # on Solaris: /usr/ccs/bin/yacc
+YFLAGS=-v -d # creates y.output and y.tab.h
+
+SPIN_OS= spin.o spinlex.o sym.o vars.o main.o ps_msc.o \
+ mesg.o flow.o sched.o run.o pangen1.o pangen2.o \
+ pangen3.o pangen4.o pangen5.o guided.o dstep.o \
+ structs.o pc_zpp.o pangen6.o reprosrc.o
+
+TL_OS= tl_parse.o tl_lex.o tl_main.o tl_trans.o tl_buchi.o \
+ tl_mem.o tl_rewrt.o tl_cache.o
+
+spin: $(SPIN_OS) $(TL_OS)
+ $(CC) $(CFLAGS) -o spin $(SPIN_OS) $(TL_OS)
+
+spin.o: spin.y
+ $(YACC) $(YFLAGS) spin.y
+ $(CC) -c y?tab.c
+ rm -f y?tab.c
+ mv y?tab.o spin.o
+
+$(SPIN_OS): spin.h
+
+$(TL_OS): tl.h
+
+main.o pangen2.o ps_msc.o: version.h
+pangen1.o: pangen1.h pangen3.h pangen6.h
+pangen2.o: pangen2.h pangen4.h pangen5.h
+
+# http://spinroot.com/uno/
+# using uno version 2.13 -- Oct 2007
+
+uno: spin.o
+ uno_local -picky dstep.c flow.c guided.c main.c mesg.c pangen3.c pangen4.c pangen5.c pangen6.c pc_zpp.c ps_msc.c reprosrc.c run.c sched.c spinlex.c structs.c sym.c tl_buchi.c tl_cache.c tl_lex.c tl_main.c tl_mem.c tl_parse.c tl_rewrt.c tl_trans.c vars.c
+ uno_local -picky pangen1.c # large includes, handle separately for now
+ uno_local -picky pangen2.c
+ rm -f spin.o y?tab.? *.uno y.output y.debug
+
+clean:
+ rm -f spin *.o y?tab.[ch] y.output y.debug
+ rm -f pan.[chmotb] a.out core *stackdump
+
+coverity:
+ cov-build --dir covty make
+ cov-translate --dir covty gcc -c *.c
+ cov-analyze --dir covty
+ cov-format-errors --dir covty -x -X
+ echo ./covty/output/errors/index.html
--- /dev/null
+/***** spin: mesg.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+#ifndef MAXQ
+#define MAXQ 2500 /* default max # queues */
+#endif
+
+extern RunList *X;
+extern Symbol *Fname;
+extern Lextok *Mtype;
+extern int verbose, TstOnly, s_trail, analyze, columns;
+extern int lineno, depth, xspin, m_loss, jumpsteps;
+extern int nproc, nstop;
+extern short Have_claim;
+
+Queue *qtab = (Queue *) 0; /* linked list of queues */
+Queue *ltab[MAXQ]; /* linear list of queues */
+int nqs = 0, firstrow = 1;
+char Buf[4096];
+
+static Lextok *n_rem = (Lextok *) 0;
+static Queue *q_rem = (Queue *) 0;
+
+static int a_rcv(Queue *, Lextok *, int);
+static int a_snd(Queue *, Lextok *);
+static int sa_snd(Queue *, Lextok *);
+static int s_snd(Queue *, Lextok *);
+extern void sr_buf(int, int);
+extern void sr_mesg(FILE *, int, int);
+extern void putarrow(int, int);
+static void sr_talk(Lextok *, int, char *, char *, int, Queue *);
+
+int
+cnt_mpars(Lextok *n)
+{ Lextok *m;
+ int i=0;
+
+ for (m = n; m; m = m->rgt)
+ i += Cnt_flds(m);
+ return i;
+}
+
+int
+qmake(Symbol *s)
+{ Lextok *m;
+ Queue *q;
+ int i;
+
+ if (!s->ini)
+ return 0;
+
+ if (nqs >= MAXQ)
+ { lineno = s->ini->ln;
+ Fname = s->ini->fn;
+ fatal("too many queues (%s)", s->name);
+ }
+ if (analyze && nqs >= 255)
+ { fatal("too many channel types", (char *)0);
+ }
+
+ if (s->ini->ntyp != CHAN)
+ return eval(s->ini);
+
+ q = (Queue *) emalloc(sizeof(Queue));
+ q->qid = ++nqs;
+ q->nslots = s->ini->val;
+ q->nflds = cnt_mpars(s->ini->rgt);
+ q->setat = depth;
+
+ i = max(1, q->nslots); /* 0-slot qs get 1 slot minimum */
+
+ q->contents = (int *) emalloc(q->nflds*i*sizeof(int));
+ q->fld_width = (int *) emalloc(q->nflds*sizeof(int));
+ q->stepnr = (int *) emalloc(i*sizeof(int));
+
+ for (m = s->ini->rgt, i = 0; m; m = m->rgt)
+ { if (m->sym && m->ntyp == STRUCT)
+ i = Width_set(q->fld_width, i, getuname(m->sym));
+ else
+ q->fld_width[i++] = m->ntyp;
+ }
+ q->nxt = qtab;
+ qtab = q;
+ ltab[q->qid-1] = q;
+
+ return q->qid;
+}
+
+int
+qfull(Lextok *n)
+{ int whichq = eval(n->lft)-1;
+
+ if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
+ return (ltab[whichq]->qlen >= ltab[whichq]->nslots);
+ return 0;
+}
+
+int
+qlen(Lextok *n)
+{ int whichq = eval(n->lft)-1;
+
+ if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
+ return ltab[whichq]->qlen;
+ return 0;
+}
+
+int
+q_is_sync(Lextok *n)
+{ int whichq = eval(n->lft)-1;
+
+ if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
+ return (ltab[whichq]->nslots == 0);
+ return 0;
+}
+
+int
+qsend(Lextok *n)
+{ int whichq = eval(n->lft)-1;
+
+ if (whichq == -1)
+ { printf("Error: sending to an uninitialized chan\n");
+ whichq = 0;
+ return 0;
+ }
+ if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
+ { ltab[whichq]->setat = depth;
+ if (ltab[whichq]->nslots > 0)
+ return a_snd(ltab[whichq], n);
+ else
+ return s_snd(ltab[whichq], n);
+ }
+ return 0;
+}
+
+int
+qrecv(Lextok *n, int full)
+{ int whichq = eval(n->lft)-1;
+
+ if (whichq == -1)
+ { if (n->sym && !strcmp(n->sym->name, "STDIN"))
+ { Lextok *m;
+
+ if (TstOnly) return 1;
+
+ for (m = n->rgt; m; m = m->rgt)
+ if (m->lft->ntyp != CONST && m->lft->ntyp != EVAL)
+ { int c = getchar();
+ (void) setval(m->lft, c);
+ } else
+ fatal("invalid use of STDIN", (char *)0);
+
+ whichq = 0;
+ return 1;
+ }
+ printf("Error: receiving from an uninitialized chan %s\n",
+ n->sym?n->sym->name:"");
+ whichq = 0;
+ return 0;
+ }
+ if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
+ { ltab[whichq]->setat = depth;
+ return a_rcv(ltab[whichq], n, full);
+ }
+ return 0;
+}
+
+static int
+sa_snd(Queue *q, Lextok *n) /* sorted asynchronous */
+{ Lextok *m;
+ int i, j, k;
+ int New, Old;
+
+ for (i = 0; i < q->qlen; i++)
+ for (j = 0, m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
+ { New = cast_val(q->fld_width[j], eval(m->lft), 0);
+ Old = q->contents[i*q->nflds+j];
+ if (New == Old)
+ continue;
+ if (New > Old)
+ break; /* inner loop */
+ goto found; /* New < Old */
+ }
+found:
+ for (j = q->qlen-1; j >= i; j--)
+ for (k = 0; k < q->nflds; k++)
+ { q->contents[(j+1)*q->nflds+k] =
+ q->contents[j*q->nflds+k]; /* shift up */
+ if (k == 0)
+ q->stepnr[j+1] = q->stepnr[j];
+ }
+ return i*q->nflds; /* new q offset */
+}
+
+void
+typ_ck(int ft, int at, char *s)
+{
+ if ((verbose&32) && ft != at
+ && (ft == CHAN || at == CHAN))
+ { char buf[128], tag1[64], tag2[64];
+ (void) sputtype(tag1, ft);
+ (void) sputtype(tag2, at);
+ sprintf(buf, "type-clash in %s, (%s<-> %s)", s, tag1, tag2);
+ non_fatal("%s", buf);
+ }
+}
+
+static int
+a_snd(Queue *q, Lextok *n)
+{ Lextok *m;
+ int i = q->qlen*q->nflds; /* q offset */
+ int j = 0; /* q field# */
+
+ if (q->nslots > 0 && q->qlen >= q->nslots)
+ return m_loss; /* q is full */
+
+ if (TstOnly) return 1;
+
+ if (n->val) i = sa_snd(q, n); /* sorted insert */
+
+ q->stepnr[i/q->nflds] = depth;
+
+ for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
+ { int New = eval(m->lft);
+ q->contents[i+j] = cast_val(q->fld_width[j], New, 0);
+ if ((verbose&16) && depth >= jumpsteps)
+ sr_talk(n, New, "Send ", "->", j, q);
+ typ_ck(q->fld_width[j], Sym_typ(m->lft), "send");
+ }
+ if ((verbose&16) && depth >= jumpsteps)
+ { for (i = j; i < q->nflds; i++)
+ sr_talk(n, 0, "Send ", "->", i, q);
+ if (j < q->nflds)
+ printf("%3d: warning: missing params in send\n",
+ depth);
+ if (m)
+ printf("%3d: warning: too many params in send\n",
+ depth);
+ }
+ q->qlen++;
+ return 1;
+}
+
+static int
+a_rcv(Queue *q, Lextok *n, int full)
+{ Lextok *m;
+ int i=0, oi, j, k;
+ extern int Rvous;
+
+ if (q->qlen == 0)
+ return 0; /* q is empty */
+try_slot:
+ /* test executability */
+ for (m = n->rgt, j=0; m && j < q->nflds; m = m->rgt, j++)
+ if ((m->lft->ntyp == CONST
+ && q->contents[i*q->nflds+j] != m->lft->val)
+ || (m->lft->ntyp == EVAL
+ && q->contents[i*q->nflds+j] != eval(m->lft->lft)))
+ { if (n->val == 0 /* fifo recv */
+ || n->val == 2 /* fifo poll */
+ || ++i >= q->qlen) /* last slot */
+ return 0; /* no match */
+ goto try_slot;
+ }
+ if (TstOnly) return 1;
+
+ if (verbose&8)
+ { if (j < q->nflds)
+ printf("%3d: warning: missing params in next recv\n",
+ depth);
+ else if (m)
+ printf("%3d: warning: too many params in next recv\n",
+ depth);
+ }
+
+ /* set the fields */
+ if (Rvous)
+ { n_rem = n;
+ q_rem = q;
+ }
+
+ oi = q->stepnr[i];
+ for (m = n->rgt, j = 0; m && j < q->nflds; m = m->rgt, j++)
+ { if (columns && !full) /* was columns == 1 */
+ continue;
+ if ((verbose&8) && !Rvous && depth >= jumpsteps)
+ { sr_talk(n, q->contents[i*q->nflds+j],
+ (full && n->val < 2)?"Recv ":"[Recv] ", "<-", j, q);
+ }
+ if (!full)
+ continue; /* test */
+ if (m && m->lft->ntyp != CONST && m->lft->ntyp != EVAL)
+ { (void) setval(m->lft, q->contents[i*q->nflds+j]);
+ typ_ck(q->fld_width[j], Sym_typ(m->lft), "recv");
+ }
+ if (n->val < 2) /* not a poll */
+ for (k = i; k < q->qlen-1; k++)
+ { q->contents[k*q->nflds+j] =
+ q->contents[(k+1)*q->nflds+j];
+ if (j == 0)
+ q->stepnr[k] = q->stepnr[k+1];
+ }
+ }
+
+ if ((!columns || full)
+ && (verbose&8) && !Rvous && depth >= jumpsteps)
+ for (i = j; i < q->nflds; i++)
+ { sr_talk(n, 0,
+ (full && n->val < 2)?"Recv ":"[Recv] ", "<-", i, q);
+ }
+ if (columns == 2 && full && !Rvous && depth >= jumpsteps)
+ putarrow(oi, depth);
+
+ if (full && n->val < 2)
+ q->qlen--;
+ return 1;
+}
+
+static int
+s_snd(Queue *q, Lextok *n)
+{ Lextok *m;
+ RunList *rX, *sX = X; /* rX=recvr, sX=sendr */
+ int i, j = 0; /* q field# */
+
+ for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
+ { q->contents[j] = cast_val(q->fld_width[j], eval(m->lft), 0);
+ typ_ck(q->fld_width[j], Sym_typ(m->lft), "rv-send");
+ }
+ q->qlen = 1;
+ if (!complete_rendez())
+ { q->qlen = 0;
+ return 0;
+ }
+ if (TstOnly)
+ { q->qlen = 0;
+ return 1;
+ }
+ q->stepnr[0] = depth;
+ if ((verbose&16) && depth >= jumpsteps)
+ { m = n->rgt;
+ rX = X; X = sX;
+ for (j = 0; m && j < q->nflds; m = m->rgt, j++)
+ sr_talk(n, eval(m->lft), "Sent ", "->", j, q);
+ for (i = j; i < q->nflds; i++)
+ sr_talk(n, 0, "Sent ", "->", i, q);
+ if (j < q->nflds)
+ printf("%3d: warning: missing params in rv-send\n",
+ depth);
+ else if (m)
+ printf("%3d: warning: too many params in rv-send\n",
+ depth);
+ X = rX; /* restore receiver's context */
+ if (!s_trail)
+ { if (!n_rem || !q_rem)
+ fatal("cannot happen, s_snd", (char *) 0);
+ m = n_rem->rgt;
+ for (j = 0; m && j < q->nflds; m = m->rgt, j++)
+ { if (m->lft->ntyp != NAME
+ || strcmp(m->lft->sym->name, "_") != 0)
+ i = eval(m->lft);
+ else i = 0;
+
+ if (verbose&8)
+ sr_talk(n_rem,i,"Recv ","<-",j,q_rem);
+ }
+ if (verbose&8)
+ for (i = j; i < q->nflds; i++)
+ sr_talk(n_rem, 0, "Recv ", "<-", j, q_rem);
+ if (columns == 2)
+ putarrow(depth, depth);
+ }
+ n_rem = (Lextok *) 0;
+ q_rem = (Queue *) 0;
+ }
+ return 1;
+}
+
+static void
+channm(Lextok *n)
+{ char lbuf[512];
+
+ if (n->sym->type == CHAN)
+ strcat(Buf, n->sym->name);
+ else if (n->sym->type == NAME)
+ strcat(Buf, lookup(n->sym->name)->name);
+ else if (n->sym->type == STRUCT)
+ { Symbol *r = n->sym;
+ if (r->context)
+ { r = findloc(r);
+ if (!r)
+ { strcat(Buf, "*?*");
+ return;
+ } }
+ ini_struct(r);
+ printf("%s", r->name);
+ strcpy(lbuf, "");
+ struct_name(n->lft, r, 1, lbuf);
+ strcat(Buf, lbuf);
+ } else
+ strcat(Buf, "-");
+ if (n->lft->lft)
+ { sprintf(lbuf, "[%d]", eval(n->lft->lft));
+ strcat(Buf, lbuf);
+ }
+}
+
+static void
+difcolumns(Lextok *n, char *tr, int v, int j, Queue *q)
+{ extern int pno;
+
+ if (j == 0)
+ { Buf[0] = '\0';
+ channm(n);
+ strcat(Buf, (strncmp(tr, "Sen", 3))?"?":"!");
+ } else
+ strcat(Buf, ",");
+ if (tr[0] == '[') strcat(Buf, "[");
+ sr_buf(v, q->fld_width[j] == MTYPE);
+ if (j == q->nflds - 1)
+ { int cnr;
+ if (s_trail) cnr = pno; else cnr = X?X->pid - Have_claim:0;
+ if (tr[0] == '[') strcat(Buf, "]");
+ pstext(cnr, Buf);
+ }
+}
+
+static void
+docolumns(Lextok *n, char *tr, int v, int j, Queue *q)
+{ int i;
+
+ if (firstrow)
+ { printf("q\\p");
+ for (i = 0; i < nproc-nstop - Have_claim; i++)
+ printf(" %3d", i);
+ printf("\n");
+ firstrow = 0;
+ }
+ if (j == 0)
+ { printf("%3d", q->qid);
+ if (X)
+ for (i = 0; i < X->pid - Have_claim; i++)
+ printf(" .");
+ printf(" ");
+ Buf[0] = '\0';
+ channm(n);
+ printf("%s%c", Buf, (strncmp(tr, "Sen", 3))?'?':'!');
+ } else
+ printf(",");
+ if (tr[0] == '[') printf("[");
+ sr_mesg(stdout, v, q->fld_width[j] == MTYPE);
+ if (j == q->nflds - 1)
+ { if (tr[0] == '[') printf("]");
+ printf("\n");
+ }
+}
+
+typedef struct QH {
+ int n;
+ struct QH *nxt;
+} QH;
+static QH *qh;
+
+void
+qhide(int q)
+{ QH *p = (QH *) emalloc(sizeof(QH));
+ p->n = q;
+ p->nxt = qh;
+ qh = p;
+}
+
+int
+qishidden(int q)
+{ QH *p;
+ for (p = qh; p; p = p->nxt)
+ if (p->n == q)
+ return 1;
+ return 0;
+}
+
+static void
+sr_talk(Lextok *n, int v, char *tr, char *a, int j, Queue *q)
+{ char s[128];
+
+ if (qishidden(eval(n->lft)))
+ return;
+
+ if (columns)
+ { if (columns == 2)
+ difcolumns(n, tr, v, j, q);
+ else
+ docolumns(n, tr, v, j, q);
+ return;
+ }
+ if (xspin)
+ { if ((verbose&4) && tr[0] != '[')
+ sprintf(s, "(state -)\t[values: %d",
+ eval(n->lft));
+ else
+ sprintf(s, "(state -)\t[%d", eval(n->lft));
+ if (strncmp(tr, "Sen", 3) == 0)
+ strcat(s, "!");
+ else
+ strcat(s, "?");
+ } else
+ { strcpy(s, tr);
+ }
+
+ if (j == 0)
+ { whoruns(1);
+ printf("line %3d %s %s",
+ n->ln, n->fn->name, s);
+ } else
+ printf(",");
+ sr_mesg(stdout, v, q->fld_width[j] == MTYPE);
+
+ if (j == q->nflds - 1)
+ { if (xspin)
+ { printf("]\n");
+ if (!(verbose&4)) printf("\n");
+ return;
+ }
+ printf("\t%s queue %d (", a, eval(n->lft));
+ Buf[0] = '\0';
+ channm(n);
+ printf("%s)\n", Buf);
+ }
+ fflush(stdout);
+}
+
+void
+sr_buf(int v, int j)
+{ int cnt = 1; Lextok *n;
+ char lbuf[512];
+
+ for (n = Mtype; n && j; n = n->rgt, cnt++)
+ if (cnt == v)
+ { if(strlen(n->lft->sym->name) >= sizeof(lbuf))
+ { non_fatal("mtype name %s too long", n->lft->sym->name);
+ break;
+ }
+ sprintf(lbuf, "%s", n->lft->sym->name);
+ strcat(Buf, lbuf);
+ return;
+ }
+ sprintf(lbuf, "%d", v);
+ strcat(Buf, lbuf);
+}
+
+void
+sr_mesg(FILE *fd, int v, int j)
+{ Buf[0] ='\0';
+ sr_buf(v, j);
+ fprintf(fd, Buf);
+}
+
+void
+doq(Symbol *s, int n, RunList *r)
+{ Queue *q;
+ int j, k;
+
+ if (!s->val) /* uninitialized queue */
+ return;
+ for (q = qtab; q; q = q->nxt)
+ if (q->qid == s->val[n])
+ { if (xspin > 0
+ && (verbose&4)
+ && q->setat < depth)
+ continue;
+ if (q->nslots == 0)
+ continue; /* rv q always empty */
+ printf("\t\tqueue %d (", q->qid);
+ if (r)
+ printf("%s(%d):", r->n->name, r->pid - Have_claim);
+ if (s->nel != 1)
+ printf("%s[%d]): ", s->name, n);
+ else
+ printf("%s): ", s->name);
+ for (k = 0; k < q->qlen; k++)
+ { printf("[");
+ for (j = 0; j < q->nflds; j++)
+ { if (j > 0) printf(",");
+ sr_mesg(stdout, q->contents[k*q->nflds+j],
+ q->fld_width[j] == MTYPE);
+ }
+ printf("]");
+ }
+ printf("\n");
+ break;
+ }
+}
+
+void
+nochan_manip(Lextok *p, Lextok *n, int d)
+{ int e = 1;
+
+ if (d == 0 && p->sym && p->sym->type == CHAN)
+ { setaccess(p->sym, ZS, 0, 'L');
+
+ if (n && n->ntyp == CONST)
+ fatal("invalid asgn to chan", (char *) 0);
+
+ if (n && n->sym && n->sym->type == CHAN)
+ { setaccess(n->sym, ZS, 0, 'V');
+ return;
+ }
+ }
+
+ if (!n || n->ntyp == LEN || n->ntyp == RUN)
+ return;
+
+ if (n->sym && n->sym->type == CHAN)
+ { if (d == 1)
+ fatal("invalid use of chan name", (char *) 0);
+ else
+ setaccess(n->sym, ZS, 0, 'V');
+ }
+
+ if (n->ntyp == NAME
+ || n->ntyp == '.')
+ e = 0; /* array index or struct element */
+
+ nochan_manip(p, n->lft, e);
+ nochan_manip(p, n->rgt, 1);
+}
+
+void
+no_internals(Lextok *n)
+{ char *sp;
+
+ if (!n->sym
+ || !n->sym->name)
+ return;
+
+ sp = n->sym->name;
+
+ if ((strlen(sp) == strlen("_nr_pr") && strcmp(sp, "_nr_pr") == 0)
+ || (strlen(sp) == strlen("_p") && strcmp(sp, "_p") == 0))
+ { fatal("attempt to assign value to system variable %s", sp);
+ }
+}
--- /dev/null
+/***** spin: pangen1.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
+
+#include "spin.h"
+#include "y.tab.h"
+#include "pangen1.h"
+#include "pangen3.h"
+#include "pangen6.h"
+
+extern FILE *tc, *th, *tt;
+extern Label *labtab;
+extern Ordered *all_names;
+extern ProcList *rdy;
+extern Queue *qtab;
+extern Symbol *Fname;
+extern int lineno, verbose, Pid, separate;
+extern int nrRdy, nqs, mst, Mpars, claimnr, eventmapnr;
+extern short has_sorted, has_random, has_provided;
+extern Queue *ltab[];
+
+int Npars=0, u_sync=0, u_async=0, hastrack = 1;
+short has_io = 0;
+short has_state=0; /* code contains c_state */
+
+static Symbol *LstSet=ZS;
+static int acceptors=0, progressors=0, nBits=0;
+static int Types[] = { UNSIGNED, BIT, BYTE, CHAN, MTYPE, SHORT, INT, STRUCT };
+
+static int doglobal(char *, int);
+static void dohidden(void);
+static void do_init(FILE *, Symbol *);
+static void end_labs(Symbol *, int);
+static void put_ptype(char *, int, int, int);
+static void tc_predef_np(void);
+static void put_pinit(ProcList *);
+ void walk_struct(FILE *, int, char *, Symbol *, char *, char *, char *);
+
+static void
+reverse_names(ProcList *p)
+{
+ if (!p) return;
+ reverse_names(p->nxt);
+ fprintf(th, " \"%s\",\n", p->n->name);
+}
+
+void
+genheader(void)
+{ ProcList *p; int i;
+
+ if (separate == 2)
+ { putunames(th);
+ goto here;
+ }
+
+ fprintf(th, "#define SYNC %d\n", u_sync);
+ fprintf(th, "#define ASYNC %d\n\n", u_async);
+ fprintf(th, "#ifndef NCORE\n");
+ fprintf(th, " #ifdef DUAL_CORE\n");
+ fprintf(th, " #define NCORE 2\n");
+ fprintf(th, " #elif QUAD_CORE\n");
+ fprintf(th, " #define NCORE 4\n");
+ fprintf(th, " #else\n");
+ fprintf(th, " #define NCORE 1\n");
+ fprintf(th, " #endif\n");
+ fprintf(th, "#endif\n");
+
+ putunames(th);
+
+ fprintf(tc, "short Air[] = { ");
+ for (p = rdy, i=0; p; p = p->nxt, i++)
+ fprintf(tc, "%s (short) Air%d", (p!=rdy)?",":"", i);
+ fprintf(tc, ", (short) Air%d", i); /* np_ */
+ fprintf(tc, " };\n");
+
+ fprintf(th, "char *procname[] = {\n");
+ reverse_names(rdy);
+ fprintf(th, " \":np_:\",\n");
+ fprintf(th, "};\n\n");
+
+here:
+ for (p = rdy; p; p = p->nxt)
+ put_ptype(p->n->name, p->tn, mst, nrRdy+1);
+ /* +1 for np_ */
+ put_ptype("np_", nrRdy, mst, nrRdy+1);
+
+ ntimes(th, 0, 1, Head0);
+
+ if (separate != 2)
+ { extern void c_add_stack(FILE *);
+ extern void c_stack_size(FILE *);
+
+ ntimes(th, 0, 1, Header);
+ fprintf(th, "#define StackSize (");
+ c_stack_size(th);
+ fprintf(th, ")\n");
+
+ c_add_stack(th);
+ ntimes(th, 0, 1, Header0);
+ }
+ ntimes(th, 0, 1, Head1);
+
+ LstSet = ZS;
+ (void) doglobal("", PUTV);
+
+ hastrack = c_add_sv(th);
+
+ fprintf(th, " uchar sv[VECTORSZ];\n");
+ fprintf(th, "} State");
+#ifdef SOLARIS
+ fprintf(th,"\n#ifdef GCC\n");
+ fprintf(th, "\t__attribute__ ((aligned(8)))");
+ fprintf(th, "\n#endif\n\t");
+#endif
+ fprintf(th, ";\n\n");
+
+ fprintf(th, "#define HAS_TRACK %d\n", hastrack);
+
+ if (separate != 2)
+ dohidden();
+}
+
+void
+genaddproc(void)
+{ ProcList *p;
+ int i = 0;
+
+ if (separate ==2) goto shortcut;
+
+ fprintf(tc, "int\naddproc(int n");
+ for (/* i = 0 */; i < Npars; i++)
+ fprintf(tc, ", int par%d", i);
+
+ ntimes(tc, 0, 1, Addp0);
+ ntimes(tc, 1, nrRdy+1, R5); /* +1 for np_ */
+ ntimes(tc, 0, 1, Addp1);
+
+ if (has_provided)
+ { fprintf(tt, "\nint\nprovided(int II, unsigned char ot, ");
+ fprintf(tt, "int tt, Trans *t)\n");
+ fprintf(tt, "{\n\tswitch(ot) {\n");
+ }
+shortcut:
+ tc_predef_np();
+ for (p = rdy; p; p = p->nxt)
+ { Pid = p->tn;
+ put_pinit(p);
+ }
+ if (separate == 2) return;
+
+ Pid = 0;
+ if (has_provided)
+ { fprintf(tt, "\tdefault: return 1; /* e.g., a claim */\n");
+ fprintf(tt, "\t}\n\treturn 0;\n}\n");
+ }
+
+ ntimes(tc, i, i+1, R6);
+ if (separate == 0)
+ ntimes(tc, 1, nrRdy+1, R5); /* +1 for np_ */
+ else
+ ntimes(tc, 1, nrRdy, R5);
+ ntimes(tc, 0, 1, R8a);
+}
+
+void
+do_locinits(FILE *fd)
+{ ProcList *p;
+
+ for (p = rdy; p; p = p->nxt)
+ c_add_locinit(fd, p->tn, p->n->name);
+}
+
+void
+genother(void)
+{ ProcList *p;
+
+ switch (separate) {
+ case 2:
+ if (claimnr >= 0)
+ ntimes(tc, claimnr, claimnr+1, R0); /* claim only */
+ break;
+ case 1:
+ ntimes(tc, 0, 1, Code0);
+ ntimes(tc, 0, claimnr, R0); /* all except claim */
+ ntimes(tc, claimnr+1, nrRdy, R0);
+ break;
+ case 0:
+ ntimes(tc, 0, 1, Code0);
+ ntimes(tc, 0, nrRdy+1, R0); /* +1 for np_ */
+ break;
+ }
+
+ for (p = rdy; p; p = p->nxt)
+ end_labs(p->n, p->tn);
+
+ switch (separate) {
+ case 2:
+ if (claimnr >= 0)
+ ntimes(tc, claimnr, claimnr+1, R0a); /* claim only */
+ return;
+ case 1:
+ ntimes(tc, 0, claimnr, R0a); /* all except claim */
+ ntimes(tc, claimnr+1, nrRdy, R0a);
+ fprintf(tc, " if (state_tables)\n");
+ fprintf(tc, " ini_claim(%d, 0);\n", claimnr);
+ break;
+ case 0:
+ ntimes(tc, 0, nrRdy, R0a); /* all */
+ break;
+ }
+
+ ntimes(tc, 0, 1, R0b);
+ if (separate == 1 && acceptors == 0)
+ acceptors = 1; /* assume at least 1 acceptstate */
+ ntimes(th, acceptors, acceptors+1, Code1);
+ ntimes(th, progressors, progressors+1, Code3);
+ ntimes(th, nrRdy+1, nrRdy+2, R2); /* +1 for np_ */
+
+ fprintf(tc, " iniglobals();\n");
+ ntimes(tc, 0, 1, Code2a);
+ ntimes(tc, 0, 1, Code2b); /* bfs option */
+ ntimes(tc, 0, 1, Code2c);
+ ntimes(tc, 0, 1, Code2d);
+ ntimes(tc, 0, nrRdy, R4);
+ fprintf(tc, "}\n\n");
+
+ fprintf(tc, "void\n");
+ fprintf(tc, "iniglobals(void)\n{\n");
+ if (doglobal("", INIV) > 0)
+ { fprintf(tc, "#ifdef VAR_RANGES\n");
+ (void) doglobal("logval(\"", LOGV);
+ fprintf(tc, "#endif\n");
+ }
+ ntimes(tc, 1, nqs+1, R3);
+ fprintf(tc, "\tMaxbody = max(Maxbody, sizeof(State)-VECTORSZ);");
+ fprintf(tc, "\n}\n\n");
+}
+
+void
+gensvmap(void)
+{
+ ntimes(tc, 0, 1, SvMap);
+}
+
+static struct {
+ char *s, *t; int n, m, p;
+} ln[] = {
+ {"end", "stopstate", 3, 0, 0},
+ {"progress", "progstate", 8, 0, 1},
+ {"accept", "accpstate", 6, 1, 0},
+ {0, 0, 0, 0, 0},
+};
+
+static void
+end_labs(Symbol *s, int i)
+{ int oln = lineno;
+ Symbol *ofn = Fname;
+ Label *l;
+ int j; char foo[128];
+
+ if ((i == claimnr && separate == 1)
+ || (i != claimnr && separate == 2))
+ return;
+
+ for (l = labtab; l; l = l->nxt)
+ for (j = 0; ln[j].n; j++)
+ if (strncmp(l->s->name, ln[j].s, ln[j].n) == 0
+ && strcmp(l->c->name, s->name) == 0)
+ { fprintf(tc, "\t%s[%d][%d] = 1;\n",
+ ln[j].t, i, l->e->seqno);
+ acceptors += ln[j].m;
+ progressors += ln[j].p;
+ if (l->e->status & D_ATOM)
+ { sprintf(foo, "%s label inside d_step",
+ ln[j].s);
+ goto complain;
+ }
+ if (j > 0 && (l->e->status & ATOM))
+ { sprintf(foo, "%s label inside atomic",
+ ln[j].s);
+ complain: lineno = l->e->n->ln;
+ Fname = l->e->n->fn;
+ printf("spin: %3d:%s, warning, %s - is invisible\n",
+ lineno, Fname?Fname->name:"-", foo);
+ }
+ }
+ /* visible states -- through remote refs: */
+ for (l = labtab; l; l = l->nxt)
+ if (l->visible
+ && strcmp(l->s->context->name, s->name) == 0)
+ fprintf(tc, "\tvisstate[%d][%d] = 1;\n",
+ i, l->e->seqno);
+
+ lineno = oln;
+ Fname = ofn;
+}
+
+void
+ntimes(FILE *fd, int n, int m, char *c[])
+{
+ int i, j;
+ for (j = 0; c[j]; j++)
+ for (i = n; i < m; i++)
+ { fprintf(fd, c[j], i, i, i, i, i, i);
+ fprintf(fd, "\n");
+ }
+}
+
+void
+prehint(Symbol *s)
+{ Lextok *n;
+
+ printf("spin: warning, ");
+ if (!s) return;
+
+ n = (s->context != ZS)?s->context->ini:s->ini;
+ if (n)
+ printf("line %3d %s, ", n->ln, n->fn->name);
+}
+
+void
+checktype(Symbol *sp, char *s)
+{ char buf[128]; int i;
+
+ if (!s
+ || (sp->type != BYTE
+ && sp->type != SHORT
+ && sp->type != INT))
+ return;
+
+ if (sp->hidden&16) /* formal parameter */
+ { ProcList *p; Lextok *f, *t;
+ int posnr = 0;
+ for (p = rdy; p; p = p->nxt)
+ if (p->n->name
+ && strcmp(s, p->n->name) == 0)
+ break;
+ if (p)
+ for (f = p->p; f; f = f->rgt) /* list of types */
+ for (t = f->lft; t; t = t->rgt, posnr++)
+ if (t->sym
+ && strcmp(t->sym->name, sp->name) == 0)
+ { checkrun(sp, posnr);
+ return;
+ }
+
+ } else if (!(sp->hidden&4))
+ { if (!(verbose&32)) return;
+ sputtype(buf, sp->type);
+ i = (int) strlen(buf);
+ while (i > 0 && buf[--i] == ' ') buf[i] = '\0';
+ prehint(sp);
+ if (sp->context)
+ printf("proctype %s:", s);
+ else
+ printf("global");
+ printf(" '%s %s' could be declared 'bit %s'\n",
+ buf, sp->name, sp->name);
+ } else if (sp->type != BYTE && !(sp->hidden&8))
+ { if (!(verbose&32)) return;
+ sputtype(buf, sp->type);
+ i = (int) strlen(buf);
+ while (buf[--i] == ' ') buf[i] = '\0';
+ prehint(sp);
+ if (sp->context)
+ printf("proctype %s:", s);
+ else
+ printf("global");
+ printf(" '%s %s' could be declared 'byte %s'\n",
+ buf, sp->name, sp->name);
+ }
+}
+
+int
+dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s)
+{ int h, j, k=0; extern int nr_errs;
+ Ordered *walk;
+ Symbol *sp;
+ char buf[64], buf2[128], buf3[128];
+
+ if (dowhat == INIV)
+ { /* initialize in order of declaration */
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp->context
+ && !sp->owner
+ && strcmp(s, sp->context->name) == 0)
+ { checktype(sp, s); /* fall through */
+ if (!(sp->hidden&16))
+ { sprintf(buf, "((P%d *)pptr(h))->", p);
+ do_var(ofd, dowhat, buf, sp, "", " = ", ";\n");
+ }
+ k++;
+ } }
+ } else
+ { for (j = 0; j < 8; j++)
+ for (h = 0; h <= 1; h++)
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp->context
+ && !sp->owner
+ && sp->type == Types[j]
+ && ((h == 0 && sp->nel == 1) || (h == 1 && sp->nel > 1))
+ && strcmp(s, sp->context->name) == 0)
+ { switch (dowhat) {
+ case LOGV:
+ if (sp->type == CHAN
+ && verbose == 0)
+ break;
+ sprintf(buf, "%s%s:", pre, s);
+ { sprintf(buf2, "\", ((P%d *)pptr(h))->", p);
+ sprintf(buf3, ");\n");
+ }
+ do_var(ofd, dowhat, "", sp, buf, buf2, buf3);
+ break;
+ case PUTV:
+ sprintf(buf, "((P%d *)pptr(h))->", p);
+ do_var(ofd, dowhat, buf, sp, "", " = ", ";\n");
+ k++;
+ break;
+ }
+ if (strcmp(s, ":never:") == 0)
+ { printf("error: %s defines local %s\n",
+ s, sp->name);
+ nr_errs++;
+ } } } }
+
+ return k;
+}
+
+void
+c_chandump(FILE *fd)
+{ Queue *q;
+ char buf[256];
+ int i;
+
+ if (!qtab)
+ { fprintf(fd, "void\nc_chandump(int unused) ");
+ fprintf(fd, "{ unused++; /* avoid complaints */ }\n");
+ return;
+ }
+
+ fprintf(fd, "void\nc_chandump(int from)\n");
+ fprintf(fd, "{ uchar *z; int slot;\n");
+
+ fprintf(fd, " from--;\n");
+ fprintf(fd, " if (from >= (int) now._nr_qs || from < 0)\n");
+ fprintf(fd, " { printf(\"pan: bad qid %%d\\n\", from+1);\n");
+ fprintf(fd, " return;\n");
+ fprintf(fd, " }\n");
+ fprintf(fd, " z = qptr(from);\n");
+ fprintf(fd, " switch (((Q0 *)z)->_t) {\n");
+
+ for (q = qtab; q; q = q->nxt)
+ { fprintf(fd, " case %d:\n\t\t", q->qid);
+ sprintf(buf, "((Q%d *)z)->", q->qid);
+
+ fprintf(fd, "for (slot = 0; slot < %sQlen; slot++)\n\t\t", buf);
+ fprintf(fd, "{ printf(\" [\");\n\t\t");
+ for (i = 0; i < q->nflds; i++)
+ { if (q->fld_width[i] == MTYPE)
+ { fprintf(fd, "\tprintm(%scontents[slot].fld%d);\n\t\t",
+ buf, i);
+ } else
+ fprintf(fd, "\tprintf(\"%%d,\", %scontents[slot].fld%d);\n\t\t",
+ buf, i);
+ }
+ fprintf(fd, " printf(\"],\");\n\t\t");
+ fprintf(fd, "}\n\t\t");
+ fprintf(fd, "break;\n");
+ }
+ fprintf(fd, " }\n");
+ fprintf(fd, " printf(\"\\n\");\n}\n");
+}
+
+void
+c_var(FILE *fd, char *pref, Symbol *sp)
+{ char buf[256];
+ int i;
+
+ switch (sp->type) {
+ case STRUCT:
+ /* c_struct(fd, pref, sp); */
+ fprintf(fd, "\t\tprintf(\"\t(struct %s)\\n\");\n",
+ sp->name);
+ sprintf(buf, "%s%s.", pref, sp->name);
+ c_struct(fd, buf, sp);
+ break;
+ case BIT: case BYTE:
+ case SHORT: case INT:
+ case UNSIGNED:
+ sputtype(buf, sp->type);
+ if (sp->nel == 1)
+ { fprintf(fd, "\tprintf(\"\t%s %s:\t%%d\\n\", %s%s);\n",
+ buf, sp->name, pref, sp->name);
+ } else
+ { fprintf(fd, "\t{\tint l_in;\n");
+ fprintf(fd, "\t\tfor (l_in = 0; l_in < %d; l_in++)\n", sp->nel);
+ fprintf(fd, "\t\t{\n");
+ fprintf(fd, "\t\t\tprintf(\"\t%s %s[%%d]:\t%%d\\n\", l_in, %s%s[l_in]);\n",
+ buf, sp->name, pref, sp->name);
+ fprintf(fd, "\t\t}\n");
+ fprintf(fd, "\t}\n");
+ }
+ break;
+ case CHAN:
+ if (sp->nel == 1)
+ { fprintf(fd, "\tprintf(\"\tchan %s (=%%d):\tlen %%d:\\t\", ",
+ sp->name);
+ fprintf(fd, "%s%s, q_len(%s%s));\n",
+ pref, sp->name, pref, sp->name);
+ fprintf(fd, "\tc_chandump(%s%s);\n", pref, sp->name);
+ } else
+ for (i = 0; i < sp->nel; i++)
+ { fprintf(fd, "\tprintf(\"\tchan %s[%d] (=%%d):\tlen %%d:\\t\", ",
+ sp->name, i);
+ fprintf(fd, "%s%s[%d], q_len(%s%s[%d]));\n",
+ pref, sp->name, i, pref, sp->name, i);
+ fprintf(fd, "\tc_chandump(%s%s[%d]);\n",
+ pref, sp->name, i);
+ }
+ break;
+ }
+}
+
+int
+c_splurge_any(ProcList *p)
+{ Ordered *walk;
+ Symbol *sp;
+
+ if (strcmp(p->n->name, ":never:") != 0
+ && strcmp(p->n->name, ":trace:") != 0
+ && strcmp(p->n->name, ":notrace:") != 0)
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (!sp->context
+ || sp->type == 0
+ || strcmp(sp->context->name, p->n->name) != 0
+ || sp->owner || (sp->hidden&1)
+ || (sp->type == MTYPE && ismtype(sp->name)))
+ continue;
+
+ return 1;
+ }
+ return 0;
+}
+
+void
+c_splurge(FILE *fd, ProcList *p)
+{ Ordered *walk;
+ Symbol *sp;
+ char pref[64];
+
+ if (strcmp(p->n->name, ":never:") != 0
+ && strcmp(p->n->name, ":trace:") != 0
+ && strcmp(p->n->name, ":notrace:") != 0)
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (!sp->context
+ || sp->type == 0
+ || strcmp(sp->context->name, p->n->name) != 0
+ || sp->owner || (sp->hidden&1)
+ || (sp->type == MTYPE && ismtype(sp->name)))
+ continue;
+
+ sprintf(pref, "((P%d *)pptr(pid))->", p->tn);
+ c_var(fd, pref, sp);
+ }
+}
+
+void
+c_wrapper(FILE *fd) /* allow pan.c to print out global sv entries */
+{ Ordered *walk;
+ ProcList *p;
+ Symbol *sp;
+ Lextok *n;
+ extern Lextok *Mtype;
+ int j;
+
+ fprintf(fd, "void\nc_globals(void)\n{\t/* int i; */\n");
+ fprintf(fd, " printf(\"global vars:\\n\");\n");
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp->context || sp->owner || (sp->hidden&1)
+ || (sp->type == MTYPE && ismtype(sp->name)))
+ continue;
+
+ c_var(fd, "now.", sp);
+ }
+ fprintf(fd, "}\n");
+
+ fprintf(fd, "void\nc_locals(int pid, int tp)\n{\t/* int i; */\n");
+ fprintf(fd, " switch(tp) {\n");
+ for (p = rdy; p; p = p->nxt)
+ { fprintf(fd, " case %d:\n", p->tn);
+ if (c_splurge_any(p))
+ { fprintf(fd, " \tprintf(\"local vars proc %%d (%s):\\n\", pid);\n",
+ p->n->name);
+ c_splurge(fd, p);
+ } else
+ { fprintf(fd, " \t/* none */\n");
+ }
+ fprintf(fd, " \tbreak;\n");
+ }
+ fprintf(fd, " }\n}\n");
+
+ fprintf(fd, "void\nprintm(int x)\n{\n");
+ fprintf(fd, " switch (x) {\n");
+ for (n = Mtype, j = 1; n && j; n = n->rgt, j++)
+ fprintf(fd, "\tcase %d: Printf(\"%s\"); break;\n",
+ j, n->lft->sym->name);
+ fprintf(fd, " default: Printf(\"%%d\", x);\n");
+ fprintf(fd, " }\n");
+ fprintf(fd, "}\n");
+}
+
+static int
+doglobal(char *pre, int dowhat)
+{ Ordered *walk;
+ Symbol *sp;
+ int j, cnt = 0;
+
+ for (j = 0; j < 8; j++)
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (!sp->context
+ && !sp->owner
+ && sp->type == Types[j])
+ { if (Types[j] != MTYPE || !ismtype(sp->name))
+ switch (dowhat) {
+ case LOGV:
+ if (sp->type == CHAN
+ && verbose == 0)
+ break;
+ if (sp->hidden&1)
+ break;
+ do_var(tc, dowhat, "", sp,
+ pre, "\", now.", ");\n");
+ break;
+ case INIV:
+ checktype(sp, (char *) 0);
+ cnt++; /* fall through */
+ case PUTV:
+ do_var(tc, dowhat, (sp->hidden&1)?"":"now.", sp,
+ "", " = ", ";\n");
+ break;
+ } } }
+ return cnt;
+}
+
+static void
+dohidden(void)
+{ Ordered *walk;
+ Symbol *sp;
+ int j;
+
+ for (j = 0; j < 8; j++)
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if ((sp->hidden&1)
+ && sp->type == Types[j])
+ { if (sp->context || sp->owner)
+ fatal("cannot hide non-globals (%s)", sp->name);
+ if (sp->type == CHAN)
+ fatal("cannot hide channels (%s)", sp->name);
+ fprintf(th, "/* hidden variable: */");
+ typ2c(sp);
+ } }
+ fprintf(th, "int _; /* a predefined write-only variable */\n\n");
+}
+
+void
+do_var(FILE *ofd, int dowhat, char *s, Symbol *sp,
+ char *pre, char *sep, char *ter)
+{ int i;
+
+ switch(dowhat) {
+ case PUTV:
+
+ if (sp->hidden&1) break;
+
+ typ2c(sp);
+ break;
+ case LOGV:
+ case INIV:
+ if (sp->type == STRUCT)
+ { /* struct may contain a chan */
+ walk_struct(ofd, dowhat, s, sp, pre, sep, ter);
+ break;
+ }
+ if (!sp->ini && dowhat != LOGV) /* it defaults to 0 */
+ break;
+ if (sp->nel == 1)
+ { fprintf(ofd, "\t\t%s%s%s%s",
+ pre, s, sp->name, sep);
+ if (dowhat == LOGV)
+ fprintf(ofd, "%s%s", s, sp->name);
+ else
+ do_init(ofd, sp);
+ fprintf(ofd, "%s", ter);
+ } else
+ { if (sp->ini && sp->ini->ntyp == CHAN)
+ { for (i = 0; i < sp->nel; i++)
+ { fprintf(ofd, "\t\t%s%s%s[%d]%s",
+ pre, s, sp->name, i, sep);
+ if (dowhat == LOGV)
+ fprintf(ofd, "%s%s[%d]",
+ s, sp->name, i);
+ else
+ do_init(ofd, sp);
+ fprintf(ofd, "%s", ter);
+ }
+ } else
+ { fprintf(ofd, "\t{\tint l_in;\n");
+ fprintf(ofd, "\t\tfor (l_in = 0; l_in < %d; l_in++)\n", sp->nel);
+ fprintf(ofd, "\t\t{\n");
+ fprintf(ofd, "\t\t\t%s%s%s[l_in]%s",
+ pre, s, sp->name, sep);
+ if (dowhat == LOGV)
+ fprintf(ofd, "%s%s[l_in]", s, sp->name);
+ else
+ putstmnt(ofd, sp->ini, 0);
+ fprintf(ofd, "%s", ter);
+ fprintf(ofd, "\t\t}\n");
+ fprintf(ofd, "\t}\n");
+ } }
+ break;
+ }
+}
+
+static void
+do_init(FILE *ofd, Symbol *sp)
+{ int i;
+
+ if (sp->ini
+ && sp->type == CHAN
+ && ((i = qmake(sp)) > 0))
+ { if (sp->ini->ntyp == CHAN)
+ fprintf(ofd, "addqueue(%d, %d)",
+ i, ltab[i-1]->nslots == 0);
+ else
+ fprintf(ofd, "%d", i);
+ } else
+ putstmnt(ofd, sp->ini, 0);
+}
+
+static int
+blog(int n) /* for small log2 without rounding problems */
+{ int m=1, r=2;
+
+ while (r < n) { m++; r *= 2; }
+ return 1+m;
+}
+
+static void
+put_ptype(char *s, int i, int m0, int m1)
+{ int k;
+
+ if (strcmp(s, ":init:") == 0)
+ fprintf(th, "#define Pinit ((P%d *)this)\n", i);
+
+ if (strcmp(s, ":never:") != 0
+ && strcmp(s, ":trace:") != 0
+ && strcmp(s, ":notrace:") != 0
+ && strcmp(s, ":init:") != 0
+ && strcmp(s, "_:never_template:_") != 0
+ && strcmp(s, "np_") != 0)
+ fprintf(th, "#define P%s ((P%d *)this)\n", s, i);
+
+ fprintf(th, "typedef struct P%d { /* %s */\n", i, s);
+ fprintf(th, " unsigned _pid : 8; /* 0..255 */\n");
+ fprintf(th, " unsigned _t : %d; /* proctype */\n", blog(m1));
+ fprintf(th, " unsigned _p : %d; /* state */\n", blog(m0));
+ LstSet = ZS;
+ nBits = 8 + blog(m1) + blog(m0);
+ k = dolocal(tc, "", PUTV, i, s); /* includes pars */
+
+ c_add_loc(th, s);
+
+ fprintf(th, "} P%d;\n", i);
+ if ((!LstSet && k > 0) || has_state)
+ fprintf(th, "#define Air%d 0\n", i);
+ else if (LstSet || k == 0) /* 5.0, added condition */
+ { fprintf(th, "#define Air%d (sizeof(P%d) - ", i, i);
+ if (k == 0)
+ { fprintf(th, "%d", (nBits+7)/8);
+ goto done;
+ }
+ if ((LstSet->type != BIT && LstSet->type != UNSIGNED)
+ || LstSet->nel != 1)
+ { fprintf(th, "Offsetof(P%d, %s) - %d*sizeof(",
+ i, LstSet->name, LstSet->nel);
+ }
+ switch(LstSet->type) {
+ case UNSIGNED:
+ fprintf(th, "%d", (nBits+7)/8);
+ break;
+ case BIT:
+ if (LstSet->nel == 1)
+ { fprintf(th, "%d", (nBits+7)/8);
+ break;
+ } /* else fall through */
+ case MTYPE: case BYTE: case CHAN:
+ fprintf(th, "uchar)"); break;
+ case SHORT:
+ fprintf(th, "short)"); break;
+ case INT:
+ fprintf(th, "int)"); break;
+ default:
+ fatal("cannot happen Air %s",
+ LstSet->name);
+ }
+done: fprintf(th, ")\n");
+ }
+}
+
+static void
+tc_predef_np(void)
+{ int i = nrRdy; /* 1+ highest proctype nr */
+
+ fprintf(th, "#define _NP_ %d\n", i);
+/* if (separate == 2) fprintf(th, "extern "); */
+ fprintf(th, "uchar reached%d[3]; /* np_ */\n", i);
+ fprintf(th, "uchar *loopstate%d; /* np_ */\n", i);
+
+ fprintf(th, "#define nstates%d 3 /* np_ */\n", i);
+ fprintf(th, "#define endstate%d 2 /* np_ */\n\n", i);
+ fprintf(th, "#define start%d 0 /* np_ */\n", i);
+
+ fprintf(tc, "\tcase %d: /* np_ */\n", i);
+ if (separate == 1)
+ { fprintf(tc, "\t\tini_claim(%d, h);\n", i);
+ } else
+ { fprintf(tc, "\t\t((P%d *)pptr(h))->_t = %d;\n", i, i);
+ fprintf(tc, "\t\t((P%d *)pptr(h))->_p = 0;\n", i);
+ fprintf(tc, "\t\treached%d[0] = 1;\n", i);
+ fprintf(tc, "\t\taccpstate[%d][1] = 1;\n", i);
+ }
+ fprintf(tc, "\t\tbreak;\n");
+}
+
+static void
+put_pinit(ProcList *P)
+{ Lextok *fp, *fpt, *t;
+ Element *e = P->s->frst;
+ Symbol *s = P->n;
+ Lextok *p = P->p;
+ int i = P->tn;
+ int ini, j, k;
+
+ if (i == claimnr
+ && separate == 1)
+ { fprintf(tc, "\tcase %d: /* %s */\n", i, s->name);
+ fprintf(tc, "\t\tini_claim(%d, h);\n", i);
+ fprintf(tc, "\t\tbreak;\n");
+ return;
+ }
+ if (i != claimnr
+ && separate == 2)
+ return;
+
+ ini = huntele(e, e->status, -1)->seqno;
+ fprintf(th, "#define start%d %d\n", i, ini);
+ if (i == claimnr)
+ fprintf(th, "#define start_claim %d\n", ini);
+ if (i == eventmapnr)
+ fprintf(th, "#define start_event %d\n", ini);
+
+ fprintf(tc, "\tcase %d: /* %s */\n", i, s->name);
+
+ fprintf(tc, "\t\t((P%d *)pptr(h))->_t = %d;\n", i, i);
+ fprintf(tc, "\t\t((P%d *)pptr(h))->_p = %d;", i, ini);
+ fprintf(tc, " reached%d[%d]=1;\n", i, ini);
+
+ if (has_provided)
+ { fprintf(tt, "\tcase %d: /* %s */\n\t\t", i, s->name);
+ if (P->prov)
+ { fprintf(tt, "if (");
+ putstmnt(tt, P->prov, 0);
+ fprintf(tt, ")\n\t\t\t");
+ }
+ fprintf(tt, "return 1;\n");
+ if (P->prov)
+ fprintf(tt, "\t\tbreak;\n");
+ }
+
+ fprintf(tc, "\t\t/* params: */\n");
+ for (fp = p, j=0; fp; fp = fp->rgt)
+ for (fpt = fp->lft; fpt; fpt = fpt->rgt, j++)
+ { t = (fpt->ntyp == ',') ? fpt->lft : fpt;
+ if (t->sym->nel != 1)
+ { lineno = t->ln;
+ Fname = t->fn;
+ fatal("array in parameter list, %s",
+ t->sym->name);
+ }
+ fprintf(tc, "\t\t((P%d *)pptr(h))->", i);
+ if (t->sym->type == STRUCT)
+ { if (full_name(tc, t, t->sym, 1))
+ { lineno = t->ln;
+ Fname = t->fn;
+ fatal("hidden array in parameter %s",
+ t->sym->name);
+ }
+ } else
+ fprintf(tc, "%s", t->sym->name);
+ fprintf(tc, " = par%d;\n", j);
+ }
+ fprintf(tc, "\t\t/* locals: */\n");
+ k = dolocal(tc, "", INIV, i, s->name);
+ if (k > 0)
+ { fprintf(tc, "#ifdef VAR_RANGES\n");
+ (void) dolocal(tc, "logval(\"", LOGV, i, s->name);
+ fprintf(tc, "#endif\n");
+ }
+
+ fprintf(tc, "#ifdef HAS_CODE\n");
+ fprintf(tc, "\t\tlocinit%d(h);\n", i);
+ fprintf(tc, "#endif\n");
+
+ dumpclaims(tc, i, s->name);
+ fprintf(tc, "\t break;\n");
+}
+
+Element *
+huntstart(Element *f)
+{ Element *e = f;
+ Element *elast = (Element *) 0;
+ int cnt = 0;
+
+ while (elast != e && cnt++ < 200) /* new 4.0.8 */
+ { elast = e;
+ if (e->n)
+ { if (e->n->ntyp == '.' && e->nxt)
+ e = e->nxt;
+ else if (e->n->ntyp == UNLESS)
+ e = e->sub->this->frst;
+ } }
+
+ if (cnt >= 200 || !e)
+ fatal("confusing control structure", (char *) 0);
+ return e;
+}
+
+Element *
+huntele(Element *f, int o, int stopat)
+{ Element *g, *e = f;
+ int cnt=0; /* a precaution against loops */
+
+ if (e)
+ for ( ; cnt < 200 && e->n; cnt++)
+ {
+ if (e->seqno == stopat)
+ break;
+
+ switch (e->n->ntyp) {
+ case GOTO:
+ g = get_lab(e->n,1);
+ cross_dsteps(e->n, g->n);
+ break;
+ case '.':
+ case BREAK:
+ if (!e->nxt)
+ return e;
+ g = e->nxt;
+ break;
+ case UNLESS:
+ g = huntele(e->sub->this->frst, o, stopat);
+ break;
+ case D_STEP:
+ case ATOMIC:
+ case NON_ATOMIC:
+ default:
+ return e;
+ }
+ if ((o & ATOM) && !(g->status & ATOM))
+ return e;
+ e = g;
+ }
+ if (cnt >= 200 || !e)
+ fatal("confusing control structure", (char *) 0);
+ return e;
+}
+
+void
+typ2c(Symbol *sp)
+{ int wsbits = sizeof(long)*8; /* wordsize in bits */
+ switch (sp->type) {
+ case UNSIGNED:
+ if (sp->hidden&1)
+ fprintf(th, "\tuchar %s;", sp->name);
+ else
+ fprintf(th, "\tunsigned %s : %d",
+ sp->name, sp->nbits);
+ LstSet = sp;
+ if (nBits%wsbits > 0
+ && wsbits - nBits%wsbits < sp->nbits)
+ { /* must padd to a word-boundary */
+ nBits += wsbits - nBits%wsbits;
+ }
+ nBits += sp->nbits;
+ break;
+ case BIT:
+ if (sp->nel == 1 && !(sp->hidden&1))
+ { fprintf(th, "\tunsigned %s : 1", sp->name);
+ LstSet = sp;
+ nBits++;
+ break;
+ } /* else fall through */
+ if (!(sp->hidden&1) && (verbose&32))
+ printf("spin: warning: bit-array %s[%d] mapped to byte-array\n",
+ sp->name, sp->nel);
+ nBits += 8*sp->nel; /* mapped onto array of uchars */
+ case MTYPE:
+ case BYTE:
+ case CHAN: /* good for up to 255 channels */
+ fprintf(th, "\tuchar %s", sp->name);
+ LstSet = sp;
+ break;
+ case SHORT:
+ fprintf(th, "\tshort %s", sp->name);
+ LstSet = sp;
+ break;
+ case INT:
+ fprintf(th, "\tint %s", sp->name);
+ LstSet = sp;
+ break;
+ case STRUCT:
+ if (!sp->Snm)
+ fatal("undeclared structure element %s", sp->name);
+ fprintf(th, "\tstruct %s %s",
+ sp->Snm->name,
+ sp->name);
+ LstSet = ZS;
+ break;
+ case CODE_FRAG:
+ case PREDEF:
+ return;
+ default:
+ fatal("variable %s undeclared", sp->name);
+ }
+
+ if (sp->nel != 1)
+ fprintf(th, "[%d]", sp->nel);
+ fprintf(th, ";\n");
+}
+
+static void
+ncases(FILE *fd, int p, int n, int m, char *c[])
+{ int i, j;
+
+ for (j = 0; c[j]; j++)
+ for (i = n; i < m; i++)
+ { fprintf(fd, c[j], i, p, i);
+ fprintf(fd, "\n");
+ }
+}
+
+void
+qlen_type(int qmax)
+{
+ fprintf(th, "\t");
+ if (qmax < 256)
+ fprintf(th, "uchar");
+ else if (qmax < 65535)
+ fprintf(th, "ushort");
+ else
+ fprintf(th, "uint");
+ fprintf(th, " Qlen; /* q_size */\n");
+}
+
+void
+genaddqueue(void)
+{ char buf0[256];
+ int j, qmax = 0;
+ Queue *q;
+
+ ntimes(tc, 0, 1, Addq0);
+ if (has_io && !nqs)
+ fprintf(th, "#define NQS 1 /* nqs=%d, but has_io */\n", nqs);
+ else
+ fprintf(th, "#define NQS %d\n", nqs);
+ fprintf(th, "short q_flds[%d];\n", nqs+1);
+ fprintf(th, "short q_max[%d];\n", nqs+1);
+
+ for (q = qtab; q; q = q->nxt)
+ if (q->nslots > qmax)
+ qmax = q->nslots;
+
+ for (q = qtab; q; q = q->nxt)
+ { j = q->qid;
+ fprintf(tc, "\tcase %d: j = sizeof(Q%d);", j, j);
+ fprintf(tc, " q_flds[%d] = %d;", j, q->nflds);
+ fprintf(tc, " q_max[%d] = %d;", j, max(1,q->nslots));
+ fprintf(tc, " break;\n");
+
+ fprintf(th, "typedef struct Q%d {\n", j);
+ qlen_type(qmax); /* 4.2.2 */
+ fprintf(th, " uchar _t; /* q_type */\n");
+ fprintf(th, " struct {\n");
+
+ for (j = 0; j < q->nflds; j++)
+ { switch (q->fld_width[j]) {
+ case BIT:
+ if (q->nflds != 1)
+ { fprintf(th, "\t\tunsigned");
+ fprintf(th, " fld%d : 1;\n", j);
+ break;
+ } /* else fall through: smaller struct */
+ case MTYPE:
+ case CHAN:
+ case BYTE:
+ fprintf(th, "\t\tuchar fld%d;\n", j);
+ break;
+ case SHORT:
+ fprintf(th, "\t\tshort fld%d;\n", j);
+ break;
+ case INT:
+ fprintf(th, "\t\tint fld%d;\n", j);
+ break;
+ default:
+ fatal("bad channel spec", "");
+ }
+ }
+ fprintf(th, " } contents[%d];\n", max(1, q->nslots));
+ fprintf(th, "} Q%d;\n", q->qid);
+ }
+
+ fprintf(th, "typedef struct Q0 {\t/* generic q */\n");
+ qlen_type(qmax); /* 4.2.2 */
+ fprintf(th, " uchar _t;\n");
+ fprintf(th, "} Q0;\n");
+
+ ntimes(tc, 0, 1, Addq1);
+
+ if (has_random)
+ { fprintf(th, "int Q_has(int");
+ for (j = 0; j < Mpars; j++)
+ fprintf(th, ", int, int");
+ fprintf(th, ");\n");
+
+ fprintf(tc, "int\nQ_has(int into");
+ for (j = 0; j < Mpars; j++)
+ fprintf(tc, ", int want%d, int fld%d", j, j);
+ fprintf(tc, ")\n");
+ fprintf(tc, "{ int i;\n\n");
+ fprintf(tc, " if (!into--)\n");
+ fprintf(tc, " uerror(\"ref to unknown chan ");
+ fprintf(tc, "(recv-poll)\");\n\n");
+ fprintf(tc, " if (into >= now._nr_qs || into < 0)\n");
+ fprintf(tc, " Uerror(\"qrecv bad queue#\");\n\n");
+ fprintf(tc, " for (i = 0; i < ((Q0 *)qptr(into))->Qlen;");
+ fprintf(tc, " i++)\n");
+ fprintf(tc, " {\n");
+ for (j = 0; j < Mpars; j++)
+ { fprintf(tc, " if (want%d && ", j);
+ fprintf(tc, "qrecv(into+1, i, %d, 0) != fld%d)\n",
+ j, j);
+ fprintf(tc, " continue;\n");
+ }
+ fprintf(tc, " return i+1;\n");
+ fprintf(tc, " }\n");
+ fprintf(tc, " return 0;\n");
+ fprintf(tc, "}\n");
+ }
+
+ fprintf(tc, "#if NQS>0\n");
+ fprintf(tc, "void\nqsend(int into, int sorted");
+ for (j = 0; j < Mpars; j++)
+ fprintf(tc, ", int fld%d", j);
+ fprintf(tc, ", int args_given)\n");
+ ntimes(tc, 0, 1, Addq11);
+
+ for (q = qtab; q; q = q->nxt)
+ { sprintf(buf0, "((Q%d *)z)->", q->qid);
+ fprintf(tc, "\tcase %d:%s\n", q->qid,
+ (q->nslots)?"":" /* =rv= */");
+ if (q->nslots == 0) /* reset handshake point */
+ fprintf(tc, "\t\t(trpt+2)->o_m = 0;\n");
+
+ if (has_sorted)
+ { fprintf(tc, "\t\tif (!sorted) goto append%d;\n", q->qid);
+ fprintf(tc, "\t\tfor (j = 0; j < %sQlen; j++)\n", buf0);
+ fprintf(tc, "\t\t{\t/* find insertion point */\n");
+ sprintf(buf0, "((Q%d *)z)->contents[j].fld", q->qid);
+ for (j = 0; j < q->nflds; j++)
+ { fprintf(tc, "\t\t\tif (fld%d > %s%d) continue;\n",
+ j, buf0, j);
+ fprintf(tc, "\t\t\tif (fld%d < %s%d) ", j, buf0, j);
+ fprintf(tc, "goto found%d;\n\n", q->qid);
+ }
+ fprintf(tc, "\t\t}\n");
+ fprintf(tc, "\tfound%d:\n", q->qid);
+ sprintf(buf0, "((Q%d *)z)->", q->qid);
+ fprintf(tc, "\t\tfor (k = %sQlen - 1; k >= j; k--)\n", buf0);
+ fprintf(tc, "\t\t{\t/* shift up */\n");
+ for (j = 0; j < q->nflds; j++)
+ { fprintf(tc, "\t\t\t%scontents[k+1].fld%d = ",
+ buf0, j);
+ fprintf(tc, "%scontents[k].fld%d;\n",
+ buf0, j);
+ }
+ fprintf(tc, "\t\t}\n");
+ fprintf(tc, "\tappend%d:\t/* insert in slot j */\n", q->qid);
+ }
+
+ fprintf(tc, "#ifdef HAS_SORTED\n");
+ fprintf(tc, "\t\t(trpt+1)->ipt = j;\n"); /* ipt was bup.oval */
+ fprintf(tc, "#endif\n");
+ fprintf(tc, "\t\t%sQlen = %sQlen + 1;\n", buf0, buf0);
+ sprintf(buf0, "((Q%d *)z)->contents[j].fld", q->qid);
+ for (j = 0; j < q->nflds; j++)
+ fprintf(tc, "\t\t%s%d = fld%d;\n", buf0, j, j);
+ fprintf(tc, "\t\tif (args_given != %d)\n", q->nflds);
+ fprintf(tc, "\t\t{ if (args_given > %d)\n", q->nflds);
+ fprintf(tc, "\t\t uerror(\"too many parameters in send stmnt\");\n");
+ fprintf(tc, "\t\t else\n");
+ fprintf(tc, "\t\t uerror(\"too few parameters in send stmnt\");\n");
+ fprintf(tc, "\t\t}\n");
+ fprintf(tc, "\t\tbreak;\n");
+ }
+ ntimes(tc, 0, 1, Addq2);
+
+ for (q = qtab; q; q = q->nxt)
+ fprintf(tc, "\tcase %d: return %d;\n", q->qid, (!q->nslots));
+
+ ntimes(tc, 0, 1, Addq3);
+
+ for (q = qtab; q; q = q->nxt)
+ fprintf(tc, "\tcase %d: return (q_sz(from) == %d);\n",
+ q->qid, max(1, q->nslots));
+
+ ntimes(tc, 0, 1, Addq4);
+ for (q = qtab; q; q = q->nxt)
+ { sprintf(buf0, "((Q%d *)z)->", q->qid);
+ fprintf(tc, " case %d:%s\n\t\t",
+ q->qid, (q->nslots)?"":" /* =rv= */");
+ if (q->nflds == 1)
+ { fprintf(tc, "if (fld == 0) r = %s", buf0);
+ fprintf(tc, "contents[slot].fld0;\n");
+ } else
+ { fprintf(tc, "switch (fld) {\n");
+ ncases(tc, q->qid, 0, q->nflds, R12);
+ fprintf(tc, "\t\tdefault: Uerror");
+ fprintf(tc, "(\"too many fields in recv\");\n");
+ fprintf(tc, "\t\t}\n");
+ }
+ fprintf(tc, "\t\tif (done)\n");
+ if (q->nslots == 0)
+ { fprintf(tc, "\t\t{ j = %sQlen - 1;\n", buf0);
+ fprintf(tc, "\t\t %sQlen = 0;\n", buf0);
+ sprintf(buf0, "\t\t\t((Q%d *)z)->contents", q->qid);
+ } else
+ { fprintf(tc, "\t\t{ j = %sQlen;\n", buf0);
+ fprintf(tc, "\t\t %sQlen = --j;\n", buf0);
+ fprintf(tc, "\t\t for (k=slot; k<j; k++)\n");
+ fprintf(tc, "\t\t {\n");
+ sprintf(buf0, "\t\t\t((Q%d *)z)->contents", q->qid);
+ for (j = 0; j < q->nflds; j++)
+ { fprintf(tc, "\t%s[k].fld%d = \n", buf0, j);
+ fprintf(tc, "\t\t%s[k+1].fld%d;\n", buf0, j);
+ }
+ fprintf(tc, "\t\t }\n");
+ }
+
+ for (j = 0; j < q->nflds; j++)
+ fprintf(tc, "%s[j].fld%d = 0;\n", buf0, j);
+ fprintf(tc, "\t\t\tif (fld+1 != %d)\n\t\t\t", q->nflds);
+ fprintf(tc, "\tuerror(\"missing pars in receive\");\n");
+ /* incompletely received msgs cannot be unrecv'ed */
+ fprintf(tc, "\t\t}\n");
+ fprintf(tc, "\t\tbreak;\n");
+ }
+ ntimes(tc, 0, 1, Addq5);
+ for (q = qtab; q; q = q->nxt)
+ fprintf(tc, " case %d: j = sizeof(Q%d); break;\n",
+ q->qid, q->qid);
+ ntimes(tc, 0, 1, R8b);
+
+ ntimes(th, 0, 1, Proto); /* tag on function prototypes */
+ fprintf(th, "void qsend(int, int");
+ for (j = 0; j < Mpars; j++)
+ fprintf(th, ", int");
+ fprintf(th, ", int);\n");
+
+ fprintf(th, "#define Addproc(x) addproc(x");
+ for (j = 0; j < Npars; j++)
+ fprintf(th, ", 0");
+ fprintf(th, ")\n");
+}
--- /dev/null
+/***** spin: pangen1.h *****/
+
+/* Copyright (c) 1989-2008 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
+
+static char *Code2a[] = { /* the tail of procedure run() */
+ "#if defined(VERI) && !defined(NOREDUCE) && !defined(NP)",
+ " if (!state_tables",
+ "#ifdef HAS_CODE",
+ " && !readtrail",
+ "#endif",
+ "#if NCORE>1",
+ " && core_id == 0",
+ "#endif",
+ " )",
+ " { printf(\"warning: for p.o. reduction to be valid \");",
+ " printf(\"the never claim must be stutter-invariant\\n\");",
+ " printf(\"(never claims generated from LTL \");",
+ " printf(\"formulae are stutter-invariant)\\n\");",
+ " }",
+ "#endif",
+ " UnBlock; /* disable rendez-vous */",
+ "#ifdef BITSTATE",
+#ifndef POWOW
+ " if (udmem)",
+ " { udmem *= 1024L*1024L;",
+ " #if NCORE>1",
+ " if (!readtrail)",
+ " { void init_SS(unsigned long);",
+ " init_SS((unsigned long) udmem);",
+ " } else",
+ " #endif",
+ " SS = (uchar *) emalloc(udmem);",
+ " bstore = bstore_mod;",
+ " } else",
+#endif
+ " #if NCORE>1",
+ " { void init_SS(unsigned long);",
+ " init_SS(ONE_L<<(ssize-3));",
+ " }",
+ " #else",
+ " SS = (uchar *) emalloc(ONE_L<<(ssize-3));",
+ " #endif",
+ "#else", /* if not BITSTATE */
+ " hinit();",
+ "#endif",
+ "#if defined(FULLSTACK) && defined(BITSTATE)",
+ " onstack_init();",
+ "#endif",
+ "#if defined(CNTRSTACK) && !defined(BFS)",
+ " LL = (uchar *) emalloc(ONE_L<<(ssize-3));",
+ "#endif",
+ " stack = ( Stack *) emalloc(sizeof(Stack));",
+ " svtack = (Svtack *) emalloc(sizeof(Svtack));",
+ " /* a place to point for Pptr of non-running procs: */",
+ " noptr = (uchar *) emalloc(Maxbody * sizeof(char));",
+ "#ifdef SVDUMP",
+ " if (vprefix > 0)",
+ " write(svfd, (uchar *) &vprefix, sizeof(int));",
+ "#endif",
+ "#ifdef VERI",
+ " Addproc(VERI); /* never - pid = 0 */",
+ "#endif",
+ " active_procs(); /* started after never */",
+ "#ifdef EVENT_TRACE",
+ " now._event = start_event;",
+ " reached[EVENT_TRACE][start_event] = 1;",
+ "#endif",
+
+ "#ifdef HAS_CODE",
+ " globinit();",
+ "#endif",
+ "#ifdef BITSTATE",
+ "go_again:",
+ "#endif",
+ " do_the_search();",
+ "#ifdef BITSTATE",
+ " if (--Nrun > 0 && HASH_CONST[++HASH_NR])",
+ " { printf(\"Run %%d:\\n\", HASH_NR);",
+ " wrap_stats();",
+ " printf(\"\\n\");",
+ " memset(SS, 0, ONE_L<<(ssize-3));",
+ "#ifdef CNTRSTACK",
+ " memset(LL, 0, ONE_L<<(ssize-3));",
+ "#endif",
+ "#ifdef FULLSTACK",
+ " memset((uchar *) S_Tab, 0, ",
+ " maxdepth*sizeof(struct H_el *));",
+ "#endif",
+ " nstates=nlinks=truncs=truncs2=ngrabs = 0;",
+ " nlost=nShadow=hcmp = 0;",
+ " Fa=Fh=Zh=Zn = 0;",
+ " PUT=PROBE=ZAPS=Ccheck=Cholds = 0;",
+ " goto go_again;",
+ " }",
+ "#endif",
+ "}",
+ "#ifdef HAS_PROVIDED",
+ "int provided(int, uchar, int, Trans *);",
+ "#endif",
+
+ "#if NCORE>1",
+ "#define GLOBAL_LOCK (0)",
+ "#ifndef CS_N",
+ "#define CS_N (256*NCORE)", /* must be a power of 2 */
+ "#endif",
+
+ "#ifdef NGQ", /* no global queue */
+ "#define NR_QS (NCORE)",
+ "#define CS_NR (CS_N+1) /* 2^N + 1, nr critical sections */",
+ "#define GQ_RD GLOBAL_LOCK", /* not really used in this mode */
+ "#define GQ_WR GLOBAL_LOCK", /* but just in case... */
+ "#define CS_ID (1 + (int) (j1 & (CS_N-1))) /* mask: 2^N - 1, zero reserved */",
+ "#define QLOCK(n) (1+n)", /* overlaps first n zones of hashtable */
+ "#else",
+ "#define NR_QS (NCORE+1)", /* add a global queue */
+ "#define CS_NR (CS_N+3)", /* 2 extra locks for global q */
+ "#define GQ_RD (1)", /* read access to global q */
+ "#define GQ_WR (2)", /* write access to global q */
+ "#define CS_ID (3 + (int) (j1 & (CS_N-1)))",
+ "#define QLOCK(n) (3+n)",/* overlaps first n zones of hashtable */
+ "#endif",
+ "",
+ "void e_critical(int);",
+ "void x_critical(int);",
+ "",
+ "#ifndef SEP_STATE",
+ " #define enter_critical(w) e_critical(w)",
+ " #define leave_critical(w) x_critical(w)",
+ "#else",
+ " #ifdef NGQ",
+ " #define enter_critical(w) { if (w < 1+NCORE) e_critical(w); }",
+ " #define leave_critical(w) { if (w < 1+NCORE) x_critical(w); }",
+ " #else",
+ " #define enter_critical(w) { if (w < 3+NCORE) e_critical(w); }",
+ " #define leave_critical(w) { if (w < 3+NCORE) x_critical(w); }",
+ " #endif",
+ "#endif",
+ "",
+ "int",
+ "cpu_printf(const char *fmt, ...)", /* only used with VERBOSE/CHECK/DEBUG */
+ "{ va_list args;",
+ " enter_critical(GLOBAL_LOCK); /* printing */",
+ " printf(\"cpu%%d: \", core_id);",
+ " fflush(stdout);",
+ " va_start(args, fmt);",
+ " vprintf(fmt, args);",
+ " va_end(args);",
+ " fflush(stdout);",
+ " leave_critical(GLOBAL_LOCK);",
+ " return 1;",
+ "}",
+ "#else",
+ "int",
+ "cpu_printf(const char *fmt, ...)",
+ "{ va_list args;",
+ " va_start(args, fmt);",
+ " vprintf(fmt, args);",
+ " va_end(args);",
+ " return 1;",
+ "}",
+ "#endif",
+
+#ifndef PRINTF
+ "int",
+ "Printf(const char *fmt, ...)",
+ "{ /* Make sure the args to Printf",
+ " * are always evaluated (e.g., they",
+ " * could contain a run stmnt)",
+ " * but do not generate the output",
+ " * during verification runs",
+ " * unless explicitly wanted",
+ " * If this fails on your system",
+ " * compile SPIN itself -DPRINTF",
+ " * and this code is not generated",
+ " */",
+ "#ifdef HAS_CODE",
+ " if (readtrail)",
+ " { va_list args;",
+ " va_start(args, fmt);",
+ " vprintf(fmt, args);",
+ " va_end(args);",
+ " return 1;",
+ " }",
+ "#endif",
+ "#ifdef PRINTF",
+ " va_list args;",
+ " va_start(args, fmt);",
+ " vprintf(fmt, args);",
+ " va_end(args);",
+ "#endif",
+ " return 1;",
+ "}",
+#endif
+ "extern void printm(int);",
+
+ "#ifndef SC",
+ "#define getframe(i) &trail[i];",
+ "#else",
+ "static long HHH, DDD, hiwater;",
+ "static long CNT1, CNT2;",
+ "static int stackwrite;",
+ "static int stackread;",
+ "static Trail frameptr;",
+ "Trail *",
+ "getframe(int d)",
+ "{",
+ " if (CNT1 == CNT2)",
+ " return &trail[d];",
+ "",
+ " if (d >= (CNT1-CNT2)*DDD)",
+ " return &trail[d - (CNT1-CNT2)*DDD];",
+ "",
+ " if (!stackread",
+ " && (stackread = open(stackfile, 0)) < 0)",
+ " { printf(\"getframe: cannot open %%s\\n\", stackfile);",
+ " wrapup();",
+ " }",
+ " if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1",
+ " || read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail))",
+ " { printf(\"getframe: frame read error\\n\");",
+ " wrapup();",
+ " }",
+ " return &frameptr;",
+ "}",
+ "#endif",
+
+ "#if !defined(SAFETY) && !defined(BITSTATE)",
+ "#if !defined(FULLSTACK) || defined(MA)",
+ "#define depth_of(x) A_depth /* an estimate */",
+ "#else",
+ "int",
+ "depth_of(struct H_el *s)",
+ "{ Trail *t; int d;",
+ " for (d = 0; d <= A_depth; d++)",
+ " { t = getframe(d);",
+ " if (s == t->ostate)",
+ " return d;",
+ " }",
+ " printf(\"pan: cannot happen, depth_of\\n\");",
+ " return depthfound;",
+ "}",
+ "#endif",
+ "#endif",
+
+ "#if NCORE>1",
+ "extern void cleanup_shm(int);",
+ "volatile unsigned int *search_terminated; /* to signal early termination */",
+ /*
+ * Meaning of bitflags in search_terminated:
+ * 1 set by pan_exit
+ * 2 set by wrapup
+ * 4 set by uerror
+ * 8 set by sudden_stop -- called after someone_crashed and [Uu]error
+ * 16 set by cleanup_shm
+ * 32 set by give_up -- called on signal
+ * 64 set by proxy_exit
+ * 128 set by proxy on write port failure
+ * 256 set by proxy on someone_crashed
+ *
+ * Flags 8|32|128|256 indicate abnormal termination
+ *
+ * The flags are checked in 4 functions in the code:
+ * sudden_stop()
+ * someone_crashed() (proxy and pan version)
+ * mem_hand_off()
+ */
+ "#endif",
+ "void",
+ "pan_exit(int val)",
+ "{ void stop_timer(void);",
+ " if (signoff)",
+ " { printf(\"--end of output--\\n\");",
+ " }",
+ "#if NCORE>1",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 1; /* pan_exit */",
+ " }",
+ "#ifdef USE_DISK",
+ " { void dsk_stats(void);",
+ " dsk_stats();",
+ " }",
+ "#endif",
+ " if (!state_tables && !readtrail)",
+ " { cleanup_shm(1);",
+ " }",
+ "#endif",
+ " if (val == 2)",
+ " { val = 0;",
+ " } else",
+ " { stop_timer();",
+ " }",
+ " exit(val);",
+ "}",
+
+ "#ifdef HAS_CODE",
+ "char *",
+ "transmognify(char *s)",
+ "{ char *v, *w;",
+ " static char buf[2][2048];",
+ " int i, toggle = 0;",
+
+ " if (!s || strlen(s) > 2047) return s;",
+ " memset(buf[0], 0, 2048);",
+ " memset(buf[1], 0, 2048);",
+ " strcpy(buf[toggle], s);",
+ " while ((v = strstr(buf[toggle], \"{c_code\")))", /* assign v */
+ " { *v = '\\0'; v++;",
+ " strcpy(buf[1-toggle], buf[toggle]);",
+ " for (w = v; *w != '}' && *w != '\\0'; w++) /* skip */;",
+ " if (*w != '}') return s;",
+ " *w = '\\0'; w++;",
+ " for (i = 0; code_lookup[i].c; i++)",
+ " if (strcmp(v, code_lookup[i].c) == 0",
+ " && strlen(v) == strlen(code_lookup[i].c))",
+ " { if (strlen(buf[1-toggle])",
+ " + strlen(code_lookup[i].t)",
+ " + strlen(w) > 2047)",
+ " return s;",
+ " strcat(buf[1-toggle], code_lookup[i].t);",
+ " break;",
+ " }",
+ " strcat(buf[1-toggle], w);",
+ " toggle = 1 - toggle;",
+ " }",
+ " buf[toggle][2047] = '\\0';",
+ " return buf[toggle];",
+ "}",
+ "#else",
+ "char * transmognify(char *s) { return s; }",
+ "#endif",
+
+ "#ifdef HAS_CODE",
+ "void",
+ "add_src_txt(int ot, int tt)",
+ "{ Trans *t;",
+ " char *q;",
+ "",
+ " for (t = trans[ot][tt]; t; t = t->nxt)",
+ " { printf(\"\\t\\t\");",
+
+ " q = transmognify(t->tp);",
+ " for ( ; q && *q; q++)",
+ " if (*q == '\\n')",
+ " printf(\"\\\\n\");",
+ " else",
+ " putchar(*q);",
+ " printf(\"\\n\");",
+ " }",
+ "}",
+ "void",
+ "wrap_trail(void)",
+ "{ static int wrap_in_progress = 0;",
+ " int i; short II;",
+ " P0 *z;",
+ "",
+ " if (wrap_in_progress++) return;",
+ "",
+ " printf(\"spin: trail ends after %%ld steps\\n\", depth);",
+ " if (onlyproc >= 0)",
+ " { if (onlyproc >= now._nr_pr) { pan_exit(0); }",
+ " II = onlyproc;",
+ " z = (P0 *)pptr(II);",
+ " printf(\"%%3ld:\tproc %%d (%%s) \",",
+ " depth, II, procname[z->_t]);",
+ " for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\" line %%3d\",",
+ " src_all[i].src[z->_p]);",
+ " break;",
+ " }",
+ " printf(\" (state %%2d)\", z->_p);",
+ " if (!stopstate[z->_t][z->_p])",
+ " printf(\" (invalid end state)\");",
+ " printf(\"\\n\");",
+ " add_src_txt(z->_t, z->_p);",
+ " pan_exit(0);",
+ " }",
+ " printf(\"#processes %%d:\\n\", now._nr_pr);",
+ " if (depth < 0) depth = 0;",
+ " for (II = 0; II < now._nr_pr; II++)",
+ " { z = (P0 *)pptr(II);",
+ " printf(\"%%3ld:\tproc %%d (%%s) \",",
+ " depth, II, procname[z->_t]);",
+ " for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\" line %%3d\",",
+ " src_all[i].src[z->_p]);",
+ " break;",
+ " }",
+ " printf(\" (state %%2d)\", z->_p);",
+ " if (!stopstate[z->_t][z->_p])",
+ " printf(\" (invalid end state)\");",
+ " printf(\"\\n\");",
+ " add_src_txt(z->_t, z->_p);",
+ " }",
+ " c_globals();",
+ " for (II = 0; II < now._nr_pr; II++)",
+ " { z = (P0 *)pptr(II);",
+ " c_locals(II, z->_t);",
+ " }",
+ "#ifdef ON_EXIT",
+ " ON_EXIT;",
+ "#endif",
+ " pan_exit(0);",
+ "}",
+ "FILE *",
+ "findtrail(void)",
+ "{ FILE *fd;",
+ " char fnm[512], *q;",
+ " char MyFile[512];", /* avoid using a non-writable string */
+ " char MySuffix[16];",
+ " int try_core;",
+ " int candidate_files;",
+ "",
+ " if (trailfilename != NULL)",
+ " { fd = fopen(trailfilename, \"r\");",
+ " if (fd == NULL)",
+ " { printf(\"pan: cannot find %%s\\n\", trailfilename);",
+ " pan_exit(1);",
+ " } /* else */",
+ " goto success;",
+ " }",
+ "talk:",
+ " try_core = 1;",
+ " candidate_files = 0;",
+ " tprefix = \"trail\";",
+ " strcpy(MyFile, TrailFile);",
+ " do { /* see if there's more than one possible trailfile */",
+ " if (whichtrail)",
+ " { sprintf(fnm, \"%%s%%d.%%s\",",
+ " MyFile, whichtrail, tprefix);",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd != NULL)",
+ " { candidate_files++;",
+ " if (verbose==100)",
+ " printf(\"trail%%d: %%s\\n\",",
+ " candidate_files, fnm);",
+ " fclose(fd);",
+ " }",
+ " if ((q = strchr(MyFile, \'.\')) != NULL)",
+ " { *q = \'\\0\';", /* e.g., strip .pml */
+ " sprintf(fnm, \"%%s%%d.%%s\",",
+ " MyFile, whichtrail, tprefix);",
+ " *q = \'.\';",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd != NULL)",
+ " { candidate_files++;",
+ " if (verbose==100)",
+ " printf(\"trail%%d: %%s\\n\",",
+ " candidate_files, fnm);",
+ " fclose(fd);",
+ " } }",
+ " } else",
+ " { sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd != NULL)",
+ " { candidate_files++;",
+ " if (verbose==100)",
+ " printf(\"trail%%d: %%s\\n\",",
+ " candidate_files, fnm);",
+ " fclose(fd);",
+ " }",
+ " if ((q = strchr(MyFile, \'.\')) != NULL)",
+ " { *q = \'\\0\';", /* e.g., strip .pml */
+ " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ " *q = \'.\';",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd != NULL)",
+ " { candidate_files++;",
+ " if (verbose==100)",
+ " printf(\"trail%%d: %%s\\n\",",
+ " candidate_files, fnm);",
+ " fclose(fd);",
+ " } } }",
+ " tprefix = MySuffix;",
+ " sprintf(tprefix, \"cpu%%d_trail\", try_core++);",
+ " } while (try_core <= NCORE);",
+ "",
+ " if (candidate_files != 1)",
+ " { if (verbose != 100)",
+ " { printf(\"error: there are %%d trail files:\\n\",",
+ " candidate_files);",
+ " verbose = 100;",
+ " goto talk;",
+ " } else",
+ " { printf(\"pan: rm or mv all except one\\n\");",
+ " exit(1);",
+ " } }",
+
+ " try_core = 1;",
+ " strcpy(MyFile, TrailFile); /* restore */",
+ " tprefix = \"trail\";",
+ "try_again:",
+ " if (whichtrail)",
+ " { sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, tprefix);",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd == NULL && (q = strchr(MyFile, \'.\')))",
+ " { *q = \'\\0\';", /* e.g., strip .pml on original file */
+ " sprintf(fnm, \"%%s%%d.%%s\",",
+ " MyFile, whichtrail, tprefix);",
+ " *q = \'.\';",
+ " fd = fopen(fnm, \"r\");",
+ " }",
+ " } else",
+ " { sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ " fd = fopen(fnm, \"r\");",
+ " if (fd == NULL && (q = strchr(MyFile, \'.\')))",
+ " { *q = \'\\0\';", /* e.g., strip .pml on original file */
+ " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ " *q = \'.\';",
+ " fd = fopen(fnm, \"r\");",
+ " } }",
+ " if (fd == NULL)",
+ " { if (try_core < NCORE)",
+ " { tprefix = MySuffix;",
+ " sprintf(tprefix, \"cpu%%d_trail\", try_core++);",
+ " goto try_again;",
+ " }",
+ " printf(\"pan: cannot find trailfile %%s\\n\", fnm);",
+ " pan_exit(1);",
+ " }",
+ "success:",
+ "#if NCORE>1 && defined(SEP_STATE)",
+ " { void set_root(void); /* for partial traces from local root */",
+ " set_root();",
+ " }",
+ "#endif",
+ " return fd;",
+ "}",
+ "",
+ "uchar do_transit(Trans *, short);",
+ "",
+ "void",
+ "getrail(void)",
+ "{ FILE *fd;",
+ " char *q;",
+ " int i, t_id, lastnever=-1; short II;",
+ " Trans *t;",
+ " P0 *z;",
+ "",
+ " fd = findtrail(); /* exits if unsuccessful */",
+ " while (fscanf(fd, \"%%ld:%%d:%%d\\n\", &depth, &i, &t_id) == 3)",
+ " { if (depth == -1)",
+ " printf(\"<<<<<START OF CYCLE>>>>>\\n\");",
+ " if (depth < 0)",
+ " continue;",
+ " if (i > now._nr_pr)",
+ " { printf(\"pan: Error, proc %%d invalid pid \", i);",
+ " printf(\"transition %%d\\n\", t_id);",
+ " break;",
+ " }",
+ " II = i;",
+ " z = (P0 *)pptr(II);",
+ " for (t = trans[z->_t][z->_p]; t; t = t->nxt)",
+ " if (t->t_id == (T_ID) t_id)",
+ " break;",
+ " if (!t)",
+ " { for (i = 0; i < NrStates[z->_t]; i++)",
+ " { t = trans[z->_t][i];",
+ " if (t && t->t_id == (T_ID) t_id)",
+ " { printf(\"\\tRecovered at state %%d\\n\", i);",
+ " z->_p = i;",
+ " goto recovered;",
+ " } }",
+ " printf(\"pan: Error, proc %%d type %%d state %%d: \",",
+ " II, z->_t, z->_p);",
+ " printf(\"transition %%d not found\\n\", t_id);",
+ " printf(\"pan: list of possible transitions in this process:\\n\");",
+ " if (z->_t >= 0 && z->_t <= _NP_)",
+ " for (t = trans[z->_t][z->_p]; t; t = t->nxt)",
+ " printf(\" t_id %%d -- case %%d, [%%s]\\n\",",
+ " t->t_id, t->forw, t->tp);",
+ " break; /* pan_exit(1); */",
+ " }",
+ "recovered:",
+ " q = transmognify(t->tp);",
+ " if (gui) simvals[0] = \'\\0\';",
+
+ " this = pptr(II);",
+ " trpt->tau |= 1;", /* timeout always possible */
+ " if (!do_transit(t, II))",
+ " { if (onlyproc >= 0 && II != onlyproc)",
+ " goto moveon;",
+ " printf(\"pan: error, next transition UNEXECUTABLE on replay\\n\");",
+ " printf(\" most likely causes: missing c_track statements\\n\");",
+ " printf(\" or illegal side-effects in c_expr statements\\n\");",
+ " }",
+
+ " if (onlyproc >= 0 && II != onlyproc)",
+ " goto moveon;",
+
+ " if (verbose)",
+ " { printf(\"%%3ld: proc %%2d (%%s) \", depth, II, procname[z->_t]);",
+
+ " for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\" line %%3d \\\"%%s\\\" \",",
+ " src_all[i].src[z->_p], PanSource);",
+ " break;",
+ " }",
+
+ " printf(\"(state %%d) trans {%%d,%%d} [%%s]\\n\",",
+ " z->_p, t_id, t->forw, q?q:\"\");",
+
+ " c_globals();",
+ " for (i = 0; i < now._nr_pr; i++)",
+ " { c_locals(i, ((P0 *)pptr(i))->_t);",
+ " }",
+ " } else",
+ " if (strcmp(procname[z->_t], \":never:\") == 0)",
+ " { if (lastnever != (int) z->_p)",
+ " { for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\"MSC: ~G %%d\\n\",",
+ " src_all[i].src[z->_p]);",
+ " break;",
+ " }",
+ " if (!src_all[i].src)",
+ " printf(\"MSC: ~R %%d\\n\", z->_p);",
+ " }",
+ " lastnever = z->_p;",
+ " goto sameas;",
+ " } else",
+ " if (strcmp(procname[z->_t], \":np_:\") != 0)",
+ " {",
+ "sameas: if (no_rck) goto moveon;",
+ " if (coltrace)",
+ " { printf(\"%%ld: \", depth);",
+ " for (i = 0; i < II; i++)",
+ " printf(\"\\t\\t\");",
+ " printf(\"%%s(%%d):\", procname[z->_t], II);",
+ " printf(\"[%%s]\\n\", q?q:\"\");",
+ " } else if (!silent)",
+ " { if (strlen(simvals) > 0) {",
+ " printf(\"%%3ld: proc %%2d (%%s)\", ",
+ " depth, II, procname[z->_t]);",
+ " for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\" line %%3d \\\"%%s\\\" \",",
+ " src_all[i].src[z->_p], PanSource);",
+ " break;",
+ " }",
+ " printf(\"(state %%d)\t[values: %%s]\\n\", z->_p, simvals);",
+ " }",
+ " printf(\"%%3ld: proc %%2d (%%s)\", ",
+ " depth, II, procname[z->_t]);",
+ " for (i = 0; src_all[i].src; i++)",
+ " if (src_all[i].tp == (int) z->_t)",
+ " { printf(\" line %%3d \\\"%%s\\\" \",",
+ " src_all[i].src[z->_p], PanSource);",
+ " break;",
+ " }",
+ " printf(\"(state %%d)\t[%%s]\\n\", z->_p, q?q:\"\");",
+ " /* printf(\"\\n\"); */",
+ " } }",
+ "moveon: z->_p = t->st;",
+ " }",
+ " wrap_trail();",
+ "}",
+ "#endif",
+ "int",
+ "f_pid(int pt)",
+ "{ int i;",
+ " P0 *z;",
+ " for (i = 0; i < now._nr_pr; i++)",
+ " { z = (P0 *)pptr(i);",
+ " if (z->_t == (unsigned) pt)",
+ " return BASE+z->_pid;",
+ " }",
+ " return -1;",
+ "}",
+ "#ifdef VERI",
+ "void check_claim(int);",
+ "#endif",
+ "",
+ "#if !defined(HASH64) && !defined(HASH32)",
+ " #define HASH32",
+ "#endif",
+ "#if defined(HASH32) && defined(SAFETY) && !defined(SFH) && !defined(SPACE)",
+ " #define SFH",
+ "#endif",
+ "#if defined(SFH) && (defined(BITSTATE) || defined(COLLAPSE) || defined(HC) || defined(HASH64))",
+ " #undef SFH", /* need 2 hash fcts, for which Jenkins is best */
+ "#endif", /* or a 64 bit hash, which we dont have for SFH */
+ "#if defined(SFH) && !defined(NOCOMP)",
+ " #define NOCOMP /* go for speed */",
+ "#endif",
+ "#if NCORE>1 && !defined(GLOB_HEAP)",
+ " #define SEP_HEAP /* version 5.1.2 */",
+ "#endif",
+ "",
+ "#ifdef BITSTATE",
+#ifndef POWOW
+ "int",
+ "bstore_mod(char *v, int n) /* hasharray size not a power of two */",
+ "{ unsigned long x, y;",
+ " unsigned int i = 1;",
+ "",
+ " d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */",
+ " x = K1; y = j3;", /* was K2 before 5.1.1 */
+ " for (;;)",
+ " { if (!(SS[x%%udmem]&(1<<y))) break;", /* take the hit in speed */
+ " if (i == hfns) {",
+ "#ifdef DEBUG",
+ " printf(\"Old bitstate\\n\");",
+ "#endif",
+ " return 1;",
+ " }",
+ " x = (x + K2 + i);", /* no mask, using mod - was K1 before 5.1.1 */
+ " y = (y + j4) & 7;",
+ " i++;",
+ " }",
+ "#ifdef RANDSTOR",
+ " if (rand()%%100 > RANDSTOR) return 0;",
+ "#endif",
+ " for (;;)",
+ " { SS[x%%udmem] |= (1<<y);",
+ " if (i == hfns) break;", /* done */
+ " x = (x + K2 + i);", /* no mask - was K1 before 5.1.1 */
+ " y = (y + j4) & 7;",
+ " i++;",
+ " }",
+ "#ifdef DEBUG",
+ " printf(\"New bitstate\\n\");",
+ "#endif",
+ " if (now._a_t&1)",
+ " { nShadow++;",
+ " }",
+ " return 0;",
+ "}",
+#endif
+ "int",
+ "bstore_reg(char *v, int n) /* extended hashing, Peter Dillinger, 2004 */",
+ "{ unsigned long x, y;",
+ " unsigned int i = 1;",
+ "",
+ " d_hash((uchar *) v, n); /* sets j1-j4 */",
+ " x = j2; y = j3;",
+ " for (;;)",
+ " { if (!(SS[x]&(1<<y))) break;", /* at least one bit not set */
+ " if (i == hfns) {",
+ "#ifdef DEBUG",
+ " printf(\"Old bitstate\\n\");",
+ "#endif",
+ " return 1;",
+ " }",
+ " x = (x + j1 + i) & nmask;",
+ " y = (y + j4) & 7;",
+ " i++;",
+ " }",
+ "#ifdef RANDSTOR",
+ " if (rand()%%100 > RANDSTOR) return 0;",
+ "#endif",
+ " for (;;)",
+ " { SS[x] |= (1<<y);",
+ " if (i == hfns) break;", /* done */
+ " x = (x + j1 + i) & nmask;",
+ " y = (y + j4) & 7;",
+ " i++;",
+ " }",
+ "#ifdef DEBUG",
+ " printf(\"New bitstate\\n\");",
+ "#endif",
+ " if (now._a_t&1)",
+ " { nShadow++;",
+ " }",
+ " return 0;",
+ "}",
+ "#endif", /* BITSTATE */
+ "unsigned long TMODE = 0666; /* file permission bits for trail files */",
+ "",
+ "int trcnt=1;",
+ "char snap[64], fnm[512];",
+ "",
+ "int",
+ "make_trail(void)",
+ "{ int fd;",
+ " char *q;",
+ " char MyFile[512];",
+ " int w_flags = O_CREAT|O_WRONLY|O_TRUNC;",
+ "",
+ " if (exclusive == 1 && iterative == 0)",
+ " { w_flags |= O_EXCL;",
+ " }",
+ "",
+ " q = strrchr(TrailFile, \'/\');",
+ " if (q == NULL) q = TrailFile; else q++;",
+ " strcpy(MyFile, q); /* TrailFile is not a writable string */",
+ "",
+ " if (iterative == 0 && Nr_Trails++ > 0)",
+ " { sprintf(fnm, \"%%s%%d.%%s\",",
+ " MyFile, Nr_Trails-1, tprefix);",
+ " } else",
+ " {",
+ "#ifdef PUTPID",
+ " sprintf(fnm, \"%%s%%d.%%s\", MyFile, getpid(), tprefix);",
+ "#else",
+ " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ "#endif",
+ " }",
+#if 1
+ " if ((fd = open(fnm, w_flags, TMODE)) < 0)",
+#else
+ " if ((fd = creat(fnm, TMODE)) < 0)",
+#endif
+ " { if ((q = strchr(MyFile, \'.\')))",
+ " { *q = \'\\0\';", /* strip .pml */
+ " if (iterative == 0 && Nr_Trails-1 > 0)",
+ " sprintf(fnm, \"%%s%%d.%%s\",",
+ " MyFile, Nr_Trails-1, tprefix);",
+ " else",
+ " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);",
+ " *q = \'.\';",
+#if 1
+ " fd = open(fnm, w_flags, TMODE);",
+#else
+ " fd = creat(fnm, TMODE);",
+#endif
+ " } }",
+ " if (fd < 0)",
+ " { printf(\"pan: cannot create %%s\\n\", fnm);",
+ " perror(\"cause\");",
+ " } else",
+ " {",
+ "#if NCORE>1 && (defined(SEP_STATE) || !defined(FULL_TRAIL))",
+ " void write_root(void); ",
+ " write_root();",
+ "#else",
+ " printf(\"pan: wrote %%s\\n\", fnm);",
+ "#endif",
+ " }",
+ " return fd;",
+ "}",
+ "",
+ "#ifndef FREQ",
+ "#define FREQ (1000000)",
+ "#endif",
+ 0
+};
+
+static char *Code2b[] = { /* breadth-first search option */
+ "#ifdef BFS",
+ "#define Q_PROVISO",
+ "#ifndef INLINE_REV",
+ "#define INLINE_REV",
+ "#endif",
+ "",
+ "typedef struct SV_Hold {",
+ " State *sv;",
+ " int sz;",
+ " struct SV_Hold *nxt;",
+ "} SV_Hold;",
+ "",
+ "typedef struct EV_Hold {",
+ " char *sv;", /* Mask */
+ " int sz;", /* vsize */
+ " int nrpr;",
+ " int nrqs;",
+ " char *po;",
+ " char *qo;",
+ " char *ps, *qs;",
+ " struct EV_Hold *nxt;",
+ "} EV_Hold;",
+ "",
+ "typedef struct BFS_Trail {",
+ " Trail *frame;",
+ " SV_Hold *onow;",
+ " EV_Hold *omask;",
+ "#ifdef Q_PROVISO",
+ " struct H_el *lstate;",
+ "#endif",
+ " short boq;",
+ " struct BFS_Trail *nxt;",
+ "} BFS_Trail;",
+ "",
+ "BFS_Trail *bfs_trail, *bfs_bot, *bfs_free;",
+ "",
+ "SV_Hold *svhold, *svfree;",
+ "",
+"#ifdef BFS_DISK",
+ "#ifndef BFS_LIMIT",
+ " #define BFS_LIMIT 100000",
+ "#endif",
+ "#ifndef BFS_DSK_LIMIT",
+ " #define BFS_DSK_LIMIT 1000000",
+ "#endif",
+
+ "#if defined(WIN32) || defined(WIN64)",
+ " #define RFLAGS (O_RDONLY|O_BINARY)",
+ " #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)",
+ "#else",
+ " #define RFLAGS (O_RDONLY)",
+ " #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC)",
+ "#endif",
+
+ "long bfs_size_limit;",
+ "int bfs_dsk_write = -1;",
+ "int bfs_dsk_read = -1;",
+ "long bfs_dsk_writes, bfs_dsk_reads;",
+ "int bfs_dsk_seqno_w, bfs_dsk_seqno_r;",
+"#endif",
+ "",
+ "uchar do_reverse(Trans *, short, uchar);",
+ "void snapshot(void);",
+ "",
+ "SV_Hold *",
+ "getsv(int n)",
+ "{ SV_Hold *h = (SV_Hold *) 0, *oh;",
+ "",
+ " oh = (SV_Hold *) 0;",
+ " for (h = svfree; h; oh = h, h = h->nxt)",
+ " { if (n == h->sz)",
+ " { if (!oh)",
+ " svfree = h->nxt;",
+ " else",
+ " oh->nxt = h->nxt;",
+ " h->nxt = (SV_Hold *) 0;",
+ " break;",
+ " }",
+ " if (n < h->sz)",
+ " { h = (SV_Hold *) 0;",
+ " break;",
+ " }",
+ " /* else continue */",
+ " }",
+ "",
+ " if (!h)",
+ " { h = (SV_Hold *) emalloc(sizeof(SV_Hold));",
+ " h->sz = n;",
+"#ifdef BFS_DISK",
+ " if (bfs_size_limit >= BFS_LIMIT)",
+ " { h->sv = (State *) 0; /* means: read disk */",
+ " bfs_dsk_writes++; /* count */",
+ " if (bfs_dsk_write < 0 /* file descriptor */",
+ " || bfs_dsk_writes%%BFS_DSK_LIMIT == 0)",
+ " { char dsk_nm[32];",
+ " if (bfs_dsk_write >= 0)",
+ " { (void) close(bfs_dsk_write);",
+ " }",
+ " sprintf(dsk_nm, \"pan_bfs_%%d.tmp\", bfs_dsk_seqno_w++);",
+ " bfs_dsk_write = open(dsk_nm, WFLAGS, 0644);",
+ " if (bfs_dsk_write < 0)",
+ " { Uerror(\"could not create tmp disk file\");",
+ " }",
+ " printf(\"pan: created disk file %%s\\n\", dsk_nm);",
+ " }",
+ " if (write(bfs_dsk_write, (char *) &now, n) != n)",
+ " { Uerror(\"aborting -- disk write failed (disk full?)\");",
+ " }",
+ " return h; /* no memcpy */",
+ " }", /* else */
+ " bfs_size_limit++;",
+"#endif",
+ " h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n);",
+ " }",
+ "",
+ " memcpy((char *)h->sv, (char *)&now, n);",
+ " return h;",
+ "}",
+ "",
+ "EV_Hold *",
+ "getsv_mask(int n)",
+ "{ EV_Hold *h;",
+ " static EV_Hold *kept = (EV_Hold *) 0;",
+ "",
+ " for (h = kept; h; h = h->nxt)",
+ " if (n == h->sz",
+ " && (memcmp((char *) Mask, (char *) h->sv, n) == 0)",
+ " && (now._nr_pr == h->nrpr)",
+ " && (now._nr_qs == h->nrqs)",
+ "#if VECTORSZ>32000",
+ " && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0)",
+ " && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0)",
+ "#else",
+ " && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0)",
+ " && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0)",
+ "#endif",
+ " && (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0)",
+ " && (memcmp((char *) q_skip, (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0))",
+ " break;",
+ " if (!h)",
+ " { h = (EV_Hold *) emalloc(sizeof(EV_Hold));",
+ " h->sz = n;",
+ " h->nrpr = now._nr_pr;",
+ " h->nrqs = now._nr_qs;",
+ "",
+ " h->sv = (char *) emalloc(n * sizeof(char));",
+ " memcpy((char *) h->sv, (char *) Mask, n);",
+ "",
+ " if (now._nr_pr > 0)",
+ " { h->ps = (char *) emalloc(now._nr_pr * sizeof(int));",
+ " memcpy((char *) h->ps, (char *) proc_skip, now._nr_pr * sizeof(uchar));",
+ "#if VECTORSZ>32000",
+ " h->po = (char *) emalloc(now._nr_pr * sizeof(int));",
+ " memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int));",
+ "#else",
+ " h->po = (char *) emalloc(now._nr_pr * sizeof(short));",
+ " memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short));",
+ "#endif",
+ " }",
+ " if (now._nr_qs > 0)",
+ " { h->qs = (char *) emalloc(now._nr_qs * sizeof(int));",
+ " memcpy((char *) h->qs, (char *) q_skip, now._nr_qs * sizeof(uchar));",
+ "#if VECTORSZ>32000",
+ " h->qo = (char *) emalloc(now._nr_qs * sizeof(int));",
+ " memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int));",
+ "#else",
+ " h->qo = (char *) emalloc(now._nr_qs * sizeof(short));",
+ " memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short));",
+ "#endif",
+ " }",
+ "",
+ " h->nxt = kept;",
+ " kept = h;",
+ " }",
+ " return h;",
+ "}",
+ "",
+ "void",
+ "freesv(SV_Hold *p)",
+ "{ SV_Hold *h, *oh;",
+ "",
+ " oh = (SV_Hold *) 0;",
+ " for (h = svfree; h; oh = h, h = h->nxt)",
+ " if (h->sz >= p->sz)",
+ " break;",
+ "",
+ " if (!oh)",
+ " { p->nxt = svfree;",
+ " svfree = p;",
+ " } else",
+ " { p->nxt = h;",
+ " oh->nxt = p;",
+ " }",
+ "}",
+ "",
+ "BFS_Trail *",
+ "get_bfs_frame(void)",
+ "{ BFS_Trail *t;",
+ "",
+ " if (bfs_free)",
+ " { t = bfs_free;",
+ " bfs_free = bfs_free->nxt;",
+ " t->nxt = (BFS_Trail *) 0;",
+ " } else",
+ " { t = (BFS_Trail *) emalloc(sizeof(BFS_Trail));",
+ " }",
+ " t->frame = (Trail *) emalloc(sizeof(Trail));", /* always new */
+ " return t;",
+ "}",
+ "",
+ "void",
+ "push_bfs(Trail *f, int d)",
+ "{ BFS_Trail *t;",
+ "",
+ " t = get_bfs_frame();",
+ " memcpy((char *)t->frame, (char *)f, sizeof(Trail));",
+ " t->frame->o_tt = d; /* depth */",
+ "",
+ " t->boq = boq;",
+ " t->onow = getsv(vsize);",
+ " t->omask = getsv_mask(vsize);",
+ "#if defined(FULLSTACK) && defined(Q_PROVISO)",
+ " t->lstate = Lstate;",
+ "#endif",
+ " if (!bfs_bot)",
+ " { bfs_bot = bfs_trail = t;",
+ " } else",
+ " { bfs_bot->nxt = t;",
+ " bfs_bot = t;",
+ " }",
+ "#ifdef CHECK",
+ " printf(\"PUSH %%u (%%d)\\n\", t->frame, d);",
+ "#endif",
+ "}",
+ "",
+ "Trail *",
+ "pop_bfs(void)",
+ "{ BFS_Trail *t;",
+ "",
+ " if (!bfs_trail)",
+ " return (Trail *) 0;",
+ "",
+ " t = bfs_trail;",
+ " bfs_trail = t->nxt;",
+ " if (!bfs_trail)",
+ " bfs_bot = (BFS_Trail *) 0;",
+ "#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE)",
+ " if (t->lstate) t->lstate->tagged = 0;",
+ "#endif",
+ "",
+ " t->nxt = bfs_free;",
+ " bfs_free = t;",
+ "",
+ " vsize = t->onow->sz;",
+ " boq = t->boq;",
+"#ifdef BFS_DISK",
+ " if (t->onow->sv == (State *) 0)",
+ " { char dsk_nm[32];",
+ " bfs_dsk_reads++; /* count */",
+ " if (bfs_dsk_read >= 0 /* file descriptor */",
+ " && bfs_dsk_reads%%BFS_DSK_LIMIT == 0)",
+ " { (void) close(bfs_dsk_read);",
+ " sprintf(dsk_nm, \"pan_bfs_%%d.tmp\", bfs_dsk_seqno_r-1);",
+ " (void) unlink(dsk_nm);",
+ " bfs_dsk_read = -1;",
+ " }",
+ " if (bfs_dsk_read < 0)",
+ " { sprintf(dsk_nm, \"pan_bfs_%%d.tmp\", bfs_dsk_seqno_r++);",
+ " bfs_dsk_read = open(dsk_nm, RFLAGS);",
+ " if (bfs_dsk_read < 0)",
+ " { Uerror(\"could not open temp disk file\");",
+ " } }",
+ " if (read(bfs_dsk_read, (char *) &now, vsize) != vsize)",
+ " { Uerror(\"bad bfs disk file read\");",
+ " }",
+ "#ifndef NOVSZ",
+ " if (now._vsz != vsize)",
+ " { Uerror(\"disk read vsz mismatch\");",
+ " }",
+ "#endif",
+ " } else",
+"#endif",
+ " memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize);",
+ " memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize);",
+
+ " if (now._nr_pr > 0)",
+ "#if VECTORSZ>32000",
+ " { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int));",
+ "#else",
+ " { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short));",
+ "#endif",
+ " memcpy((char *)proc_skip, (char *)t->omask->ps, now._nr_pr * sizeof(uchar));",
+ " }",
+ " if (now._nr_qs > 0)",
+ "#if VECTORSZ>32000",
+ " { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int));",
+ "#else",
+ " { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short));",
+ "#endif",
+ " memcpy((uchar *)q_skip, (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar));",
+ " }",
+
+"#ifdef BFS_DISK",
+ " if (t->onow->sv != (State *) 0)",
+"#endif",
+ " freesv(t->onow); /* omask not freed */",
+ "#ifdef CHECK",
+ " printf(\"POP %%u (%%d)\\n\", t->frame, t->frame->o_tt);",
+ "#endif",
+ " return t->frame;",
+ "}",
+ "",
+ "void",
+ "store_state(Trail *ntrpt, int shortcut, short oboq)",
+ "{",
+ "#ifdef VERI",
+ " Trans *t2 = (Trans *) 0;",
+ " uchar ot; int tt, E_state;",
+ " uchar o_opm = trpt->o_pm, *othis = this;",
+ "",
+ " if (shortcut)",
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"claim: shortcut\\n\");",
+ "#endif",
+ " goto store_it; /* no claim move */",
+ " }",
+ "",
+ " this = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */",
+ " trpt->o_pm = 0;", /* to interpret else in never claim */
+ "",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+ "",
+ "#ifdef HAS_UNLESS",
+ " E_state = 0;",
+ "#endif",
+ " for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0)",
+ " {",
+ "#ifdef HAS_UNLESS",
+ " if (E_state > 0",
+ " && E_state != t2->e_trans)",
+ " break;",
+ "#endif",
+ " if (do_transit(t2, 0))",
+ " {",
+ "#ifdef VERBOSE",
+ " if (!reached[ot][t2->st])",
+ " printf(\"depth: %%d -- claim move from %%d -> %%d\\n\",",
+ " trpt->o_tt, ((P0 *)this)->_p, t2->st);",
+ "#endif",
+ "#ifdef HAS_UNLESS",
+ " E_state = t2->e_trans;",
+ "#endif",
+ " if (t2->st > 0)",
+ " { ((P0 *)this)->_p = t2->st;",
+ " reached[ot][t2->st] = 1;",
+ "#ifndef NOCLAIM",
+ " check_claim(t2->st);",
+ "#endif",
+ " }",
+ " if (now._nr_pr == 0) /* claim terminated */",
+ " uerror(\"end state in claim reached\");",
+ "",
+ "#ifdef PEG",
+ " peg[t2->forw]++;",
+ "#endif",
+ " trpt->o_pm |= 1;",
+ " if (t2->atom&2)", /* atomic in claim */
+ " Uerror(\"atomic in claim not supported in BFS mode\");",
+ "store_it:",
+ "",
+ "#endif", /* VERI */
+ "",
+ "#ifdef BITSTATE",
+ " if (!bstore((char *)&now, vsize))",
+ "#else",
+ "#ifdef MA",
+ " if (!gstore((char *)&now, vsize, 0))",
+ "#else",
+ " if (!hstore((char *)&now, vsize))",
+ "#endif",
+ "#endif",
+ " { static long sdone = (long) 0; long ndone;",
+ " nstates++;",
+ "#ifndef NOREDUCE",
+ " trpt->tau |= 64;", /* succ definitely outside stack */
+ "#endif",
+ " ndone = (unsigned long) (nstates/((double) FREQ));",
+ " if (ndone != sdone && mreached%%10 != 0)",
+ " { snapshot();",
+ " sdone = ndone;",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)",
+ " if (nstates > ((double)(1<<(ssize+1))))",
+ " { void resize_hashtable(void);",
+ " resize_hashtable();",
+ " }",
+ "#endif",
+ " }",
+ "#if SYNC",
+ " if (boq != -1)",
+ " midrv++;",
+ " else if (oboq != -1)",
+ " { Trail *x;",
+ " x = (Trail *) trpt->ostate; /* pre-rv state */",
+ " if (x) x->o_pm |= 4; /* mark success */",
+ " }",
+ "#endif",
+ " push_bfs(ntrpt, trpt->o_tt+1);",
+ " } else",
+ " { truncs++;",
+
+ "#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO)",
+ "#if !defined(BITSTATE)",
+ " if (Lstate && Lstate->tagged) trpt->tau |= 64;",
+ "#else",
+ " if (trpt->tau&32)",
+ " { BFS_Trail *tprov;",
+ " for (tprov = bfs_trail; tprov; tprov = tprov->nxt)",
+ " if (tprov->onow->sv != (State *) 0",
+ " && memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize) == 0)",
+ " { trpt->tau |= 64;",
+ " break; /* state is in queue */",
+ " } }",
+ "#endif",
+ "#endif",
+ " }",
+ "#ifdef VERI",
+ " ((P0 *)this)->_p = tt; /* reset claim */",
+ " if (t2)",
+ " do_reverse(t2, 0, 0);",
+ " else",
+ " break;",
+ " } }",
+ " this = othis;",
+ " trpt->o_pm = o_opm;",
+ "#endif",
+ "}",
+ "",
+ "Trail *ntrpt;", /* 4.2.8 */
+ "",
+ "void",
+ "bfs(void)",
+ "{ Trans *t; Trail *otrpt, *x;",
+ " uchar _n, _m, ot, nps = 0;",
+ " int tt, E_state;",
+ " short II, From = (short) (now._nr_pr-1), To = BASE;",
+ " short oboq = boq;",
+ "",
+ " ntrpt = (Trail *) emalloc(sizeof(Trail));",
+ " trpt->ostate = (struct H_el *) 0;",
+ " trpt->tau = 0;",
+ "",
+ " trpt->o_tt = -1;",
+ " store_state(ntrpt, 0, oboq); /* initial state */",
+ "",
+ " while ((otrpt = pop_bfs())) /* also restores now */",
+ " { memcpy((char *) trpt, (char *) otrpt, sizeof(Trail));",
+ "#if defined(C_States) && (HAS_TRACK==1)",
+ " c_revert((uchar *) &(now.c_state[0]));",
+ "#endif",
+ " if (trpt->o_pm & 4)",
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"Revisit of atomic not needed (%%d)\\n\",",
+ " trpt->o_pm);", /* at least 1 rv succeeded */
+ "#endif",
+ " continue;",
+ " }",
+ "#ifndef NOREDUCE",
+ " nps = 0;",
+ "#endif",
+ " if (trpt->o_pm == 8)",
+ " { revrv++;",
+ " if (trpt->tau&8)",
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"Break atomic (pm:%%d,tau:%%d)\\n\",",
+ " trpt->o_pm, trpt->tau);",
+ "#endif",
+ " trpt->tau &= ~8;",
+ " }",
+ "#ifndef NOREDUCE",
+ " else if (trpt->tau&32)", /* was a preselected move */
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"Void preselection (pm:%%d,tau:%%d)\\n\",",
+ " trpt->o_pm, trpt->tau);",
+ "#endif",
+ " trpt->tau &= ~32;",
+ " nps = 1; /* no preselection in repeat */",
+ " }",
+ "#endif",
+ " }",
+ " trpt->o_pm &= ~(4|8);",
+ " if (trpt->o_tt > mreached)",
+ " { mreached = trpt->o_tt;",
+ " if (mreached%%10 == 0)",
+ " { snapshot();",
+ " } }",
+ " depth = trpt->o_tt;",
+
+ " if (depth >= maxdepth)",
+ " {",
+ "#if SYNC",
+ " Trail *x;",
+ " if (boq != -1)",
+ " { x = (Trail *) trpt->ostate;",
+ " if (x) x->o_pm |= 4; /* not failing */",
+ " }",
+ "#endif",
+ " truncs++;",
+ " if (!warned)",
+ " { warned = 1;",
+ " printf(\"error: max search depth too small\\n\");",
+ " }",
+ " if (bounded)",
+ " uerror(\"depth limit reached\");",
+ " continue;",
+ " }",
+
+/* PO */
+ "#ifndef NOREDUCE",
+ " if (boq == -1 && !(trpt->tau&8) && nps == 0)",
+ " for (II = now._nr_pr-1; II >= BASE; II -= 1)",
+ " {",
+ "Pickup: this = pptr(II);",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+ " if (trans[ot][tt]->atom & 8)", /* safe */
+ " { t = trans[ot][tt];",
+ " if (t->qu[0] != 0)",
+ " { Ccheck++;",
+ " if (!q_cond(II, t))",
+ " continue;",
+ " Cholds++;",
+ " }",
+ " From = To = II;",
+ " trpt->tau |= 32; /* preselect marker */",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: proc %%d PreSelected (tau=%%d)\\n\", ",
+ " depth, II, trpt->tau);",
+ "#endif",
+ " goto MainLoop;",
+ " } }",
+ " trpt->tau &= ~32;", /* not preselected */
+ "#endif",
+/* PO */
+ "Repeat:",
+ " if (trpt->tau&8) /* atomic */",
+ " { From = To = (short ) trpt->pr;",
+ " nlinks++;",
+ " } else",
+ " { From = now._nr_pr-1;",
+ " To = BASE;",
+ " }",
+ "MainLoop:",
+ " _n = _m = 0;",
+ " for (II = From; II >= To; II -= 1)",
+ " {",
+ " this = (((uchar *)&now)+proc_offset[II]);",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+ "#if SYNC",
+ " /* no rendezvous with same proc */",
+ " if (boq != -1 && trpt->pr == II) continue;",
+ "#endif",
+ " ntrpt->pr = (uchar) II;",
+ " ntrpt->st = tt; ",
+ " trpt->o_pm &= ~1; /* no move yet */",
+ "#ifdef EVENT_TRACE",
+ " trpt->o_event = now._event;",
+ "#endif",
+ "#ifdef HAS_PROVIDED",
+ " if (!provided(II, ot, tt, t)) continue;",
+ "#endif",
+ "#ifdef HAS_UNLESS",
+ " E_state = 0;",
+ "#endif",
+ " for (t = trans[ot][tt]; t; t = t->nxt)",
+ " {",
+ "#ifdef HAS_UNLESS",
+ " if (E_state > 0",
+ " && E_state != t->e_trans)",
+ " break;",
+ "#endif",
+ " ntrpt->o_t = t;",
+ "",
+ " oboq = boq;",
+ "",
+ " if (!(_m = do_transit(t, II)))",
+ " continue;",
+ "",
+ " trpt->o_pm |= 1; /* we moved */",
+ " (trpt+1)->o_m = _m; /* for unsend */",
+ "#ifdef PEG",
+ " peg[t->forw]++;",
+ "#endif",
+ "#ifdef CHECK",
+ " printf(\"%%3d: proc %%d exec %%d, \",",
+ " depth, II, t->forw);",
+ " printf(\"%%d to %%d, %%s %%s %%s\",",
+ " tt, t->st, t->tp,",
+ " (t->atom&2)?\"atomic\":\"\",",
+ " (boq != -1)?\"rendez-vous\":\"\");",
+ "#ifdef HAS_UNLESS",
+ " if (t->e_trans)",
+ " printf(\" (escapes to state %%d)\", t->st);",
+ "#endif",
+ " printf(\" %%saccepting [tau=%%d]\\n\",",
+ " (trpt->o_pm&2)?\"\":\"non-\", trpt->tau);",
+ "#endif",
+ "#ifdef HAS_UNLESS",
+ " E_state = t->e_trans;",
+ "#if SYNC>0",
+ " if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */))",
+ " { fprintf(efd, \"error:\tthe use of rendezvous stmnt in the escape clause\\n\");",
+ " fprintf(efd, \"\tof an unless stmnt is not compatible with -DBFS\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#endif",
+ "#endif",
+ " if (t->st > 0) ((P0 *)this)->_p = t->st;",
+ "",
+ " /* ptr to pred: */ ntrpt->ostate = (struct H_el *) otrpt;",
+ " ntrpt->st = tt;",
+ " if (boq == -1 && (t->atom&2)) /* atomic */",
+ " ntrpt->tau = 8; /* record for next move */",
+ " else",
+ " ntrpt->tau = 0;",
+ "",
+ " store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq);",
+ "#ifdef EVENT_TRACE",
+ " now._event = trpt->o_event;",
+ "#endif",
+ "",
+ " /* undo move and continue */",
+ " trpt++; /* this is where ovals and ipt are set */",
+ " do_reverse(t, II, _m); /* restore now. */",
+ " trpt--;",
+ "#ifdef CHECK",
+ " #if NCORE>1",
+ " enter_critical(GLOBAL_LOCK); /* in verbose mode only */",
+ " printf(\"cpu%%d: \", core_id);",
+ " #endif",
+
+ " printf(\"%%3d: proc %%d \", depth, II);",
+ " printf(\"reverses %%d, %%d to %%d,\",",
+ " t->forw, tt, t->st);",
+ " printf(\" %%s [abit=%%d,adepth=%%d,\",",
+ " t->tp, now._a_t, A_depth);",
+ " printf(\"tau=%%d,%%d]\\n\",",
+ " trpt->tau, (trpt-1)->tau);",
+ " #if NCORE>1",
+ " leave_critical(GLOBAL_LOCK);",
+ " #endif",
+ "#endif",
+ " reached[ot][t->st] = 1;",
+ " reached[ot][tt] = 1;",
+ "",
+ " ((P0 *)this)->_p = tt;",
+ " _n |= _m;",
+ " } }",
+/* PO */
+ "#ifndef NOREDUCE",
+ " /* preselected - no succ definitely outside stack */",
+ " if ((trpt->tau&32) && !(trpt->tau&64))",
+ " { From = now._nr_pr-1; To = BASE;",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ",
+ " depth, II+1, (int) _n, trpt->tau);",
+ "#endif",
+ " _n = 0; trpt->tau &= ~32;",
+ " if (II >= BASE)",
+ " goto Pickup;",
+ " goto MainLoop;",
+ " }",
+ " trpt->tau &= ~(32|64);",
+ "#endif",
+/* PO */
+ " if (_n != 0)",
+ " continue;",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: no move [II=%%d, tau=%%d, boq=%%d, _nr_pr=%%d]\\n\",",
+ " depth, II, trpt->tau, boq, now._nr_pr);",
+ "#endif",
+ " if (boq != -1)",
+ " { failedrv++;",
+ " x = (Trail *) trpt->ostate; /* pre-rv state */",
+ " if (!x) continue; /* root state */",
+ " if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */",
+ " { x->o_pm |= 8; /* mark failure */",
+ " this = (((uchar *)&now)+proc_offset[otrpt->pr]);",
+ "#ifdef VERBOSE",
+ " printf(\"\\treset state of %%d from %%d to %%d\\n\",",
+ " otrpt->pr, ((P0 *)this)->_p, otrpt->st);",
+ "#endif",
+ " ((P0 *)this)->_p = otrpt->st;",
+ " unsend(boq); /* retract rv offer */",
+ " boq = -1;",
+
+ " push_bfs(x, x->o_tt);",
+ "#ifdef VERBOSE",
+ " printf(\"failed rv, repush with %%d\\n\", x->o_pm);",
+ "#endif",
+ " }",
+ "#ifdef VERBOSE",
+ " else printf(\"failed rv, tau at parent: %%d\\n\", x->tau);",
+ "#endif",
+ " } else if (now._nr_pr > 0)",
+ " {",
+ " if ((trpt->tau&8)) /* atomic */",
+ " { trpt->tau &= ~(1|8); /* 1=timeout, 8=atomic */",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: atomic step proc %%d blocks\\n\",",
+ " depth, II+1);",
+ "#endif",
+ " goto Repeat;",
+ " }",
+ "",
+ " if (!(trpt->tau&1)) /* didn't try timeout yet */",
+ " { trpt->tau |= 1;",
+ "#ifdef DEBUG",
+ " printf(\"%%d: timeout\\n\", depth);",
+ "#endif",
+ " goto MainLoop;",
+ " }",
+ "#ifndef VERI",
+ " if (!noends && !a_cycles && !endstate())",
+ " uerror(\"invalid end state\");",
+ "#endif",
+ " } }",
+ "}",
+ "",
+ "void",
+ "putter(Trail *trpt, int fd)",
+ "{ long j;",
+ "",
+ " if (!trpt) return;",
+ "",
+ " if (trpt != (Trail *) trpt->ostate)",
+ " putter((Trail *) trpt->ostate, fd);",
+ "",
+ " if (trpt->o_t)",
+ " { sprintf(snap, \"%%d:%%d:%%d\\n\",",
+ " trcnt++, trpt->pr, trpt->o_t->t_id);",
+ " j = strlen(snap);",
+ " if (write(fd, snap, j) != j)",
+ " { printf(\"pan: error writing %%s\\n\", fnm);",
+ " pan_exit(1);",
+ " } }",
+ "}",
+ "",
+ "void",
+ "nuerror(char *str)",
+ "{ int fd = make_trail();",
+ " int j;",
+ "",
+ " if (fd < 0) return;",
+ "#ifdef VERI",
+ " sprintf(snap, \"-2:%%d:-2\\n\", VERI);",
+ " write(fd, snap, strlen(snap));",
+ "#endif",
+ "#ifdef MERGED",
+ " sprintf(snap, \"-4:-4:-4\\n\");",
+ " write(fd, snap, strlen(snap));",
+ "#endif",
+ " trcnt = 1;",
+ " putter(trpt, fd);",
+ " if (ntrpt->o_t)", /* 4.2.8 -- Alex example, missing last transition */
+ " { sprintf(snap, \"%%d:%%d:%%d\\n\",",
+ " trcnt++, ntrpt->pr, ntrpt->o_t->t_id);",
+ " j = strlen(snap);",
+ " if (write(fd, snap, j) != j)",
+ " { printf(\"pan: error writing %%s\\n\", fnm);",
+ " pan_exit(1);",
+ " } }",
+ " close(fd);",
+ " if (errors >= upto && upto != 0)",
+ " { wrapup();",
+ " }",
+ "}",
+ "#endif", /* BFS */
+ 0,
+};
+
+static char *Code2d[] = {
+ "clock_t start_time;",
+ "#if NCORE>1",
+ "clock_t crash_stamp;",
+ "#endif",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ "struct tms start_tm;",
+ "#endif",
+ "",
+ "void",
+ "start_timer(void)",
+ "{",
+ "#if defined(WIN32) || defined(WIN64)",
+ " start_time = clock();",
+ "#else",
+ " start_time = times(&start_tm);",
+ "#endif",
+ "}",
+ "",
+ "void",
+ "stop_timer(void)",
+ "{ clock_t stop_time;",
+ " double delta_time;",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ " struct tms stop_tm;",
+ " stop_time = times(&stop_tm);",
+ " delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));",
+ "#else",
+ " stop_time = clock();",
+ " delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);",
+ "#endif",
+ " if (readtrail || delta_time < 0.00) return;",
+ "#if NCORE>1",
+ " if (core_id == 0 && nstates > (double) 0)",
+ " { printf(\"\\ncpu%%d: elapsed time %%.3g seconds (%%g states visited)\\n\", core_id, delta_time, nstates);",
+ " if (delta_time > 0.01)",
+ " { printf(\"cpu%%d: rate %%g states/second\\n\", core_id, nstates/delta_time);",
+ " }",
+ " { void check_overkill(void);",
+ " check_overkill();",
+ " } }",
+ "#else",
+ " printf(\"\\npan: elapsed time %%.3g seconds\\n\", delta_time);",
+ " if (delta_time > 0.01)",
+ " { printf(\"pan: rate %%9.8g states/second\\n\", nstates/delta_time);",
+ " if (verbose)",
+ " { printf(\"pan: avg transition delay %%.5g usec\\n\",",
+ " delta_time/(nstates+truncs));",
+ " } }",
+ "#endif",
+ "}",
+ "",
+ "#if NCORE>1",
+ "#ifdef T_ALERT",
+ "double t_alerts[17];",
+ "",
+ "void",
+ "crash_report(void)",
+ "{ int i;",
+ " printf(\"crash alert intervals:\\n\");",
+ " for (i = 0; i < 17; i++)",
+ " { printf(\"%%d\\t%%g\\n\", i, t_alerts[i]);",
+ "} }",
+ "#endif",
+ "",
+ "void",
+ "crash_reset(void)",
+ "{ /* false alarm */",
+ " if (crash_stamp != (clock_t) 0)",
+ " {",
+ "#ifdef T_ALERT",
+ " double delta_time;",
+ " int i;",
+ "#if defined(WIN32) || defined(WIN64)",
+ " delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);",
+ "#else",
+ " delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));",
+ "#endif",
+ " for (i = 0; i < 16; i++)",
+ " { if (delta_time <= (i*30))",
+ " { t_alerts[i] = delta_time;",
+ " break;",
+ " } }",
+ " if (i == 16) t_alerts[i] = delta_time;",
+ "#endif",
+ " if (verbose)",
+ " printf(\"cpu%%d: crash alert off\\n\", core_id);",
+ " }",
+ " crash_stamp = (clock_t) 0;",
+ "}",
+ "",
+ "int",
+ "crash_test(double maxtime)",
+ "{ double delta_time;",
+ " if (crash_stamp == (clock_t) 0)",
+ " { /* start timing */",
+ "#if defined(WIN32) || defined(WIN64)",
+ " crash_stamp = clock();",
+ "#else",
+ " crash_stamp = times(&start_tm);",
+ "#endif",
+ " if (verbose)",
+ " { printf(\"cpu%%d: crash detection\\n\", core_id);",
+ " }",
+ " return 0;",
+ " }",
+ "#if defined(WIN32) || defined(WIN64)",
+ " delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);",
+ "#else",
+ " delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));",
+ "#endif",
+ " return (delta_time >= maxtime);",
+ "}",
+ "#endif",
+ "",
+ "void",
+ "do_the_search(void)",
+ "{ int i;",
+ " depth = mreached = 0;",
+ " trpt = &trail[0];",
+ "#ifdef VERI",
+ " trpt->tau |= 4; /* the claim moves first */",
+ "#endif",
+ " for (i = 0; i < (int) now._nr_pr; i++)",
+ " { P0 *ptr = (P0 *) pptr(i);",
+ "#ifndef NP",
+ " if (!(trpt->o_pm&2)",
+ " && accpstate[ptr->_t][ptr->_p])",
+ " { trpt->o_pm |= 2;",
+ " }",
+ "#else",
+ " if (!(trpt->o_pm&4)",
+ " && progstate[ptr->_t][ptr->_p])",
+ " { trpt->o_pm |= 4;",
+ " }",
+ "#endif",
+ " }",
+ "#ifdef EVENT_TRACE",
+ "#ifndef NP",
+ " if (accpstate[EVENT_TRACE][now._event])",
+ " { trpt->o_pm |= 2;",
+ " }",
+ "#else",
+ " if (progstate[EVENT_TRACE][now._event])",
+ " { trpt->o_pm |= 4;",
+ " }",
+ "#endif",
+ "#endif",
+ "#ifndef NOCOMP",
+ " Mask[0] = Mask[1] = 1; /* _nr_pr, _nr_qs */",
+ " if (!a_cycles)",
+ " { i = &(now._a_t) - (uchar *) &now;",
+ " Mask[i] = 1; /* _a_t */",
+ " }",
+ "#ifndef NOFAIR",
+ " if (!fairness)",
+ " { int j = 0;",
+ " i = &(now._cnt[0]) - (uchar *) &now;",
+ " while (j++ < NFAIR)",
+ " Mask[i++] = 1; /* _cnt[] */",
+ " }",
+ "#endif",
+ "#endif",
+ "#ifndef NOFAIR",
+ " if (fairness",
+ " && (a_cycles && (trpt->o_pm&2)))",
+ " { now._a_t = 2; /* set the A-bit */",
+ " now._cnt[0] = now._nr_pr + 1;", /* NEW: +1 */
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: fairness Rule 1, cnt=%%d, _a_t=%%d\\n\",",
+ " depth, now._cnt[now._a_t&1], now._a_t);",
+ "#endif",
+ " }",
+ "#endif",
+
+ " c_stack_start = (char *) &i; /* meant to be read-only */",
+
+ "#if defined(HAS_CODE) && defined (C_INIT)",
+ " C_INIT; /* initialization of data that must precede fork() */",
+ " c_init_done++;",
+ "#endif",
+
+ "#if defined(C_States) && (HAS_TRACK==1)",
+ " /* capture initial state of tracked C objects */",
+ " c_update((uchar *) &(now.c_state[0]));",
+ "#endif",
+
+ "#ifdef HAS_CODE",
+ " if (readtrail) getrail(); /* no return */",
+ "#endif",
+ " start_timer();",
+ "#ifdef BFS",
+ " bfs();",
+ "#else",
+ "#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)",
+ " /* initial state of tracked & unmatched objects */",
+ " c_stack((uchar *) &(svtack->c_stack[0]));",
+ "#endif",
+ "#ifdef RANDOMIZE",
+ " #if RANDOMIZE>0",
+ " srand(RANDOMIZE);",
+ " #else",
+ " srand(123);",
+ " #endif",
+ "#endif",
+ "#if NCORE>1",
+ " mem_get();",
+ "#else",
+ " new_state(); /* start 1st DFS */",
+ "#endif",
+ "#endif",
+ "}",
+
+ "#ifdef INLINE_REV",
+ "uchar",
+ "do_reverse(Trans *t, short II, uchar M)",
+ "{ uchar _m = M;",
+ " int tt = (int) ((P0 *)this)->_p;",
+ "#include REVERSE_MOVES",
+ "R999: return _m;",
+ "}",
+ "#endif",
+
+ "#ifndef INLINE",
+ "#ifdef EVENT_TRACE",
+ "static char _tp = 'n'; static int _qid = 0;",
+ "#endif",
+ "uchar",
+ "do_transit(Trans *t, short II)",
+ "{ uchar _m = 0;",
+ " int tt = (int) ((P0 *)this)->_p;",
+ "#ifdef M_LOSS",
+ " uchar delta_m = 0;",
+ "#endif",
+ "#ifdef EVENT_TRACE",
+ " short oboq = boq;",
+ " uchar ot = (uchar) ((P0 *)this)->_t;",
+ " if (ot == EVENT_TRACE) boq = -1;",
+ "#define continue { boq = oboq; return 0; }",
+ "#else",
+ "#define continue return 0",
+ "#ifdef SEPARATE",
+ " uchar ot = (uchar) ((P0 *)this)->_t;",
+ "#endif",
+ "#endif",
+ "#include FORWARD_MOVES",
+ "P999:",
+ "#ifdef EVENT_TRACE",
+ " if (ot == EVENT_TRACE) boq = oboq;",
+ "#endif",
+ " return _m;",
+ "#undef continue",
+ "}",
+
+ "#ifdef EVENT_TRACE",
+ "void",
+ "require(char tp, int qid)",
+ "{ Trans *t;",
+ " _tp = tp; _qid = qid;",
+ "",
+ " if (now._event != endevent)",
+ " for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt)",
+ " { if (do_transit(t, EVENT_TRACE))",
+ " { now._event = t->st;",
+ " reached[EVENT_TRACE][t->st] = 1;",
+ "#ifdef VERBOSE",
+ " printf(\" event_trace move to -> %%d\\n\", t->st);",
+ "#endif",
+ "#ifndef BFS",
+ "#ifndef NP",
+ " if (accpstate[EVENT_TRACE][now._event])",
+ " (trpt+1)->o_pm |= 2;",
+ "#else",
+ " if (progstate[EVENT_TRACE][now._event])",
+ " (trpt+1)->o_pm |= 4;",
+ "#endif",
+ "#endif",
+ "#ifdef NEGATED_TRACE",
+ " if (now._event == endevent)",
+ " {",
+ "#ifndef BFS",
+ " depth++; trpt++;",
+ "#endif",
+ " uerror(\"event_trace error (all events matched)\");",
+ "#ifndef BFS",
+ " trpt--; depth--;",
+ "#endif",
+ " break;",
+ " }",
+ "#endif",
+ " for (t = t->nxt; t; t = t->nxt)",
+ " { if (do_transit(t, EVENT_TRACE))",
+ " Uerror(\"non-determinism in event-trace\");",
+ " }",
+ " return;",
+ " }",
+ "#ifdef VERBOSE",
+ " else",
+ " printf(\" event_trace miss '%%c' -- %%d, %%d, %%d\\n\",",
+ " tp, qid, now._event, t->forw);",
+ "#endif",
+ " }",
+ "#ifdef NEGATED_TRACE",
+ " now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */",
+ "#else",
+ "#ifndef BFS",
+ " depth++; trpt++;",
+ "#endif",
+ " uerror(\"event_trace error (no matching event)\");",
+ "#ifndef BFS",
+ " trpt--; depth--;",
+ "#endif",
+ "#endif",
+ "}",
+ "#endif",
+
+ "int",
+ "enabled(int iam, int pid)",
+ "{ Trans *t; uchar *othis = this;",
+ " int res = 0; int tt; uchar ot;",
+ "#ifdef VERI",
+ " /* if (pid > 0) */ pid++;",
+ "#endif",
+ " if (pid == iam)",
+ " Uerror(\"used: enabled(pid=thisproc)\");",
+ " if (pid < 0 || pid >= (int) now._nr_pr)",
+ " return 0;",
+ " this = pptr(pid);",
+ " TstOnly = 1;",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+ " for (t = trans[ot][tt]; t; t = t->nxt)",
+ " if (do_transit(t, (short) pid))",
+ " { res = 1;",
+ " break;",
+ " }",
+ " TstOnly = 0;",
+ " this = othis;",
+ " return res;",
+ "}",
+ "#endif",
+ "void",
+ "snap_time(void)",
+ "{ clock_t stop_time;",
+ " double delta_time;",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ " struct tms stop_tm;",
+ " stop_time = times(&stop_tm);",
+ " delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));",
+ "#else",
+ " stop_time = clock();",
+ " delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);",
+ "#endif",
+ " if (delta_time > 0.01)",
+ " { printf(\"t= %%6.3g \", delta_time);",
+ " printf(\"R= %%7.0g\", nstates/delta_time);",
+ " }",
+ " printf(\"\\n\");",
+ " if (quota > 0.1 && delta_time > quota)",
+ " { printf(\"Time limit of %%6.3g minutes exceeded\\n\", quota/60.0);",
+ "#if NCORE>1",
+ " fflush(stdout);",
+ " leave_critical(GLOBAL_LOCK);",
+ " sudden_stop(\"time-limit\");",
+ " exit(1);",
+ "#endif",
+ " wrapup();",
+ " }",
+ "}",
+ "void",
+ "snapshot(void)",
+ "{",
+ "#if NCORE>1",
+ " enter_critical(GLOBAL_LOCK); /* snapshot */",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"Depth= %%7ld States= %%8.3g \",",
+ "#if NCORE>1",
+ " (long) (nr_handoffs * z_handoff) +",
+ "#endif",
+ " mreached, nstates);",
+ " printf(\"Transitions= %%8.3g \", nstates+truncs);",
+ "#ifdef MA",
+ " printf(\"Nodes= %%7d \", nr_states);",
+ "#endif",
+ " printf(\"Memory= %%9.3f\\t\", memcnt/1048576.);",
+ " snap_time();",
+ " fflush(stdout);",
+ "#if NCORE>1",
+ " leave_critical(GLOBAL_LOCK);",
+ "#endif",
+ "}",
+
+ "#ifdef SC",
+ "void",
+ "stack2disk(void)",
+ "{",
+ " if (!stackwrite",
+ " && (stackwrite = creat(stackfile, TMODE)) < 0)",
+ " Uerror(\"cannot create stackfile\");",
+ "",
+ " if (write(stackwrite, trail, DDD*sizeof(Trail))",
+ " != DDD*sizeof(Trail))",
+ " Uerror(\"stackfile write error -- disk is full?\");",
+ "",
+ " memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail));",
+ " memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail));",
+ " CNT1++;",
+ "}",
+ "void",
+ "disk2stack(void)",
+ "{ long have;",
+ "",
+ " CNT2++;",
+ " memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail));",
+ "",
+ " if (!stackwrite",
+ " || lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1)",
+ " Uerror(\"disk2stack lseek error\");",
+ "",
+ " if (!stackread",
+ " && (stackread = open(stackfile, 0)) < 0)",
+ " Uerror(\"cannot open stackfile\");",
+ "",
+ " if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1)",
+ " Uerror(\"disk2stack lseek error\");",
+ "",
+ " have = read(stackread, trail, DDD*sizeof(Trail));",
+ " if (have != DDD*sizeof(Trail))",
+ " Uerror(\"stackfile read error\");",
+ "}",
+ "#endif",
+
+ "uchar *",
+ "Pptr(int x)", /* as a fct, to avoid a problem with the p9 compiler */
+ "{ if (x < 0 || x >= MAXPROC || !proc_offset[x])", /* does not exist */
+ " return noptr;",
+ " else",
+ " return (uchar *) pptr(x);",
+ "}",
+ "int qs_empty(void);",
+
+ "/*",
+ " * new_state() is the main DFS search routine in the verifier",
+ " * it has a lot of code ifdef-ed together to support",
+ " * different search modes, which makes it quite unreadable.",
+ " * if you are studying the code, first use the C preprocessor",
+ " * to generate a specific version from the pan.c source,",
+ " * e.g. by saying:",
+ " * gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c",
+ " * and then study the resulting file, rather than this one",
+ " */",
+ "#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA))",
+ "",
+ "#ifdef NSUCC",
+ "int N_succ[512];",
+ "void",
+ "tally_succ(int cnt)",
+ "{ if (cnt < 512) N_succ[cnt]++;",
+ " else printf(\"tally_succ: cnt %%d exceeds range\\n\", cnt);",
+ "}",
+ "",
+ "void",
+ "dump_succ(void)",
+ "{ int i; double sum = 0.0;",
+ " double w_avg = 0.0;",
+ " printf(\"Successor counts:\\n\");",
+ " for (i = 0; i < 512; i++)",
+ " { sum += (double) N_succ[i];",
+ " }",
+ " for (i = 0; i < 512; i++)",
+ " { if (N_succ[i] > 0)",
+ " { printf(\"%%3d\t%%10d\t(%%.4g %%%% of total)\\n\",",
+ " i, N_succ[i], (100.0 * (double) N_succ[i])/sum);",
+ " w_avg += (double) i * (double) N_succ[i];",
+ " } }",
+ " if (sum > N_succ[0])",
+ " printf(\"mean %%.4g (without 0: %%.4g)\\n\", w_avg / sum, w_avg / (sum - (double) N_succ[0]));",
+ "}",
+ "#endif",
+ "",
+ "void",
+ "new_state(void)",
+ "{ Trans *t;",
+ " uchar _n, _m, ot;",
+ "#ifdef RANDOMIZE",
+ " short ooi, eoi;",
+ "#endif",
+ "#ifdef M_LOSS",
+ " uchar delta_m = 0;",
+ "#endif",
+ " short II, JJ = 0, kk;",
+ " int tt;",
+"#ifdef REVERSE",
+ " short From = BASE, To = now._nr_pr-1;",
+"#else",
+ " short From = now._nr_pr-1, To = BASE;",
+"#endif",
+ "Down:",
+ "#ifdef CHECK",
+ " cpu_printf(\"%%d: Down - %%s %%saccepting [pids %%d-%%d]\\n\",",
+ " depth, (trpt->tau&4)?\"claim\":\"program\",",
+ " (trpt->o_pm&2)?\"\":\"non-\", From, To);",
+ "#endif",
+ "#ifdef SCHED",
+ " if (depth > 0)",
+ " { trpt->sched_limit = (trpt-1)->sched_limit;",
+ " } else",
+ " { trpt->sched_limit = 0;",
+ " }",
+ "#endif",
+
+ "#ifdef SC",
+ " if (depth > hiwater)",
+ " { stack2disk();",
+ " maxdepth += DDD;",
+ " hiwater += DDD;",
+ " trpt -= DDD;",
+ " if(verbose)",
+ " printf(\"zap %%d: %%d (maxdepth now %%d)\\n\",",
+ " CNT1, hiwater, maxdepth);",
+ " }",
+ "#endif",
+
+ " trpt->tau &= ~(16|32|64); /* make sure these are off */",
+ "#if defined(FULLSTACK) && defined(MA)",
+ " trpt->proviso = 0;",
+ "#endif",
+ "#ifdef NSUCC",
+ " trpt->n_succ = 0;",
+ "#endif",
+ "#if NCORE>1",
+ " if (mem_hand_off())",
+ " {",
+ "#if SYNC",
+ " (trpt+1)->o_n = 1; /* not a deadlock: as below */",
+ "#endif",
+ "#ifndef LOOPSTATE",
+ " (trpt-1)->tau |= 16; /* worstcase guess: as below */",
+ "#endif",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ "#endif",
+
+ " if (depth >= maxdepth)",
+ " { if (!warned)",
+ " { warned = 1;",
+ " printf(\"error: max search depth too small\\n\");",
+ " }",
+ " if (bounded)",
+ " { uerror(\"depth limit reached\");",
+ " }",
+ " truncs++;",
+ "#if SYNC",
+ " (trpt+1)->o_n = 1; /* not a deadlock */",
+ "#endif",
+ "#ifndef LOOPSTATE",
+ " (trpt-1)->tau |= 16; /* worstcase guess */",
+ "#endif",
+
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ "AllOver:",
+ "#if (defined(FULLSTACK) && !defined(MA)) || NCORE>1",
+ " /* if atomic or rv move, carry forward previous state */",
+ " trpt->ostate = (trpt-1)->ostate;", /* was: = (struct H_el *) 0;*/
+ "#endif",
+ "#ifdef VERI",
+ " if ((trpt->tau&4) || ((trpt-1)->tau&128))",
+ "#endif",
+ " if (boq == -1) { /* if not mid-rv */",
+ "#ifndef SAFETY",
+ " /* this check should now be redundant",
+ " * because the seed state also appears",
+ " * on the 1st dfs stack and would be",
+ " * matched in hstore below",
+ " */",
+ " if ((now._a_t&1) && depth > A_depth)",
+ " { if (!memcmp((char *)&A_Root, ",
+ " (char *)&now, vsize))",
+ " {",
+ " depthfound = A_depth;",
+ "#ifdef CHECK",
+ " printf(\"matches seed\\n\");",
+ "#endif",
+ "#ifdef NP",
+ " uerror(\"non-progress cycle\");",
+ "#else",
+ " uerror(\"acceptance cycle\");",
+ "#endif",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ "#ifdef CHECK",
+ " printf(\"not seed\\n\");",
+ "#endif",
+ " }",
+ "#endif",
+ " if (!(trpt->tau&8)) /* if no atomic move */",
+ " {",
+ "#ifdef BITSTATE",
+ "#ifdef CNTRSTACK", /* -> bitstate, reduced, safety */
+ " II = bstore((char *)&now, vsize);",
+ " trpt->j6 = j1; trpt->j7 = j2;",
+ " JJ = LL[j1] && LL[j2];",
+ "#else",
+ "#ifdef FULLSTACK",
+ " JJ = onstack_now();", /* sets j1 */
+ "#else",
+ "#ifndef NOREDUCE",
+ " JJ = II; /* worstcase guess for p.o. */",
+ "#endif",
+ "#endif",
+ " II = bstore((char *)&now, vsize);", /* sets j1-j4 */
+ "#endif",
+ "#else",
+ "#ifdef MA",
+ " II = gstore((char *)&now, vsize, 0);",
+ "#ifndef FULLSTACK",
+ " JJ = II;",
+ "#else",
+ " JJ = (II == 2)?1:0;",
+ "#endif",
+ "#else",
+ " II = hstore((char *)&now, vsize);",
+ "#ifdef FULLSTACK",
+ " JJ = (II == 2)?1:0;",
+ "#endif",
+ "#endif",
+ "#endif",
+ " kk = (II == 1 || II == 2);",
+
+ /* II==0 new state */
+ /* II==1 old state */
+ /* II==2 on current dfs stack */
+ /* II==3 on 1st dfs stack */
+ "#ifndef SAFETY",
+
+ "#if NCORE==1 || defined (SEP_STATE)", /* or else we don't know which stack its on */
+ " if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2)))",
+ " #ifndef NOFAIR",
+ "#if 0",
+ " if (!fairness || ((now._a_t&1) && now._cnt[1] == 1)) /* 5.1.4 */",
+ "#else",
+ " if (a_cycles && !fairness) /* 5.1.6 -- example by Hirofumi Watanabe */",
+ "#endif",
+ " #endif",
+ " {",
+ " II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */",
+ "#ifdef VERBOSE",
+ " printf(\"state match on dfs stack\\n\");",
+ "#endif",
+ " goto same_case;",
+ " }",
+ "#endif",
+
+ "#if defined(FULLSTACK) && defined(BITSTATE)",
+ " if (!JJ && (now._a_t&1) && depth > A_depth)",
+ " { int oj1 = j1;",
+ " uchar o_a_t = now._a_t;",
+ " now._a_t &= ~(1|16|32);", /* 1st stack */
+ " if (onstack_now())", /* changes j1 */
+ " { II = 3;",
+ "#ifdef VERBOSE",
+ " printf(\"state match on 1st dfs stack\\n\");",
+ "#endif",
+ " }",
+ " now._a_t = o_a_t;", /* restore */
+ " j1 = oj1;",
+ " }",
+ "#endif",
+ " if (II == 3 && a_cycles && (now._a_t&1))",
+ " {",
+ "#ifndef NOFAIR",
+ " if (fairness && now._cnt[1] > 1) /* was != 0 */",
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"\tfairness count non-zero\\n\");",
+ "#endif",
+ " II = 0;", /* treat as new state */
+ " } else",
+ "#endif",
+ " {",
+ "#ifndef BITSTATE",
+ " nShadow--;",
+ "#endif",
+ "same_case: if (Lstate) depthfound = Lstate->D;",
+ "#ifdef NP",
+ " uerror(\"non-progress cycle\");",
+ "#else",
+ " uerror(\"acceptance cycle\");",
+ "#endif",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ " }",
+ "#endif",
+
+ "#ifndef NOREDUCE",
+ "#ifndef SAFETY",
+ "#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)",
+ " if (II != 0 && (!Lstate || Lstate->cpu_id < core_id))",
+ " { (trpt-1)->tau |= 16;", /* treat as a stack state */
+ " }",
+ "#endif",
+ " if ((II && JJ) || (II == 3))",
+ " { /* marker for liveness proviso */",
+ "#ifndef LOOPSTATE",
+ " (trpt-1)->tau |= 16;", /* truncated on stack */
+ "#endif",
+ " truncs2++;",
+ " }",
+ "#else",
+ "#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)",
+ " if (!(II != 0 && (!Lstate || Lstate->cpu_id < core_id)))",
+ " { /* treat as stack state */",
+ " (trpt-1)->tau |= 16;",
+ " } else",
+ " { /* treat as non-stack state */",
+ " (trpt-1)->tau |= 64;",
+ " }",
+ "#endif",
+ " if (!II || !JJ)",
+ " { /* successor outside stack */",
+ " (trpt-1)->tau |= 64;",
+ " }",
+ "#endif",
+ "#endif",
+ " if (II)",
+ " { truncs++;",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " if (depth == 0)",
+ " { return;",
+ " } }",
+ "#endif",
+ " goto Up;",
+ " }",
+ " if (!kk)",
+ " { static long sdone = (long) 0; long ndone;",
+ " nstates++;",
+ "#if defined(ZAPH) && defined(BITSTATE)",
+ " zstates += (double) hfns;",
+ "#endif",
+ " ndone = (unsigned long) (nstates/((double) FREQ));",
+ " if (ndone != sdone)",
+ " { snapshot();",
+ " sdone = ndone;",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)",
+ " if (nstates > ((double)(ONE_L<<(ssize+1))))",
+ " { void resize_hashtable(void);",
+ " resize_hashtable();",
+ " }",
+ "#endif",
+ "#if defined(ZAPH) && defined(BITSTATE)",
+ " if (zstates > ((double)(ONE_L<<(ssize-2))))",
+ " { /* more than half the bits set */",
+ " void zap_hashtable(void);",
+ " zap_hashtable();",
+ " zstates = 0;",
+ " }",
+ "#endif",
+ " }",
+ "#ifdef SVDUMP",
+ " if (vprefix > 0)",
+ " if (write(svfd, (uchar *) &now, vprefix) != vprefix)",
+ " { fprintf(efd, \"writing %%s.svd failed\\n\", PanSource);",
+ " wrapup();",
+ " }",
+ "#endif",
+ "#if defined(MA) && defined(W_XPT)",
+ " if ((unsigned long) nstates%%W_XPT == 0)",
+ " { void w_xpoint(void);",
+ " w_xpoint();",
+ " }",
+ "#endif",
+ " }",
+
+ "#if defined(FULLSTACK) || defined(CNTRSTACK)",
+ " onstack_put();",
+ "#ifdef DEBUG2",
+ "#if defined(FULLSTACK) && !defined(MA)",
+ " printf(\"%%d: putting %%u (%%d)\\n\", depth,",
+ " trpt->ostate, ",
+ " (trpt->ostate)?trpt->ostate->tagged:0);",
+ "#else",
+ " printf(\"%%d: putting\\n\", depth);",
+ "#endif",
+ "#endif",
+ "#else",
+ " #if NCORE>1",
+ " trpt->ostate = Lstate;",
+ " #endif",
+ "#endif",
+ " } }",
+
+
+ " if (depth > mreached)",
+ " mreached = depth;",
+ "#ifdef VERI",
+ " if (trpt->tau&4)",
+ "#endif",
+ " trpt->tau &= ~(1|2); /* timeout and -request off */",
+ " _n = 0;",
+ "#if SYNC",
+ " (trpt+1)->o_n = 0;",
+ "#endif",
+ "#ifdef VERI",
+ " if (now._nr_pr == 0) /* claim terminated */",
+ " uerror(\"end state in claim reached\");",
+ " check_claim(((P0 *)pptr(0))->_p);",
+ "Stutter:",
+ " if (trpt->tau&4) /* must make a claimmove */",
+ " {",
+
+ "#ifndef NOFAIR",
+ " if ((now._a_t&2) /* A-bit set */",
+ " && now._cnt[now._a_t&1] == 1)",
+ " { now._a_t &= ~2;",
+ " now._cnt[now._a_t&1] = 0;",
+ " trpt->o_pm |= 16;",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: fairness Rule 3.: _a_t = %%d\\n\",",
+ " depth, now._a_t);",
+ "#endif",
+ " }",
+ "#endif",
+
+ " II = 0; /* never */",
+ " goto Veri0;",
+ " }",
+ "#endif",
+ "#ifndef NOREDUCE",
+ " /* Look for a process with only safe transitions */",
+ " /* (special rules apply in the 2nd dfs) */",
+ " if (boq == -1 && From != To",
+ "",
+ "#ifdef SAFETY",
+ " #if NCORE>1",
+ " && (depth < z_handoff)", /* not for border states */
+ " #endif",
+ " )",
+ "#else",
+ " #if NCORE>1",
+ " && ((a_cycles) || (!a_cycles && depth < z_handoff))",
+ " #endif",
+ " && (!(now._a_t&1)",
+ " || (a_cycles &&",
+ " #ifndef BITSTATE",
+ "#ifdef MA",
+ "#ifdef VERI",
+ " !((trpt-1)->proviso))",
+ "#else",
+ " !(trpt->proviso))",
+ "#endif",
+ "#else",
+ "#ifdef VERI",
+ " (trpt-1)->ostate &&",
+ " !(((char *)&((trpt-1)->ostate->state))[0] & 128))", /* proviso bit in _a_t */
+ "#else",
+ " !(((char *)&(trpt->ostate->state))[0] & 128))",
+ "#endif",
+ "#endif",
+ " #else",
+ "#ifdef VERI",
+ " (trpt-1)->ostate &&",
+ " (trpt-1)->ostate->proviso == 0)",
+ "#else",
+ " trpt->ostate->proviso == 0)",
+ "#endif",
+ " #endif",
+ " ))",
+ "#endif", /* SAFETY */
+ "",
+"#ifdef REVERSE",
+ " for (II = From; II <= To; II++)",
+"#else",
+ " for (II = From; II >= To; II--)",
+"#endif",
+ " {",
+ "Resume: /* pick up here if preselect fails */",
+ " this = pptr(II);",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+ " if (trans[ot][tt]->atom & 8)",
+ " { t = trans[ot][tt];",
+ " if (t->qu[0] != 0)",
+ " { Ccheck++;",
+ " if (!q_cond(II, t))",
+ " continue;",
+ " Cholds++;",
+ " }",
+ " From = To = II; /* the process preselected */",
+ "#ifdef NIBIS",
+ " t->om = 0;",
+ "#endif",
+ " trpt->tau |= 32; /* preselect marker */",
+ "#ifdef DEBUG",
+ "#ifdef NIBIS",
+ " printf(\"%%3d: proc %%d Pre\", depth, II);",
+ " printf(\"Selected (om=%%d, tau=%%d)\\n\", ",
+ " t->om, trpt->tau);",
+ "#else",
+ " printf(\"%%3d: proc %%d PreSelected (tau=%%d)\\n\", ",
+ " depth, II, trpt->tau);",
+ "#endif",
+ "#endif",
+ " goto Again;",
+ " }",
+ " }",
+ " trpt->tau &= ~32;",
+ "#endif",
+ "#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI))",
+ "Again:",
+ "#endif",
+
+ " /* The Main Expansion Loop over Processes */",
+
+ " trpt->o_pm &= ~(8|16|32|64); /* fairness-marks */",
+ "#ifndef NOFAIR",
+ " if (fairness && boq == -1",
+ "#ifdef VERI",
+ " && (!(trpt->tau&4) && !((trpt-1)->tau&128))",
+ "#endif",
+ " && !(trpt->tau&8))",
+ " { /* A_bit = 1; Cnt = N in acc states with A_bit 0 */",
+ " if (!(now._a_t&2))", /* A-bit not set */
+ " {",
+ " if (a_cycles && (trpt->o_pm&2))",
+ " { /* Accepting state */",
+ " now._a_t |= 2;",
+ " now._cnt[now._a_t&1] = now._nr_pr + 1;", /* NEW +1 */
+ " trpt->o_pm |= 8;",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: fairness Rule 1: cnt=%%d, _a_t=%%d\\n\",",
+ " depth, now._cnt[now._a_t&1], now._a_t);",
+ "#endif",
+ " }",
+ " } else", /* A-bit set */
+ " { /* A_bit = 0 when Cnt 0 */",
+ " if (now._cnt[now._a_t&1] == 1)",
+ " { now._a_t &= ~2;", /* reset a-bit */
+ " now._cnt[now._a_t&1] = 0;",
+ " trpt->o_pm |= 16;",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: fairness Rule 3: _a_t = %%d\\n\",",
+ " depth, now._a_t);",
+ "#endif",
+ " } } }",
+ "#endif",
+ "",
+"#ifdef REVERSE",
+ " for (II = From; II <= To; II++)",
+"#else",
+ " for (II = From; II >= To; II--)",
+"#endif",
+ " {",
+ "#if SYNC",
+ " /* no rendezvous with same proc */",
+ " if (boq != -1 && trpt->pr == II) continue;",
+ "#endif",
+ "#ifdef SCHED",
+ " /* limit max nr of interleavings */",
+ " if (From != To", /* not a PO or atomic move */
+ " && depth > 0", /* there is a prior move */
+ " #ifdef VERI",
+ " && II != 0", /* never claim can always move */
+ " #endif",
+ " && (trpt-1)->pr != II", /* context switch */
+ " && trpt->sched_limit >= sched_max)",
+ " { continue;",
+ " }",
+ "#endif",
+ "#ifdef VERI",
+ "Veri0:",
+ "#endif",
+ " this = pptr(II);",
+ " tt = (int) ((P0 *)this)->_p;",
+ " ot = (uchar) ((P0 *)this)->_t;",
+
+ "#ifdef NIBIS",
+ " /* don't repeat a previous preselected expansion */",
+ " /* could hit this if reduction proviso was false */",
+ " t = trans[ot][tt];",
+ " if (!(trpt->tau&4)", /* not claim */
+ " && !(trpt->tau&1)", /* not timeout */
+ " && !(trpt->tau&32)", /* not preselected */
+ " && (t->atom & 8)", /* local */
+ " && boq == -1", /* not inside rendezvous */
+ " && From != To)", /* not inside atomic seq */
+ " { if (t->qu[0] == 0", /* unconditional */
+ " || q_cond(II, t))", /* true condition */
+ " { _m = t->om;",
+ " if (_m>_n||(_n>3&&_m!=0)) _n=_m;",
+ " continue; /* did it before */",
+ " } }",
+ "#endif",
+ " trpt->o_pm &= ~1; /* no move in this pid yet */",
+ "#ifdef EVENT_TRACE",
+ " (trpt+1)->o_event = now._event;",
+ "#endif",
+ " /* Fairness: Cnt++ when Cnt == II */",
+ "#ifndef NOFAIR",
+ " trpt->o_pm &= ~64; /* didn't apply rule 2 */",
+ " if (fairness",
+ " && boq == -1", /* not mid rv - except rcv - NEW 3.0.8 */
+ " && !(trpt->o_pm&32)", /* Rule 2 not in effect */
+ " && (now._a_t&2)", /* A-bit is set */
+ " && now._cnt[now._a_t&1] == II+2)",
+ " { now._cnt[now._a_t&1] -= 1;",
+ "#ifdef VERI",
+ " /* claim need not participate */",
+ " if (II == 1)",
+ " now._cnt[now._a_t&1] = 1;",
+ "#endif",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: proc %%d fairness \", depth, II);",
+ " printf(\"Rule 2: --cnt to %%d (%%d)\\n\",",
+ " now._cnt[now._a_t&1], now._a_t);",
+ "#endif",
+ " trpt->o_pm |= (32|64);",
+ " }",
+ "#endif",
+ "#ifdef HAS_PROVIDED",
+ " if (!provided(II, ot, tt, t)) continue;",
+ "#endif",
+ " /* check all trans of proc II - escapes first */",
+ "#ifdef HAS_UNLESS",
+ " trpt->e_state = 0;",
+ "#endif",
+ " (trpt+1)->pr = (uchar) II;", /* for uerror */
+ " (trpt+1)->st = tt;",
+
+ "#ifdef RANDOMIZE",
+ " for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++)",
+ " { if (strcmp(t->tp, \"else\") == 0)",
+ " { eoi++;",
+ " break;",
+ " } }",
+ " if (eoi > 0)",
+ " { t = trans[ot][tt];",
+ " #ifdef VERBOSE",
+ " printf(\"randomizer: suppressed, saw else\\n\");",
+ " #endif",
+ " } else",
+ " { eoi = rand()%%ooi;",
+ " #ifdef VERBOSE",
+ " printf(\"randomizer: skip %%d in %%d\\n\", eoi, ooi);",
+ " #endif",
+ " for (t = trans[ot][tt]; t; t = t->nxt)",
+ " if (eoi-- <= 0) break;",
+ " }",
+ "domore:",
+ " for ( ; t && ooi > 0; t = t->nxt, ooi--)",
+ "#else", /* ie dont randomize */
+ " for (t = trans[ot][tt]; t; t = t->nxt)",
+ "#endif",
+ " {",
+ "#ifdef HAS_UNLESS",
+ " /* exploring all transitions from",
+ " * a single escape state suffices",
+ " */",
+ " if (trpt->e_state > 0",
+ " && trpt->e_state != t->e_trans)",
+ " {",
+ "#ifdef DEBUG",
+ " printf(\"skip 2nd escape %%d (did %%d before)\\n\",",
+ " t->e_trans, trpt->e_state);",
+ "#endif",
+ " break;",
+ " }",
+ "#endif",
+ " (trpt+1)->o_t = t;", /* for uerror */
+ "#ifdef INLINE",
+ "#include FORWARD_MOVES",
+ "P999: /* jumps here when move succeeds */",
+ "#else",
+ " if (!(_m = do_transit(t, II))) continue;",
+ "#endif",
+ "#ifdef SCHED",
+ " if (depth > 0",
+ " #ifdef VERI",
+ " && II != 0",
+ " #endif",
+ " && (trpt-1)->pr != II)",
+ " { trpt->sched_limit = 1 + (trpt-1)->sched_limit;",
+ " }",
+ "#endif",
+ " if (boq == -1)",
+ "#ifdef CTL",
+ " /* for branching-time, can accept reduction only if */",
+ " /* the persistent set contains just 1 transition */",
+ " { if ((trpt->tau&32) && (trpt->o_pm&1))",
+ " trpt->tau |= 16;", /* CTL */
+ " trpt->o_pm |= 1; /* we moved */",
+ " }",
+ "#else",
+ " trpt->o_pm |= 1; /* we moved */",
+ "#endif",
+
+ "#ifdef LOOPSTATE",
+ " if (loopstate[ot][tt])",
+ " {",
+ "#ifdef VERBOSE",
+ " printf(\"exiting from loopstate:\\n\");",
+ "#endif",
+ " trpt->tau |= 16;", /* exiting loopstate */
+ " cnt_loops++;",
+ " }",
+ "#endif",
+
+ "#ifdef PEG",
+ " peg[t->forw]++;",
+ "#endif",
+ "#if defined(VERBOSE) || defined(CHECK)",
+ "#if defined(SVDUMP)",
+ " cpu_printf(\"%%3d: proc %%d exec %%d \\n\", depth, II, t->t_id);",
+ "#else",
+ " cpu_printf(\"%%3d: proc %%d exec %%d, %%d to %%d, %%s %%s %%s %%saccepting [tau=%%d]\\n\", ",
+ " depth, II, t->forw, tt, t->st, t->tp,",
+ " (t->atom&2)?\"atomic\":\"\",",
+ " (boq != -1)?\"rendez-vous\":\"\",",
+ " (trpt->o_pm&2)?\"\":\"non-\", trpt->tau);",
+ "#ifdef HAS_UNLESS",
+ " if (t->e_trans)",
+ " cpu_printf(\"\\t(escape to state %%d)\\n\", t->st);",
+ "#endif",
+ "#endif",
+ "#ifdef RANDOMIZE",
+ " cpu_printf(\"\\t(randomizer %%d)\\n\", ooi);",
+ "#endif",
+ "#endif",
+
+ "#ifdef HAS_LAST",
+ "#ifdef VERI",
+ " if (II != 0)",
+ "#endif",
+ " now._last = II - BASE;",
+ "#endif",
+ "#ifdef HAS_UNLESS",
+ " trpt->e_state = t->e_trans;",
+ "#endif",
+
+ " depth++; trpt++;",
+ " trpt->pr = (uchar) II;",
+ " trpt->st = tt;",
+ " trpt->o_pm &= ~(2|4);",
+ " if (t->st > 0)",
+ " { ((P0 *)this)->_p = t->st;",
+ "/* moved down reached[ot][t->st] = 1; */",
+ " }",
+ "#ifndef SAFETY",
+ " if (a_cycles)",
+ " {",
+ "#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP))",
+ " int ii;",
+ "#endif",
+ "#define P__Q ((P0 *)pptr(ii))",
+ "#if ACCEPT_LAB>0",
+ "#ifdef NP",
+ " /* state 1 of np_ claim is accepting */",
+ " if (((P0 *)pptr(0))->_p == 1)",
+ " trpt->o_pm |= 2;",
+ "#else",
+ " for (ii = 0; ii < (int) now._nr_pr; ii++)",
+ " { if (accpstate[P__Q->_t][P__Q->_p])",
+ " { trpt->o_pm |= 2;",
+ " break;",
+ " } }",
+ "#endif",
+ "#endif",
+ "#if defined(HAS_NP) && PROG_LAB>0",
+ " for (ii = 0; ii < (int) now._nr_pr; ii++)",
+ " { if (progstate[P__Q->_t][P__Q->_p])",
+ " { trpt->o_pm |= 4;",
+ " break;",
+ " } }",
+ "#endif",
+ "#undef P__Q",
+ " }",
+ "#endif",
+ " trpt->o_t = t; trpt->o_n = _n;",
+ " trpt->o_ot = ot; trpt->o_tt = tt;",
+ " trpt->o_To = To; trpt->o_m = _m;",
+ " trpt->tau = 0;",
+ "#ifdef RANDOMIZE",
+ " trpt->oo_i = ooi;",
+ "#endif",
+ " if (boq != -1 || (t->atom&2))",
+ " { trpt->tau |= 8;",
+ "#ifdef VERI",
+ " /* atomic sequence in claim */",
+ " if((trpt-1)->tau&4)",
+ " trpt->tau |= 4;",
+ " else",
+ " trpt->tau &= ~4;",
+ " } else",
+ " { if ((trpt-1)->tau&4)",
+ " trpt->tau &= ~4;",
+ " else",
+ " trpt->tau |= 4;",
+ " }",
+ " /* if claim allowed timeout, so */",
+ " /* does the next program-step: */",
+ " if (((trpt-1)->tau&1) && !(trpt->tau&4))",
+ " trpt->tau |= 1;",
+ "#else",
+ " } else",
+ " trpt->tau &= ~8;",
+ "#endif",
+ " if (boq == -1 && (t->atom&2))",
+ " { From = To = II; nlinks++;",
+ " } else",
+"#ifdef REVERSE",
+ " { From = BASE; To = now._nr_pr-1;",
+"#else",
+ " { From = now._nr_pr-1; To = BASE;",
+"#endif",
+ " }",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Push_Stack_Tree(II, t->t_id);",
+ " }",
+ "#endif",
+ " goto Down; /* pseudo-recursion */",
+ "Up:",
+ "#ifdef CHECK",
+ " cpu_printf(\"%%d: Up - %%s\\n\", depth,",
+ " (trpt->tau&4)?\"claim\":\"program\");",
+ "#endif",
+ "#if NCORE>1",
+ " iam_alive();",
+ " #ifdef USE_DISK",
+ " mem_drain();",
+ " #endif",
+ "#endif",
+ "#if defined(MA) || NCORE>1",
+ " if (depth <= 0) return;",
+ " /* e.g., if first state is old, after a restart */",
+ "#endif",
+
+ "#ifdef SC",
+ " if (CNT1 > CNT2",
+ " && depth < hiwater - (HHH-DDD) + 2)",
+ " {",
+ " trpt += DDD;",
+ " disk2stack();",
+ " maxdepth -= DDD;",
+ " hiwater -= DDD;",
+ " if(verbose)",
+ " printf(\"unzap %%d: %%d\\n\", CNT2, hiwater);",
+ " }",
+ "#endif",
+
+ "#ifndef NOFAIR",
+ " if (trpt->o_pm&128) /* fairness alg */",
+ " { now._cnt[now._a_t&1] = trpt->bup.oval;",
+ " _n = 1; trpt->o_pm &= ~128;",
+ " depth--; trpt--;",
+ "#if defined(VERBOSE) || defined(CHECK)",
+ " printf(\"%%3d: reversed fairness default move\\n\", depth);",
+ "#endif",
+ " goto Q999;",
+ " }",
+ "#endif",
+
+ "#ifdef HAS_LAST",
+ "#ifdef VERI",
+ " { int d; Trail *trl;",
+ " now._last = 0;",
+ " for (d = 1; d < depth; d++)",
+ " { trl = getframe(depth-d); /* was (trpt-d) */",
+ " if (trl->pr != 0)",
+ " { now._last = trl->pr - BASE;",
+ " break;",
+ " } } }",
+ "#else",
+ " now._last = (depth<1)?0:(trpt-1)->pr;",
+ "#endif",
+ "#endif",
+ "#ifdef EVENT_TRACE",
+ " now._event = trpt->o_event;",
+ "#endif",
+ "#ifndef SAFETY",
+ " if ((now._a_t&1) && depth <= A_depth)",
+ " return; /* to checkcycles() */",
+ "#endif",
+ " t = trpt->o_t; _n = trpt->o_n;",
+ " ot = trpt->o_ot; II = trpt->pr;",
+ " tt = trpt->o_tt; this = pptr(II);",
+ " To = trpt->o_To; _m = trpt->o_m;",
+ "#ifdef RANDOMIZE",
+ " ooi = trpt->oo_i;",
+ "#endif",
+ "#ifdef INLINE_REV",
+ " _m = do_reverse(t, II, _m);",
+ "#else",
+ "#include REVERSE_MOVES",
+ "R999: /* jumps here when done */",
+ "#endif",
+
+ "#ifdef VERBOSE",
+ " cpu_printf(\"%%3d: proc %%d reverses %%d, %%d to %%d\\n\",",
+ " depth, II, t->forw, tt, t->st);",
+ " cpu_printf(\"\\t%%s [abit=%%d,adepth=%%d,tau=%%d,%%d]\\n\", ",
+ " t->tp, now._a_t, A_depth, trpt->tau, (trpt-1)->tau);",
+ "#endif",
+ "#ifndef NOREDUCE",
+ " /* pass the proviso tags */",
+ " if ((trpt->tau&8) /* rv or atomic */",
+ " && (trpt->tau&16))",
+ " (trpt-1)->tau |= 16;", /* pass upward */
+ "#ifdef SAFETY",
+ " if ((trpt->tau&8) /* rv or atomic */",
+ " && (trpt->tau&64))",
+ " (trpt-1)->tau |= 64;",
+ "#endif",
+ "#endif",
+ " depth--; trpt--;",
+ "",
+ "#ifdef NSUCC",
+ " trpt->n_succ++;",
+ "#endif",
+ "#ifdef NIBIS",
+ " (trans[ot][tt])->om = _m; /* head of list */",
+ "#endif",
+
+ " /* i.e., not set if rv fails */",
+ " if (_m)",
+ " {",
+ "#if defined(VERI) && !defined(NP)",
+ " if (II == 0 && verbose && !reached[ot][t->st])",
+ " {",
+ " printf(\"depth %%d: Claim reached state %%d (line %%d)\\n\",",
+ " depth, t->st, src_claim [t->st]);",
+ " fflush(stdout);",
+ " }",
+ "#endif",
+ " reached[ot][t->st] = 1;",
+ " reached[ot][tt] = 1;",
+ " }",
+ "#ifdef HAS_UNLESS",
+ " else trpt->e_state = 0; /* undo */",
+ "#endif",
+
+ " if (_m>_n||(_n>3&&_m!=0)) _n=_m;",
+ " ((P0 *)this)->_p = tt;",
+ " } /* all options */",
+
+ "#ifdef RANDOMIZE",
+ " if (!t && ooi > 0)", /* means we skipped some initial options */
+ " { t = trans[ot][tt];",
+ " #ifdef VERBOSE",
+ " printf(\"randomizer: continue for %%d more\\n\", ooi);",
+ " #endif",
+ " goto domore;",
+ " }",
+ " #ifdef VERBOSE",
+ " else",
+ " printf(\"randomizer: done\\n\");",
+ " #endif",
+ "#endif",
+
+ "#ifndef NOFAIR",
+ " /* Fairness: undo Rule 2 */",
+ " if ((trpt->o_pm&32)",/* rule 2 was applied */
+ " && (trpt->o_pm&64))",/* by this process II */
+ " { if (trpt->o_pm&1)",/* it didn't block */
+ " {",
+ "#ifdef VERI",
+ " if (now._cnt[now._a_t&1] == 1)",
+ " now._cnt[now._a_t&1] = 2;",
+ "#endif",
+ " now._cnt[now._a_t&1] += 1;",
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: proc %%d fairness \", depth, II);",
+ " printf(\"undo Rule 2, cnt=%%d, _a_t=%%d\\n\",",
+ " now._cnt[now._a_t&1], now._a_t);",
+ "#endif",
+ " trpt->o_pm &= ~(32|64);",
+ " } else", /* process blocked */
+ " { if (_n > 0)", /* a prev proc didn't */
+ " {", /* start over */
+ " trpt->o_pm &= ~64;",
+"#ifdef REVERSE",
+ " II = From-1;", /* after loop incr II == From */
+"#else",
+ " II = From+1;", /* after loop decr II == From */
+"#endif",
+ " } } }",
+ "#endif",
+
+ "#ifdef VERI",
+ " if (II == 0) break; /* never claim */",
+ "#endif",
+ " } /* all processes */",
+
+ "#ifdef NSUCC",
+ " tally_succ(trpt->n_succ);",
+ "#endif",
+
+ "#ifdef SCHED",
+ " if (_n == 0 /* no process could move */",
+ " #ifdef VERI",
+ " && II != 0",
+ " #endif",
+ " && depth > 0",
+ " && trpt->sched_limit >= sched_max)",
+ " { _n = 1; /* not a deadlock */",
+ " }",
+ "#endif",
+
+ "#ifndef NOFAIR",
+ " /* Fairness: undo Rule 2 */",
+ " if (trpt->o_pm&32) /* remains if proc blocked */",
+ " {",
+ "#ifdef VERI",
+ " if (now._cnt[now._a_t&1] == 1)",
+ " now._cnt[now._a_t&1] = 2;",
+ "#endif",
+ " now._cnt[now._a_t&1] += 1;",
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: proc -- fairness \", depth);",
+ " printf(\"undo Rule 2, cnt=%%d, _a_t=%%d\\n\",",
+ " now._cnt[now._a_t&1], now._a_t);",
+ "#endif",
+ " trpt->o_pm &= ~32;",
+ " }",
+"#ifndef NP",
+ /* 12/97 non-progress cycles cannot be created
+ * by stuttering extension, here or elsewhere
+ */
+ " if (fairness",
+ " && _n == 0 /* nobody moved */",
+ "#ifdef VERI",
+ " && !(trpt->tau&4) /* in program move */",
+ "#endif",
+ " && !(trpt->tau&8) /* not an atomic one */",
+ "#ifdef OTIM",
+ " && ((trpt->tau&1) || endstate())",
+ "#else",
+ "#ifdef ETIM",
+ " && (trpt->tau&1) /* already tried timeout */",
+ "#endif",
+ "#endif",
+ "#ifndef NOREDUCE",
+ " /* see below */",
+ " && !((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))",
+ "#endif",
+ " && now._cnt[now._a_t&1] > 0) /* needed more procs */",
+ " { depth++; trpt++;",
+ " trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4));",
+ " trpt->bup.oval = now._cnt[now._a_t&1];",
+ " now._cnt[now._a_t&1] = 1;",
+ "#ifdef VERI",
+ " trpt->tau = 4;",
+ "#else",
+ " trpt->tau = 0;",
+ "#endif",
+"#ifdef REVERSE",
+ " From = BASE; To = now._nr_pr-1;",
+"#else",
+ " From = now._nr_pr-1; To = BASE;",
+"#endif",
+ "#if defined(VERBOSE) || defined(CHECK)",
+ " printf(\"%%3d: fairness default move \", depth);",
+ " printf(\"(all procs block)\\n\");",
+ "#endif",
+ " goto Down;",
+ " }",
+"#endif",
+ "Q999: /* returns here with _n>0 when done */;",
+
+ " if (trpt->o_pm&8)",
+ " { now._a_t &= ~2;",
+ " now._cnt[now._a_t&1] = 0;",
+ " trpt->o_pm &= ~8;",
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: fairness undo Rule 1, _a_t=%%d\\n\",",
+ " depth, now._a_t);",
+ "#endif",
+ " }",
+ " if (trpt->o_pm&16)",
+ " { now._a_t |= 2;", /* restore a-bit */
+ " now._cnt[now._a_t&1] = 1;", /* NEW: restore cnt */
+ " trpt->o_pm &= ~16;",
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: fairness undo Rule 3, _a_t=%%d\\n\",",
+ " depth, now._a_t);",
+ "#endif",
+ " }",
+ "#endif",
+
+ "#ifndef NOREDUCE",
+"#ifdef SAFETY",
+ "#ifdef LOOPSTATE",
+ " /* at least one move that was preselected at this */",
+ " /* level, blocked or was a loop control flow point */",
+ " if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))",
+ "#else",
+ " /* preselected move - no successors outside stack */",
+ " if ((trpt->tau&32) && !(trpt->tau&64))",
+ "#endif",
+"#ifdef REVERSE",
+ " { From = BASE; To = now._nr_pr-1;",
+"#else",
+ " { From = now._nr_pr-1; To = BASE;",
+"#endif",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ",
+ " depth, II+1, _n, trpt->tau);",
+ "#endif",
+ " _n = 0; trpt->tau &= ~(16|32|64);",
+"#ifdef REVERSE",
+ " if (II <= To) /* II already decremented */",
+"#else",
+ " if (II >= BASE) /* II already decremented */",
+"#endif",
+ " goto Resume;",
+ " else",
+ " goto Again;",
+ " }",
+"#else",
+ " /* at least one move that was preselected at this */",
+ " /* level, blocked or truncated at the next level */",
+ "/* implied: #ifdef FULLSTACK */",
+ " if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))",
+ " {",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ",
+ " depth, II+1, (int) _n, trpt->tau);",
+ "#endif",
+ " if (a_cycles && (trpt->tau&16))",
+ " { if (!(now._a_t&1))",
+ " {",
+ "#ifdef DEBUG",
+ " printf(\"%%3d: setting proviso bit\\n\", depth);",
+ "#endif",
+ "#ifndef BITSTATE",
+ "#ifdef MA",
+ "#ifdef VERI",
+ " (trpt-1)->proviso = 1;",
+ "#else",
+ " trpt->proviso = 1;",
+ "#endif",
+ "#else",
+ "#ifdef VERI",
+ " if ((trpt-1)->ostate)",
+ " ((char *)&((trpt-1)->ostate->state))[0] |= 128;",
+ "#else",
+ " ((char *)&(trpt->ostate->state))[0] |= 128;",
+ "#endif",
+ "#endif",
+ "#else",
+ "#ifdef VERI",
+ " if ((trpt-1)->ostate)",
+ " (trpt-1)->ostate->proviso = 1;",
+ "#else",
+ " trpt->ostate->proviso = 1;",
+ "#endif",
+ "#endif",
+"#ifdef REVERSE",
+ " From = BASE; To = now._nr_pr-1;",
+"#else",
+ " From = now._nr_pr-1; To = BASE;",
+"#endif",
+ " _n = 0; trpt->tau &= ~(16|32|64);",
+ " goto Again; /* do full search */",
+ " } /* else accept reduction */",
+ " } else",
+"#ifdef REVERSE",
+ " { From = BASE; To = now._nr_pr-1;",
+"#else",
+ " { From = now._nr_pr-1; To = BASE;",
+"#endif",
+ " _n = 0; trpt->tau &= ~(16|32|64);",
+"#ifdef REVERSE",
+ " if (II <= To) /* already decremented */",
+"#else",
+ " if (II >= BASE) /* already decremented */",
+"#endif",
+ " goto Resume;",
+ " else",
+ " goto Again;",
+ " } }",
+ "/* #endif */",
+"#endif",
+ "#endif",
+
+ " if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2)))",
+ " {",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%3d: no move [II=%%d, tau=%%d, boq=%%d]\\n\",",
+ " depth, II, trpt->tau, boq);",
+ "#endif",
+ "#if SYNC",
+ " /* ok if a rendez-vous fails: */",
+ " if (boq != -1) goto Done;",
+ "#endif",
+ " /* ok if no procs or we're at maxdepth */",
+ " if ((now._nr_pr == 0 && (!strict || qs_empty()))",
+ "#ifdef OTIM",
+ " || endstate()",
+ "#endif",
+ " || depth >= maxdepth-1) goto Done;",
+
+ " if ((trpt->tau&8) && !(trpt->tau&4))",
+ " { trpt->tau &= ~(1|8);",
+ " /* 1=timeout, 8=atomic */",
+"#ifdef REVERSE",
+ " From = BASE; To = now._nr_pr-1;",
+"#else",
+ " From = now._nr_pr-1; To = BASE;",
+"#endif",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%3d: atomic step proc %%d unexecutable\\n\", depth, II+1);",
+ "#endif",
+ "#ifdef VERI",
+ " trpt->tau |= 4; /* switch to claim */",
+ "#endif",
+ " goto AllOver;",
+ " }",
+
+ "#ifdef ETIM",
+ " if (!(trpt->tau&1)) /* didn't try timeout yet */",
+ " {",
+ "#ifdef VERI",
+ " if (trpt->tau&4)",
+ " {",
+ "#ifndef NTIM",
+ " if (trpt->tau&2) /* requested */",
+ "#endif",
+ " { trpt->tau |= 1;",
+ " trpt->tau &= ~2;",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%d: timeout\\n\", depth);",
+ "#endif",
+ " goto Stutter;",
+ " } }",
+ " else",
+ " { /* only claim can enable timeout */",
+ " if ((trpt->tau&8)",
+ " && !((trpt-1)->tau&4))",
+ "/* blocks inside an atomic */ goto BreakOut;",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%d: req timeout\\n\",",
+ " depth);",
+ "#endif",
+ " (trpt-1)->tau |= 2; /* request */",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ "#else",
+
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%d: timeout\\n\", depth);",
+ "#endif",
+ " trpt->tau |= 1;",
+ " goto Again;",
+ "#endif",
+ " }",
+ "#endif",
+
+ /* old location of atomic block code */
+ "#ifdef VERI",
+ "BreakOut:",
+ "#ifndef NOSTUTTER",
+ " if (!(trpt->tau&4))",
+ " { trpt->tau |= 4; /* claim stuttering */",
+ " trpt->tau |= 128; /* stutter mark */",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%d: claim stutter\\n\", depth);",
+ "#endif",
+ " goto Stutter;",
+ " }",
+ "#else",
+ " ;",
+ "#endif",
+ "#else",
+ " if (!noends && !a_cycles && !endstate())",
+ " { depth--; trpt--; /* new 4.2.3 */",
+ " uerror(\"invalid end state\");",
+ " depth++; trpt++;",
+ " }",
+ "#ifndef NOSTUTTER",
+ " else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */",
+ " { depth--; trpt--;",
+ " uerror(\"accept stutter\");",
+ " depth++; trpt++;",
+ " }",
+ "#endif",
+ "#endif",
+ " }",
+ "Done:",
+ " if (!(trpt->tau&8)) /* not in atomic seqs */",
+ " {",
+ "#ifndef SAFETY",
+ " if (_n != 0", /* we made a move */
+ "#ifdef VERI",
+ " /* --after-- a program-step, i.e., */",
+ " /* after backtracking a claim-step */",
+ " && (trpt->tau&4)",
+ " /* with at least one running process */",
+ " /* unless in a stuttered accept state */",
+ " && ((now._nr_pr > 1) || (trpt->o_pm&2))",
+ "#endif",
+ " && !(now._a_t&1))", /* not in 2nd DFS */
+ " {",
+ "#ifndef NOFAIR",
+ " if (fairness)", /* implies a_cycles */
+ " {",
+ "#ifdef VERBOSE",
+ " cpu_printf(\"Consider check %%d %%d...\\n\",",
+ " now._a_t, now._cnt[0]);",
+ "#endif",
+#if 0
+ the a-bit is set, which means that the fairness
+ counter is running -- it was started in an accepting state.
+ we check that the counter reached 1, which means that all
+ processes moved least once.
+ this means we can start the search for cycles -
+ to be able to return to this state, we should be able to
+ run down the counter to 1 again -- which implies a visit to
+ the accepting state -- even though the Seed state for this
+ search is itself not necessarily accepting
+#endif
+ " if ((now._a_t&2) /* A-bit */",
+ " && (now._cnt[0] == 1))",
+ " checkcycles();",
+ " } else",
+ "#endif",
+ " if (a_cycles && (trpt->o_pm&2))",
+ " checkcycles();",
+ " }",
+ "#endif",
+"#ifndef MA",
+ "#if defined(FULLSTACK) || defined(CNTRSTACK)",
+ "#ifdef VERI",
+ " if (boq == -1",
+ " && (((trpt->tau&4) && !(trpt->tau&128))",
+ " || ( (trpt-1)->tau&128)))",
+ "#else",
+ " if (boq == -1)",
+ "#endif",
+ " {",
+ "#ifdef DEBUG2",
+ "#if defined(FULLSTACK)",
+ " printf(\"%%d: zapping %%u (%%d)\\n\",",
+ " depth, trpt->ostate,",
+ " (trpt->ostate)?trpt->ostate->tagged:0);",
+ "#endif",
+ "#endif",
+ " onstack_zap();",
+ " }",
+ "#endif",
+"#else",
+ "#ifdef VERI",
+ " if (boq == -1",
+ " && (((trpt->tau&4) && !(trpt->tau&128))",
+ " || ( (trpt-1)->tau&128)))",
+ "#else",
+ " if (boq == -1)",
+ "#endif",
+ " {",
+ "#ifdef DEBUG",
+ " printf(\"%%d: zapping\\n\", depth);",
+ "#endif",
+ " onstack_zap();",
+ "#ifndef NOREDUCE",
+ " if (trpt->proviso)",
+ " gstore((char *) &now, vsize, 1);",
+ "#endif",
+ " }",
+"#endif",
+ " }",
+ " if (depth > 0)",
+ " {",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ " if (upto > 0)",
+ " { Pop_Stack_Tree();",
+ " }",
+ "#endif",
+ " goto Up;",
+ " }",
+ "}\n",
+ "#else",
+ "void new_state(void) { /* place holder */ }",
+ "#endif", /* BFS */
+ "",
+ "void",
+ "assert(int a, char *s, int ii, int tt, Trans *t)",
+ "{",
+ " if (!a && !noasserts)",
+ " { char bad[1024];",
+ " strcpy(bad, \"assertion violated \");",
+ " if (strlen(s) > 1000)",
+ " { strncpy(&bad[19], (const char *) s, 1000);",
+ " bad[1019] = '\\0';",
+ " } else",
+ " strcpy(&bad[19], s);",
+ " uerror(bad);",
+ " }",
+ "}",
+ "#ifndef NOBOUNDCHECK",
+ "int",
+ "Boundcheck(int x, int y, int a1, int a2, Trans *a3)",
+ "{",
+ " assert((x >= 0 && x < y), \"- invalid array index\",",
+ " a1, a2, a3);",
+ " return x;",
+ "}",
+ "#endif",
+ "void",
+ "wrap_stats(void)",
+ "{",
+ " if (nShadow>0)",
+ " printf(\"%%9.8g states, stored (%%g visited)\\n\",",
+ " nstates - nShadow, nstates);",
+ " else",
+ " printf(\"%%9.8g states, stored\\n\", nstates);",
+ "#ifdef BFS",
+ "#if SYNC",
+ " printf(\" %%8g nominal states (- rv and atomic)\\n\", nstates-midrv-nlinks+revrv);",
+ " printf(\" %%8g rvs succeeded\\n\", midrv-failedrv);",
+ "#else",
+ " printf(\" %%8g nominal states (stored-atomic)\\n\", nstates-nlinks);",
+ "#endif",
+ "#ifdef DEBUG",
+ " printf(\" %%8g midrv\\n\", midrv);",
+ " printf(\" %%8g failedrv\\n\", failedrv);",
+ " printf(\" %%8g revrv\\n\", revrv);",
+ "#endif",
+ "#endif",
+ " printf(\"%%9.8g states, matched\\n\", truncs);",
+ "#ifdef CHECK",
+ " printf(\"%%9.8g matches within stack\\n\",truncs2);",
+ "#endif",
+ " if (nShadow>0)",
+ " printf(\"%%9.8g transitions (= visited+matched)\\n\",",
+ " nstates+truncs);",
+ " else",
+ " printf(\"%%9.8g transitions (= stored+matched)\\n\",",
+ " nstates+truncs);",
+ " printf(\"%%9.8g atomic steps\\n\", nlinks);",
+ " if (nlost) printf(\"%%g lost messages\\n\", (double) nlost);",
+ "",
+ "#ifndef BITSTATE",
+ " printf(\"hash conflicts: %%9.8g (resolved)\\n\", hcmp);",
+ " #ifndef AUTO_RESIZE",
+ " if (hcmp > (double) (1<<ssize))",
+ " { printf(\"hint: increase hashtable-size (-w) to reduce runtime\\n\");",
+ " } /* in multi-core: also reduces lock delays on access to hashtable */",
+ " #endif",
+ "#else",
+ "#ifdef CHECK",
+ " printf(\"%%8g states allocated for dfs stack\\n\", ngrabs);",
+ "#endif",
+ " if (udmem)",
+ " printf(\"\\nhash factor: %%4g (best if > 100.)\\n\\n\",",
+ " (double)(((double) udmem) * 8.0) / (double) nstates);",
+ " else",
+ " printf(\"\\nhash factor: %%4g (best if > 100.)\\n\\n\",",
+ " (double)(1<<(ssize-8)) / (double) nstates * 256.0);",
+ " printf(\"bits set per state: %%u (-k%%u)\\n\", hfns, hfns);",
+ " #if 0",
+#ifndef POWOW
+ " if (udmem)",
+ " { printf(\"total bits available: %%8g (-M%%ld)\\n\",",
+ " ((double) udmem) * 8.0, udmem/(1024L*1024L));",
+ " } else",
+#endif
+ " printf(\"total bits available: %%8g (-w%%d)\\n\",",
+ " ((double) (ONE_L << (ssize-4)) * 16.0), ssize);",
+ " #endif",
+ "#endif",
+"#ifdef BFS_DISK",
+ " printf(\"bfs disk reads: %%ld writes %%ld -- diff %%ld\\n\",",
+ " bfs_dsk_reads, bfs_dsk_writes, bfs_dsk_writes-bfs_dsk_reads);",
+ " if (bfs_dsk_read >= 0) (void) close(bfs_dsk_read);",
+ " if (bfs_dsk_write >= 0) (void) close(bfs_dsk_write);",
+ " (void) unlink(\"pan_bfs_dsk.tmp\");",
+"#endif",
+ "}",
+ "",
+ "void",
+ "wrapup(void)",
+ "{",
+ "#if defined(BITSTATE) || !defined(NOCOMP)",
+ " double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0;",
+ " #if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM))",
+ " int mverbose = 1;",
+ " #else",
+ " int mverbose = verbose;",
+ " #endif",
+ "#endif",
+ "#if NCORE>1",
+ " if (verbose) cpu_printf(\"wrapup -- %%d error(s)\\n\", errors);",
+ " if (core_id != 0)",
+ " {",
+ "#ifdef USE_DISK",
+ " void dsk_stats(void);",
+ " dsk_stats();",
+ "#endif",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 2; /* wrapup */",
+ " }",
+ " exit(0); /* normal termination, not an error */",
+ " }",
+ "#endif",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ " signal(SIGINT, SIG_DFL);",
+ "#endif",
+ " printf(\"\\n(%%s)\\n\", SpinVersion);",
+ " if (!done) printf(\"Warning: Search not completed\\n\");",
+ "#ifdef SC",
+ " (void) unlink((const char *)stackfile);",
+ "#endif",
+ "#if NCORE>1",
+ " if (a_cycles)",
+ " { printf(\" + Multi-Core (NCORE=%%d)\\n\", NCORE);",
+ " } else",
+ " { printf(\" + Multi-Core (NCORE=%%d -z%%d)\\n\", NCORE, z_handoff);",
+ " }",
+ "#endif",
+ "#ifdef BFS",
+ " printf(\" + Using Breadth-First Search\\n\");",
+ "#endif",
+ "#ifndef NOREDUCE",
+ " printf(\" + Partial Order Reduction\\n\");",
+ "#endif",
+ "#ifdef REVERSE",
+ " printf(\" + Reverse Depth-First Search Order\\n\");",
+ "#endif",
+ "#ifdef T_REVERSE",
+ " printf(\" + Reverse Transition Ordering\\n\");",
+ "#endif",
+ "#ifdef RANDOMIZE",
+ " printf(\" + Randomized Transition Ordering\\n\");",
+ "#endif",
+ "#ifdef SCHED",
+ " printf(\" + Scheduling Restriction (-DSCHED=%%d)\\n\", sched_max);",
+ "#endif",
+#if 0
+ "#ifdef Q_PROVISO",
+ " printf(\" + Queue Proviso\\n\");",
+ "#endif",
+#endif
+ "#ifdef COLLAPSE",
+ " printf(\" + Compression\\n\");",
+ "#endif",
+ "#ifdef MA",
+ " printf(\" + Graph Encoding (-DMA=%%d)\\n\", MA);",
+ " #ifdef R_XPT",
+ " printf(\" Restarted from checkpoint %%s.xpt\\n\", PanSource);",
+ " #endif",
+ "#endif",
+ "#ifdef CHECK",
+ " #ifdef FULLSTACK",
+ " printf(\" + FullStack Matching\\n\");",
+ " #endif",
+ " #ifdef CNTRSTACK",
+ " printf(\" + CntrStack Matching\\n\");",
+ " #endif",
+ "#endif",
+ "#ifdef BITSTATE",
+ " printf(\"\\nBit statespace search for:\\n\");",
+ "#else",
+ "#ifdef HC",
+ " printf(\"\\nHash-Compact %%d search for:\\n\", HC);",
+ "#else",
+ " printf(\"\\nFull statespace search for:\\n\");",
+ "#endif",
+ "#endif",
+ "#ifdef EVENT_TRACE",
+ "#ifdef NEGATED_TRACE",
+ " printf(\"\tnotrace assertion \t+\\n\");",
+ "#else",
+ " printf(\"\ttrace assertion \t+\\n\");",
+ "#endif",
+ "#endif",
+ "#ifdef VERI",
+ " printf(\"\tnever claim \t+\\n\");",
+ " printf(\"\tassertion violations\t\");",
+ " if (noasserts)",
+ " printf(\"- (disabled by -A flag)\\n\");",
+ " else",
+ " printf(\"+ (if within scope of claim)\\n\");",
+ "#else",
+ "#ifdef NOCLAIM",
+ " printf(\"\tnever claim \t- (not selected)\\n\");",
+ "#else",
+ " printf(\"\tnever claim \t- (none specified)\\n\");",
+ "#endif",
+ " printf(\"\tassertion violations\t\");",
+ " if (noasserts)",
+ " printf(\"- (disabled by -A flag)\\n\");",
+ " else",
+ " printf(\"+\\n\");",
+ "#endif",
+ "#ifndef SAFETY",
+ "#ifdef NP",
+ " printf(\"\tnon-progress cycles \t\");",
+ "#else",
+ " printf(\"\tacceptance cycles \t\");",
+ "#endif",
+ " if (a_cycles)",
+ " printf(\"+ (fairness %%sabled)\\n\",",
+ " fairness?\"en\":\"dis\");",
+ " else printf(\"- (not selected)\\n\");",
+ "#else",
+ " printf(\"\tcycle checks \t- (disabled by -DSAFETY)\\n\");",
+ "#endif",
+ "#ifdef VERI",
+ " printf(\"\tinvalid end states\t- \");",
+ " printf(\"(disabled by \");",
+ " if (noends)",
+ " printf(\"-E flag)\\n\\n\");",
+ " else",
+ " printf(\"never claim)\\n\\n\");",
+ "#else",
+ " printf(\"\tinvalid end states\t\");",
+ " if (noends)",
+ " printf(\"- (disabled by -E flag)\\n\\n\");",
+ " else",
+ " printf(\"+\\n\\n\");",
+ "#endif",
+ " printf(\"State-vector %%d byte, depth reached %%ld\", hmax,",
+ "#if NCORE>1",
+ " (nr_handoffs * z_handoff) +",
+ "#endif",
+ " mreached);",
+ " printf(\", errors: %%d\\n\", errors);",
+ " fflush(stdout);",
+ "#ifdef MA",
+ " if (done)",
+ " { extern void dfa_stats(void);",
+ " if (maxgs+a_cycles+2 < MA)",
+ " printf(\"MA stats: -DMA=%%d is sufficient\\n\",",
+ " maxgs+a_cycles+2);",
+ " dfa_stats();",
+ " }",
+ "#endif",
+ " wrap_stats();",
+ "#ifdef CHECK",
+ " printf(\"stackframes: %%d/%%d\\n\\n\", smax, svmax);",
+ " printf(\"stats: fa %%d, fh %%d, zh %%d, zn %%d - \",",
+ " Fa, Fh, Zh, Zn);",
+ " printf(\"check %%d holds %%d\\n\", Ccheck, Cholds);",
+ " printf(\"stack stats: puts %%d, probes %%d, zaps %%d\\n\",",
+ " PUT, PROBE, ZAPS);",
+ "#else",
+ " printf(\"\\n\");",
+ "#endif",
+ "",
+ "#if defined(BITSTATE) || !defined(NOCOMP)",
+ " nr1 = (nstates-nShadow)*",
+ " (double)(hmax+sizeof(struct H_el)-sizeof(unsigned));",
+ "#ifdef BFS",
+ " nr2 = 0.0;",
+ "#else",
+ " nr2 = (double) ((maxdepth+3)*sizeof(Trail));",
+ "#endif",
+
+ "#ifndef BITSTATE",
+ "#if !defined(MA) || defined(COLLAPSE)",
+ " nr3 = (double) (ONE_L<<ssize)*sizeof(struct H_el *);",
+ "#endif",
+ "#else",
+#ifndef POWOW
+ " if (udmem)",
+ " nr3 = (double) (udmem);",
+ " else",
+#endif
+ " nr3 = (double) (ONE_L<<(ssize-3));",
+ "#ifdef CNTRSTACK",
+ " nr5 = (double) (ONE_L<<(ssize-3));",
+ "#endif",
+ "#ifdef FULLSTACK",
+ " nr5 = (double) (maxdepth*sizeof(struct H_el *));",
+ "#endif",
+ "#endif",
+ " nr4 = (double) (svmax * (sizeof(Svtack) + hmax))",
+ " + (double) (smax * (sizeof(Stack) + Maxbody));",
+ "#ifndef MA",
+ " if (mverbose || memcnt < nr1+nr2+nr3+nr4+nr5)",
+ "#endif",
+ " { double remainder = memcnt;",
+ " double tmp_nr = memcnt-nr3-nr4-(nr2-fragment)-nr5;",
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ " tmp_nr -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;",
+ "#endif",
+ " if (tmp_nr < 0.0) tmp_nr = 0.;",
+ " printf(\"Stats on memory usage (in Megabytes):\\n\");",
+ " printf(\"%%9.3f\tequivalent memory usage for states\",",
+ " nr1/1048576.); /* 1024*1024=1048576 */",
+ " printf(\" (stored*(State-vector + overhead))\\n\");",
+ " #if NCORE>1 && !defined(WIN32) && !defined(WIN64)",
+ " printf(\"%%9.3f\tshared memory reserved for state storage\\n\",",
+ " mem_reserved/1048576.);",
+ " #ifdef SEP_HEAP",
+ " printf(\"\t\tin %%d local heaps of %%7.3f MB each\\n\",",
+ " NCORE, mem_reserved/(NCORE*1048576.));",
+ " #endif",
+ " printf(\"\\n\");",
+ " #endif",
+ "#ifdef BITSTATE",
+#ifndef POWOW
+ " if (udmem)",
+ " printf(\"%%9.3f\tmemory used for hash array (-M%%ld)\\n\",",
+ " nr3/1048576., udmem/(1024L*1024L));",
+ " else",
+#endif
+ " printf(\"%%9.3f\tmemory used for hash array (-w%%d)\\n\",",
+ " nr3/1048576., ssize);",
+ " if (nr5 > 0.0)",
+ " printf(\"%%9.3f\tmemory used for bit stack\\n\",",
+ " nr5/1048576.);",
+ " remainder = remainder - nr3 - nr5;",
+ "#else",
+ " printf(\"%%9.3f\tactual memory usage for states\",",
+ " tmp_nr/1048576.);",
+ " remainder -= tmp_nr;",
+ " printf(\" (\");",
+ " if (tmp_nr > 0.)",
+ " { if (tmp_nr > nr1) printf(\"unsuccessful \");",
+ " printf(\"compression: %%.2f%%%%)\\n\",",
+ " (100.0*tmp_nr)/nr1);",
+ " } else",
+ " printf(\"less than 1k)\\n\");",
+ "#ifndef MA",
+ " if (tmp_nr > 0.)",
+ " { printf(\" \tstate-vector as stored = %%.0f byte\",",
+ " (tmp_nr)/(nstates-nShadow) -",
+ " (double) (sizeof(struct H_el) - sizeof(unsigned)));",
+ " printf(\" + %%ld byte overhead\\n\",",
+ " (long int) sizeof(struct H_el)-sizeof(unsigned));",
+ " }",
+ "#endif",
+ "#if !defined(MA) || defined(COLLAPSE)",
+ " printf(\"%%9.3f\tmemory used for hash table (-w%%d)\\n\",",
+ " nr3/1048576., ssize);",
+ " remainder -= nr3;",
+ "#endif",
+ "#endif",
+ "#ifndef BFS",
+ " printf(\"%%9.3f\tmemory used for DFS stack (-m%%ld)\\n\",",
+ " nr2/1048576., maxdepth);",
+ " remainder -= nr2;",
+ "#endif",
+ "#if NCORE>1",
+ " remainder -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;",
+ " printf(\"%%9.3f\tshared memory used for work-queues\\n\",",
+ " (GWQ_SIZE + (double) NCORE * LWQ_SIZE) /1048576.);",
+ " printf(\"\t\tin %%d queues of %%7.3f MB each\",",
+ " NCORE, (double) LWQ_SIZE /1048576.);",
+ " #ifndef NGQ",
+ " printf(\" + a global q of %%7.3f MB\\n\",",
+ " (double) GWQ_SIZE / 1048576.);",
+ " #else",
+ " printf(\"\\n\");",
+ " #endif",
+ " #endif",
+ " if (remainder - fragment > 1048576.)",
+ " printf(\"%%9.3f\tother (proc and chan stacks)\\n\",",
+ " (remainder-fragment)/1048576.);",
+ " if (fragment > 1048576.)",
+ " printf(\"%%9.3f\tmemory lost to fragmentation\\n\",",
+ " fragment/1048576.);",
+ " printf(\"%%9.3f\ttotal actual memory usage\\n\\n\",",
+ " memcnt/1048576.);",
+ " }",
+ "#ifndef MA",
+ " else",
+ "#endif",
+ "#endif",
+ "#ifndef MA",
+ " printf(\"%%9.3f\tmemory usage (Mbyte)\\n\\n\",",
+ " memcnt/1048576.);",
+ "#endif",
+ "#ifdef COLLAPSE",
+ " printf(\"nr of templates: [ globals chans procs ]\\n\");",
+ " printf(\"collapse counts: [ \");",
+ " { int i; for (i = 0; i < 256+2; i++)",
+ " if (ncomps[i] != 0)",
+ " printf(\"%%d \", ncomps[i]);",
+ " printf(\"]\\n\");",
+ " }",
+ "#endif",
+
+ " if ((done || verbose) && !no_rck) do_reach();",
+ "#ifdef PEG",
+ " { int i;",
+ " printf(\"\\nPeg Counts (transitions executed):\\n\");",
+ " for (i = 1; i < NTRANS; i++)",
+ " { if (peg[i]) putpeg(i, peg[i]);",
+ " } }",
+ "#endif",
+ "#ifdef VAR_RANGES",
+ " dumpranges();",
+ "#endif",
+ "#ifdef SVDUMP",
+ " if (vprefix > 0) close(svfd);",
+ "#endif",
+ "#ifdef LOOPSTATE",
+ " printf(\"%%g loopstates hit\\n\", cnt_loops);",
+ "#endif",
+ "#ifdef NSUCC",
+ " dump_succ();",
+ "#endif",
+ "#if NCORE>1 && defined(T_ALERT)",
+ " crash_report();",
+ "#endif",
+ " pan_exit(0);",
+ "}\n",
+ "void",
+ "stopped(int arg)",
+ "{ printf(\"Interrupted\\n\");",
+ "#if NCORE>1",
+ " was_interrupted = 1;",
+ "#endif",
+ " wrapup();",
+ " pan_exit(0);",
+ "}",
+ "",
+ "#ifdef SFH",
+ "/*",
+ " * super fast hash, based on Paul Hsieh's function",
+ " * http://www.azillionmonkeys.com/qed/hash.html",
+ " */",
+ "#include <stdint.h>", /* for uint32_t etc */
+ " #undef get16bits",
+ " #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \\",
+ " || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)",
+ " #define get16bits(d) (*((const uint16_t *) (d)))",
+ " #endif",
+ "",
+ " #ifndef get16bits",
+ " #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\\",
+ " +(uint32_t)(((const uint8_t *)(d))[0]) )",
+ " #endif",
+ "",
+ "void",
+ "d_sfh(const char *s, int len)",
+ "{ uint32_t h = len, tmp;",
+ " int rem;",
+ "",
+ " rem = len & 3;",
+ " len >>= 2;",
+ "",
+ " for ( ; len > 0; len--)",
+ " { h += get16bits(s);",
+ " tmp = (get16bits(s+2) << 11) ^ h;",
+ " h = (h << 16) ^ tmp;",
+ " s += 2*sizeof(uint16_t);",
+ " h += h >> 11;",
+ " }",
+ " switch (rem) {",
+ " case 3: h += get16bits(s);",
+ " h ^= h << 16;",
+ " h ^= s[sizeof(uint16_t)] << 18;",
+ " h += h >> 11;",
+ " break;",
+ " case 2: h += get16bits(s);",
+ " h ^= h << 11;",
+ " h += h >> 17;",
+ " break;",
+ " case 1: h += *s;",
+ " h ^= h << 10;",
+ " h += h >> 1;",
+ " break;",
+ " }",
+ " h ^= h << 3;",
+ " h += h >> 5;",
+ " h ^= h << 4;",
+ " h += h >> 17;",
+ " h ^= h << 25;",
+ " h += h >> 6;",
+ "",
+ " K1 = h;",
+ "}",
+ "#endif", /* SFH */
+ "",
+ "#include <stdint.h>", /* uint32_t etc. */
+ "#if defined(HASH64) || defined(WIN64)",
+ "/* 64-bit Jenkins hash, 1997",
+ " * http://burtleburtle.net/bob/c/lookup8.c",
+ " */",
+ "#define mix(a,b,c) \\",
+ "{ a -= b; a -= c; a ^= (c>>43); \\",
+ " b -= c; b -= a; b ^= (a<<9); \\",
+ " c -= a; c -= b; c ^= (b>>8); \\",
+ " a -= b; a -= c; a ^= (c>>38); \\",
+ " b -= c; b -= a; b ^= (a<<23); \\",
+ " c -= a; c -= b; c ^= (b>>5); \\",
+ " a -= b; a -= c; a ^= (c>>35); \\",
+ " b -= c; b -= a; b ^= (a<<49); \\",
+ " c -= a; c -= b; c ^= (b>>11); \\",
+ " a -= b; a -= c; a ^= (c>>12); \\",
+ " b -= c; b -= a; b ^= (a<<18); \\",
+ " c -= a; c -= b; c ^= (b>>22); \\",
+ "}",
+ "#else",
+ "/* 32-bit Jenkins hash, 2006",
+ " * http://burtleburtle.net/bob/c/lookup3.c",
+ " */",
+ "#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))",
+ "",
+ "#define mix(a,b,c) \\",
+ "{ a -= c; a ^= rot(c, 4); c += b; \\",
+ " b -= a; b ^= rot(a, 6); a += c; \\",
+ " c -= b; c ^= rot(b, 8); b += a; \\",
+ " a -= c; a ^= rot(c,16); c += b; \\",
+ " b -= a; b ^= rot(a,19); a += c; \\",
+ " c -= b; c ^= rot(b, 4); b += a; \\",
+ "}",
+ "",
+ "#define final(a,b,c) \\",
+ "{ c ^= b; c -= rot(b,14); \\",
+ " a ^= c; a -= rot(c,11); \\",
+ " b ^= a; b -= rot(a,25); \\",
+ " c ^= b; c -= rot(b,16); \\",
+ " a ^= c; a -= rot(c,4); \\",
+ " b ^= a; b -= rot(a,14); \\",
+ " c ^= b; c -= rot(b,24); \\",
+ "}",
+ "#endif",
+ "",
+ "void",
+ "d_hash(uchar *kb, int nbytes)",
+ "{ uint8_t *bp;",
+ "#if defined(HASH64) || defined(WIN64)",
+ " uint64_t a = 0, b, c, n;",
+ " uint64_t *k = (uint64_t *) kb;",
+ "#else",
+ " uint32_t a, b, c, n;",
+ " uint32_t *k = (uint32_t *) kb;",
+ "#endif",
+ " /* extend to multiple of words, if needed */",
+ " n = nbytes/WS; /* nr of words */",
+ " a = nbytes - (n*WS);",
+ " if (a > 0)",
+ " { n++;",
+ " bp = kb + nbytes;",
+ " switch (a) {",
+ " case 3: *bp++ = 0; /* fall thru */",
+ " case 2: *bp++ = 0; /* fall thru */",
+ " case 1: *bp = 0;",
+ " case 0: break;",
+ " } }",
+ "#if defined(HASH64) || defined(WIN64)",
+ " b = HASH_CONST[HASH_NR];",
+ " c = 0x9e3779b97f4a7c13LL; /* arbitrary value */",
+ " while (n >= 3)",
+ " { a += k[0];",
+ " b += k[1];",
+ " c += k[2];",
+ " mix(a,b,c);",
+ " n -= 3;",
+ " k += 3;",
+ " }",
+ " c += (((uint64_t) nbytes)<<3);",
+ " switch (n) {",
+ " case 2: b += k[1];",
+ " case 1: a += k[0];",
+ " case 0: break;",
+ " }",
+ " mix(a,b,c);",
+ "#else", /* 32 bit version: */
+ " a = c = 0xdeadbeef + (n<<2);",
+ " b = HASH_CONST[HASH_NR];",
+ " while (n > 3)",
+ " { a += k[0];",
+ " b += k[1];",
+ " c += k[2];",
+ " mix(a,b,c);",
+ " n -= 3;",
+ " k += 3;",
+ " }",
+ " switch (n) { ",
+ " case 3: c += k[2];",
+ " case 2: b += k[1];",
+ " case 1: a += k[0];",
+ " case 0: break;",
+ " }",
+ " final(a,b,c);",
+ "#endif",
+ " j1 = c&nmask; j3 = a&7; /* 1st bit */",
+ " j2 = b&nmask; j4 = (a>>3)&7; /* 2nd bit */",
+ " K1 = c; K2 = b;",
+ "}",
+ "",
+ "void",
+ "s_hash(uchar *cp, int om)",
+ "{",
+ "#if defined(SFH)",
+ " d_sfh((const char *) cp, om); /* sets K1 */",
+ "#else",
+ " d_hash(cp, om); /* sets K1 etc */",
+ "#endif",
+ "#ifdef BITSTATE",
+ " if (S_Tab == H_tab)", /* state stack in bitstate search */
+ " j1 = K1 %% omaxdepth;",
+ " else",
+ "#endif", /* if (S_Tab != H_Tab) */
+ " if (ssize < 8*WS)",
+ " j1 = K1&mask;",
+ " else",
+ " j1 = K1;",
+ "}",
+ "#ifndef RANDSTOR",
+ "int *prerand;",
+ "void",
+ "inirand(void)",
+ "{ int i;",
+ " srand(123); /* fixed startpoint */",
+ " prerand = (int *) emalloc((omaxdepth+3)*sizeof(int));",
+ " for (i = 0; i < omaxdepth+3; i++)",
+ " prerand[i] = rand();",
+ "}",
+ "int",
+ "pan_rand(void)",
+ "{ if (!prerand) inirand();",
+ " return prerand[depth];",
+ "}",
+ "#endif",
+ "",
+ "void",
+ "set_masks(void) /* 4.2.5 */",
+ "{",
+ " if (WS == 4 && ssize >= 32)",
+ " { mask = 0xffffffff;",
+ "#ifdef BITSTATE",
+ " switch (ssize) {",
+ " case 34: nmask = (mask>>1); break;",
+ " case 33: nmask = (mask>>2); break;",
+ " default: nmask = (mask>>3); break;",
+ " }",
+ "#else",
+ " nmask = mask;",
+ "#endif",
+ " } else if (WS == 8)",
+ " { mask = ((ONE_L<<ssize)-1); /* hash init */",
+ "#ifdef BITSTATE",
+ " nmask = mask>>3;",
+ "#else",
+ " nmask = mask;",
+ "#endif",
+ " } else if (WS != 4)",
+ " { fprintf(stderr, \"pan: wordsize %%ld not supported\\n\", (long int) WS);",
+ " exit(1);",
+ " } else /* WS == 4 and ssize < 32 */",
+ " { mask = ((ONE_L<<ssize)-1); /* hash init */",
+ " nmask = (mask>>3);",
+ " }",
+ "}",
+ "",
+ "static long reclaim_size;",
+ "static char *reclaim_mem;",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)",
+ "#if NCORE>1",
+ " #error cannot combine AUTO_RESIZE with NCORE>1 yet",
+ "#endif",
+ "static struct H_el **N_tab;",
+ "void",
+ "reverse_capture(struct H_el *p)",
+ "{ if (!p) return;",
+ " reverse_capture(p->nxt);",
+ " /* last element of list moves first */",
+ " /* to preserve list-order */",
+ " j2 = p->m_K1;",
+ " if (ssize < 8*WS) /* probably always true */",
+ " { j2 &= mask;",
+ " }",
+ " p->nxt = N_tab[j2];",
+ " N_tab[j2] = p;",
+ "}",
+ "void",
+ "resize_hashtable(void)",
+ "{",
+ " if (WS == 4 && ssize >= 27 - 1)",
+ " { return; /* canot increase further */",
+ " }",
+ "",
+ " ssize += 2; /* 4x size */",
+ "",
+ " printf(\"pan: resizing hashtable to -w%%d.. \", ssize);",
+ "",
+ " N_tab = (struct H_el **)",
+ " emalloc((ONE_L<<ssize)*sizeof(struct H_el *));",
+ "",
+ " set_masks(); /* they changed */",
+ "",
+ " for (j1 = 0; j1 < (ONE_L << (ssize - 2)); j1++)",
+ " { reverse_capture(H_tab[j1]);",
+ " }",
+ " reclaim_mem = (char *) H_tab;",
+ " reclaim_size = (ONE_L << (ssize - 2));",
+ " H_tab = N_tab;",
+ "",
+ " printf(\" done\\n\");",
+ "}",
+ "#endif",
+ "#if defined(ZAPH) && defined(BITSTATE)",
+ "void",
+ "zap_hashtable(void)",
+ "{ cpu_printf(\"pan: resetting hashtable\\n\");",
+ " if (udmem)",
+ " { memset(SS, 0, udmem);",
+ " } else",
+ " { memset(SS, 0, ONE_L<<(ssize-3));",
+ " }",
+ "}",
+ "#endif",
+ "",
+ "int",
+ "main(int argc, char *argv[])",
+ "{ void to_compile(void);\n",
+ " efd = stderr; /* default */",
+ "#ifdef BITSTATE",
+ " bstore = bstore_reg; /* default */",
+ "#endif",
+ "#if NCORE>1",
+ " { int i, j;",
+ " strcpy(o_cmdline, \"\");",
+ " for (j = 1; j < argc; j++)",
+ " { strcat(o_cmdline, argv[j]);",
+ " strcat(o_cmdline, \" \");",
+ " }",
+ " /* printf(\"Command Line: %%s\\n\", o_cmdline); */",
+ " if (strlen(o_cmdline) >= sizeof(o_cmdline))",
+ " { Uerror(\"option list too long\");",
+ " } }",
+ "#endif",
+ " while (argc > 1 && argv[1][0] == '-')",
+ " { switch (argv[1][1]) {",
+ "#ifndef SAFETY",
+ "#ifdef NP",
+ " case 'a': fprintf(efd, \"error: -a disabled\");",
+ " usage(efd); break;",
+ "#else",
+ " case 'a': a_cycles = 1; break;",
+ "#endif",
+ "#endif",
+ " case 'A': noasserts = 1; break;",
+ " case 'b': bounded = 1; break;",
+ "#ifdef HAS_CODE",
+ " case 'C': coltrace = 1; goto samething;",
+ "#endif",
+ " case 'c': upto = atoi(&argv[1][2]); break;",
+ " case 'd': state_tables++; break;",
+ " case 'e': every_error = 1; Nr_Trails = 1; break;",
+ " case 'E': noends = 1; break;",
+ "#ifdef SC",
+ " case 'F': if (strlen(argv[1]) > 2)",
+ " stackfile = &argv[1][2];",
+ " break;",
+ "#endif",
+ "#if !defined(SAFETY) && !defined(NOFAIR)",
+ " case 'f': fairness = 1; break;",
+ "#endif",
+ "#ifdef HAS_CODE",
+ " case 'g': gui = 1; goto samething;",
+ "#endif",
+ " case 'h': if (!argv[1][2]) usage(efd); else",
+ " HASH_NR = atoi(&argv[1][2])%%33; break;",
+ " case 'I': iterative = 2; every_error = 1; break;",
+ " case 'i': iterative = 1; every_error = 1; break;",
+ " case 'J': like_java = 1; break; /* Klaus Havelund */",
+ "#ifdef BITSTATE",
+ " case 'k': hfns = atoi(&argv[1][2]); break;",
+ "#endif",
+ "#ifdef SCHED",
+ " case 'L': sched_max = atoi(&argv[1][2]); break;",
+ "#endif",
+ "#ifndef SAFETY",
+ "#ifdef NP",
+ " case 'l': a_cycles = 1; break;",
+ "#else",
+ " case 'l': fprintf(efd, \"error: -l disabled\");",
+ " usage(efd); break;",
+ "#endif",
+ "#endif",
+#ifndef POWOW
+ "#ifdef BITSTATE",
+ " case 'M': udmem = atoi(&argv[1][2]); break;",
+ " case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break;",
+ "#else",
+ " case 'M': case 'G':",
+ " fprintf(stderr, \"-M and -G affect only -DBITSTATE\\n\");",
+ " break;",
+ "#endif",
+#endif
+ " case 'm': maxdepth = atoi(&argv[1][2]); break;",
+ " case 'n': no_rck = 1; break;",
+ " case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]);",
+ " if (argv[2][0] != '-') /* check next arg */",
+ " { trailfilename = argv[2];",
+ " argc--; argv++; /* skip next arg */",
+ " }",
+ " break;",
+ "#ifdef SVDUMP",
+ " case 'p': vprefix = atoi(&argv[1][2]); break;",
+ "#endif",
+ "#if NCORE==1",
+ " case 'Q': quota = (double) 60.0 * (double) atoi(&argv[1][2]); break;",
+ "#endif",
+ " case 'q': strict = 1; break;",
+ " case 'R': Nrun = atoi(&argv[1][2]); break;",
+ "#ifdef HAS_CODE",
+ " case 'r':",
+ "samething: readtrail = 1;",
+ " if (isdigit(argv[1][2]))",
+ " whichtrail = atoi(&argv[1][2]);",
+ " else if (argc > 2 && argv[2][0] != '-') /* check next arg */",
+ " { trailfilename = argv[2];",
+ " argc--; argv++; /* skip next arg */",
+ " }",
+ " break;",
+ " case 'S': silent = 1; goto samething;",
+ "#endif",
+ "#ifdef BITSTATE",
+ " case 's': hfns = 1; break;",
+ "#endif",
+ " case 'T': TMODE = 0444; break;",
+ " case 't': if (argv[1][2]) tprefix = &argv[1][2]; break;",
+ " case 'V': start_timer(); printf(\"Generated by %%s\\n\", SpinVersion);",
+ " to_compile(); pan_exit(2); break;",
+ " case 'v': verbose++; break;",
+ " case 'w': ssize = atoi(&argv[1][2]); break;",
+ " case 'Y': signoff = 1; break;",
+ " case 'X': efd = stdout; break;",
+ " case 'x': exclusive = 1; break;",
+ "#if NCORE>1",
+ " /* -B ip is passthru to proxy of remote ip address: */",
+ " case 'B': argc--; argv++; break;",
+ " case 'Q': worker_pids[0] = atoi(&argv[1][2]); break;",
+ " /* -Un means that the nth worker should be instantiated as a proxy */",
+ " case 'U': proxy_pid = atoi(&argv[1][2]); break;",
+ " /* -W means that this copy is started by a cluster-server as a remote */",
+ " /* this flag is passed to ./pan_proxy, which interprets it */",
+ " case 'W': remote_party++; break;",
+ " case 'Z': core_id = atoi(&argv[1][2]);",
+ " if (verbose)",
+ " { printf(\"cpu%%d: pid %%d parent %%d\\n\",",
+ " core_id, getpid(), worker_pids[0]);",
+ " }",
+ " break;",
+ " case 'z': z_handoff = atoi(&argv[1][2]); break;",
+ "#else",
+ " case 'z': break; /* ignored for single-core */",
+ "#endif",
+ " default : fprintf(efd, \"saw option -%%c\\n\", argv[1][1]); usage(efd); break;",
+ " }",
+ " argc--; argv++;",
+ " }",
+ " if (iterative && TMODE != 0666)",
+ " { TMODE = 0666;",
+ " fprintf(efd, \"warning: -T ignored when -i or -I is used\\n\");",
+ " }",
+ "#if defined(HASH32) && !defined(SFH)",
+ " if (WS > 4)",
+ " { fprintf(efd, \"strong warning: compiling -DHASH32 on a 64-bit machine\\n\");",
+ " fprintf(efd, \" without -DSFH can slow down performance a lot\\n\");",
+ " }",
+ "#endif",
+ "#if defined(WIN32) || defined(WIN64)",
+ " if (TMODE == 0666)",
+ " TMODE = _S_IWRITE | _S_IREAD;",
+ " else",
+ " TMODE = _S_IREAD;",
+ "#endif",
+ "#if NCORE>1",
+ " store_proxy_pid = proxy_pid; /* for checks in mem_file() and someone_crashed() */",
+ " if (core_id != 0) { proxy_pid = 0; }",
+ " #ifndef SEP_STATE",
+ " if (core_id == 0 && a_cycles)",
+ " { fprintf(efd, \"hint: this search may be more efficient \");",
+ " fprintf(efd, \"if pan.c is compiled -DSEP_STATE\\n\");",
+ " }",
+ " #endif",
+ " if (z_handoff < 0)",
+ " { z_handoff = 20; /* conservative default - for non-liveness checks */",
+ " }",
+ "#if defined(NGQ) || defined(LWQ_FIXED)",
+ " LWQ_SIZE = (double) (128.*1048576.);",
+ "#else",
+ " LWQ_SIZE = (double) ( z_handoff + 2.) * (double) sizeof(SM_frame);",
+ /* the added margin of +2 is not really necessary */
+ "#endif",
+ " #if NCORE>2",
+ " if (a_cycles)",
+ " { fprintf(efd, \"warning: the intended nr of cores to be used in liveness mode is 2\\n\");",
+ " #ifndef SEP_STATE",
+ " fprintf(efd, \"warning: without -DSEP_STATE there is no guarantee that all liveness violations are found\\n\");",
+ " #endif",
+ " }", /* it still works though, the later cores get states from the global q */
+ " #endif",
+ " #ifdef HAS_HIDDEN",
+ " #error cannot use hidden variables when compiling multi-core",
+ " #endif",
+ "#endif",
+ "#ifdef BITSTATE",
+ " if (hfns <= 0)",
+ " { hfns = 1;",
+ " fprintf(efd, \"warning: using -k%%d as minimal usable value\\n\", hfns);",
+ " }",
+ "#endif",
+ " omaxdepth = maxdepth;",
+ "#ifdef BITSTATE",
+ " if (WS == 4 && ssize > 34)", /* 32-bit word size */
+ " { ssize = 34;",
+ " fprintf(efd, \"warning: using -w%%d as max usable value\\n\", ssize);",
+ "/*",
+ " * -w35 would not work: 35-3 = 32 but 1^31 is the largest",
+ " * power of 2 that can be represented in an unsigned long",
+ " */",
+ " }",
+ "#else",
+ " if (WS == 4 && ssize > 27)",
+ " { ssize = 27;",
+ " fprintf(efd, \"warning: using -w%%d as max usable value\\n\", ssize);",
+ "/*",
+ " * for emalloc, the lookup table size multiplies by 4 for the pointers",
+ " * the largest power of 2 that can be represented in a ulong is 1^31",
+ " * hence the largest number of lookup table slots is 31-4 = 27",
+ " */",
+ " }",
+
+ "#endif",
+ "#ifdef SC",
+ " hiwater = HHH = maxdepth-10;",
+ " DDD = HHH/2;",
+ " if (!stackfile)",
+ " { stackfile = (char *) emalloc(strlen(PanSource)+4+1);",
+ " sprintf(stackfile, \"%%s._s_\", PanSource);",
+ " }",
+ " if (iterative)",
+ " { fprintf(efd, \"error: cannot use -i or -I with -DSC\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#endif",
+
+ "#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA)",
+ " #warning -DR_XPT and -DW_XPT assume -DMA (ignored)",
+ "#endif",
+
+ " if (iterative && a_cycles)",
+ " fprintf(efd, \"warning: -i or -I work for safety properties only\\n\");",
+
+ "#ifdef BFS",
+ " #ifdef SC",
+ " #error -DBFS not compatible with -DSC",
+ " #endif",
+ " #ifdef HAS_LAST",
+ " #error -DBFS not compatible with _last",
+ " #endif",
+ " #ifdef HAS_STACK",
+ " #error cannot use c_track UnMatched with BFS",
+ " #endif",
+ " #ifdef REACH",
+ " #warning -DREACH is redundant when -DBFS is used",
+ " #endif",
+ "#endif",
+ "#if defined(MERGED) && defined(PEG)",
+ " #error to use -DPEG use: spin -o3 -a",
+ "#endif",
+ "#ifdef HC",
+ " #ifdef SFH", /* cannot happen -- undef-ed in this case */
+ " #error cannot combine -DHC and -DSFH",
+ " /* use of NOCOMP is the real reason */",
+ " #else",
+ " #ifdef NOCOMP",
+ " #error cannot combine -DHC and -DNOCOMP",
+ " #endif",
+ " #endif",
+ " #ifdef BITSTATE",
+ " #error cannot combine -DHC and -DBITSTATE",
+ " #endif",
+ "#endif",
+ "#if defined(SAFETY) && defined(NP)",
+ " #error cannot combine -DNP and -DBFS or -DSAFETY",
+ "#endif",
+ "#ifdef MA",
+ " #ifdef BITSTATE",
+ " #error cannot combine -DMA and -DBITSTATE",
+ " #endif",
+ " #if MA <= 0",
+ " #error usage: -DMA=N with N > 0 and N < VECTORSZ",
+ " #endif",
+ "#endif",
+ "#ifdef COLLAPSE",
+ " #ifdef BITSTATE",
+ " #error cannot combine -DBITSTATE and -DCOLLAPSE",
+ " #endif",
+ " #ifdef SFH",
+ " #error cannot combine -DCOLLAPSE and -DSFH",
+ " /* use of NOCOMP is the real reason */",
+ " #else",
+ " #ifdef NOCOMP",
+ " #error cannot combine -DCOLLAPSE and -DNOCOMP",
+ " #endif",
+ " #endif",
+ "#endif",
+ " if (maxdepth <= 0 || ssize <= 1) usage(efd);",
+ "#if SYNC>0 && !defined(NOREDUCE)",
+ " if (a_cycles && fairness)",
+ " { fprintf(efd, \"error: p.o. reduction not compatible with \");",
+ " fprintf(efd, \"fairness (-f) in models\\n\");",
+ " fprintf(efd, \" with rendezvous operations: \");",
+ " fprintf(efd, \"recompile with -DNOREDUCE\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#endif",
+ "#if defined(REM_VARS) && !defined(NOREDUCE)",
+ " #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE)",
+ "#endif",
+ "#if defined(NOCOMP) && !defined(BITSTATE)",
+ " if (a_cycles)",
+ " { fprintf(efd, \"error: use of -DNOCOMP voids -l and -a\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#endif",
+
+ "#ifdef MEMLIM",
+ " memlim = ((double) MEMLIM) * (double) (1<<20); /* size in Mbyte */",
+ "#endif",
+
+ "#ifndef BITSTATE",
+ " if (Nrun > 1) HASH_NR = Nrun - 1;",
+ "#endif",
+ " if (Nrun < 1 || Nrun > 32)",
+ " { fprintf(efd, \"error: invalid arg for -R\\n\");",
+ " usage(efd);",
+ " }",
+ "#ifndef SAFETY",
+ " if (fairness && !a_cycles)",
+ " { fprintf(efd, \"error: -f requires -a or -l\\n\");",
+ " usage(efd);",
+ " }",
+ " #if ACCEPT_LAB==0",
+ " if (a_cycles)",
+ " { fprintf(efd, \"error: no accept labels defined \");",
+ " fprintf(efd, \"in model (for option -a)\\n\");",
+ " usage(efd);",
+ " }",
+ " #endif",
+ "#endif",
+ "#ifndef NOREDUCE",
+ " #ifdef HAS_ENABLED",
+ " #error use of enabled() requires -DNOREDUCE",
+ " #endif",
+ " #ifdef HAS_PCVALUE",
+ " #error use of pcvalue() requires -DNOREDUCE",
+ " #endif",
+ " #ifdef HAS_BADELSE",
+ " #error use of 'else' combined with i/o stmnts requires -DNOREDUCE",
+ " #endif",
+ " #ifdef HAS_LAST",
+ " #error use of _last requires -DNOREDUCE",
+ " #endif",
+ "#endif",
+
+ "#if SYNC>0 && !defined(NOREDUCE)",
+ " #ifdef HAS_UNLESS",
+ " fprintf(efd, \"warning: use of a rendezvous stmnts in the escape\\n\");",
+ " fprintf(efd, \"\tof an unless clause, if present, could make p.o. reduction\\n\");",
+ " fprintf(efd, \"\tinvalid (use -DNOREDUCE to avoid this)\\n\");",
+ " #ifdef BFS",
+ " fprintf(efd, \"\t(this type of rv is also not compatible with -DBFS)\\n\");",
+ " #endif",
+ " #endif",
+ "#endif",
+ "#if SYNC>0 && defined(BFS)",
+ " #warning use of rendezvous with BFS does not preserve all invalid endstates",
+ "#endif",
+ "#if !defined(REACH) && !defined(BITSTATE)",
+ " if (iterative != 0 && a_cycles == 0)",
+ " { fprintf(efd, \"warning: -i and -I need -DREACH to work accurately\\n\");",
+ " }",
+ "#endif",
+ "#if defined(BITSTATE) && defined(REACH)",
+ " #warning -DREACH is voided by -DBITSTATE",
+ "#endif",
+ "#if defined(MA) && defined(REACH)",
+ " #warning -DREACH is voided by -DMA",
+ "#endif",
+ "#if defined(FULLSTACK) && defined(CNTRSTACK)",
+ " #error cannot combine -DFULLSTACK and -DCNTRSTACK",
+ "#endif",
+ "#if defined(VERI)",
+ " #if ACCEPT_LAB>0",
+ " #ifndef BFS",
+ " if (!a_cycles",
+ " #ifdef HAS_CODE",
+ " && !readtrail",
+ " #endif",
+ " #if NCORE>1",
+ " && core_id == 0",
+ " #endif",
+ " && !state_tables)",
+ " { fprintf(efd, \"warning: never claim + accept labels \");",
+ " fprintf(efd, \"requires -a flag to fully verify\\n\");",
+ " }",
+ " #else",
+ " if (!state_tables",
+ " #ifdef HAS_CODE",
+ " && !readtrail",
+ " #endif",
+ " )",
+ " { fprintf(efd, \"warning: verification in BFS mode \");",
+ " fprintf(efd, \"is restricted to safety properties\\n\");",
+ " }",
+ " #endif",
+ " #endif",
+ "#endif",
+ "#ifndef SAFETY",
+ " if (!a_cycles",
+ " #ifdef HAS_CODE",
+ " && !readtrail",
+ " #endif",
+ " #if NCORE>1",
+ " && core_id == 0",
+ " #endif",
+ " && !state_tables)",
+ " { fprintf(efd, \"hint: this search is more efficient \");",
+ " fprintf(efd, \"if pan.c is compiled -DSAFETY\\n\");",
+ " }",
+ " #ifndef NOCOMP",
+ " if (!a_cycles)",
+ " { S_A = 0;",
+ " } else",
+ " { if (!fairness)",
+ " { S_A = 1; /* _a_t */",
+ " #ifndef NOFAIR",
+ " } else /* _a_t and _cnt[NFAIR] */",
+ " { S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2;",
+ " /* -2 because first two uchars in now are masked */",
+ " #endif",
+ " } }",
+ " #endif",
+ "#endif",
+ " signal(SIGINT, stopped);",
+ " set_masks();",
+ "#ifdef BFS",
+ " trail = (Trail *) emalloc(6*sizeof(Trail));",
+ " trail += 3;",
+ "#else",
+ " trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail));",
+ " trail++; /* protect trpt-1 refs at depth 0 */",
+ "#endif",
+ "#ifdef SVDUMP",
+ " if (vprefix > 0)",
+ " { char nm[64];",
+ " sprintf(nm, \"%%s.svd\", PanSource);",
+ " if ((svfd = creat(nm, TMODE)) < 0)",
+ " { fprintf(efd, \"couldn't create %%s\\n\", nm);",
+ " vprefix = 0;",
+ " } }",
+ "#endif",
+ "#ifdef RANDSTOR",
+ " srand(123);",
+ "#endif",
+ "#if SYNC>0 && ASYNC==0",
+ " set_recvs();",
+ "#endif",
+ " run();",
+ " done = 1;",
+ " wrapup();",
+ " return 0;",
+ "}", /* end of main() */
+ "",
+ "void",
+ "usage(FILE *fd)",
+ "{",
+ " fprintf(fd, \"%%s\\n\", SpinVersion);",
+ " fprintf(fd, \"Valid Options are:\\n\");",
+ "#ifndef SAFETY",
+ "#ifdef NP",
+ " fprintf(fd, \"\t-a -> is disabled by -DNP \");",
+ " fprintf(fd, \"(-DNP compiles for -l only)\\n\");",
+ "#else",
+ " fprintf(fd, \"\t-a find acceptance cycles\\n\");",
+ "#endif",
+ "#else",
+ " fprintf(fd, \"\t-a,-l,-f -> are disabled by -DSAFETY\\n\");",
+ "#endif",
+ " fprintf(fd, \"\t-A ignore assert() violations\\n\");",
+ " fprintf(fd, \"\t-b consider it an error to exceed the depth-limit\\n\");",
+ " fprintf(fd, \"\t-cN stop at Nth error \");",
+ " fprintf(fd, \"(defaults to -c1)\\n\");",
+ " fprintf(fd, \"\t-d print state tables and stop\\n\");",
+ " fprintf(fd, \"\t-e create trails for all errors\\n\");",
+ " fprintf(fd, \"\t-E ignore invalid end states\\n\");",
+ "#ifdef SC",
+ " fprintf(fd, \"\t-Ffile use 'file' to store disk-stack\\n\");",
+ "#endif",
+ "#ifndef NOFAIR",
+ " fprintf(fd, \"\t-f add weak fairness (to -a or -l)\\n\");",
+ "#endif",
+ " fprintf(fd, \"\t-hN use different hash-seed N:1..32\\n\");",
+ " fprintf(fd, \"\t-i search for shortest path to error\\n\");",
+ " fprintf(fd, \"\t-I like -i, but approximate and faster\\n\");",
+ " fprintf(fd, \"\t-J reverse eval order of nested unlesses\\n\");",
+ "#ifdef BITSTATE",
+ " fprintf(fd, \"\t-kN set N bits per state (defaults to 3)\\n\");",
+ "#endif",
+ "#ifdef SCHED",
+ " fprintf(fd, \"\t-LN set scheduling restriction to N (default 10)\\n\");",
+ "#endif",
+ "#ifndef SAFETY",
+ "#ifdef NP",
+ " fprintf(fd, \"\t-l find non-progress cycles\\n\");",
+ "#else",
+ " fprintf(fd, \"\t-l find non-progress cycles -> \");",
+ " fprintf(fd, \"disabled, requires \");",
+ " fprintf(fd, \"compilation with -DNP\\n\");",
+ "#endif",
+ "#endif",
+#ifndef POWOW
+ "#ifdef BITSTATE",
+ " fprintf(fd, \"\t-MN use N Megabytes for bitstate hash array\\n\");",
+ " fprintf(fd, \"\t-GN use N Gigabytes for bitstate hash array\\n\");",
+ "#endif",
+#endif
+ " fprintf(fd, \"\t-mN max depth N steps (default=10k)\\n\");",
+ " fprintf(fd, \"\t-n no listing of unreached states\\n\");",
+ "#ifdef SVDUMP",
+ " fprintf(fd, \"\t-pN create svfile (save N bytes per state)\\n\");",
+ "#endif",
+ " fprintf(fd, \"\t-QN set time-limit on execution of N minutes\\n\");",
+ " fprintf(fd, \"\t-q require empty chans in valid end states\\n\");",
+ "#ifdef HAS_CODE",
+ " fprintf(fd, \"\t-r read and execute trail - can add -v,-n,-PN,-g,-C\\n\");",
+ " fprintf(fd, \"\t-rN read and execute N-th error trail\\n\");",
+ " fprintf(fd, \"\t-C read and execute trail - columnated output (can add -v,-n)\\n\");",
+ " fprintf(fd, \"\t-PN read and execute trail - restrict trail output to proc N\\n\");",
+ " fprintf(fd, \"\t-g read and execute trail + msc gui support\\n\");",
+ " fprintf(fd, \"\t-S silent replay: only user defined printfs show\\n\");",
+ "#endif",
+ "#ifdef BITSTATE",
+ " fprintf(fd, \"\t-RN repeat run Nx with N \");",
+ " fprintf(fd, \"[1..32] independent hash functions\\n\");",
+ " fprintf(fd, \"\t-s same as -k1 (single bit per state)\\n\");",
+ "#endif",
+ " fprintf(fd, \"\t-T create trail files in read-only mode\\n\");",
+ " fprintf(fd, \"\t-tsuf replace .trail with .suf on trailfiles\\n\");",
+ " fprintf(fd, \"\t-V print SPIN version number\\n\");",
+ " fprintf(fd, \"\t-v verbose -- filenames in unreached state listing\\n\");",
+ " fprintf(fd, \"\t-wN hashtable of 2^N entries \");",
+ " fprintf(fd, \"(defaults to -w%%d)\\n\", ssize);",
+ " fprintf(fd, \"\t-x do not overwrite an existing trail file\\n\");",
+ "#if NCORE>1",
+ " fprintf(fd, \"\t-zN handoff states below depth N to 2nd cpu (multi_core)\\n\");",
+ "#endif",
+ "#ifdef HAS_CODE",
+ " fprintf(fd, \"\\n\toptions -r, -C, -PN, -g, and -S can optionally be followed by\\n\");",
+ " fprintf(fd, \"\ta filename argument, as in \'-r filename\', naming the trailfile\\n\");",
+ "#endif",
+ "#if NCORE>1",
+ " multi_usage(fd);",
+ "#endif",
+ " exit(1);",
+ "}",
+ "",
+ "char *",
+ "Malloc(unsigned long n)",
+ "{ char *tmp;",
+ "#ifdef MEMLIM",
+ " if (memcnt+ (double) n > memlim) goto err;",
+ "#endif",
+"#if 1",
+ " tmp = (char *) malloc(n);",
+ " if (!tmp)",
+"#else",
+ /* on linux machines, a large amount of memory is set aside
+ * for malloc, whether it is used or not
+ * using sbrk would make this memory arena inaccessible
+ * the reason for using sbrk was originally to provide a
+ * small additional speedup (since this memory is never released)
+ */
+ " tmp = (char *) sbrk(n);",
+ " if (tmp == (char *) -ONE_L)",
+"#endif",
+ " {",
+ "#ifdef MEMLIM",
+ "err:",
+ "#endif",
+ " printf(\"pan: out of memory\\n\");",
+ "#ifdef MEMLIM",
+ " printf(\"\t%%g bytes used\\n\", memcnt);",
+ " printf(\"\t%%g bytes more needed\\n\", (double) n);",
+ " printf(\"\t%%g bytes limit\\n\",",
+ " memlim);",
+ "#endif",
+ "#ifdef COLLAPSE",
+ " printf(\"hint: to reduce memory, recompile with\\n\");",
+ "#ifndef MA",
+ " printf(\" -DMA=%%d # better/slower compression, or\\n\", hmax);",
+ "#endif",
+ " printf(\" -DBITSTATE # supertrace, approximation\\n\");",
+ "#else",
+ "#ifndef BITSTATE",
+ " printf(\"hint: to reduce memory, recompile with\\n\");",
+ "#ifndef HC",
+ " printf(\" -DCOLLAPSE # good, fast compression, or\\n\");",
+ "#ifndef MA",
+ " printf(\" -DMA=%%d # better/slower compression, or\\n\", hmax);",
+ "#endif",
+ " printf(\" -DHC # hash-compaction, approximation\\n\");",
+ "#endif",
+ " printf(\" -DBITSTATE # supertrace, approximation\\n\");",
+ "#endif",
+ "#endif",
+ "#if NCORE>1",
+ " #ifdef FULL_TRAIL",
+ " printf(\" omit -DFULL_TRAIL or use pan -c0 to reduce memory\\n\");",
+ " #endif",
+ " #ifdef SEP_STATE",
+ " printf(\"hint: to reduce memory, recompile without\\n\");",
+ " printf(\" -DSEP_STATE # may be faster, but uses more memory\\n\");",
+ " #endif",
+ "#endif",
+ " wrapup();",
+ " }",
+ " memcnt += (double) n;",
+ " return tmp;",
+ "}",
+ "",
+ "#define CHUNK (100*VECTORSZ)",
+ "",
+ "char *",
+ "emalloc(unsigned long n) /* never released or reallocated */",
+ "{ char *tmp;",
+ " if (n == 0)",
+ " return (char *) NULL;",
+ " if (n&(sizeof(void *)-1)) /* for proper alignment */",
+ " n += sizeof(void *)-(n&(sizeof(void *)-1));",
+ " if ((unsigned long) left < n)", /* was: (left < (long)n) */
+ " { grow = (n < CHUNK) ? CHUNK : n;",
+#if 1
+ " have = Malloc(grow);",
+#else
+ " /* gcc's sbrk can give non-aligned result */",
+ " grow += sizeof(void *); /* allow realignment */",
+ " have = Malloc(grow);",
+ " if (((unsigned) have)&(sizeof(void *)-1))",
+ " { have += (long) (sizeof(void *) ",
+ " - (((unsigned) have)&(sizeof(void *)-1)));",
+ " grow -= sizeof(void *);",
+ " }",
+#endif
+ " fragment += (double) left;",
+ " left = grow;",
+ " }",
+ " tmp = have;",
+ " have += (long) n;",
+ " left -= (long) n;",
+ " memset(tmp, 0, n);",
+ " return tmp;",
+ "}",
+
+ "void",
+ "Uerror(char *str)",
+ "{ /* always fatal */",
+ " uerror(str);",
+ "#if NCORE>1",
+ " sudden_stop(\"Uerror\");",
+ "#endif",
+ " wrapup();",
+ "}\n",
+ "#if defined(MA) && !defined(SAFETY)",
+ "int",
+ "Unwind(void)",
+ "{ Trans *t; uchar ot, _m; int tt; short II;",
+ "#ifdef VERBOSE",
+ " int i;",
+ "#endif",
+ " uchar oat = now._a_t;",
+ " now._a_t &= ~(1|16|32);",
+ " memcpy((char *) &comp_now, (char *) &now, vsize);",
+ " now._a_t = oat;",
+ "Up:",
+ "#ifdef SC",
+ " trpt = getframe(depth);",
+ "#endif",
+ "#ifdef VERBOSE",
+ " printf(\"%%d State: \", depth);",
+ " for (i = 0; i < vsize; i++) printf(\"%%d%%s,\",",
+ " ((char *)&now)[i], Mask[i]?\"*\":\"\");",
+ " printf(\"\\n\");",
+ "#endif",
+ "#ifndef NOFAIR",
+ " if (trpt->o_pm&128) /* fairness alg */",
+ " { now._cnt[now._a_t&1] = trpt->bup.oval;",
+ " depth--;",
+ "#ifdef SC",
+ " trpt = getframe(depth);",
+ "#else",
+ " trpt--;",
+ "#endif",
+ " goto Q999;",
+ " }",
+ "#endif",
+ "#ifdef HAS_LAST",
+ "#ifdef VERI",
+ " { int d; Trail *trl;",
+ " now._last = 0;",
+ " for (d = 1; d < depth; d++)",
+ " { trl = getframe(depth-d); /* was trl = (trpt-d); */",
+ " if (trl->pr != 0)",
+ " { now._last = trl->pr - BASE;",
+ " break;",
+ " } } }",
+ "#else",
+ " now._last = (depth<1)?0:(trpt-1)->pr;",
+ "#endif",
+ "#endif",
+ "#ifdef EVENT_TRACE",
+ " now._event = trpt->o_event;",
+ "#endif",
+ " if ((now._a_t&1) && depth <= A_depth)",
+ " { now._a_t &= ~(1|16|32);",
+ " if (fairness) now._a_t |= 2; /* ? */",
+ " A_depth = 0;",
+ " goto CameFromHere; /* checkcycles() */",
+ " }",
+ " t = trpt->o_t;",
+ " ot = trpt->o_ot; II = trpt->pr;",
+ " tt = trpt->o_tt; this = pptr(II);",
+ " _m = do_reverse(t, II, trpt->o_m);",
+ "#ifdef VERBOSE",
+ " printf(\"%%3d: proc %%d \", depth, II);",
+ " printf(\"reverses %%d, %%d to %%d,\",",
+ " t->forw, tt, t->st);",
+ " printf(\" %%s [abit=%%d,adepth=%%d,\", ",
+ " t->tp, now._a_t, A_depth);",
+ " printf(\"tau=%%d,%%d] <unwind>\\n\", ",
+ " trpt->tau, (trpt-1)->tau);",
+ "#endif",
+ " depth--;",
+ "#ifdef SC",
+ " trpt = getframe(depth);",
+ "#else",
+ " trpt--;",
+ "#endif",
+ " /* reached[ot][t->st] = 1; 3.4.13 */",
+ " ((P0 *)this)->_p = tt;",
+ "#ifndef NOFAIR",
+ " if ((trpt->o_pm&32))",
+ " {",
+ "#ifdef VERI",
+ " if (now._cnt[now._a_t&1] == 0)",
+ " now._cnt[now._a_t&1] = 1;",
+ "#endif",
+ " now._cnt[now._a_t&1] += 1;",
+ " }",
+ "Q999:",
+ " if (trpt->o_pm&8)",
+ " { now._a_t &= ~2;",
+ " now._cnt[now._a_t&1] = 0;",
+ " }",
+ " if (trpt->o_pm&16)",
+ " now._a_t |= 2;",
+ "#endif",
+ "CameFromHere:",
+ " if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0)",
+ " return depth;",
+ " if (depth > 0) goto Up;",
+ " return 0;",
+ "}",
+ "#endif",
+ "static char unwinding;",
+ "void",
+ "uerror(char *str)",
+ "{ static char laststr[256];",
+ " int is_cycle;",
+ "",
+ " if (unwinding) return; /* 1.4.2 */",
+ " if (strncmp(str, laststr, 254))",
+ "#if NCORE>1",
+ " cpu_printf(\"pan: %%s (at depth %%ld)\\n\", str,",
+ "#else",
+ " printf(\"pan: %%s (at depth %%ld)\\n\", str,",
+ "#endif",
+ "#if NCORE>1",
+ " (nr_handoffs * z_handoff) + ",
+ "#endif",
+ " ((depthfound==-1)?depth:depthfound));",
+ " strncpy(laststr, str, 254);",
+ " errors++;",
+ "#ifdef HAS_CODE",
+ " if (readtrail) { wrap_trail(); return; }",
+ "#endif",
+ " is_cycle = (strstr(str, \" cycle\") != (char *) 0);",
+ " if (!is_cycle)",
+ " { depth++; trpt++;", /* include failed step */
+ " }",
+ " if ((every_error != 0)",
+ " || errors == upto)",
+ " {",
+ "#if defined(MA) && !defined(SAFETY)",
+ " if (is_cycle)",
+ " { int od = depth;",
+ " unwinding = 1;",
+ " depthfound = Unwind();",
+ " unwinding = 0;",
+ " depth = od;",
+ " }",
+ "#endif",
+ "#if NCORE>1",
+ " writing_trail = 1;",
+ "#endif",
+"#ifdef BFS",
+ " if (depth > 1) trpt--;",
+ " nuerror(str);",
+ " if (depth > 1) trpt++;",
+"#else",
+ " putrail();",
+"#endif",
+ "#if defined(MA) && !defined(SAFETY)",
+ " if (strstr(str, \" cycle\"))",
+ " { if (every_error)",
+ " printf(\"sorry: MA writes 1 trail max\\n\");",
+ " wrapup(); /* no recovery from unwind */",
+ " }",
+ "#endif",
+ "#if NCORE>1",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 4; /* uerror */",
+ " }",
+ " writing_trail = 0;",
+ "#endif",
+ " }",
+ " if (!is_cycle)",
+ " { depth--; trpt--; /* undo */",
+ " }",
+"#ifndef BFS",
+ " if (iterative != 0 && maxdepth > 0)",
+ " { maxdepth = (iterative == 1)?(depth-1):(depth/2);",
+ " warned = 1;",
+ " printf(\"pan: reducing search depth to %%ld\\n\",",
+ " maxdepth);",
+ " } else",
+"#endif",
+ " if (errors >= upto && upto != 0)",
+ " {",
+ "#if NCORE>1",
+ " sudden_stop(\"uerror\");",
+ "#endif",
+ " wrapup();",
+ " }",
+ " depthfound = -1;",
+ "}\n",
+ "int",
+ "xrefsrc(int lno, S_F_MAP *mp, int M, int i)",
+ "{ Trans *T; int j, retval=1;",
+ " for (T = trans[M][i]; T; T = T->nxt)",
+ " if (T && T->tp)",
+ " { if (strcmp(T->tp, \".(goto)\") == 0",
+ " || strncmp(T->tp, \"goto :\", 6) == 0)",
+ " return 1; /* not reported */",
+ "",
+ " printf(\"\\tline %%d\", lno);",
+ " if (verbose)",
+ " for (j = 0; j < sizeof(mp); j++)",
+ " if (i >= mp[j].from && i <= mp[j].upto)",
+ " { printf(\", \\\"%%s\\\"\", mp[j].fnm);",
+ " break;",
+ " }",
+ " printf(\", state %%d\", i);",
+ " if (strcmp(T->tp, \"\") != 0)",
+ " { char *q;",
+ " q = transmognify(T->tp);",
+ " printf(\", \\\"%%s\\\"\", q?q:\"\");",
+ " } else if (stopstate[M][i])",
+ " printf(\", -end state-\");",
+ " printf(\"\\n\");",
+ " retval = 0; /* reported */",
+ " }",
+ " return retval;",
+ "}\n",
+ "void",
+ "r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp)",
+ "{ int i, m=0;\n",
+ "#ifdef VERI",
+ " if (M == VERI && !verbose) return;",
+ "#endif",
+ " printf(\"unreached in proctype %%s\\n\", procname[M]);",
+ " for (i = 1; i < N; i++)",
+#if 0
+ " if (which[i] == 0 /* && trans[M][i] */)",
+#else
+ " if (which[i] == 0",
+ " && (mapstate[M][i] == 0",
+ " || which[mapstate[M][i]] == 0))",
+#endif
+ " m += xrefsrc((int) src[i], mp, M, i);",
+ " else",
+ " m++;",
+ " printf(\"\t(%%d of %%d states)\\n\", N-1-m, N-1);",
+ "}",
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ "static long rev_trail_cnt;",
+ "",
+ "#ifdef FULL_TRAIL",
+ "void",
+ "rev_trail(int fd, volatile Stack_Tree *st_tr)",
+ "{ long j; char snap[64];",
+ "",
+ " if (!st_tr)",
+ " { return;",
+ " }",
+ " rev_trail(fd, st_tr->prv);",
+ "#ifdef VERBOSE",
+ " printf(\"%%d (%%d) LRT [%%d,%%d] -- %%9u (root %%9u)\\n\",",
+ " depth, rev_trail_cnt, st_tr->pr, st_tr->t_id, st_tr, stack_last[core_id]);",
+ "#endif",
+ " if (st_tr->pr != 255)", /* still needed? */
+ " { sprintf(snap, \"%%ld:%%d:%%d\\n\", ",
+ " rev_trail_cnt++, st_tr->pr, st_tr->t_id);",
+ " j = strlen(snap);",
+ " if (write(fd, snap, j) != j)",
+ " { printf(\"pan: error writing trailfile\\n\");",
+ " close(fd);",
+ " wrapup();",
+ " return;",
+ " }",
+ " } else /* handoff point */",
+ " { if (a_cycles)",
+ " { write(fd, \"-1:-1:-1\\n\", 9);",
+ " } }",
+ "}",
+ "#endif", /* FULL_TRAIL */
+ "#endif", /* NCORE>1 */
+ "",
+ "void",
+ "putrail(void)",
+ "{ int fd;",
+ "#if defined VERI || defined(MERGED)",
+ " char snap[64];",
+ "#endif",
+ "#if NCORE==1 || defined(SEP_STATE) || !defined(FULL_TRAIL)",
+ " long i, j;",
+ " Trail *trl;",
+ "#endif",
+ " fd = make_trail();",
+ " if (fd < 0) return;",
+ "#ifdef VERI",
+ " sprintf(snap, \"-2:%%d:-2\\n\", VERI);",
+ " write(fd, snap, strlen(snap));",
+ "#endif",
+ "#ifdef MERGED",
+ " sprintf(snap, \"-4:-4:-4\\n\");",
+ " write(fd, snap, strlen(snap));",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && defined(FULL_TRAIL)",
+ " rev_trail_cnt = 1;",
+ " enter_critical(GLOBAL_LOCK);",
+ " rev_trail(fd, stack_last[core_id]);",
+ " leave_critical(GLOBAL_LOCK);",
+ "#else",
+ " i = 1; /* trail starts at position 1 */",
+ " #if NCORE>1 && defined(SEP_STATE)",
+ " if (cur_Root.m_vsize > 0) { i++; depth++; }",
+ " #endif",
+ " for ( ; i <= depth; i++)",
+ " { if (i == depthfound+1)",
+ " write(fd, \"-1:-1:-1\\n\", 9);",
+ " trl = getframe(i);",
+ " if (!trl->o_t) continue;",
+ " if (trl->o_pm&128) continue;",
+ " sprintf(snap, \"%%ld:%%d:%%d\\n\", ",
+ " i, trl->pr, trl->o_t->t_id);",
+ " j = strlen(snap);",
+ " if (write(fd, snap, j) != j)",
+ " { printf(\"pan: error writing trailfile\\n\");",
+ " close(fd);",
+ " wrapup();",
+ " } }",
+ "#endif",
+ " close(fd);",
+ "#if NCORE>1",
+ " cpu_printf(\"pan: wrote trailfile\\n\");",
+ "#endif",
+ "}\n",
+ "void",
+ "sv_save(void) /* push state vector onto save stack */",
+ "{ if (!svtack->nxt)",
+ " { svtack->nxt = (Svtack *) emalloc(sizeof(Svtack));",
+ " svtack->nxt->body = emalloc(vsize*sizeof(char));",
+ " svtack->nxt->lst = svtack;",
+ " svtack->nxt->m_delta = vsize;",
+ " svmax++;",
+ " } else if (vsize > svtack->nxt->m_delta)",
+ " { svtack->nxt->body = emalloc(vsize*sizeof(char));",
+ " svtack->nxt->lst = svtack;",
+ " svtack->nxt->m_delta = vsize;",
+ " svmax++;",
+ " }",
+ " svtack = svtack->nxt;",
+ "#if SYNC",
+ " svtack->o_boq = boq;",
+ "#endif",
+ " svtack->o_delta = vsize; /* don't compress */",
+ " memcpy((char *)(svtack->body), (char *) &now, vsize);",
+ "#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)",
+ " c_stack((uchar *) &(svtack->c_stack[0]));",
+ "#endif",
+ "#ifdef DEBUG",
+ " cpu_printf(\"%%d: sv_save\\n\", depth);",
+ "#endif",
+ "}\n",
+ "void",
+ "sv_restor(void) /* pop state vector from save stack */",
+ "{",
+ " memcpy((char *)&now, svtack->body, svtack->o_delta);",
+ "#if SYNC",
+ " boq = svtack->o_boq;",
+ "#endif",
+
+ "#if defined(C_States) && (HAS_TRACK==1)",
+ "#ifdef HAS_STACK",
+ " c_unstack((uchar *) &(svtack->c_stack[0]));",
+ "#endif",
+ " c_revert((uchar *) &(now.c_state[0]));",
+ "#endif",
+
+ " if (vsize != svtack->o_delta)",
+ " Uerror(\"sv_restor\");",
+ " if (!svtack->lst)",
+ " Uerror(\"error: v_restor\");",
+ " svtack = svtack->lst;",
+ "#ifdef DEBUG",
+ " cpu_printf(\" sv_restor\\n\");",
+ "#endif",
+ "}\n",
+ "void",
+ "p_restor(int h)",
+ "{ int i; char *z = (char *) &now;\n",
+ " proc_offset[h] = stack->o_offset;",
+ " proc_skip[h] = (uchar) stack->o_skip;",
+ "#ifndef XUSAFE",
+ " p_name[h] = stack->o_name;",
+ "#endif",
+ "#ifndef NOCOMP",
+ " for (i = vsize + stack->o_skip; i > vsize; i--)",
+ " Mask[i-1] = 1; /* align */",
+ "#endif",
+ " vsize += stack->o_skip;",
+ " memcpy(z+vsize, stack->body, stack->o_delta);",
+ " vsize += stack->o_delta;",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ "#ifndef NOCOMP",
+ " for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++)",
+ " Mask[vsize - i] = 1; /* pad */",
+ " Mask[proc_offset[h]] = 1; /* _pid */",
+ "#endif",
+ " if (BASE > 0 && h > 0)",
+ " ((P0 *)pptr(h))->_pid = h-BASE;",
+ " else",
+ " ((P0 *)pptr(h))->_pid = h;",
+ " i = stack->o_delqs;",
+ " now._nr_pr += 1;",
+ " if (!stack->lst) /* debugging */",
+ " Uerror(\"error: p_restor\");",
+ " stack = stack->lst;",
+ " this = pptr(h);",
+ " while (i-- > 0)",
+ " q_restor();",
+ "}\n",
+ "void",
+ "q_restor(void)",
+ "{ char *z = (char *) &now;",
+ "#ifndef NOCOMP",
+ " int k, k_end;",
+ "#endif",
+ " q_offset[now._nr_qs] = stack->o_offset;",
+ " q_skip[now._nr_qs] = (uchar) stack->o_skip;",
+ "#ifndef XUSAFE",
+ " q_name[now._nr_qs] = stack->o_name;",
+ "#endif",
+ " vsize += stack->o_skip;",
+ " memcpy(z+vsize, stack->body, stack->o_delta);",
+ " vsize += stack->o_delta;",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ " now._nr_qs += 1;",
+ "#ifndef NOCOMP",
+ " k_end = stack->o_offset;",
+ " k = k_end - stack->o_skip;",
+ "#if SYNC",
+ "#ifndef BFS",
+ " if (q_zero(now._nr_qs)) k_end += stack->o_delta;",
+ "#endif",
+ "#endif",
+ " for ( ; k < k_end; k++)",
+ " Mask[k] = 1;",
+ "#endif",
+ " if (!stack->lst) /* debugging */",
+ " Uerror(\"error: q_restor\");",
+ " stack = stack->lst;",
+ "}",
+
+ "typedef struct IntChunks {",
+ " int *ptr;",
+ " struct IntChunks *nxt;",
+ "} IntChunks;",
+ "IntChunks *filled_chunks[512];",
+ "IntChunks *empty_chunks[512];",
+
+ "int *",
+ "grab_ints(int nr)",
+ "{ IntChunks *z;",
+ " if (nr >= 512) Uerror(\"cannot happen grab_int\");",
+ " if (filled_chunks[nr])",
+ " { z = filled_chunks[nr];",
+ " filled_chunks[nr] = filled_chunks[nr]->nxt;",
+ " } else ",
+ " { z = (IntChunks *) emalloc(sizeof(IntChunks));",
+ " z->ptr = (int *) emalloc(nr * sizeof(int));",
+ " }",
+ " z->nxt = empty_chunks[nr];",
+ " empty_chunks[nr] = z;",
+ " return z->ptr;",
+ "}",
+ "void",
+ "ungrab_ints(int *p, int nr)",
+ "{ IntChunks *z;",
+ " if (!empty_chunks[nr]) Uerror(\"cannot happen ungrab_int\");",
+ " z = empty_chunks[nr];",
+ " empty_chunks[nr] = empty_chunks[nr]->nxt;",
+ " z->ptr = p;",
+ " z->nxt = filled_chunks[nr];",
+ " filled_chunks[nr] = z;",
+ "}",
+ "int",
+ "delproc(int sav, int h)",
+ "{ int d, i=0;",
+ "#ifndef NOCOMP",
+ " int o_vsize = vsize;",
+ "#endif",
+ " if (h+1 != (int) now._nr_pr) return 0;\n",
+ " while (now._nr_qs",
+ " && q_offset[now._nr_qs-1] > proc_offset[h])",
+ " { delq(sav);",
+ " i++;",
+ " }",
+ " d = vsize - proc_offset[h];",
+ " if (sav)",
+ " { if (!stack->nxt)",
+ " { stack->nxt = (Stack *)",
+ " emalloc(sizeof(Stack));",
+ " stack->nxt->body = ",
+ " emalloc(Maxbody*sizeof(char));",
+ " stack->nxt->lst = stack;",
+ " smax++;",
+ " }",
+ " stack = stack->nxt;",
+ " stack->o_offset = proc_offset[h];",
+ "#if VECTORSZ>32000",
+ " stack->o_skip = (int) proc_skip[h];",
+ "#else",
+ " stack->o_skip = (short) proc_skip[h];",
+ "#endif",
+ "#ifndef XUSAFE",
+ " stack->o_name = p_name[h];",
+ "#endif",
+ " stack->o_delta = d;",
+ " stack->o_delqs = i;",
+ " memcpy(stack->body, (char *)pptr(h), d);",
+ " }",
+ " vsize = proc_offset[h];",
+ " now._nr_pr = now._nr_pr - 1;",
+ " memset((char *)pptr(h), 0, d);",
+ " vsize -= (int) proc_skip[h];",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ "#ifndef NOCOMP",
+ " for (i = vsize; i < o_vsize; i++)",
+ " Mask[i] = 0; /* reset */",
+ "#endif",
+ " return 1;",
+ "}\n",
+ "void",
+ "delq(int sav)",
+ "{ int h = now._nr_qs - 1;",
+ " int d = vsize - q_offset[now._nr_qs - 1];",
+ "#ifndef NOCOMP",
+ " int k, o_vsize = vsize;",
+ "#endif",
+ " if (sav)",
+ " { if (!stack->nxt)",
+ " { stack->nxt = (Stack *)",
+ " emalloc(sizeof(Stack));",
+ " stack->nxt->body = ",
+ " emalloc(Maxbody*sizeof(char));",
+ " stack->nxt->lst = stack;",
+ " smax++;",
+ " }",
+ " stack = stack->nxt;",
+ " stack->o_offset = q_offset[h];",
+ "#if VECTORSZ>32000",
+ " stack->o_skip = (int) q_skip[h];",
+ "#else",
+ " stack->o_skip = (short) q_skip[h];",
+ "#endif",
+ "#ifndef XUSAFE",
+ " stack->o_name = q_name[h];",
+ "#endif",
+ " stack->o_delta = d;",
+ " memcpy(stack->body, (char *)qptr(h), d);",
+ " }",
+ " vsize = q_offset[h];",
+ " now._nr_qs = now._nr_qs - 1;",
+ " memset((char *)qptr(h), 0, d);",
+ " vsize -= (int) q_skip[h];",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ "#ifndef NOCOMP",
+ " for (k = vsize; k < o_vsize; k++)",
+ " Mask[k] = 0; /* reset */",
+ "#endif",
+ "}\n",
+ "int",
+ "qs_empty(void)",
+ "{ int i;",
+ " for (i = 0; i < (int) now._nr_qs; i++)",
+ " { if (q_sz(i) > 0)",
+ " return 0;",
+ " }",
+ " return 1;",
+ "}\n",
+ "int",
+ "endstate(void)",
+ "{ int i; P0 *ptr;",
+ " for (i = BASE; i < (int) now._nr_pr; i++)",
+ " { ptr = (P0 *) pptr(i);",
+ " if (!stopstate[ptr->_t][ptr->_p])",
+ " return 0;",
+ " }",
+ " if (strict) return qs_empty();",
+ "#if defined(EVENT_TRACE) && !defined(OTIM)",
+ " if (!stopstate[EVENT_TRACE][now._event] && !a_cycles)",
+ " { printf(\"pan: event_trace not completed\\n\");",
+ " return 0;",
+ " }",
+ "#endif",
+ " return 1;",
+ "}\n",
+ "#ifndef SAFETY",
+ "void",
+ "checkcycles(void)",
+ "{ uchar o_a_t = now._a_t;",
+ "#ifdef SCHED",
+ " int o_limit;",
+ "#endif",
+ "#ifndef NOFAIR",
+ " uchar o_cnt = now._cnt[1];",
+ "#endif",
+ "#ifdef FULLSTACK",
+ "#ifndef MA",
+ " struct H_el *sv = trpt->ostate; /* save */",
+ "#else",
+ " uchar prov = trpt->proviso; /* save */",
+ "#endif",
+ "#endif",
+ "#ifdef DEBUG",
+ " { int i; uchar *v = (uchar *) &now;",
+ " printf(\" set Seed state \");",
+ "#ifndef NOFAIR",
+ " if (fairness) printf(\"(cnt = %%d:%%d, nrpr=%%d) \",",
+ " now._cnt[0], now._cnt[1], now._nr_pr);",
+ "#endif",
+ " /* for (i = 0; i < n; i++) printf(\"%%d,\", v[i]); */",
+ " printf(\"\\n\");",
+ " }",
+ " printf(\"%%d: cycle check starts\\n\", depth);",
+ "#endif",
+ " now._a_t |= (1|16|32);",
+ " /* 1 = 2nd DFS; (16|32) to help hasher */",
+ "#ifndef NOFAIR",
+#if 0
+ " if (fairness)",
+ " { now._a_t &= ~2; /* pre-apply Rule 3 */",
+ " now._cnt[1] = 0;", /* reset both a-bit and cnt=0 */
+ " /* avoid matching seed on claim stutter on this state */",
+ " }",
+#else
+ " now._cnt[1] = now._cnt[0];",
+#endif
+ "#endif",
+ " memcpy((char *)&A_Root, (char *)&now, vsize);",
+ " A_depth = depthfound = depth;",
+
+ "#if NCORE>1",
+ " mem_put_acc();", /* handoff accept states */
+ "#else",
+ " #ifdef SCHED",
+ " o_limit = trpt->sched_limit;",
+ " trpt->sched_limit = 0;",
+ " #endif",
+ " new_state(); /* start 2nd DFS */",
+ " #ifdef SCHED",
+ " trpt->sched_limit = o_limit;",
+ " #endif",
+ "#endif",
+
+ " now._a_t = o_a_t;",
+ "#ifndef NOFAIR",
+ " now._cnt[1] = o_cnt;",
+ "#endif",
+ " A_depth = 0; depthfound = -1;",
+ "#ifdef DEBUG",
+ " printf(\"%%d: cycle check returns\\n\", depth);",
+ "#endif",
+ "#ifdef FULLSTACK",
+ "#ifndef MA",
+ " trpt->ostate = sv; /* restore */",
+ "#else",
+ " trpt->proviso = prov;",
+ "#endif",
+ "#endif",
+ "}",
+ "#endif\n",
+ "#if defined(FULLSTACK) && defined(BITSTATE)",
+ "struct H_el *Free_list = (struct H_el *) 0;",
+ "void",
+ "onstack_init(void) /* to store stack states in a bitstate search */",
+ "{ S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *));",
+ "}",
+ "struct H_el *",
+ "grab_state(int n)",
+ "{ struct H_el *v, *last = 0;",
+ " if (H_tab == S_Tab)",
+ " { for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt)",
+ " { if ((int) v->tagged == n)",
+ " { if (last)",
+ " last->nxt = v->nxt;",
+ " else",
+ "gotcha: Free_list = v->nxt;",
+ " v->tagged = 0;",
+ " v->nxt = 0;",
+ "#ifdef COLLAPSE",
+ " v->ln = 0;",
+ "#endif",
+ " return v;",
+ " }",
+ " Fh++; last=v;",
+ " }",
+ " /* new: second try */",
+ " v = Free_list;", /* try to avoid emalloc */
+ " if (v && ((int) v->tagged >= n))",
+ " goto gotcha;",
+ " ngrabs++;",
+ " }",
+ " return (struct H_el *)",
+ " emalloc(sizeof(struct H_el)+n-sizeof(unsigned));",
+ "}\n",
+ "#else",
+
+ "#if NCORE>1",
+ "struct H_el *",
+ "grab_state(int n)",
+ "{ struct H_el *grab_shared(int);",
+ " return grab_shared(sizeof(struct H_el)+n-sizeof(unsigned));",
+ "}",
+ "#else",
+ " #ifndef AUTO_RESIZE",
+ " #define grab_state(n) (struct H_el *) \\",
+ " emalloc(sizeof(struct H_el)+n-sizeof(unsigned long));",
+ " #else",
+ " struct H_el *",
+ " grab_state(int n)",
+ " { struct H_el *p;",
+ " int cnt = sizeof(struct H_el)+n-sizeof(unsigned long);",
+ "",
+ " if (reclaim_size >= cnt+WS)",
+ " { if ((cnt & (WS-1)) != 0) /* alignment */",
+ " { cnt += WS - (cnt & (WS-1));",
+ " }",
+ " p = (struct H_el *) reclaim_mem;",
+ " reclaim_mem += cnt;",
+ " reclaim_size -= cnt;",
+ " memset(p, 0, cnt);",
+ " } else",
+ " { p = (struct H_el *) emalloc(cnt);",
+ " }",
+ " return p;",
+ " }",
+ " #endif",
+ "#endif",
+
+ "#endif",
+"#ifdef COLLAPSE",
+ "unsigned long",
+ "ordinal(char *v, long n, short tp)",
+ "{ struct H_el *tmp, *ntmp; long m;",
+ " struct H_el *olst = (struct H_el *) 0;",
+ " s_hash((uchar *)v, n);",
+
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ " enter_critical(CS_ID); /* uses spinlock - 1..128 */",
+ "#endif",
+
+ " tmp = H_tab[j1];",
+ " if (!tmp)",
+ " { tmp = grab_state(n);",
+ " H_tab[j1] = tmp;",
+ " } else",
+ " for ( ;; olst = tmp, tmp = tmp->nxt)",
+ " { m = memcmp(((char *)&(tmp->state)), v, n);",
+ " if (n == tmp->ln)",
+ " {",
+ " if (m == 0)",
+ " goto done;",
+ " if (m < 0)",
+ " {",
+ "Insert: ntmp = grab_state(n);",
+ " ntmp->nxt = tmp;",
+ " if (!olst)",
+ " H_tab[j1] = ntmp;",
+ " else",
+ " olst->nxt = ntmp;",
+ " tmp = ntmp;",
+ " break;",
+ " } else if (!tmp->nxt)",
+ " {",
+ "Append: tmp->nxt = grab_state(n);",
+ " tmp = tmp->nxt;",
+ " break;",
+ " }",
+ " continue;",
+ " }",
+ " if (n < tmp->ln)",
+ " goto Insert;",
+ " else if (!tmp->nxt)",
+ " goto Append;",
+ " }",
+ " m = ++ncomps[tp];",
+ "#ifdef FULLSTACK",
+ " tmp->tagged = m;",
+ "#else",
+ " tmp->st_id = m;",
+ "#endif",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE)",
+ " tmp->m_K1 = K1;",
+ "#endif",
+ " memcpy(((char *)&(tmp->state)), v, n);",
+ " tmp->ln = n;",
+ "done:",
+
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ " leave_critical(CS_ID); /* uses spinlock */",
+ "#endif",
+
+ "#ifdef FULLSTACK",
+ " return tmp->tagged;",
+ "#else",
+ " return tmp->st_id;",
+ "#endif",
+ "}",
+ "",
+ "int",
+ "compress(char *vin, int nin) /* collapse compression */",
+ "{ char *w, *v = (char *) &comp_now;",
+ " int i, j;",
+ " unsigned long n;",
+ " static char *x;",
+ " static uchar nbytes[513]; /* 1 + 256 + 256 */",
+ " static unsigned short nbytelen;",
+ " long col_q(int, char *);",
+ " long col_p(int, char *);",
+ "#ifndef SAFETY",
+ " if (a_cycles)",
+ " *v++ = now._a_t;",
+ "#ifndef NOFAIR",
+ " if (fairness)",
+ " for (i = 0; i < NFAIR; i++)",
+ " *v++ = now._cnt[i];",
+ "#endif",
+ "#endif",
+ " nbytelen = 0;",
+
+ "#ifndef JOINPROCS",
+ " for (i = 0; i < (int) now._nr_pr; i++)",
+ " { n = col_p(i, (char *) 0);",
+ "#ifdef NOFIX",
+ " nbytes[nbytelen] = 0;",
+ "#else",
+ " nbytes[nbytelen] = 1;",
+ " *v++ = ((P0 *) pptr(i))->_t;",
+ "#endif",
+ " *v++ = n&255;",
+ " if (n >= (1<<8))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>8)&255;",
+ " }",
+ " if (n >= (1<<16))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>16)&255;",
+ " }",
+ " if (n >= (1<<24))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>24)&255;",
+ " }",
+ " nbytelen++;",
+ " }",
+ "#else",
+ " x = scratch;",
+ " for (i = 0; i < (int) now._nr_pr; i++)",
+ " x += col_p(i, x);",
+ " n = ordinal(scratch, x-scratch, 2); /* procs */",
+ " *v++ = n&255;",
+ " nbytes[nbytelen] = 0;",
+ " if (n >= (1<<8))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>8)&255;",
+ " }",
+ " if (n >= (1<<16))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>16)&255;",
+ " }",
+ " if (n >= (1<<24))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>24)&255;",
+ " }",
+ " nbytelen++;",
+ "#endif",
+ "#ifdef SEPQS",
+ " for (i = 0; i < (int) now._nr_qs; i++)",
+ " { n = col_q(i, (char *) 0);",
+ " nbytes[nbytelen] = 0;",
+ " *v++ = n&255;",
+ " if (n >= (1<<8))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>8)&255;",
+ " }",
+ " if (n >= (1<<16))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>16)&255;",
+ " }",
+ " if (n >= (1<<24))",
+ " { nbytes[nbytelen]++;",
+ " *v++ = (n>>24)&255;",
+ " }",
+ " nbytelen++;",
+ " }",
+ "#endif",
+
+ "#ifdef NOVSZ",
+ " /* 3 = _a_t, _nr_pr, _nr_qs */",
+ " w = (char *) &now + 3 * sizeof(uchar);",
+ "#ifndef NOFAIR",
+ " w += NFAIR;",
+ "#endif",
+ "#else",
+ "#if VECTORSZ<65536",
+ " w = (char *) &(now._vsz) + sizeof(unsigned short);",
+ "#else",
+ " w = (char *) &(now._vsz) + sizeof(unsigned long);",
+ "#endif",
+ "#endif",
+ " x = scratch;",
+ " *x++ = now._nr_pr;",
+ " *x++ = now._nr_qs;",
+
+ " if (now._nr_qs > 0 && qptr(0) < pptr(0))",
+ " n = qptr(0) - (uchar *) w;",
+ " else",
+ " n = pptr(0) - (uchar *) w;",
+ " j = w - (char *) &now;",
+ " for (i = 0; i < (int) n; i++, w++)",
+ " if (!Mask[j++]) *x++ = *w;",
+ "#ifndef SEPQS",
+ " for (i = 0; i < (int) now._nr_qs; i++)",
+ " x += col_q(i, x);",
+ "#endif",
+
+ " x--;",
+ " for (i = 0, j = 6; i < nbytelen; i++)",
+ " { if (j == 6)",
+ " { j = 0;",
+ " *(++x) = 0;",
+ " } else",
+ " j += 2;",
+ " *x |= (nbytes[i] << j);",
+ " }",
+ " x++;",
+ " for (j = 0; j < WS-1; j++)",
+ " *x++ = 0;",
+ " x -= j; j = 0;",
+ " n = ordinal(scratch, x-scratch, 0); /* globals */",
+ " *v++ = n&255;",
+ " if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; }",
+ " if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; }",
+ " if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; }",
+ " *v++ = j; /* add last count as a byte */",
+
+ " for (i = 0; i < WS-1; i++)",
+ " *v++ = 0;",
+ " v -= i;",
+ "#if 0",
+ " printf(\"collapse %%d -> %%d\\n\",",
+ " vsize, v - (char *)&comp_now);",
+ "#endif",
+ " return v - (char *)&comp_now;",
+ "}",
+
+"#else",
+"#if !defined(NOCOMP)",
+ "int",
+ "compress(char *vin, int n) /* default compression */",
+ "{",
+ "#ifdef HC",
+ " int delta = 0;",
+ " s_hash((uchar *)vin, n); /* sets K1 and K2 */",
+ "#ifndef SAFETY",
+ " if (S_A)",
+ " { delta++; /* _a_t */",
+ "#ifndef NOFAIR",
+ " if (S_A > NFAIR)",
+ " delta += NFAIR; /* _cnt[] */",
+ "#endif",
+ " }",
+ "#endif",
+ " memcpy((char *) &comp_now + delta, (char *) &K1, WS);",
+ " delta += WS;",
+ "#if HC>0",
+ " memcpy((char *) &comp_now + delta, (char *) &K2, HC);",
+ " delta += HC;",
+ "#endif",
+ " return delta;",
+ "#else",
+ " char *vv = vin;",
+ " char *v = (char *) &comp_now;",
+ " int i;",
+ " #ifndef NO_FAST_C", /* disable faster compress */
+ " int r = 0, unroll = n/8;", /* most sv are much longer */
+ " if (unroll > 0)",
+ " { i = 0;",
+ " while (r++ < unroll)",
+ " { /* unroll 8 times, avoid ifs */",
+ " /* 1 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 2 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 3 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 4 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 5 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 6 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 7 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " /* 8 */ *v = *vv++;",
+ " v += 1 - Mask[i++];",
+ " }",
+ " r = n - i; /* the rest, at most 7 */",
+ " switch (r) {",
+ " case 7: *v = *vv++; v += 1 - Mask[i++];",
+ " case 6: *v = *vv++; v += 1 - Mask[i++];",
+ " case 5: *v = *vv++; v += 1 - Mask[i++];",
+ " case 4: *v = *vv++; v += 1 - Mask[i++];",
+ " case 3: *v = *vv++; v += 1 - Mask[i++];",
+ " case 2: *v = *vv++; v += 1 - Mask[i++];",
+ " case 1: *v = *vv++; v += 1 - Mask[i++];",
+ " case 0: break;",
+ " }",
+ " r = (n+WS-1)/WS; /* words rounded up */",
+ " r *= WS; /* bytes */",
+ " i = r - i; /* remainder */",
+ " switch (i) {", /* fill word */
+ " case 7: *v++ = 0; /* fall thru */",
+ " case 6: *v++ = 0;",
+ " case 5: *v++ = 0;",
+ " case 4: *v++ = 0;",
+ " case 3: *v++ = 0;",
+ " case 2: *v++ = 0;",
+ " case 1: *v++ = 0;",
+ " case 0: break;",
+ " default: Uerror(\"unexpected wordsize\");",
+ " }",
+ " v -= i;",
+ " } else",
+ " #endif",
+ " { for (i = 0; i < n; i++, vv++)",
+ " if (!Mask[i]) *v++ = *vv;",
+ " for (i = 0; i < WS-1; i++)",
+ " *v++ = 0;",
+ " v -= i;",
+ " }",
+ "#if 0",
+ " printf(\"compress %%d -> %%d\\n\",",
+ " n, v - (char *)&comp_now);",
+ "#endif",
+ " return v - (char *)&comp_now;",
+ "#endif",
+ "}",
+"#endif",
+"#endif",
+ "#if defined(FULLSTACK) && defined(BITSTATE)",
+"#if defined(MA)",
+ "#if !defined(onstack_now)",
+ "int onstack_now(void) {}", /* to suppress compiler errors */
+ "#endif",
+ "#if !defined(onstack_put)",
+ "void onstack_put(void) {}", /* for this invalid combination */
+ "#endif",
+ "#if !defined(onstack_zap)",
+ "void onstack_zap(void) {}", /* of directives */
+ "#endif",
+"#else",
+ "void",
+ "onstack_zap(void)",
+ "{ struct H_el *v, *w, *last = 0;",
+ " struct H_el **tmp = H_tab;",
+ " char *nv; int n, m;\n",
+ " static char warned = 0;",
+ "",
+ " H_tab = S_Tab;",
+ "#ifndef NOCOMP",
+ " nv = (char *) &comp_now;",
+ " n = compress((char *)&now, vsize);",
+ "#else",
+ "#if defined(BITSTATE) && defined(LC)",
+ " nv = (char *) &comp_now;",
+ " n = compact_stack((char *)&now, vsize);",
+ "#else",
+ " nv = (char *) &now;",
+ " n = vsize;",
+ "#endif",
+ "#endif",
+ "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))",
+ " s_hash((uchar *)nv, n);",
+ "#endif",
+ " H_tab = tmp;",
+ " for (v = S_Tab[j1]; v; Zh++, last=v, v=v->nxt)",
+ " { m = memcmp(&(v->state), nv, n);",
+ " if (m == 0)",
+ " goto Found;",
+ " if (m < 0)",
+ " break;",
+ " }",
+ "/* NotFound: */",
+ "#ifndef ZAPH",
+ " #if defined(BITSTATE) && NCORE>1",
+ " /* seen this happen, likely harmless, but not yet understood */",
+ " if (warned == 0)",
+ " #endif",
+ " { /* Uerror(\"stack out of wack - zap\"); */",
+ " cpu_printf(\"pan: warning, stack incomplete\\n\");",
+ " warned = 1;",
+ " }",
+ "#endif",
+ " return;",
+ "Found:",
+ " ZAPS++;",
+ " if (last)",
+ " last->nxt = v->nxt;",
+ " else",
+ " S_Tab[j1] = v->nxt;",
+ " v->tagged = (unsigned) n;",
+ "#if !defined(NOREDUCE) && !defined(SAFETY)",
+ " v->proviso = 0;",
+ "#endif",
+ " v->nxt = last = (struct H_el *) 0;",
+ " for (w = Free_list; w; Fa++, last=w, w = w->nxt)",
+ " { if ((int) w->tagged <= n)",
+ " { if (last)",
+ " { v->nxt = w;",
+ " last->nxt = v;",
+ " } else",
+ " { v->nxt = Free_list;",
+ " Free_list = v;",
+ " }",
+ " return;",
+ " }",
+ " if (!w->nxt)",
+ " { w->nxt = v;",
+ " return;",
+ " } }",
+ " Free_list = v;",
+ "}",
+ "void",
+ "onstack_put(void)",
+ "{ struct H_el **tmp = H_tab;",
+ " H_tab = S_Tab;",
+ " if (hstore((char *)&now, vsize) != 0)",
+ "#if defined(BITSTATE) && defined(LC)",
+ " printf(\"pan: warning, double stack entry\\n\");",
+ "#else",
+ " #ifndef ZAPH",
+ " Uerror(\"cannot happen - unstack_put\");",
+ " #endif",
+ "#endif",
+ " H_tab = tmp;",
+ " trpt->ostate = Lstate;",
+ " PUT++;",
+ "}",
+ "int",
+ "onstack_now(void)",
+ "{ struct H_el *tmp;",
+ " struct H_el **tmp2 = H_tab;",
+ " char *v; int n, m = 1;\n",
+ " H_tab = S_Tab;",
+ "#ifdef NOCOMP",
+ "#if defined(BITSTATE) && defined(LC)",
+ " v = (char *) &comp_now;",
+ " n = compact_stack((char *)&now, vsize);",
+ "#else",
+ " v = (char *) &now;",
+ " n = vsize;",
+ "#endif",
+ "#else",
+ " v = (char *) &comp_now;",
+ " n = compress((char *)&now, vsize);",
+ "#endif",
+ "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))",
+ " s_hash((uchar *)v, n);",
+ "#endif",
+ " H_tab = tmp2;",
+ " for (tmp = S_Tab[j1]; tmp; Zn++, tmp = tmp->nxt)",
+ " { m = memcmp(((char *)&(tmp->state)),v,n);",
+ " if (m <= 0)",
+ " { Lstate = (struct H_el *) tmp;",
+ " break;",
+ " } }",
+ " PROBE++;",
+ " return (m == 0);",
+ "}",
+ "#endif",
+"#endif",
+
+ "#ifndef BITSTATE",
+ "void",
+ "hinit(void)",
+ "{",
+ " #ifdef MA",
+ "#ifdef R_XPT",
+ " { void r_xpoint(void);",
+ " r_xpoint();",
+ " }",
+ "#else",
+ " dfa_init((unsigned short) (MA+a_cycles));",
+ "#if NCORE>1 && !defined(COLLAPSE)",
+ " if (!readtrail)",
+ " { void init_HT(unsigned long);",
+ " init_HT(0L);",
+ " }",
+ "#endif",
+ "#endif",
+ " #endif",
+ " #if !defined(MA) || defined(COLLAPSE)",
+ "#if NCORE>1",
+ " if (!readtrail)",
+ " { void init_HT(unsigned long);",
+ " init_HT((unsigned long) (ONE_L<<ssize)*sizeof(struct H_el *));",
+ " } else",
+ "#endif",
+ " H_tab = (struct H_el **)",
+ " emalloc((ONE_L<<ssize)*sizeof(struct H_el *));",
+ " #endif",
+ "}",
+ "#endif\n",
+
+ "#if !defined(BITSTATE) || defined(FULLSTACK)",
+
+ "#ifdef DEBUG",
+ "void",
+ "dumpstate(int wasnew, char *v, int n, int tag)",
+ "{ int i;",
+ "#ifndef SAFETY",
+ " if (S_A)",
+ " { printf(\"\tstate tags %%d (%%d::%%d): \",",
+ " V_A, wasnew, v[0]);",
+ "#ifdef FULLSTACK",
+ " printf(\" %%d \", tag);",
+ "#endif",
+ " printf(\"\\n\");",
+ " }",
+ "#endif",
+ "#ifdef SDUMP",
+ "#ifndef NOCOMP",
+ " printf(\"\t State: \");",
+ " for (i = 0; i < vsize; i++) printf(\"%%d%%s,\",",
+ " ((char *)&now)[i], Mask[i]?\"*\":\"\");",
+ "#endif",
+ " printf(\"\\n\tVector: \");",
+ " for (i = 0; i < n; i++) printf(\"%%d,\", v[i]);",
+ " printf(\"\\n\");",
+ "#endif",
+ "}",
+ "#endif",
+
+"#ifdef MA",
+ "int",
+ "gstore(char *vin, int nin, uchar pbit)",
+ "{ int n, i;",
+ " int ret_val = 1;",
+ " uchar *v;",
+ " static uchar Info[MA+1];",
+ "#ifndef NOCOMP",
+ " n = compress(vin, nin);",
+ " v = (uchar *) &comp_now;",
+ "#else",
+ " n = nin;",
+ " v = vin;",
+ "#endif",
+ " if (n >= MA)",
+ " { printf(\"pan: error, MA too small, recompile pan.c\");",
+ " printf(\" with -DMA=N with N>%%d\\n\", n);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (n > (int) maxgs)",
+ " { maxgs = (unsigned int) n;",
+ " }",
+ " for (i = 0; i < n; i++)",
+ " { Info[i] = v[i];",
+ " }",
+ " for ( ; i < MA-1; i++)",
+ " { Info[i] = 0;",
+ " }",
+ " Info[MA-1] = pbit;",
+ " if (a_cycles) /* place _a_t at the end */",
+ " { Info[MA] = Info[0];",
+ " Info[0] = 0;",
+ " }",
+ "",
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ " enter_critical(GLOBAL_LOCK); /* crude, but necessary */",
+ " /* to make this mode work, also replace emalloc with grab_shared inside store MA routines */",
+ "#endif",
+ "",
+ " if (!dfa_store(Info))",
+ " { if (pbit == 0",
+ " && (now._a_t&1)",
+ " && depth > A_depth)",
+ " { Info[MA] &= ~(1|16|32); /* _a_t */",
+ " if (dfa_member(MA))", /* was !dfa_member(MA) */
+ " { Info[MA-1] = 4; /* off-stack bit */",
+ " nShadow++;",
+ " if (!dfa_member(MA-1))",
+ " { ret_val = 3;",
+ " #ifdef VERBOSE",
+ " printf(\"intersected 1st dfs stack\\n\");",
+ " #endif",
+ " goto done;",
+ " } } }",
+ " ret_val = 0;",
+ " #ifdef VERBOSE",
+ " printf(\"new state\\n\");",
+ " #endif",
+ " goto done;",
+ " }",
+ "#ifdef FULLSTACK",
+ " if (pbit == 0)",
+ " { Info[MA-1] = 1; /* proviso bit */",
+ "#ifndef BFS",
+ " trpt->proviso = dfa_member(MA-1);",
+ "#endif",
+ " Info[MA-1] = 4; /* off-stack bit */",
+ " if (dfa_member(MA-1))",
+ " { ret_val = 1; /* off-stack */",
+ " #ifdef VERBOSE",
+ " printf(\"old state\\n\");",
+ " #endif",
+ " } else",
+ " { ret_val = 2; /* on-stack */",
+ " #ifdef VERBOSE",
+ " printf(\"on-stack\\n\");",
+ " #endif",
+ " }",
+ " goto done;",
+ " }",
+ "#endif",
+ " ret_val = 1;",
+ "#ifdef VERBOSE",
+ " printf(\"old state\\n\");",
+ "#endif",
+ "done:",
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ " leave_critical(GLOBAL_LOCK);",
+ "#endif",
+ " return ret_val; /* old state */",
+ "}",
+"#endif",
+
+ "#if defined(BITSTATE) && defined(LC)",
+ "int",
+ "compact_stack(char *vin, int n)", /* special case of HC4 */
+ "{ int delta = 0;",
+ " s_hash((uchar *)vin, n); /* sets K1 and K2 */",
+ "#ifndef SAFETY",
+ " delta++; /* room for state[0] |= 128 */",
+ "#endif",
+ " memcpy((char *) &comp_now + delta, (char *) &K1, WS);",
+ " delta += WS;",
+ " memcpy((char *) &comp_now + delta, (char *) &K2, WS);",
+ " delta += WS; /* use all available bits */",
+ " return delta;",
+ "}",
+ "#endif",
+
+ "int",
+ "hstore(char *vin, int nin) /* hash table storage */",
+ "{ struct H_el *ntmp;",
+ " struct H_el *tmp, *olst = (struct H_el *) 0;",
+ " char *v; int n, m=0;",
+ "#ifdef HC",
+ " uchar rem_a;",
+ "#endif",
+ "#ifdef NOCOMP", /* defined by BITSTATE */
+ "#if defined(BITSTATE) && defined(LC)",
+ " if (S_Tab == H_tab)",
+ " { v = (char *) &comp_now;",
+ " n = compact_stack(vin, nin);",
+ " } else",
+ " { v = vin; n = nin;",
+ " }",
+ "#else",
+ " v = vin; n = nin;",
+ "#endif",
+ "#else",
+ " v = (char *) &comp_now;",
+ " #ifdef HC",
+ " rem_a = now._a_t;", /* new 5.0 */
+ " now._a_t = 0;", /* for hashing/state matching to work right */
+ " #endif",
+ " n = compress(vin, nin);", /* with HC, this calls s_hash -- but on vin, not on v... */
+ " #ifdef HC",
+ " now._a_t = rem_a;", /* new 5.0 */
+ " #endif",
+ /* with HC4 -a, compress copies K1 and K2 into v[], leaving v[0] free for the a-bit */
+ "#ifndef SAFETY",
+ " if (S_A)",
+ " { v[0] = 0; /* _a_t */",
+ "#ifndef NOFAIR",
+ " if (S_A > NFAIR)",
+ " for (m = 0; m < NFAIR; m++)",
+ " v[m+1] = 0; /* _cnt[] */",
+ "#endif",
+ " m = 0;",
+ " }",
+ " #endif",
+ "#endif",
+ "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))",
+ " s_hash((uchar *)v, n);",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " enter_critical(CS_ID); /* uses spinlock */",
+ "#endif",
+
+ " tmp = H_tab[j1];",
+ " if (!tmp)",
+ " { tmp = grab_state(n);",
+ "#if NCORE>1",
+ " if (!tmp)",
+ " { /* if we get here -- we've already issued a warning */",
+ " /* but we want to allow the normal distributed termination */",
+ " /* to collect the stats on all cpus in the wrapup */",
+ " #if !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ " #endif",
+ " return 1; /* allow normal termination */",
+ " }",
+ "#endif",
+ " H_tab[j1] = tmp;",
+ " } else",
+ " { for (;; hcmp++, olst = tmp, tmp = tmp->nxt)",
+ " { /* skip the _a_t and the _cnt bytes */",
+ "#ifdef COLLAPSE",
+ " if (tmp->ln != 0)",
+ " { if (!tmp->nxt) goto Append;",
+ " continue;",
+ " }",
+ "#endif",
+ " m = memcmp(((char *)&(tmp->state)) + S_A, ",
+ " v + S_A, n - S_A);",
+ " if (m == 0) {",
+ "#ifdef SAFETY",
+ "#define wasnew 0",
+ "#else",
+ " int wasnew = 0;",
+ "#endif",
+
+ "#ifndef SAFETY",
+ "#ifndef NOCOMP",
+ " if (S_A)",
+ " { if ((((char *)&(tmp->state))[0] & V_A) != V_A)",
+ " { wasnew = 1; nShadow++;",
+ " ((char *)&(tmp->state))[0] |= V_A;",
+ " }",
+ "#ifndef NOFAIR",
+ " if (S_A > NFAIR)",
+ " { /* 0 <= now._cnt[now._a_t&1] < MAXPROC */",
+ " unsigned ci, bp; /* index, bit pos */",
+ " ci = (now._cnt[now._a_t&1] / 8);",
+ " bp = (now._cnt[now._a_t&1] - 8*ci);",
+ " if (now._a_t&1) /* use tail-bits in _cnt */",
+ " { ci = (NFAIR - 1) - ci;",
+ " bp = 7 - bp; /* bp = 0..7 */",
+ " }",
+ " ci++; /* skip over _a_t */",
+ " bp = 1 << bp; /* the bit mask */",
+ " if ((((char *)&(tmp->state))[ci] & bp)==0)",
+ " { if (!wasnew)",
+ " { wasnew = 1;",
+ " nShadow++;",
+ " }",
+ " ((char *)&(tmp->state))[ci] |= bp;",
+ " }",
+ " }",
+ " /* else: wasnew == 0, i.e., old state */",
+ "#endif",
+ " }",
+ "#endif",
+ "#endif",
+
+ "#if NCORE>1",
+ " Lstate = (struct H_el *) tmp;",
+ "#endif",
+
+ "#ifdef FULLSTACK",
+ "#ifndef SAFETY", /* or else wasnew == 0 */
+ " if (wasnew)",
+ " { Lstate = (struct H_el *) tmp;",
+ " tmp->tagged |= V_A;",
+ " if ((now._a_t&1)",
+ " && (tmp->tagged&A_V)",
+ " && depth > A_depth)",
+ " {",
+ "intersect:",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"1st dfs-stack intersected on state %%d+\\n\",",
+ " (int) tmp->st_id);",
+ "#endif",
+
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+
+ " return 3;",
+ " }",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"\tNew state %%d+\\n\", (int) tmp->st_id);",
+ "#endif",
+ "#ifdef DEBUG",
+ " dumpstate(1, (char *)&(tmp->state),n,tmp->tagged);",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+ " return 0;",
+ " } else",
+ "#endif",
+ " if ((S_A)?(tmp->tagged&V_A):tmp->tagged)",
+ " { Lstate = (struct H_el *) tmp;",
+ "#ifndef SAFETY",
+ " /* already on current dfs stack */",
+ " /* but may also be on 1st dfs stack */",
+ " if ((now._a_t&1)",
+ " && (tmp->tagged&A_V)",
+
+ " && depth > A_depth",
+ /* new (Zhang's example) */
+ "#ifndef NOFAIR",
+ " && (!fairness || now._cnt[1] <= 1)",
+ "#endif",
+ " )",
+
+ " goto intersect;",
+ "#endif",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"\tStack state %%d\\n\", (int) tmp->st_id);",
+ "#endif",
+ "#ifdef DEBUG",
+ " dumpstate(0, (char *)&(tmp->state),n,tmp->tagged);",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+ " return 2; /* match on stack */",
+ " }",
+ "#else",
+ " if (wasnew)",
+ " {",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"\tNew state %%d+\\n\", (int) tmp->st_id);",
+ "#endif",
+ "#ifdef DEBUG",
+ " dumpstate(1, (char *)&(tmp->state), n, 0);",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+ " return 0;",
+ " }",
+ "#endif",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"\tOld state %%d\\n\", (int) tmp->st_id);",
+ "#endif",
+ "#ifdef DEBUG",
+ " dumpstate(0, (char *)&(tmp->state), n, 0);",
+ "#endif",
+ "#ifdef REACH",
+ " if (tmp->D > depth)",
+ " { tmp->D = depth;",
+ "#ifdef CHECK",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ " printf(\"\t\tReVisiting (from smaller depth)\\n\");",
+ "#endif",
+ " nstates--;",
+#if 0
+ possible variation of iterative search for shortest counter-example (pan -i
+ and pan -I) suggested by Pierre Moro (for safety properties):
+ state revisits on shorter depths do not start until after
+ the first counter-example is found. this assumes that the max search
+ depth is set large enough that a first (possibly long) counter-example
+ can be found
+ if set too short, this variant can miss the counter-example, even if
+ it would otherwise be shorter than the depth-limit.
+ (p.m. unsure if this preserves the guarantee of finding the
+ shortest counter-example - so not enabled yet)
+ " if (errors > 0 && iterative)", /* Moro */
+#endif
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+ " return 0;",
+ " }",
+ "#endif",
+ "#if (defined(BFS) && defined(Q_PROVISO)) || NCORE>1",
+ " Lstate = (struct H_el *) tmp;",
+ "#endif",
+ "#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ "#endif",
+ " return 1; /* match outside stack */",
+ " } else if (m < 0)",
+ " { /* insert state before tmp */",
+ " ntmp = grab_state(n);",
+ "#if NCORE>1",
+ " if (!ntmp)",
+ " {",
+ " #if !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ " #endif",
+ " return 1; /* allow normal termination */",
+ " }",
+ "#endif",
+ " ntmp->nxt = tmp;",
+ " if (!olst)",
+ " H_tab[j1] = ntmp;",
+ " else",
+ " olst->nxt = ntmp;",
+ " tmp = ntmp;",
+ " break;",
+ " } else if (!tmp->nxt)",
+ " { /* append after tmp */",
+ "#ifdef COLLAPSE",
+ "Append:",
+ "#endif",
+ " tmp->nxt = grab_state(n);",
+ "#if NCORE>1",
+ " if (!tmp->nxt)",
+ " {",
+ " #if !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ " #endif",
+ " return 1; /* allow normal termination */",
+ " }",
+ "#endif",
+ " tmp = tmp->nxt;",
+ " break;",
+ " } }",
+ " }",
+ "#ifdef CHECK",
+ " tmp->st_id = (unsigned) nstates;",
+ "#if NCORE>1",
+ " printf(\"cpu%%d: \", core_id);",
+ "#endif",
+ "#ifdef BITSTATE",
+ " printf(\" Push state %%d\\n\", ((int) nstates) - 1);",
+ "#else",
+ " printf(\" New state %%d\\n\", (int) nstates);",
+ "#endif",
+ "#endif",
+ "#if !defined(SAFETY) || defined(REACH)",
+ " tmp->D = depth;",
+ "#endif",
+ "#ifndef SAFETY",
+ "#ifndef NOCOMP",
+ " if (S_A)",
+ " { v[0] = V_A;",
+ "#ifndef NOFAIR",
+ " if (S_A > NFAIR)",
+ " { unsigned ci, bp; /* as above */",
+ " ci = (now._cnt[now._a_t&1] / 8);",
+ " bp = (now._cnt[now._a_t&1] - 8*ci);",
+ " if (now._a_t&1)",
+ " { ci = (NFAIR - 1) - ci;",
+ " bp = 7 - bp; /* bp = 0..7 */",
+ " }",
+ " v[1+ci] = 1 << bp;",
+ " }",
+ "#endif",
+ " }",
+ "#endif",
+ "#endif",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE)",
+ " tmp->m_K1 = K1;",
+ "#endif",
+ " memcpy(((char *)&(tmp->state)), v, n);",
+ "#ifdef FULLSTACK",
+ " tmp->tagged = (S_A)?V_A:(depth+1);",
+ "#ifdef DEBUG",
+ " dumpstate(-1, v, n, tmp->tagged);",
+ "#endif",
+ " Lstate = (struct H_el *) tmp;",
+ "#else",
+ " #ifdef DEBUG",
+ " dumpstate(-1, v, n, 0);",
+ " #endif",
+ " #if NCORE>1",
+ " Lstate = (struct H_el *) tmp;",
+ " #endif",
+ "#endif",
+
+ "/* #if NCORE>1 && !defined(SEP_STATE) */",
+ "#if NCORE>1",
+ " #ifdef V_PROVISO",
+ " tmp->cpu_id = core_id;",
+ " #endif",
+ " #if !defined(SEP_STATE) && !defined(BITSTATE)",
+ " leave_critical(CS_ID);",
+ " #endif",
+ "#endif",
+
+ " return 0;",
+ "}",
+ "#endif",
+ "#include TRANSITIONS",
+ "void",
+ "do_reach(void)",
+ "{",
+ 0,
+};
--- /dev/null
+/***** spin: pangen2.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
+
+#include "spin.h"
+#include "version.h"
+#include "y.tab.h"
+#include "pangen2.h"
+#include "pangen4.h"
+#include "pangen5.h"
+
+#define DELTA 500 /* sets an upperbound on nr of chan names */
+
+#define blurb(fd, e) { fprintf(fd, "\n"); if (!merger) fprintf(fd, "\t\t/* %s:%d */\n", \
+ e->n->fn->name, e->n->ln); }
+#define tr_map(m, e) { if (!merger) fprintf(tt, "\t\ttr_2_src(%d, %s, %d);\n", \
+ m, e->n->fn->name, e->n->ln); }
+
+extern ProcList *rdy;
+extern RunList *run;
+extern Symbol *Fname, *oFname, *context;
+extern char *claimproc, *eventmap;
+extern int lineno, verbose, Npars, Mpars;
+extern int m_loss, has_remote, has_remvar, merger, rvopt, separate;
+extern int Ntimeouts, Etimeouts, deadvar;
+extern int u_sync, u_async, nrRdy, Unique;
+extern int GenCode, IsGuard, Level, TestOnly;
+extern short has_stack;
+extern char *NextLab[];
+
+FILE *tc, *th, *tt, *tb;
+static FILE *tm;
+
+int OkBreak = -1, has_hidden = 0; /* has_hidden set in sym.c and structs.c */
+short nocast=0; /* to turn off casts in lvalues */
+short terse=0; /* terse printing of varnames */
+short no_arrays=0;
+short has_last=0; /* spec refers to _last */
+short has_badelse=0; /* spec contains else combined with chan refs */
+short has_enabled=0; /* spec contains enabled() */
+short has_pcvalue=0; /* spec contains pc_value() */
+short has_np=0; /* spec contains np_ */
+short has_sorted=0; /* spec contains `!!' (sorted-send) operator */
+short has_random=0; /* spec contains `??' (random-recv) operator */
+short has_xu=0; /* spec contains xr or xs assertions */
+short has_unless=0; /* spec contains unless statements */
+short has_provided=0; /* spec contains PROVIDED clauses on procs */
+short has_code=0; /* spec contains c_code, c_expr, c_state */
+short evalindex=0; /* evaluate index of var names */
+int mst=0; /* max nr of state/process */
+int claimnr = -1; /* claim process, if any */
+int eventmapnr = -1; /* event trace, if any */
+int Pid; /* proc currently processed */
+int multi_oval; /* set in merges, used also in pangen4.c */
+
+#define MAXMERGE 256 /* max nr of bups per merge sequence */
+
+static short CnT[MAXMERGE];
+static Lextok XZ, YZ[MAXMERGE];
+static int didcase, YZmax, YZcnt;
+
+static Lextok *Nn[2];
+static int Det; /* set if deterministic */
+static int T_sum, T_mus, t_cyc;
+static int TPE[2], EPT[2];
+static int uniq=1;
+static int multi_needed, multi_undo;
+static short AllGlobal=0; /* set if process has provided clause */
+static short withprocname=0; /* prefix local varnames with procname */
+static short _isok=0; /* checks usage of predefined variable _ */
+
+int has_global(Lextok *);
+void Fatal(char *, char *);
+static int getweight(Lextok *);
+static int scan_seq(Sequence *);
+static void genconditionals(void);
+static void mark_seq(Sequence *);
+static void patch_atomic(Sequence *);
+static void put_seq(Sequence *, int, int);
+static void putproc(ProcList *);
+static void Tpe(Lextok *);
+extern void spit_recvs(FILE *, FILE*);
+
+static int
+fproc(char *s)
+{ ProcList *p;
+
+ for (p = rdy; p; p = p->nxt)
+ if (strcmp(p->n->name, s) == 0)
+ return p->tn;
+
+ fatal("proctype %s not found", s);
+ return -1;
+}
+
+static void
+reverse_procs(RunList *q)
+{
+ if (!q) return;
+ reverse_procs(q->nxt);
+ fprintf(tc, " Addproc(%d);\n", q->tn);
+}
+
+static void
+forward_procs(RunList *q)
+{
+ if (!q) return;
+ fprintf(tc, " Addproc(%d);\n", q->tn);
+ forward_procs(q->nxt);
+}
+
+static void
+tm_predef_np(void)
+{
+ fprintf(th, "#define _T5 %d\n", uniq++);
+ fprintf(th, "#define _T2 %d\n", uniq++);
+
+ if (Unique < (1 << (8*sizeof(unsigned char)) )) /* was uniq before */
+ { fprintf(th, "#define T_ID unsigned char\n");
+ } else if (Unique < (1 << (8*sizeof(unsigned short)) ))
+ { fprintf(th, "#define T_ID unsigned short\n");
+ } else
+ { fprintf(th, "#define T_ID unsigned int\n");
+ }
+
+ fprintf(tm, "\tcase _T5:\t/* np_ */\n");
+
+ if (separate == 2)
+ fprintf(tm, "\t\tif (!((!(o_pm&4) && !(tau&128))))\n");
+ else
+ fprintf(tm, "\t\tif (!((!(trpt->o_pm&4) && !(trpt->tau&128))))\n");
+
+ fprintf(tm, "\t\t\tcontinue;\n");
+ fprintf(tm, "\t\t/* else fall through */\n");
+ fprintf(tm, "\tcase _T2:\t/* true */\n");
+ fprintf(tm, "\t\t_m = 3; goto P999;\n");
+}
+
+static void
+tt_predef_np(void)
+{
+ fprintf(tt, "\t/* np_ demon: */\n");
+ fprintf(tt, "\ttrans[_NP_] = ");
+ fprintf(tt, "(Trans **) emalloc(2*sizeof(Trans *));\n");
+ fprintf(tt, "\tT = trans[_NP_][0] = ");
+ fprintf(tt, "settr(9997,0,1,_T5,0,\"(np_)\", 1,2,0);\n");
+ fprintf(tt, "\t T->nxt = ");
+ fprintf(tt, "settr(9998,0,0,_T2,0,\"(1)\", 0,2,0);\n");
+ fprintf(tt, "\tT = trans[_NP_][1] = ");
+ fprintf(tt, "settr(9999,0,1,_T5,0,\"(np_)\", 1,2,0);\n");
+}
+
+static struct {
+ char *nm[3];
+} Cfile[] = {
+ { { "pan.c", "pan_s.c", "pan_t.c" } },
+ { { "pan.h", "pan_s.h", "pan_t.h" } },
+ { { "pan.t", "pan_s.t", "pan_t.t" } },
+ { { "pan.m", "pan_s.m", "pan_t.m" } },
+ { { "pan.b", "pan_s.b", "pan_t.b" } }
+};
+
+void
+gensrc(void)
+{ ProcList *p;
+
+ if (!(tc = fopen(Cfile[0].nm[separate], "w")) /* main routines */
+ || !(th = fopen(Cfile[1].nm[separate], "w")) /* header file */
+ || !(tt = fopen(Cfile[2].nm[separate], "w")) /* transition matrix */
+ || !(tm = fopen(Cfile[3].nm[separate], "w")) /* forward moves */
+ || !(tb = fopen(Cfile[4].nm[separate], "w"))) /* backward moves */
+ { printf("spin: cannot create pan.[chtmfb]\n");
+ alldone(1);
+ }
+
+ fprintf(th, "#define SpinVersion \"%s\"\n", SpinVersion);
+ fprintf(th, "#define PanSource \"%s\"\n\n", oFname->name);
+
+ fprintf(th, "#ifdef WIN64\n");
+ fprintf(th, "#define ONE_L ((unsigned long) 1)\n");
+ fprintf(th, "#define long long long\n");
+ fprintf(th, "#else\n");
+ fprintf(th, "#define ONE_L (1L)\n");
+ fprintf(th, "#endif\n");
+
+ if (separate != 2)
+ { fprintf(th, "char *TrailFile = PanSource; /* default */\n");
+ fprintf(th, "char *trailfilename;\n");
+ }
+
+ fprintf(th, "#if defined(BFS)\n");
+ fprintf(th, "#ifndef SAFETY\n");
+ fprintf(th, "#define SAFETY\n");
+ fprintf(th, "#endif\n");
+ fprintf(th, "#ifndef XUSAFE\n");
+ fprintf(th, "#define XUSAFE\n");
+ fprintf(th, "#endif\n");
+ fprintf(th, "#endif\n");
+
+ fprintf(th, "#ifndef uchar\n");
+ fprintf(th, "#define uchar unsigned char\n");
+ fprintf(th, "#endif\n");
+ fprintf(th, "#ifndef uint\n");
+ fprintf(th, "#define uint unsigned int\n");
+ fprintf(th, "#endif\n");
+
+ if (sizeof(void *) > 4) /* 64 bit machine */
+ { fprintf(th, "#ifndef HASH32\n");
+ fprintf(th, "#define HASH64\n");
+ fprintf(th, "#endif\n");
+ }
+#if 0
+ if (sizeof(long)==sizeof(int))
+ fprintf(th, "#define long int\n");
+#endif
+ if (separate == 1 && !claimproc)
+ { Symbol *n = (Symbol *) emalloc(sizeof(Symbol));
+ Sequence *s = (Sequence *) emalloc(sizeof(Sequence));
+ claimproc = n->name = "_:never_template:_";
+ ready(n, ZN, s, 0, ZN);
+ }
+ if (separate == 2)
+ { if (has_remote)
+ { printf("spin: warning, make sure that the S1 model\n");
+ printf(" includes the same remote references\n");
+ }
+ fprintf(th, "#ifndef NFAIR\n");
+ fprintf(th, "#define NFAIR 2 /* must be >= 2 */\n");
+ fprintf(th, "#endif\n");
+ if (has_last)
+ fprintf(th, "#define HAS_LAST %d\n", has_last);
+ goto doless;
+ }
+
+ fprintf(th, "#define DELTA %d\n", DELTA);
+ fprintf(th, "#ifdef MA\n");
+ fprintf(th, " #if NCORE>1 && !defined(SEP_STATE)\n");
+ fprintf(th, " #define SEP_STATE\n");
+ fprintf(th, " #endif\n");
+ fprintf(th, "#if MA==1\n"); /* user typed -DMA without size */
+ fprintf(th, "#undef MA\n#define MA 100\n");
+ fprintf(th, "#endif\n#endif\n");
+ fprintf(th, "#ifdef W_XPT\n");
+ fprintf(th, "#if W_XPT==1\n"); /* user typed -DW_XPT without size */
+ fprintf(th, "#undef W_XPT\n#define W_XPT 1000000\n");
+ fprintf(th, "#endif\n#endif\n");
+ fprintf(th, "#ifndef NFAIR\n");
+ fprintf(th, "#define NFAIR 2 /* must be >= 2 */\n");
+ fprintf(th, "#endif\n");
+ if (Ntimeouts)
+ fprintf(th, "#define NTIM %d\n", Ntimeouts);
+ if (Etimeouts)
+ fprintf(th, "#define ETIM %d\n", Etimeouts);
+ if (has_remvar)
+ fprintf(th, "#define REM_VARS 1\n");
+ if (has_remote)
+ fprintf(th, "#define REM_REFS %d\n", has_remote); /* not yet used */
+ if (has_hidden)
+ fprintf(th, "#define HAS_HIDDEN %d\n", has_hidden);
+ if (has_last)
+ fprintf(th, "#define HAS_LAST %d\n", has_last);
+ if (has_sorted)
+ fprintf(th, "#define HAS_SORTED %d\n", has_sorted);
+ if (m_loss)
+ fprintf(th, "#define M_LOSS\n");
+ if (has_random)
+ fprintf(th, "#define HAS_RANDOM %d\n", has_random);
+ fprintf(th, "#define HAS_CODE\n"); /* doesn't seem to cause measurable overhead */
+ if (has_stack)
+ fprintf(th, "#define HAS_STACK %d\n", has_stack);
+ if (has_enabled)
+ fprintf(th, "#define HAS_ENABLED 1\n");
+ if (has_unless)
+ fprintf(th, "#define HAS_UNLESS %d\n", has_unless);
+ if (has_provided)
+ fprintf(th, "#define HAS_PROVIDED %d\n", has_provided);
+ if (has_pcvalue)
+ fprintf(th, "#define HAS_PCVALUE %d\n", has_pcvalue);
+ if (has_badelse)
+ fprintf(th, "#define HAS_BADELSE %d\n", has_badelse);
+ if (has_enabled
+ || has_pcvalue
+ || has_badelse
+ || has_last)
+ { fprintf(th, "#ifndef NOREDUCE\n");
+ fprintf(th, "#define NOREDUCE 1\n");
+ fprintf(th, "#endif\n");
+ }
+ if (has_np)
+ fprintf(th, "#define HAS_NP %d\n", has_np);
+ if (merger)
+ fprintf(th, "#define MERGED 1\n");
+
+doless:
+ fprintf(th, "#ifdef NP /* includes np_ demon */\n");
+ if (!has_np)
+ fprintf(th, "#define HAS_NP 2\n");
+ fprintf(th, "#define VERI %d\n", nrRdy);
+ fprintf(th, "#define endclaim 3 /* none */\n");
+ fprintf(th, "#endif\n");
+ if (claimproc)
+ { claimnr = fproc(claimproc);
+ /* NP overrides claimproc */
+ fprintf(th, "#if !defined(NOCLAIM) && !defined NP\n");
+ fprintf(th, "#define VERI %d\n", claimnr);
+ fprintf(th, "#define endclaim endstate%d\n", claimnr);
+ fprintf(th, "#endif\n");
+ }
+ if (eventmap)
+ { eventmapnr = fproc(eventmap);
+ fprintf(th, "#define EVENT_TRACE %d\n", eventmapnr);
+ fprintf(th, "#define endevent endstate%d\n", eventmapnr);
+ if (eventmap[2] == 'o') /* ":notrace:" */
+ fprintf(th, "#define NEGATED_TRACE 1\n");
+ }
+
+ fprintf(th, "typedef struct S_F_MAP {\n");
+ fprintf(th, " char *fnm; int from; int upto;\n");
+ fprintf(th, "} S_F_MAP;\n");
+
+ fprintf(tc, "/*** Generated by %s ***/\n", SpinVersion);
+ fprintf(tc, "/*** From source: %s ***/\n\n", oFname->name);
+
+ ntimes(tc, 0, 1, Pre0);
+
+ plunk_c_decls(tc); /* types can be refered to in State */
+
+ switch (separate) {
+ case 0: fprintf(tc, "#include \"pan.h\"\n"); break;
+ case 1: fprintf(tc, "#include \"pan_s.h\"\n"); break;
+ case 2: fprintf(tc, "#include \"pan_t.h\"\n"); break;
+ }
+
+ fprintf(tc, "#ifdef LOOPSTATE\n");
+ fprintf(tc, "double cnt_loops;\n");
+ fprintf(tc, "#endif\n");
+
+ fprintf(tc, "State A_Root; /* seed-state for cycles */\n");
+ fprintf(tc, "State now; /* the full state-vector */\n");
+ plunk_c_fcts(tc); /* State can be used in fcts */
+
+ if (separate != 2)
+ ntimes(tc, 0, 1, Preamble);
+ else
+ fprintf(tc, "extern int verbose; extern long depth;\n");
+
+ fprintf(tc, "#ifndef NOBOUNDCHECK\n");
+ fprintf(tc, "#define Index(x, y)\tBoundcheck(x, y, II, tt, t)\n");
+ fprintf(tc, "#else\n");
+ fprintf(tc, "#define Index(x, y)\tx\n");
+ fprintf(tc, "#endif\n");
+
+ c_preview(); /* sets hastrack */
+
+ for (p = rdy; p; p = p->nxt)
+ mst = max(p->s->maxel, mst);
+
+ if (separate != 2)
+ { fprintf(tt, "#ifdef PEG\n");
+ fprintf(tt, "struct T_SRC {\n");
+ fprintf(tt, " char *fl; int ln;\n");
+ fprintf(tt, "} T_SRC[NTRANS];\n\n");
+ fprintf(tt, "void\ntr_2_src(int m, char *file, int ln)\n");
+ fprintf(tt, "{ T_SRC[m].fl = file;\n");
+ fprintf(tt, " T_SRC[m].ln = ln;\n");
+ fprintf(tt, "}\n\n");
+ fprintf(tt, "void\nputpeg(int n, int m)\n");
+ fprintf(tt, "{ printf(\"%%5d\ttrans %%4d \", m, n);\n");
+ fprintf(tt, " printf(\"file %%s line %%3d\\n\",\n");
+ fprintf(tt, " T_SRC[n].fl, T_SRC[n].ln);\n");
+ fprintf(tt, "}\n");
+ if (!merger)
+ { fprintf(tt, "#else\n");
+ fprintf(tt, "#define tr_2_src(m,f,l)\n");
+ }
+ fprintf(tt, "#endif\n\n");
+ fprintf(tt, "void\nsettable(void)\n{\tTrans *T;\n");
+ fprintf(tt, "\tTrans *settr(int, int, int, int, int,");
+ fprintf(tt, " char *, int, int, int);\n\n");
+ fprintf(tt, "\ttrans = (Trans ***) ");
+ fprintf(tt, "emalloc(%d*sizeof(Trans **));\n", nrRdy+1);
+ /* +1 for np_ automaton */
+
+ if (separate == 1)
+ {
+ fprintf(tm, " if (II == 0)\n");
+ fprintf(tm, " { _m = step_claim(trpt->o_pm, trpt->tau, tt, ot, t);\n");
+ fprintf(tm, " if (_m) goto P999; else continue;\n");
+ fprintf(tm, " } else\n");
+ }
+
+ fprintf(tm, "#define rand pan_rand\n");
+ fprintf(tm, "#if defined(HAS_CODE) && defined(VERBOSE)\n");
+ fprintf(tm, " cpu_printf(\"Pr: %%d Tr: %%d\\n\", II, t->forw);\n");
+ fprintf(tm, "#endif\n");
+ fprintf(tm, " switch (t->forw) {\n");
+ } else
+ { fprintf(tt, "#ifndef PEG\n");
+ fprintf(tt, "#define tr_2_src(m,f,l)\n");
+ fprintf(tt, "#endif\n");
+ fprintf(tt, "void\nset_claim(void)\n{\tTrans *T;\n");
+ fprintf(tt, "\textern Trans ***trans;\n");
+ fprintf(tt, "\textern Trans *settr(int, int, int, int, int,");
+ fprintf(tt, " char *, int, int, int);\n\n");
+
+ fprintf(tm, "#define rand pan_rand\n");
+ fprintf(tm, "#if defined(HAS_CODE) && defined(VERBOSE)\n");
+ fprintf(tm, " cpu_printf(\"Pr: %%d Tr: %%d\\n\", II, forw);\n");
+ fprintf(tm, "#endif\n");
+ fprintf(tm, " switch (forw) {\n");
+ }
+
+ fprintf(tm, " default: Uerror(\"bad forward move\");\n");
+ fprintf(tm, " case 0: /* if without executable clauses */\n");
+ fprintf(tm, " continue;\n");
+ fprintf(tm, " case 1: /* generic 'goto' or 'skip' */\n");
+ if (separate != 2)
+ fprintf(tm, " IfNotBlocked\n");
+ fprintf(tm, " _m = 3; goto P999;\n");
+ fprintf(tm, " case 2: /* generic 'else' */\n");
+ if (separate == 2)
+ fprintf(tm, " if (o_pm&1) continue;\n");
+ else
+ { fprintf(tm, " IfNotBlocked\n");
+ fprintf(tm, " if (trpt->o_pm&1) continue;\n");
+ }
+ fprintf(tm, " _m = 3; goto P999;\n");
+ uniq = 3;
+
+ if (separate == 1)
+ fprintf(tb, " if (II == 0) goto R999;\n");
+
+ fprintf(tb, " switch (t->back) {\n");
+ fprintf(tb, " default: Uerror(\"bad return move\");\n");
+ fprintf(tb, " case 0: goto R999; /* nothing to undo */\n");
+
+ for (p = rdy; p; p = p->nxt)
+ putproc(p);
+
+
+ if (separate != 2)
+ { fprintf(th, "struct {\n");
+ fprintf(th, " int tp; short *src;\n");
+ fprintf(th, "} src_all[] = {\n");
+ for (p = rdy; p; p = p->nxt)
+ fprintf(th, " { %d, &src_ln%d[0] },\n",
+ p->tn, p->tn);
+ fprintf(th, " { 0, (short *) 0 }\n");
+ fprintf(th, "};\n");
+ fprintf(th, "short *frm_st0;\n"); /* records src states for transitions in never claim */
+ } else
+ { fprintf(th, "extern short *frm_st0;\n");
+ }
+
+ gencodetable(th);
+
+ if (separate != 1)
+ { tm_predef_np();
+ tt_predef_np();
+ }
+ fprintf(tt, "}\n\n"); /* end of settable() */
+
+ fprintf(tm, "#undef rand\n");
+ fprintf(tm, " }\n\n");
+ fprintf(tb, " }\n\n");
+
+ if (separate != 2)
+ { ntimes(tt, 0, 1, Tail);
+ genheader();
+ if (separate == 1)
+ { fprintf(th, "#define FORWARD_MOVES\t\"pan_s.m\"\n");
+ fprintf(th, "#define REVERSE_MOVES\t\"pan_s.b\"\n");
+ fprintf(th, "#define SEPARATE\n");
+ fprintf(th, "#define TRANSITIONS\t\"pan_s.t\"\n");
+ fprintf(th, "extern void ini_claim(int, int);\n");
+ } else
+ { fprintf(th, "#define FORWARD_MOVES\t\"pan.m\"\n");
+ fprintf(th, "#define REVERSE_MOVES\t\"pan.b\"\n");
+ fprintf(th, "#define TRANSITIONS\t\"pan.t\"\n");
+ }
+ genaddproc();
+ genother();
+ genaddqueue();
+ genunio();
+ genconditionals();
+ gensvmap();
+ if (!run) fatal("no runable process", (char *)0);
+ fprintf(tc, "void\n");
+ fprintf(tc, "active_procs(void)\n{\n");
+#if 1
+ fprintf(tc, " if (!permuted) {\n");
+ reverse_procs(run);
+ fprintf(tc, " } else {\n");
+ forward_procs(run);
+ fprintf(tc, " }\n");
+#else
+ reverse_procs(run);
+#endif
+ fprintf(tc, "}\n");
+ ntimes(tc, 0, 1, Dfa);
+ ntimes(tc, 0, 1, Xpt);
+
+ fprintf(th, "#define NTRANS %d\n", uniq);
+ fprintf(th, "#ifdef PEG\n");
+ fprintf(th, "long peg[NTRANS];\n");
+ fprintf(th, "#endif\n");
+
+ if (u_sync && !u_async)
+ spit_recvs(th, tc);
+ } else
+ { genheader();
+ fprintf(th, "#define FORWARD_MOVES\t\"pan_t.m\"\n");
+ fprintf(th, "#define REVERSE_MOVES\t\"pan_t.b\"\n");
+ fprintf(th, "#define TRANSITIONS\t\"pan_t.t\"\n");
+ fprintf(tc, "extern int Maxbody;\n");
+ fprintf(tc, "#if VECTORSZ>32000\n");
+ fprintf(tc, "extern int proc_offset[];\n");
+ fprintf(tc, "#else\n");
+ fprintf(tc, "extern short proc_offset[];\n");
+ fprintf(tc, "#endif\n");
+ fprintf(tc, "extern uchar proc_skip[];\n");
+ fprintf(tc, "extern uchar *reached[];\n");
+ fprintf(tc, "extern uchar *accpstate[];\n");
+ fprintf(tc, "extern uchar *progstate[];\n");
+ fprintf(tc, "extern uchar *stopstate[];\n");
+ fprintf(tc, "extern uchar *visstate[];\n\n");
+ fprintf(tc, "extern short *mapstate[];\n");
+
+ fprintf(tc, "void\nini_claim(int n, int h)\n{");
+ fprintf(tc, "\textern State now;\n");
+ fprintf(tc, "\textern void set_claim(void);\n\n");
+ fprintf(tc, "#ifdef PROV\n");
+ fprintf(tc, "#include PROV\n");
+ fprintf(tc, "#endif\n");
+ fprintf(tc, "\tset_claim();\n");
+ genother();
+ fprintf(tc, "\n\tswitch (n) {\n");
+ genaddproc();
+ fprintf(tc, "\t}\n");
+ fprintf(tc, "\n}\n");
+ fprintf(tc, "int\nstep_claim(int o_pm, int tau, int tt, int ot, Trans *t)\n");
+ fprintf(tc, "{ int forw = t->forw; int _m = 0; extern char *noptr; int II=0;\n");
+ fprintf(tc, " extern State now;\n");
+ fprintf(tc, "#define continue return 0\n");
+ fprintf(tc, "#include \"pan_t.m\"\n");
+ fprintf(tc, "P999:\n\treturn _m;\n}\n");
+ fprintf(tc, "#undef continue\n");
+ fprintf(tc, "int\nrev_claim(int backw)\n{ return 0; }\n");
+ fprintf(tc, "#include TRANSITIONS\n");
+ }
+ if (separate != 1)
+ ntimes(tc, 0, 1, Nvr1);
+
+ if (separate != 2)
+ { c_wrapper(tc);
+ c_chandump(tc);
+ }
+}
+
+static int
+find_id(Symbol *s)
+{ ProcList *p;
+
+ if (s)
+ for (p = rdy; p; p = p->nxt)
+ if (s == p->n)
+ return p->tn;
+ return 0;
+}
+
+static void
+dolen(Symbol *s, char *pre, int pid, int ai, int qln)
+{
+ if (ai > 0)
+ fprintf(tc, "\n\t\t\t || ");
+ fprintf(tc, "%s(", pre);
+ if (!(s->hidden&1))
+ { if (s->context)
+ fprintf(tc, "((P%d *)this)->", pid);
+ else
+ fprintf(tc, "now.");
+ }
+ fprintf(tc, "%s", s->name);
+ if (qln > 1) fprintf(tc, "[%d]", ai);
+ fprintf(tc, ")");
+}
+
+struct AA { char TT[9]; char CC[8]; };
+
+static struct AA BB[4] = {
+ { "Q_FULL_F", " q_full" },
+ { "Q_FULL_T", "!q_full" },
+ { "Q_EMPT_F", " !q_len" },
+ { "Q_EMPT_T", " q_len" }
+ };
+
+static struct AA DD[4] = {
+ { "Q_FULL_F", " q_e_f" }, /* empty or full */
+ { "Q_FULL_T", "!q_full" },
+ { "Q_EMPT_F", " q_e_f" },
+ { "Q_EMPT_T", " q_len" }
+ };
+ /* this reduces the number of cases where 's' and 'r'
+ are considered conditionally safe under the
+ partial order reduction rules; as a price for
+ this simple implementation, it also affects the
+ cases where nfull and nempty can be considered
+ safe -- since these are labeled the same way as
+ 's' and 'r' respectively
+ it only affects reduction, not functionality
+ */
+
+void
+bb_or_dd(int j, int which)
+{
+ if (which)
+ { if (has_unless)
+ fprintf(tc, "%s", DD[j].CC);
+ else
+ fprintf(tc, "%s", BB[j].CC);
+ } else
+ { if (has_unless)
+ fprintf(tc, "%s", DD[j].TT);
+ else
+ fprintf(tc, "%s", BB[j].TT);
+ }
+}
+
+void
+Done_case(char *nm, Symbol *z)
+{ int j, k;
+ int nid = z->Nid;
+ int qln = z->nel;
+
+ fprintf(tc, "\t\tcase %d: if (", nid);
+ for (j = 0; j < 4; j++)
+ { fprintf(tc, "\t(t->ty[i] == ");
+ bb_or_dd(j, 0);
+ fprintf(tc, " && (");
+ for (k = 0; k < qln; k++)
+ { if (k > 0)
+ fprintf(tc, "\n\t\t\t || ");
+ bb_or_dd(j, 1);
+ fprintf(tc, "(%s%s", nm, z->name);
+ if (qln > 1)
+ fprintf(tc, "[%d]", k);
+ fprintf(tc, ")");
+ }
+ fprintf(tc, "))\n\t\t\t ");
+ if (j < 3)
+ fprintf(tc, "|| ");
+ else
+ fprintf(tc, " ");
+ }
+ fprintf(tc, ") return 0; break;\n");
+}
+
+static void
+Docase(Symbol *s, int pid, int nid)
+{ int i, j;
+
+ fprintf(tc, "\t\tcase %d: if (", nid);
+ for (j = 0; j < 4; j++)
+ { fprintf(tc, "\t(t->ty[i] == ");
+ bb_or_dd(j, 0);
+ fprintf(tc, " && (");
+ if (has_unless)
+ { for (i = 0; i < s->nel; i++)
+ dolen(s, DD[j].CC, pid, i, s->nel);
+ } else
+ { for (i = 0; i < s->nel; i++)
+ dolen(s, BB[j].CC, pid, i, s->nel);
+ }
+ fprintf(tc, "))\n\t\t\t ");
+ if (j < 3)
+ fprintf(tc, "|| ");
+ else
+ fprintf(tc, " ");
+ }
+ fprintf(tc, ") return 0; break;\n");
+}
+
+static void
+genconditionals(void)
+{ Symbol *s;
+ int last=0, j;
+ extern Ordered *all_names;
+ Ordered *walk;
+
+ fprintf(th, "#define LOCAL 1\n");
+ fprintf(th, "#define Q_FULL_F 2\n");
+ fprintf(th, "#define Q_EMPT_F 3\n");
+ fprintf(th, "#define Q_EMPT_T 4\n");
+ fprintf(th, "#define Q_FULL_T 5\n");
+ fprintf(th, "#define TIMEOUT_F 6\n");
+ fprintf(th, "#define GLOBAL 7\n");
+ fprintf(th, "#define BAD 8\n");
+ fprintf(th, "#define ALPHA_F 9\n");
+
+ fprintf(tc, "int\n");
+ fprintf(tc, "q_cond(short II, Trans *t)\n");
+ fprintf(tc, "{ int i = 0;\n");
+ fprintf(tc, " for (i = 0; i < 6; i++)\n");
+ fprintf(tc, " { if (t->ty[i] == TIMEOUT_F) return %s;\n",
+ (Etimeouts)?"(!(trpt->tau&1))":"1");
+ fprintf(tc, " if (t->ty[i] == ALPHA_F)\n");
+ fprintf(tc, "#ifdef GLOB_ALPHA\n");
+ fprintf(tc, " return 0;\n");
+ fprintf(tc, "#else\n\t\t\treturn ");
+ fprintf(tc, "(II+1 == (short) now._nr_pr && II+1 < MAXPROC);\n");
+ fprintf(tc, "#endif\n");
+
+ /* we switch on the chan name from the spec (as identified by
+ * the corresponding Nid number) rather than the actual qid
+ * because we cannot predict at compile time which specific qid
+ * will be accessed by the statement at runtime. that is:
+ * we do not know which qid to pass to q_cond at runtime
+ * but we do know which name is used. if it's a chan array, we
+ * must check all elements of the array for compliance (bummer)
+ */
+ fprintf(tc, " switch (t->qu[i]) {\n");
+ fprintf(tc, " case 0: break;\n");
+
+ for (walk = all_names; walk; walk = walk->next)
+ { s = walk->entry;
+ if (s->owner) continue;
+ j = find_id(s->context);
+ if (s->type == CHAN)
+ { if (last == s->Nid) continue; /* chan array */
+ last = s->Nid;
+ Docase(s, j, last);
+ } else if (s->type == STRUCT)
+ { /* struct may contain a chan */
+ char pregat[128];
+ extern void walk2_struct(char *, Symbol *);
+ strcpy(pregat, "");
+ if (!(s->hidden&1))
+ { if (s->context)
+ sprintf(pregat, "((P%d *)this)->",j);
+ else
+ sprintf(pregat, "now.");
+ }
+ walk2_struct(pregat, s);
+ }
+ }
+ fprintf(tc, " \tdefault: Uerror(\"unknown qid - q_cond\");\n");
+ fprintf(tc, " \t\t\treturn 0;\n");
+ fprintf(tc, " \t}\n");
+ fprintf(tc, " }\n");
+ fprintf(tc, " return 1;\n");
+ fprintf(tc, "}\n");
+}
+
+static void
+putproc(ProcList *p)
+{ Pid = p->tn;
+ Det = p->det;
+
+ if (Pid == claimnr
+ && separate == 1)
+ { fprintf(th, "extern uchar reached%d[];\n", Pid);
+#if 0
+ fprintf(th, "extern short nstates%d;\n", Pid);
+#else
+ fprintf(th, "\n#define nstates%d %d\t/* %s */\n",
+ Pid, p->s->maxel, p->n->name);
+#endif
+ fprintf(th, "extern short src_ln%d[];\n", Pid);
+ fprintf(th, "extern uchar *loopstate%d;\n", Pid);
+ fprintf(th, "extern S_F_MAP src_file%d[];\n", Pid);
+ fprintf(th, "#define endstate%d %d\n",
+ Pid, p->s->last?p->s->last->seqno:0);
+ fprintf(th, "#define src_claim src_ln%d\n", claimnr);
+
+ return;
+ }
+ if (Pid != claimnr
+ && separate == 2)
+ { fprintf(th, "extern short src_ln%d[];\n", Pid);
+ fprintf(th, "extern uchar *loopstate%d;\n", Pid);
+ return;
+ }
+
+ AllGlobal = (p->prov)?1:0; /* process has provided clause */
+
+ fprintf(th, "\n#define nstates%d %d\t/* %s */\n",
+ Pid, p->s->maxel, p->n->name);
+ if (Pid == claimnr)
+ fprintf(th, "#define nstates_claim nstates%d\n", Pid);
+ if (Pid == eventmapnr)
+ fprintf(th, "#define nstates_event nstates%d\n", Pid);
+
+ fprintf(th, "#define endstate%d %d\n",
+ Pid, p->s->last?p->s->last->seqno:0);
+ fprintf(tm, "\n /* PROC %s */\n", p->n->name);
+ fprintf(tb, "\n /* PROC %s */\n", p->n->name);
+ fprintf(tt, "\n /* proctype %d: %s */\n", Pid, p->n->name);
+ fprintf(tt, "\n trans[%d] = (Trans **)", Pid);
+ fprintf(tt, " emalloc(%d*sizeof(Trans *));\n\n", p->s->maxel);
+
+ if (Pid == eventmapnr)
+ { fprintf(th, "\n#define in_s_scope(x_y3_) 0");
+ fprintf(tc, "\n#define in_r_scope(x_y3_) 0");
+ }
+
+ put_seq(p->s, 2, 0);
+ if (Pid == eventmapnr)
+ { fprintf(th, "\n\n");
+ fprintf(tc, "\n\n");
+ }
+ dumpsrc(p->s->maxel, Pid);
+}
+
+static void
+addTpe(int x)
+{ int i;
+
+ if (x <= 2) return;
+
+ for (i = 0; i < T_sum; i++)
+ if (TPE[i] == x)
+ return;
+ TPE[(T_sum++)%2] = x;
+}
+
+static void
+cnt_seq(Sequence *s)
+{ Element *f;
+ SeqList *h;
+
+ if (s)
+ for (f = s->frst; f; f = f->nxt)
+ { Tpe(f->n); /* sets EPT */
+ addTpe(EPT[0]);
+ addTpe(EPT[1]);
+ for (h = f->sub; h; h = h->nxt)
+ cnt_seq(h->this);
+ if (f == s->last)
+ break;
+ }
+}
+
+static void
+typ_seq(Sequence *s)
+{
+ T_sum = 0;
+ TPE[0] = 2; TPE[1] = 0;
+ cnt_seq(s);
+ if (T_sum > 2) /* more than one type */
+ { TPE[0] = 5*DELTA; /* non-mixing */
+ TPE[1] = 0;
+ }
+}
+
+static int
+hidden(Lextok *n)
+{
+ if (n)
+ switch (n->ntyp) {
+ case FULL: case EMPTY:
+ case NFULL: case NEMPTY: case TIMEOUT:
+ Nn[(T_mus++)%2] = n;
+ break;
+ case '!': case UMIN: case '~': case ASSERT: case 'c':
+ (void) hidden(n->lft);
+ break;
+ case '/': case '*': case '-': case '+':
+ case '%': case LT: case GT: case '&': case '^':
+ case '|': case LE: case GE: case NE: case '?':
+ case EQ: case OR: case AND: case LSHIFT: case RSHIFT:
+ (void) hidden(n->lft);
+ (void) hidden(n->rgt);
+ break;
+ }
+ return T_mus;
+}
+
+static int
+getNid(Lextok *n)
+{
+ if (n->sym
+ && n->sym->type == STRUCT
+ && n->rgt && n->rgt->lft)
+ return getNid(n->rgt->lft);
+
+ if (!n->sym || n->sym->Nid == 0)
+ { fatal("bad channel name '%s'",
+ (n->sym)?n->sym->name:"no name");
+ }
+ return n->sym->Nid;
+}
+
+static int
+valTpe(Lextok *n)
+{ int res = 2;
+ /*
+ 2 = local
+ 2+1 .. 2+1*DELTA = nfull, 's' - require q_full==false
+ 2+1+1*DELTA .. 2+2*DELTA = nempty, 'r' - require q_len!=0
+ 2+1+2*DELTA .. 2+3*DELTA = empty - require q_len==0
+ 2+1+3*DELTA .. 2+4*DELTA = full - require q_full==true
+ 5*DELTA = non-mixing (i.e., always makes the selection global)
+ 6*DELTA = timeout (conditionally safe)
+ 7*DELTA = @, process deletion (conditionally safe)
+ */
+ switch (n->ntyp) { /* a series of fall-thru cases: */
+ case FULL: res += DELTA; /* add 3*DELTA + chan nr */
+ case EMPTY: res += DELTA; /* add 2*DELTA + chan nr */
+ case 'r':
+ case NEMPTY: res += DELTA; /* add 1*DELTA + chan nr */
+ case 's':
+ case NFULL: res += getNid(n->lft); /* add channel nr */
+ break;
+
+ case TIMEOUT: res = 6*DELTA; break;
+ case '@': res = 7*DELTA; break;
+ default: break;
+ }
+ return res;
+}
+
+static void
+Tpe(Lextok *n) /* mixing in selections */
+{
+ EPT[0] = 2; EPT[1] = 0;
+
+ if (!n) return;
+
+ T_mus = 0;
+ Nn[0] = Nn[1] = ZN;
+
+ if (n->ntyp == 'c')
+ { if (hidden(n->lft) > 2)
+ { EPT[0] = 5*DELTA; /* non-mixing */
+ EPT[1] = 0;
+ return;
+ }
+ } else
+ Nn[0] = n;
+
+ if (Nn[0]) EPT[0] = valTpe(Nn[0]);
+ if (Nn[1]) EPT[1] = valTpe(Nn[1]);
+}
+
+static void
+put_escp(Element *e)
+{ int n;
+ SeqList *x;
+
+ if (e->esc /* && e->n->ntyp != GOTO */ && e->n->ntyp != '.')
+ { for (x = e->esc, n = 0; x; x = x->nxt, n++)
+ { int i = huntele(x->this->frst, e->status, -1)->seqno;
+ fprintf(tt, "\ttrans[%d][%d]->escp[%d] = %d;\n",
+ Pid, e->seqno, n, i);
+ fprintf(tt, "\treached%d[%d] = 1;\n",
+ Pid, i);
+ }
+ for (x = e->esc, n=0; x; x = x->nxt, n++)
+ { fprintf(tt, " /* escape #%d: %d */\n", n,
+ huntele(x->this->frst, e->status, -1)->seqno);
+ put_seq(x->this, 2, 0); /* args?? */
+ }
+ fprintf(tt, " /* end-escapes */\n");
+ }
+}
+
+static void
+put_sub(Element *e, int Tt0, int Tt1)
+{ Sequence *s = e->n->sl->this;
+ Element *g = ZE;
+ int a;
+
+ patch_atomic(s);
+ putskip(s->frst->seqno);
+ g = huntstart(s->frst);
+ a = g->seqno;
+
+ if (0) printf("put_sub %d -> %d -> %d\n", e->seqno, s->frst->seqno, a);
+
+ if ((e->n->ntyp == ATOMIC
+ || e->n->ntyp == D_STEP)
+ && scan_seq(s))
+ mark_seq(s);
+ s->last->nxt = e->nxt;
+
+ typ_seq(s); /* sets TPE */
+
+ if (e->n->ntyp == D_STEP)
+ { int inherit = (e->status&(ATOM|L_ATOM));
+ fprintf(tm, "\tcase %d: ", uniq++);
+ fprintf(tm, "/* STATE %d - line %d %s - [",
+ e->seqno, e->n->ln, e->n->fn->name);
+ comment(tm, e->n, 0);
+ fprintf(tm, "] */\n\t\t");
+
+ if (s->last->n->ntyp == BREAK)
+ OkBreak = target(huntele(s->last->nxt,
+ s->last->status, -1))->Seqno;
+ else
+ OkBreak = -1;
+
+ if (!putcode(tm, s, e->nxt, 0, e->n->ln, e->seqno))
+ {
+ fprintf(tm, "\n#if defined(C_States) && (HAS_TRACK==1)\n");
+ fprintf(tm, "\t\tc_update((uchar *) &(now.c_state[0]));\n");
+ fprintf(tm, "#endif\n");
+
+ fprintf(tm, "\t\t_m = %d", getweight(s->frst->n));
+ if (m_loss && s->frst->n->ntyp == 's')
+ fprintf(tm, "+delta_m; delta_m = 0");
+ fprintf(tm, "; goto P999;\n\n");
+ }
+
+ fprintf(tb, "\tcase %d: ", uniq-1);
+ fprintf(tb, "/* STATE %d */\n", e->seqno);
+ fprintf(tb, "\t\tsv_restor();\n");
+ fprintf(tb, "\t\tgoto R999;\n");
+ if (e->nxt)
+ a = huntele(e->nxt, e->status, -1)->seqno;
+ else
+ a = 0;
+ tr_map(uniq-1, e);
+ fprintf(tt, "/*->*/\ttrans[%d][%d]\t= ",
+ Pid, e->seqno);
+ fprintf(tt, "settr(%d,%d,%d,%d,%d,\"",
+ e->Seqno, D_ATOM|inherit, a, uniq-1, uniq-1);
+ comment(tt, e->n, e->seqno);
+ fprintf(tt, "\", %d, ", (s->frst->status&I_GLOB)?1:0);
+ fprintf(tt, "%d, %d);\n", TPE[0], TPE[1]);
+ put_escp(e);
+ } else
+ { /* ATOMIC or NON_ATOMIC */
+ fprintf(tt, "\tT = trans[ %d][%d] = ", Pid, e->seqno);
+ fprintf(tt, "settr(%d,%d,0,0,0,\"",
+ e->Seqno, (e->n->ntyp == ATOMIC)?ATOM:0);
+ comment(tt, e->n, e->seqno);
+ if ((e->status&CHECK2)
+ || (g->status&CHECK2))
+ s->frst->status |= I_GLOB;
+ fprintf(tt, "\", %d, %d, %d);",
+ (s->frst->status&I_GLOB)?1:0, Tt0, Tt1);
+ blurb(tt, e);
+ fprintf(tt, "\tT->nxt\t= ");
+ fprintf(tt, "settr(%d,%d,%d,0,0,\"",
+ e->Seqno, (e->n->ntyp == ATOMIC)?ATOM:0, a);
+ comment(tt, e->n, e->seqno);
+ fprintf(tt, "\", %d, ", (s->frst->status&I_GLOB)?1:0);
+ if (e->n->ntyp == NON_ATOMIC)
+ { fprintf(tt, "%d, %d);", Tt0, Tt1);
+ blurb(tt, e);
+ put_seq(s, Tt0, Tt1);
+ } else
+ { fprintf(tt, "%d, %d);", TPE[0], TPE[1]);
+ blurb(tt, e);
+ put_seq(s, TPE[0], TPE[1]);
+ }
+ }
+}
+
+typedef struct CaseCache {
+ int m, b, owner;
+ Element *e;
+ Lextok *n;
+ FSM_use *u;
+ struct CaseCache *nxt;
+} CaseCache;
+
+static CaseCache *casing[6];
+
+static int
+identical(Lextok *p, Lextok *q)
+{
+ if ((!p && q) || (p && !q))
+ return 0;
+ if (!p)
+ return 1;
+
+ if (p->ntyp != q->ntyp
+ || p->ismtyp != q->ismtyp
+ || p->val != q->val
+ || p->indstep != q->indstep
+ || p->sym != q->sym
+ || p->sq != q->sq
+ || p->sl != q->sl)
+ return 0;
+
+ return identical(p->lft, q->lft)
+ && identical(p->rgt, q->rgt);
+}
+
+static int
+samedeads(FSM_use *a, FSM_use *b)
+{ FSM_use *p, *q;
+
+ for (p = a, q = b; p && q; p = p->nxt, q = q->nxt)
+ if (p->var != q->var
+ || p->special != q->special)
+ return 0;
+ return (!p && !q);
+}
+
+static Element *
+findnext(Element *f)
+{ Element *g;
+
+ if (f->n->ntyp == GOTO)
+ { g = get_lab(f->n, 1);
+ return huntele(g, f->status, -1);
+ }
+ return f->nxt;
+}
+
+static Element *
+advance(Element *e, int stopat)
+{ Element *f = e;
+
+ if (stopat)
+ while (f && f->seqno != stopat)
+ { f = findnext(f);
+ if (!f)
+ { break;
+ }
+ switch (f->n->ntyp) {
+ case GOTO:
+ case '.':
+ case PRINT:
+ case PRINTM:
+ break;
+ default:
+ return f;
+ } }
+ return (Element *) 0;
+}
+
+static int
+equiv_merges(Element *a, Element *b)
+{ Element *f, *g;
+ int stopat_a, stopat_b;
+
+ if (a->merge_start)
+ stopat_a = a->merge_start;
+ else
+ stopat_a = a->merge;
+
+ if (b->merge_start)
+ stopat_b = b->merge_start;
+ else
+ stopat_b = b->merge;
+
+ if (!stopat_a && !stopat_b)
+ return 1;
+
+ for (;;)
+ {
+ f = advance(a, stopat_a);
+ g = advance(b, stopat_b);
+ if (!f && !g)
+ return 1;
+ if (f && g)
+ return identical(f->n, g->n);
+ else
+ return 0;
+ }
+ return 1; /* not reached */
+}
+
+static CaseCache *
+prev_case(Element *e, int owner)
+{ int j; CaseCache *nc;
+
+ switch (e->n->ntyp) {
+ case 'r': j = 0; break;
+ case 's': j = 1; break;
+ case 'c': j = 2; break;
+ case ASGN: j = 3; break;
+ case ASSERT: j = 4; break;
+ default: j = 5; break;
+ }
+ for (nc = casing[j]; nc; nc = nc->nxt)
+ if (identical(nc->n, e->n)
+ && samedeads(nc->u, e->dead)
+ && equiv_merges(nc->e, e)
+ && nc->owner == owner)
+ return nc;
+
+ return (CaseCache *) 0;
+}
+
+static void
+new_case(Element *e, int m, int b, int owner)
+{ int j; CaseCache *nc;
+
+ switch (e->n->ntyp) {
+ case 'r': j = 0; break;
+ case 's': j = 1; break;
+ case 'c': j = 2; break;
+ case ASGN: j = 3; break;
+ case ASSERT: j = 4; break;
+ default: j = 5; break;
+ }
+ nc = (CaseCache *) emalloc(sizeof(CaseCache));
+ nc->owner = owner;
+ nc->m = m;
+ nc->b = b;
+ nc->e = e;
+ nc->n = e->n;
+ nc->u = e->dead;
+ nc->nxt = casing[j];
+ casing[j] = nc;
+}
+
+static int
+nr_bup(Element *e)
+{ FSM_use *u;
+ Lextok *v;
+ int nr = 0;
+
+ switch (e->n->ntyp) {
+ case ASGN:
+ nr++;
+ break;
+ case 'r':
+ if (e->n->val >= 1)
+ nr++; /* random recv */
+ for (v = e->n->rgt; v; v = v->rgt)
+ { if ((v->lft->ntyp == CONST
+ || v->lft->ntyp == EVAL))
+ continue;
+ nr++;
+ }
+ break;
+ default:
+ break;
+ }
+ for (u = e->dead; u; u = u->nxt)
+ { switch (u->special) {
+ case 2: /* dead after write */
+ if (e->n->ntyp == ASGN
+ && e->n->rgt->ntyp == CONST
+ && e->n->rgt->val == 0)
+ break;
+ nr++;
+ break;
+ case 1: /* dead after read */
+ nr++;
+ break;
+ } }
+ return nr;
+}
+
+static int
+nrhops(Element *e)
+{ Element *f = e, *g;
+ int cnt = 0;
+ int stopat;
+
+ if (e->merge_start)
+ stopat = e->merge_start;
+ else
+ stopat = e->merge;
+#if 0
+ printf("merge: %d merge_start %d - seqno %d\n",
+ e->merge, e->merge_start, e->seqno);
+#endif
+ do {
+ cnt += nr_bup(f);
+
+ if (f->n->ntyp == GOTO)
+ { g = get_lab(f->n, 1);
+ if (g->seqno == stopat)
+ f = g;
+ else
+ f = huntele(g, f->status, stopat);
+ } else
+ {
+ f = f->nxt;
+ }
+
+ if (f && !f->merge && !f->merge_single && f->seqno != stopat)
+ { fprintf(tm, "\n\t\tbad hop %s:%d -- at %d, <",
+ f->n->fn->name,f->n->ln, f->seqno);
+ comment(tm, f->n, 0);
+ fprintf(tm, "> looking for %d -- merge %d:%d:%d\n\t\t",
+ stopat, f->merge, f->merge_start, f->merge_single);
+ break;
+ }
+ } while (f && f->seqno != stopat);
+
+ return cnt;
+}
+
+static void
+check_needed(void)
+{
+ if (multi_needed)
+ { fprintf(tm, "(trpt+1)->bup.ovals = grab_ints(%d);\n\t\t",
+ multi_needed);
+ multi_undo = multi_needed;
+ multi_needed = 0;
+ }
+}
+
+static void
+doforward(FILE *tm_fd, Element *e)
+{ FSM_use *u;
+
+ putstmnt(tm_fd, e->n, e->seqno);
+
+ if (e->n->ntyp != ELSE && Det)
+ { fprintf(tm_fd, ";\n\t\tif (trpt->o_pm&1)\n\t\t");
+ fprintf(tm_fd, "\tuerror(\"non-determinism in D_proctype\")");
+ }
+ if (deadvar && !has_code)
+ for (u = e->dead; u; u = u->nxt)
+ { fprintf(tm_fd, ";\n\t\t/* dead %d: %s */ ",
+ u->special, u->var->name);
+
+ switch (u->special) {
+ case 2: /* dead after write -- lval already bupped */
+ if (e->n->ntyp == ASGN) /* could be recv or asgn */
+ { if (e->n->rgt->ntyp == CONST
+ && e->n->rgt->val == 0)
+ continue; /* already set to 0 */
+ }
+ if (e->n->ntyp != 'r')
+ { XZ.sym = u->var;
+ fprintf(tm_fd, "\n#ifdef HAS_CODE\n");
+ fprintf(tm_fd, "\t\tif (!readtrail)\n");
+ fprintf(tm_fd, "#endif\n\t\t\t");
+ putname(tm_fd, "", &XZ, 0, " = 0");
+ break;
+ } /* else fall through */
+ case 1: /* dead after read -- add asgn of rval -- needs bup */
+ YZ[YZmax].sym = u->var; /* store for pan.b */
+ CnT[YZcnt]++; /* this step added bups */
+ if (multi_oval)
+ { check_needed();
+ fprintf(tm_fd, "(trpt+1)->bup.ovals[%d] = ",
+ multi_oval-1);
+ multi_oval++;
+ } else
+ fprintf(tm_fd, "(trpt+1)->bup.oval = ");
+ putname(tm_fd, "", &YZ[YZmax], 0, ";\n");
+ fprintf(tm_fd, "#ifdef HAS_CODE\n");
+ fprintf(tm_fd, "\t\tif (!readtrail)\n");
+ fprintf(tm_fd, "#endif\n\t\t\t");
+ putname(tm_fd, "", &YZ[YZmax], 0, " = 0");
+ YZmax++;
+ break;
+ } }
+ fprintf(tm_fd, ";\n\t\t");
+}
+
+static int
+dobackward(Element *e, int casenr)
+{
+ if (!any_undo(e->n) && CnT[YZcnt] == 0)
+ { YZcnt--;
+ return 0;
+ }
+
+ if (!didcase)
+ { fprintf(tb, "\n\tcase %d: ", casenr);
+ fprintf(tb, "/* STATE %d */\n\t\t", e->seqno);
+ didcase++;
+ }
+
+ _isok++;
+ while (CnT[YZcnt] > 0) /* undo dead variable resets */
+ { CnT[YZcnt]--;
+ YZmax--;
+ if (YZmax < 0)
+ fatal("cannot happen, dobackward", (char *)0);
+ fprintf(tb, ";\n\t/* %d */\t", YZmax);
+ putname(tb, "", &YZ[YZmax], 0, " = trpt->bup.oval");
+ if (multi_oval > 0)
+ { multi_oval--;
+ fprintf(tb, "s[%d]", multi_oval-1);
+ }
+ }
+
+ if (e->n->ntyp != '.')
+ { fprintf(tb, ";\n\t\t");
+ undostmnt(e->n, e->seqno);
+ }
+ _isok--;
+
+ YZcnt--;
+ return 1;
+}
+
+static void
+lastfirst(int stopat, Element *fin, int casenr)
+{ Element *f = fin, *g;
+
+ if (f->n->ntyp == GOTO)
+ { g = get_lab(f->n, 1);
+ if (g->seqno == stopat)
+ f = g;
+ else
+ f = huntele(g, f->status, stopat);
+ } else
+ f = f->nxt;
+
+ if (!f || f->seqno == stopat
+ || (!f->merge && !f->merge_single))
+ return;
+ lastfirst(stopat, f, casenr);
+#if 0
+ fprintf(tb, "\n\t/* merge %d -- %d:%d %d:%d:%d (casenr %d) ",
+ YZcnt,
+ f->merge_start, f->merge,
+ f->seqno, f?f->seqno:-1, stopat,
+ casenr);
+ comment(tb, f->n, 0);
+ fprintf(tb, " */\n");
+ fflush(tb);
+#endif
+ dobackward(f, casenr);
+}
+
+static int modifier;
+
+static void
+lab_transfer(Element *to, Element *from)
+{ Symbol *ns, *s = has_lab(from, (1|2|4));
+ Symbol *oc;
+ int ltp, usedit=0;
+
+ if (!s) return;
+
+ /* "from" could have all three labels -- rename
+ * to prevent jumps to the transfered copies
+ */
+ oc = context; /* remember */
+ for (ltp = 1; ltp < 8; ltp *= 2) /* 1, 2, and 4 */
+ if ((s = has_lab(from, ltp)) != (Symbol *) 0)
+ { ns = (Symbol *) emalloc(sizeof(Symbol));
+ ns->name = (char *) emalloc((int) strlen(s->name) + 4);
+ sprintf(ns->name, "%s%d", s->name, modifier);
+
+ context = s->context;
+ set_lab(ns, to);
+ usedit++;
+ }
+ context = oc; /* restore */
+ if (usedit)
+ { if (modifier++ > 990)
+ fatal("modifier overflow error", (char *) 0);
+ }
+}
+
+static int
+case_cache(Element *e, int a)
+{ int bupcase = 0, casenr = uniq, fromcache = 0;
+ CaseCache *Cached = (CaseCache *) 0;
+ Element *f, *g;
+ int j, nrbups, mark, ntarget;
+ extern int ccache;
+
+ mark = (e->status&ATOM); /* could lose atomicity in a merge chain */
+
+ if (e->merge_mark > 0
+ || (merger && e->merge_in == 0))
+ { /* state nominally unreachable (part of merge chains) */
+ if (e->n->ntyp != '.'
+ && e->n->ntyp != GOTO)
+ { fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno);
+ fprintf(tt, "settr(0,0,0,0,0,\"");
+ comment(tt, e->n, e->seqno);
+ fprintf(tt, "\",0,0,0);\n");
+ } else
+ { fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno);
+ casenr = 1; /* mhs example */
+ j = a;
+ goto haveit; /* pakula's example */
+ }
+
+ return -1;
+ }
+
+ fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno);
+
+ if (ccache
+ && Pid != claimnr
+ && Pid != eventmapnr
+ && (Cached = prev_case(e, Pid)))
+ { bupcase = Cached->b;
+ casenr = Cached->m;
+ fromcache = 1;
+
+ fprintf(tm, "/* STATE %d - line %d %s - [",
+ e->seqno, e->n->ln, e->n->fn->name);
+ comment(tm, e->n, 0);
+ fprintf(tm, "] (%d:%d - %d) same as %d (%d:%d - %d) */\n",
+ e->merge_start, e->merge, e->merge_in,
+ casenr,
+ Cached->e->merge_start, Cached->e->merge, Cached->e->merge_in);
+
+ goto gotit;
+ }
+
+ fprintf(tm, "\tcase %d: /* STATE %d - line %d %s - [",
+ uniq++, e->seqno, e->n->ln, e->n->fn->name);
+ comment(tm, e->n, 0);
+ nrbups = (e->merge || e->merge_start) ? nrhops(e) : nr_bup(e);
+ fprintf(tm, "] (%d:%d:%d - %d) */\n\t\t",
+ e->merge_start, e->merge, nrbups, e->merge_in);
+
+ if (nrbups > MAXMERGE-1)
+ fatal("merge requires more than 256 bups", (char *)0);
+
+ if (e->n->ntyp != 'r' && Pid != claimnr && Pid != eventmapnr)
+ fprintf(tm, "IfNotBlocked\n\t\t");
+
+ if (multi_needed != 0 || multi_undo != 0)
+ fatal("cannot happen, case_cache", (char *) 0);
+
+ if (nrbups > 1)
+ { multi_oval = 1;
+ multi_needed = nrbups; /* allocated after edge condition */
+ } else
+ multi_oval = 0;
+
+ memset(CnT, 0, sizeof(CnT));
+ YZmax = YZcnt = 0;
+
+/* NEW 4.2.6 */
+ if (Pid == claimnr)
+ {
+ fprintf(tm, "\n#if defined(VERI) && !defined(NP)\n\t\t");
+ fprintf(tm, "{ static int reported%d = 0;\n\t\t", e->seqno);
+ /* source state changes in retrans and must be looked up in frm_st0[t->forw] */
+ fprintf(tm, " if (verbose && !reported%d)\n\t\t", e->seqno);
+ fprintf(tm, " { printf(\"depth %%d: Claim reached state %%d (line %%d)\\n\",\n\t\t");
+ fprintf(tm, " depth, frm_st0[t->forw], src_claim[%d]);\n\t\t", e->seqno);
+ fprintf(tm, " reported%d = 1;\n\t\t", e->seqno);
+ fprintf(tm, " fflush(stdout);\n\t\t");
+ fprintf(tm, "} }\n");
+ fprintf(tm, "#endif\n\t\t");
+ }
+/* end */
+
+ /* the src xrefs have the numbers in e->seqno builtin */
+ fprintf(tm, "reached[%d][%d] = 1;\n\t\t", Pid, e->seqno);
+
+ doforward(tm, e);
+
+ if (e->merge_start)
+ ntarget = e->merge_start;
+ else
+ ntarget = e->merge;
+
+ if (ntarget)
+ { f = e;
+
+more: if (f->n->ntyp == GOTO)
+ { g = get_lab(f->n, 1);
+ if (g->seqno == ntarget)
+ f = g;
+ else
+ f = huntele(g, f->status, ntarget);
+ } else
+ f = f->nxt;
+
+
+ if (f && f->seqno != ntarget)
+ { if (!f->merge && !f->merge_single)
+ { fprintf(tm, "/* stop at bad hop %d, %d */\n\t\t",
+ f->seqno, ntarget);
+ goto out;
+ }
+ fprintf(tm, "/* merge: ");
+ comment(tm, f->n, 0);
+ fprintf(tm, "(%d, %d, %d) */\n\t\t", f->merge, f->seqno, ntarget);
+ fprintf(tm, "reached[%d][%d] = 1;\n\t\t", Pid, f->seqno);
+ YZcnt++;
+ lab_transfer(e, f);
+ mark = f->status&(ATOM|L_ATOM); /* last step wins */
+ doforward(tm, f);
+ if (f->merge_in == 1) f->merge_mark++;
+
+ goto more;
+ } }
+out:
+ fprintf(tm, "_m = %d", getweight(e->n));
+ if (m_loss && e->n->ntyp == 's') fprintf(tm, "+delta_m; delta_m = 0");
+ fprintf(tm, "; goto P999; /* %d */\n", YZcnt);
+
+ multi_needed = 0;
+ didcase = 0;
+
+ if (ntarget)
+ lastfirst(ntarget, e, casenr); /* mergesteps only */
+
+ dobackward(e, casenr); /* the original step */
+
+ fprintf(tb, ";\n\t\t");
+
+ if (e->merge || e->merge_start)
+ { if (!didcase)
+ { fprintf(tb, "\n\tcase %d: ", casenr);
+ fprintf(tb, "/* STATE %d */", e->seqno);
+ didcase++;
+ } else
+ fprintf(tb, ";");
+ } else
+ fprintf(tb, ";");
+ fprintf(tb, "\n\t\t");
+
+ if (multi_undo)
+ { fprintf(tb, "ungrab_ints(trpt->bup.ovals, %d);\n\t\t",
+ multi_undo);
+ multi_undo = 0;
+ }
+ if (didcase)
+ { fprintf(tb, "goto R999;\n");
+ bupcase = casenr;
+ }
+
+ if (!e->merge && !e->merge_start)
+ new_case(e, casenr, bupcase, Pid);
+
+gotit:
+ j = a;
+ if (e->merge_start)
+ j = e->merge_start;
+ else if (e->merge)
+ j = e->merge;
+haveit:
+ fprintf(tt, "%ssettr(%d,%d,%d,%d,%d,\"", fromcache?"/* c */ ":"",
+ e->Seqno, mark, j, casenr, bupcase);
+
+ return (fromcache)?0:casenr;
+}
+
+static void
+put_el(Element *e, int Tt0, int Tt1)
+{ int a, casenr, Global_ref;
+ Element *g = ZE;
+
+ if (e->n->ntyp == GOTO)
+ { g = get_lab(e->n, 1);
+ g = huntele(g, e->status, -1);
+ cross_dsteps(e->n, g->n);
+ a = g->seqno;
+ } else if (e->nxt)
+ { g = huntele(e->nxt, e->status, -1);
+ a = g->seqno;
+ } else
+ a = 0;
+ if (g
+ && (g->status&CHECK2 /* entering remotely ref'd state */
+ || e->status&CHECK2)) /* leaving remotely ref'd state */
+ e->status |= I_GLOB;
+
+ /* don't remove dead edges in here, to preserve structure of fsm */
+ if (e->merge_start || e->merge)
+ goto non_generic;
+
+ /*** avoid duplicate or redundant cases in pan.m ***/
+ switch (e->n->ntyp) {
+ case ELSE:
+ casenr = 2; /* standard else */
+ putskip(e->seqno);
+ goto generic_case;
+ /* break; */
+ case '.':
+ case GOTO:
+ case BREAK:
+ putskip(e->seqno);
+ casenr = 1; /* standard goto */
+generic_case: fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno);
+ fprintf(tt, "settr(%d,%d,%d,%d,0,\"",
+ e->Seqno, e->status&ATOM, a, casenr);
+ break;
+#ifndef PRINTF
+ case PRINT:
+ goto non_generic;
+ case PRINTM:
+ goto non_generic;
+#endif
+ case 'c':
+ if (e->n->lft->ntyp == CONST
+ && e->n->lft->val == 1) /* skip or true */
+ { casenr = 1;
+ putskip(e->seqno);
+ goto generic_case;
+ }
+ goto non_generic;
+
+ default:
+non_generic:
+ casenr = case_cache(e, a);
+ if (casenr < 0) return; /* unreachable state */
+ break;
+ }
+ /* tailend of settr(...); */
+ Global_ref = (e->status&I_GLOB)?1:has_global(e->n);
+ comment(tt, e->n, e->seqno);
+ fprintf(tt, "\", %d, ", Global_ref);
+ if (Tt0 != 2)
+ { fprintf(tt, "%d, %d);", Tt0, Tt1);
+ } else
+ { Tpe(e->n); /* sets EPT */
+ fprintf(tt, "%d, %d);", EPT[0], EPT[1]);
+ }
+ if ((e->merge_start && e->merge_start != a)
+ || (e->merge && e->merge != a))
+ { fprintf(tt, " /* m: %d -> %d,%d */\n",
+ a, e->merge_start, e->merge);
+ fprintf(tt, " reached%d[%d] = 1;",
+ Pid, a); /* Sheinman's example */
+ }
+ fprintf(tt, "\n");
+
+ if (casenr > 2)
+ tr_map(casenr, e);
+ put_escp(e);
+}
+
+static void
+nested_unless(Element *e, Element *g)
+{ struct SeqList *y = e->esc, *z = g->esc;
+
+ for ( ; y && z; y = y->nxt, z = z->nxt)
+ if (z->this != y->this)
+ break;
+ if (!y && !z)
+ return;
+
+ if (g->n->ntyp != GOTO
+ && g->n->ntyp != '.'
+ && e->sub->nxt)
+ { printf("error: (%s:%d) saw 'unless' on a guard:\n",
+ (e->n)?e->n->fn->name:"-",
+ (e->n)?e->n->ln:0);
+ printf("=====>instead of\n");
+ printf(" do (or if)\n");
+ printf(" :: ...\n");
+ printf(" :: stmnt1 unless stmnt2\n");
+ printf(" od (of fi)\n");
+ printf("=====>use\n");
+ printf(" do (or if)\n");
+ printf(" :: ...\n");
+ printf(" :: stmnt1\n");
+ printf(" od (or fi) unless stmnt2\n");
+ printf("=====>or rewrite\n");
+ }
+}
+
+static void
+put_seq(Sequence *s, int Tt0, int Tt1)
+{ SeqList *h;
+ Element *e, *g;
+ int a, deadlink;
+
+ if (0) printf("put_seq %d\n", s->frst->seqno);
+
+ for (e = s->frst; e; e = e->nxt)
+ {
+ if (0) printf(" step %d\n", e->seqno);
+ if (e->status & DONE)
+ {
+ if (0) printf(" done before\n");
+ goto checklast;
+ }
+ e->status |= DONE;
+
+ if (e->n->ln)
+ putsrc(e);
+
+ if (e->n->ntyp == UNLESS)
+ {
+ if (0) printf(" an unless\n");
+ put_seq(e->sub->this, Tt0, Tt1);
+ } else if (e->sub)
+ {
+ if (0) printf(" has sub\n");
+ fprintf(tt, "\tT = trans[%d][%d] = ",
+ Pid, e->seqno);
+ fprintf(tt, "settr(%d,%d,0,0,0,\"",
+ e->Seqno, e->status&ATOM);
+ comment(tt, e->n, e->seqno);
+ if (e->status&CHECK2)
+ e->status |= I_GLOB;
+ fprintf(tt, "\", %d, %d, %d);",
+ (e->status&I_GLOB)?1:0, Tt0, Tt1);
+ blurb(tt, e);
+ for (h = e->sub; h; h = h->nxt)
+ { putskip(h->this->frst->seqno);
+ g = huntstart(h->this->frst);
+ if (g->esc)
+ nested_unless(e, g);
+ a = g->seqno;
+
+ if (g->n->ntyp == 'c'
+ && g->n->lft->ntyp == CONST
+ && g->n->lft->val == 0 /* 0 or false */
+ && !g->esc)
+ { fprintf(tt, "#if 0\n\t/* dead link: */\n");
+ deadlink = 1;
+ if (verbose&32)
+ printf("spin: line %3d %s, Warning: condition is always false\n",
+ g->n->ln, g->n->fn?g->n->fn->name:"");
+ } else
+ deadlink = 0;
+ if (0) printf(" settr %d %d\n", a, 0);
+ if (h->nxt)
+ fprintf(tt, "\tT = T->nxt\t= ");
+ else
+ fprintf(tt, "\t T->nxt\t= ");
+ fprintf(tt, "settr(%d,%d,%d,0,0,\"",
+ e->Seqno, e->status&ATOM, a);
+ comment(tt, e->n, e->seqno);
+ if (g->status&CHECK2)
+ h->this->frst->status |= I_GLOB;
+ fprintf(tt, "\", %d, %d, %d);",
+ (h->this->frst->status&I_GLOB)?1:0,
+ Tt0, Tt1);
+ blurb(tt, e);
+ if (deadlink)
+ fprintf(tt, "#endif\n");
+ }
+ for (h = e->sub; h; h = h->nxt)
+ put_seq(h->this, Tt0, Tt1);
+ } else
+ {
+ if (0) printf(" [non]atomic %d\n", e->n->ntyp);
+ if (e->n->ntyp == ATOMIC
+ || e->n->ntyp == D_STEP
+ || e->n->ntyp == NON_ATOMIC)
+ put_sub(e, Tt0, Tt1);
+ else
+ {
+ if (0) printf(" put_el %d\n", e->seqno);
+ put_el(e, Tt0, Tt1);
+ }
+ }
+checklast: if (e == s->last)
+ break;
+ }
+ if (0) printf("put_seq done\n");
+}
+
+static void
+patch_atomic(Sequence *s) /* catch goto's that break the chain */
+{ Element *f, *g;
+ SeqList *h;
+
+ for (f = s->frst; f ; f = f->nxt)
+ {
+ if (f->n && f->n->ntyp == GOTO)
+ { g = get_lab(f->n,1);
+ cross_dsteps(f->n, g->n);
+ if ((f->status & (ATOM|L_ATOM))
+ && !(g->status & (ATOM|L_ATOM)))
+ { f->status &= ~ATOM;
+ f->status |= L_ATOM;
+ }
+ /* bridge atomics */
+ if ((f->status & L_ATOM)
+ && (g->status & (ATOM|L_ATOM)))
+ { f->status &= ~L_ATOM;
+ f->status |= ATOM;
+ }
+ } else
+ for (h = f->sub; h; h = h->nxt)
+ patch_atomic(h->this);
+ if (f == s->extent)
+ break;
+ }
+}
+
+static void
+mark_seq(Sequence *s)
+{ Element *f;
+ SeqList *h;
+
+ for (f = s->frst; f; f = f->nxt)
+ { f->status |= I_GLOB;
+
+ if (f->n->ntyp == ATOMIC
+ || f->n->ntyp == NON_ATOMIC
+ || f->n->ntyp == D_STEP)
+ mark_seq(f->n->sl->this);
+
+ for (h = f->sub; h; h = h->nxt)
+ mark_seq(h->this);
+ if (f == s->last)
+ return;
+ }
+}
+
+static Element *
+find_target(Element *e)
+{ Element *f;
+
+ if (!e) return e;
+
+ if (t_cyc++ > 32)
+ { fatal("cycle of goto jumps", (char *) 0);
+ }
+ switch (e->n->ntyp) {
+ case GOTO:
+ f = get_lab(e->n,1);
+ cross_dsteps(e->n, f->n);
+ f = find_target(f);
+ break;
+ case BREAK:
+ if (e->nxt)
+ { f = find_target(huntele(e->nxt, e->status, -1));
+ break; /* new 5.0 -- was missing */
+ }
+ /* else fall through */
+ default:
+ f = e;
+ break;
+ }
+ return f;
+}
+
+Element *
+target(Element *e)
+{
+ if (!e) return e;
+ lineno = e->n->ln;
+ Fname = e->n->fn;
+ t_cyc = 0;
+ return find_target(e);
+}
+
+static int
+seq_has_el(Sequence *s, Element *g) /* new to version 5.0 */
+{ Element *f;
+ SeqList *h;
+
+ for (f = s->frst; f; f = f->nxt) /* g in same atomic? */
+ { if (f == g)
+ { return 1;
+ }
+ if (f->status & CHECK3)
+ { continue;
+ }
+ f->status |= CHECK3; /* protect against cycles */
+ for (h = f->sub; h; h = h->nxt)
+ { if (h->this && seq_has_el(h->this, g))
+ { return 1;
+ } } }
+ return 0;
+}
+
+static int
+scan_seq(Sequence *s)
+{ Element *f, *g;
+ SeqList *h;
+
+ for (f = s->frst; f; f = f->nxt)
+ { if ((f->status&CHECK2)
+ || has_global(f->n))
+ return 1;
+ if (f->n->ntyp == GOTO /* may exit or reach other atomic */
+ && !(f->status & D_ATOM)) /* cannot jump from d_step */
+ { /* consider jump from an atomic without globals into
+ * an atomic with globals
+ * example by Claus Traulsen, 22 June 2007
+ */
+ g = target(f);
+#if 1
+ if (g && !seq_has_el(s, g)) /* not internal to this atomic/dstep */
+
+#else
+ if (g
+ && !(f->status & L_ATOM)
+ && !(g->status & (ATOM|L_ATOM)))
+#endif
+ { fprintf(tt, "\t/* mark-down line %d status %d = %d */\n", f->n->ln, f->status, (f->status & D_ATOM));
+ return 1; /* assume worst case */
+ } }
+ for (h = f->sub; h; h = h->nxt)
+ if (scan_seq(h->this))
+ return 1;
+ if (f == s->last)
+ break;
+ }
+ return 0;
+}
+
+static int
+glob_args(Lextok *n)
+{ int result = 0;
+ Lextok *v;
+
+ for (v = n->rgt; v; v = v->rgt)
+ { if (v->lft->ntyp == CONST)
+ continue;
+ if (v->lft->ntyp == EVAL)
+ result += has_global(v->lft->lft);
+ else
+ result += has_global(v->lft);
+ }
+ return result;
+}
+
+static int
+proc_is_safe(const Lextok *n)
+{ ProcList *p;
+ /* not safe unless no local var inits are used */
+ /* note that a local variable init could refer to a global */
+
+ for (p = rdy; p; p = p->nxt)
+ { if (strcmp(n->sym->name, p->n->name) == 0)
+ { /* printf("proc %s safety: %d\n", p->n->name, p->unsafe); */
+ return (p->unsafe != 0);
+ } }
+ non_fatal("bad call to proc_is_safe", (char *) 0);
+ /* cannot happen */
+ return 0;
+}
+
+int
+has_global(Lextok *n)
+{ Lextok *v;
+
+ if (!n) return 0;
+ if (AllGlobal) return 1; /* global provided clause */
+
+ switch (n->ntyp) {
+ case ATOMIC:
+ case D_STEP:
+ case NON_ATOMIC:
+ return scan_seq(n->sl->this);
+
+ case '.':
+ case BREAK:
+ case GOTO:
+ case CONST:
+ return 0;
+
+ case ELSE: return n->val; /* true if combined with chan refs */
+
+ case 's': return glob_args(n)!=0 || ((n->sym->xu&(XS|XX)) != XS);
+ case 'r': return glob_args(n)!=0 || ((n->sym->xu&(XR|XX)) != XR);
+ case 'R': return glob_args(n)!=0 || (((n->sym->xu)&(XR|XS|XX)) != (XR|XS));
+ case NEMPTY: return ((n->sym->xu&(XR|XX)) != XR);
+ case NFULL: return ((n->sym->xu&(XS|XX)) != XS);
+ case FULL: return ((n->sym->xu&(XR|XX)) != XR);
+ case EMPTY: return ((n->sym->xu&(XS|XX)) != XS);
+ case LEN: return (((n->sym->xu)&(XR|XS|XX)) != (XR|XS));
+
+ case NAME:
+ if (n->sym->context
+ || (n->sym->hidden&64)
+ || strcmp(n->sym->name, "_pid") == 0
+ || strcmp(n->sym->name, "_") == 0)
+ return 0;
+ return 1;
+
+ case RUN:
+ return proc_is_safe(n);
+
+ case C_CODE: case C_EXPR:
+ return glob_inline(n->sym->name);
+
+ case ENABLED: case PC_VAL: case NONPROGRESS:
+ case 'p': case 'q':
+ case TIMEOUT:
+ return 1;
+
+ /* @ was 1 (global) since 2.8.5
+ in 3.0 it is considered local and
+ conditionally safe, provided:
+ II is the youngest process
+ and nrprocs < MAXPROCS
+ */
+ case '@': return 0;
+
+ case '!': case UMIN: case '~': case ASSERT:
+ return has_global(n->lft);
+
+ case '/': case '*': case '-': case '+':
+ case '%': case LT: case GT: case '&': case '^':
+ case '|': case LE: case GE: case NE: case '?':
+ case EQ: case OR: case AND: case LSHIFT:
+ case RSHIFT: case 'c': case ASGN:
+ return has_global(n->lft) || has_global(n->rgt);
+
+ case PRINT:
+ for (v = n->lft; v; v = v->rgt)
+ if (has_global(v->lft)) return 1;
+ return 0;
+ case PRINTM:
+ return has_global(n->lft);
+ }
+ return 0;
+}
+
+static void
+Bailout(FILE *fd, char *str)
+{
+ if (!GenCode)
+ fprintf(fd, "continue%s", str);
+ else if (IsGuard)
+ fprintf(fd, "%s%s", NextLab[Level], str);
+ else
+ fprintf(fd, "Uerror(\"block in step seq\")%s", str);
+}
+
+#define cat0(x) putstmnt(fd,now->lft,m); fprintf(fd, x); \
+ putstmnt(fd,now->rgt,m)
+#define cat1(x) fprintf(fd,"("); cat0(x); fprintf(fd,")")
+#define cat2(x,y) fprintf(fd,x); putstmnt(fd,y,m)
+#define cat3(x,y,z) fprintf(fd,x); putstmnt(fd,y,m); fprintf(fd,z)
+
+void
+putstmnt(FILE *fd, Lextok *now, int m)
+{ Lextok *v;
+ int i, j;
+
+ if (!now) { fprintf(fd, "0"); return; }
+ lineno = now->ln;
+ Fname = now->fn;
+
+ switch (now->ntyp) {
+ case CONST: fprintf(fd, "%d", now->val); break;
+ case '!': cat3(" !(", now->lft, ")"); break;
+ case UMIN: cat3(" -(", now->lft, ")"); break;
+ case '~': cat3(" ~(", now->lft, ")"); break;
+
+ case '/': cat1("/"); break;
+ case '*': cat1("*"); break;
+ case '-': cat1("-"); break;
+ case '+': cat1("+"); break;
+ case '%': cat1("%%"); break;
+ case '&': cat1("&"); break;
+ case '^': cat1("^"); break;
+ case '|': cat1("|"); break;
+ case LT: cat1("<"); break;
+ case GT: cat1(">"); break;
+ case LE: cat1("<="); break;
+ case GE: cat1(">="); break;
+ case NE: cat1("!="); break;
+ case EQ: cat1("=="); break;
+ case OR: cat1("||"); break;
+ case AND: cat1("&&"); break;
+ case LSHIFT: cat1("<<"); break;
+ case RSHIFT: cat1(">>"); break;
+
+ case TIMEOUT:
+ if (separate == 2)
+ fprintf(fd, "((tau)&1)");
+ else
+ fprintf(fd, "((trpt->tau)&1)");
+ if (GenCode)
+ printf("spin: line %3d, warning: 'timeout' in d_step sequence\n",
+ lineno);
+ /* is okay as a guard */
+ break;
+
+ case RUN:
+ if (now->sym == NULL)
+ Fatal("internal error pangen2.c", (char *) 0);
+ if (claimproc
+ && strcmp(now->sym->name, claimproc) == 0)
+ fatal("claim %s, (not runnable)", claimproc);
+ if (eventmap
+ && strcmp(now->sym->name, eventmap) == 0)
+ fatal("eventmap %s, (not runnable)", eventmap);
+
+ if (GenCode)
+ fatal("'run' in d_step sequence (use atomic)",
+ (char *)0);
+
+ fprintf(fd,"addproc(%d", fproc(now->sym->name));
+ for (v = now->lft, i = 0; v; v = v->rgt, i++)
+ { cat2(", ", v->lft);
+ }
+ check_param_count(i, now);
+
+ if (i > Npars)
+ { /* printf("\t%d parameters used, max %d expected\n", i, Npars); */
+ fatal("too many parameters in run %s(...)", now->sym->name);
+ }
+ for ( ; i < Npars; i++)
+ fprintf(fd, ", 0");
+ fprintf(fd, ")");
+ break;
+
+ case ENABLED:
+ cat3("enabled(II, ", now->lft, ")");
+ break;
+
+ case NONPROGRESS:
+ /* o_pm&4=progress, tau&128=claim stutter */
+ if (separate == 2)
+ fprintf(fd, "(!(o_pm&4) && !(tau&128))");
+ else
+ fprintf(fd, "(!(trpt->o_pm&4) && !(trpt->tau&128))");
+ break;
+
+ case PC_VAL:
+ cat3("((P0 *) Pptr(", now->lft, "+BASE))->_p");
+ break;
+
+ case LEN:
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&1) || ");
+ putname(fd, "q_R_check(", now->lft, m, "");
+ fprintf(fd, ", II)) &&\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&2) || ");
+ putname(fd, "q_S_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ putname(fd, "q_len(", now->lft, m, ")");
+ break;
+
+ case FULL:
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&1) || ");
+ putname(fd, "q_R_check(", now->lft, m, "");
+ fprintf(fd, ", II)) &&\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&2) || ");
+ putname(fd, "q_S_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ putname(fd, "q_full(", now->lft, m, ")");
+ break;
+
+ case EMPTY:
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&1) || ");
+ putname(fd, "q_R_check(", now->lft, m, "");
+ fprintf(fd, ", II)) &&\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&2) || ");
+ putname(fd, "q_S_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ putname(fd, "(q_len(", now->lft, m, ")==0)");
+ break;
+
+ case NFULL:
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&2) || ");
+ putname(fd, "q_S_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ putname(fd, "(!q_full(", now->lft, m, "))");
+ break;
+
+ case NEMPTY:
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&1) || ");
+ putname(fd, "q_R_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ putname(fd, "(q_len(", now->lft, m, ")>0)");
+ break;
+
+ case 's':
+ if (Pid == eventmapnr)
+ { fprintf(fd, "if ((ot == EVENT_TRACE && _tp != 's') ");
+ putname(fd, "|| _qid+1 != ", now->lft, m, "");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ continue;
+ fprintf(fd, " \\\n\t\t|| qrecv(");
+ putname(fd, "", now->lft, m, ", ");
+ putname(fd, "q_len(", now->lft, m, ")-1, ");
+ fprintf(fd, "%d, 0) != ", i);
+ if (v->lft->ntyp == CONST)
+ putstmnt(fd, v->lft, m);
+ else /* EVAL */
+ putstmnt(fd, v->lft->lft, m);
+ }
+ fprintf(fd, ")\n");
+ fprintf(fd, "\t\t continue");
+ putname(th, " || (x_y3_ == ", now->lft, m, ")");
+ break;
+ }
+ if (TestOnly)
+ { if (m_loss)
+ fprintf(fd, "1");
+ else
+ putname(fd, "!q_full(", now->lft, m, ")");
+ break;
+ }
+ if (has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "if (q_claim[", now->lft, m, "]&2) ");
+ putname(fd, "q_S_check(", now->lft, m, ", II);");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ fprintf(fd, "if (q_%s",
+ (u_sync > 0 && u_async == 0)?"len":"full");
+ putname(fd, "(", now->lft, m, "))\n");
+
+ if (m_loss)
+ fprintf(fd, "\t\t{ nlost++; delta_m = 1; } else {");
+ else
+ { fprintf(fd, "\t\t\t");
+ Bailout(fd, ";");
+ }
+
+ if (has_enabled)
+ fprintf(fd, "\n\t\tif (TstOnly) return 1;");
+
+ if (u_sync && !u_async && rvopt)
+ fprintf(fd, "\n\n\t\tif (no_recvs(II)) continue;\n");
+
+ fprintf(fd, "\n#ifdef HAS_CODE\n");
+ fprintf(fd, "\t\tif (readtrail && gui) {\n");
+ fprintf(fd, "\t\t\tchar simtmp[32];\n");
+ putname(fd, "\t\t\tsprintf(simvals, \"%%d!\", ", now->lft, m, ");\n");
+ _isok++;
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft, "); strcat(simvals, simtmp);");
+ if (v->rgt)
+ fprintf(fd, "\t\tstrcat(simvals, \",\");\n");
+ }
+ _isok--;
+ fprintf(fd, "\t\t}\n");
+ fprintf(fd, "#endif\n\t\t");
+
+ putname(fd, "\n\t\tqsend(", now->lft, m, "");
+ fprintf(fd, ", %d", now->val);
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { cat2(", ", v->lft);
+ }
+ if (i > Mpars)
+ { terse++;
+ putname(stdout, "channel name: ", now->lft, m, "\n");
+ terse--;
+ printf(" %d msg parameters sent, %d expected\n", i, Mpars);
+ fatal("too many pars in send", "");
+ }
+ for (j = i; i < Mpars; i++)
+ fprintf(fd, ", 0");
+ fprintf(fd, ", %d)", j);
+ if (u_sync)
+ { fprintf(fd, ";\n\t\t");
+ if (u_async)
+ putname(fd, "if (q_zero(", now->lft, m, ")) ");
+ putname(fd, "{ boq = ", now->lft, m, "");
+ if (GenCode)
+ fprintf(fd, "; Uerror(\"rv-attempt in d_step\")");
+ fprintf(fd, "; }");
+ }
+ if (m_loss)
+ fprintf(fd, ";\n\t\t}\n\t\t"); /* end of m_loss else */
+ break;
+
+ case 'r':
+ if (Pid == eventmapnr)
+ { fprintf(fd, "if ((ot == EVENT_TRACE && _tp != 'r') ");
+ putname(fd, "|| _qid+1 != ", now->lft, m, "");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ continue;
+ fprintf(fd, " \\\n\t\t|| qrecv(");
+ putname(fd, "", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0) != ", i);
+ if (v->lft->ntyp == CONST)
+ putstmnt(fd, v->lft, m);
+ else /* EVAL */
+ putstmnt(fd, v->lft->lft, m);
+ }
+ fprintf(fd, ")\n");
+ fprintf(fd, "\t\t continue");
+
+ putname(tc, " || (x_y3_ == ", now->lft, m, ")");
+
+ break;
+ }
+ if (TestOnly)
+ { fprintf(fd, "((");
+ if (u_sync) fprintf(fd, "(boq == -1 && ");
+
+ putname(fd, "q_len(", now->lft, m, ")");
+
+ if (u_sync && now->val <= 1)
+ { putname(fd, ") || (boq == ", now->lft,m," && ");
+ putname(fd, "q_zero(", now->lft,m,"))");
+ }
+
+ fprintf(fd, ")");
+ if (now->val == 0 || now->val == 2)
+ { for (v = now->rgt, i=j=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp == CONST)
+ { cat3("\n\t\t&& (", v->lft, " == ");
+ putname(fd, "qrecv(", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0))", i);
+ } else if (v->lft->ntyp == EVAL)
+ { cat3("\n\t\t&& (", v->lft->lft, " == ");
+ putname(fd, "qrecv(", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0))", i);
+ } else
+ { j++; continue;
+ }
+ }
+ } else
+ { fprintf(fd, "\n\t\t&& Q_has(");
+ putname(fd, "", now->lft, m, "");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp == CONST)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft, m);
+ } else if (v->lft->ntyp == EVAL)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft->lft, m);
+ } else
+ { fprintf(fd, ", 0, 0");
+ } }
+ for ( ; i < Mpars; i++)
+ fprintf(fd, ", 0, 0");
+ fprintf(fd, ")");
+ }
+ fprintf(fd, ")");
+ break;
+ }
+ if (has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "if (q_claim[", now->lft, m, "]&1) ");
+ putname(fd, "q_R_check(", now->lft, m, ", II);");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ if (u_sync)
+ { if (now->val >= 2)
+ { if (u_async)
+ { fprintf(fd, "if (");
+ putname(fd, "q_zero(", now->lft,m,"))");
+ fprintf(fd, "\n\t\t{\t");
+ }
+ fprintf(fd, "uerror(\"polling ");
+ fprintf(fd, "rv chan\");\n\t\t");
+ if (u_async)
+ fprintf(fd, " continue;\n\t\t}\n\t\t");
+ fprintf(fd, "IfNotBlocked\n\t\t");
+ } else
+ { fprintf(fd, "if (");
+ if (u_async == 0)
+ putname(fd, "boq != ", now->lft,m,") ");
+ else
+ { putname(fd, "q_zero(", now->lft,m,"))");
+ fprintf(fd, "\n\t\t{\tif (boq != ");
+ putname(fd, "", now->lft,m,") ");
+ Bailout(fd, ";\n\t\t} else\n\t\t");
+ fprintf(fd, "{\tif (boq != -1) ");
+ }
+ Bailout(fd, ";\n\t\t");
+ if (u_async)
+ fprintf(fd, "}\n\t\t");
+ } }
+ putname(fd, "if (q_len(", now->lft, m, ") == 0) ");
+ Bailout(fd, "");
+
+ for (v = now->rgt, j=0; v; v = v->rgt)
+ { if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ j++; /* count settables */
+ }
+ fprintf(fd, ";\n\n\t\tXX=1");
+/* test */ if (now->val == 0 || now->val == 2)
+ { for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp == CONST)
+ { fprintf(fd, ";\n\t\t");
+ cat3("if (", v->lft, " != ");
+ putname(fd, "qrecv(", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0)) ", i);
+ Bailout(fd, "");
+ } else if (v->lft->ntyp == EVAL)
+ { fprintf(fd, ";\n\t\t");
+ cat3("if (", v->lft->lft, " != ");
+ putname(fd, "qrecv(", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0)) ", i);
+ Bailout(fd, "");
+ } }
+ } else /* random receive: val 1 or 3 */
+ { fprintf(fd, ";\n\t\tif (!(XX = Q_has(");
+ putname(fd, "", now->lft, m, "");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp == CONST)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft, m);
+ } else if (v->lft->ntyp == EVAL)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft->lft, m);
+ } else
+ { fprintf(fd, ", 0, 0");
+ } }
+ for ( ; i < Mpars; i++)
+ fprintf(fd, ", 0, 0");
+ fprintf(fd, "))) ");
+ Bailout(fd, "");
+ fprintf(fd, ";\n\t\t");
+ if (multi_oval)
+ { check_needed();
+ fprintf(fd, "(trpt+1)->bup.ovals[%d] = ",
+ multi_oval-1);
+ multi_oval++;
+ } else
+ fprintf(fd, "(trpt+1)->bup.oval = ");
+ fprintf(fd, "XX");
+ }
+
+ if (has_enabled)
+ fprintf(fd, ";\n\t\tif (TstOnly) return 1");
+
+ if (j == 0 && now->val >= 2)
+ { fprintf(fd, ";\n\t\t");
+ break; /* poll without side-effect */
+ }
+
+ if (!GenCode)
+ { int jj = 0;
+ fprintf(fd, ";\n\t\t");
+ /* no variables modified */
+ if (j == 0 && now->val == 0)
+ { fprintf(fd, "if (q_flds[((Q0 *)qptr(");
+ putname(fd, "", now->lft, m, "-1))->_t]");
+ fprintf(fd, " != %d)\n\t", i);
+ fprintf(fd, "\t\tUerror(\"wrong nr of msg fields in rcv\");\n\t\t");
+ }
+
+ for (v = now->rgt; v; v = v->rgt)
+ if ((v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL))
+ jj++; /* nr of vars needing bup */
+
+ if (jj)
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { char tempbuf[64];
+
+ if ((v->lft->ntyp == CONST
+ || v->lft->ntyp == EVAL))
+ continue;
+
+ if (multi_oval)
+ { check_needed();
+ sprintf(tempbuf, "(trpt+1)->bup.ovals[%d] = ",
+ multi_oval-1);
+ multi_oval++;
+ } else
+ sprintf(tempbuf, "(trpt+1)->bup.oval = ");
+
+ if (v->lft->sym && !strcmp(v->lft->sym->name, "_"))
+ { fprintf(fd, tempbuf);
+ putname(fd, "qrecv(", now->lft, m, "");
+ fprintf(fd, ", XX-1, %d, 0);\n\t\t", i);
+ } else
+ { _isok++;
+ cat3(tempbuf, v->lft, ";\n\t\t");
+ _isok--;
+ }
+ }
+
+ if (jj) /* check for double entries q?x,x */
+ { Lextok *w;
+
+ for (v = now->rgt; v; v = v->rgt)
+ { if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL
+ && v->lft->sym
+ && v->lft->sym->type != STRUCT /* not a struct */
+ && v->lft->sym->nel == 1 /* not an array */
+ && strcmp(v->lft->sym->name, "_") != 0)
+ for (w = v->rgt; w; w = w->rgt)
+ if (v->lft->sym == w->lft->sym)
+ { fatal("cannot use var ('%s') in multiple msg fields",
+ v->lft->sym->name);
+ } } }
+ }
+/* set */ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { if ((v->lft->ntyp == CONST
+ || v->lft->ntyp == EVAL) && v->rgt)
+ continue;
+ fprintf(fd, ";\n\t\t");
+
+ if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL
+ && v->lft->sym != NULL
+ && strcmp(v->lft->sym->name, "_") != 0)
+ { nocast=1;
+ _isok++;
+ putstmnt(fd, v->lft, m);
+ _isok--;
+ nocast=0;
+ fprintf(fd, " = ");
+ }
+ putname(fd, "qrecv(", now->lft, m, ", ");
+ fprintf(fd, "XX-1, %d, ", i);
+ fprintf(fd, "%d)", (v->rgt || now->val >= 2)?0:1);
+
+ if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL
+ && v->lft->sym != NULL
+ && strcmp(v->lft->sym->name, "_") != 0
+ && (v->lft->ntyp != NAME
+ || v->lft->sym->type != CHAN))
+ { fprintf(fd, ";\n#ifdef VAR_RANGES");
+ fprintf(fd, "\n\t\tlogval(\"");
+ withprocname = terse = nocast = 1;
+ _isok++;
+ putstmnt(fd,v->lft,m);
+ withprocname = terse = nocast = 0;
+ fprintf(fd, "\", ");
+ putstmnt(fd,v->lft,m);
+ _isok--;
+ fprintf(fd, ");\n#endif\n");
+ fprintf(fd, "\t\t");
+ }
+ }
+ fprintf(fd, ";\n\t\t");
+
+ fprintf(fd, "\n#ifdef HAS_CODE\n");
+ fprintf(fd, "\t\tif (readtrail && gui) {\n");
+ fprintf(fd, "\t\t\tchar simtmp[32];\n");
+ putname(fd, "\t\t\tsprintf(simvals, \"%%d?\", ", now->lft, m, ");\n");
+ _isok++;
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp != EVAL)
+ { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft, "); strcat(simvals, simtmp);");
+ } else
+ { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft->lft, "); strcat(simvals, simtmp);");
+ }
+ if (v->rgt)
+ fprintf(fd, "\t\tstrcat(simvals, \",\");\n");
+ }
+ _isok--;
+ fprintf(fd, "\t\t}\n");
+ fprintf(fd, "#endif\n\t\t");
+
+ if (u_sync)
+ { putname(fd, "if (q_zero(", now->lft, m, "))");
+ fprintf(fd, "\n\t\t{ boq = -1;\n");
+
+ fprintf(fd, "#ifndef NOFAIR\n"); /* NEW 3.0.8 */
+ fprintf(fd, "\t\t\tif (fairness\n");
+ fprintf(fd, "\t\t\t&& !(trpt->o_pm&32)\n");
+ fprintf(fd, "\t\t\t&& (now._a_t&2)\n");
+ fprintf(fd, "\t\t\t&& now._cnt[now._a_t&1] == II+2)\n");
+ fprintf(fd, "\t\t\t{ now._cnt[now._a_t&1] -= 1;\n");
+ fprintf(fd, "#ifdef VERI\n");
+ fprintf(fd, "\t\t\t if (II == 1)\n");
+ fprintf(fd, "\t\t\t now._cnt[now._a_t&1] = 1;\n");
+ fprintf(fd, "#endif\n");
+ fprintf(fd, "#ifdef DEBUG\n");
+ fprintf(fd, "\t\t\tprintf(\"%%3d: proc %%d fairness \", depth, II);\n");
+ fprintf(fd, "\t\t\tprintf(\"Rule 2: --cnt to %%d (%%d)\\n\",\n");
+ fprintf(fd, "\t\t\t now._cnt[now._a_t&1], now._a_t);\n");
+ fprintf(fd, "#endif\n");
+ fprintf(fd, "\t\t\t trpt->o_pm |= (32|64);\n");
+ fprintf(fd, "\t\t\t}\n");
+ fprintf(fd, "#endif\n");
+
+ fprintf(fd, "\n\t\t}");
+ }
+ break;
+
+ case 'R':
+ if (!terse && !TestOnly && has_xu)
+ { fprintf(fd, "\n#ifndef XUSAFE\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&1) || ");
+ fprintf(fd, "q_R_check(");
+ putname(fd, "", now->lft, m, ", II)) &&\n\t\t");
+ putname(fd, "(!(q_claim[", now->lft, m, "]&2) || ");
+ putname(fd, "q_S_check(", now->lft, m, ", II)) &&");
+ fprintf(fd, "\n#endif\n\t\t");
+ }
+ if (u_sync>0)
+ putname(fd, "not_RV(", now->lft, m, ") && \\\n\t\t");
+
+ for (v = now->rgt, i=j=0; v; v = v->rgt, i++)
+ if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ { j++; continue;
+ }
+ if (now->val == 0 || i == j)
+ { putname(fd, "(q_len(", now->lft, m, ") > 0");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ continue;
+ fprintf(fd, " \\\n\t\t&& qrecv(");
+ putname(fd, "", now->lft, m, ", ");
+ fprintf(fd, "0, %d, 0) == ", i);
+ if (v->lft->ntyp == CONST)
+ putstmnt(fd, v->lft, m);
+ else /* EVAL */
+ putstmnt(fd, v->lft->lft, m);
+ }
+ fprintf(fd, ")");
+ } else
+ { putname(fd, "Q_has(", now->lft, m, "");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v->lft->ntyp == CONST)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft, m);
+ } else if (v->lft->ntyp == EVAL)
+ { fprintf(fd, ", 1, ");
+ putstmnt(fd, v->lft->lft, m);
+ } else
+ fprintf(fd, ", 0, 0");
+ }
+ for ( ; i < Mpars; i++)
+ fprintf(fd, ", 0, 0");
+ fprintf(fd, ")");
+ }
+ break;
+
+ case 'c':
+ preruse(fd, now->lft); /* preconditions */
+ cat3("if (!(", now->lft, "))\n\t\t\t");
+ Bailout(fd, "");
+ break;
+
+ case ELSE:
+ if (!GenCode)
+ { if (separate == 2)
+ fprintf(fd, "if (o_pm&1)\n\t\t\t");
+ else
+ fprintf(fd, "if (trpt->o_pm&1)\n\t\t\t");
+ Bailout(fd, "");
+ } else
+ { fprintf(fd, "/* else */");
+ }
+ break;
+
+ case '?':
+ if (now->lft)
+ { cat3("( (", now->lft, ") ? ");
+ }
+ if (now->rgt)
+ { cat3("(", now->rgt->lft, ") : ");
+ cat3("(", now->rgt->rgt, ") )");
+ }
+ break;
+
+ case ASGN:
+ if (has_enabled)
+ fprintf(fd, "if (TstOnly) return 1;\n\t\t");
+ _isok++;
+
+ if (!GenCode)
+ { if (multi_oval)
+ { char tempbuf[64];
+ check_needed();
+ sprintf(tempbuf, "(trpt+1)->bup.ovals[%d] = ",
+ multi_oval-1);
+ multi_oval++;
+ cat3(tempbuf, now->lft, ";\n\t\t");
+ } else
+ { cat3("(trpt+1)->bup.oval = ", now->lft, ";\n\t\t");
+ } }
+ nocast = 1; putstmnt(fd,now->lft,m); nocast = 0;
+ fprintf(fd," = ");
+ _isok--;
+ putstmnt(fd,now->rgt,m);
+
+ if (now->sym->type != CHAN
+ || verbose > 0)
+ { fprintf(fd, ";\n#ifdef VAR_RANGES");
+ fprintf(fd, "\n\t\tlogval(\"");
+ withprocname = terse = nocast = 1;
+ _isok++;
+ putstmnt(fd,now->lft,m);
+ withprocname = terse = nocast = 0;
+ fprintf(fd, "\", ");
+ putstmnt(fd,now->lft,m);
+ _isok--;
+ fprintf(fd, ");\n#endif\n");
+ fprintf(fd, "\t\t");
+ }
+ break;
+
+ case PRINT:
+ if (has_enabled)
+ fprintf(fd, "if (TstOnly) return 1;\n\t\t");
+#ifdef PRINTF
+ fprintf(fd, "printf(%s", now->sym->name);
+#else
+ fprintf(fd, "Printf(%s", now->sym->name);
+#endif
+ for (v = now->lft; v; v = v->rgt)
+ { cat2(", ", v->lft);
+ }
+ fprintf(fd, ")");
+ break;
+
+ case PRINTM:
+ if (has_enabled)
+ fprintf(fd, "if (TstOnly) return 1;\n\t\t");
+ fprintf(fd, "printm(");
+ if (now->lft && now->lft->ismtyp)
+ fprintf(fd, "%d", now->lft->val);
+ else
+ putstmnt(fd, now->lft, m);
+ fprintf(fd, ")");
+ break;
+
+ case NAME:
+ if (!nocast && now->sym && Sym_typ(now) < SHORT)
+ putname(fd, "((int)", now, m, ")");
+ else
+ putname(fd, "", now, m, "");
+ break;
+
+ case 'p':
+ putremote(fd, now, m);
+ break;
+
+ case 'q':
+ if (terse)
+ fprintf(fd, "%s", now->sym->name);
+ else
+ fprintf(fd, "%d", remotelab(now));
+ break;
+
+ case C_EXPR:
+ fprintf(fd, "(");
+ plunk_expr(fd, now->sym->name);
+#if 1
+ fprintf(fd, ")");
+#else
+ fprintf(fd, ") /* %s */ ", now->sym->name);
+#endif
+ break;
+
+ case C_CODE:
+ if (now->sym)
+ fprintf(fd, "/* %s */\n\t\t", now->sym->name);
+ if (has_enabled)
+ fprintf(fd, "if (TstOnly) return 1;\n\t\t");
+ if (!GenCode) /* not in d_step */
+ { fprintf(fd, "sv_save();\n\t\t");
+ /* store the old values for reverse moves */
+ }
+
+ if (now->sym)
+ plunk_inline(fd, now->sym->name, 1);
+ else
+ Fatal("internal error pangen2.c", (char *) 0);
+
+ if (!GenCode)
+ { fprintf(fd, "\n"); /* state changed, capture it */
+ fprintf(fd, "#if defined(C_States) && (HAS_TRACK==1)\n");
+ fprintf(fd, "\t\tc_update((uchar *) &(now.c_state[0]));\n");
+ fprintf(fd, "#endif\n");
+ }
+ break;
+
+ case ASSERT:
+ if (has_enabled)
+ fprintf(fd, "if (TstOnly) return 1;\n\t\t");
+
+ cat3("assert(", now->lft, ", ");
+ terse = nocast = 1;
+ cat3("\"", now->lft, "\", II, tt, t)");
+ terse = nocast = 0;
+ break;
+
+ case '.':
+ case BREAK:
+ case GOTO:
+ if (Pid == eventmapnr)
+ fprintf(fd, "Uerror(\"cannot get here\")");
+ putskip(m);
+ break;
+
+ case '@':
+ if (Pid == eventmapnr)
+ { fprintf(fd, "return 0");
+ break;
+ }
+
+ if (has_enabled)
+ { fprintf(fd, "if (TstOnly)\n\t\t\t");
+ fprintf(fd, "return (II+1 == now._nr_pr);\n\t\t");
+ }
+ fprintf(fd, "if (!delproc(1, II)) ");
+ Bailout(fd, "");
+ break;
+
+ default:
+ printf("spin: bad node type %d (.m) - line %d\n",
+ now->ntyp, now->ln);
+ fflush(tm);
+ alldone(1);
+ }
+}
+
+void
+putname(FILE *fd, char *pre, Lextok *n, int m, char *suff) /* varref */
+{ Symbol *s = n->sym;
+ lineno = n->ln; Fname = n->fn;
+
+ if (!s)
+ fatal("no name - putname", (char *) 0);
+
+ if (s->context && context && s->type)
+ s = findloc(s); /* it's a local var */
+
+ if (!s)
+ { fprintf(fd, "%s%s%s", pre, n->sym->name, suff);
+ return;
+ }
+ if (!s->type) /* not a local name */
+ s = lookup(s->name); /* must be a global */
+
+ if (!s->type)
+ { if (strcmp(pre, ".") != 0)
+ non_fatal("undeclared variable '%s'", s->name);
+ s->type = INT;
+ }
+
+ if (s->type == PROCTYPE)
+ fatal("proctype-name '%s' used as array-name", s->name);
+
+ fprintf(fd, pre);
+ if (!terse && !s->owner && evalindex != 1)
+ { if (s->context
+ || strcmp(s->name, "_p") == 0
+ || strcmp(s->name, "_pid") == 0)
+ { fprintf(fd, "((P%d *)this)->", Pid);
+ } else
+ { int x = strcmp(s->name, "_");
+ if (!(s->hidden&1) && x != 0)
+ fprintf(fd, "now.");
+ if (x == 0 && _isok == 0)
+ fatal("attempt to read value of '_'", 0);
+ } }
+
+ if (withprocname
+ && s->context
+ && strcmp(pre, "."))
+ fprintf(fd, "%s:", s->context->name);
+
+ if (evalindex != 1)
+ fprintf(fd, "%s", s->name);
+
+ if (s->nel != 1)
+ { if (no_arrays)
+ {
+ non_fatal("ref to array element invalid in this context",
+ (char *)0);
+ printf("\thint: instead of, e.g., x[rs] qu[3], use\n");
+ printf("\tchan nm_3 = qu[3]; x[rs] nm_3;\n");
+ printf("\tand use nm_3 in sends/recvs instead of qu[3]\n");
+ }
+ /* an xr or xs reference to an array element
+ * becomes an exclusion tag on the array itself -
+ * which could result in invalidly labeling
+ * operations on other elements of this array to
+ * be also safe under the partial order reduction
+ * (see procedure has_global())
+ */
+
+ if (evalindex == 2)
+ { fprintf(fd, "[%%d]");
+ } else if (evalindex == 1)
+ { evalindex = 0; /* no good if index is indexed array */
+ fprintf(fd, ", ");
+ putstmnt(fd, n->lft, m);
+ evalindex = 1;
+ } else
+ { if (terse
+ || (n->lft
+ && n->lft->ntyp == CONST
+ && n->lft->val < s->nel)
+ || (!n->lft && s->nel > 0))
+ { cat3("[", n->lft, "]");
+ } else
+ { cat3("[ Index(", n->lft, ", ");
+ fprintf(fd, "%d) ]", s->nel);
+ }
+ }
+ }
+ if (s->type == STRUCT && n->rgt && n->rgt->lft)
+ { putname(fd, ".", n->rgt->lft, m, "");
+ }
+ fprintf(fd, suff);
+}
+
+void
+putremote(FILE *fd, Lextok *n, int m) /* remote reference */
+{ int promoted = 0;
+ int pt;
+
+ if (terse)
+ { fprintf(fd, "%s", n->lft->sym->name); /* proctype name */
+ if (n->lft->lft)
+ { fprintf(fd, "[");
+ putstmnt(fd, n->lft->lft, m); /* pid */
+ fprintf(fd, "]");
+ }
+ fprintf(fd, ".%s", n->sym->name);
+ } else
+ { if (Sym_typ(n) < SHORT)
+ { promoted = 1;
+ fprintf(fd, "((int)");
+ }
+
+ pt = fproc(n->lft->sym->name);
+ fprintf(fd, "((P%d *)Pptr(", pt);
+ if (n->lft->lft)
+ { fprintf(fd, "BASE+");
+ putstmnt(fd, n->lft->lft, m);
+ } else
+ fprintf(fd, "f_pid(%d)", pt);
+ fprintf(fd, "))->%s", n->sym->name);
+ }
+ if (n->rgt)
+ { fprintf(fd, "[");
+ putstmnt(fd, n->rgt, m); /* array var ref */
+ fprintf(fd, "]");
+ }
+ if (promoted) fprintf(fd, ")");
+}
+
+static int
+getweight(Lextok *n)
+{ /* this piece of code is a remnant of early versions
+ * of the verifier -- in the current version of Spin
+ * only non-zero values matter - so this could probably
+ * simply return 1 in all cases.
+ */
+ switch (n->ntyp) {
+ case 'r': return 4;
+ case 's': return 2;
+ case TIMEOUT: return 1;
+ case 'c': if (has_typ(n->lft, TIMEOUT)) return 1;
+ }
+ return 3;
+}
+
+int
+has_typ(Lextok *n, int m)
+{
+ if (!n) return 0;
+ if (n->ntyp == m) return 1;
+ return (has_typ(n->lft, m) || has_typ(n->rgt, m));
+}
+
+static int runcount, opcount;
+
+static void
+do_count(Lextok *n, int checkop)
+{
+ if (!n) return;
+
+ switch (n->ntyp) {
+ case RUN:
+ runcount++;
+ break;
+ default:
+ if (checkop) opcount++;
+ break;
+ }
+ do_count(n->lft, checkop && (n->ntyp != RUN));
+ do_count(n->rgt, checkop);
+}
+
+void
+count_runs(Lextok *n)
+{
+ runcount = opcount = 0;
+ do_count(n, 1);
+ if (runcount > 1)
+ fatal("more than one run operator in expression", "");
+ if (runcount == 1 && opcount > 1)
+ fatal("use of run operator in compound expression", "");
+}
+
+void
+any_runs(Lextok *n)
+{
+ runcount = opcount = 0;
+ do_count(n, 0);
+ if (runcount >= 1)
+ fatal("run operator used in invalid context", "");
+}
--- /dev/null
+/***** spin: pangen2.h *****/
+
+/* Copyright (c) 1989-2007 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
+
+static char *Nvr1[] = { /* allow separate compilation */
+ "#ifdef VERI",
+ "void",
+ "check_claim(int st)",
+ "{",
+ " if (st == endclaim)",
+ " uerror(\"claim violated!\");",
+ " if (stopstate[VERI][st])",
+ " uerror(\"end state in claim reached\");",
+ "}",
+ "#endif",
+ 0,
+};
+
+static char *Pre0[] = {
+"#ifdef SC",
+ "#define _FILE_OFFSET_BITS 64", /* to allow file sizes greater than 2Gb */
+"#endif",
+ "#include <stdio.h>",
+ "#include <signal.h>",
+ "#include <stdlib.h>",
+ "#include <stdarg.h>",
+ "#include <string.h>",
+ "#include <ctype.h>",
+ "#include <errno.h>",
+ "#if defined(WIN32) || defined(WIN64)",
+ "#include <time.h>",
+ "#else",
+ "#include <unistd.h>",
+ "#include <sys/times.h>",
+ "#endif",
+ "#include <sys/types.h>", /* defines off_t */
+ "#include <sys/stat.h>",
+ "#include <fcntl.h>",
+
+ "#define Offsetof(X, Y) ((unsigned long)(&(((X *)0)->Y)))",
+ "#ifndef max",
+ "#define max(a,b) (((a)<(b)) ? (b) : (a))",
+ "#endif",
+ "#ifndef PRINTF",
+ "int Printf(const char *fmt, ...); /* prototype only */",
+ "#endif",
+ 0,
+};
+
+static char *Preamble[] = {
+
+ "#ifdef CNTRSTACK",
+ "#define onstack_now() (LL[trpt->j6] && LL[trpt->j7])",
+ "#define onstack_put() LL[trpt->j6]++; LL[trpt->j7]++",
+ "#define onstack_zap() LL[trpt->j6]--; LL[trpt->j7]--",
+ "#endif",
+
+ "#if !defined(SAFETY) && !defined(NOCOMP)",
+ /*
+ * V_A identifies states in the current statespace
+ * A_V identifies states in the 'other' statespace
+ * S_A remembers how many leading bytes in the sv
+ * are used for these markers + fairness bits
+ */
+ "#define V_A (((now._a_t&1)?2:1) << (now._a_t&2))",
+ "#define A_V (((now._a_t&1)?1:2) << (now._a_t&2))",
+ "int S_A = 0;",
+ "#else",
+ "#define V_A 0",
+ "#define A_V 0",
+ "#define S_A 0",
+ "#endif",
+
+"#ifdef MA",
+ "#undef onstack_now",
+ "#undef onstack_put",
+ "#undef onstack_zap",
+ "#define onstack_put() ;",
+ "#define onstack_zap() gstore((char *) &now, vsize, 4)",
+"#else",
+ "#if defined(FULLSTACK) && !defined(BITSTATE)",
+ "#define onstack_put() trpt->ostate = Lstate",
+ "#define onstack_zap() { \\",
+ " if (trpt->ostate) \\",
+ " trpt->ostate->tagged = \\",
+ " (S_A)? (trpt->ostate->tagged&~V_A) : 0; \\",
+ " }",
+ "#endif",
+"#endif",
+
+ "#ifndef NO_V_PROVISO",
+ "#define V_PROVISO",
+ "#endif",
+ "#if !defined(NO_RESIZE) && !defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(SPACE) && NCORE==1",
+ " #define AUTO_RESIZE",
+ "#endif",
+ "",
+ "struct H_el {",
+ " struct H_el *nxt;",
+ "#ifdef FULLSTACK",
+ " unsigned int tagged;",
+ " #if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY)",
+ " unsigned int proviso;", /* uses just 1 bit 0/1 */
+ " #endif",
+ "#endif",
+ "#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK))",
+ " unsigned long st_id;",
+ "#endif",
+ "#if !defined(SAFETY) || defined(REACH)",
+ " unsigned int D;",
+ "#endif",
+ "#if NCORE>1",
+ " /* could cost 1 extra word: 4 bytes if 32-bit and 8 bytes if 64-bit */",
+ " #ifdef V_PROVISO",
+ " uchar cpu_id; /* id of cpu that created the state */",
+ " #endif",
+ "#endif",
+ "#ifdef COLLAPSE",
+ " #if VECTORSZ<65536",
+ " unsigned short ln;", /* length of vector */
+ " #else",
+ " unsigned long ln;", /* length of vector */
+ " #endif",
+ "#endif",
+ "#if defined(AUTO_RESIZE) && !defined(BITSTATE)",
+ " unsigned long m_K1;",
+ "#endif",
+ " unsigned long state;",
+ "} **H_tab, **S_Tab;\n",
+
+ "typedef struct Trail {",
+ " int st; /* current state */",
+ " uchar pr; /* process id */",
+ " uchar tau; /* 8 bit-flags */",
+ " uchar o_pm; /* 8 more bit-flags */",
+ "#if 0",
+ " Meaning of bit-flags:",
+ " tau&1 -> timeout enabled",
+ " tau&2 -> request to enable timeout 1 level up (in claim)",
+ " tau&4 -> current transition is a claim move",
+ " tau&8 -> current transition is an atomic move",
+ " tau&16 -> last move was truncated on stack",
+ " tau&32 -> current transition is a preselected move",
+ " tau&64 -> at least one next state is not on the stack",
+ " tau&128 -> current transition is a stutter move",
+
+ " o_pm&1 -> the current pid moved -- implements else",
+ " o_pm&2 -> this is an acceptance state",
+ " o_pm&4 -> this is a progress state",
+ " o_pm&8 -> fairness alg rule 1 undo mark",
+ " o_pm&16 -> fairness alg rule 3 undo mark",
+ " o_pm&32 -> fairness alg rule 2 undo mark",
+ " o_pm&64 -> the current proc applied rule2",
+ " o_pm&128 -> a fairness, dummy move - all procs blocked",
+ "#endif",
+ "#ifdef NSUCC",
+ " uchar n_succ; /* nr of successor states */",
+ "#endif",
+ "#if defined(FULLSTACK) && defined(MA) && !defined(BFS)",
+ " uchar proviso;",
+ "#endif",
+ "#ifndef BFS",
+ " uchar o_n, o_ot; /* to save locals */",
+ "#endif",
+ " uchar o_m;",
+ "#ifdef EVENT_TRACE",
+ "#if nstates_event<256",
+ " uchar o_event;",
+ "#else",
+ " unsigned short o_event;",
+ "#endif",
+ "#endif",
+ " int o_tt;",
+ "#ifndef BFS",
+ " short o_To;",
+ "#ifdef RANDOMIZE",
+ " short oo_i;",
+ "#endif",
+ "#endif",
+ "#if defined(HAS_UNLESS) && !defined(BFS)",
+ " int e_state; /* if escape trans - state of origin */",
+ "#endif",
+ "#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS) || (NCORE>1)",
+ " struct H_el *ostate; /* pointer to stored state */",
+ "#endif",
+ /* CNTRSTACK when !NOREDUCE && BITSTATE && SAFETY, uses LL[] */
+ "#if defined(CNTRSTACK) && !defined(BFS)",
+ " long j6, j7;",
+ "#endif",
+ " Trans *o_t;", /* transition fct, next state */
+ "#ifdef SCHED",
+ " /* based on Qadeer&Rehof, Tacas 2005, LNCS 3440, pp. 93-107 */",
+ " #if NCORE>1",
+ " #error \"-DSCHED cannot be combined with -DNCORE (yet)\"",
+ " #endif",
+ " int sched_limit;",
+ "#endif",
+ "#ifdef HAS_SORTED",
+ " short ipt;", /* insertion slot in q */
+ "#endif",
+ " union {",
+ " int oval;", /* single backup value of variable */
+ " int *ovals;", /* ptr to multiple values */
+ " } bup;",
+ "} Trail;",
+ "Trail *trail, *trpt;",
+
+ "FILE *efd;",
+ "uchar *this;",
+ "long maxdepth=10000;",
+ "long omaxdepth=10000;",
+ "#ifdef SCHED",
+ "int sched_max = 10;",
+ "#endif",
+ "#ifdef PERMUTED",
+ " uchar permuted = 1;",
+ "#else",
+ " uchar permuted = 0;",
+ "#endif",
+ "double quota; /* time limit */",
+ "#if NCORE>1",
+ "long z_handoff = -1;",
+ "#endif",
+ "#ifdef SC", /* stack cycling */
+ "char *stackfile;",
+ "#endif",
+ "uchar *SS, *LL;",
+ "uchar HASH_NR = 0;",
+ "",
+ "double memcnt = (double) 0;",
+ "double memlim = (double) (1<<30); /* 1 GB */",
+ "#if NCORE>1",
+ "double mem_reserved = (double) 0;",
+ "#endif",
+ "",
+ "/* for emalloc: */",
+ "static char *have;",
+ "static long left = 0L;",
+ "static double fragment = (double) 0;",
+ "static unsigned long grow;",
+ "",
+ "unsigned int HASH_CONST[] = {",
+ " /* asuming 4 bytes per int */",
+ " 0x88888EEF, 0x00400007,",
+ " 0x04c11db7, 0x100d4e63,",
+ " 0x0fc22f87, 0x3ff0c3ff,",
+ " 0x38e84cd7, 0x02b148e9,",
+ " 0x98b2e49d, 0xb616d379,",
+ " 0xa5247fd9, 0xbae92a15,",
+ " 0xb91c8bc5, 0x8e5880f3,",
+ " 0xacd7c069, 0xb4c44bb3,",
+ " 0x2ead1fb7, 0x8e428171,",
+ " 0xdbebd459, 0x828ae611,",
+ " 0x6cb25933, 0x86cdd651,",
+ " 0x9e8f5f21, 0xd5f8d8e7,",
+ " 0x9c4e956f, 0xb5cf2c71,",
+ " 0x2e805a6d, 0x33fc3a55,",
+ " 0xaf203ed1, 0xe31f5909,",
+ " 0x5276db35, 0x0c565ef7,",
+ " 0x273d1aa5, 0x8923b1dd,",
+ " 0",
+ "};",
+ "#if NCORE>1",
+ "extern int core_id;",
+ "#endif",
+ "long mreached=0;",
+ "int done=0, errors=0, Nrun=1;",
+ "int c_init_done=0;",
+ "char *c_stack_start = (char *) 0;",
+ "double nstates=0, nlinks=0, truncs=0, truncs2=0;",
+ "double nlost=0, nShadow=0, hcmp=0, ngrabs=0;",
+ "#if defined(ZAPH) && defined(BITSTATE)",
+ "double zstates = 0;",
+ "#endif",
+ "int c_init_run;",
+ "#ifdef BFS",
+ "double midrv=0, failedrv=0, revrv=0;",
+ "#endif",
+ "unsigned long nr_states=0; /* nodes in DFA */",
+ "long Fa=0, Fh=0, Zh=0, Zn=0;",
+ "long PUT=0, PROBE=0, ZAPS=0;",
+ "long Ccheck=0, Cholds=0;",
+ "int a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0;",
+ "#ifdef HAS_CODE",
+ "int gui = 0, coltrace = 0, readtrail = 0;",
+ "int whichtrail = 0, onlyproc = -1, silent = 0;",
+ "#endif",
+ "int state_tables=0, fairness=0, no_rck=0, Nr_Trails=0;",
+ "char simvals[128];",
+ "#ifndef INLINE",
+ "int TstOnly=0;",
+ "#endif",
+ "unsigned long mask, nmask;",
+ "#ifdef BITSTATE",
+ "int ssize=23; /* 1 Mb */",
+ "#else",
+ "int ssize=19; /* 512K slots */",
+ "#endif",
+ "int hmax=0, svmax=0, smax=0;",
+ "int Maxbody=0, XX;",
+ "uchar *noptr; /* used by macro Pptr(x) */",
+ "#ifdef VAR_RANGES",
+ "void logval(char *, int);",
+ "void dumpranges(void);",
+ "#endif",
+
+ "#ifdef MA",
+ "#define INLINE_REV",
+ "extern void dfa_init(unsigned short);",
+ "extern int dfa_member(unsigned long);",
+ "extern int dfa_store(uchar *);",
+ "unsigned int maxgs = 0;",
+ "#endif",
+ "",
+ "#ifdef ALIGNED",
+ " State comp_now __attribute__ ((aligned (8)));",
+ " /* gcc 64-bit aligned for Itanium2 systems */",
+ " /* MAJOR runtime penalty if not used on those systems */",
+ "#else",
+ " State comp_now; /* compressed state vector */",
+ "#endif",
+ "",
+ "State comp_msk;",
+ "uchar *Mask = (uchar *) &comp_msk;",
+ "#ifdef COLLAPSE",
+ "State comp_tmp;",
+ "static char *scratch = (char *) &comp_tmp;",
+ "#endif",
+
+ "Stack *stack; /* for queues, processes */",
+ "Svtack *svtack; /* for old state vectors */",
+ "#ifdef BITSTATE",
+ "static unsigned int hfns = 3; /* new default */",
+ "#endif",
+ "static unsigned long j1;",
+ "static unsigned long K1, K2;",
+ "static unsigned long j2, j3, j4;",
+ "#ifdef BITSTATE",
+#ifndef POWOW
+ "static long udmem;",
+#endif
+ "#endif",
+ "static long A_depth = 0;",
+ "long depth = 0;",
+ /* depth: not static to support -S2, but possible clash with embedded code */
+ "#if NCORE>1",
+ "long nr_handoffs = 0;",
+ "#endif",
+ "static uchar warned = 0, iterative = 0, exclusive = 0, like_java = 0, every_error = 0;",
+ "static uchar noasserts = 0, noends = 0, bounded = 0;",
+ "#if SYNC>0 && ASYNC==0",
+ "void set_recvs(void);",
+ "int no_recvs(int);",
+ "#endif",
+ "#if SYNC",
+ "#define IfNotBlocked if (boq != -1) continue;",
+ "#define UnBlock boq = -1",
+ "#else",
+ "#define IfNotBlocked /* cannot block */",
+ "#define UnBlock /* don't bother */",
+ "#endif\n",
+ "#ifdef BITSTATE",
+ "int (*bstore)(char *, int);",
+ "int bstore_reg(char *, int);",
+#ifndef POWOW
+ "int bstore_mod(char *, int);",
+#endif
+ "#endif",
+ "void active_procs(void);",
+ "void cleanup(void);",
+ "void do_the_search(void);",
+ "void find_shorter(int);",
+ "void iniglobals(void);",
+ "void stopped(int);",
+ "void wrapup(void);",
+ "int *grab_ints(int);",
+ "void ungrab_ints(int *, int);",
+ 0,
+};
+
+static char *Tail[] = {
+ "Trans *",
+ "settr( int t_id, int a, int b, int c, int d,",
+ " char *t, int g, int tpe0, int tpe1)",
+ "{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));\n",
+ " tmp->atom = a&(6|32); /* only (2|8|32) have meaning */",
+ " if (!g) tmp->atom |= 8; /* no global references */",
+ " tmp->st = b;",
+ " tmp->tpe[0] = tpe0;",
+ " tmp->tpe[1] = tpe1;",
+ " tmp->tp = t;",
+ " tmp->t_id = t_id;",
+ " tmp->forw = c;",
+ " tmp->back = d;",
+ " return tmp;",
+ "}\n",
+ "Trans *",
+ "cpytr(Trans *a)",
+ "{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));\n",
+ " int i;",
+ " tmp->atom = a->atom;",
+ " tmp->st = a->st;",
+ "#ifdef HAS_UNLESS",
+ " tmp->e_trans = a->e_trans;",
+ " for (i = 0; i < HAS_UNLESS; i++)",
+ " tmp->escp[i] = a->escp[i];",
+ "#endif",
+ " tmp->tpe[0] = a->tpe[0];",
+ " tmp->tpe[1] = a->tpe[1];",
+ " for (i = 0; i < 6; i++)",
+ " { tmp->qu[i] = a->qu[i];",
+ " tmp->ty[i] = a->ty[i];",
+ " }",
+ " tmp->tp = (char *) emalloc(strlen(a->tp)+1);",
+ " strcpy(tmp->tp, a->tp);",
+ " tmp->t_id = a->t_id;",
+ " tmp->forw = a->forw;",
+ " tmp->back = a->back;",
+ " return tmp;",
+ "}\n",
+ "#ifndef NOREDUCE",
+ "int",
+ "srinc_set(int n)",
+ "{ if (n <= 2) return LOCAL;",
+ " if (n <= 2+ DELTA) return Q_FULL_F; /* 's' or nfull */",
+ " if (n <= 2+2*DELTA) return Q_EMPT_F; /* 'r' or nempty */",
+ " if (n <= 2+3*DELTA) return Q_EMPT_T; /* empty */",
+ " if (n <= 2+4*DELTA) return Q_FULL_T; /* full */",
+ " if (n == 5*DELTA) return GLOBAL;",
+ " if (n == 6*DELTA) return TIMEOUT_F;",
+ " if (n == 7*DELTA) return ALPHA_F;",
+ " Uerror(\"cannot happen srinc_class\");",
+ " return BAD;",
+ "}",
+ "int",
+ "srunc(int n, int m)",
+ "{ switch(m) {",
+ " case Q_FULL_F: return n-2;",
+ " case Q_EMPT_F: return n-2-DELTA;",
+ " case Q_EMPT_T: return n-2-2*DELTA;",
+ " case Q_FULL_T: return n-2-3*DELTA;",
+ " case ALPHA_F:",
+ " case TIMEOUT_F: return 257; /* non-zero, and > MAXQ */",
+ " }",
+ " Uerror(\"cannot happen srunc\");",
+ " return 0;",
+ "}",
+ "#endif",
+ "int cnt;",
+ "#ifdef HAS_UNLESS",
+ "int",
+ "isthere(Trans *a, int b)", /* is b already in a's list? */
+ "{ Trans *t;",
+ " for (t = a; t; t = t->nxt)",
+ " if (t->t_id == b)",
+ " return 1;",
+ " return 0;",
+ "}",
+ "#endif",
+ "#ifndef NOREDUCE",
+ "int",
+ "mark_safety(Trans *t) /* for conditional safety */",
+ "{ int g = 0, i, j, k;",
+ "",
+ " if (!t) return 0;",
+ " if (t->qu[0])",
+ " return (t->qu[1])?2:1; /* marked */",
+ "",
+ " for (i = 0; i < 2; i++)",
+ " { j = srinc_set(t->tpe[i]);",
+ " if (j >= GLOBAL && j != ALPHA_F)",
+ " return -1;",
+ " if (j != LOCAL)",
+ " { k = srunc(t->tpe[i], j);",
+ " if (g == 0",
+ " || t->qu[0] != k",
+ " || t->ty[0] != j)",
+ " { t->qu[g] = k;",
+ " t->ty[g] = j;",
+ " g++;",
+ " } } }",
+ " return g;",
+ "}",
+ "#endif",
+ "void",
+ "retrans(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])",
+ " /* process n, with m states, is=initial state */",
+ "{ Trans *T0, *T1, *T2, *T3;",
+ " int i, k;",
+ "#ifndef NOREDUCE",
+ " int g, h, j, aa;",
+ "#endif",
+ "#ifdef HAS_UNLESS",
+ " int p;",
+ "#endif",
+ " if (state_tables >= 4)",
+ " { printf(\"STEP 1 proctype %%s\\n\", ",
+ " procname[n]);",
+ " for (i = 1; i < m; i++)",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " crack(n, i, T0, srcln);",
+ " return;",
+ " }",
+ " do {",
+ " for (i = 1, cnt = 0; i < m; i++)",
+ " { T2 = trans[n][i];",
+ " T1 = T2?T2->nxt:(Trans *)0;",
+ "/* prescan: */ for (T0 = T1; T0; T0 = T0->nxt)",
+ "/* choice in choice */ { if (T0->st && trans[n][T0->st]",
+ " && trans[n][T0->st]->nxt)",
+ " break;",
+ " }",
+ "#if 0",
+ " if (T0)",
+ " printf(\"\\tstate %%d / %%d: choice in choice\\n\",",
+ " i, T0->st);",
+ "#endif",
+ " if (T0)",
+ " for (T0 = T1; T0; T0 = T0->nxt)",
+ " { T3 = trans[n][T0->st];",
+ " if (!T3->nxt)",
+ " { T2->nxt = cpytr(T0);",
+ " T2 = T2->nxt;",
+ " imed(T2, T0->st, n, i);",
+ " continue;",
+ " }",
+ " do { T3 = T3->nxt;",
+ " T2->nxt = cpytr(T3);",
+ " T2 = T2->nxt;",
+ " imed(T2, T0->st, n, i);",
+ " } while (T3->nxt);",
+ " cnt++;",
+ " }",
+ " }",
+ " } while (cnt);",
+
+ " if (state_tables >= 3)",
+ " { printf(\"STEP 2 proctype %%s\\n\", ",
+ " procname[n]);",
+ " for (i = 1; i < m; i++)",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " crack(n, i, T0, srcln);",
+ " return;",
+ " }",
+ " for (i = 1; i < m; i++)",
+ " { if (trans[n][i] && trans[n][i]->nxt) /* optimize */",
+ " { T1 = trans[n][i]->nxt;",
+ "#if 0",
+ " printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",",
+ " T1->st, T1->forw, i);",
+ "#endif",
+ " if (!trans[n][T1->st]) continue;",
+ " T0 = cpytr(trans[n][T1->st]);",
+ " trans[n][i] = T0;",
+ " reach[T1->st] = 1;",
+ " imed(T0, T1->st, n, i);",
+ " for (T1 = T1->nxt; T1; T1 = T1->nxt)",
+ " {",
+ "#if 0",
+ " printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",",
+ " T1->st, T1->forw, i);",
+ "#endif",
+ " if (!trans[n][T1->st]) continue;",
+ " T0->nxt = cpytr(trans[n][T1->st]);",
+ " T0 = T0->nxt;",
+ " reach[T1->st] = 1;",
+ " imed(T0, T1->st, n, i);",
+ " } } }",
+ " if (state_tables >= 2)",
+ " { printf(\"STEP 3 proctype %%s\\n\", ",
+ " procname[n]);",
+ " for (i = 1; i < m; i++)",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " crack(n, i, T0, srcln);",
+ " return;",
+ " }",
+ "#ifdef HAS_UNLESS",
+ " for (i = 1; i < m; i++)",
+ " { if (!trans[n][i]) continue;",
+ " /* check for each state i if an",
+ " * escape to some state p is defined",
+ " * if so, copy and mark p's transitions",
+ " * and prepend them to the transition-",
+ " * list of state i",
+ " */",
+ " if (!like_java) /* the default */",
+ " { for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " for (k = HAS_UNLESS-1; k >= 0; k--)",
+ " { if (p = T0->escp[k])",
+ " for (T1 = trans[n][p]; T1; T1 = T1->nxt)",
+ " { if (isthere(trans[n][i], T1->t_id))",
+ " continue;",
+ " T2 = cpytr(T1);",
+ " T2->e_trans = p;",
+ " T2->nxt = trans[n][i];",
+ " trans[n][i] = T2;",
+ " } }",
+ " } else /* outermost unless checked first */",
+ " { Trans *T4;",
+ " T4 = T3 = (Trans *) 0;",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " for (k = HAS_UNLESS-1; k >= 0; k--)",
+ " { if (p = T0->escp[k])",
+ " for (T1 = trans[n][p]; T1; T1 = T1->nxt)",
+ " { if (isthere(trans[n][i], T1->t_id))",
+ " continue;",
+ " T2 = cpytr(T1);",
+ " T2->nxt = (Trans *) 0;",
+ " T2->e_trans = p;",
+ " if (T3) T3->nxt = T2;",
+ " else T4 = T2;",
+ " T3 = T2;",
+ " } }",
+ " if (T4)",
+ " { T3->nxt = trans[n][i];",
+ " trans[n][i] = T4;",
+ " }",
+ " }",
+ " }",
+ "#endif",
+
+ "#ifndef NOREDUCE",
+ " for (i = 1; i < m; i++)",
+ " { if (a_cycles)",
+ " { /* moves through these states are visible */",
+ " #if PROG_LAB>0 && defined(HAS_NP)",
+ " if (progstate[n][i])",
+ " goto degrade;",
+ " for (T1 = trans[n][i]; T1; T1 = T1->nxt)",
+ " if (progstate[n][T1->st])",
+ " goto degrade;",
+ " #endif",
+ " if (accpstate[n][i] || visstate[n][i])",
+ " goto degrade;",
+ " for (T1 = trans[n][i]; T1; T1 = T1->nxt)",
+ " if (accpstate[n][T1->st])",
+ " goto degrade;",
+ " }",
+ " T1 = trans[n][i];",
+ " if (!T1) continue;",
+ " g = mark_safety(T1); /* V3.3.1 */",
+ " if (g < 0) goto degrade; /* global */",
+ " /* check if mixing of guards preserves reduction */",
+ " if (T1->nxt)",
+ " { k = 0;",
+ " for (T0 = T1; T0; T0 = T0->nxt)",
+ " { if (!(T0->atom&8))",
+ " goto degrade;",
+ " for (aa = 0; aa < 2; aa++)",
+ " { j = srinc_set(T0->tpe[aa]);",
+ " if (j >= GLOBAL && j != ALPHA_F)",
+ " goto degrade;",
+ " if (T0->tpe[aa]",
+ " && T0->tpe[aa]",
+ " != T1->tpe[0])",
+ " k = 1;",
+ " } }",
+ " /* g = 0; V3.3.1 */",
+ " if (k) /* non-uniform selection */",
+ " for (T0 = T1; T0; T0 = T0->nxt)",
+ " for (aa = 0; aa < 2; aa++)",
+ " { j = srinc_set(T0->tpe[aa]);",
+ " if (j != LOCAL)",
+ " { k = srunc(T0->tpe[aa], j);",
+ " for (h = 0; h < 6; h++)",
+ " if (T1->qu[h] == k",
+ " && T1->ty[h] == j)",
+ " break;",
+ " if (h >= 6)",
+ " { T1->qu[g%%6] = k;",
+ " T1->ty[g%%6] = j;",
+ " g++;",
+ " } } }",
+ " if (g > 6)",
+ " { T1->qu[0] = 0; /* turn it off */",
+ " printf(\"pan: warning, line %%d, \",",
+ " srcln[i]);",
+ " printf(\"too many stmnt types (%%d)\",",
+ " g);",
+ " printf(\" in selection\\n\");",
+ " goto degrade;",
+ " }",
+ " }",
+ " /* mark all options global if >=1 is global */",
+ " for (T1 = trans[n][i]; T1; T1 = T1->nxt)",
+ " if (!(T1->atom&8)) break;",
+ " if (T1)",
+ "degrade: for (T1 = trans[n][i]; T1; T1 = T1->nxt)",
+ " T1->atom &= ~8; /* mark as unsafe */",
+
+ " /* can only mix 'r's or 's's if on same chan */",
+ " /* and not mixed with other local operations */",
+ " T1 = trans[n][i];",
+
+ " if (!T1 || T1->qu[0]) continue;",
+
+ " j = T1->tpe[0];",
+ " if (T1->nxt && T1->atom&8)",
+ " { if (j == 5*DELTA)",
+ " { printf(\"warning: line %%d \", srcln[i]);",
+ " printf(\"mixed condition \");",
+ " printf(\"(defeats reduction)\\n\");",
+ " goto degrade;",
+ " }",
+ " for (T0 = T1; T0; T0 = T0->nxt)",
+ " for (aa = 0; aa < 2; aa++)",
+ " if (T0->tpe[aa] && T0->tpe[aa] != j)",
+ " { printf(\"warning: line %%d \", srcln[i]);",
+ " printf(\"[%%d-%%d] mixed %%stion \",",
+ " T0->tpe[aa], j, ",
+ " (j==5*DELTA)?\"condi\":\"selec\");",
+ " printf(\"(defeats reduction)\\n\");",
+ " printf(\" '%%s' <-> '%%s'\\n\",",
+ " T1->tp, T0->tp);",
+ " goto degrade;",
+ " } }",
+ " }",
+ "#endif",
+ " for (i = 1; i < m; i++)", /* R */
+ " { T2 = trans[n][i];",
+ " if (!T2",
+ " || T2->nxt",
+ " || strncmp(T2->tp, \".(goto)\", 7)",
+ " || !stopstate[n][i])",
+ " continue;",
+ " stopstate[n][T2->st] = 1;",
+ " }",
+ " if (state_tables)",
+ " { printf(\"proctype \");",
+ " if (!strcmp(procname[n], \":init:\"))",
+ " printf(\"init\\n\");",
+ " else",
+ " printf(\"%%s\\n\", procname[n]);",
+ " for (i = 1; i < m; i++)",
+ " reach[i] = 1;",
+ " tagtable(n, m, is, srcln, reach);",
+ " } else",
+ " for (i = 1; i < m; i++)",
+ " { int nrelse;",
+ " if (strcmp(procname[n], \":never:\") != 0)",
+ " { for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " { if (T0->st == i",
+ " && strcmp(T0->tp, \"(1)\") == 0)",
+ " { printf(\"error: proctype '%%s' \",",
+ " procname[n]);",
+ " printf(\"line %%d, state %%d: has un\",",
+ " srcln[i], i);",
+ " printf(\"conditional self-loop\\n\");",
+ " pan_exit(1);",
+ " } } }",
+ " nrelse = 0;",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " { if (strcmp(T0->tp, \"else\") == 0)",
+ " nrelse++;",
+ " }",
+ " if (nrelse > 1)",
+ " { printf(\"error: proctype '%%s' state\",",
+ " procname[n]);",
+ " printf(\" %%d, inherits %%d\", i, nrelse);",
+ " printf(\" 'else' stmnts\\n\");",
+ " pan_exit(1);",
+ " } }",
+ " if (!state_tables && strcmp(procname[n], \":never:\") == 0)",
+ " { int h = 0;",
+ " for (i = 1; i < m; i++)",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " if (T0->forw > h) h = T0->forw;",
+ " h++;",
+ " frm_st0 = (short *) emalloc(h * sizeof(short));",
+ " for (i = 1; i < m; i++)",
+ " for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+ " frm_st0[T0->forw] = i;",
+ " }",
+ "#ifndef LOOPSTATE",
+ " if (state_tables)",
+ "#endif",
+ " do_dfs(n, m, is, srcln, reach, lstate);",
+ "#ifdef T_REVERSE",
+ " /* process n, with m states, is=initial state -- reverse list */",
+ " if (!state_tables && strcmp(procname[n], \":never:\") != 0)",
+ " { for (i = 1; i < m; i++)", /* for each state */
+ " { Trans *T4 = (Trans *) 0;",
+ " T1 = (Trans *) 0;", /* points to reversed list */
+ " T2 = (Trans *) 0;", /* points to first entry */
+ " T3 = (Trans *) 0;", /* remembers any else */
+ " for (T0 = trans[n][i]; T0; T0 = T4)",
+ " { T4 = T0->nxt;",
+ " if (strcmp(T0->tp, \"else\") == 0)",
+ " { T3 = T0;",
+ " T0->nxt = (Trans *) 0;",
+ " } else",
+ " { T0->nxt = T1;",
+ " if (!T1) { T2 = T0; }",
+ " T1 = T0;",
+ " } }",
+ " if (T2 && T3) { T2->nxt = T3; }", /* at the end */
+ " trans[n][i] = T1; /* reversed -- else at end */",
+ " } }",
+ "#endif",
+ "}",
+ "void",
+ "imed(Trans *T, int v, int n, int j) /* set intermediate state */",
+ "{ progstate[n][T->st] |= progstate[n][v];",
+ " accpstate[n][T->st] |= accpstate[n][v];",
+ " stopstate[n][T->st] |= stopstate[n][v];",
+ " mapstate[n][j] = T->st;",
+ "}",
+ "void",
+ "tagtable(int n, int m, int is, short srcln[], uchar reach[])",
+ "{ Trans *z;\n",
+ " if (is >= m || !trans[n][is]",
+ " || is <= 0 || reach[is] == 0)",
+ " return;",
+ " reach[is] = 0;",
+ " if (state_tables)",
+ " for (z = trans[n][is]; z; z = z->nxt)",
+ " crack(n, is, z, srcln);",
+ " for (z = trans[n][is]; z; z = z->nxt)",
+ " {",
+ "#ifdef HAS_UNLESS",
+ " int i, j;",
+ "#endif",
+ " tagtable(n, m, z->st, srcln, reach);",
+ "#ifdef HAS_UNLESS",
+ " for (i = 0; i < HAS_UNLESS; i++)",
+ " { j = trans[n][is]->escp[i];",
+ " if (!j) break;",
+ " tagtable(n, m, j, srcln, reach);",
+ " }",
+ "#endif",
+ " }",
+ "}",
+ "void",
+ "dfs_table(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])",
+ "{ Trans *z;\n",
+ " if (is >= m || is <= 0 || !trans[n][is])",
+ " return;",
+ " if ((reach[is] & (4|8|16)) != 0)",
+ " { if ((reach[is] & (8|16)) == 16) /* on stack, not yet recorded */",
+ " { lstate[is] = 1;",
+ " reach[is] |= 8; /* recorded */",
+ " if (state_tables)",
+ " { printf(\"state %%d line %%d is a loopstate\\n\", is, srcln[is]);",
+ " } }",
+ " return;",
+ " }",
+ " reach[is] |= (4|16); /* visited | onstack */",
+ " for (z = trans[n][is]; z; z = z->nxt)",
+ " {",
+ "#ifdef HAS_UNLESS",
+ " int i, j;",
+ "#endif",
+ " dfs_table(n, m, z->st, srcln, reach, lstate);",
+ "#ifdef HAS_UNLESS",
+ " for (i = 0; i < HAS_UNLESS; i++)",
+ " { j = trans[n][is]->escp[i];",
+ " if (!j) break;",
+ " dfs_table(n, m, j, srcln, reach, lstate);",
+ " }",
+ "#endif",
+ " }",
+ " reach[is] &= ~16; /* no longer on stack */",
+ "}",
+ "void",
+ "do_dfs(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])",
+ "{ int i;",
+ " dfs_table(n, m, is, srcln, reach, lstate);",
+ " for (i = 0; i < m; i++)",
+ " reach[i] &= ~(4|8|16);",
+ "}",
+ "void",
+ "crack(int n, int j, Trans *z, short srcln[])",
+ "{ int i;\n",
+ " if (!z) return;",
+ " printf(\"\tstate %%3d -(tr %%3d)-> state %%3d \",",
+ " j, z->forw, z->st);",
+ " printf(\"[id %%3d tp %%3d\", z->t_id, z->tpe[0]);",
+ " if (z->tpe[1]) printf(\",%%d\", z->tpe[1]);",
+ "#ifdef HAS_UNLESS",
+ " if (z->e_trans)",
+ " printf(\" org %%3d\", z->e_trans);",
+ " else if (state_tables >= 2)",
+ " for (i = 0; i < HAS_UNLESS; i++)",
+ " { if (!z->escp[i]) break;",
+ " printf(\" esc %%d\", z->escp[i]);",
+ " }",
+ "#endif",
+ " printf(\"]\");",
+ " printf(\" [%%s%%s%%s%%s%%s] line %%d => \",",
+ " z->atom&6?\"A\":z->atom&32?\"D\":\"-\",",
+ " accpstate[n][j]?\"a\" :\"-\",",
+ " stopstate[n][j]?\"e\" : \"-\",",
+ " progstate[n][j]?\"p\" : \"-\",",
+ " z->atom & 8 ?\"L\":\"G\",",
+ " srcln[j]);",
+ " for (i = 0; z->tp[i]; i++)",
+ " if (z->tp[i] == \'\\n\')",
+ " printf(\"\\\\n\");",
+ " else",
+ " putchar(z->tp[i]);",
+ " if (z->qu[0])",
+ " { printf(\"\\t[\");",
+ " for (i = 0; i < 6; i++)",
+ " if (z->qu[i])",
+ " printf(\"(%%d,%%d)\",",
+ " z->qu[i], z->ty[i]);",
+ " printf(\"]\");",
+ " }",
+ " printf(\"\\n\");",
+ " fflush(stdout);",
+ "}",
+ "",
+ "#ifdef VAR_RANGES",
+ "#define BYTESIZE 32 /* 2^8 : 2^3 = 256:8 = 32 */",
+ "",
+ "typedef struct Vr_Ptr {",
+ " char *nm;",
+ " uchar vals[BYTESIZE];",
+ " struct Vr_Ptr *nxt;",
+ "} Vr_Ptr;",
+ "Vr_Ptr *ranges = (Vr_Ptr *) 0;",
+ "",
+ "void",
+ "logval(char *s, int v)",
+ "{ Vr_Ptr *tmp;",
+ "",
+ " if (v<0 || v > 255) return;",
+ " for (tmp = ranges; tmp; tmp = tmp->nxt)",
+ " if (!strcmp(tmp->nm, s))",
+ " goto found;",
+ " tmp = (Vr_Ptr *) emalloc(sizeof(Vr_Ptr));",
+ " tmp->nxt = ranges;",
+ " ranges = tmp;",
+ " tmp->nm = s;",
+ "found:",
+ " tmp->vals[(v)/8] |= 1<<((v)%%8);",
+ "}",
+ "",
+ "void",
+ "dumpval(uchar X[], int range)",
+ "{ int w, x, i, j = -1;",
+ "",
+ " for (w = i = 0; w < range; w++)",
+ " for (x = 0; x < 8; x++, i++)",
+ " {",
+ "from: if ((X[w] & (1<<x)))",
+ " { printf(\"%%d\", i);",
+ " j = i;",
+ " goto upto;",
+ " } }",
+ " return;",
+ " for (w = 0; w < range; w++)",
+ " for (x = 0; x < 8; x++, i++)",
+ " {",
+ "upto: if (!(X[w] & (1<<x)))",
+ " { if (i-1 == j)",
+ " printf(\", \");",
+ " else",
+ " printf(\"-%%d, \", i-1);",
+ " goto from;",
+ " } }",
+ " if (j >= 0 && j != 255)",
+ " printf(\"-255\");",
+ "}",
+ "",
+ "void",
+ "dumpranges(void)",
+ "{ Vr_Ptr *tmp;",
+ " printf(\"\\nValues assigned within \");",
+ " printf(\"interval [0..255]:\\n\");",
+ " for (tmp = ranges; tmp; tmp = tmp->nxt)",
+ " { printf(\"\\t%%s\\t: \", tmp->nm);",
+ " dumpval(tmp->vals, BYTESIZE);",
+ " printf(\"\\n\");",
+ " }",
+ "}",
+ "#endif",
+ 0,
+};
--- /dev/null
+/***** spin: pangen3.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern FILE *th;
+extern int claimnr, eventmapnr;
+
+typedef struct SRC {
+ short ln, st; /* linenr, statenr */
+ Symbol *fn; /* filename */
+ struct SRC *nxt;
+} SRC;
+
+static int col;
+static Symbol *lastfnm;
+static Symbol lastdef;
+static int lastfrom;
+static SRC *frst = (SRC *) 0;
+static SRC *skip = (SRC *) 0;
+
+extern void sr_mesg(FILE *, int, int);
+
+static void
+putnr(int n)
+{
+ if (col++ == 8)
+ { fprintf(th, "\n\t");
+ col = 1;
+ }
+ fprintf(th, "%3d, ", n);
+}
+
+static void
+putfnm(int j, Symbol *s)
+{
+ if (lastfnm && lastfnm == s && j != -1)
+ return;
+
+ if (lastfnm)
+ fprintf(th, "{ %s, %d, %d },\n\t",
+ lastfnm->name,
+ lastfrom,
+ j-1);
+ lastfnm = s;
+ lastfrom = j;
+}
+
+static void
+putfnm_flush(int j)
+{
+ if (lastfnm)
+ fprintf(th, "{ %s, %d, %d }\n",
+ lastfnm->name,
+ lastfrom, j);
+}
+
+void
+putskip(int m) /* states that need not be reached */
+{ SRC *tmp;
+
+ for (tmp = skip; tmp; tmp = tmp->nxt)
+ if (tmp->st == m)
+ return;
+ tmp = (SRC *) emalloc(sizeof(SRC));
+ tmp->st = (short) m;
+ tmp->nxt = skip;
+ skip = tmp;
+}
+
+void
+unskip(int m) /* a state that needs to be reached after all */
+{ SRC *tmp, *lst=(SRC *)0;
+
+ for (tmp = skip; tmp; lst = tmp, tmp = tmp->nxt)
+ if (tmp->st == m)
+ { if (tmp == skip)
+ skip = skip->nxt;
+ else if (lst) /* always true, but helps coverity */
+ lst->nxt = tmp->nxt;
+ break;
+ }
+}
+
+void
+putsrc(Element *e) /* match states to source lines */
+{ SRC *tmp;
+ int n, m;
+
+ if (!e || !e->n) return;
+
+ n = e->n->ln;
+ m = e->seqno;
+
+ for (tmp = frst; tmp; tmp = tmp->nxt)
+ if (tmp->st == m)
+ { if (tmp->ln != n || tmp->fn != e->n->fn)
+ printf("putsrc mismatch %d - %d, file %s\n", n,
+ tmp->ln, tmp->fn->name);
+ return;
+ }
+ tmp = (SRC *) emalloc(sizeof(SRC));
+ tmp->ln = (short) n;
+ tmp->st = (short) m;
+ tmp->fn = e->n->fn;
+ tmp->nxt = frst;
+ frst = tmp;
+}
+
+static void
+dumpskip(int n, int m)
+{ SRC *tmp, *lst;
+ int j;
+
+ fprintf(th, "uchar reached%d [] = {\n\t", m);
+ for (j = 0, col = 0; j <= n; j++)
+ { lst = (SRC *) 0;
+ for (tmp = skip; tmp; lst = tmp, tmp = tmp->nxt)
+ if (tmp->st == j)
+ { putnr(1);
+ if (lst)
+ lst->nxt = tmp->nxt;
+ else
+ skip = tmp->nxt;
+ break;
+ }
+ if (!tmp)
+ putnr(0);
+ }
+ fprintf(th, "};\n");
+
+ fprintf(th, "uchar *loopstate%d;\n", m);
+
+ if (m == claimnr)
+ fprintf(th, "#define reached_claim reached%d\n", m);
+ if (m == eventmapnr)
+ fprintf(th, "#define reached_event reached%d\n", m);
+
+ skip = (SRC *) 0;
+}
+
+void
+dumpsrc(int n, int m)
+{ SRC *tmp, *lst;
+ int j;
+
+ fprintf(th, "short src_ln%d [] = {\n\t", m);
+ for (j = 0, col = 0; j <= n; j++)
+ { lst = (SRC *) 0;
+ for (tmp = frst; tmp; lst = tmp, tmp = tmp->nxt)
+ if (tmp->st == j)
+ { putnr(tmp->ln);
+ break;
+ }
+ if (!tmp)
+ putnr(0);
+ }
+ fprintf(th, "};\n");
+
+ lastfnm = (Symbol *) 0;
+ lastdef.name = "\"-\"";
+ fprintf(th, "S_F_MAP src_file%d [] = {\n\t", m);
+ for (j = 0, col = 0; j <= n; j++)
+ { lst = (SRC *) 0;
+ for (tmp = frst; tmp; lst = tmp, tmp = tmp->nxt)
+ if (tmp->st == j)
+ { putfnm(j, tmp->fn);
+ if (lst)
+ lst->nxt = tmp->nxt;
+ else
+ frst = tmp->nxt;
+ break;
+ }
+ if (!tmp)
+ putfnm(j, &lastdef);
+ }
+ putfnm_flush(j);
+ fprintf(th, "};\n");
+
+ if (m == claimnr)
+ fprintf(th, "#define src_claim src_ln%d\n", m);
+ if (m == eventmapnr)
+ fprintf(th, "#define src_event src_ln%d\n", m);
+
+ frst = (SRC *) 0;
+ dumpskip(n, m);
+}
+
+#define Cat0(x) comwork(fd,now->lft,m); fprintf(fd, x); \
+ comwork(fd,now->rgt,m)
+#define Cat1(x) fprintf(fd,"("); Cat0(x); fprintf(fd,")")
+#define Cat2(x,y) fprintf(fd,x); comwork(fd,y,m)
+#define Cat3(x,y,z) fprintf(fd,x); comwork(fd,y,m); fprintf(fd,z)
+
+static int
+symbolic(FILE *fd, Lextok *tv)
+{ Lextok *n; extern Lextok *Mtype;
+ int cnt = 1;
+
+ if (tv->ismtyp)
+ for (n = Mtype; n; n = n->rgt, cnt++)
+ if (cnt == tv->val)
+ { fprintf(fd, "%s", n->lft->sym->name);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+comwork(FILE *fd, Lextok *now, int m)
+{ Lextok *v;
+ int i, j;
+
+ if (!now) { fprintf(fd, "0"); return; }
+ switch (now->ntyp) {
+ case CONST: sr_mesg(fd, now->val, now->ismtyp); break;
+ case '!': Cat3("!(", now->lft, ")"); break;
+ case UMIN: Cat3("-(", now->lft, ")"); break;
+ case '~': Cat3("~(", now->lft, ")"); break;
+
+ case '/': Cat1("/"); break;
+ case '*': Cat1("*"); break;
+ case '-': Cat1("-"); break;
+ case '+': Cat1("+"); break;
+ case '%': Cat1("%%"); break;
+ case '&': Cat1("&"); break;
+ case '^': Cat1("^"); break;
+ case '|': Cat1("|"); break;
+ case LE: Cat1("<="); break;
+ case GE: Cat1(">="); break;
+ case GT: Cat1(">"); break;
+ case LT: Cat1("<"); break;
+ case NE: Cat1("!="); break;
+ case EQ: Cat1("=="); break;
+ case OR: Cat1("||"); break;
+ case AND: Cat1("&&"); break;
+ case LSHIFT: Cat1("<<"); break;
+ case RSHIFT: Cat1(">>"); break;
+
+ case RUN: fprintf(fd, "run %s(", now->sym->name);
+ for (v = now->lft; v; v = v->rgt)
+ if (v == now->lft)
+ { comwork(fd, v->lft, m);
+ } else
+ { Cat2(",", v->lft);
+ }
+ fprintf(fd, ")");
+ break;
+
+ case LEN: putname(fd, "len(", now->lft, m, ")");
+ break;
+ case FULL: putname(fd, "full(", now->lft, m, ")");
+ break;
+ case EMPTY: putname(fd, "empty(", now->lft, m, ")");
+ break;
+ case NFULL: putname(fd, "nfull(", now->lft, m, ")");
+ break;
+ case NEMPTY: putname(fd, "nempty(", now->lft, m, ")");
+ break;
+
+ case 's': putname(fd, "", now->lft, m, now->val?"!!":"!");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v != now->rgt) fprintf(fd,",");
+ if (!symbolic(fd, v->lft))
+ comwork(fd,v->lft,m);
+ }
+ break;
+ case 'r': putname(fd, "", now->lft, m, "?");
+ switch (now->val) {
+ case 0: break;
+ case 1: fprintf(fd, "?"); break;
+ case 2: fprintf(fd, "<"); break;
+ case 3: fprintf(fd, "?<"); break;
+ }
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v != now->rgt) fprintf(fd,",");
+ if (!symbolic(fd, v->lft))
+ comwork(fd,v->lft,m);
+ }
+ if (now->val >= 2)
+ fprintf(fd, ">");
+ break;
+ case 'R': putname(fd, "", now->lft, m, now->val?"??[":"?[");
+ for (v = now->rgt, i=0; v; v = v->rgt, i++)
+ { if (v != now->rgt) fprintf(fd,",");
+ if (!symbolic(fd, v->lft))
+ comwork(fd,v->lft,m);
+ }
+ fprintf(fd, "]");
+ break;
+
+ case ENABLED: Cat3("enabled(", now->lft, ")");
+ break;
+
+ case EVAL: Cat3("eval(", now->lft, ")");
+ break;
+
+ case NONPROGRESS:
+ fprintf(fd, "np_");
+ break;
+
+ case PC_VAL: Cat3("pc_value(", now->lft, ")");
+ break;
+
+ case 'c': Cat3("(", now->lft, ")");
+ break;
+
+ case '?': if (now->lft)
+ { Cat3("( (", now->lft, ") -> ");
+ }
+ if (now->rgt)
+ { Cat3("(", now->rgt->lft, ") : ");
+ Cat3("(", now->rgt->rgt, ") )");
+ }
+ break;
+
+ case ASGN: comwork(fd,now->lft,m);
+ fprintf(fd," = ");
+ comwork(fd,now->rgt,m);
+ break;
+
+ case PRINT: { char c, buf[512];
+ strncpy(buf, now->sym->name, 510);
+ for (i = j = 0; i < 510; i++, j++)
+ { c = now->sym->name[i];
+ buf[j] = c;
+ if (c == '\\') buf[++j] = c;
+ if (c == '\"') buf[j] = '\'';
+ if (c == '\0') break;
+ }
+ if (now->ntyp == PRINT)
+ fprintf(fd, "printf");
+ else
+ fprintf(fd, "annotate");
+ fprintf(fd, "(%s", buf);
+ }
+ for (v = now->lft; v; v = v->rgt)
+ { Cat2(",", v->lft);
+ }
+ fprintf(fd, ")");
+ break;
+ case PRINTM: fprintf(fd, "printm(");
+ comwork(fd, now->lft, m);
+ fprintf(fd, ")");
+ break;
+ case NAME: putname(fd, "", now, m, "");
+ break;
+ case 'p': putremote(fd, now, m);
+ break;
+ case 'q': fprintf(fd, "%s", now->sym->name);
+ break;
+ case C_EXPR:
+ case C_CODE: fprintf(fd, "{%s}", now->sym->name);
+ break;
+ case ASSERT: Cat3("assert(", now->lft, ")");
+ break;
+ case '.': fprintf(fd, ".(goto)"); break;
+ case GOTO: fprintf(fd, "goto %s", now->sym->name); break;
+ case BREAK: fprintf(fd, "break"); break;
+ case ELSE: fprintf(fd, "else"); break;
+ case '@': fprintf(fd, "-end-"); break;
+
+ case D_STEP: fprintf(fd, "D_STEP"); break;
+ case ATOMIC: fprintf(fd, "ATOMIC"); break;
+ case NON_ATOMIC: fprintf(fd, "sub-sequence"); break;
+ case IF: fprintf(fd, "IF"); break;
+ case DO: fprintf(fd, "DO"); break;
+ case UNLESS: fprintf(fd, "unless"); break;
+ case TIMEOUT: fprintf(fd, "timeout"); break;
+ default: if (isprint(now->ntyp))
+ fprintf(fd, "'%c'", now->ntyp);
+ else
+ fprintf(fd, "%d", now->ntyp);
+ break;
+ }
+}
+
+void
+comment(FILE *fd, Lextok *now, int m)
+{ extern short terse, nocast;
+
+ terse=nocast=1;
+ comwork(fd, now, m);
+ terse=nocast=0;
+}
--- /dev/null
+/***** spin: pangen3.h *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
+
+static char *Head0[] = {
+ "#if defined(BFS) && defined(REACH)",
+ "#undef REACH", /* redundant with bfs */
+ "#endif",
+ "#ifdef VERI",
+ "#define BASE 1",
+ "#else",
+ "#define BASE 0",
+ "#endif",
+ "typedef struct Trans {",
+ " short atom; /* if &2 = atomic trans; if &8 local */",
+ "#ifdef HAS_UNLESS",
+ " short escp[HAS_UNLESS]; /* lists the escape states */",
+ " short e_trans; /* if set, this is an escp-trans */",
+ "#endif",
+ " short tpe[2]; /* class of operation (for reduction) */",
+ " short qu[6]; /* for conditional selections: qid's */",
+ " uchar ty[6]; /* ditto: type's */",
+ "#ifdef NIBIS",
+ " short om; /* completion status of preselects */",
+ "#endif",
+ " char *tp; /* src txt of statement */",
+ " int st; /* the nextstate */",
+ " int t_id; /* transition id, unique within proc */",
+ " int forw; /* index forward transition */",
+ " int back; /* index return transition */",
+ " struct Trans *nxt;",
+ "} Trans;\n",
+ "#define qptr(x) (((uchar *)&now)+(int)q_offset[x])",
+ "#define pptr(x) (((uchar *)&now)+(int)proc_offset[x])",
+/* "#define Pptr(x) ((proc_offset[x])?pptr(x):noptr)", */
+ "extern uchar *Pptr(int);",
+
+ "#define q_sz(x) (((Q0 *)qptr(x))->Qlen)\n",
+ "#ifndef VECTORSZ",
+ "#define VECTORSZ 1024 /* sv size in bytes */",
+ "#endif\n",
+ 0,
+};
+
+static char *Header[] = {
+ "#ifdef VERBOSE",
+ "#ifndef CHECK",
+ "#define CHECK",
+ "#endif",
+ "#ifndef DEBUG",
+ "#define DEBUG",
+ "#endif",
+ "#endif",
+ "#ifdef SAFETY",
+ "#ifndef NOFAIR",
+ "#define NOFAIR",
+ "#endif",
+ "#endif",
+ "#ifdef NOREDUCE",
+ "#ifndef XUSAFE",
+ "#define XUSAFE",
+ "#endif",
+ "#if !defined(SAFETY) && !defined(MA)",
+ "#define FULLSTACK",
+ "#endif",
+ "#else",
+ "#ifdef BITSTATE",
+ "#if defined(SAFETY) && !defined(HASH64)",
+ "#define CNTRSTACK",
+ "#else",
+ "#define FULLSTACK",
+ "#endif",
+ "#else",
+ "#define FULLSTACK",
+ "#endif",
+ "#endif",
+ "#ifdef BITSTATE",
+ "#ifndef NOCOMP",
+ "#define NOCOMP",
+ "#endif",
+ "#if !defined(LC) && defined(SC)",
+ "#define LC",
+ "#endif",
+ "#endif",
+ "#if defined(COLLAPSE2) || defined(COLLAPSE3) || defined(COLLAPSE4)",
+ "/* accept the above for backward compatibility */",
+ "#define COLLAPSE",
+ "#endif",
+ "#ifdef HC",
+ "#undef HC",
+ "#define HC4",
+ "#endif",
+ "#ifdef HC0", /* 32 bits */
+ "#define HC 0",
+ "#endif",
+ "#ifdef HC1", /* 32+8 bits */
+ "#define HC 1",
+ "#endif",
+ "#ifdef HC2", /* 32+16 bits */
+ "#define HC 2",
+ "#endif",
+ "#ifdef HC3", /* 32+24 bits */
+ "#define HC 3",
+ "#endif",
+ "#ifdef HC4", /* 32+32 bits - combine with -DMA=8 */
+ "#define HC 4",
+ "#endif",
+ "#ifdef COLLAPSE",
+ "#if NCORE>1 && !defined(SEP_STATE)",
+ "unsigned long *ncomps; /* in shared memory */",
+ "#else",
+ "unsigned long ncomps[256+2];",
+ "#endif",
+ "#endif",
+
+ "#define MAXQ 255",
+ "#define MAXPROC 255",
+ "#define WS sizeof(void *) /* word size in bytes */",
+ "typedef struct Stack { /* for queues and processes */",
+ "#if VECTORSZ>32000",
+ " int o_delta;",
+ " int o_offset;",
+ " int o_skip;",
+ " int o_delqs;",
+ "#else",
+ " short o_delta;",
+ " short o_offset;",
+ " short o_skip;",
+ " short o_delqs;",
+ "#endif",
+ " short o_boq;",
+ "#ifndef XUSAFE",
+ " char *o_name;",
+ "#endif",
+ " char *body;",
+ " struct Stack *nxt;",
+ " struct Stack *lst;",
+ "} Stack;\n",
+ "typedef struct Svtack { /* for complete state vector */",
+ "#if VECTORSZ>32000",
+ " int o_delta;",
+ " int m_delta;",
+ "#else",
+ " short o_delta; /* current size of frame */",
+ " short m_delta; /* maximum size of frame */",
+ "#endif",
+ "#if SYNC",
+ " short o_boq;",
+ "#endif",
+ 0,
+};
+
+static char *Header0[] = {
+ " char *body;",
+ " struct Svtack *nxt;",
+ " struct Svtack *lst;",
+ "} Svtack;\n",
+ "Trans ***trans; /* 1 ptr per state per proctype */\n",
+ "struct H_el *Lstate;",
+ "int depthfound = -1; /* loop detection */",
+ "#if VECTORSZ>32000",
+ "int proc_offset[MAXPROC];",
+ "int q_offset[MAXQ];",
+ "#else",
+ "short proc_offset[MAXPROC];",
+ "short q_offset[MAXQ];",
+ "#endif",
+ "uchar proc_skip[MAXPROC];",
+ "uchar q_skip[MAXQ];",
+ "unsigned long vsize; /* vector size in bytes */",
+ "#ifdef SVDUMP",
+ "int vprefix=0, svfd; /* runtime option -pN */",
+ "#endif",
+ "char *tprefix = \"trail\"; /* runtime option -tsuffix */",
+ "short boq = -1; /* blocked_on_queue status */",
+ 0,
+};
+
+static char *Head1[] = {
+ "typedef struct State {",
+ " uchar _nr_pr;",
+ " uchar _nr_qs;",
+ " uchar _a_t; /* cycle detection */",
+#if 0
+ in _a_t: bits 0,4, and 5 =(1|16|32) are set during a 2nd dfs
+ bit 1 is used as the A-bit for fairness
+ bit 7 (128) is the proviso bit, for reduced 2nd dfs (acceptance)
+#endif
+ "#ifndef NOFAIR",
+ " uchar _cnt[NFAIR]; /* counters, weak fairness */",
+ "#endif",
+
+ "#ifndef NOVSZ",
+#ifdef SOLARIS
+ "#if 0",
+ /* v3.4
+ * noticed alignment problems with some Solaris
+ * compilers, if widest field isn't wordsized
+ */
+#else
+ "#if VECTORSZ<65536",
+#endif
+ " unsigned short _vsz;",
+ "#else",
+ " unsigned long _vsz;",
+ "#endif",
+ "#endif",
+
+ "#ifdef HAS_LAST", /* cannot go before _cnt - see hstore() */
+ " uchar _last; /* pid executed in last step */",
+ "#endif",
+ "#ifdef EVENT_TRACE",
+ "#if nstates_event<256",
+ " uchar _event;",
+ "#else",
+ " unsigned short _event;",
+ "#endif",
+ "#endif",
+ 0,
+};
+
+static char *Addp0[] = {
+ /* addproc(....parlist... */ ")",
+ "{ int j, h = now._nr_pr;",
+ "#ifndef NOCOMP",
+ " int k;",
+ "#endif",
+ " uchar *o_this = this;\n",
+ "#ifndef INLINE",
+ " if (TstOnly) return (h < MAXPROC);",
+ "#endif",
+ "#ifndef NOBOUNDCHECK",
+ "/* redefine Index only within this procedure */",
+ "#undef Index",
+ "#define Index(x, y) Boundcheck(x, y, 0, 0, 0)",
+ "#endif",
+ " if (h >= MAXPROC)",
+ " Uerror(\"too many processes\");",
+ " switch (n) {",
+ " case 0: j = sizeof(P0); break;",
+ 0,
+};
+
+static char *Addp1[] = {
+ " default: Uerror(\"bad proc - addproc\");",
+ " }",
+ " if (vsize%%WS)",
+ " proc_skip[h] = WS-(vsize%%WS);",
+ " else",
+ " proc_skip[h] = 0;",
+ "#ifndef NOCOMP",
+ " for (k = vsize + (int) proc_skip[h]; k > vsize; k--)",
+ " Mask[k-1] = 1; /* align */",
+ "#endif",
+ " vsize += (int) proc_skip[h];",
+ " proc_offset[h] = vsize;",
+ "#ifdef SVDUMP",
+ " if (vprefix > 0)",
+ " { int dummy = 0;",
+ " write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */",
+ " write(svfd, (uchar *) &h, sizeof(int));",
+ " write(svfd, (uchar *) &n, sizeof(int));",
+ "#if VECTORSZ>32000",
+ " write(svfd, (uchar *) &proc_offset[h], sizeof(int));",
+ "#else",
+ " write(svfd, (uchar *) &proc_offset[h], sizeof(short));",
+ "#endif",
+ " write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */",
+ " }",
+ "#endif",
+ " now._nr_pr += 1;",
+ " if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2))",
+ " { printf(\"pan: error: too many processes -- current\");",
+ " printf(\" max is %%d procs (-DNFAIR=%%d)\\n\",",
+ " (8*NFAIR)/2 - 2, NFAIR);",
+ " printf(\"\\trecompile with -DNFAIR=%%d\\n\",",
+ " NFAIR+1);",
+ " pan_exit(1);",
+ " }",
+
+ " vsize += j;",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ "#ifndef NOCOMP",
+ " for (k = 1; k <= Air[n]; k++)",
+ " Mask[vsize - k] = 1; /* pad */",
+ " Mask[vsize-j] = 1; /* _pid */",
+ "#endif",
+ " hmax = max(hmax, vsize);",
+ " if (vsize >= VECTORSZ)",
+ " { printf(\"pan: error, VECTORSZ too small, recompile pan.c\");",
+ " printf(\" with -DVECTORSZ=N with N>%%d\\n\", (int) vsize);",
+ " Uerror(\"aborting\");",
+ " }",
+ " memset((char *)pptr(h), 0, j);",
+ " this = pptr(h);",
+ " if (BASE > 0 && h > 0)",
+ " ((P0 *)this)->_pid = h-BASE;",
+ " else",
+ " ((P0 *)this)->_pid = h;",
+ " switch (n) {",
+ 0,
+};
+
+static char *Addq0[] = {
+ "int",
+ "addqueue(int n, int is_rv)",
+ "{ int j=0, i = now._nr_qs;",
+ "#ifndef NOCOMP",
+ " int k;",
+ "#endif",
+ " if (i >= MAXQ)",
+ " Uerror(\"too many queues\");",
+ " switch (n) {",
+ 0,
+};
+
+static char *Addq1[] = {
+ " default: Uerror(\"bad queue - addqueue\");",
+ " }",
+ " if (vsize%%WS)",
+ " q_skip[i] = WS-(vsize%%WS);",
+ " else",
+ " q_skip[i] = 0;",
+ "#ifndef NOCOMP",
+ " k = vsize;",
+ "#ifndef BFS",
+ " if (is_rv) k += j;",
+ "#endif",
+ " for (k += (int) q_skip[i]; k > vsize; k--)",
+ " Mask[k-1] = 1;",
+ "#endif",
+ " vsize += (int) q_skip[i];",
+ " q_offset[i] = vsize;",
+ " now._nr_qs += 1;",
+ " vsize += j;",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ " hmax = max(hmax, vsize);",
+ " if (vsize >= VECTORSZ)",
+ " Uerror(\"VECTORSZ is too small, edit pan.h\");",
+ " memset((char *)qptr(i), 0, j);",
+ " ((Q0 *)qptr(i))->_t = n;",
+ " return i+1;",
+ "}\n",
+ 0,
+};
+
+static char *Addq11[] = {
+ "{ int j; uchar *z;\n",
+ "#ifdef HAS_SORTED",
+ " int k;",
+ "#endif",
+ " if (!into--)",
+ " uerror(\"ref to uninitialized chan name (sending)\");",
+ " if (into >= (int) now._nr_qs || into < 0)",
+ " Uerror(\"qsend bad queue#\");",
+ " z = qptr(into);",
+ " j = ((Q0 *)qptr(into))->Qlen;",
+ " switch (((Q0 *)qptr(into))->_t) {",
+ 0,
+};
+
+static char *Addq2[] = {
+ " case 0: printf(\"queue %%d was deleted\\n\", into+1);",
+ " default: Uerror(\"bad queue - qsend\");",
+ " }",
+ "#ifdef EVENT_TRACE",
+ " if (in_s_scope(into+1))",
+ " require('s', into);",
+ "#endif",
+ "}",
+ "#endif\n",
+ "#if SYNC",
+ "int",
+ "q_zero(int from)",
+ "{ if (!from--)",
+ " { uerror(\"ref to uninitialized chan name (q_zero)\");",
+ " return 0;",
+ " }",
+ " switch(((Q0 *)qptr(from))->_t) {",
+ 0,
+};
+
+static char *Addq3[] = {
+ " case 0: printf(\"queue %%d was deleted\\n\", from+1);",
+ " }",
+ " Uerror(\"bad queue q-zero\");",
+ " return -1;",
+ "}",
+ "int",
+ "not_RV(int from)",
+ "{ if (q_zero(from))",
+ " { printf(\"==>> a test of the contents of a rv \");",
+ " printf(\"channel always returns FALSE\\n\");",
+ " uerror(\"error to poll rendezvous channel\");",
+ " }",
+ " return 1;",
+ "}",
+ "#endif",
+ "#ifndef XUSAFE",
+ "void",
+ "setq_claim(int x, int m, char *s, int y, char *p)",
+ "{ if (x == 0)",
+ " uerror(\"x[rs] claim on uninitialized channel\");",
+ " if (x < 0 || x > MAXQ)",
+ " Uerror(\"cannot happen setq_claim\");",
+ " q_claim[x] |= m;",
+ " p_name[y] = p;",
+ " q_name[x] = s;",
+ " if (m&2) q_S_check(x, y);",
+ " if (m&1) q_R_check(x, y);",
+ "}",
+ "short q_sender[MAXQ+1];",
+ "int",
+ "q_S_check(int x, int who)",
+ "{ if (!q_sender[x])",
+ " { q_sender[x] = who+1;",
+ "#if SYNC",
+ " if (q_zero(x))",
+ " { printf(\"chan %%s (%%d), \",",
+ " q_name[x], x-1);",
+ " printf(\"sndr proc %%s (%%d)\\n\",",
+ " p_name[who], who);",
+ " uerror(\"xs chans cannot be used for rv\");",
+ " }",
+ "#endif",
+ " } else",
+ " if (q_sender[x] != who+1)",
+ " { printf(\"pan: xs assertion violated: \");",
+ " printf(\"access to chan <%%s> (%%d)\\npan: by \",",
+ " q_name[x], x-1);",
+ " if (q_sender[x] > 0 && p_name[q_sender[x]-1])",
+ " printf(\"%%s (proc %%d) and by \",",
+ " p_name[q_sender[x]-1], q_sender[x]-1);",
+ " printf(\"%%s (proc %%d)\\n\",",
+ " p_name[who], who);",
+ " uerror(\"error, partial order reduction invalid\");",
+ " }",
+ " return 1;",
+ "}",
+ "short q_recver[MAXQ+1];",
+ "int",
+ "q_R_check(int x, int who)",
+ "{ if (!q_recver[x])",
+ " { q_recver[x] = who+1;",
+ "#if SYNC",
+ " if (q_zero(x))",
+ " { printf(\"chan %%s (%%d), \",",
+ " q_name[x], x-1);",
+ " printf(\"recv proc %%s (%%d)\\n\",",
+ " p_name[who], who);",
+ " uerror(\"xr chans cannot be used for rv\");",
+ " }",
+ "#endif",
+ " } else",
+ " if (q_recver[x] != who+1)",
+ " { printf(\"pan: xr assertion violated: \");",
+ " printf(\"access to chan %%s (%%d)\\npan: \",",
+ " q_name[x], x-1);",
+ " if (q_recver[x] > 0 && p_name[q_recver[x]-1])",
+ " printf(\"by %%s (proc %%d) and \",",
+ " p_name[q_recver[x]-1], q_recver[x]-1);",
+ " printf(\"by %%s (proc %%d)\\n\",",
+ " p_name[who], who);",
+ " uerror(\"error, partial order reduction invalid\");",
+ " }",
+ " return 1;",
+ "}",
+ "#endif",
+ "int",
+ "q_len(int x)",
+ "{ if (!x--)",
+ " uerror(\"ref to uninitialized chan name (len)\");",
+ " return ((Q0 *)qptr(x))->Qlen;",
+ "}\n",
+ "int",
+ "q_full(int from)",
+ "{ if (!from--)",
+ " uerror(\"ref to uninitialized chan name (qfull)\");",
+ " switch(((Q0 *)qptr(from))->_t) {",
+ 0,
+};
+
+static char *Addq4[] = {
+ " case 0: printf(\"queue %%d was deleted\\n\", from+1);",
+ " }",
+ " Uerror(\"bad queue - q_full\");",
+ " return 0;",
+ "}\n",
+ "#ifdef HAS_UNLESS",
+ "int",
+ "q_e_f(int from)",
+ "{ /* empty or full */",
+ " return !q_len(from) || q_full(from);",
+ "}",
+ "#endif",
+ "#if NQS>0",
+ "int",
+ "qrecv(int from, int slot, int fld, int done)",
+ "{ uchar *z;",
+ " int j, k, r=0;\n",
+ " if (!from--)",
+ " uerror(\"ref to uninitialized chan name (receiving)\");",
+ " if (from >= (int) now._nr_qs || from < 0)",
+ " Uerror(\"qrecv bad queue#\");",
+ " z = qptr(from);",
+ "#ifdef EVENT_TRACE",
+ " if (done && (in_r_scope(from+1)))",
+ " require('r', from);",
+ "#endif",
+ " switch (((Q0 *)qptr(from))->_t) {",
+ 0,
+};
+
+static char *Addq5[] = {
+ " case 0: printf(\"queue %%d was deleted\\n\", from+1);",
+ " default: Uerror(\"bad queue - qrecv\");",
+ " }",
+ " return r;",
+ "}",
+ "#endif\n",
+ "#ifndef BITSTATE",
+ "#ifdef COLLAPSE",
+ "long",
+ "col_q(int i, char *z)",
+ "{ int j=0, k;",
+ " char *x, *y;",
+ " Q0 *ptr = (Q0 *) qptr(i);",
+ " switch (ptr->_t) {",
+ 0,
+};
+
+static char *Code0[] = {
+ "void",
+ "run(void)",
+ "{ /* int i; */",
+ " memset((char *)&now, 0, sizeof(State));",
+ " vsize = (unsigned long) (sizeof(State) - VECTORSZ);",
+ "#ifndef NOVSZ",
+ " now._vsz = vsize;",
+ "#endif",
+ "/* optional provisioning statements, e.g. to */",
+ "/* set hidden variables, used as constants */",
+ "#ifdef PROV",
+ "#include PROV",
+ "#endif",
+ " settable();",
+ 0,
+};
+
+static char *R0[] = {
+ " Maxbody = max(Maxbody, ((int) sizeof(P%d)));",
+ " reached[%d] = reached%d;",
+ " accpstate[%d] = (uchar *) emalloc(nstates%d);",
+ " progstate[%d] = (uchar *) emalloc(nstates%d);",
+ " loopstate%d = loopstate[%d] = (uchar *) emalloc(nstates%d);",
+ " stopstate[%d] = (uchar *) emalloc(nstates%d);",
+ " visstate[%d] = (uchar *) emalloc(nstates%d);",
+ " mapstate[%d] = (short *) emalloc(nstates%d * sizeof(short));",
+ "#ifdef HAS_CODE",
+ " NrStates[%d] = nstates%d;",
+ "#endif",
+ " stopstate[%d][endstate%d] = 1;",
+ 0,
+};
+
+static char *R0a[] = {
+ " retrans(%d, nstates%d, start%d, src_ln%d, reached%d, loopstate%d);",
+ 0,
+};
+static char *R0b[] = {
+ " if (state_tables)",
+ " { printf(\"\\nTransition Type: \");",
+ " printf(\"A=atomic; D=d_step; L=local; G=global\\n\");",
+ " printf(\"Source-State Labels: \");",
+ " printf(\"p=progress; e=end; a=accept;\\n\");",
+ "#ifdef MERGED",
+ " printf(\"Note: statement merging was used. Only the first\\n\");",
+ " printf(\" stmnt executed in each merge sequence is shown\\n\");",
+ " printf(\" (use spin -a -o3 to disable statement merging)\\n\");",
+ "#endif",
+ " pan_exit(0);",
+ " }",
+ 0,
+};
+
+static char *Code1[] = {
+ "#ifdef NP",
+ " #define ACCEPT_LAB 1 /* at least 1 in np_ */",
+ "#else",
+ " #define ACCEPT_LAB %d /* user-defined accept labels */",
+ "#endif",
+ "#ifdef MEMCNT",
+ " #ifdef MEMLIM",
+ " #warning -DMEMLIM takes precedence over -DMEMCNT",
+ " #undef MEMCNT",
+ " #else",
+ " #if MEMCNT<20",
+ " #warning using minimal value -DMEMCNT=20 (=1MB)",
+ " #define MEMLIM (1)",
+ " #undef MEMCNT",
+ " #else",
+ " #if MEMCNT==20",
+ " #define MEMLIM (1)",
+ " #undef MEMCNT",
+ " #else",
+ " #if MEMCNT>=50",
+ " #error excessive value for MEMCNT",
+ " #else",
+ " #define MEMLIM (1<<(MEMCNT-20))",
+ " #endif",
+ " #endif",
+ " #endif",
+ " #endif",
+ "#endif",
+
+ "#if NCORE>1 && !defined(MEMLIM)",
+ " #define MEMLIM (2048) /* need a default, using 2 GB */",
+ "#endif",
+ 0,
+};
+
+static char *Code3[] = {
+ "#define PROG_LAB %d /* progress labels */",
+ 0,
+};
+
+static char *R2[] = {
+ "uchar *accpstate[%d];",
+ "uchar *progstate[%d];",
+ "uchar *loopstate[%d];",
+ "uchar *reached[%d];",
+ "uchar *stopstate[%d];",
+ "uchar *visstate[%d];",
+ "short *mapstate[%d];",
+ "#ifdef HAS_CODE",
+ "int NrStates[%d];",
+ "#endif",
+ 0,
+};
+static char *R3[] = {
+ " Maxbody = max(Maxbody, ((int) sizeof(Q%d)));",
+ 0,
+};
+static char *R4[] = {
+ " r_ck(reached%d, nstates%d, %d, src_ln%d, src_file%d);",
+ 0,
+};
+static char *R5[] = {
+ " case %d: j = sizeof(P%d); break;",
+ 0,
+};
+static char *R6[] = {
+ " }",
+ " this = o_this;",
+ " return h-BASE;",
+ "#ifndef NOBOUNDCHECK",
+ "#undef Index",
+ "#define Index(x, y) Boundcheck(x, y, II, tt, t)",
+ "#endif",
+ "}\n",
+ "#if defined(BITSTATE) && defined(COLLAPSE)",
+ "/* just to allow compilation, to generate the error */",
+ "long col_p(int i, char *z) { return 0; }",
+ "long col_q(int i, char *z) { return 0; }",
+ "#endif",
+ "#ifndef BITSTATE",
+ "#ifdef COLLAPSE",
+ "long",
+ "col_p(int i, char *z)",
+ "{ int j, k; unsigned long ordinal(char *, long, short);",
+ " char *x, *y;",
+ " P0 *ptr = (P0 *) pptr(i);",
+ " switch (ptr->_t) {",
+ " case 0: j = sizeof(P0); break;",
+ 0,
+};
+static char *R8a[] = {
+ " default: Uerror(\"bad proctype - collapse\");",
+ " }",
+ " if (z) x = z; else x = scratch;",
+ " y = (char *) ptr; k = proc_offset[i];",
+
+ " for ( ; j > 0; j--, y++)",
+ " if (!Mask[k++]) *x++ = *y;",
+
+ " for (j = 0; j < WS-1; j++)",
+ " *x++ = 0;",
+ " x -= j;",
+ " if (z) return (long) (x - z);",
+ " return ordinal(scratch, x-scratch, (short) (2+ptr->_t));",
+ "}",
+ "#endif",
+ "#endif",
+ 0,
+};
+static char *R8b[] = {
+ " default: Uerror(\"bad qtype - collapse\");",
+ " }",
+ " if (z) x = z; else x = scratch;",
+ " y = (char *) ptr; k = q_offset[i];",
+
+ " /* no need to store the empty slots at the end */",
+ " j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]);",
+
+ " for ( ; j > 0; j--, y++)",
+ " if (!Mask[k++]) *x++ = *y;",
+
+ " for (j = 0; j < WS-1; j++)",
+ " *x++ = 0;",
+ " x -= j;",
+ " if (z) return (long) (x - z);",
+ " return ordinal(scratch, x-scratch, 1); /* chan */",
+ "}",
+ "#endif",
+ "#endif",
+ 0,
+};
+
+static char *R12[] = {
+ "\t\tcase %d: r = ((Q%d *)z)->contents[slot].fld%d; break;",
+ 0,
+};
+char *R13[] = {
+ "int ",
+ "unsend(int into)",
+ "{ int _m=0, j; uchar *z;\n",
+ "#ifdef HAS_SORTED",
+ " int k;",
+ "#endif",
+ " if (!into--)",
+ " uerror(\"ref to uninitialized chan (unsend)\");",
+ " z = qptr(into);",
+ " j = ((Q0 *)z)->Qlen;",
+ " ((Q0 *)z)->Qlen = --j;",
+ " switch (((Q0 *)qptr(into))->_t) {",
+ 0,
+};
+char *R14[] = {
+ " default: Uerror(\"bad queue - unsend\");",
+ " }",
+ " return _m;",
+ "}\n",
+ "void",
+ "unrecv(int from, int slot, int fld, int fldvar, int strt)",
+ "{ int j; uchar *z;\n",
+ " if (!from--)",
+ " uerror(\"ref to uninitialized chan (unrecv)\");",
+ " z = qptr(from);",
+ " j = ((Q0 *)z)->Qlen;",
+ " if (strt) ((Q0 *)z)->Qlen = j+1;",
+ " switch (((Q0 *)qptr(from))->_t) {",
+ 0,
+};
+char *R15[] = {
+ " default: Uerror(\"bad queue - qrecv\");",
+ " }",
+ "}",
+ 0,
+};
+static char *Proto[] = {
+ "",
+ "/** function prototypes **/",
+ "char *emalloc(unsigned long);",
+ "char *Malloc(unsigned long);",
+ "int Boundcheck(int, int, int, int, Trans *);",
+ "int addqueue(int, int);",
+ "/* int atoi(char *); */",
+ "/* int abort(void); */",
+ "int close(int);", /* should probably remove this */
+#if 0
+ "#ifndef SC",
+ "int creat(char *, unsigned short);",
+ "int write(int, void *, unsigned);",
+ "#endif",
+#endif
+ "int delproc(int, int);",
+ "int endstate(void);",
+ "int hstore(char *, int);",
+"#ifdef MA",
+ "int gstore(char *, int, uchar);",
+"#endif",
+ "int q_cond(short, Trans *);",
+ "int q_full(int);",
+ "int q_len(int);",
+ "int q_zero(int);",
+ "int qrecv(int, int, int, int);",
+ "int unsend(int);",
+ "/* void *sbrk(int); */",
+ "void Uerror(char *);",
+ "void assert(int, char *, int, int, Trans *);",
+ "void c_chandump(int);",
+ "void c_globals(void);",
+ "void c_locals(int, int);",
+ "void checkcycles(void);",
+ "void crack(int, int, Trans *, short *);",
+ "void d_sfh(const char *, int);",
+ "void sfh(const char *, int);",
+ "void d_hash(uchar *, int);",
+ "void s_hash(uchar *, int);",
+ "void r_hash(uchar *, int);",
+ "void delq(int);",
+ "void do_reach(void);",
+ "void pan_exit(int);",
+ "void exit(int);",
+ "void hinit(void);",
+ "void imed(Trans *, int, int, int);",
+ "void new_state(void);",
+ "void p_restor(int);",
+ "void putpeg(int, int);",
+ "void putrail(void);",
+ "void q_restor(void);",
+ "void retrans(int, int, int, short *, uchar *, uchar *);",
+ "void settable(void);",
+ "void setq_claim(int, int, char *, int, char *);",
+ "void sv_restor(void);",
+ "void sv_save(void);",
+ "void tagtable(int, int, int, short *, uchar *);",
+ "void do_dfs(int, int, int, short *, uchar *, uchar *);",
+ "void uerror(char *);",
+ "void unrecv(int, int, int, int, int);",
+ "void usage(FILE *);",
+ "void wrap_stats(void);",
+ "#if defined(FULLSTACK) && defined(BITSTATE)",
+ "int onstack_now(void);",
+ "void onstack_init(void);",
+ "void onstack_put(void);",
+ "void onstack_zap(void);",
+ "#endif",
+ "#ifndef XUSAFE",
+ "int q_S_check(int, int);",
+ "int q_R_check(int, int);",
+ "uchar q_claim[MAXQ+1];",
+ "char *q_name[MAXQ+1];",
+ "char *p_name[MAXPROC+1];",
+ "#endif",
+ 0,
+};
+
+static char *SvMap[] = {
+ "void",
+ "to_compile(void)",
+ "{ char ctd[1024], carg[64];",
+ "#ifdef BITSTATE",
+ " strcpy(ctd, \"-DBITSTATE \");",
+ "#else",
+ " strcpy(ctd, \"\");",
+ "#endif",
+ "#ifdef NOVSZ",
+ " strcat(ctd, \"-DNOVSZ \");",
+ "#endif",
+ "#ifdef REVERSE",
+ " strcat(ctd, \"-DREVERSE \");",
+ "#endif",
+ "#ifdef T_REVERSE",
+ " strcat(ctd, \"-DT_REVERSE \");",
+ "#endif",
+ "#ifdef RANDOMIZE",
+ " #if RANDOMIZE>0",
+ " sprintf(carg, \"-DRANDOMIZE=%%d \", RANDOMIZE);",
+ " strcat(ctd, carg);",
+ " #else",
+ " strcat(ctd, \"-DRANDOMIZE \");",
+ " #endif",
+ "#endif",
+ "#ifdef SCHED",
+ " sprintf(carg, \"-DSCHED=%%d \", SCHED);",
+ " strcat(ctd, carg);",
+ "#endif",
+ "#ifdef BFS",
+ " strcat(ctd, \"-DBFS \");",
+ "#endif",
+ "#ifdef MEMLIM",
+ " sprintf(carg, \"-DMEMLIM=%%d \", MEMLIM);",
+ " strcat(ctd, carg);",
+ "#else",
+ "#ifdef MEMCNT",
+ " sprintf(carg, \"-DMEMCNT=%%d \", MEMCNT);",
+ " strcat(ctd, carg);",
+ "#endif",
+ "#endif",
+ "#ifdef NOCLAIM",
+ " strcat(ctd, \"-DNOCLAIM \");",
+ "#endif",
+ "#ifdef SAFETY",
+ " strcat(ctd, \"-DSAFETY \");",
+ "#else",
+ "#ifdef NOFAIR",
+ " strcat(ctd, \"-DNOFAIR \");",
+ "#else",
+ "#ifdef NFAIR",
+ " if (NFAIR != 2)",
+ " { sprintf(carg, \"-DNFAIR=%%d \", NFAIR);",
+ " strcat(ctd, carg);",
+ " }",
+ "#endif",
+ "#endif",
+ "#endif",
+ "#ifdef NOREDUCE",
+ " strcat(ctd, \"-DNOREDUCE \");",
+ "#else",
+ "#ifdef XUSAFE",
+ " strcat(ctd, \"-DXUSAFE \");",
+ "#endif",
+ "#endif",
+ "#ifdef NP",
+ " strcat(ctd, \"-DNP \");",
+ "#endif",
+ "#ifdef PEG",
+ " strcat(ctd, \"-DPEG \");",
+ "#endif",
+ "#ifdef VAR_RANGES",
+ " strcat(ctd, \"-DVAR_RANGES \");",
+ "#endif",
+ "#ifdef HC0",
+ " strcat(ctd, \"-DHC0 \");",
+ "#endif",
+ "#ifdef HC1",
+ " strcat(ctd, \"-DHC1 \");",
+ "#endif",
+ "#ifdef HC2",
+ " strcat(ctd, \"-DHC2 \");",
+ "#endif",
+ "#ifdef HC3",
+ " strcat(ctd, \"-DHC3 \");",
+ "#endif",
+ "#ifdef HC4",
+ " strcat(ctd, \"-DHC4 \");",
+ "#endif",
+ "#ifdef CHECK",
+ " strcat(ctd, \"-DCHECK \");",
+ "#endif",
+ "#ifdef CTL",
+ " strcat(ctd, \"-DCTL \");",
+ "#endif",
+ "#ifdef NIBIS",
+ " strcat(ctd, \"-DNIBIS \");",
+ "#endif",
+ "#ifdef NOBOUNDCHECK",
+ " strcat(ctd, \"-DNOBOUNDCHECK \");",
+ "#endif",
+ "#ifdef NOSTUTTER",
+ " strcat(ctd, \"-DNOSTUTTER \");",
+ "#endif",
+ "#ifdef REACH",
+ " strcat(ctd, \"-DREACH \");",
+ "#endif",
+ "#ifdef PRINTF",
+ " strcat(ctd, \"-DPRINTF \");",
+ "#endif",
+ "#ifdef OTIM",
+ " strcat(ctd, \"-DOTIM \");",
+ "#endif",
+ "#ifdef COLLAPSE",
+ " strcat(ctd, \"-DCOLLAPSE \");",
+ "#endif",
+ "#ifdef MA",
+ " sprintf(carg, \"-DMA=%%d \", MA);",
+ " strcat(ctd, carg);",
+ "#endif",
+ "#ifdef SVDUMP",
+ " strcat(ctd, \"-DSVDUMP \");",
+ "#endif",
+ "#ifdef VECTORSZ",
+ " if (VECTORSZ != 1024)",
+ " { sprintf(carg, \"-DVECTORSZ=%%d \", VECTORSZ);",
+ " strcat(ctd, carg);",
+ " }",
+ "#endif",
+ "#ifdef VERBOSE",
+ " strcat(ctd, \"-DVERBOSE \");",
+ "#endif",
+ "#ifdef CHECK",
+ " strcat(ctd, \"-DCHECK \");",
+ "#endif",
+ "#ifdef SDUMP",
+ " strcat(ctd, \"-DSDUMP \");",
+ "#endif",
+ "#if NCORE>1",
+ " sprintf(carg, \"-DNCORE=%%d \", NCORE);",
+ " strcat(ctd, carg);",
+ "#endif",
+ "#ifdef SFH",
+ " sprintf(carg, \"-DSFH \");",
+ " strcat(ctd, carg);",
+ "#endif",
+ "#ifdef VMAX",
+ " if (VMAX != 256)",
+ " { sprintf(carg, \"-DVMAX=%%d \", VMAX);",
+ " strcat(ctd, carg);",
+ " }",
+ "#endif",
+ "#ifdef PMAX",
+ " if (PMAX != 16)",
+ " { sprintf(carg, \"-DPMAX=%%d \", PMAX);",
+ " strcat(ctd, carg);",
+ " }",
+ "#endif",
+ "#ifdef QMAX",
+ " if (QMAX != 16)",
+ " { sprintf(carg, \"-DQMAX=%%d \", QMAX);",
+ " strcat(ctd, carg);",
+ " }",
+ "#endif",
+ "#ifdef SET_WQ_SIZE",
+ " sprintf(carg, \"-DSET_WQ_SIZE=%%d \", SET_WQ_SIZE);",
+ " strcat(ctd, carg);",
+ "#endif",
+ " printf(\"Compiled as: cc -o pan %%span.c\\n\", ctd);",
+ "}",
+ 0,
+};
--- /dev/null
+/***** spin: pangen4.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern FILE *tc, *tb;
+extern Queue *qtab;
+extern Symbol *Fname;
+extern int lineno, m_loss, Pid, eventmapnr, multi_oval;
+extern short nocast, has_provided, has_sorted;
+extern char *R13[], *R14[], *R15[];
+
+static void check_proc(Lextok *, int);
+
+void
+undostmnt(Lextok *now, int m)
+{ Lextok *v;
+ int i, j;
+
+ if (!now)
+ { fprintf(tb, "0");
+ return;
+ }
+ lineno = now->ln;
+ Fname = now->fn;
+ switch (now->ntyp) {
+ case CONST: case '!': case UMIN:
+ case '~': case '/': case '*':
+ case '-': case '+': case '%':
+ case LT: case GT: case '&':
+ case '|': case LE: case GE:
+ case NE: case EQ: case OR:
+ case AND: case LSHIFT: case RSHIFT:
+ case TIMEOUT: case LEN: case NAME:
+ case FULL: case EMPTY: case 'R':
+ case NFULL: case NEMPTY: case ENABLED:
+ case '?': case PC_VAL: case '^':
+ case C_EXPR:
+ case NONPROGRESS:
+ putstmnt(tb, now, m);
+ break;
+
+ case RUN:
+ fprintf(tb, "delproc(0, now._nr_pr-1)");
+ break;
+
+ case 's':
+ if (Pid == eventmapnr) break;
+
+ if (m_loss)
+ fprintf(tb, "if (_m == 2) ");
+ putname(tb, "_m = unsend(", now->lft, m, ")");
+ break;
+
+ case 'r':
+ if (Pid == eventmapnr) break;
+
+ for (v = now->rgt, i=j=0; v; v = v->rgt, i++)
+ if (v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL)
+ j++;
+ if (j == 0 && now->val >= 2)
+ break; /* poll without side-effect */
+
+ { int ii = 0, jj;
+
+ for (v = now->rgt; v; v = v->rgt)
+ if ((v->lft->ntyp != CONST
+ && v->lft->ntyp != EVAL))
+ ii++; /* nr of things bupped */
+ if (now->val == 1)
+ { ii++;
+ jj = multi_oval - ii - 1;
+ fprintf(tb, "XX = trpt->bup.oval");
+ if (multi_oval > 0)
+ { fprintf(tb, "s[%d]", jj);
+ jj++;
+ }
+ fprintf(tb, ";\n\t\t");
+ } else
+ { fprintf(tb, "XX = 1;\n\t\t");
+ jj = multi_oval - ii - 1;
+ }
+
+ if (now->val < 2) /* not for channel poll */
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { switch(v->lft->ntyp) {
+ case CONST:
+ case EVAL:
+ fprintf(tb, "unrecv");
+ putname(tb, "(", now->lft, m, ", XX-1, ");
+ fprintf(tb, "%d, ", i);
+ if (v->lft->ntyp == EVAL)
+ undostmnt(v->lft->lft, m);
+ else
+ undostmnt(v->lft, m);
+ fprintf(tb, ", %d);\n\t\t", (i==0)?1:0);
+ break;
+ default:
+ fprintf(tb, "unrecv");
+ putname(tb, "(", now->lft, m, ", XX-1, ");
+ fprintf(tb, "%d, ", i);
+ if (v->lft->sym
+ && !strcmp(v->lft->sym->name, "_"))
+ { fprintf(tb, "trpt->bup.oval");
+ if (multi_oval > 0)
+ fprintf(tb, "s[%d]", jj);
+ } else
+ putstmnt(tb, v->lft, m);
+
+ fprintf(tb, ", %d);\n\t\t", (i==0)?1:0);
+ if (multi_oval > 0)
+ jj++;
+ break;
+ } }
+ jj = multi_oval - ii - 1;
+
+ if (now->val == 1 && multi_oval > 0)
+ jj++; /* new 3.4.0 */
+
+ for (v = now->rgt, i = 0; v; v = v->rgt, i++)
+ { switch(v->lft->ntyp) {
+ case CONST:
+ case EVAL:
+ break;
+ default:
+ if (!v->lft->sym
+ || strcmp(v->lft->sym->name, "_") != 0)
+ { nocast=1; putstmnt(tb,v->lft,m);
+ nocast=0; fprintf(tb, " = trpt->bup.oval");
+ if (multi_oval > 0)
+ fprintf(tb, "s[%d]", jj);
+ fprintf(tb, ";\n\t\t");
+ }
+ if (multi_oval > 0)
+ jj++;
+ break;
+ } }
+ multi_oval -= ii;
+ }
+ break;
+
+ case '@':
+ fprintf(tb, "p_restor(II);\n\t\t");
+ break;
+
+ case ASGN:
+ nocast=1; putstmnt(tb,now->lft,m);
+ nocast=0; fprintf(tb, " = trpt->bup.oval");
+ if (multi_oval > 0)
+ { multi_oval--;
+ fprintf(tb, "s[%d]", multi_oval-1);
+ }
+ check_proc(now->rgt, m);
+ break;
+
+ case 'c':
+ check_proc(now->lft, m);
+ break;
+
+ case '.':
+ case GOTO:
+ case ELSE:
+ case BREAK:
+ break;
+
+ case C_CODE:
+ fprintf(tb, "sv_restor();\n");
+ break;
+
+ case ASSERT:
+ case PRINT:
+ check_proc(now, m);
+ break;
+ case PRINTM:
+ break;
+
+ default:
+ printf("spin: bad node type %d (.b)\n", now->ntyp);
+ alldone(1);
+ }
+}
+
+int
+any_undo(Lextok *now)
+{ /* is there anything to undo on a return move? */
+ if (!now) return 1;
+ switch (now->ntyp) {
+ case 'c': return any_oper(now->lft, RUN);
+ case ASSERT:
+ case PRINT: return any_oper(now, RUN);
+
+ case PRINTM:
+ case '.':
+ case GOTO:
+ case ELSE:
+ case BREAK: return 0;
+ default: return 1;
+ }
+}
+
+int
+any_oper(Lextok *now, int oper)
+{ /* check if an expression contains oper operator */
+ if (!now) return 0;
+ if (now->ntyp == oper)
+ return 1;
+ return (any_oper(now->lft, oper) || any_oper(now->rgt, oper));
+}
+
+static void
+check_proc(Lextok *now, int m)
+{
+ if (!now)
+ return;
+ if (now->ntyp == '@' || now->ntyp == RUN)
+ { fprintf(tb, ";\n\t\t");
+ undostmnt(now, m);
+ }
+ check_proc(now->lft, m);
+ check_proc(now->rgt, m);
+}
+
+void
+genunio(void)
+{ char buf1[256];
+ Queue *q; int i;
+
+ ntimes(tc, 0, 1, R13);
+ for (q = qtab; q; q = q->nxt)
+ { fprintf(tc, "\tcase %d:\n", q->qid);
+
+ if (has_sorted)
+ { sprintf(buf1, "((Q%d *)z)->contents", q->qid);
+ fprintf(tc, "#ifdef HAS_SORTED\n");
+ fprintf(tc, "\t\tj = trpt->ipt;\n"); /* ipt was bup.oval */
+ fprintf(tc, "#endif\n");
+ fprintf(tc, "\t\tfor (k = j; k < ((Q%d *)z)->Qlen; k++)\n",
+ q->qid);
+ fprintf(tc, "\t\t{\n");
+ for (i = 0; i < q->nflds; i++)
+ fprintf(tc, "\t\t\t%s[k].fld%d = %s[k+1].fld%d;\n",
+ buf1, i, buf1, i);
+ fprintf(tc, "\t\t}\n");
+ fprintf(tc, "\t\tj = ((Q0 *)z)->Qlen;\n");
+ }
+
+ sprintf(buf1, "((Q%d *)z)->contents[j].fld", q->qid);
+ for (i = 0; i < q->nflds; i++)
+ fprintf(tc, "\t\t%s%d = 0;\n", buf1, i);
+ if (q->nslots==0)
+ { /* check if rendezvous succeeded, 1 level down */
+ fprintf(tc, "\t\t_m = (trpt+1)->o_m;\n");
+ fprintf(tc, "\t\tif (_m) (trpt-1)->o_pm |= 1;\n");
+ fprintf(tc, "\t\tUnBlock;\n");
+ } else
+ fprintf(tc, "\t\t_m = trpt->o_m;\n");
+
+ fprintf(tc, "\t\tbreak;\n");
+ }
+ ntimes(tc, 0, 1, R14);
+ for (q = qtab; q; q = q->nxt)
+ { sprintf(buf1, "((Q%d *)z)->contents", q->qid);
+ fprintf(tc, " case %d:\n", q->qid);
+ if (q->nslots == 0)
+ fprintf(tc, "\t\tif (strt) boq = from+1;\n");
+ else if (q->nslots > 1) /* shift */
+ { fprintf(tc, "\t\tif (strt && slot<%d)\n",
+ q->nslots-1);
+ fprintf(tc, "\t\t{\tfor (j--; j>=slot; j--)\n");
+ fprintf(tc, "\t\t\t{");
+ for (i = 0; i < q->nflds; i++)
+ { fprintf(tc, "\t%s[j+1].fld%d =\n\t\t\t",
+ buf1, i);
+ fprintf(tc, "\t%s[j].fld%d;\n\t\t\t",
+ buf1, i);
+ }
+ fprintf(tc, "}\n\t\t}\n");
+ }
+ strcat(buf1, "[slot].fld");
+ fprintf(tc, "\t\tif (strt) {\n");
+ for (i = 0; i < q->nflds; i++)
+ fprintf(tc, "\t\t\t%s%d = 0;\n", buf1, i);
+ fprintf(tc, "\t\t}\n");
+ if (q->nflds == 1) /* set */
+ fprintf(tc, "\t\tif (fld == 0) %s0 = fldvar;\n",
+ buf1);
+ else
+ { fprintf(tc, "\t\tswitch (fld) {\n");
+ for (i = 0; i < q->nflds; i++)
+ { fprintf(tc, "\t\tcase %d:\t%s", i, buf1);
+ fprintf(tc, "%d = fldvar; break;\n", i);
+ }
+ fprintf(tc, "\t\t}\n");
+ }
+ fprintf(tc, "\t\tbreak;\n");
+ }
+ ntimes(tc, 0, 1, R15);
+}
+
+extern void explain(int);
+
+int
+proper_enabler(Lextok *n)
+{
+ if (!n) return 1;
+ switch (n->ntyp) {
+ case NEMPTY: case FULL:
+ case NFULL: case EMPTY:
+ case LEN: case 'R':
+ case NAME:
+ has_provided = 1;
+ if (strcmp(n->sym->name, "_pid") == 0)
+ return 1;
+ return (!(n->sym->context));
+
+ case C_EXPR:
+ case CONST:
+ case TIMEOUT:
+ has_provided = 1;
+ return 1;
+
+ case ENABLED: case PC_VAL:
+ return proper_enabler(n->lft);
+
+ case '!': case UMIN: case '~':
+ return proper_enabler(n->lft);
+
+ case '/': case '*': case '-': case '+':
+ case '%': case LT: case GT: case '&': case '^':
+ case '|': case LE: case GE: case NE: case '?':
+ case EQ: case OR: case AND: case LSHIFT:
+ case RSHIFT: case 'c':
+ return proper_enabler(n->lft) && proper_enabler(n->rgt);
+ default:
+ break;
+ }
+ printf("spin: saw ");
+ explain(n->ntyp);
+ printf("\n");
+ return 0;
+}
--- /dev/null
+/***** spin: pangen4.h *****/
+
+/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* The DFA code below was written by Anuj Puri and Gerard J. Holzmann in */
+/* May 1997, and was inspired by earlier work on data compression using */
+/* sharing tree data structures and graph-encoded sets by J-Ch. Gregoire */
+/* (INRS Telecom, Quebec, Canada) and D.Zampunieris (Univ.Namur, Belgium) */
+
+/* The splay routine code included here is based on the public domain */
+/* version written by D. Sleator <sleator@cs.cmu.edu> in 1992. */
+
+static char *Dfa[] = {
+ "#ifdef MA",
+ "/*",
+ "#include <stdio.h>",
+ "#define uchar unsigned char",
+ "*/",
+ "#define ulong unsigned long",
+ "#define ushort unsigned short",
+ "",
+ "#define TWIDTH 256",
+ "#define HASH(y,n) (n)*(((long)y))",
+ "#define INRANGE(e,h) ((h>=e->From && h<=e->To)||(e->s==1 && e->S==h))",
+ "",
+ "extern char *emalloc(unsigned long); /* imported routine */",
+ "extern void dfa_init(ushort); /* 4 exported routines */",
+ "extern int dfa_member(ulong);",
+ "extern int dfa_store(uchar *);",
+ "extern void dfa_stats(void);",
+ "",
+ "typedef struct Edge {",
+ " uchar From, To; /* max range 0..255 */",
+ " uchar s, S; /* if s=1, S is singleton */",
+ " struct Vertex *Dst;",
+ " struct Edge *Nxt;",
+ "} Edge;",
+ "",
+ "typedef struct Vertex {",
+ " ulong key, num; /* key for splay tree, nr incoming edges */",
+ " uchar from[2], to[2]; /* in-node predefined edge info */",
+ " struct Vertex *dst[2];/* most nodes have 2 or more edges */",
+ " struct Edge *Succ; /* in case there are more edges */",
+ " struct Vertex *lnk, *left, *right; /* splay tree plumbing */",
+ "} Vertex;",
+ "",
+ "static Edge *free_edges;",
+ "static Vertex *free_vertices;",
+ "static Vertex **layers; /* one splay tree of nodes per layer */",
+ "static Vertex **path; /* run of word in the DFA */",
+ "static Vertex *R, *F, *NF; /* Root, Final, Not-Final */",
+ "static uchar *word, *lastword;/* string, and last string inserted */",
+ "static int dfa_depth, iv=0, nv=0, pfrst=0, Tally;",
+ "",
+ "static void insert_it(Vertex *, int); /* splay-tree code */",
+ "static void delete_it(Vertex *, int);",
+ "static Vertex *find_it(Vertex *, Vertex *, uchar, int);",
+ "",
+ "static void",
+ "recyc_edges(Edge *e)",
+ "{",
+ " if (!e) return;",
+ " recyc_edges(e->Nxt);",
+ " e->Nxt = free_edges;",
+ " free_edges = e;",
+ "}",
+ "",
+ "static Edge *",
+ "new_edge(Vertex *dst)",
+ "{ Edge *e;",
+ "",
+ " if (free_edges)",
+ " { e = free_edges;",
+ " free_edges = e->Nxt;",
+ " e->From = e->To = e->s = e->S = 0;",
+ " e->Nxt = (Edge *) 0;",
+ " } else",
+ " e = (Edge *) emalloc(sizeof(Edge));",
+ " e->Dst = dst;",
+ "",
+ " return e;",
+ "}",
+ "",
+ "static void",
+ "recyc_vertex(Vertex *v)",
+ "{",
+ " recyc_edges(v->Succ);",
+ " v->Succ = (Edge *) free_vertices;",
+ " free_vertices = v;",
+ " nr_states--;",
+ "}",
+ "",
+ "static Vertex *",
+ "new_vertex(void)",
+ "{ Vertex *v;",
+ "",
+ " if (free_vertices)",
+ " { v = free_vertices;",
+ " free_vertices = (Vertex *) v->Succ;",
+ " v->Succ = (Edge *) 0;",
+ " v->num = 0;",
+ " } else",
+ " v = (Vertex *) emalloc(sizeof(Vertex));",
+ "",
+ " nr_states++;",
+ " return v; ",
+ "}",
+ "",
+ "static Vertex *",
+ "allDelta(Vertex *v, int n)",
+ "{ Vertex *dst = new_vertex();",
+ "",
+ " v->from[0] = 0;",
+ " v->to[0] = 255;",
+ " v->dst[0] = dst;",
+ " dst->num = 256;",
+ " insert_it(v, n);",
+ " return dst;",
+ "}",
+ "",
+ "static void",
+ "insert_edge(Vertex *v, Edge *e)",
+ "{ /* put new edge first */",
+ " if (!v->dst[0])",
+ " { v->dst[0] = e->Dst;",
+ " v->from[0] = e->From;",
+ " v->to[0] = e->To;",
+ " recyc_edges(e);",
+ " return;",
+ " }",
+ " if (!v->dst[1])",
+ " { v->from[1] = v->from[0]; v->from[0] = e->From;",
+ " v->to[1] = v->to[0]; v->to[0] = e->To;",
+ " v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;",
+ " recyc_edges(e);",
+ " return;",
+ " } /* shift */",
+ " { int f = v->from[1];",
+ " int t = v->to[1];",
+ " Vertex *d = v->dst[1];",
+ " v->from[1] = v->from[0]; v->from[0] = e->From;",
+ " v->to[1] = v->to[0]; v->to[0] = e->To;",
+ " v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;",
+ " e->From = f;",
+ " e->To = t;",
+ " e->Dst = d;",
+ " }",
+ " e->Nxt = v->Succ;",
+ " v->Succ = e;",
+ "}",
+ "",
+ "static void",
+ "copyRecursive(Vertex *v, Edge *e)",
+ "{ Edge *f;",
+ " if (e->Nxt) copyRecursive(v, e->Nxt);",
+ " f = new_edge(e->Dst);",
+ " f->From = e->From;",
+ " f->To = e->To;",
+ " f->s = e->s;",
+ " f->S = e->S;",
+ " f->Nxt = v->Succ;",
+ " v->Succ = f;",
+ "}",
+ "",
+ "static void",
+ "copyEdges(Vertex *to, Vertex *from)",
+ "{ int i;",
+ " for (i = 0; i < 2; i++)",
+ " { to->from[i] = from->from[i];",
+ " to->to[i] = from->to[i];",
+ " to->dst[i] = from->dst[i];",
+ " }",
+ " if (from->Succ) copyRecursive(to, from->Succ);",
+ "}",
+ "",
+ "static Edge *",
+ "cacheDelta(Vertex *v, int h, int first)",
+ "{ static Edge *ov, tmp; int i;",
+ "",
+ " if (!first && INRANGE(ov,h))",
+ " return ov; /* intercepts about 10%% */",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i] && h >= v->from[i] && h <= v->to[i])",
+ " { tmp.From = v->from[i];",
+ " tmp.To = v->to[i];",
+ " tmp.Dst = v->dst[i];",
+ " tmp.s = tmp.S = 0;",
+ " ov = &tmp;",
+ " return ov;",
+ " }",
+ " for (ov = v->Succ; ov; ov = ov->Nxt)",
+ " if (INRANGE(ov,h)) return ov;",
+ "",
+ " Uerror(\"cannot get here, cacheDelta\");",
+ " return (Edge *) 0;",
+ "}",
+ "",
+ "static Vertex *",
+ "Delta(Vertex *v, int h) /* v->delta[h] */",
+ "{ Edge *e;",
+ "",
+ " if (v->dst[0] && h >= v->from[0] && h <= v->to[0])",
+ " return v->dst[0]; /* oldest edge */",
+ " if (v->dst[1] && h >= v->from[1] && h <= v->to[1])",
+ " return v->dst[1];",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " if (INRANGE(e,h))",
+ " return e->Dst;",
+ " Uerror(\"cannot happen Delta\");",
+ " return (Vertex *) 0;",
+ "}",
+ "",
+ "static void",
+ "numDelta(Vertex *v, int d)",
+ "{ Edge *e;",
+ " ulong cnt;",
+ " int i;",
+ "",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i])",
+ " { cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]);",
+ " if (d == 1 && cnt < v->dst[i]->num) goto bad;",
+ " v->dst[i]->num = cnt;",
+ " }",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " { cnt = e->Dst->num + d*(1 + e->To - e->From + e->s);",
+ " if (d == 1 && cnt < e->Dst->num)",
+ "bad: Uerror(\"too many incoming edges\");",
+ " e->Dst->num = cnt;",
+ " }",
+ "}",
+ "",
+ "static void",
+ "setDelta(Vertex *v, int h, Vertex *newdst) /* v->delta[h] = newdst; */",
+ "{ Edge *e, *f = (Edge *) 0, *g;",
+ " int i;",
+ "",
+ " /* remove the old entry, if there */",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i] && h >= v->from[i] && h <= v->to[i])",
+ " { if (h == v->from[i])",
+ " { if (h == v->to[i])",
+ " { v->dst[i] = (Vertex *) 0;",
+ " v->from[i] = v->to[i] = 0;",
+ " } else",
+ " v->from[i]++;",
+ " } else if (h == v->to[i])",
+ " { v->to[i]--;",
+ " } else",
+ " { g = new_edge(v->dst[i]);/* same dst */",
+ " g->From = v->from[i];",
+ " g->To = h-1; /* left half */",
+ " v->from[i] = h+1; /* right half */",
+ " insert_edge(v, g);",
+ " }",
+ " goto part2;",
+ " }",
+ " for (e = v->Succ; e; f = e, e = e->Nxt)",
+ " { if (e->s == 1 && e->S == h)",
+ " { e->s = e->S = 0;",
+ " goto rem_tst;",
+ " }",
+ " if (h >= e->From && h <= e->To)",
+ " { if (h == e->From)",
+ " { if (h == e->To)",
+ " { if (e->s)",
+ " { e->From = e->To = e->S;",
+ " e->s = 0;",
+ " break;",
+ " } else",
+ " goto rem_do;",
+ " } else",
+ " e->From++;",
+ " } else if (h == e->To)",
+ " { e->To--;",
+ " } else /* split */",
+ " { g = new_edge(e->Dst); /* same dst */",
+ " g->From = e->From;",
+ " g->To = h-1; /* g=left half */",
+ " e->From = h+1; /* e=right half */",
+ " g->Nxt = e->Nxt; /* insert g */",
+ " e->Nxt = g; /* behind e */",
+ " break; /* done */",
+ " }",
+ "",
+ "rem_tst: if (e->From > e->To)",
+ " { if (e->s == 0) {",
+ "rem_do: if (f)",
+ " f->Nxt = e->Nxt;",
+ " else",
+ " v->Succ = e->Nxt;",
+ " e->Nxt = (Edge *) 0;",
+ " recyc_edges(e);",
+ " } else",
+ " { e->From = e->To = e->S;",
+ " e->s = 0;",
+ " } }",
+ " break;",
+ " } }",
+ "part2:",
+ " /* check if newdst is already there */",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i] == newdst)",
+ " { if (h+1 == (int) v->from[i])",
+ " { v->from[i] = h;",
+ " return;",
+ " }",
+ " if (h == (int) v->to[i]+1)",
+ " { v->to[i] = h;",
+ " return;",
+ " } }",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " { if (e->Dst == newdst)",
+ " { if (h+1 == (int) e->From)",
+ " { e->From = h;",
+ " if (e->s == 1 && e->S+1 == e->From)",
+ " { e->From = e->S;",
+ " e->s = e->S = 0;",
+ " }",
+ " return;",
+ " }",
+ " if (h == (int) e->To+1)",
+ " { e->To = h;",
+ " if (e->s == 1 && e->S == e->To+1)",
+ " { e->To = e->S;",
+ " e->s = e->S = 0;",
+ " }",
+ " return;",
+ " }",
+ " if (e->s == 0)",
+ " { e->s = 1;",
+ " e->S = h;",
+ " return;",
+ " } } }",
+ " /* add as a new edge */",
+ " e = new_edge(newdst);",
+ " e->From = e->To = h;",
+ " insert_edge(v, e);",
+ "}",
+ "",
+ "static ulong",
+ "cheap_key(Vertex *v)",
+ "{ ulong vk2 = 0;",
+ "",
+ " if (v->dst[0])",
+ " { vk2 = (ulong) v->dst[0];",
+ " if ((ulong) v->dst[1] > vk2)",
+ " vk2 = (ulong) v->dst[1];",
+ " } else if (v->dst[1])",
+ " vk2 = (ulong) v->dst[1]; ",
+ " if (v->Succ)",
+ " { Edge *e;",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " if ((ulong) e->Dst > vk2)",
+ " vk2 = (ulong) e->Dst;",
+ " }",
+ " Tally = (vk2>>2)&(TWIDTH-1);",
+ " return v->key;",
+ "}",
+ "",
+ "static ulong",
+ "mk_key(Vertex *v) /* not sensitive to order */",
+ "{ ulong m = 0, vk2 = 0;",
+ " Edge *e;",
+ "",
+ " if (v->dst[0])",
+ " { m += HASH(v->dst[0], v->to[0] - v->from[0] + 1);",
+ " vk2 = (ulong) v->dst[0]; ",
+ " }",
+ " if (v->dst[1])",
+ " { m += HASH(v->dst[1], v->to[1] - v->from[1] + 1);",
+ " if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1]; ",
+ " }",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " { m += HASH(e->Dst, e->To - e->From + 1 + e->s);",
+ " if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst; ",
+ " }",
+ " Tally = (vk2>>2)&(TWIDTH-1);",
+ " return m;",
+ "}",
+ "",
+ "static ulong",
+ "mk_special(int sigma, Vertex *n, Vertex *v)",
+ "{ ulong m = 0, vk2 = 0;",
+ " Edge *f;",
+ " int i;",
+ "",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i])",
+ " { if (sigma >= v->from[i] && sigma <= v->to[i])",
+ " { m += HASH(v->dst[i], v->to[i]-v->from[i]);",
+ " if ((ulong) v->dst[i] > vk2",
+ " && v->to[i] > v->from[i])",
+ " vk2 = (ulong) v->dst[i]; ",
+ " } else",
+ " { m += HASH(v->dst[i], v->to[i]-v->from[i]+1);",
+ " if ((ulong) v->dst[i] > vk2)",
+ " vk2 = (ulong) v->dst[i]; ",
+ " } }",
+ " for (f = v->Succ; f; f = f->Nxt)",
+ " { if (sigma >= f->From && sigma <= f->To)",
+ " { m += HASH(f->Dst, f->To - f->From + f->s);",
+ " if ((ulong) f->Dst > vk2",
+ " && f->To - f->From + f->s > 0)",
+ " vk2 = (ulong) f->Dst; ",
+ " } else if (f->s == 1 && sigma == f->S)",
+ " { m += HASH(f->Dst, f->To - f->From + 1);",
+ " if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; ",
+ " } else",
+ " { m += HASH(f->Dst, f->To - f->From + 1 + f->s);",
+ " if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; ",
+ " } }",
+ "",
+ " if ((ulong) n > vk2) vk2 = (ulong) n; ",
+ " Tally = (vk2>>2)&(TWIDTH-1);",
+ " m += HASH(n, 1);",
+ " return m;",
+ "}",
+ "",
+ "void ",
+ "dfa_init(ushort nr_layers)",
+ "{ int i; Vertex *r, *t;",
+ "",
+ " dfa_depth = nr_layers; /* one byte per layer */",
+ " path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));",
+ " layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));",
+ " lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));",
+ " lastword[dfa_depth] = lastword[0] = 255;",
+ " path[0] = R = new_vertex(); F = new_vertex();",
+ "",
+ " for (i = 1, r = R; i < dfa_depth; i++, r = t)",
+ " t = allDelta(r, i-1);",
+ " NF = allDelta(r, i-1);",
+ "}",
+ "",
+ "#if 0",
+ "static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; }",
+ "#endif",
+ "",
+ "double",
+ "tree_stats(Vertex *t)",
+ "{ Edge *e; double cnt=0.0;",
+ " if (!t) return 0;",
+ " if (!t->key) return 0;",
+ " t->key = 0; /* precaution */",
+ " if (t->dst[0]) cnt++;",
+ " if (t->dst[1]) cnt++;",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " cnt++;",
+ " cnt += tree_stats(t->lnk);",
+ " cnt += tree_stats(t->left);",
+ " cnt += tree_stats(t->right);",
+ " return cnt;",
+ "}",
+ "",
+ "void",
+ "dfa_stats(void)",
+ "{ int i, j; double cnt = 0.0;",
+ " for (j = 0; j < TWIDTH; j++)",
+ " for (i = 0; i < dfa_depth+1; i++)",
+ " cnt += tree_stats(layers[i*TWIDTH+j]);",
+ " printf(\"Minimized Automaton:\t%%6d nodes and %%6g edges\\n\",",
+ " nr_states, cnt);",
+ "}",
+ "",
+ "int",
+ "dfa_member(ulong n)",
+ "{ Vertex **p, **q;",
+ " uchar *w = &word[n];",
+ " int i;",
+ "",
+ " p = &path[n]; q = (p+1);",
+ " for (i = n; i < dfa_depth; i++)",
+ " *q++ = Delta(*p++, *w++);",
+ " return (*p == F);",
+ "}",
+ "",
+ "int",
+ "dfa_store(uchar *sv)",
+ "{ Vertex **p, **q, *s, *y, *old, *new = F;",
+ " uchar *w, *u = lastword;",
+ " int i, j, k;",
+ "",
+ " w = word = sv;",
+ " while (*w++ == *u++) /* find first byte that differs */",
+ " ;",
+ " pfrst = (int) (u - lastword) - 1;",
+ " memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst);",
+ " if (pfrst > iv) pfrst = iv;",
+ " if (pfrst > nv) pfrst = nv;",
+ "/* phase1: */",
+ " p = &path[pfrst]; q = (p+1); w = &word[pfrst];",
+ " for (i = pfrst; i < dfa_depth; i++)",
+ " *q++ = Delta(*p++, *w++); /* (*p)->delta[*w++]; */",
+ "",
+ " if (*p == F) return 1; /* it's already there */",
+ "/* phase2: */",
+ " iv = dfa_depth;",
+ " do { iv--;",
+ " old = new;",
+ " new = find_it(path[iv], old, word[iv], iv);",
+ " } while (new && iv > 0);",
+ "",
+ "/* phase3: */",
+ " nv = k = 0; s = path[0];",
+ " for (j = 1; j <= iv; ++j) ",
+ " if (path[j]->num > 1)",
+ " { y = new_vertex();",
+ " copyEdges(y, path[j]);",
+ " insert_it(y, j);",
+ " numDelta(y, 1);",
+ " delete_it(s, j-1);",
+ " setDelta(s, word[j-1], y);",
+ " insert_it(s, j-1);",
+ " y->num = 1; /* initial value 1 */",
+ " s = y;",
+ " path[j]->num--; /* only 1 moved from j to y */",
+ " k = 1;",
+ " } else",
+ " { s = path[j];",
+ " if (!k) nv = j;",
+ " }",
+ " y = Delta(s, word[iv]);",
+ " y->num--;",
+ " delete_it(s, iv); ",
+ " setDelta(s, word[iv], old);",
+ " insert_it(s, iv); ",
+ " old->num++;",
+ "",
+ " for (j = iv+1; j < dfa_depth; j++)",
+ " if (path[j]->num == 0)",
+ " { numDelta(path[j], -1);",
+ " delete_it(path[j], j);",
+ " recyc_vertex(path[j]);",
+ " } else",
+ " break;",
+ " return 0;",
+ "}",
+ "",
+ "static Vertex *",
+ "splay(ulong i, Vertex *t)",
+ "{ Vertex N, *l, *r, *y;",
+ "",
+ " if (!t) return t;",
+ " N.left = N.right = (Vertex *) 0;",
+ " l = r = &N;",
+ " for (;;)",
+ " { if (i < t->key)",
+ " { if (!t->left) break;",
+ " if (i < t->left->key)",
+ " { y = t->left;",
+ " t->left = y->right;",
+ " y->right = t;",
+ " t = y;",
+ " if (!t->left) break;",
+ " }",
+ " r->left = t;",
+ " r = t;",
+ " t = t->left;",
+ " } else if (i > t->key)",
+ " { if (!t->right) break;",
+ " if (i > t->right->key)",
+ " { y = t->right;",
+ " t->right = y->left;",
+ " y->left = t;",
+ " t = y;",
+ " if (!t->right) break;",
+ " }",
+ " l->right = t;",
+ " l = t;",
+ " t = t->right;",
+ " } else",
+ " break;",
+ " }",
+ " l->right = t->left;",
+ " r->left = t->right;",
+ " t->left = N.right;",
+ " t->right = N.left;",
+ " return t;",
+ "}",
+ "",
+ "static void",
+ "insert_it(Vertex *v, int L)",
+ "{ Vertex *new, *t;",
+ " ulong i; int nr;",
+ "",
+ " i = mk_key(v);",
+ " nr = ((L*TWIDTH)+Tally);",
+ " t = layers[nr];",
+ "",
+ " v->key = i; ",
+ " if (!t)",
+ " { layers[nr] = v;",
+ " return;",
+ " }",
+ " t = splay(i, t);",
+ " if (i < t->key)",
+ " { new = v;",
+ " new->left = t->left;",
+ " new->right = t;",
+ " t->left = (Vertex *) 0;",
+ " } else if (i > t->key)",
+ " { new = v;",
+ " new->right = t->right;",
+ " new->left = t;",
+ " t->right = (Vertex *) 0;",
+ " } else /* it's already there */",
+ " { v->lnk = t->lnk; /* put in linked list off v */",
+ " t->lnk = v;",
+ " new = t;",
+ " }",
+ " layers[nr] = new;",
+ "}",
+ "",
+ "static int",
+ "checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma)",
+ "{ Edge *g, *f;",
+ " int i, k, j = 1;",
+ "",
+ " for (k = 0; k < 2; k++)",
+ " if (h->dst[k])",
+ " { if (sigma >= h->from[k] && sigma <= h->to[k])",
+ " { if (h->dst[k] != n) goto no_match;",
+ " }",
+ " for (i = h->from[k]; i <= h->to[k]; i++)",
+ " { if (i == sigma) continue;",
+ " g = cacheDelta(v, i, j); j = 0;",
+ " if (h->dst[k] != g->Dst)",
+ " goto no_match;",
+ " if (g->s == 0 || g->S != i)",
+ " i = g->To;",
+ " } }",
+ " for (f = h->Succ; f; f = f->Nxt)",
+ " { if (INRANGE(f,sigma))",
+ " { if (f->Dst != n) goto no_match;",
+ " }",
+ " for (i = f->From; i <= f->To; i++)",
+ " { if (i == sigma) continue;",
+ " g = cacheDelta(v, i, j); j = 0;",
+ " if (f->Dst != g->Dst)",
+ " goto no_match;",
+ " if (g->s == 1 && i == g->S)",
+ " continue;",
+ " i = g->To;",
+ " }",
+ " if (f->s && f->S != sigma)",
+ " { g = cacheDelta(v, f->S, 1);",
+ " if (f->Dst != g->Dst)",
+ " goto no_match;",
+ " }",
+ " }",
+ " if (h->Succ || h->dst[0] || h->dst[1]) return 1;",
+ "no_match:",
+ " return 0;",
+ "}",
+ "",
+ "static Vertex *",
+ "find_it(Vertex *v, Vertex *n, uchar sigma, int L)",
+ "{ Vertex *z, *t;",
+ " ulong i; int nr;",
+ "",
+ " i = mk_special(sigma,n,v);",
+ " nr = ((L*TWIDTH)+Tally);",
+ " t = layers[nr];",
+ "",
+ " if (!t) return (Vertex *) 0;",
+ " layers[nr] = t = splay(i, t);",
+ " if (i == t->key)",
+ " for (z = t; z; z = z->lnk)",
+ " if (checkit(z, v, n, sigma))",
+ " return z;",
+ "",
+ " return (Vertex *) 0;",
+ "}",
+ "",
+ "static void",
+ "delete_it(Vertex *v, int L)",
+ "{ Vertex *x, *t;",
+ " ulong i; int nr;",
+ "",
+ " i = cheap_key(v);",
+ " nr = ((L*TWIDTH)+Tally);",
+ " t = layers[nr];",
+ " if (!t) return;",
+ "",
+ " t = splay(i, t);",
+ " if (i == t->key)",
+ " { Vertex *z, *y = (Vertex *) 0;",
+ " for (z = t; z && z != v; y = z, z = z->lnk)",
+ " ;",
+ " if (z != v) goto bad;",
+ " if (y)",
+ " { y->lnk = z->lnk;",
+ " z->lnk = (Vertex *) 0;",
+ " layers[nr] = t;",
+ " return;",
+ " } else if (z->lnk) /* z == t == v */",
+ " { y = z->lnk;",
+ " y->left = t->left;",
+ " y->right = t->right;",
+ " t->left = t->right = t->lnk = (Vertex *) 0;",
+ " layers[nr] = y;",
+ " return;",
+ " }",
+ " /* delete the node itself */",
+ " if (!t->left)",
+ " { x = t->right;",
+ " } else",
+ " { x = splay(i, t->left);",
+ " x->right = t->right;",
+ " }",
+ " t->left = t->right = t->lnk = (Vertex *) 0;",
+ " layers[nr] = x;",
+ " return;",
+ " }",
+ "bad: Uerror(\"cannot happen delete\");",
+ "}",
+ "#endif", /* MA */
+ 0,
+};
--- /dev/null
+/***** spin: pangen5.c *****/
+
+/* Copyright (c) 1999-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+typedef struct BuildStack {
+ FSM_trans *t;
+ struct BuildStack *nxt;
+} BuildStack;
+
+extern ProcList *rdy;
+extern int verbose, eventmapnr, claimnr, rvopt, export_ast, u_sync;
+extern Element *Al_El;
+
+static FSM_state *fsm_free;
+static FSM_trans *trans_free;
+static BuildStack *bs, *bf;
+static int max_st_id;
+static int cur_st_id;
+int o_max;
+FSM_state *fsm;
+FSM_state **fsm_tbl;
+FSM_use *use_free;
+
+static void ana_seq(Sequence *);
+static void ana_stmnt(FSM_trans *, Lextok *, int);
+
+extern void AST_slice(void);
+extern void AST_store(ProcList *, int);
+extern int has_global(Lextok *);
+extern void exit(int);
+
+static void
+fsm_table(void)
+{ FSM_state *f;
+ max_st_id += 2;
+ /* fprintf(stderr, "omax %d, max=%d\n", o_max, max_st_id); */
+ if (o_max < max_st_id)
+ { o_max = max_st_id;
+ fsm_tbl = (FSM_state **) emalloc(max_st_id * sizeof(FSM_state *));
+ } else
+ memset((char *)fsm_tbl, 0, max_st_id * sizeof(FSM_state *));
+ cur_st_id = max_st_id;
+ max_st_id = 0;
+
+ for (f = fsm; f; f = f->nxt)
+ fsm_tbl[f->from] = f;
+}
+
+static int
+FSM_DFS(int from, FSM_use *u)
+{ FSM_state *f;
+ FSM_trans *t;
+ FSM_use *v;
+ int n;
+
+ if (from == 0)
+ return 1;
+
+ f = fsm_tbl[from];
+
+ if (!f)
+ { printf("cannot find state %d\n", from);
+ fatal("fsm_dfs: cannot happen\n", (char *) 0);
+ }
+
+ if (f->seen)
+ return 1;
+ f->seen = 1;
+
+ for (t = f->t; t; t = t->nxt)
+ {
+ for (n = 0; n < 2; n++)
+ for (v = t->Val[n]; v; v = v->nxt)
+ if (u->var == v->var)
+ return n; /* a read or write */
+
+ if (!FSM_DFS(t->to, u))
+ return 0;
+ }
+ return 1;
+}
+
+static void
+new_dfs(void)
+{ int i;
+
+ for (i = 0; i < cur_st_id; i++)
+ if (fsm_tbl[i])
+ fsm_tbl[i]->seen = 0;
+}
+
+static int
+good_dead(Element *e, FSM_use *u)
+{
+ switch (u->special) {
+ case 2: /* ok if it's a receive */
+ if (e->n->ntyp == ASGN
+ && e->n->rgt->ntyp == CONST
+ && e->n->rgt->val == 0)
+ return 0;
+ break;
+ case 1: /* must be able to use oval */
+ if (e->n->ntyp != 'c'
+ && e->n->ntyp != 'r')
+ return 0; /* can't really happen */
+ break;
+ }
+ return 1;
+}
+
+#if 0
+static int howdeep = 0;
+#endif
+
+static int
+eligible(FSM_trans *v)
+{ Element *el = ZE;
+ Lextok *lt = ZN;
+
+ if (v) el = v->step;
+ if (el) lt = v->step->n;
+
+ if (!lt /* dead end */
+ || v->nxt /* has alternatives */
+ || el->esc /* has an escape */
+ || (el->status&CHECK2) /* remotely referenced */
+ || lt->ntyp == ATOMIC
+ || lt->ntyp == NON_ATOMIC /* used for inlines -- should be able to handle this */
+ || lt->ntyp == IF
+ || lt->ntyp == C_CODE
+ || lt->ntyp == C_EXPR
+ || has_lab(el, 0) /* any label at all */
+
+ || lt->ntyp == DO
+ || lt->ntyp == UNLESS
+ || lt->ntyp == D_STEP
+ || lt->ntyp == ELSE
+ || lt->ntyp == '@'
+ || lt->ntyp == 'c'
+ || lt->ntyp == 'r'
+ || lt->ntyp == 's')
+ return 0;
+
+ if (!(el->status&(2|4))) /* not atomic */
+ { int unsafe = (el->status&I_GLOB)?1:has_global(el->n);
+ if (unsafe)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+canfill_in(FSM_trans *v)
+{ Element *el = v->step;
+ Lextok *lt = v->step->n;
+
+ if (!lt /* dead end */
+ || v->nxt /* has alternatives */
+ || el->esc /* has an escape */
+ || (el->status&CHECK2)) /* remotely referenced */
+ return 0;
+
+ if (!(el->status&(2|4)) /* not atomic */
+ && ((el->status&I_GLOB)
+ || has_global(el->n))) /* and not safe */
+ return 0;
+
+ return 1;
+}
+
+static int
+pushbuild(FSM_trans *v)
+{ BuildStack *b;
+
+ for (b = bs; b; b = b->nxt)
+ if (b->t == v)
+ return 0;
+ if (bf)
+ { b = bf;
+ bf = bf->nxt;
+ } else
+ b = (BuildStack *) emalloc(sizeof(BuildStack));
+ b->t = v;
+ b->nxt = bs;
+ bs = b;
+ return 1;
+}
+
+static void
+popbuild(void)
+{ BuildStack *f;
+ if (!bs)
+ fatal("cannot happen, popbuild", (char *) 0);
+ f = bs;
+ bs = bs->nxt;
+ f->nxt = bf;
+ bf = f; /* freelist */
+}
+
+static int
+build_step(FSM_trans *v)
+{ FSM_state *f;
+ Element *el;
+#if 0
+ Lextok *lt = ZN;
+#endif
+ int st;
+ int r;
+
+ if (!v) return -1;
+
+ el = v->step;
+ st = v->to;
+
+ if (!el) return -1;
+
+ if (v->step->merge)
+ return v->step->merge; /* already done */
+
+ if (!eligible(v)) /* non-blocking */
+ return -1;
+
+ if (!pushbuild(v)) /* cycle detected */
+ return -1; /* break cycle */
+
+ f = fsm_tbl[st];
+#if 0
+ lt = v->step->n;
+ if (verbose&32)
+ { if (++howdeep == 1)
+ printf("spin: %s, line %3d, merge:\n",
+ lt->fn->name,
+ lt->ln);
+ printf("\t[%d] <seqno %d>\t", howdeep, el->seqno);
+ comment(stdout, lt, 0);
+ printf(";\n");
+ }
+#endif
+ r = build_step(f->t);
+ v->step->merge = (r == -1) ? st : r;
+#if 0
+ if (verbose&32)
+ { printf(" merge value: %d (st=%d,r=%d, line %d)\n",
+ v->step->merge, st, r, el->n->ln);
+ howdeep--;
+ }
+#endif
+ popbuild();
+
+ return v->step->merge;
+}
+
+static void
+FSM_MERGER(/* char *pname */ void) /* find candidates for safely merging steps */
+{ FSM_state *f, *g;
+ FSM_trans *t;
+ Lextok *lt;
+
+ for (f = fsm; f; f = f->nxt) /* all states */
+ for (t = f->t; t; t = t->nxt) /* all edges */
+ { if (!t->step) continue; /* happens with 'unless' */
+
+ t->step->merge_in = f->in; /* ?? */
+
+ if (t->step->merge)
+ continue;
+ lt = t->step->n;
+
+ if (lt->ntyp == 'c'
+ || lt->ntyp == 'r'
+ || lt->ntyp == 's') /* blocking stmnts */
+ continue; /* handled in 2nd scan */
+
+ if (!eligible(t))
+ continue;
+
+ g = fsm_tbl[t->to];
+ if (!g || !eligible(g->t))
+ {
+#define SINGLES
+#ifdef SINGLES
+ t->step->merge_single = t->to;
+#if 0
+ if ((verbose&32))
+ { printf("spin: %s, line %3d, merge_single:\n\t<seqno %d>\t",
+ t->step->n->fn->name,
+ t->step->n->ln,
+ t->step->seqno);
+ comment(stdout, t->step->n, 0);
+ printf(";\n");
+ }
+#endif
+#endif
+ /* t is an isolated eligible step:
+ *
+ * a merge_start can connect to a proper
+ * merge chain or to a merge_single
+ * a merge chain can be preceded by
+ * a merge_start, but not by a merge_single
+ */
+
+ continue;
+ }
+
+ (void) build_step(t);
+ }
+
+ /* 2nd scan -- find possible merge_starts */
+
+ for (f = fsm; f; f = f->nxt) /* all states */
+ for (t = f->t; t; t = t->nxt) /* all edges */
+ { if (!t->step || t->step->merge)
+ continue;
+
+ lt = t->step->n;
+#if 0
+ 4.1.3:
+ an rv send operation inside an atomic, *loses* atomicity
+ when executed
+ and should therefore never be merged with a subsequent
+ statement within the atomic sequence
+ the same is not true for non-rv send operations
+#endif
+
+ if (lt->ntyp == 'c' /* potentially blocking stmnts */
+ || lt->ntyp == 'r'
+ || (lt->ntyp == 's' && u_sync == 0)) /* added !u_sync in 4.1.3 */
+ { if (!canfill_in(t)) /* atomic, non-global, etc. */
+ continue;
+
+ g = fsm_tbl[t->to];
+ if (!g || !g->t || !g->t->step)
+ continue;
+ if (g->t->step->merge)
+ t->step->merge_start = g->t->step->merge;
+#ifdef SINGLES
+ else if (g->t->step->merge_single)
+ t->step->merge_start = g->t->step->merge_single;
+#endif
+#if 0
+ if ((verbose&32)
+ && t->step->merge_start)
+ { printf("spin: %s, line %3d, merge_START:\n\t<seqno %d>\t",
+ lt->fn->name,
+ lt->ln,
+ t->step->seqno);
+ comment(stdout, lt, 0);
+ printf(";\n");
+ }
+#endif
+ }
+ }
+}
+
+static void
+FSM_ANA(void)
+{ FSM_state *f;
+ FSM_trans *t;
+ FSM_use *u, *v, *w;
+ int n;
+
+ for (f = fsm; f; f = f->nxt) /* all states */
+ for (t = f->t; t; t = t->nxt) /* all edges */
+ for (n = 0; n < 2; n++) /* reads and writes */
+ for (u = t->Val[n]; u; u = u->nxt)
+ { if (!u->var->context /* global */
+ || u->var->type == CHAN
+ || u->var->type == STRUCT)
+ continue;
+ new_dfs();
+ if (FSM_DFS(t->to, u)) /* cannot hit read before hitting write */
+ u->special = n+1; /* means, reset to 0 after use */
+ }
+
+ if (!export_ast)
+ for (f = fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ for (n = 0; n < 2; n++)
+ for (u = t->Val[n], w = (FSM_use *) 0; u; )
+ { if (u->special)
+ { v = u->nxt;
+ if (!w) /* remove from list */
+ t->Val[n] = v;
+ else
+ w->nxt = v;
+#if q
+ if (verbose&32)
+ { printf("%s : %3d: %d -> %d \t",
+ t->step->n->fn->name,
+ t->step->n->ln,
+ f->from,
+ t->to);
+ comment(stdout, t->step->n, 0);
+ printf("\t%c%d: %s\n", n==0?'R':'L',
+ u->special, u->var->name);
+ }
+#endif
+ if (good_dead(t->step, u))
+ { u->nxt = t->step->dead; /* insert into dead */
+ t->step->dead = u;
+ }
+ u = v;
+ } else
+ { w = u;
+ u = u->nxt;
+ } }
+}
+
+void
+rel_use(FSM_use *u)
+{
+ if (!u) return;
+ rel_use(u->nxt);
+ u->var = (Symbol *) 0;
+ u->special = 0;
+ u->nxt = use_free;
+ use_free = u;
+}
+
+static void
+rel_trans(FSM_trans *t)
+{
+ if (!t) return;
+ rel_trans(t->nxt);
+ rel_use(t->Val[0]);
+ rel_use(t->Val[1]);
+ t->Val[0] = t->Val[1] = (FSM_use *) 0;
+ t->nxt = trans_free;
+ trans_free = t;
+}
+
+static void
+rel_state(FSM_state *f)
+{
+ if (!f) return;
+ rel_state(f->nxt);
+ rel_trans(f->t);
+ f->t = (FSM_trans *) 0;
+ f->nxt = fsm_free;
+ fsm_free = f;
+}
+
+static void
+FSM_DEL(void)
+{
+ rel_state(fsm);
+ fsm = (FSM_state *) 0;
+}
+
+static FSM_state *
+mkstate(int s)
+{ FSM_state *f;
+
+ /* fsm_tbl isn't allocated yet */
+ for (f = fsm; f; f = f->nxt)
+ if (f->from == s)
+ break;
+ if (!f)
+ { if (fsm_free)
+ { f = fsm_free;
+ memset(f, 0, sizeof(FSM_state));
+ fsm_free = fsm_free->nxt;
+ } else
+ f = (FSM_state *) emalloc(sizeof(FSM_state));
+ f->from = s;
+ f->t = (FSM_trans *) 0;
+ f->nxt = fsm;
+ fsm = f;
+ if (s > max_st_id)
+ max_st_id = s;
+ }
+ return f;
+}
+
+static FSM_trans *
+get_trans(int to)
+{ FSM_trans *t;
+
+ if (trans_free)
+ { t = trans_free;
+ memset(t, 0, sizeof(FSM_trans));
+ trans_free = trans_free->nxt;
+ } else
+ t = (FSM_trans *) emalloc(sizeof(FSM_trans));
+
+ t->to = to;
+ return t;
+}
+
+static void
+FSM_EDGE(int from, int to, Element *e)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ f = mkstate(from); /* find it or else make it */
+ t = get_trans(to);
+
+ t->step = e;
+ t->nxt = f->t;
+ f->t = t;
+
+ f = mkstate(to);
+ f->in++;
+
+ if (export_ast)
+ { t = get_trans(from);
+ t->step = e;
+ t->nxt = f->p; /* from is a predecessor of to */
+ f->p = t;
+ }
+
+ if (t->step)
+ ana_stmnt(t, t->step->n, 0);
+}
+
+#define LVAL 1
+#define RVAL 0
+
+static void
+ana_var(FSM_trans *t, Lextok *now, int usage)
+{ FSM_use *u, *v;
+
+ if (!t || !now || !now->sym)
+ return;
+
+ if (now->sym->name[0] == '_'
+ && (strcmp(now->sym->name, "_") == 0
+ || strcmp(now->sym->name, "_pid") == 0
+ || strcmp(now->sym->name, "_last") == 0))
+ return;
+
+ v = t->Val[usage];
+ for (u = v; u; u = u->nxt)
+ if (u->var == now->sym)
+ return; /* it's already there */
+
+ if (!now->lft)
+ { /* not for array vars -- it's hard to tell statically
+ if the index would, at runtime, evaluate to the
+ same values at lval and rval references
+ */
+ if (use_free)
+ { u = use_free;
+ use_free = use_free->nxt;
+ } else
+ u = (FSM_use *) emalloc(sizeof(FSM_use));
+
+ u->var = now->sym;
+ u->nxt = t->Val[usage];
+ t->Val[usage] = u;
+ } else
+ ana_stmnt(t, now->lft, RVAL); /* index */
+
+ if (now->sym->type == STRUCT
+ && now->rgt
+ && now->rgt->lft)
+ ana_var(t, now->rgt->lft, usage);
+}
+
+static void
+ana_stmnt(FSM_trans *t, Lextok *now, int usage)
+{ Lextok *v;
+
+ if (!t || !now) return;
+
+ switch (now->ntyp) {
+ case '.':
+ case BREAK:
+ case GOTO:
+ case CONST:
+ case TIMEOUT:
+ case NONPROGRESS:
+ case ELSE:
+ case '@':
+ case 'q':
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ case C_CODE:
+ case C_EXPR:
+ break;
+
+ case '!':
+ case UMIN:
+ case '~':
+ case ENABLED:
+ case PC_VAL:
+ case LEN:
+ case FULL:
+ case EMPTY:
+ case NFULL:
+ case NEMPTY:
+ case ASSERT:
+ case 'c':
+ ana_stmnt(t, now->lft, RVAL);
+ break;
+
+ case '/':
+ case '*':
+ case '-':
+ case '+':
+ case '%':
+ case '&':
+ case '^':
+ case '|':
+ case LT:
+ case GT:
+ case LE:
+ case GE:
+ case NE:
+ case EQ:
+ case OR:
+ case AND:
+ case LSHIFT:
+ case RSHIFT:
+ ana_stmnt(t, now->lft, RVAL);
+ ana_stmnt(t, now->rgt, RVAL);
+ break;
+
+ case ASGN:
+ ana_stmnt(t, now->lft, LVAL);
+ ana_stmnt(t, now->rgt, RVAL);
+ break;
+
+ case PRINT:
+ case RUN:
+ for (v = now->lft; v; v = v->rgt)
+ ana_stmnt(t, v->lft, RVAL);
+ break;
+
+ case PRINTM:
+ if (now->lft && !now->lft->ismtyp)
+ ana_stmnt(t, now->lft, RVAL);
+ break;
+
+ case 's':
+ ana_stmnt(t, now->lft, RVAL);
+ for (v = now->rgt; v; v = v->rgt)
+ ana_stmnt(t, v->lft, RVAL);
+ break;
+
+ case 'R':
+ case 'r':
+ ana_stmnt(t, now->lft, RVAL);
+ for (v = now->rgt; v; v = v->rgt)
+ { if (v->lft->ntyp == EVAL)
+ ana_stmnt(t, v->lft->lft, RVAL);
+ else
+ if (v->lft->ntyp != CONST
+ && now->ntyp != 'R') /* was v->lft->ntyp */
+ ana_stmnt(t, v->lft, LVAL);
+ }
+ break;
+
+ case '?':
+ ana_stmnt(t, now->lft, RVAL);
+ if (now->rgt)
+ { ana_stmnt(t, now->rgt->lft, RVAL);
+ ana_stmnt(t, now->rgt->rgt, RVAL);
+ }
+ break;
+
+ case NAME:
+ ana_var(t, now, usage);
+ break;
+
+ case 'p': /* remote ref */
+ ana_stmnt(t, now->lft->lft, RVAL); /* process id */
+ ana_var(t, now, RVAL);
+ ana_var(t, now->rgt, RVAL);
+ break;
+
+ default:
+ printf("spin: bad node type %d line %d (ana_stmnt)\n", now->ntyp, now->ln);
+ fatal("aborting", (char *) 0);
+ }
+}
+
+void
+ana_src(int dataflow, int merger) /* called from main.c and guided.c */
+{ ProcList *p;
+ Element *e;
+#if 0
+ int counter = 1;
+#endif
+ for (p = rdy; p; p = p->nxt)
+ { if (p->tn == eventmapnr
+ || p->tn == claimnr)
+ continue;
+
+ ana_seq(p->s);
+ fsm_table();
+
+ e = p->s->frst;
+#if 0
+ if (dataflow || merger)
+ { printf("spin: %d, optimizing '%s'",
+ counter++, p->n->name);
+ fflush(stdout);
+ }
+#endif
+ if (dataflow)
+ { FSM_ANA();
+ }
+ if (merger)
+ { FSM_MERGER(/* p->n->name */);
+ huntele(e, e->status, -1)->merge_in = 1; /* start-state */
+#if 0
+ printf("\n");
+#endif
+ }
+ if (export_ast)
+ AST_store(p, huntele(e, e->status, -1)->seqno);
+
+ FSM_DEL();
+ }
+ for (e = Al_El; e; e = e->Nxt)
+ {
+ if (!(e->status&DONE) && (verbose&32))
+ { printf("unreachable code: ");
+ printf("%s, line %3d: ",
+ e->n->fn->name, e->n->ln);
+ comment(stdout, e->n, 0);
+ printf("\n");
+ }
+ e->status &= ~DONE;
+ }
+ if (export_ast)
+ { AST_slice();
+ exit(0);
+ }
+}
+
+void
+spit_recvs(FILE *f1, FILE *f2) /* called from pangen2.c */
+{ Element *e;
+ Sequence *s;
+ extern int Unique;
+
+ fprintf(f1, "unsigned char Is_Recv[%d];\n", Unique);
+
+ fprintf(f2, "void\nset_recvs(void)\n{\n");
+ for (e = Al_El; e; e = e->Nxt)
+ { if (!e->n) continue;
+
+ switch (e->n->ntyp) {
+ case 'r':
+markit: fprintf(f2, "\tIs_Recv[%d] = 1;\n", e->Seqno);
+ break;
+ case D_STEP:
+ s = e->n->sl->this;
+ switch (s->frst->n->ntyp) {
+ case DO:
+ fatal("unexpected: do at start of d_step", (char *) 0);
+ case IF: /* conservative: fall through */
+ case 'r': goto markit;
+ }
+ break;
+ }
+ }
+ fprintf(f2, "}\n");
+
+ if (rvopt)
+ {
+ fprintf(f2, "int\nno_recvs(int me)\n{\n");
+ fprintf(f2, " int h; uchar ot; short tt;\n");
+ fprintf(f2, " Trans *t;\n");
+ fprintf(f2, " for (h = BASE; h < (int) now._nr_pr; h++)\n");
+ fprintf(f2, " { if (h == me) continue;\n");
+ fprintf(f2, " tt = (short) ((P0 *)pptr(h))->_p;\n");
+ fprintf(f2, " ot = (uchar) ((P0 *)pptr(h))->_t;\n");
+ fprintf(f2, " for (t = trans[ot][tt]; t; t = t->nxt)\n");
+ fprintf(f2, " if (Is_Recv[t->t_id]) return 0;\n");
+ fprintf(f2, " }\n");
+ fprintf(f2, " return 1;\n");
+ fprintf(f2, "}\n");
+ }
+}
+
+static void
+ana_seq(Sequence *s)
+{ SeqList *h;
+ Sequence *t;
+ Element *e, *g;
+ int From, To;
+
+ for (e = s->frst; e; e = e->nxt)
+ { if (e->status & DONE)
+ goto checklast;
+
+ e->status |= DONE;
+
+ From = e->seqno;
+
+ if (e->n->ntyp == UNLESS)
+ ana_seq(e->sub->this);
+ else if (e->sub)
+ { for (h = e->sub; h; h = h->nxt)
+ { g = huntstart(h->this->frst);
+ To = g->seqno;
+
+ if (g->n->ntyp != 'c'
+ || g->n->lft->ntyp != CONST
+ || g->n->lft->val != 0
+ || g->esc)
+ FSM_EDGE(From, To, e);
+ /* else it's a dead link */
+ }
+ for (h = e->sub; h; h = h->nxt)
+ ana_seq(h->this);
+ } else if (e->n->ntyp == ATOMIC
+ || e->n->ntyp == D_STEP
+ || e->n->ntyp == NON_ATOMIC)
+ {
+ t = e->n->sl->this;
+ g = huntstart(t->frst);
+ t->last->nxt = e->nxt;
+ To = g->seqno;
+ FSM_EDGE(From, To, e);
+
+ ana_seq(t);
+ } else
+ { if (e->n->ntyp == GOTO)
+ { g = get_lab(e->n, 1);
+ g = huntele(g, e->status, -1);
+ To = g->seqno;
+ } else if (e->nxt)
+ { g = huntele(e->nxt, e->status, -1);
+ To = g->seqno;
+ } else
+ To = 0;
+
+ FSM_EDGE(From, To, e);
+
+ if (e->esc
+ && e->n->ntyp != GOTO
+ && e->n->ntyp != '.')
+ for (h = e->esc; h; h = h->nxt)
+ { g = huntstart(h->this->frst);
+ To = g->seqno;
+ FSM_EDGE(From, To, ZE);
+ ana_seq(h->this);
+ }
+ }
+
+checklast: if (e == s->last)
+ break;
+ }
+}
--- /dev/null
+/***** spin: pangen5.h *****/
+
+/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+static char *Xpt[] = {
+ "#if defined(MA) && (defined(W_XPT) || defined(R_XPT))",
+ "static Vertex **temptree;",
+ "static char wbuf[4096];",
+ "static int WCNT = 4096, wcnt=0;",
+ "static uchar stacker[MA+1];",
+ "static ulong stackcnt = 0;",
+ "extern double nstates, nlinks, truncs, truncs2;",
+ "",
+ "static void",
+ "xwrite(int fd, char *b, int n)",
+ "{",
+ " if (wcnt+n >= 4096)",
+ " { write(fd, wbuf, wcnt);",
+ " wcnt = 0;",
+ " }",
+ " memcpy(&wbuf[wcnt], b, n);",
+ " wcnt += n;",
+ "}",
+ "",
+ "static void",
+ "wclose(fd)",
+ "{",
+ " if (wcnt > 0)",
+ " write(fd, wbuf, wcnt);",
+ " wcnt = 0;",
+ " close(fd);",
+ "}",
+ "",
+ "static void",
+ "w_vertex(int fd, Vertex *v)",
+ "{ char t[3]; int i; Edge *e;",
+ "",
+ " xwrite(fd, (char *) &v, sizeof(Vertex *));",
+ " t[0] = 0;",
+ " for (i = 0; i < 2; i++)",
+ " if (v->dst[i])",
+ " { t[1] = v->from[i], t[2] = v->to[i];",
+ " xwrite(fd, t, 3);",
+ " xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *));",
+ " }",
+ " for (e = v->Succ; e; e = e->Nxt)",
+ " { t[1] = e->From, t[2] = e->To;",
+ " xwrite(fd, t, 3);",
+ " xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));",
+ "",
+ " if (e->s)",
+ " { t[1] = t[2] = e->S;",
+ " xwrite(fd, t, 3);",
+ " xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));",
+ " } }",
+ "}",
+ "",
+ "static void",
+ "w_layer(int fd, Vertex *v)",
+ "{ uchar c=1;",
+ "",
+ " if (!v) return;",
+ " xwrite(fd, (char *) &c, 1);",
+ " w_vertex(fd, v);",
+ " w_layer(fd, v->lnk);",
+ " w_layer(fd, v->left);",
+ " w_layer(fd, v->right);",
+ "}",
+ "",
+ "void",
+ "w_xpoint(void)",
+ "{ int fd; char nm[64];",
+ " int i, j; uchar c;",
+ " static uchar xwarned = 0;",
+ "",
+ " sprintf(nm, \"%%s.xpt\", PanSource);",
+ " if ((fd = creat(nm, 0666)) <= 0)",
+ " if (!xwarned)",
+ " { xwarned = 1;",
+ " printf(\"cannot creat checkpoint file\\n\");",
+ " return;",
+ " }",
+ " xwrite(fd, (char *) &nstates, sizeof(double));",
+ " xwrite(fd, (char *) &truncs, sizeof(double));",
+ " xwrite(fd, (char *) &truncs2, sizeof(double));",
+ " xwrite(fd, (char *) &nlinks, sizeof(double));",
+ " xwrite(fd, (char *) &dfa_depth, sizeof(int));",
+ " xwrite(fd, (char *) &R, sizeof(Vertex *));",
+ " xwrite(fd, (char *) &F, sizeof(Vertex *));",
+ " xwrite(fd, (char *) &NF, sizeof(Vertex *));",
+ "",
+ " for (j = 0; j < TWIDTH; j++)",
+ " for (i = 0; i < dfa_depth+1; i++)",
+ " { w_layer(fd, layers[i*TWIDTH+j]);",
+ " c = 2; xwrite(fd, (char *) &c, 1);",
+ " }",
+ " wclose(fd);",
+ "}",
+ "",
+ "static void",
+ "xread(int fd, char *b, int n)",
+ "{ int m = wcnt; int delta = 0;",
+ " if (m < n)",
+ " { if (m > 0) memcpy(b, &wbuf[WCNT-m], m);",
+ " delta = m;",
+ " WCNT = wcnt = read(fd, wbuf, 4096);",
+ " if (wcnt < n-m)",
+ " Uerror(\"xread failed -- insufficient data\");",
+ " n -= m;",
+ " }",
+ " memcpy(&b[delta], &wbuf[WCNT-wcnt], n);",
+ " wcnt -= n;",
+ "}",
+ "",
+ "static void",
+ "x_cleanup(Vertex *c)",
+ "{ Edge *e; /* remove the tree and edges from c */",
+ " if (!c) return;",
+ " for (e = c->Succ; e; e = e->Nxt)",
+ " x_cleanup(e->Dst);",
+ " recyc_vertex(c);",
+ "}",
+ "",
+ "static void",
+ "x_remove(void)",
+ "{ Vertex *tmp; int i, s;",
+ " int r, j;",
+ " /* double-check: */",
+ " stacker[dfa_depth-1] = 0; r = dfa_store(stacker);",
+ " stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1);",
+ " if (r != 1 || j != 0)",
+ " { printf(\"%%d: \", stackcnt);",
+ " for (i = 0; i < dfa_depth; i++)",
+ " printf(\"%%d,\", stacker[i]);",
+ " printf(\" -- not a stackstate <o:%%d,4:%%d>\\n\", r, j);",
+ " return;",
+ " }",
+ " stacker[dfa_depth-1] = 1;",
+ " s = dfa_member(dfa_depth-1);",
+ "",
+ " { tmp = F; F = NF; NF = tmp; } /* complement */",
+ " if (s) dfa_store(stacker);",
+ " stacker[dfa_depth-1] = 0;",
+ " dfa_store(stacker);",
+ " stackcnt++;",
+ " { tmp = F; F = NF; NF = tmp; }",
+ "}",
+ "",
+ "static void",
+ "x_rm_stack(Vertex *t, int k)",
+ "{ int j; Edge *e;",
+ "",
+ " if (k == 0)",
+ " { x_remove();",
+ " return;",
+ " }",
+ " if (t)",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " { for (j = e->From; j <= (int) e->To; j++)",
+ " { stacker[k] = (uchar) j;",
+ " x_rm_stack(e->Dst, k-1);",
+ " }",
+ " if (e->s)",
+ " { stacker[k] = e->S;",
+ " x_rm_stack(e->Dst, k-1);",
+ " } }",
+ "}",
+ "",
+ "static Vertex *",
+ "insert_withkey(Vertex *v, int L)",
+ "{ Vertex *new, *t = temptree[L];",
+ "",
+ " if (!t) { temptree[L] = v; return v; }",
+ " t = splay(v->key, t);",
+ " if (v->key < t->key)",
+ " { new = v;",
+ " new->left = t->left;",
+ " new->right = t;",
+ " t->left = (Vertex *) 0;",
+ " } else if (v->key > t->key)",
+ " { new = v;",
+ " new->right = t->right;",
+ " new->left = t;",
+ " t->right = (Vertex *) 0;",
+ " } else",
+ " { if (t != R && t != F && t != NF)",
+ " Uerror(\"double insert, bad checkpoint data\");",
+ " else",
+ " { recyc_vertex(v);",
+ " new = t;",
+ " } }",
+ " temptree[L] = new;",
+ "",
+ " return new;",
+ "}",
+ "",
+ "static Vertex *",
+ "find_withkey(Vertex *v, int L)",
+ "{ Vertex *t = temptree[L];",
+ " if (t)",
+ " { temptree[L] = t = splay((ulong) v, t);",
+ " if (t->key == (ulong) v)",
+ " return t;",
+ " }",
+ " Uerror(\"not found error, bad checkpoint data\");",
+ " return (Vertex *) 0;",
+ "}",
+ "",
+ "void",
+ "r_layer(int fd, int n)",
+ "{ Vertex *v;",
+ " Edge *e;",
+ " char c, t[2];",
+ "",
+ " for (;;)",
+ " { xread(fd, &c, 1);",
+ " if (c == 2) break;",
+ " if (c == 1)",
+ " { v = new_vertex();",
+ " xread(fd, (char *) &(v->key), sizeof(Vertex *));",
+ " v = insert_withkey(v, n);",
+ " } else /* c == 0 */",
+ " { e = new_edge((Vertex *) 0);",
+ " xread(fd, t, 2);",
+ " e->From = t[0];",
+ " e->To = t[1];",
+ " xread(fd, (char *) &(e->Dst), sizeof(Vertex *));",
+ " insert_edge(v, e);",
+ " } }",
+ "}",
+ "",
+ "static void",
+ "v_fix(Vertex *t, int nr)",
+ "{ int i; Edge *e;",
+ "",
+ " if (!t) return;",
+ "",
+ " for (i = 0; i < 2; i++)",
+ " if (t->dst[i])",
+ " t->dst[i] = find_withkey(t->dst[i], nr);",
+ "",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " e->Dst = find_withkey(e->Dst, nr);",
+ " ",
+ " v_fix(t->left, nr);",
+ " v_fix(t->right, nr);",
+ "}",
+ "",
+ "static void",
+ "v_insert(Vertex *t, int nr)",
+ "{ Edge *e; int i;",
+ "",
+ " if (!t) return;",
+ " v_insert(t->left, nr);",
+ " v_insert(t->right, nr);",
+ "",
+ " /* remove only leafs from temptree */",
+ " t->left = t->right = t->lnk = (Vertex *) 0;",
+ " insert_it(t, nr); /* into layers */",
+ " for (i = 0; i < 2; i++)",
+ " if (t->dst[i])",
+ " t->dst[i]->num += (t->to[i] - t->from[i] + 1);",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " e->Dst->num += (e->To - e->From + 1 + e->s);",
+ "}",
+ "",
+ "static void",
+ "x_fixup(void)",
+ "{ int i;",
+ "",
+ " for (i = 0; i < dfa_depth; i++)",
+ " v_fix(temptree[i], (i+1));",
+ "",
+ " for (i = dfa_depth; i >= 0; i--)",
+ " v_insert(temptree[i], i);",
+ "}",
+ "",
+ "static Vertex *",
+ "x_tail(Vertex *t, ulong want)",
+ "{ int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0;",
+ "",
+ " if (!t) return v;",
+ "",
+ " yes = no = 0;",
+ " for (i = 0; i < 2; i++)",
+ " if ((ulong) t->dst[i] == want)",
+ " { /* was t->from[i] <= 0 && t->to[i] >= 0 */",
+ " /* but from and to are uchar */",
+ " if (t->from[i] == 0)",
+ " yes = 1;",
+ " else",
+ " if (t->from[i] <= 4 && t->to[i] >= 4)",
+ " no = 1;",
+ " }",
+ "",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " if ((ulong) e->Dst == want)",
+ " { /* was INRANGE(e,0) but From and To are uchar */",
+ " if ((e->From == 0) || (e->s==1 && e->S==0))",
+ " yes = 1;",
+ " else if (INRANGE(e, 4))",
+ " no = 1;",
+ " }",
+ " if (yes && !no) return t;",
+ " v = x_tail(t->left, want); if (v) return v;",
+ " v = x_tail(t->right, want); if (v) return v;",
+ " return (Vertex *) 0;",
+ "}",
+ "",
+ "static void",
+ "x_anytail(Vertex *t, Vertex *c, int nr)",
+ "{ int i; Edge *e, *f; Vertex *v;",
+ "",
+ " if (!t) return;",
+ "",
+ " for (i = 0; i < 2; i++)",
+ " if ((ulong) t->dst[i] == c->key)",
+ " { v = new_vertex(); v->key = t->key;",
+ " f = new_edge(v);",
+ " f->From = t->from[i];",
+ " f->To = t->to[i];",
+ " f->Nxt = c->Succ;",
+ " c->Succ = f;",
+ " if (nr > 0)",
+ " x_anytail(temptree[nr-1], v, nr-1);",
+ " }",
+ "",
+ " for (e = t->Succ; e; e = e->Nxt)",
+ " if ((ulong) e->Dst == c->key)",
+ " { v = new_vertex(); v->key = t->key;",
+ " f = new_edge(v);",
+ " f->From = e->From;",
+ " f->To = e->To;",
+ " f->s = e->s;",
+ " f->S = e->S;",
+ " f->Nxt = c->Succ;",
+ " c->Succ = f;",
+ " x_anytail(temptree[nr-1], v, nr-1);",
+ " }",
+ "",
+ " x_anytail(t->left, c, nr);",
+ " x_anytail(t->right, c, nr);",
+ "}",
+ "",
+ "static Vertex *",
+ "x_cpy_rev(void)",
+ "{ Vertex *c, *v; /* find 0 and !4 predecessor of F */",
+ "",
+ " v = x_tail(temptree[dfa_depth-1], F->key);",
+ " if (!v) return (Vertex *) 0;",
+ "",
+ " c = new_vertex(); c->key = v->key;",
+ "",
+ " /* every node on dfa_depth-2 that has v->key as succ */",
+ " /* make copy and let c point to these (reversing ptrs) */",
+ "",
+ " x_anytail(temptree[dfa_depth-2], c, dfa_depth-2);",
+ " ",
+ " return c;",
+ "}",
+ "",
+ "void",
+ "r_xpoint(void)",
+ "{ int fd; char nm[64]; Vertex *d;",
+ " int i, j;",
+ "",
+ " wcnt = 0;",
+ " sprintf(nm, \"%%s.xpt\", PanSource);",
+ " if ((fd = open(nm, 0)) < 0) /* O_RDONLY */",
+ " Uerror(\"cannot open checkpoint file\");",
+ "",
+ " xread(fd, (char *) &nstates, sizeof(double));",
+ " xread(fd, (char *) &truncs, sizeof(double));",
+ " xread(fd, (char *) &truncs2, sizeof(double));",
+ " xread(fd, (char *) &nlinks, sizeof(double));",
+ " xread(fd, (char *) &dfa_depth, sizeof(int));",
+ "",
+ " if (dfa_depth != MA+a_cycles)",
+ " Uerror(\"bad dfa_depth in checkpoint file\");",
+ "",
+ " path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));",
+ " layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));",
+ " temptree = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *));",
+ " lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));",
+ " lastword[dfa_depth] = lastword[0] = 255; ",
+ "",
+ " path[0] = R = new_vertex();",
+ " xread(fd, (char *) &R->key, sizeof(Vertex *));",
+ " R = insert_withkey(R, 0);",
+ "",
+ " F = new_vertex();",
+ " xread(fd, (char *) &F->key, sizeof(Vertex *));",
+ " F = insert_withkey(F, dfa_depth);",
+ "",
+ " NF = new_vertex();",
+ " xread(fd, (char *) &NF->key, sizeof(Vertex *));",
+ " NF = insert_withkey(NF, dfa_depth);",
+ "",
+ " for (j = 0; j < TWIDTH; j++)",
+ " for (i = 0; i < dfa_depth+1; i++)",
+ " r_layer(fd, i);",
+ "",
+ " if (wcnt != 0) Uerror(\"bad count in checkpoint file\");",
+ "",
+ " d = x_cpy_rev();",
+ " x_fixup();",
+ " stacker[dfa_depth-1] = 0;",
+ " x_rm_stack(d, dfa_depth-2);",
+ " x_cleanup(d);",
+ " close(fd);",
+ "",
+ " printf(\"pan: removed %%d stackstates\\n\", stackcnt);",
+ " nstates -= (double) stackcnt;",
+ "}",
+ "#endif",
+ 0,
+};
--- /dev/null
+/***** spin: pangen6.c *****/
+
+/* Copyright (c) 2000-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Abstract syntax tree analysis / slicing (spin option -A) */
+/* AST_store stores the fsms's for each proctype */
+/* AST_track keeps track of variables used in properties */
+/* AST_slice starts the slicing algorithm */
+/* it first collects more info and then calls */
+/* AST_criteria to process the slice criteria */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern Ordered *all_names;
+extern FSM_use *use_free;
+extern FSM_state **fsm_tbl;
+extern FSM_state *fsm;
+extern int verbose, o_max;
+
+static FSM_trans *cur_t;
+static FSM_trans *expl_par;
+static FSM_trans *expl_var;
+static FSM_trans *explicit;
+
+extern void rel_use(FSM_use *);
+
+#define ulong unsigned long
+
+typedef struct Pair {
+ FSM_state *h;
+ int b;
+ struct Pair *nxt;
+} Pair;
+
+typedef struct AST {
+ ProcList *p; /* proctype decl */
+ int i_st; /* start state */
+ int nstates, nwords;
+ int relevant;
+ Pair *pairs; /* entry and exit nodes of proper subgraphs */
+ FSM_state *fsm; /* proctype body */
+ struct AST *nxt; /* linked list */
+} AST;
+
+typedef struct RPN { /* relevant proctype names */
+ Symbol *rn;
+ struct RPN *nxt;
+} RPN;
+
+typedef struct ALIAS { /* channel aliasing info */
+ Lextok *cnm; /* this chan */
+ int origin; /* debugging - origin of the alias */
+ struct ALIAS *alias; /* can be an alias for these other chans */
+ struct ALIAS *nxt; /* linked list */
+} ALIAS;
+
+typedef struct ChanList {
+ Lextok *s; /* containing stmnt */
+ Lextok *n; /* point of reference - could be struct */
+ struct ChanList *nxt; /* linked list */
+} ChanList;
+
+/* a chan alias can be created in one of three ways:
+ assignement to chan name
+ a = b -- a is now an alias for b
+ passing chan name as parameter in run
+ run x(b) -- proctype x(chan a)
+ passing chan name through channel
+ x!b -- x?a
+ */
+
+#define USE 1
+#define DEF 2
+#define DEREF_DEF 4
+#define DEREF_USE 8
+
+static AST *ast;
+static ALIAS *chalcur;
+static ALIAS *chalias;
+static ChanList *chanlist;
+static Slicer *slicer;
+static Slicer *rel_vars; /* all relevant variables */
+static int AST_Changes;
+static int AST_Round;
+static RPN *rpn;
+static int in_recv = 0;
+
+static int AST_mutual(Lextok *, Lextok *, int);
+static void AST_dominant(void);
+static void AST_hidden(void);
+static void AST_setcur(Lextok *);
+static void check_slice(Lextok *, int);
+static void curtail(AST *);
+static void def_use(Lextok *, int);
+static void name_AST_track(Lextok *, int);
+static void show_expl(void);
+
+static int
+AST_isini(Lextok *n) /* is this an initialized channel */
+{ Symbol *s;
+
+ if (!n || !n->sym) return 0;
+
+ s = n->sym;
+
+ if (s->type == CHAN)
+ return (s->ini->ntyp == CHAN); /* freshly instantiated */
+
+ if (s->type == STRUCT && n->rgt)
+ return AST_isini(n->rgt->lft);
+
+ return 0;
+}
+
+static void
+AST_var(Lextok *n, Symbol *s, int toplevel)
+{
+ if (!s) return;
+
+ if (toplevel)
+ { if (s->context && s->type)
+ printf(":%s:L:", s->context->name);
+ else
+ printf("G:");
+ }
+ printf("%s", s->name); /* array indices ignored */
+
+ if (s->type == STRUCT && n && n->rgt && n->rgt->lft)
+ { printf(":");
+ AST_var(n->rgt->lft, n->rgt->lft->sym, 0);
+ }
+}
+
+static void
+name_def_indices(Lextok *n, int code)
+{
+ if (!n || !n->sym) return;
+
+ if (n->sym->nel != 1)
+ def_use(n->lft, code); /* process the index */
+
+ if (n->sym->type == STRUCT /* and possible deeper ones */
+ && n->rgt)
+ name_def_indices(n->rgt->lft, code);
+}
+
+static void
+name_def_use(Lextok *n, int code)
+{ FSM_use *u;
+
+ if (!n) return;
+
+ if ((code&USE)
+ && cur_t->step
+ && cur_t->step->n)
+ { switch (cur_t->step->n->ntyp) {
+ case 'c': /* possible predicate abstraction? */
+ n->sym->colnr |= 2; /* yes */
+ break;
+ default:
+ n->sym->colnr |= 1; /* no */
+ break;
+ }
+ }
+
+ for (u = cur_t->Val[0]; u; u = u->nxt)
+ if (AST_mutual(n, u->n, 1)
+ && u->special == code)
+ return;
+
+ if (use_free)
+ { u = use_free;
+ use_free = use_free->nxt;
+ } else
+ u = (FSM_use *) emalloc(sizeof(FSM_use));
+
+ u->n = n;
+ u->special = code;
+ u->nxt = cur_t->Val[0];
+ cur_t->Val[0] = u;
+
+ name_def_indices(n, USE|(code&(~DEF))); /* not def, but perhaps deref */
+}
+
+static void
+def_use(Lextok *now, int code)
+{ Lextok *v;
+
+ if (now)
+ switch (now->ntyp) {
+ case '!':
+ case UMIN:
+ case '~':
+ case 'c':
+ case ENABLED:
+ case ASSERT:
+ case EVAL:
+ def_use(now->lft, USE|code);
+ break;
+
+ case LEN:
+ case FULL:
+ case EMPTY:
+ case NFULL:
+ case NEMPTY:
+ def_use(now->lft, DEREF_USE|USE|code);
+ break;
+
+ case '/':
+ case '*':
+ case '-':
+ case '+':
+ case '%':
+ case '&':
+ case '^':
+ case '|':
+ case LE:
+ case GE:
+ case GT:
+ case LT:
+ case NE:
+ case EQ:
+ case OR:
+ case AND:
+ case LSHIFT:
+ case RSHIFT:
+ def_use(now->lft, USE|code);
+ def_use(now->rgt, USE|code);
+ break;
+
+ case ASGN:
+ def_use(now->lft, DEF|code);
+ def_use(now->rgt, USE|code);
+ break;
+
+ case TYPE: /* name in parameter list */
+ name_def_use(now, code);
+ break;
+
+ case NAME:
+ name_def_use(now, code);
+ break;
+
+ case RUN:
+ name_def_use(now, USE); /* procname - not really needed */
+ for (v = now->lft; v; v = v->rgt)
+ def_use(v->lft, USE); /* params */
+ break;
+
+ case 's':
+ def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ def_use(v->lft, USE|code);
+ break;
+
+ case 'r':
+ def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ { if (v->lft->ntyp == EVAL)
+ def_use(v->lft, code); /* will add USE */
+ else if (v->lft->ntyp != CONST)
+ def_use(v->lft, DEF|code);
+ }
+ break;
+
+ case 'R':
+ def_use(now->lft, DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ { if (v->lft->ntyp == EVAL)
+ def_use(v->lft, code); /* will add USE */
+ }
+ break;
+
+ case '?':
+ def_use(now->lft, USE|code);
+ if (now->rgt)
+ { def_use(now->rgt->lft, code);
+ def_use(now->rgt->rgt, code);
+ }
+ break;
+
+ case PRINT:
+ for (v = now->lft; v; v = v->rgt)
+ def_use(v->lft, USE|code);
+ break;
+
+ case PRINTM:
+ def_use(now->lft, USE);
+ break;
+
+ case CONST:
+ case ELSE: /* ? */
+ case NONPROGRESS:
+ case PC_VAL:
+ case 'p':
+ case 'q':
+ break;
+
+ case '.':
+ case GOTO:
+ case BREAK:
+ case '@':
+ case D_STEP:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case IF:
+ case DO:
+ case UNLESS:
+ case TIMEOUT:
+ case C_CODE:
+ case C_EXPR:
+ default:
+ break;
+ }
+}
+
+static int
+AST_add_alias(Lextok *n, int nr)
+{ ALIAS *ca;
+ int res;
+
+ for (ca = chalcur->alias; ca; ca = ca->nxt)
+ if (AST_mutual(ca->cnm, n, 1))
+ { res = (ca->origin&nr);
+ ca->origin |= nr; /* 1, 2, or 4 - run, asgn, or rcv */
+ return (res == 0); /* 0 if already there with same origin */
+ }
+
+ ca = (ALIAS *) emalloc(sizeof(ALIAS));
+ ca->cnm = n;
+ ca->origin = nr;
+ ca->nxt = chalcur->alias;
+ chalcur->alias = ca;
+ return 1;
+}
+
+static void
+AST_run_alias(char *pn, char *s, Lextok *t, int parno)
+{ Lextok *v;
+ int cnt;
+
+ if (!t) return;
+
+ if (t->ntyp == RUN)
+ { if (strcmp(t->sym->name, s) == 0)
+ for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
+ if (cnt == parno)
+ { AST_add_alias(v->lft, 1); /* RUN */
+ break;
+ }
+ } else
+ { AST_run_alias(pn, s, t->lft, parno);
+ AST_run_alias(pn, s, t->rgt, parno);
+ }
+}
+
+static void
+AST_findrun(char *s, int parno)
+{ FSM_state *f;
+ FSM_trans *t;
+ AST *a;
+
+ for (a = ast; a; a = a->nxt) /* automata */
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* transitions */
+ { if (t->step)
+ AST_run_alias(a->p->n->name, s, t->step->n, parno);
+ }
+}
+
+static void
+AST_par_chans(ProcList *p) /* find local chan's init'd to chan passed as param */
+{ Ordered *walk;
+ Symbol *sp;
+
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp
+ && sp->context
+ && strcmp(sp->context->name, p->n->name) == 0
+ && sp->Nid >= 0 /* not itself a param */
+ && sp->type == CHAN
+ && sp->ini->ntyp == NAME) /* != CONST and != CHAN */
+ { Lextok *x = nn(ZN, 0, ZN, ZN);
+ x->sym = sp;
+ AST_setcur(x);
+ AST_add_alias(sp->ini, 2); /* ASGN */
+ } }
+}
+
+static void
+AST_para(ProcList *p)
+{ Lextok *f, *t, *c;
+ int cnt = 0;
+
+ AST_par_chans(p);
+
+ for (f = p->p; f; f = f->rgt) /* list of types */
+ for (t = f->lft; t; t = t->rgt)
+ { if (t->ntyp != ',')
+ c = t;
+ else
+ c = t->lft; /* expanded struct */
+
+ cnt++;
+ if (Sym_typ(c) == CHAN)
+ { ALIAS *na = (ALIAS *) emalloc(sizeof(ALIAS));
+
+ na->cnm = c;
+ na->nxt = chalias;
+ chalcur = chalias = na;
+#if 0
+ printf("%s -- (par) -- ", p->n->name);
+ AST_var(c, c->sym, 1);
+ printf(" => <<");
+#endif
+ AST_findrun(p->n->name, cnt);
+#if 0
+ printf(">>\n");
+#endif
+ }
+ }
+}
+
+static void
+AST_haschan(Lextok *c)
+{
+ if (!c) return;
+ if (Sym_typ(c) == CHAN)
+ { AST_add_alias(c, 2); /* ASGN */
+#if 0
+ printf("<<");
+ AST_var(c, c->sym, 1);
+ printf(">>\n");
+#endif
+ } else
+ { AST_haschan(c->rgt);
+ AST_haschan(c->lft);
+ }
+}
+
+static int
+AST_nrpar(Lextok *n) /* 's' or 'r' */
+{ Lextok *m;
+ int j = 0;
+
+ for (m = n->rgt; m; m = m->rgt)
+ j++;
+ return j;
+}
+
+static int
+AST_ord(Lextok *n, Lextok *s)
+{ Lextok *m;
+ int j = 0;
+
+ for (m = n->rgt; m; m = m->rgt)
+ { j++;
+ if (s->sym == m->lft->sym)
+ return j;
+ }
+ return 0;
+}
+
+#if 0
+static void
+AST_ownership(Symbol *s)
+{
+ if (!s) return;
+ printf("%s:", s->name);
+ AST_ownership(s->owner);
+}
+#endif
+
+static int
+AST_mutual(Lextok *a, Lextok *b, int toplevel)
+{ Symbol *as, *bs;
+
+ if (!a && !b) return 1;
+
+ if (!a || !b) return 0;
+
+ as = a->sym;
+ bs = b->sym;
+
+ if (!as || !bs) return 0;
+
+ if (toplevel && as->context != bs->context)
+ return 0;
+
+ if (as->type != bs->type)
+ return 0;
+
+ if (strcmp(as->name, bs->name) != 0)
+ return 0;
+
+ if (as->type == STRUCT && a->rgt && b->rgt) /* we know that a and b are not null */
+ return AST_mutual(a->rgt->lft, b->rgt->lft, 0);
+
+ return 1;
+}
+
+static void
+AST_setcur(Lextok *n) /* set chalcur */
+{ ALIAS *ca;
+
+ for (ca = chalias; ca; ca = ca->nxt)
+ if (AST_mutual(ca->cnm, n, 1)) /* if same chan */
+ { chalcur = ca;
+ return;
+ }
+
+ ca = (ALIAS *) emalloc(sizeof(ALIAS));
+ ca->cnm = n;
+ ca->nxt = chalias;
+ chalcur = chalias = ca;
+}
+
+static void
+AST_other(AST *a) /* check chan params in asgns and recvs */
+{ FSM_state *f;
+ FSM_trans *t;
+ FSM_use *u;
+ ChanList *cl;
+
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* transitions */
+ for (u = t->Val[0]; u; u = u->nxt) /* def/use info */
+ if (Sym_typ(u->n) == CHAN
+ && (u->special&DEF)) /* def of chan-name */
+ { AST_setcur(u->n);
+ switch (t->step->n->ntyp) {
+ case ASGN:
+ AST_haschan(t->step->n->rgt);
+ break;
+ case 'r':
+ /* guess sends where name may originate */
+ for (cl = chanlist; cl; cl = cl->nxt) /* all sends */
+ { int aa = AST_nrpar(cl->s);
+ int bb = AST_nrpar(t->step->n);
+ if (aa != bb) /* matching nrs of params */
+ continue;
+
+ aa = AST_ord(cl->s, cl->n);
+ bb = AST_ord(t->step->n, u->n);
+ if (aa != bb) /* same position in parlist */
+ continue;
+
+ AST_add_alias(cl->n, 4); /* RCV assume possible match */
+ }
+ break;
+ default:
+ printf("type = %d\n", t->step->n->ntyp);
+ non_fatal("unexpected chan def type", (char *) 0);
+ break;
+ } }
+}
+
+static void
+AST_aliases(void)
+{ ALIAS *na, *ca;
+
+ for (na = chalias; na; na = na->nxt)
+ { printf("\npossible aliases of ");
+ AST_var(na->cnm, na->cnm->sym, 1);
+ printf("\n\t");
+ for (ca = na->alias; ca; ca = ca->nxt)
+ { if (!ca->cnm->sym)
+ printf("no valid name ");
+ else
+ AST_var(ca->cnm, ca->cnm->sym, 1);
+ printf("<");
+ if (ca->origin & 1) printf("RUN ");
+ if (ca->origin & 2) printf("ASGN ");
+ if (ca->origin & 4) printf("RCV ");
+ printf("[%s]", AST_isini(ca->cnm)?"Initzd":"Name");
+ printf(">");
+ if (ca->nxt) printf(", ");
+ }
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void
+AST_indirect(FSM_use *uin, FSM_trans *t, char *cause, char *pn)
+{ FSM_use *u;
+
+ /* this is a newly discovered relevant statement */
+ /* all vars it uses to contribute to its DEF are new criteria */
+
+ if (!(t->relevant&1)) AST_Changes++;
+
+ t->round = AST_Round;
+ t->relevant = 1;
+
+ if ((verbose&32) && t->step)
+ { printf("\tDR %s [[ ", pn);
+ comment(stdout, t->step->n, 0);
+ printf("]]\n\t\tfully relevant %s", cause);
+ if (uin) { printf(" due to "); AST_var(uin->n, uin->n->sym, 1); }
+ printf("\n");
+ }
+ for (u = t->Val[0]; u; u = u->nxt)
+ if (u != uin
+ && (u->special&(USE|DEREF_USE)))
+ { if (verbose&32)
+ { printf("\t\t\tuses(%d): ", u->special);
+ AST_var(u->n, u->n->sym, 1);
+ printf("\n");
+ }
+ name_AST_track(u->n, u->special); /* add to slice criteria */
+ }
+}
+
+static void
+def_relevant(char *pn, FSM_trans *t, Lextok *n, int ischan)
+{ FSM_use *u;
+ ALIAS *na, *ca;
+ int chanref;
+
+ /* look for all DEF's of n
+ * mark those stmnts relevant
+ * mark all var USEs in those stmnts as criteria
+ */
+
+ if (n->ntyp != ELSE)
+ for (u = t->Val[0]; u; u = u->nxt)
+ { chanref = (Sym_typ(u->n) == CHAN);
+
+ if (ischan != chanref /* no possible match */
+ || !(u->special&(DEF|DEREF_DEF))) /* not a def */
+ continue;
+
+ if (AST_mutual(u->n, n, 1))
+ { AST_indirect(u, t, "(exact match)", pn);
+ continue;
+ }
+
+ if (chanref)
+ for (na = chalias; na; na = na->nxt)
+ { if (!AST_mutual(u->n, na->cnm, 1))
+ continue;
+ for (ca = na->alias; ca; ca = ca->nxt)
+ if (AST_mutual(ca->cnm, n, 1)
+ && AST_isini(ca->cnm))
+ { AST_indirect(u, t, "(alias match)", pn);
+ break;
+ }
+ if (ca) break;
+ } }
+}
+
+static void
+AST_relevant(Lextok *n)
+{ AST *a;
+ FSM_state *f;
+ FSM_trans *t;
+ int ischan;
+
+ /* look for all DEF's of n
+ * mark those stmnts relevant
+ * mark all var USEs in those stmnts as criteria
+ */
+
+ if (!n) return;
+ ischan = (Sym_typ(n) == CHAN);
+
+ if (verbose&32)
+ { printf("<<ast_relevant (ntyp=%d) ", n->ntyp);
+ AST_var(n, n->sym, 1);
+ printf(">>\n");
+ }
+
+ for (t = expl_par; t; t = t->nxt) /* param assignments */
+ { if (!(t->relevant&1))
+ def_relevant(":params:", t, n, ischan);
+ }
+
+ for (t = expl_var; t; t = t->nxt)
+ { if (!(t->relevant&1)) /* var inits */
+ def_relevant(":vars:", t, n, ischan);
+ }
+
+ for (a = ast; a; a = a->nxt) /* all other stmnts */
+ { if (strcmp(a->p->n->name, ":never:") != 0
+ && strcmp(a->p->n->name, ":trace:") != 0
+ && strcmp(a->p->n->name, ":notrace:") != 0)
+ for (f = a->fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ { if (!(t->relevant&1))
+ def_relevant(a->p->n->name, t, n, ischan);
+ } }
+}
+
+static int
+AST_relpar(char *s)
+{ FSM_trans *t, *T;
+ FSM_use *u;
+
+ for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
+ for (t = T; t; t = t->nxt)
+ { if (t->relevant&1)
+ for (u = t->Val[0]; u; u = u->nxt)
+ { if (u->n->sym->type
+ && u->n->sym->context
+ && strcmp(u->n->sym->context->name, s) == 0)
+ {
+ if (verbose&32)
+ { printf("proctype %s relevant, due to symbol ", s);
+ AST_var(u->n, u->n->sym, 1);
+ printf("\n");
+ }
+ return 1;
+ } } }
+ return 0;
+}
+
+static void
+AST_dorelevant(void)
+{ AST *a;
+ RPN *r;
+
+ for (r = rpn; r; r = r->nxt)
+ { for (a = ast; a; a = a->nxt)
+ if (strcmp(a->p->n->name, r->rn->name) == 0)
+ { a->relevant |= 1;
+ break;
+ }
+ if (!a)
+ fatal("cannot find proctype %s", r->rn->name);
+ }
+}
+
+static void
+AST_procisrelevant(Symbol *s)
+{ RPN *r;
+ for (r = rpn; r; r = r->nxt)
+ if (strcmp(r->rn->name, s->name) == 0)
+ return;
+ r = (RPN *) emalloc(sizeof(RPN));
+ r->rn = s;
+ r->nxt = rpn;
+ rpn = r;
+}
+
+static int
+AST_proc_isrel(char *s)
+{ AST *a;
+
+ for (a = ast; a; a = a->nxt)
+ if (strcmp(a->p->n->name, s) == 0)
+ return (a->relevant&1);
+ non_fatal("cannot happen, missing proc in ast", (char *) 0);
+ return 0;
+}
+
+static int
+AST_scoutrun(Lextok *t)
+{
+ if (!t) return 0;
+
+ if (t->ntyp == RUN)
+ return AST_proc_isrel(t->sym->name);
+ return (AST_scoutrun(t->lft) || AST_scoutrun(t->rgt));
+}
+
+static void
+AST_tagruns(void)
+{ AST *a;
+ FSM_state *f;
+ FSM_trans *t;
+
+ /* if any stmnt inside a proctype is relevant
+ * or any parameter passed in a run
+ * then so are all the run statements on that proctype
+ */
+
+ for (a = ast; a; a = a->nxt)
+ { if (strcmp(a->p->n->name, ":never:") == 0
+ || strcmp(a->p->n->name, ":trace:") == 0
+ || strcmp(a->p->n->name, ":notrace:") == 0
+ || strcmp(a->p->n->name, ":init:") == 0)
+ { a->relevant |= 1; /* the proctype is relevant */
+ continue;
+ }
+ if (AST_relpar(a->p->n->name))
+ a->relevant |= 1;
+ else
+ { for (f = a->fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ if (t->relevant)
+ goto yes;
+yes: if (f)
+ a->relevant |= 1;
+ }
+ }
+
+ for (a = ast; a; a = a->nxt)
+ for (f = a->fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ if (t->step
+ && AST_scoutrun(t->step->n))
+ { AST_indirect((FSM_use *)0, t, ":run:", a->p->n->name);
+ /* BUT, not all actual params are relevant */
+ }
+}
+
+static void
+AST_report(AST *a, Element *e) /* ALSO deduce irrelevant vars */
+{
+ if (!(a->relevant&2))
+ { a->relevant |= 2;
+ printf("spin: redundant in proctype %s (for given property):\n",
+ a->p->n->name);
+ }
+ printf(" line %3d %s (state %d)",
+ e->n?e->n->ln:-1,
+ e->n?e->n->fn->name:"-",
+ e->seqno);
+ printf(" [");
+ comment(stdout, e->n, 0);
+ printf("]\n");
+}
+
+static int
+AST_always(Lextok *n)
+{
+ if (!n) return 0;
+
+ if (n->ntyp == '@' /* -end */
+ || n->ntyp == 'p') /* remote reference */
+ return 1;
+ return AST_always(n->lft) || AST_always(n->rgt);
+}
+
+static void
+AST_edge_dump(AST *a, FSM_state *f)
+{ FSM_trans *t;
+ FSM_use *u;
+
+ for (t = f->t; t; t = t->nxt) /* edges */
+ {
+ if (t->step && AST_always(t->step->n))
+ t->relevant |= 1; /* always relevant */
+
+ if (verbose&32)
+ { switch (t->relevant) {
+ case 0: printf(" "); break;
+ case 1: printf("*%3d ", t->round); break;
+ case 2: printf("+%3d ", t->round); break;
+ case 3: printf("#%3d ", t->round); break;
+ default: printf("? "); break;
+ }
+
+ printf("%d\t->\t%d\t", f->from, t->to);
+ if (t->step)
+ comment(stdout, t->step->n, 0);
+ else
+ printf("Unless");
+
+ for (u = t->Val[0]; u; u = u->nxt)
+ { printf(" <");
+ AST_var(u->n, u->n->sym, 1);
+ printf(":%d>", u->special);
+ }
+ printf("\n");
+ } else
+ { if (t->relevant)
+ continue;
+
+ if (t->step)
+ switch(t->step->n->ntyp) {
+ case ASGN:
+ case 's':
+ case 'r':
+ case 'c':
+ if (t->step->n->lft->ntyp != CONST)
+ AST_report(a, t->step);
+ break;
+
+ case PRINT: /* don't report */
+ case PRINTM:
+ case ASSERT:
+ case C_CODE:
+ case C_EXPR:
+ default:
+ break;
+ } } }
+}
+
+static void
+AST_dfs(AST *a, int s, int vis)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ f = fsm_tbl[s];
+ if (f->seen) return;
+
+ f->seen = 1;
+ if (vis) AST_edge_dump(a, f);
+
+ for (t = f->t; t; t = t->nxt)
+ AST_dfs(a, t->to, vis);
+}
+
+static void
+AST_dump(AST *a)
+{ FSM_state *f;
+
+ for (f = a->fsm; f; f = f->nxt)
+ { f->seen = 0;
+ fsm_tbl[f->from] = f;
+ }
+
+ if (verbose&32)
+ printf("AST_START %s from %d\n", a->p->n->name, a->i_st);
+
+ AST_dfs(a, a->i_st, 1);
+}
+
+static void
+AST_sends(AST *a)
+{ FSM_state *f;
+ FSM_trans *t;
+ FSM_use *u;
+ ChanList *cl;
+
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* transitions */
+ { if (t->step
+ && t->step->n
+ && t->step->n->ntyp == 's')
+ for (u = t->Val[0]; u; u = u->nxt)
+ { if (Sym_typ(u->n) == CHAN
+ && ((u->special&USE) && !(u->special&DEREF_USE)))
+ {
+#if 0
+ printf("%s -- (%d->%d) -- ",
+ a->p->n->name, f->from, t->to);
+ AST_var(u->n, u->n->sym, 1);
+ printf(" -> chanlist\n");
+#endif
+ cl = (ChanList *) emalloc(sizeof(ChanList));
+ cl->s = t->step->n;
+ cl->n = u->n;
+ cl->nxt = chanlist;
+ chanlist = cl;
+} } } }
+
+static ALIAS *
+AST_alfind(Lextok *n)
+{ ALIAS *na;
+
+ for (na = chalias; na; na = na->nxt)
+ if (AST_mutual(na->cnm, n, 1))
+ return na;
+ return (ALIAS *) 0;
+}
+
+static void
+AST_trans(void)
+{ ALIAS *na, *ca, *da, *ea;
+ int nchanges;
+
+ do {
+ nchanges = 0;
+ for (na = chalias; na; na = na->nxt)
+ { chalcur = na;
+ for (ca = na->alias; ca; ca = ca->nxt)
+ { da = AST_alfind(ca->cnm);
+ if (da)
+ for (ea = da->alias; ea; ea = ea->nxt)
+ { nchanges += AST_add_alias(ea->cnm,
+ ea->origin|ca->origin);
+ } } }
+ } while (nchanges > 0);
+
+ chalcur = (ALIAS *) 0;
+}
+
+static void
+AST_def_use(AST *a)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* all edges */
+ { cur_t = t;
+ rel_use(t->Val[0]); /* redo Val; doesn't cover structs */
+ rel_use(t->Val[1]);
+ t->Val[0] = t->Val[1] = (FSM_use *) 0;
+
+ if (!t->step) continue;
+
+ def_use(t->step->n, 0); /* def/use info, including structs */
+ }
+ cur_t = (FSM_trans *) 0;
+}
+
+static void
+name_AST_track(Lextok *n, int code)
+{ extern int nr_errs;
+#if 0
+ printf("AST_name: ");
+ AST_var(n, n->sym, 1);
+ printf(" -- %d\n", code);
+#endif
+ if (in_recv && (code&DEF) && (code&USE))
+ { printf("spin: error: DEF and USE of same var in rcv stmnt: ");
+ AST_var(n, n->sym, 1);
+ printf(" -- %d\n", code);
+ nr_errs++;
+ }
+ check_slice(n, code);
+}
+
+void
+AST_track(Lextok *now, int code) /* called from main.c */
+{ Lextok *v; extern int export_ast;
+
+ if (!export_ast) return;
+
+ if (now)
+ switch (now->ntyp) {
+ case LEN:
+ case FULL:
+ case EMPTY:
+ case NFULL:
+ case NEMPTY:
+ AST_track(now->lft, DEREF_USE|USE|code);
+ break;
+
+ case '/':
+ case '*':
+ case '-':
+ case '+':
+ case '%':
+ case '&':
+ case '^':
+ case '|':
+ case LE:
+ case GE:
+ case GT:
+ case LT:
+ case NE:
+ case EQ:
+ case OR:
+ case AND:
+ case LSHIFT:
+ case RSHIFT:
+ AST_track(now->rgt, USE|code);
+ /* fall through */
+ case '!':
+ case UMIN:
+ case '~':
+ case 'c':
+ case ENABLED:
+ case ASSERT:
+ AST_track(now->lft, USE|code);
+ break;
+
+ case EVAL:
+ AST_track(now->lft, USE|(code&(~DEF)));
+ break;
+
+ case NAME:
+ name_AST_track(now, code);
+ if (now->sym->nel != 1)
+ AST_track(now->lft, USE|code); /* index */
+ break;
+
+ case 'R':
+ AST_track(now->lft, DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ AST_track(v->lft, code); /* a deeper eval can add USE */
+ break;
+
+ case '?':
+ AST_track(now->lft, USE|code);
+ if (now->rgt)
+ { AST_track(now->rgt->lft, code);
+ AST_track(now->rgt->rgt, code);
+ }
+ break;
+
+/* added for control deps: */
+ case TYPE:
+ name_AST_track(now, code);
+ break;
+ case ASGN:
+ AST_track(now->lft, DEF|code);
+ AST_track(now->rgt, USE|code);
+ break;
+ case RUN:
+ name_AST_track(now, USE);
+ for (v = now->lft; v; v = v->rgt)
+ AST_track(v->lft, USE|code);
+ break;
+ case 's':
+ AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ AST_track(v->lft, USE|code);
+ break;
+ case 'r':
+ AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
+ for (v = now->rgt; v; v = v->rgt)
+ { in_recv++;
+ AST_track(v->lft, DEF|code);
+ in_recv--;
+ }
+ break;
+ case PRINT:
+ for (v = now->lft; v; v = v->rgt)
+ AST_track(v->lft, USE|code);
+ break;
+ case PRINTM:
+ AST_track(now->lft, USE);
+ break;
+/* end add */
+ case 'p':
+#if 0
+ 'p' -sym-> _p
+ /
+ '?' -sym-> a (proctype)
+ /
+ b (pid expr)
+#endif
+ AST_track(now->lft->lft, USE|code);
+ AST_procisrelevant(now->lft->sym);
+ break;
+
+ case CONST:
+ case ELSE:
+ case NONPROGRESS:
+ case PC_VAL:
+ case 'q':
+ break;
+
+ case '.':
+ case GOTO:
+ case BREAK:
+ case '@':
+ case D_STEP:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case IF:
+ case DO:
+ case UNLESS:
+ case TIMEOUT:
+ case C_CODE:
+ case C_EXPR:
+ break;
+
+ default:
+ printf("AST_track, NOT EXPECTED ntyp: %d\n", now->ntyp);
+ break;
+ }
+}
+
+static int
+AST_dump_rel(void)
+{ Slicer *rv;
+ Ordered *walk;
+ char buf[64];
+ int banner=0;
+
+ if (verbose&32)
+ { printf("Relevant variables:\n");
+ for (rv = rel_vars; rv; rv = rv->nxt)
+ { printf("\t");
+ AST_var(rv->n, rv->n->sym, 1);
+ printf("\n");
+ }
+ return 1;
+ }
+ for (rv = rel_vars; rv; rv = rv->nxt)
+ rv->n->sym->setat = 1; /* mark it */
+
+ for (walk = all_names; walk; walk = walk->next)
+ { Symbol *s;
+ s = walk->entry;
+ if (!s->setat
+ && (s->type != MTYPE || s->ini->ntyp != CONST)
+ && s->type != STRUCT /* report only fields */
+ && s->type != PROCTYPE
+ && !s->owner
+ && sputtype(buf, s->type))
+ { if (!banner)
+ { banner = 1;
+ printf("spin: redundant vars (for given property):\n");
+ }
+ printf("\t");
+ symvar(s);
+ } }
+ return banner;
+}
+
+static void
+AST_suggestions(void)
+{ Symbol *s;
+ Ordered *walk;
+ FSM_state *f;
+ FSM_trans *t;
+ AST *a;
+ int banner=0;
+ int talked=0;
+
+ for (walk = all_names; walk; walk = walk->next)
+ { s = walk->entry;
+ if (s->colnr == 2 /* only used in conditionals */
+ && (s->type == BYTE
+ || s->type == SHORT
+ || s->type == INT
+ || s->type == MTYPE))
+ { if (!banner)
+ { banner = 1;
+ printf("spin: consider using predicate");
+ printf(" abstraction to replace:\n");
+ }
+ printf("\t");
+ symvar(s);
+ } }
+
+ /* look for source and sink processes */
+
+ for (a = ast; a; a = a->nxt) /* automata */
+ { banner = 0;
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* transitions */
+ { if (t->step)
+ switch (t->step->n->ntyp) {
+ case 's':
+ banner |= 1;
+ break;
+ case 'r':
+ banner |= 2;
+ break;
+ case '.':
+ case D_STEP:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case IF:
+ case DO:
+ case UNLESS:
+ case '@':
+ case GOTO:
+ case BREAK:
+ case PRINT:
+ case PRINTM:
+ case ASSERT:
+ case C_CODE:
+ case C_EXPR:
+ break;
+ default:
+ banner |= 4;
+ goto no_good;
+ }
+ }
+no_good: if (banner == 1 || banner == 2)
+ { printf("spin: proctype %s defines a %s process\n",
+ a->p->n->name,
+ banner==1?"source":"sink");
+ talked |= banner;
+ } else if (banner == 3)
+ { printf("spin: proctype %s mimics a buffer\n",
+ a->p->n->name);
+ talked |= 4;
+ }
+ }
+ if (talked&1)
+ { printf("\tto reduce complexity, consider merging the code of\n");
+ printf("\teach source process into the code of its target\n");
+ }
+ if (talked&2)
+ { printf("\tto reduce complexity, consider merging the code of\n");
+ printf("\teach sink process into the code of its source\n");
+ }
+ if (talked&4)
+ printf("\tto reduce complexity, avoid buffer processes\n");
+}
+
+static void
+AST_preserve(void)
+{ Slicer *sc, *nx, *rv;
+
+ for (sc = slicer; sc; sc = nx)
+ { if (!sc->used)
+ break; /* done */
+
+ nx = sc->nxt;
+
+ for (rv = rel_vars; rv; rv = rv->nxt)
+ if (AST_mutual(sc->n, rv->n, 1))
+ break;
+
+ if (!rv) /* not already there */
+ { sc->nxt = rel_vars;
+ rel_vars = sc;
+ } }
+ slicer = sc;
+}
+
+static void
+check_slice(Lextok *n, int code)
+{ Slicer *sc;
+
+ for (sc = slicer; sc; sc = sc->nxt)
+ if (AST_mutual(sc->n, n, 1)
+ && sc->code == code)
+ return; /* already there */
+
+ sc = (Slicer *) emalloc(sizeof(Slicer));
+ sc->n = n;
+
+ sc->code = code;
+ sc->used = 0;
+ sc->nxt = slicer;
+ slicer = sc;
+}
+
+static void
+AST_data_dep(void)
+{ Slicer *sc;
+
+ /* mark all def-relevant transitions */
+ for (sc = slicer; sc; sc = sc->nxt)
+ { sc->used = 1;
+ if (verbose&32)
+ { printf("spin: slice criterion ");
+ AST_var(sc->n, sc->n->sym, 1);
+ printf(" type=%d\n", Sym_typ(sc->n));
+ }
+ AST_relevant(sc->n);
+ }
+ AST_tagruns(); /* mark 'run's relevant if target proctype is relevant */
+}
+
+static int
+AST_blockable(AST *a, int s)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ f = fsm_tbl[s];
+
+ for (t = f->t; t; t = t->nxt)
+ { if (t->relevant&2)
+ return 1;
+
+ if (t->step && t->step->n)
+ switch (t->step->n->ntyp) {
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ if (AST_blockable(a, t->to))
+ { t->round = AST_Round;
+ t->relevant |= 2;
+ return 1;
+ }
+ /* else fall through */
+ default:
+ break;
+ }
+ else if (AST_blockable(a, t->to)) /* Unless */
+ { t->round = AST_Round;
+ t->relevant |= 2;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+AST_spread(AST *a, int s)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ f = fsm_tbl[s];
+
+ for (t = f->t; t; t = t->nxt)
+ { if (t->relevant&2)
+ continue;
+
+ if (t->step && t->step->n)
+ switch (t->step->n->ntyp) {
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ AST_spread(a, t->to);
+ /* fall thru */
+ default:
+ t->round = AST_Round;
+ t->relevant |= 2;
+ break;
+ }
+ else /* Unless */
+ { AST_spread(a, t->to);
+ t->round = AST_Round;
+ t->relevant |= 2;
+ }
+ }
+}
+
+static int
+AST_notrelevant(Lextok *n)
+{ Slicer *s;
+
+ for (s = rel_vars; s; s = s->nxt)
+ if (AST_mutual(s->n, n, 1))
+ return 0;
+ for (s = slicer; s; s = s->nxt)
+ if (AST_mutual(s->n, n, 1))
+ return 0;
+ return 1;
+}
+
+static int
+AST_withchan(Lextok *n)
+{
+ if (!n) return 0;
+ if (Sym_typ(n) == CHAN)
+ return 1;
+ return AST_withchan(n->lft) || AST_withchan(n->rgt);
+}
+
+static int
+AST_suspect(FSM_trans *t)
+{ FSM_use *u;
+ /* check for possible overkill */
+ if (!t || !t->step || !AST_withchan(t->step->n))
+ return 0;
+ for (u = t->Val[0]; u; u = u->nxt)
+ if (AST_notrelevant(u->n))
+ return 1;
+ return 0;
+}
+
+static void
+AST_shouldconsider(AST *a, int s)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ f = fsm_tbl[s];
+ for (t = f->t; t; t = t->nxt)
+ { if (t->step && t->step->n)
+ switch (t->step->n->ntyp) {
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ AST_shouldconsider(a, t->to);
+ break;
+ default:
+ AST_track(t->step->n, 0);
+/*
+ AST_track is called here for a blockable stmnt from which
+ a relevant stmnmt was shown to be reachable
+ for a condition this makes all USEs relevant
+ but for a channel operation it only makes the executability
+ relevant -- in those cases, parameters that aren't already
+ relevant may be replaceable with arbitrary tokens
+ */
+ if (AST_suspect(t))
+ { printf("spin: possibly redundant parameters in: ");
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ }
+ break;
+ }
+ else /* an Unless */
+ AST_shouldconsider(a, t->to);
+ }
+}
+
+static int
+FSM_critical(AST *a, int s)
+{ FSM_state *f;
+ FSM_trans *t;
+
+ /* is a 1-relevant stmnt reachable from this state? */
+
+ f = fsm_tbl[s];
+ if (f->seen)
+ goto done;
+ f->seen = 1;
+ f->cr = 0;
+ for (t = f->t; t; t = t->nxt)
+ if ((t->relevant&1)
+ || FSM_critical(a, t->to))
+ { f->cr = 1;
+
+ if (verbose&32)
+ { printf("\t\t\t\tcritical(%d) ", t->relevant);
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ }
+ break;
+ }
+#if 0
+ else {
+ if (verbose&32)
+ { printf("\t\t\t\tnot-crit ");
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ }
+ }
+#endif
+done:
+ return f->cr;
+}
+
+static void
+AST_ctrl(AST *a)
+{ FSM_state *f;
+ FSM_trans *t;
+ int hit;
+
+ /* add all blockable transitions
+ * from which relevant transitions can be reached
+ */
+ if (verbose&32)
+ printf("CTL -- %s\n", a->p->n->name);
+
+ /* 1 : mark all blockable edges */
+ for (f = a->fsm; f; f = f->nxt)
+ { if (!(f->scratch&2)) /* not part of irrelevant subgraph */
+ for (t = f->t; t; t = t->nxt)
+ { if (t->step && t->step->n)
+ switch (t->step->n->ntyp) {
+ case 'r':
+ case 's':
+ case 'c':
+ case ELSE:
+ t->round = AST_Round;
+ t->relevant |= 2; /* mark for next phases */
+ if (verbose&32)
+ { printf("\tpremark ");
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ }
+ break;
+ default:
+ break;
+ } } }
+
+ /* 2: keep only 2-marked stmnts from which 1-marked stmnts can be reached */
+ for (f = a->fsm; f; f = f->nxt)
+ { fsm_tbl[f->from] = f;
+ f->seen = 0; /* used in dfs from FSM_critical */
+ }
+ for (f = a->fsm; f; f = f->nxt)
+ { if (!FSM_critical(a, f->from))
+ for (t = f->t; t; t = t->nxt)
+ if (t->relevant&2)
+ { t->relevant &= ~2; /* clear mark */
+ if (verbose&32)
+ { printf("\t\tnomark ");
+ if (t->step && t->step->n)
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ } } }
+
+ /* 3 : lift marks across IF/DO etc. */
+ for (f = a->fsm; f; f = f->nxt)
+ { hit = 0;
+ for (t = f->t; t; t = t->nxt)
+ { if (t->step && t->step->n)
+ switch (t->step->n->ntyp) {
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ if (AST_blockable(a, t->to))
+ hit = 1;
+ break;
+ default:
+ break;
+ }
+ else if (AST_blockable(a, t->to)) /* Unless */
+ hit = 1;
+
+ if (hit) break;
+ }
+ if (hit) /* at least one outgoing trans can block */
+ for (t = f->t; t; t = t->nxt)
+ { t->round = AST_Round;
+ t->relevant |= 2; /* lift */
+ if (verbose&32)
+ { printf("\t\t\tliftmark ");
+ if (t->step && t->step->n)
+ comment(stdout, t->step->n, 0);
+ printf("\n");
+ }
+ AST_spread(a, t->to); /* and spread to all guards */
+ } }
+
+ /* 4: nodes with 2-marked out-edges contribute new slice criteria */
+ for (f = a->fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ if (t->relevant&2)
+ { AST_shouldconsider(a, f->from);
+ break; /* inner loop */
+ }
+}
+
+static void
+AST_control_dep(void)
+{ AST *a;
+
+ for (a = ast; a; a = a->nxt)
+ if (strcmp(a->p->n->name, ":never:") != 0
+ && strcmp(a->p->n->name, ":trace:") != 0
+ && strcmp(a->p->n->name, ":notrace:") != 0)
+ AST_ctrl(a);
+}
+
+static void
+AST_prelabel(void)
+{ AST *a;
+ FSM_state *f;
+ FSM_trans *t;
+
+ for (a = ast; a; a = a->nxt)
+ { if (strcmp(a->p->n->name, ":never:") != 0
+ && strcmp(a->p->n->name, ":trace:") != 0
+ && strcmp(a->p->n->name, ":notrace:") != 0)
+ for (f = a->fsm; f; f = f->nxt)
+ for (t = f->t; t; t = t->nxt)
+ { if (t->step
+ && t->step->n
+ && t->step->n->ntyp == ASSERT
+ )
+ { t->relevant |= 1;
+ } } }
+}
+
+static void
+AST_criteria(void)
+{ /*
+ * remote labels are handled separately -- by making
+ * sure they are not pruned away during optimization
+ */
+ AST_Changes = 1; /* to get started */
+ for (AST_Round = 1; slicer && AST_Changes; AST_Round++)
+ { AST_Changes = 0;
+ AST_data_dep();
+ AST_preserve(); /* moves processed vars from slicer to rel_vars */
+ AST_dominant(); /* mark data-irrelevant subgraphs */
+ AST_control_dep(); /* can add data deps, which add control deps */
+
+ if (verbose&32)
+ printf("\n\nROUND %d -- changes %d\n",
+ AST_Round, AST_Changes);
+ }
+}
+
+static void
+AST_alias_analysis(void) /* aliasing of promela channels */
+{ AST *a;
+
+ for (a = ast; a; a = a->nxt)
+ AST_sends(a); /* collect chan-names that are send across chans */
+
+ for (a = ast; a; a = a->nxt)
+ AST_para(a->p); /* aliasing of chans thru proctype parameters */
+
+ for (a = ast; a; a = a->nxt)
+ AST_other(a); /* chan params in asgns and recvs */
+
+ AST_trans(); /* transitive closure of alias table */
+
+ if (verbose&32)
+ AST_aliases(); /* show channel aliasing info */
+}
+
+void
+AST_slice(void)
+{ AST *a;
+ int spurious = 0;
+
+ if (!slicer)
+ { non_fatal("no slice criteria (or no claim) specified",
+ (char *) 0);
+ spurious = 1;
+ }
+ AST_dorelevant(); /* mark procs refered to in remote refs */
+
+ for (a = ast; a; a = a->nxt)
+ AST_def_use(a); /* compute standard def/use information */
+
+ AST_hidden(); /* parameter passing and local var inits */
+
+ AST_alias_analysis(); /* channel alias analysis */
+
+ AST_prelabel(); /* mark all 'assert(...)' stmnts as relevant */
+ AST_criteria(); /* process the slice criteria from
+ * asserts and from the never claim
+ */
+ if (!spurious || (verbose&32))
+ { spurious = 1;
+ for (a = ast; a; a = a->nxt)
+ { AST_dump(a); /* marked up result */
+ if (a->relevant&2) /* it printed something */
+ spurious = 0;
+ }
+ if (!AST_dump_rel() /* relevant variables */
+ && spurious)
+ printf("spin: no redundancies found (for given property)\n");
+ }
+ AST_suggestions();
+
+ if (verbose&32)
+ show_expl();
+}
+
+void
+AST_store(ProcList *p, int start_state)
+{ AST *n_ast;
+
+ if (strcmp(p->n->name, ":never:") != 0
+ && strcmp(p->n->name, ":trace:") != 0
+ && strcmp(p->n->name, ":notrace:") != 0)
+ { n_ast = (AST *) emalloc(sizeof(AST));
+ n_ast->p = p;
+ n_ast->i_st = start_state;
+ n_ast->relevant = 0;
+ n_ast->fsm = fsm;
+ n_ast->nxt = ast;
+ ast = n_ast;
+ }
+ fsm = (FSM_state *) 0; /* hide it from FSM_DEL */
+}
+
+static void
+AST_add_explicit(Lextok *d, Lextok *u)
+{ FSM_trans *e = (FSM_trans *) emalloc(sizeof(FSM_trans));
+
+ e->to = 0; /* or start_state ? */
+ e->relevant = 0; /* to be determined */
+ e->step = (Element *) 0; /* left blank */
+ e->Val[0] = e->Val[1] = (FSM_use *) 0;
+
+ cur_t = e;
+
+ def_use(u, USE);
+ def_use(d, DEF);
+
+ cur_t = (FSM_trans *) 0;
+
+ e->nxt = explicit;
+ explicit = e;
+}
+
+static void
+AST_fp1(char *s, Lextok *t, Lextok *f, int parno)
+{ Lextok *v;
+ int cnt;
+
+ if (!t) return;
+
+ if (t->ntyp == RUN)
+ { if (strcmp(t->sym->name, s) == 0)
+ for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
+ if (cnt == parno)
+ { AST_add_explicit(f, v->lft);
+ break;
+ }
+ } else
+ { AST_fp1(s, t->lft, f, parno);
+ AST_fp1(s, t->rgt, f, parno);
+ }
+}
+
+static void
+AST_mk1(char *s, Lextok *c, int parno)
+{ AST *a;
+ FSM_state *f;
+ FSM_trans *t;
+
+ /* concoct an extra FSM_trans *t with the asgn of
+ * formal par c to matching actual pars made explicit
+ */
+
+ for (a = ast; a; a = a->nxt) /* automata */
+ for (f = a->fsm; f; f = f->nxt) /* control states */
+ for (t = f->t; t; t = t->nxt) /* transitions */
+ { if (t->step)
+ AST_fp1(s, t->step->n, c, parno);
+ }
+}
+
+static void
+AST_par_init(void) /* parameter passing -- hidden assignments */
+{ AST *a;
+ Lextok *f, *t, *c;
+ int cnt;
+
+ for (a = ast; a; a = a->nxt)
+ { if (strcmp(a->p->n->name, ":never:") == 0
+ || strcmp(a->p->n->name, ":trace:") == 0
+ || strcmp(a->p->n->name, ":notrace:") == 0
+ || strcmp(a->p->n->name, ":init:") == 0)
+ continue; /* have no params */
+
+ cnt = 0;
+ for (f = a->p->p; f; f = f->rgt) /* types */
+ for (t = f->lft; t; t = t->rgt) /* formals */
+ { cnt++; /* formal par count */
+ c = (t->ntyp != ',')? t : t->lft; /* the formal parameter */
+ AST_mk1(a->p->n->name, c, cnt); /* all matching run statements */
+ } }
+}
+
+static void
+AST_var_init(void) /* initialized vars (not chans) - hidden assignments */
+{ Ordered *walk;
+ Lextok *x;
+ Symbol *sp;
+ AST *a;
+
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp
+ && !sp->context /* globals */
+ && sp->type != PROCTYPE
+ && sp->ini
+ && (sp->type != MTYPE || sp->ini->ntyp != CONST) /* not mtype defs */
+ && sp->ini->ntyp != CHAN)
+ { x = nn(ZN, TYPE, ZN, ZN);
+ x->sym = sp;
+ AST_add_explicit(x, sp->ini);
+ } }
+
+ for (a = ast; a; a = a->nxt)
+ { if (strcmp(a->p->n->name, ":never:") != 0
+ && strcmp(a->p->n->name, ":trace:") != 0
+ && strcmp(a->p->n->name, ":notrace:") != 0) /* claim has no locals */
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp
+ && sp->context
+ && strcmp(sp->context->name, a->p->n->name) == 0
+ && sp->Nid >= 0 /* not a param */
+ && sp->type != LABEL
+ && sp->ini
+ && sp->ini->ntyp != CHAN)
+ { x = nn(ZN, TYPE, ZN, ZN);
+ x->sym = sp;
+ AST_add_explicit(x, sp->ini);
+ } } }
+}
+
+static void
+show_expl(void)
+{ FSM_trans *t, *T;
+ FSM_use *u;
+
+ printf("\nExplicit List:\n");
+ for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
+ { for (t = T; t; t = t->nxt)
+ { if (!t->Val[0]) continue;
+ printf("%s", t->relevant?"*":" ");
+ printf("%3d", t->round);
+ for (u = t->Val[0]; u; u = u->nxt)
+ { printf("\t<");
+ AST_var(u->n, u->n->sym, 1);
+ printf(":%d>, ", u->special);
+ }
+ printf("\n");
+ }
+ printf("==\n");
+ }
+ printf("End\n");
+}
+
+static void
+AST_hidden(void) /* reveal all hidden assignments */
+{
+ AST_par_init();
+ expl_par = explicit;
+ explicit = (FSM_trans *) 0;
+
+ AST_var_init();
+ expl_var = explicit;
+ explicit = (FSM_trans *) 0;
+}
+
+#define BPW (8*sizeof(ulong)) /* bits per word */
+
+static int
+bad_scratch(FSM_state *f, int upto)
+{ FSM_trans *t;
+#if 0
+ 1. all internal branch-points have else-s
+ 2. all non-branchpoints have non-blocking out-edge
+ 3. all internal edges are non-relevant
+ subgraphs like this need NOT contribute control-dependencies
+#endif
+
+ if (!f->seen
+ || (f->scratch&4))
+ return 0;
+
+ if (f->scratch&8)
+ return 1;
+
+ f->scratch |= 4;
+
+ if (verbose&32) printf("X[%d:%d:%d] ", f->from, upto, f->scratch);
+
+ if (f->scratch&1)
+ { if (verbose&32)
+ printf("\tbad scratch: %d\n", f->from);
+bad: f->scratch &= ~4;
+ /* f->scratch |= 8; wrong */
+ return 1;
+ }
+
+ if (f->from != upto)
+ for (t = f->t; t; t = t->nxt)
+ if (bad_scratch(fsm_tbl[t->to], upto))
+ goto bad;
+
+ return 0;
+}
+
+static void
+mark_subgraph(FSM_state *f, int upto)
+{ FSM_trans *t;
+
+ if (f->from == upto
+ || !f->seen
+ || (f->scratch&2))
+ return;
+
+ f->scratch |= 2;
+
+ for (t = f->t; t; t = t->nxt)
+ mark_subgraph(fsm_tbl[t->to], upto);
+}
+
+static void
+AST_pair(AST *a, FSM_state *h, int y)
+{ Pair *p;
+
+ for (p = a->pairs; p; p = p->nxt)
+ if (p->h == h
+ && p->b == y)
+ return;
+
+ p = (Pair *) emalloc(sizeof(Pair));
+ p->h = h;
+ p->b = y;
+ p->nxt = a->pairs;
+ a->pairs = p;
+}
+
+static void
+AST_checkpairs(AST *a)
+{ Pair *p;
+
+ for (p = a->pairs; p; p = p->nxt)
+ { if (verbose&32)
+ printf(" inspect pair %d %d\n", p->b, p->h->from);
+ if (!bad_scratch(p->h, p->b)) /* subgraph is clean */
+ { if (verbose&32)
+ printf("subgraph: %d .. %d\n", p->b, p->h->from);
+ mark_subgraph(p->h, p->b);
+ }
+ }
+}
+
+static void
+subgraph(AST *a, FSM_state *f, int out)
+{ FSM_state *h;
+ int i, j;
+ ulong *g;
+#if 0
+ reverse dominance suggests that this is a possible
+ entry and exit node for a proper subgraph
+#endif
+ h = fsm_tbl[out];
+
+ i = f->from / BPW;
+ j = f->from % BPW;
+ g = h->mod;
+
+ if (verbose&32)
+ printf("possible pair %d %d -- %d\n",
+ f->from, h->from, (g[i]&(1<<j))?1:0);
+
+ if (g[i]&(1<<j)) /* also a forward dominance pair */
+ AST_pair(a, h, f->from); /* record this pair */
+}
+
+static void
+act_dom(AST *a)
+{ FSM_state *f;
+ FSM_trans *t;
+ int i, j, cnt;
+
+ for (f = a->fsm; f; f = f->nxt)
+ { if (!f->seen) continue;
+#if 0
+ f->from is the exit-node of a proper subgraph, with
+ the dominator its entry-node, if:
+ a. this node has more than 1 reachable predecessor
+ b. the dominator has more than 1 reachable successor
+ (need reachability - in case of reverse dominance)
+ d. the dominator is reachable, and not equal to this node
+#endif
+ for (t = f->p, i = 0; t; t = t->nxt)
+ i += fsm_tbl[t->to]->seen;
+ if (i <= 1) continue; /* a. */
+
+ for (cnt = 1; cnt < a->nstates; cnt++) /* 0 is endstate */
+ { if (cnt == f->from
+ || !fsm_tbl[cnt]->seen)
+ continue; /* c. */
+
+ i = cnt / BPW;
+ j = cnt % BPW;
+ if (!(f->dom[i]&(1<<j)))
+ continue;
+
+ for (t = fsm_tbl[cnt]->t, i = 0; t; t = t->nxt)
+ i += fsm_tbl[t->to]->seen;
+ if (i <= 1)
+ continue; /* b. */
+
+ if (f->mod) /* final check in 2nd phase */
+ subgraph(a, f, cnt); /* possible entry-exit pair */
+ }
+ }
+}
+
+static void
+reachability(AST *a)
+{ FSM_state *f;
+
+ for (f = a->fsm; f; f = f->nxt)
+ f->seen = 0; /* clear */
+ AST_dfs(a, a->i_st, 0); /* mark 'seen' */
+}
+
+static int
+see_else(FSM_state *f)
+{ FSM_trans *t;
+
+ for (t = f->t; t; t = t->nxt)
+ { if (t->step
+ && t->step->n)
+ switch (t->step->n->ntyp) {
+ case ELSE:
+ return 1;
+ case IF:
+ case DO:
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ if (see_else(fsm_tbl[t->to]))
+ return 1;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
+is_guard(FSM_state *f)
+{ FSM_state *g;
+ FSM_trans *t;
+
+ for (t = f->p; t; t = t->nxt)
+ { g = fsm_tbl[t->to];
+ if (!g->seen)
+ continue;
+
+ if (t->step
+ && t->step->n)
+ switch(t->step->n->ntyp) {
+ case IF:
+ case DO:
+ return 1;
+ case ATOMIC:
+ case NON_ATOMIC:
+ case D_STEP:
+ if (is_guard(g))
+ return 1;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static void
+curtail(AST *a)
+{ FSM_state *f, *g;
+ FSM_trans *t;
+ int i, haselse, isrel, blocking;
+#if 0
+ mark nodes that do not satisfy these requirements:
+ 1. all internal branch-points have else-s
+ 2. all non-branchpoints have non-blocking out-edge
+ 3. all internal edges are non-data-relevant
+#endif
+ if (verbose&32)
+ printf("Curtail %s:\n", a->p->n->name);
+
+ for (f = a->fsm; f; f = f->nxt)
+ { if (!f->seen
+ || (f->scratch&(1|2)))
+ continue;
+
+ isrel = haselse = i = blocking = 0;
+
+ for (t = f->t; t; t = t->nxt)
+ { g = fsm_tbl[t->to];
+
+ isrel |= (t->relevant&1); /* data relevant */
+ i += g->seen;
+
+ if (t->step
+ && t->step->n)
+ { switch (t->step->n->ntyp) {
+ case IF:
+ case DO:
+ haselse |= see_else(g);
+ break;
+ case 'c':
+ case 's':
+ case 'r':
+ blocking = 1;
+ break;
+ } } }
+#if 0
+ if (verbose&32)
+ printf("prescratch %d -- %d %d %d %d -- %d\n",
+ f->from, i, isrel, blocking, haselse, is_guard(f));
+#endif
+ if (isrel /* 3. */
+ || (i == 1 && blocking) /* 2. */
+ || (i > 1 && !haselse)) /* 1. */
+ { if (!is_guard(f))
+ { f->scratch |= 1;
+ if (verbose&32)
+ printf("scratch %d -- %d %d %d %d\n",
+ f->from, i, isrel, blocking, haselse);
+ }
+ }
+ }
+}
+
+static void
+init_dom(AST *a)
+{ FSM_state *f;
+ int i, j, cnt;
+#if 0
+ (1) D(s0) = {s0}
+ (2) for s in S - {s0} do D(s) = S
+#endif
+
+ for (f = a->fsm; f; f = f->nxt)
+ { if (!f->seen) continue;
+
+ f->dom = (ulong *)
+ emalloc(a->nwords * sizeof(ulong));
+
+ if (f->from == a->i_st)
+ { i = a->i_st / BPW;
+ j = a->i_st % BPW;
+ f->dom[i] = (1<<j); /* (1) */
+ } else /* (2) */
+ { for (i = 0; i < a->nwords; i++)
+ f->dom[i] = (ulong) ~0; /* all 1's */
+
+ if (a->nstates % BPW)
+ for (i = (a->nstates % BPW); i < (int) BPW; i++)
+ f->dom[a->nwords-1] &= ~(1<<i); /* clear tail */
+
+ for (cnt = 0; cnt < a->nstates; cnt++)
+ if (!fsm_tbl[cnt]->seen)
+ { i = cnt / BPW;
+ j = cnt % BPW;
+ f->dom[i] &= ~(1<<j);
+ } } }
+}
+
+static int
+dom_perculate(AST *a, FSM_state *f)
+{ static ulong *ndom = (ulong *) 0;
+ static int on = 0;
+ int i, j, cnt = 0;
+ FSM_state *g;
+ FSM_trans *t;
+
+ if (on < a->nwords)
+ { on = a->nwords;
+ ndom = (ulong *)
+ emalloc(on * sizeof(ulong));
+ }
+
+ for (i = 0; i < a->nwords; i++)
+ ndom[i] = (ulong) ~0;
+
+ for (t = f->p; t; t = t->nxt) /* all reachable predecessors */
+ { g = fsm_tbl[t->to];
+ if (g->seen)
+ for (i = 0; i < a->nwords; i++)
+ ndom[i] &= g->dom[i]; /* (5b) */
+ }
+
+ i = f->from / BPW;
+ j = f->from % BPW;
+ ndom[i] |= (1<<j); /* (5a) */
+
+ for (i = 0; i < a->nwords; i++)
+ if (f->dom[i] != ndom[i])
+ { cnt++;
+ f->dom[i] = ndom[i];
+ }
+
+ return cnt;
+}
+
+static void
+dom_forward(AST *a)
+{ FSM_state *f;
+ int cnt;
+
+ init_dom(a); /* (1,2) */
+ do {
+ cnt = 0;
+ for (f = a->fsm; f; f = f->nxt)
+ { if (f->seen
+ && f->from != a->i_st) /* (4) */
+ cnt += dom_perculate(a, f); /* (5) */
+ }
+ } while (cnt); /* (3) */
+ dom_perculate(a, fsm_tbl[a->i_st]);
+}
+
+static void
+AST_dominant(void)
+{ FSM_state *f;
+ FSM_trans *t;
+ AST *a;
+ int oi;
+ static FSM_state no_state;
+#if 0
+ find dominators
+ Aho, Sethi, & Ullman, Compilers - principles, techniques, and tools
+ Addison-Wesley, 1986, p.671.
+
+ (1) D(s0) = {s0}
+ (2) for s in S - {s0} do D(s) = S
+
+ (3) while any D(s) changes do
+ (4) for s in S - {s0} do
+ (5) D(s) = {s} union with intersection of all D(p)
+ where p are the immediate predecessors of s
+
+ the purpose is to find proper subgraphs
+ (one entry node, one exit node)
+#endif
+ if (AST_Round == 1) /* computed once, reused in every round */
+ for (a = ast; a; a = a->nxt)
+ { a->nstates = 0;
+ for (f = a->fsm; f; f = f->nxt)
+ { a->nstates++; /* count */
+ fsm_tbl[f->from] = f; /* fast lookup */
+ f->scratch = 0; /* clear scratch marks */
+ }
+ for (oi = 0; oi < a->nstates; oi++)
+ if (!fsm_tbl[oi])
+ fsm_tbl[oi] = &no_state;
+
+ a->nwords = (a->nstates + BPW - 1) / BPW; /* round up */
+
+ if (verbose&32)
+ { printf("%s (%d): ", a->p->n->name, a->i_st);
+ printf("states=%d (max %d), words = %d, bpw %d, overflow %d\n",
+ a->nstates, o_max, a->nwords,
+ (int) BPW, (int) (a->nstates % BPW));
+ }
+
+ reachability(a);
+ dom_forward(a); /* forward dominance relation */
+
+ curtail(a); /* mark ineligible edges */
+ for (f = a->fsm; f; f = f->nxt)
+ { t = f->p;
+ f->p = f->t;
+ f->t = t; /* invert edges */
+
+ f->mod = f->dom;
+ f->dom = (ulong *) 0;
+ }
+ oi = a->i_st;
+ if (fsm_tbl[0]->seen) /* end-state reachable - else leave it */
+ a->i_st = 0; /* becomes initial state */
+
+ dom_forward(a); /* reverse dominance -- don't redo reachability! */
+ act_dom(a); /* mark proper subgraphs, if any */
+ AST_checkpairs(a); /* selectively place 2 scratch-marks */
+
+ for (f = a->fsm; f; f = f->nxt)
+ { t = f->p;
+ f->p = f->t;
+ f->t = t; /* restore */
+ }
+ a->i_st = oi; /* restore */
+ } else
+ for (a = ast; a; a = a->nxt)
+ { for (f = a->fsm; f; f = f->nxt)
+ { fsm_tbl[f->from] = f;
+ f->scratch &= 1; /* preserve 1-marks */
+ }
+ for (oi = 0; oi < a->nstates; oi++)
+ if (!fsm_tbl[oi])
+ fsm_tbl[oi] = &no_state;
+
+ curtail(a); /* mark ineligible edges */
+
+ for (f = a->fsm; f; f = f->nxt)
+ { t = f->p;
+ f->p = f->t;
+ f->t = t; /* invert edges */
+ }
+
+ AST_checkpairs(a); /* recompute 2-marks */
+
+ for (f = a->fsm; f; f = f->nxt)
+ { t = f->p;
+ f->p = f->t;
+ f->t = t; /* restore */
+ } }
+}
--- /dev/null
+/***** spin: pangen6.h *****/
+
+/* Copyright (c) 2006-2007 by the California Institute of Technology. */
+/* ALL RIGHTS RESERVED. United States Government Sponsorship acknowledged */
+/* Supporting routines for a multi-core extension of the SPIN software */
+/* Developed as part of Reliable Software Engineering Project ESAS/6G */
+/* Like all SPIN Software this software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Any commercial use must be negotiated with the Office of Technology */
+/* Transfer at the California Institute of Technology. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Bug-reports and/or questions can be send to: bugs@spinroot.com */
+
+static char *Code2c[] = { /* multi-core option - Spin 5.0 and later */
+ "#if NCORE>1",
+ "#if defined(WIN32) || defined(WIN64)",
+ "#ifndef _CONSOLE",
+ " #define _CONSOLE",
+ "#endif",
+ " #ifdef WIN64",
+ "#undef long",
+ " #endif",
+ "#include <windows.h>",
+ "",
+ " #ifdef WIN64",
+ " #define long long long",
+ " #endif",
+ "#else",
+ "#include <sys/ipc.h>",
+ "#include <sys/sem.h>",
+ "#include <sys/shm.h>",
+ "#endif",
+ "",
+ "/* code common to cygwin/linux and win32/win64: */",
+ "",
+ "#ifdef VERBOSE",
+ " #define VVERBOSE (1)",
+ "#else",
+ " #define VVERBOSE (0)",
+ "#endif",
+ "",
+ "/* the following values must be larger than 256 and must fit in an int */",
+ "#define QUIT 1024 /* terminate now command */",
+ "#define QUERY 512 /* termination status query message */",
+ "#define QUERY_F 513 /* query failed, cannot quit */",
+ "",
+ "#define GN_FRAMES (int) (GWQ_SIZE / (double) sizeof(SM_frame))",
+ "#define LN_FRAMES (int) (LWQ_SIZE / (double) sizeof(SM_frame))",
+ "",
+ "#ifndef VMAX",
+ " #define VMAX VECTORSZ",
+ "#endif",
+ "#ifndef PMAX",
+ " #define PMAX 64",
+ "#endif",
+ "#ifndef QMAX",
+ " #define QMAX 64",
+ "#endif",
+ "",
+ "#if VECTORSZ>32000",
+ " #define OFFT int",
+ "#else",
+ " #define OFFT short",
+ "#endif",
+ "",
+ "#ifdef SET_SEG_SIZE",
+ " /* no longer usefule -- being recomputed for local heap size anyway */",
+ " double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.);",
+ "#else",
+ " double SEG_SIZE = (1048576.*1024.); /* 1GB default shared memory pool segments */",
+ "#endif",
+ "",
+ "double LWQ_SIZE = 0.; /* initialized in main */",
+ "",
+ "#ifdef SET_WQ_SIZE",
+ " #ifdef NGQ",
+ " #warning SET_WQ_SIZE applies to global queue -- ignored",
+ " double GWQ_SIZE = 0.;",
+ " #else",
+ " double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.);",
+ " /* must match the value in pan_proxy.c, if used */",
+ " #endif",
+ "#else",
+ " #ifdef NGQ",
+ " double GWQ_SIZE = 0.;",
+ " #else",
+ " double GWQ_SIZE = (128.*1048576.); /* 128 MB default queue sizes */",
+ " #endif",
+ "#endif",
+ "",
+ "/* Crash Detection Parameters */",
+ "#ifndef ONESECOND",
+ " #define ONESECOND (1<<25)", /* name is somewhat of a misnomer */
+ "#endif",
+ "#ifndef SHORT_T",
+ " #define SHORT_T (0.1)",
+ "#endif",
+ "#ifndef LONG_T",
+ " #define LONG_T (600)",
+ "#endif",
+ "",
+ "double OneSecond = (double) (ONESECOND); /* waiting for a free slot -- checks crash */",
+ "double TenSeconds = 10. * (ONESECOND); /* waiting for a lock -- check for a crash */",
+ "",
+ "/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */",
+ "double Delay = ((double) SHORT_T) * (ONESECOND); /* termination detection trigger */",
+ "double OneHour = ((double) LONG_T) * (ONESECOND); /* timeout termination detection */",
+ "",
+ "typedef struct SM_frame SM_frame;",
+ "typedef struct SM_results SM_results;",
+ "typedef struct sh_Allocater sh_Allocater;",
+ "",
+ "struct SM_frame { /* about 6K per slot */",
+ " volatile int m_vsize; /* 0 means free slot */",
+ " volatile int m_boq; /* >500 is a control message */",
+ "#ifdef FULL_TRAIL",
+ " volatile struct Stack_Tree *m_stack; /* ptr to previous state */",
+ "#endif",
+ " volatile uchar m_tau;",
+ " volatile uchar m_o_pm;",
+ " volatile int nr_handoffs; /* to compute real_depth */",
+ " volatile char m_now [VMAX];",
+ " volatile char m_Mask [(VMAX + 7)/8];",
+ " volatile OFFT m_p_offset[PMAX];",
+ " volatile OFFT m_q_offset[QMAX];",
+ " volatile uchar m_p_skip [PMAX];",
+ " volatile uchar m_q_skip [QMAX];",
+ "#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+ " volatile uchar m_c_stack [StackSize];",
+ /* captures contents of c_stack[] for unmatched objects */
+ "#endif",
+ "};",
+ "",
+ "int proxy_pid; /* id of proxy if nonzero -- receive half */",
+ "int store_proxy_pid;",
+ "short remote_party;",
+ "int proxy_pid_snd; /* id of proxy if nonzero -- send half */",
+ "char o_cmdline[512]; /* to pass options to children */",
+ "",
+ "int iamin[CS_NR+NCORE]; /* non-shared */",
+ "",
+"#if defined(WIN32) || defined(WIN64)",
+ "int tas(volatile LONG *);",
+ "",
+ "HANDLE proxy_handle_snd; /* for Windows Create and Terminate */",
+ "",
+ "struct sh_Allocater { /* shared memory for states */",
+ " volatile char *dc_arena; /* to allocate states from */",
+ " volatile long pattern; /* to detect overruns */",
+ " volatile long dc_size; /* nr of bytes left */",
+ " volatile void *dc_start; /* where memory segment starts */",
+ " volatile void *dc_id; /* to attach, detach, remove shared memory segments */",
+ " volatile sh_Allocater *nxt; /* linked list of pools */",
+ "};",
+ "DWORD worker_pids[NCORE]; /* root mem of pids of all workers created */",
+ "HANDLE worker_handles[NCORE]; /* for windows Create and Terminate */",
+ "void * shmid [NR_QS]; /* return value from CreateFileMapping */",
+ "void * shmid_M; /* shared mem for state allocation in hashtable */",
+ "",
+ "#ifdef SEP_STATE",
+ " void *shmid_X;",
+ "#else",
+ " void *shmid_S; /* shared bitstate arena or hashtable */",
+ "#endif",
+"#else",
+ "int tas(volatile int *);",
+ "",
+ "struct sh_Allocater { /* shared memory for states */",
+ " volatile char *dc_arena; /* to allocate states from */",
+ " volatile long pattern; /* to detect overruns */",
+ " volatile long dc_size; /* nr of bytes left */",
+ " volatile char *dc_start; /* where memory segment starts */",
+ " volatile int dc_id; /* to attach, detach, remove shared memory segments */",
+ " volatile sh_Allocater *nxt; /* linked list of pools */",
+ "};",
+ "",
+ "int worker_pids[NCORE]; /* root mem of pids of all workers created */",
+ "int shmid [NR_QS]; /* return value from shmget */",
+ "int nibis = 0; /* set after shared mem has been released */",
+ "int shmid_M; /* shared mem for state allocation in hashtable */",
+ "#ifdef SEP_STATE",
+ " long shmid_X;",
+ "#else",
+ " int shmid_S; /* shared bitstate arena or hashtable */",
+ " volatile sh_Allocater *first_pool; /* of shared state memory */",
+ " volatile sh_Allocater *last_pool;",
+ "#endif", /* SEP_STATE */
+"#endif", /* WIN32 || WIN64 */
+ "",
+ "struct SM_results { /* for shuttling back final stats */",
+ " volatile int m_vsize; /* avoid conflicts with frames */",
+ " volatile int m_boq; /* these 2 fields are not written in record_info */",
+ " /* probably not all fields really need to be volatile */",
+ " volatile double m_memcnt;",
+ " volatile double m_nstates;",
+ " volatile double m_truncs;",
+ " volatile double m_truncs2;",
+ " volatile double m_nShadow;",
+ " volatile double m_nlinks;",
+ " volatile double m_ngrabs;",
+ " volatile double m_nlost;",
+ " volatile double m_hcmp;",
+ " volatile double m_frame_wait;",
+ " volatile int m_hmax;",
+ " volatile int m_svmax;",
+ " volatile int m_smax;",
+ " volatile int m_mreached;",
+ " volatile int m_errors;",
+ " volatile int m_VMAX;",
+ " volatile short m_PMAX;",
+ " volatile short m_QMAX;",
+ " volatile uchar m_R; /* reached info for all proctypes */",
+ "};",
+ "",
+ "int core_id = 0; /* internal process nr, to know which q to use */",
+ "unsigned long nstates_put = 0; /* statistics */",
+ "unsigned long nstates_get = 0;",
+ "int query_in_progress = 0; /* termination detection */",
+ "",
+ "double free_wait = 0.; /* waiting for a free frame */",
+ "double frame_wait = 0.; /* waiting for a full frame */",
+ "double lock_wait = 0.; /* waiting for access to cs */",
+ "double glock_wait[3]; /* waiting for access to global lock */",
+ "",
+ "char *sprefix = \"rst\";",
+ "uchar was_interrupted, issued_kill, writing_trail;",
+ "",
+ "static SM_frame cur_Root; /* current root, to be safe with error trails */",
+ "",
+ "SM_frame *m_workq [NR_QS]; /* per cpu work queues + global q */",
+ "char *shared_mem[NR_QS]; /* return value from shmat */",
+ "#ifdef SEP_HEAP",
+ "char *my_heap;",
+ "long my_size;",
+ "#endif",
+ "volatile sh_Allocater *dc_shared; /* assigned at initialization */",
+ "",
+ "static int vmax_seen, pmax_seen, qmax_seen;",
+ "static double gq_tries, gq_hasroom, gq_hasnoroom;",
+ "",
+ "volatile int *prfree;", /* [NCORE] */
+ "volatile int *prfull;", /* [NCORE] */
+ "volatile int *prcnt;", /* [NCORE] */
+ "volatile int *prmax;", /* [NCORE] */
+ "",
+ "volatile int *sh_lock; /* mutual exclusion locks - in shared memory */",
+ "volatile double *is_alive; /* to detect when processes crash */",
+ "volatile int *grfree, *grfull, *grcnt, *grmax; /* access to shared global q */",
+ "volatile double *gr_readmiss, *gr_writemiss;",
+ "static int lrfree; /* used for temporary recording of slot */",
+ "static int dfs_phase2;",
+ "",
+ "void mem_put(int); /* handoff state to other cpu */",
+ "void mem_put_acc(void); /* liveness mode */",
+ "void mem_get(void); /* get state from work queue */",
+ "void sudden_stop(char *);",
+ "#if 0",
+ "void enter_critical(int);",
+ "void leave_critical(int);",
+ "#endif",
+ "",
+ "void",
+ "record_info(SM_results *r)",
+ "{ int i;",
+ " uchar *ptr;",
+ "",
+ "#ifdef SEP_STATE",
+ " if (0)",
+ " { cpu_printf(\"nstates %%g nshadow %%g -- memory %%-6.3f Mb\\n\",",
+ " nstates, nShadow, memcnt/(1048576.));",
+ " }",
+ " r->m_memcnt = 0;",
+ "#else",
+ " #ifdef BITSTATE",
+ " r->m_memcnt = 0; /* it's shared */",
+ " #endif",
+ " r->m_memcnt = memcnt;",
+ "#endif",
+ " if (a_cycles && core_id == 1)",
+ " { r->m_nstates = nstates;",
+ " r->m_nShadow = nstates;",
+ " } else",
+ " { r->m_nstates = nstates;",
+ " r->m_nShadow = nShadow;",
+ " }",
+ " r->m_truncs = truncs;",
+ " r->m_truncs2 = truncs2;",
+ " r->m_nlinks = nlinks;",
+ " r->m_ngrabs = ngrabs;",
+ " r->m_nlost = nlost;",
+ " r->m_hcmp = hcmp;",
+ " r->m_frame_wait = frame_wait;",
+ " r->m_hmax = hmax;",
+ " r->m_svmax = svmax;",
+ " r->m_smax = smax;",
+ " r->m_mreached = mreached;",
+ " r->m_errors = errors;",
+ " r->m_VMAX = vmax_seen;",
+ " r->m_PMAX = (short) pmax_seen;",
+ " r->m_QMAX = (short) qmax_seen;",
+ " ptr = (uchar *) &(r->m_R);",
+ " for (i = 0; i <= _NP_; i++) /* all proctypes */",
+ " { memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar));",
+ " ptr += NrStates[i]*sizeof(uchar);",
+ " }",
+ " if (verbose>1)",
+ " { cpu_printf(\"Put Results nstates %%g (sz %%d)\\n\", nstates, ptr - &(r->m_R));",
+ " }",
+ "}",
+ "",
+ "void snapshot(void);",
+ "",
+ "void",
+ "retrieve_info(SM_results *r)",
+ "{ int i, j;",
+ " volatile uchar *ptr;",
+ "",
+ " snapshot(); /* for a final report */",
+ "",
+ " enter_critical(GLOBAL_LOCK);",
+ "#ifdef SEP_HEAP",
+ " if (verbose)",
+ " { printf(\"cpu%%d: local heap-left %%ld KB (%%d MB)\\n\",",
+ " core_id, (int) (my_size/1024), (int) (my_size/1048576));",
+ " }",
+ "#endif",
+ " if (verbose && core_id == 0)",
+ " { printf(\"qmax: \");",
+ " for (i = 0; i < NCORE; i++)",
+ " { printf(\"%%d \", prmax[i]);",
+ " }",
+ "#ifndef NGQ",
+ " printf(\"G: %%d\", *grmax);",
+ "#endif",
+ " printf(\"\\n\");",
+ " }",
+ " leave_critical(GLOBAL_LOCK);",
+ "",
+ " memcnt += r->m_memcnt;",
+ " nstates += r->m_nstates;",
+ " nShadow += r->m_nShadow;",
+ " truncs += r->m_truncs;",
+ " truncs2 += r->m_truncs2;",
+ " nlinks += r->m_nlinks;",
+ " ngrabs += r->m_ngrabs;",
+ " nlost += r->m_nlost;",
+ " hcmp += r->m_hcmp;",
+ " /* frame_wait += r->m_frame_wait; */",
+ " errors += r->m_errors;",
+ "",
+ " if (hmax < r->m_hmax) hmax = r->m_hmax;",
+ " if (svmax < r->m_svmax) svmax = r->m_svmax;",
+ " if (smax < r->m_smax) smax = r->m_smax;",
+ " if (mreached < r->m_mreached) mreached = r->m_mreached;",
+ "",
+ " if (vmax_seen < r->m_VMAX) vmax_seen = r->m_VMAX;",
+ " if (pmax_seen < (int) r->m_PMAX) pmax_seen = (int) r->m_PMAX;",
+ " if (qmax_seen < (int) r->m_QMAX) qmax_seen = (int) r->m_QMAX;",
+ "",
+ " ptr = &(r->m_R);",
+ " for (i = 0; i <= _NP_; i++) /* all proctypes */",
+ " { for (j = 0; j < NrStates[i]; j++)",
+ " { if (*(ptr + j) != 0)",
+ " { reached[i][j] = 1;",
+ " } }",
+ " ptr += NrStates[i]*sizeof(uchar);",
+ " }",
+ " if (verbose>1)",
+ " { cpu_printf(\"Got Results (%%d)\\n\", ptr - &(r->m_R));",
+ " snapshot();",
+ " }",
+ "}",
+ "",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ "static void",
+ "rm_shared_segments(void)",
+ "{ int m;",
+ " volatile sh_Allocater *nxt_pool;",
+ " /*",
+ " * mark all shared memory segments for removal ",
+ " * the actual removes wont happen intil last process dies or detaches",
+ " * the shmctl calls can return -1 if not all procs have detached yet",
+ " */",
+ " for (m = 0; m < NR_QS; m++) /* +1 for global q */",
+ " { if (shmid[m] != -1)",
+ " { (void) shmctl(shmid[m], IPC_RMID, NULL);",
+ " } }",
+ "#ifdef SEP_STATE",
+ " if (shmid_M != -1)",
+ " { (void) shmctl(shmid_M, IPC_RMID, NULL);",
+ " }",
+ "#else",
+ " if (shmid_S != -1)",
+ " { (void) shmctl(shmid_S, IPC_RMID, NULL);",
+ " }",
+ " for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)",
+ " { shmid_M = (int) (last_pool->dc_id);",
+ " nxt_pool = last_pool->nxt; /* as a pre-caution only */",
+ " if (shmid_M != -1)",
+ " { (void) shmctl(shmid_M, IPC_RMID, NULL);",
+ " } }",
+ "#endif",
+ "}",
+ "#endif",
+ "",
+ "void",
+ "sudden_stop(char *s)",
+ "{ char b[64];",
+ " int i;",
+ "",
+ " printf(\"cpu%%d: stop - %%s\\n\", core_id, s);",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ " if (proxy_pid != 0)",
+ " { rm_shared_segments();",
+ " }",
+ "#endif",
+ " if (search_terminated != NULL)",
+ " { if (*search_terminated != 0)",
+ " { if (verbose)",
+ " { printf(\"cpu%%d: termination initiated (%%d)\\n\",",
+ " core_id, *search_terminated);",
+ " }",
+ " } else",
+ " { if (verbose)",
+ " { printf(\"cpu%%d: initiated termination\\n\", core_id);",
+ " }",
+ " *search_terminated |= 8; /* sudden_stop */",
+ " }",
+ " if (core_id == 0)",
+ " { if (((*search_terminated) & 4) /* uerror in one of the cpus */",
+ " && !((*search_terminated) & (8|32|128|256))) /* abnormal stop */",
+ " { if (errors == 0) errors++; /* we know there is at least 1 */",
+ " }",
+ " wrapup(); /* incomplete stats, but at least something */",
+ " }",
+ " return;",
+ " } /* else: should rarely happen, take more drastic measures */",
+ "",
+ " if (core_id == 0) /* local root process */",
+ " { for (i = 1; i < NCORE; i++) /* not for 0 of course */",
+ " {",
+ "#if defined(WIN32) || defined(WIN64)",
+ " DWORD dwExitCode = 0;",
+ " GetExitCodeProcess(worker_handles[i], &dwExitCode);",
+ " if (dwExitCode == STILL_ACTIVE)",
+ " { TerminateProcess(worker_handles[i], 0);",
+ " }",
+ " printf(\"cpu0: terminate %%d %%d\\n\",",
+ " worker_pids[i], (dwExitCode == STILL_ACTIVE));",
+ "#else",
+ " sprintf(b, \"kill -%%d %%d\", SIGKILL, worker_pids[i]);",
+ " system(b); /* if this is a proxy: receive half */",
+ " printf(\"cpu0: %%s\\n\", b);",
+ "#endif",
+ " }",
+ " issued_kill++;",
+ " } else",
+ " { /* on WIN32/WIN64 -- these merely kills the root process... */",
+ " if (was_interrupted == 0)", /* 2=SIGINT to root to trigger stop */
+ " { sprintf(b, \"kill -%%d %%d\", SIGINT, worker_pids[0]);",
+ " system(b); /* warn the root process */",
+ " printf(\"cpu%%d: %%s\\n\", core_id, b);",
+ " issued_kill++;",
+ " } }",
+ "}",
+ "",
+ "#define iam_alive() is_alive[core_id]++", /* for crash detection */
+ "",
+ "extern int crash_test(double);",
+ "extern void crash_reset(void);",
+ "",
+ "int",
+ "someone_crashed(int wait_type)",
+ "{ static double last_value = 0.0;",
+ " static int count = 0;",
+ "",
+ " if (search_terminated == NULL",
+ " || *search_terminated != 0)",
+ " {",
+ " if (!(*search_terminated & (8|32|128|256)))",
+ " { if (count++ < 100*NCORE)",
+ " { return 0;",
+ " } }",
+ " return 1;",
+ " }",
+ " /* check left neighbor only */",
+ " if (last_value == is_alive[(core_id + NCORE - 1) %% NCORE])",
+ " { if (count++ >= 100) /* to avoid unnecessary checks */",
+ " { return 1;",
+ " }",
+ " return 0;",
+ " }",
+ " last_value = is_alive[(core_id + NCORE - 1) %% NCORE];",
+ " count = 0;",
+ " crash_reset();",
+ " return 0;",
+ "}",
+ "",
+ "void",
+ "sleep_report(void)",
+ "{",
+ " enter_critical(GLOBAL_LOCK);",
+ " if (verbose)",
+ " {",
+ "#ifdef NGQ",
+ " printf(\"cpu%%d: locks: global %%g\\tother %%g\\t\",",
+ " core_id, glock_wait[0], lock_wait - glock_wait[0]);",
+ "#else",
+ " printf(\"cpu%%d: locks: GL %%g, RQ %%g, WQ %%g, HT %%g\\t\",",
+ " core_id, glock_wait[0], glock_wait[1], glock_wait[2],",
+ " lock_wait - glock_wait[0] - glock_wait[1] - glock_wait[2]);",
+ "#endif",
+ " printf(\"waits: states %%g slots %%g\\n\", frame_wait, free_wait);",
+ "#ifndef NGQ",
+ " printf(\"cpu%%d: gq [tries %%g, room %%g, noroom %%g]\\n\", core_id, gq_tries, gq_hasroom, gq_hasnoroom);",
+ " if (core_id == 0 && (*gr_readmiss >= 1.0 || *gr_readmiss >= 1.0 || *grcnt != 0))",
+ " printf(\"cpu0: gq [readmiss: %%g, writemiss: %%g cnt %%d]\\n\", *gr_readmiss, *gr_writemiss, *grcnt);",
+ "#endif",
+ " }",
+ " if (free_wait > 1000000.)",
+ " #ifndef NGQ",
+ " if (!a_cycles)",
+ " { printf(\"hint: this search may be faster with a larger work-queue\\n\");",
+ " printf(\" (-DSET_WQ_SIZE=N with N>%%g), and/or with -DUSE_DISK\\n\",",
+ " GWQ_SIZE/sizeof(SM_frame));",
+ " printf(\" or with a larger value for -zN (N>%%d)\\n\", z_handoff);",
+ " #else",
+ " { printf(\"hint: this search may be faster if compiled without -DNGQ, with -DUSE_DISK, \");",
+ " printf(\"or with a larger -zN (N>%%d)\\n\", z_handoff);",
+ " #endif",
+ " }",
+ " leave_critical(GLOBAL_LOCK);",
+ "}",
+ "",
+ "#ifndef MAX_DSK_FILE",
+ " #define MAX_DSK_FILE 1000000 /* default is max 1M states per file */",
+ "#endif",
+ "",
+ "void",
+ "multi_usage(FILE *fd)",
+ "{ static int warned = 0;",
+ " if (warned > 0) { return; } else { warned++; }",
+ " fprintf(fd, \"\\n\");",
+ " fprintf(fd, \"Defining multi-core mode:\\n\\n\");",
+ " fprintf(fd, \" -DDUAL_CORE --> same as -DNCORE=2\\n\");",
+ " fprintf(fd, \" -DQUAD_CORE --> same as -DNCORE=4\\n\");",
+ " fprintf(fd, \" -DNCORE=N --> enables multi_core verification if N>1\\n\");",
+ " fprintf(fd, \"\\n\");",
+ " fprintf(fd, \"Additional directives supported in multi-core mode:\\n\\n\");",
+ " fprintf(fd, \" -DSEP_STATE --> forces separate statespaces instead of a single shared state space\\n\");",
+ " fprintf(fd, \" -DNUSE_DISK --> use disk for storing states when a work queue overflows\\n\");",
+ " fprintf(fd, \" -DMAX_DSK_FILE --> max nr of states per diskfile (%%d)\\n\", MAX_DSK_FILE);",
+ " fprintf(fd, \" -DFULL_TRAIL --> support full error trails (increases memory use)\\n\");",
+ " fprintf(fd, \"\\n\");",
+ " fprintf(fd, \"More advanced use (should rarely need changing):\\n\\n\");",
+ " fprintf(fd, \" To change the nr of states that can be stored in the global queue\\n\");",
+ " fprintf(fd, \" (lower numbers allow for more states to be stored, prefer multiples of 8):\\n\");",
+ " fprintf(fd, \" -DVMAX=N --> upperbound on statevector for handoffs (N=%%d)\\n\", VMAX);",
+ " fprintf(fd, \" -DPMAX=N --> upperbound on nr of procs (default: N=%%d)\\n\", PMAX);",
+ " fprintf(fd, \" -DQMAX=N --> upperbound on nr of channels (default: N=%%d)\\n\", QMAX);",
+ " fprintf(fd, \"\\n\");",
+#if 0
+ "#if !defined(WIN32) && !defined(WIN64)",
+ " fprintf(fd, \" To change the size of spin's individual shared memory segments for cygwin/linux:\\n\");",
+ " fprintf(fd, \" -DSET_SEG_SIZE=N --> default %%g (Mbytes)\\n\", SEG_SIZE/(1048576.));",
+ " fprintf(fd, \"\\n\");",
+ "#endif",
+#endif
+ " fprintf(fd, \" To set the total amount of memory reserved for the global workqueue:\\n\");",
+ " fprintf(fd, \" -DSET_WQ_SIZE=N --> default: N=128 (defined in MBytes)\\n\\n\");",
+#if 0
+ " fprintf(fd, \" To omit the global workqueue completely (bad idea):\\n\");",
+ " fprintf(fd, \" -DNGQ\\n\\n\");",
+#endif
+ " fprintf(fd, \" To force the use of a single global heap, instead of separate heaps:\\n\");",
+ " fprintf(fd, \" -DGLOB_HEAP\\n\");",
+ " fprintf(fd, \"\\n\");",
+ " fprintf(fd, \" To define a fct to initialize data before spawning processes (use quotes):\\n\");",
+ " fprintf(fd, \" \\\"-DC_INIT=fct()\\\"\\n\");",
+ " fprintf(fd, \"\\n\");",
+ " fprintf(fd, \" Timer settings for termination and crash detection:\\n\");",
+ " fprintf(fd, \" -DSHORT_T=N --> timeout for termination detection trigger (N=%%g)\\n\", (double) SHORT_T);",
+ " fprintf(fd, \" -DLONG_T=N --> timeout for giving up on termination detection (N=%%g)\\n\", (double) LONG_T);",
+ " fprintf(fd, \" -DONESECOND --> (1<<29) --> timeout waiting for a free slot -- to check for crash\\n\");",
+ " fprintf(fd, \" -DT_ALERT --> collect stats on crash alert timeouts\\n\\n\");",
+ " fprintf(fd, \"Help with Linux/Windows/Cygwin configuration for multi-core:\\n\");",
+ " fprintf(fd, \" http://spinroot.com/spin/multicore/V5_Readme.html\\n\");",
+ " fprintf(fd, \"\\n\");",
+ "}",
+ "#if NCORE>1 && defined(FULL_TRAIL)",
+ "typedef struct Stack_Tree {",
+ " uchar pr; /* process that made transition */",
+ " T_ID t_id; /* id of transition */",
+ " volatile struct Stack_Tree *prv; /* backward link towards root */",
+ "} Stack_Tree;",
+ "",
+ "struct H_el *grab_shared(int);",
+ "volatile Stack_Tree **stack_last; /* in shared memory */",
+ "char *stack_cache = NULL; /* local */",
+ "int nr_cached = 0; /* local */",
+ "",
+ "#ifndef CACHE_NR",
+ " #define CACHE_NR 1024",
+ "#endif",
+ "",
+ "volatile Stack_Tree *",
+ "stack_prefetch(void)",
+ "{ volatile Stack_Tree *st;",
+ "",
+ " if (nr_cached == 0)",
+ " { stack_cache = (char *) grab_shared(CACHE_NR * sizeof(Stack_Tree));",
+ " nr_cached = CACHE_NR;",
+ " }",
+ " st = (volatile Stack_Tree *) stack_cache;",
+ " stack_cache += sizeof(Stack_Tree);",
+ " nr_cached--;",
+ " return st;",
+ "}",
+ "",
+ "void",
+ "Push_Stack_Tree(short II, T_ID t_id)",
+ "{ volatile Stack_Tree *st;",
+ "",
+ " st = (volatile Stack_Tree *) stack_prefetch();",
+ " st->pr = II;",
+ " st->t_id = t_id;",
+ " st->prv = (Stack_Tree *) stack_last[core_id];",
+ " stack_last[core_id] = st;",
+ "}",
+ "",
+ "void",
+ "Pop_Stack_Tree(void)",
+ "{ volatile Stack_Tree *cf = stack_last[core_id];",
+ "",
+ " if (cf)",
+ " { stack_last[core_id] = cf->prv;",
+ " } else if (nr_handoffs * z_handoff + depth > 0)",
+ " { printf(\"cpu%%d: error pop_stack_tree (depth %%d)\\n\",",
+ " core_id, depth);",
+ " }",
+ "}",
+ "#endif", /* NCORE>1 && FULL_TRAIL */
+ "",
+ "void",
+ "e_critical(int which)",
+ "{ double cnt_start;",
+ "",
+ " if (readtrail || iamin[which] > 0)",
+ " { if (!readtrail && verbose)",
+ " { printf(\"cpu%%d: Double Lock on %%d (now %%d)\\n\",",
+ " core_id, which, iamin[which]+1);",
+ " fflush(stdout);",
+ " }",
+ " iamin[which]++; /* local variable */",
+ " return;",
+ " }",
+ "",
+ " cnt_start = lock_wait;",
+ "",
+ " while (sh_lock != NULL) /* as long as we have shared memory */",
+ " { int r = tas(&sh_lock[which]);",
+ " if (r == 0)",
+ " { iamin[which] = 1;",
+ " return; /* locked */",
+ " }",
+ "",
+ " lock_wait++;",
+ "#ifndef NGQ",
+ " if (which < 3) { glock_wait[which]++; }",
+ "#else",
+ " if (which == 0) { glock_wait[which]++; }",
+ "#endif",
+ " iam_alive();",
+ "",
+ " if (lock_wait - cnt_start > TenSeconds)",
+ " { printf(\"cpu%%d: lock timeout on %%d\\n\", core_id, which);",
+ " cnt_start = lock_wait;",
+ " if (someone_crashed(1))",
+ " { sudden_stop(\"lock timeout\");",
+ " pan_exit(1);",
+ " } } }",
+ "}",
+ "",
+ "void",
+ "x_critical(int which)",
+ "{",
+ " if (iamin[which] != 1)",
+ " { if (iamin[which] > 1)",
+ " { iamin[which]--; /* this is thread-local - no races on this one */",
+ " if (!readtrail && verbose)",
+ " { printf(\"cpu%%d: Partial Unlock on %%d (%%d more needed)\\n\",",
+ " core_id, which, iamin[which]);",
+ " fflush(stdout);",
+ " }",
+ " return;",
+ " } else /* iamin[which] <= 0 */",
+ " { if (!readtrail)",
+ " { printf(\"cpu%%d: Invalid Unlock iamin[%%d] = %%d\\n\",",
+ " core_id, which, iamin[which]);",
+ " fflush(stdout);",
+ " }",
+ " return;",
+ " } }",
+ "",
+ " if (sh_lock != NULL)",
+ " { iamin[which] = 0;",
+ " sh_lock[which] = 0; /* unlock */",
+ " }",
+ "}",
+ "",
+ "void",
+ "#if defined(WIN32) || defined(WIN64)",
+ "start_proxy(char *s, DWORD r_pid)",
+ "#else",
+ "start_proxy(char *s, int r_pid)",
+ "#endif",
+ "{ char Q_arg[16], Z_arg[16], Y_arg[16];",
+ " char *args[32], *ptr;",
+ " int argcnt = 0;",
+ "",
+ " sprintf(Q_arg, \"-Q%%d\", getpid());",
+ " sprintf(Y_arg, \"-Y%%d\", r_pid);",
+ " sprintf(Z_arg, \"-Z%%d\", proxy_pid /* core_id */);",
+ "",
+ " args[argcnt++] = \"proxy\";",
+ " args[argcnt++] = s; /* -r or -s */",
+ " args[argcnt++] = Q_arg;",
+ " args[argcnt++] = Z_arg;",
+ " args[argcnt++] = Y_arg;",
+ "",
+ " if (strlen(o_cmdline) > 0)",
+ " { ptr = o_cmdline; /* assume args separated by spaces */",
+ " do { args[argcnt++] = ptr++;",
+ " if ((ptr = strchr(ptr, ' ')) != NULL)",
+ " { while (*ptr == ' ')",
+ " { *ptr++ = '\\0';",
+ " }",
+ " } else",
+ " { break;",
+ " }",
+ " } while (argcnt < 31);",
+ " }",
+ " args[argcnt] = NULL;",
+ "#if defined(WIN32) || defined(WIN64)",
+ " execvp(\"pan_proxy\", args); /* no return */",
+ "#else",
+ " execvp(\"./pan_proxy\", args); /* no return */",
+ "#endif",
+ " Uerror(\"pan_proxy exec failed\");",
+ "}",
+ "/*** end of common code fragment ***/",
+ "",
+ "#if !defined(WIN32) && !defined(WIN64)",
+ "void",
+ "init_shm(void) /* initialize shared work-queues - linux/cygwin */",
+ "{ key_t key[NR_QS];",
+ " int n, m;",
+ " int must_exit = 0;",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 3: allocate shared workqueues %%g MB\\n\",",
+ " ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.) );",
+ " }",
+ " for (m = 0; m < NR_QS; m++) /* last q is the global q */",
+ " { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;",
+ " key[m] = ftok(PanSource, m+1);", /* m must be nonzero, 1..NCORE */
+ " if (key[m] == -1)",
+ " { perror(\"ftok shared queues\"); must_exit = 1; break;",
+ " }",
+ "",
+ " if (core_id == 0) /* root creates */",
+ " { /* check for stale copy */",
+ " shmid[m] = shmget(key[m], (size_t) qsize, 0600);",
+ " if (shmid[m] != -1) /* yes there is one; remove it */",
+ " { printf(\"cpu0: removing stale q%%d, status: %%d\\n\",",
+ " m, shmctl(shmid[m], IPC_RMID, NULL));",
+ " }",
+ " shmid[m] = shmget(key[m], (size_t) qsize, 0600|IPC_CREAT|IPC_EXCL);",
+ " memcnt += qsize;",
+ " } else /* workers attach */",
+ " { shmid[m] = shmget(key[m], (size_t) qsize, 0600);",
+ " /* never called, since we create shm *before* we fork */",
+ " }",
+ " if (shmid[m] == -1)",
+ " { perror(\"shmget shared queues\"); must_exit = 1; break;",
+ " }",
+ "",
+ " shared_mem[m] = (char *) shmat(shmid[m], (void *) 0, 0); /* attach */",
+ " if (shared_mem[m] == (char *) -1)",
+ " { fprintf(stderr, \"error: cannot attach shared wq %%d (%%d Mb)\\n\",",
+ " m+1, (int) (qsize/(1048576.)));",
+ " perror(\"shmat shared queues\"); must_exit = 1; break;",
+ " }",
+ "",
+ " m_workq[m] = (SM_frame *) shared_mem[m];",
+ " if (core_id == 0)",
+ " { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;",
+ " for (n = 0; n < nframes; n++)",
+ " { m_workq[m][n].m_vsize = 0;",
+ " m_workq[m][n].m_boq = 0;",
+ " } } }",
+ "",
+ " if (must_exit)",
+ " { rm_shared_segments();",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1); /* calls cleanup_shm */",
+ " }",
+ "}",
+ "",
+ "static uchar *",
+ "prep_shmid_S(size_t n) /* either sets SS or H_tab, linux/cygwin */",
+ "{ char *rval;",
+ "#ifndef SEP_STATE",
+ " key_t key;",
+ "",
+ " if (verbose && core_id == 0)",
+ " {",
+ " #ifdef BITSTATE",
+ " printf(\"cpu0: step 1: allocate shared bitstate %%g Mb\\n\",",
+ " (double) n / (1048576.));",
+ " #else",
+ " printf(\"cpu0: step 1: allocate shared hastable %%g Mb\\n\",",
+ " (double) n / (1048576.));",
+ " #endif",
+ " }",
+ " #ifdef MEMLIM", /* memlim has a value */
+ " if (memcnt + (double) n > memlim)",
+ " { printf(\"cpu0: S %%8g + %%d Kb exceeds memory limit of %%8g Mb\\n\",",
+ " memcnt/1024., n/1024, memlim/(1048576.));",
+ " printf(\"cpu0: insufficient memory -- aborting\\n\");",
+ " exit(1);",
+ " }",
+ " #endif",
+ "",
+ " key = ftok(PanSource, NCORE+2); /* different from queues */",
+ " if (key == -1)",
+ " { perror(\"ftok shared bitstate or hashtable\");",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "",
+ " if (core_id == 0) /* root */",
+ " { shmid_S = shmget(key, n, 0600);",
+ " if (shmid_S != -1)",
+ " { printf(\"cpu0: removing stale segment, status: %%d\\n\",",
+ " shmctl(shmid_S, IPC_RMID, NULL));",
+ " }",
+ " shmid_S = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);",
+ " memcnt += (double) n;",
+ " } else /* worker */",
+ " { shmid_S = shmget(key, n, 0600);",
+ " }",
+ " if (shmid_S == -1)",
+ " { perror(\"shmget shared bitstate or hashtable too large?\");",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "",
+ " rval = (char *) shmat(shmid_S, (void *) 0, 0); /* attach */",
+ " if ((char *) rval == (char *) -1)",
+ " { perror(\"shmat shared bitstate or hashtable\");",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#else",
+ " rval = (char *) emalloc(n);",
+ "#endif",
+ " return (uchar *) rval;",
+ "}",
+ "",
+ "#define TRY_AGAIN 1",
+ "#define NOT_AGAIN 0",
+ "",
+ "static char shm_prep_result;",
+ "",
+ "static uchar *",
+ "prep_state_mem(size_t n) /* sets memory arena for states linux/cygwin */",
+ "{ char *rval;",
+ " key_t key;",
+ " static int cnt = 3; /* start larger than earlier ftok calls */",
+ "",
+ " shm_prep_result = NOT_AGAIN; /* default */",
+ " if (verbose && core_id == 0)",
+ " { printf(\"cpu0: step 2+: pre-allocate memory arena %%d of %%6.2g Mb\\n\",",
+ " cnt-3, (double) n / (1048576.));",
+ " }",
+ " #ifdef MEMLIM",
+ " if (memcnt + (double) n > memlim)",
+ " { printf(\"cpu0: error: M %%.0f + %%.0f Kb exceeds memory limit of %%.0f Mb\\n\",",
+ " memcnt/1024.0, (double) n/1024.0, memlim/(1048576.));",
+ " return NULL;",
+ " }",
+ " #endif",
+ "",
+ " key = ftok(PanSource, NCORE+cnt); cnt++;", /* starts at NCORE+3 */
+ " if (key == -1)",
+ " { perror(\"ftok T\");",
+ " printf(\"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "",
+ " if (core_id == 0)",
+ " { shmid_M = shmget(key, n, 0600);",
+ " if (shmid_M != -1)",
+ " { printf(\"cpu0: removing stale memory segment %%d, status: %%d\\n\",",
+ " cnt-3, shmctl(shmid_M, IPC_RMID, NULL));",
+ " }",
+ " shmid_M = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);",
+ " /* memcnt += (double) n; -- only amount actually used is counted */",
+ " } else",
+ " { shmid_M = shmget(key, n, 0600);",
+ " ",
+ " }",
+ " if (shmid_M == -1)",
+ " { if (verbose)",
+ " { printf(\"error: failed to get pool of shared memory %%d of %%.0f Mb\\n\",",
+ " cnt-3, ((double)n)/(1048576.));",
+ " perror(\"state mem\");",
+ " printf(\"pan: check './pan --' for usage details\\n\");",
+ " }",
+ " shm_prep_result = TRY_AGAIN;",
+ " return NULL;",
+ " }",
+ " rval = (char *) shmat(shmid_M, (void *) 0, 0); /* attach */",
+ "",
+ " if ((char *) rval == (char *) -1)",
+ " { printf(\"cpu%%d error: failed to attach pool of shared memory %%d of %%.0f Mb\\n\",",
+ " core_id, cnt-3, ((double)n)/(1048576.));",
+ " perror(\"state mem\");",
+ " return NULL;",
+ " }",
+ " return (uchar *) rval;",
+ "}",
+ "",
+ "void",
+ "init_HT(unsigned long n) /* cygwin/linux version */",
+ "{ volatile char *x;",
+ " double get_mem;",
+ "#ifndef SEP_STATE",
+ " volatile char *dc_mem_start;",
+ " double need_mem, got_mem = 0.;",
+ "#endif",
+ "",
+"#ifdef SEP_STATE",
+ " #ifndef MEMLIM",
+ " if (verbose)",
+ " { printf(\"cpu0: steps 0,1: no -DMEMLIM set\\n\");", /* cannot happen */
+ " }",
+ " #else",
+ " if (verbose)",
+ " { printf(\"cpu0: steps 0,1: -DMEMLIM=%%d Mb - (hashtable %%g Mb + workqueues %%g Mb)\\n\",",
+ " MEMLIM, ((double)n/(1048576.)), (((double) NCORE * LWQ_SIZE) + GWQ_SIZE) /(1048576.) );",
+ " }",
+ " #endif",
+ " get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *) + 4*sizeof(void *) + 2*sizeof(double);",
+ " /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */",
+ " get_mem += 4 * NCORE * sizeof(void *); /* prfree, prfull, prcnt, prmax */",
+ " #ifdef FULL_TRAIL",
+ " get_mem += (NCORE) * sizeof(Stack_Tree *); /* NCORE * stack_last */",
+ " #endif",
+ " x = (volatile char *) prep_state_mem((size_t) get_mem); /* work queues and basic structs */",
+ " shmid_X = (long) x;",
+ " if (x == NULL)", /* do not repeat for smaller sizes */
+ " { printf(\"cpu0: could not allocate shared memory, see ./pan --\\n\");",
+ " exit(1);",
+ " }",
+ " search_terminated = (volatile unsigned int *) x; /* comes first */",
+ " x += sizeof(void *); /* maintain alignment */",
+ "",
+ " is_alive = (volatile double *) x;",
+ " x += NCORE * sizeof(double);",
+ "",
+ " sh_lock = (volatile int *) x;",
+ " x += CS_NR * sizeof(void *);", /* allow 1 word per entry */
+ "",
+ " grfree = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grfull = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grcnt = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grmax = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " prfree = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prfull = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prcnt = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prmax = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " gr_readmiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ " gr_writemiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ "",
+ " #ifdef FULL_TRAIL",
+ " stack_last = (volatile Stack_Tree **) x;",
+ " x += NCORE * sizeof(Stack_Tree *);",
+ " #endif",
+ "",
+ " #ifndef BITSTATE",
+ " H_tab = (struct H_el **) emalloc(n);",
+ " #endif",
+"#else",
+ " #ifndef MEMLIM",
+ " #warning MEMLIM not set", /* cannot happen */
+ " #define MEMLIM (2048)",
+ " #endif",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 0: -DMEMLIM=%%d Mb minus hashtable+workqs (%%g + %%g Mb) leaves %%g Mb\\n\",",
+ " MEMLIM, ((double)n/(1048576.)), (NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),",
+ " (memlim - memcnt - (double) n - (NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));",
+ " }",
+ " #ifndef BITSTATE",
+ " H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */",
+ " #endif",
+ " need_mem = memlim - memcnt - ((double) NCORE * LWQ_SIZE) - GWQ_SIZE;",
+ " if (need_mem <= 0.)",
+ " { Uerror(\"internal error -- shared state memory\");",
+ " }",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 2: pre-allocate shared state memory %%g Mb\\n\",",
+ " need_mem/(1048576.));",
+ " }",
+ "#ifdef SEP_HEAP",
+ " SEG_SIZE = need_mem / NCORE;",
+ " if (verbose && core_id == 0)",
+ " { printf(\"cpu0: setting segsize to %%6g MB\\n\",",
+ " SEG_SIZE/(1048576.));",
+ " }",
+ " #if defined(CYGWIN) || defined(__CYGWIN__)",
+ " if (SEG_SIZE > 512.*1024.*1024.)",
+ " { printf(\"warning: reducing SEG_SIZE of %%g MB to 512MB (exceeds max for Cygwin)\\n\",",
+ " SEG_SIZE/(1024.*1024.));",
+ " SEG_SIZE = 512.*1024.*1024.;",
+ " }",
+ " #endif",
+ "#endif",
+ " mem_reserved = need_mem;",
+ " while (need_mem > 1024.)",
+ " { get_mem = need_mem;",
+ "shm_more:",
+ " if (get_mem > (double) SEG_SIZE)",
+ " { get_mem = (double) SEG_SIZE;",
+ " }",
+ " if (get_mem <= 0.0) break;",
+ "",
+ " /* for allocating states: */",
+ " x = dc_mem_start = (volatile char *) prep_state_mem((size_t) get_mem);",
+ " if (x == NULL)",
+ " { if (shm_prep_result == NOT_AGAIN",
+ " || first_pool != NULL",
+ " || SEG_SIZE < (16. * 1048576.))",
+ " { break;",
+ " }",
+ " SEG_SIZE /= 2.;",
+ " if (verbose)",
+ " { printf(\"pan: lowered segsize to %f\\n\", SEG_SIZE);",
+ " }",
+ " if (SEG_SIZE >= 1024.)",
+ " { goto shm_more;", /* always terminates */
+ " }",
+ " break;",
+ " }",
+ "",
+ " need_mem -= get_mem;",
+ " got_mem += get_mem;",
+ " if (first_pool == NULL)",
+ " { search_terminated = (volatile unsigned int *) x; /* comes first */",
+ " x += sizeof(void *); /* maintain alignment */",
+ "",
+ " is_alive = (volatile double *) x;",
+ " x += NCORE * sizeof(double);",
+ "",
+ " sh_lock = (volatile int *) x;",
+ " x += CS_NR * sizeof(void *);", /* allow 1 word per entry */
+ "",
+ " grfree = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grfull = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grcnt = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grmax = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " prfree = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prfull = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prcnt = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prmax = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " gr_readmiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ " gr_writemiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ " #ifdef FULL_TRAIL",
+ " stack_last = (volatile Stack_Tree **) x;",
+ " x += NCORE * sizeof(Stack_Tree *);",
+ " #endif",
+ " if (((long)x)&(sizeof(void *)-1)) /* 64-bit word alignment */",
+ " { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1));",
+ " }",
+ "",
+ " #ifdef COLLAPSE",
+ " ncomps = (unsigned long *) x;",
+ " x += (256+2) * sizeof(unsigned long);",
+ " #endif",
+ " }",
+ "",
+ " dc_shared = (sh_Allocater *) x; /* must be in shared memory */",
+ " x += sizeof(sh_Allocater);",
+ "",
+ " if (core_id == 0) /* root only */",
+ " { dc_shared->dc_id = shmid_M;",
+ " dc_shared->dc_start = dc_mem_start;",
+ " dc_shared->dc_arena = x;",
+ " dc_shared->pattern = 1234567; /* protection */",
+ " dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start);",
+ " dc_shared->nxt = (long) 0;",
+ "",
+ " if (last_pool == NULL)",
+ " { first_pool = last_pool = dc_shared;",
+ " } else",
+ " { last_pool->nxt = dc_shared;",
+ " last_pool = dc_shared;",
+ " }",
+ " } else if (first_pool == NULL)",
+ " { first_pool = dc_shared;",
+ " } }",
+ "",
+ " if (need_mem > 1024.)",
+ " { printf(\"cpu0: could allocate only %%g Mb of shared memory (wanted %%g more)\\n\",",
+ " got_mem/(1048576.), need_mem/(1048576.));",
+ " }",
+ "",
+ " if (!first_pool)",
+ " { printf(\"cpu0: insufficient memory -- aborting.\\n\");",
+ " exit(1);",
+ " }",
+ " /* we are still single-threaded at this point, with core_id 0 */",
+ " dc_shared = first_pool;",
+ "",
+"#endif", /* !SEP_STATE */
+ "}",
+ "",
+ " /* Test and Set assembly code */",
+ "",
+ " #if defined(i386) || defined(__i386__) || defined(__x86_64__)",
+ " int",
+ " tas(volatile int *s) /* tested */",
+ " { int r;",
+ " __asm__ __volatile__(",
+ " \"xchgl %%0, %%1 \\n\\t\"",
+ " : \"=r\"(r), \"=m\"(*s)",
+ " : \"0\"(1), \"m\"(*s)",
+ " : \"memory\");",
+ " ",
+ " return r;",
+ " }",
+ " #elif defined(__arm__)",
+ " int",
+ " tas(volatile int *s) /* not tested */",
+ " { int r = 1;",
+ " __asm__ __volatile__(",
+ " \"swpb %%0, %%0, [%%3] \\n\"",
+ " : \"=r\"(r), \"=m\"(*s)",
+ " : \"0\"(r), \"r\"(s));",
+ "",
+ " return r;",
+ " }",
+ " #elif defined(sparc) || defined(__sparc__)",
+ " int",
+ " tas(volatile int *s) /* not tested */",
+ " { int r = 1;",
+ " __asm__ __volatile__(",
+ " \" ldstub [%%2], %%0 \\n\"",
+ " : \"=r\"(r), \"=m\"(*s)",
+ " : \"r\"(s));",
+ "",
+ " return r;",
+ " }",
+ " #elif defined(ia64) || defined(__ia64__)",
+ " /* Intel Itanium */",
+ " int",
+ " tas(volatile int *s) /* tested */",
+ " { long int r;",
+ " __asm__ __volatile__(",
+ " \" xchg4 %%0=%%1,%%2 \\n\"",
+ " : \"=r\"(r), \"+m\"(*s)",
+ " : \"r\"(1)",
+ " : \"memory\");",
+ " return (int) r;",
+ " }",
+ " #else",
+ " #error missing definition of test and set operation for this platform",
+ " #endif",
+ "",
+ "void",
+ "cleanup_shm(int val)",
+ "{ volatile sh_Allocater *nxt_pool;",
+ " unsigned long cnt = 0;",
+ " int m;",
+ "",
+ " if (nibis != 0)",
+ " { printf(\"cpu%%d: Redundant call to cleanup_shm(%%d)\\n\", core_id, val);",
+ " return;",
+ " } else",
+ " { nibis = 1;",
+ " }",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 16; /* cleanup_shm */",
+ " }",
+ "",
+ " for (m = 0; m < NR_QS; m++)",
+ " { if (shmdt((void *) shared_mem[m]) > 0)",
+ " { perror(\"shmdt detaching from shared queues\");",
+ " } }",
+ "",
+ "#ifdef SEP_STATE",
+ " if (shmdt((void *) shmid_X) != 0)",
+ " { perror(\"shmdt detaching from shared state memory\");",
+ " }",
+ "#else",
+ " #ifdef BITSTATE",
+ " if (SS > 0 && shmdt((void *) SS) != 0)",
+ " { if (verbose)",
+ " { perror(\"shmdt detaching from shared bitstate arena\");",
+ " } }",
+ " #else",
+ " if (core_id == 0)",
+ " { /* before detaching: */",
+ " for (nxt_pool = dc_shared; nxt_pool != NULL; nxt_pool = nxt_pool->nxt)",
+ " { cnt += nxt_pool->dc_size;",
+ " }",
+ " if (verbose)",
+ " { printf(\"cpu0: done, %%ld Mb of shared state memory left\\n\",",
+ " cnt / (long)(1048576));",
+ " } }",
+ "",
+ " if (shmdt((void *) H_tab) != 0)",
+ " { perror(\"shmdt detaching from shared hashtable\");",
+ " }",
+ "",
+ " for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)",
+ " { nxt_pool = last_pool->nxt;",
+ " if (shmdt((void *) last_pool->dc_start) != 0)",
+ " { perror(\"shmdt detaching from shared state memory\");",
+ " } }",
+ " first_pool = last_pool = NULL; /* precaution */",
+ " #endif",
+ "#endif",
+ " /* detached from shared memory - so cannot use cpu_printf */",
+ " if (verbose)",
+ " { printf(\"cpu%%d: done -- got %%d states from queue\\n\",",
+ " core_id, nstates_get);",
+ " }",
+ "}",
+ "",
+ "extern void give_up(int);",
+ "extern void Read_Queue(int);",
+ "",
+ "void",
+ "mem_get(void)",
+ "{ SM_frame *f;",
+ " int is_parent;",
+ "",
+ "#if defined(MA) && !defined(SEP_STATE)",
+ " #error MA without SEP_STATE is not supported with multi-core",
+ "#endif",
+ "#ifdef BFS",
+ " #error BFS is not supported with multi-core",
+ "#endif",
+ "#ifdef SC",
+ " #error SC is not supported with multi-core",
+ "#endif",
+ " init_shm(); /* we are single threaded when this starts */",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 4: calling fork()\\n\");",
+ " }",
+ " fflush(stdout);",
+ "",
+ "/* if NCORE > 1 the child or the parent should fork N-1 more times",
+ " * the parent is the only process with core_id == 0 and is_parent > 0",
+ " * the workers have is_parent = 0 and core_id = 1..NCORE-1",
+ " */",
+ " if (core_id == 0)",
+ " { worker_pids[0] = getpid(); /* for completeness */",
+ " while (++core_id < NCORE) /* first worker sees core_id = 1 */",
+ " { is_parent = fork();",
+ " if (is_parent == -1)",
+ " { Uerror(\"fork failed\");",
+ " }",
+ " if (is_parent == 0) /* this is a worker process */",
+ " { if (proxy_pid == core_id) /* always non-zero */",
+ " { start_proxy(\"-r\", 0); /* no return */",
+ " }",
+ " goto adapt; /* root process continues spawning */",
+ " }",
+ " worker_pids[core_id] = is_parent;",
+ " }",
+ " /* note that core_id is now NCORE */",
+ " if (proxy_pid > 0 && proxy_pid < NCORE)", /* add send-half of proxy */
+ " { proxy_pid_snd = fork();",
+ " if (proxy_pid_snd == -1)",
+ " { Uerror(\"proxy fork failed\");",
+ " }",
+ " if (proxy_pid_snd == 0)",
+ " { start_proxy(\"-s\", worker_pids[proxy_pid]); /* no return */",
+ " } } /* else continue */",
+
+ " if (is_parent > 0)",
+ " { core_id = 0; /* reset core_id for root process */",
+ " }",
+ " } else /* worker */",
+ " { static char db0[16]; /* good for up to 10^6 cores */",
+ " static char db1[16];",
+ "adapt: tprefix = db0; sprefix = db1;",
+ " sprintf(tprefix, \"cpu%%d_trail\", core_id);",
+ " sprintf(sprefix, \"cpu%%d_rst\", core_id);",
+ " memcnt = 0; /* count only additionally allocated memory */",
+ " }",
+ " signal(SIGINT, give_up);",
+ "",
+ " if (proxy_pid == 0) /* not in a cluster setup, pan_proxy must attach */",
+ " { rm_shared_segments(); /* mark all shared segments for removal on exit */",
+ " }", /* doing it early means less chance of being unable to do this */
+ " if (verbose)",
+ " { cpu_printf(\"starting core_id %%d -- pid %%d\\n\", core_id, getpid());",
+ " }",
+
+ "#if defined(SEP_HEAP) && !defined(SEP_STATE)", /* set my_heap and adjust dc_shared */
+ " { int i;",
+ " volatile sh_Allocater *ptr;",
+ " ptr = first_pool;",
+ " for (i = 0; i < NCORE && ptr != NULL; i++)",
+ " { if (i == core_id)",
+ " { my_heap = (char *) ptr->dc_arena;",
+ " my_size = (long) ptr->dc_size;",
+ " if (verbose)",
+ " cpu_printf(\"local heap %%ld MB\\n\", my_size/(1048576));",
+ " break;",
+ " }",
+ " ptr = ptr->nxt; /* local */",
+ " }",
+ " if (my_heap == NULL)",
+ " { printf(\"cpu%%d: no local heap\\n\", core_id);",
+ " pan_exit(1);",
+ " } /* else */",
+ " #if defined(CYGWIN) || defined(__CYGWIN__)",
+ " ptr = first_pool;",
+ " for (i = 0; i < NCORE && ptr != NULL; i++)",
+ " { ptr = ptr->nxt; /* local */",
+ " }",
+ " dc_shared = ptr; /* any remainder */",
+ " #else",
+ " dc_shared = NULL; /* used all mem for local heaps */",
+ " #endif",
+ " }",
+ "#endif",
+
+ " if (core_id == 0 && !remote_party)",
+ " { new_state(); /* cpu0 explores root */",
+ " if (verbose)",
+ " cpu_printf(\"done with 1st dfs, nstates %%g (put %%d states), read q\\n\",",
+ " nstates, nstates_put);",
+ " dfs_phase2 = 1;",
+ " }",
+ " Read_Queue(core_id); /* all cores */",
+ "",
+ " if (verbose)",
+ " { cpu_printf(\"put %%6d states into queue -- got %%6d\\n\",",
+ " nstates_put, nstates_get);",
+ " }",
+ " if (proxy_pid != 0)",
+ " { rm_shared_segments();",
+ " }",
+ " done = 1;",
+ " wrapup();",
+ " exit(0);",
+ "}",
+ "",
+ "#else",
+ "int unpack_state(SM_frame *, int);",
+ "#endif",
+ "",
+ "struct H_el *",
+ "grab_shared(int n)",
+ "{",
+ "#ifndef SEP_STATE",
+ " char *rval = (char *) 0;",
+ "",
+ " if (n == 0)",
+ " { printf(\"cpu%%d: grab shared zero\\n\", core_id); fflush(stdout);",
+ " return (struct H_el *) rval;",
+ " } else if (n&(sizeof(void *)-1))",
+ " { n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */",
+ " }",
+ "",
+ "#ifdef SEP_HEAP",
+ " /* no locking */",
+ " if (my_heap != NULL && my_size > n)",
+ " { rval = my_heap;",
+ " my_heap += n;",
+ " my_size -= n;",
+ " goto done;",
+ " }",
+ "#endif",
+ "",
+ " if (!dc_shared)",
+ " { sudden_stop(\"pan: out of memory\");",
+ " }",
+ "",
+ " /* another lock is always already in effect when this is called */",
+ " /* but not always the same lock -- i.e., on different parts of the hashtable */",
+ " enter_critical(GLOBAL_LOCK); /* this must be independently mutex */",
+ "#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64)",
+ " { static int noted = 0;",
+ " if (!noted)",
+ " { noted = 1;",
+ " printf(\"cpu%%d: global heap has %%ld bytes left, needed %%d\\n\",",
+ " core_id, dc_shared?dc_shared->dc_size:0, n);",
+ " } }",
+ "#endif",
+ "#if 0", /* for debugging */
+ " if (dc_shared->pattern != 1234567)",
+ " { leave_critical(GLOBAL_LOCK);",
+ " Uerror(\"overrun -- memory corruption\");",
+ " }",
+ "#endif",
+ " if (dc_shared->dc_size < n)",
+ " { if (verbose)",
+ " { printf(\"Next Pool %%g Mb + %%d\\n\", memcnt/(1048576.), n);",
+ " }",
+ " if (dc_shared->nxt == NULL",
+ " || dc_shared->nxt->dc_arena == NULL",
+ " || dc_shared->nxt->dc_size < n)",
+ " { printf(\"cpu%%d: memcnt %%g Mb + wanted %%d bytes more\\n\",",
+ " core_id, memcnt / (1048576.), n);",
+ " leave_critical(GLOBAL_LOCK);",
+ " sudden_stop(\"out of memory -- aborting\");",
+ " wrapup(); /* exits */",
+ " } else",
+ " { dc_shared = (sh_Allocater *) dc_shared->nxt;",
+ " } }",
+ "",
+ " rval = (char *) dc_shared->dc_arena;",
+ " dc_shared->dc_arena += n;",
+ " dc_shared->dc_size -= (long) n;",
+ "#if 0",
+ " if (VVERBOSE)",
+ " printf(\"cpu%%d grab shared (%%d bytes) -- %%ld left\\n\",",
+ " core_id, n, dc_shared->dc_size);",
+ "#endif",
+ " leave_critical(GLOBAL_LOCK);",
+ "done:",
+ " memset(rval, 0, n);",
+ " memcnt += (double) n;",
+ "",
+ " return (struct H_el *) rval;",
+ "#else",
+ " return (struct H_el *) emalloc(n);",
+ "#endif",
+ "}",
+ "",
+ "SM_frame *",
+ "Get_Full_Frame(int n)",
+ "{ SM_frame *f;",
+ " double cnt_start = frame_wait;",
+ "",
+ " f = &m_workq[n][prfull[n]];",
+ " while (f->m_vsize == 0) /* await full slot LOCK : full frame */",
+ " { iam_alive();",
+ "#ifndef NGQ",
+ " #ifndef SAFETY",
+ " if (!a_cycles || core_id != 0)",
+ " #endif",
+ " if (*grcnt > 0) /* accessed outside lock, but safe even if wrong */",
+ " { enter_critical(GQ_RD); /* gq - read access */",
+ " if (*grcnt > 0) /* could have changed */",
+ " { f = &m_workq[NCORE][*grfull]; /* global q */",
+ " if (f->m_vsize == 0)",
+ " { /* writer is still filling the slot */",
+ " *gr_writemiss++;",
+ " f = &m_workq[n][prfull[n]]; /* reset */",
+ " } else",
+ " { *grfull = (*grfull+1) %% (GN_FRAMES);",
+ " enter_critical(GQ_WR);",
+ " *grcnt = *grcnt - 1;",
+ " leave_critical(GQ_WR);",
+ " leave_critical(GQ_RD);",
+ " return f;",
+ " } }",
+ " leave_critical(GQ_RD);",
+ " }",
+ "#endif",
+ " if (frame_wait++ - cnt_start > Delay)",
+ " { if (0)", /* too frequent to enable this one */
+ " { cpu_printf(\"timeout on q%%d -- %%u -- query %%d\\n\",",
+ " n, f, query_in_progress);",
+ " }",
+ " return (SM_frame *) 0; /* timeout */",
+ " } }",
+ " iam_alive();",
+ " if (VVERBOSE) cpu_printf(\"got frame from q%%d\\n\", n);",
+ " prfull[n] = (prfull[n] + 1) %% (LN_FRAMES);",
+ " enter_critical(QLOCK(n));",
+ " prcnt[n]--; /* lock out increments */",
+ " leave_critical(QLOCK(n));",
+ " return f;",
+ "}",
+ "",
+ "SM_frame *",
+ "Get_Free_Frame(int n)",
+ "{ SM_frame *f;",
+ " double cnt_start = free_wait;",
+ "",
+ " if (VVERBOSE) { cpu_printf(\"get free frame from q%%d\\n\", n); }",
+ "",
+ " if (n == NCORE) /* global q */",
+ " { f = &(m_workq[n][lrfree]);",
+ " } else",
+ " { f = &(m_workq[n][prfree[n]]);",
+ " }",
+ " while (f->m_vsize != 0) /* await free slot LOCK : free slot */",
+ " { iam_alive();",
+ " if (free_wait++ - cnt_start > OneSecond)",
+ " { if (verbose)",
+ " { cpu_printf(\"timeout waiting for free slot q%%d\\n\", n);",
+ " }",
+ " cnt_start = free_wait;",
+ " if (someone_crashed(1))",
+ " { printf(\"cpu%%d: search terminated\\n\", core_id);",
+ " sudden_stop(\"get free frame\");",
+ " pan_exit(1);",
+ " } } }",
+ " if (n != NCORE)",
+ " { prfree[n] = (prfree[n] + 1) %% (LN_FRAMES);",
+ " enter_critical(QLOCK(n));",
+ " prcnt[n]++; /* lock out decrements */",
+ " if (prmax[n] < prcnt[n])",
+ " { prmax[n] = prcnt[n];",
+ " }",
+ " leave_critical(QLOCK(n));",
+ " }",
+ " return f;",
+ "}",
+ ""
+ "#ifndef NGQ",
+ "int",
+ "GlobalQ_HasRoom(void)",
+ "{ int rval = 0;",
+ "",
+ " gq_tries++;",
+ " if (*grcnt < GN_FRAMES) /* there seems to be room */",
+ " { enter_critical(GQ_WR); /* gq write access */",
+ " if (*grcnt < GN_FRAMES)",
+ " { if (m_workq[NCORE][*grfree].m_vsize != 0)",
+ " { /* can happen if reader is slow emptying slot */",
+ " *gr_readmiss++;",
+ " goto out; /* dont wait: release lock and return */",
+ " }",
+ " lrfree = *grfree; /* Get_Free_Frame use lrfree in this mode */",
+ " *grfree = (*grfree + 1) %% GN_FRAMES;", /* next process looks at next slot */
+ " *grcnt = *grcnt + 1; /* count nr of slots filled -- no additional lock needed */",
+ " if (*grmax < *grcnt) *grmax = *grcnt;",
+ " leave_critical(GQ_WR); /* for short lock duration */",
+ " gq_hasroom++;",
+ " mem_put(NCORE); /* copy state into reserved slot */",
+ " rval = 1; /* successfull handoff */",
+ " } else",
+ " { gq_hasnoroom++;",
+ "out: leave_critical(GQ_WR);", /* should be rare */
+ " } }",
+ " return rval;",
+ "}",
+ "#endif",
+ "",
+ "int",
+ "unpack_state(SM_frame *f, int from_q)",
+ "{ int i, j;",
+ " static struct H_el D_State;",
+ "",
+ " if (f->m_vsize > 0)",
+ " { boq = f->m_boq;",
+ " if (boq > 256)",
+ " { cpu_printf(\"saw control %%d, expected state\\n\", boq);",
+ " return 0;",
+ " }",
+ " vsize = f->m_vsize;",
+ "correct:",
+ " memcpy((uchar *) &now, (uchar *) f->m_now, vsize);",
+ " for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+ " { Mask[i] = (f->m_Mask[i/8] & (1<<j)) ? 1 : 0;",
+ " }",
+ " if (now._nr_pr > 0)",
+ " { memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT));",
+ " memcpy((uchar *) proc_skip, (uchar *) f->m_p_skip, now._nr_pr * sizeof(uchar));",
+ " }",
+ " if (now._nr_qs > 0)",
+ " { memcpy((uchar *) q_offset, (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT));",
+ " memcpy((uchar *) q_skip, (uchar *) f->m_q_skip, now._nr_qs * sizeof(uchar));",
+ " }",
+ "#ifndef NOVSZ",
+ " if (vsize != now._vsz)",
+ " { cpu_printf(\"vsize %%d != now._vsz %%d (type %%d) %%d\\n\",",
+ " vsize, now._vsz, f->m_boq, f->m_vsize);",
+ " vsize = now._vsz;",
+ " goto correct; /* rare event: a race */",
+ " }",
+ "#endif",
+ " hmax = max(hmax, vsize);",
+ "",
+ " if (f != &cur_Root)",
+ " { memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame));",
+ " }",
+ "",
+ " if (((now._a_t) & 1) == 1) /* i.e., when starting nested DFS */",
+ " { A_depth = depthfound = 0;",
+ " memcpy((uchar *)&A_Root, (uchar *)&now, vsize);",
+ " }",
+ " nr_handoffs = f->nr_handoffs;",
+ " } else",
+ " { cpu_printf(\"pan: state empty\\n\");",
+ " }",
+ "",
+ " depth = 0;",
+ " trpt = &trail[1];",
+ " trpt->tau = f->m_tau;",
+ " trpt->o_pm = f->m_o_pm;",
+ "",
+ " (trpt-1)->ostate = &D_State; /* stub */",
+ " trpt->ostate = &D_State;",
+ "",
+ "#ifdef FULL_TRAIL",
+ " if (upto > 0)",
+ " { stack_last[core_id] = (Stack_Tree *) f->m_stack;",
+ " }",
+ " #if defined(VERBOSE)",
+ " if (stack_last[core_id])",
+ " { cpu_printf(\"%%d: UNPACK -- SET m_stack %%u (%%d,%%d)\\n\",",
+ " depth, stack_last[core_id], stack_last[core_id]->pr,",
+ " stack_last[core_id]->t_id);",
+ " }",
+ " #endif",
+ "#endif",
+ "",
+ " if (!trpt->o_t)",
+ " { static Trans D_Trans;",
+ " trpt->o_t = &D_Trans;",
+ " }",
+ "",
+ " #ifdef VERI",
+ " if ((trpt->tau & 4) != 4)",
+ " { trpt->tau |= 4; /* the claim moves first */",
+ " cpu_printf(\"warning: trpt was not up to date\\n\");",
+ " }",
+ " #endif",
+ "",
+ " for (i = 0; i < (int) now._nr_pr; i++)",
+ " { P0 *ptr = (P0 *) pptr(i);",
+ " #ifndef NP",
+ " if (accpstate[ptr->_t][ptr->_p])",
+ " { trpt->o_pm |= 2;",
+ " }",
+ " #else",
+ " if (progstate[ptr->_t][ptr->_p])",
+ " { trpt->o_pm |= 4;",
+ " }",
+ " #endif",
+ " }",
+ "",
+ " #ifdef EVENT_TRACE",
+ " #ifndef NP",
+ " if (accpstate[EVENT_TRACE][now._event])",
+ " { trpt->o_pm |= 2;",
+ " }",
+ " #else",
+ " if (progstate[EVENT_TRACE][now._event])",
+ " { trpt->o_pm |= 4;",
+ " }",
+ " #endif",
+ " #endif",
+ "",
+ " #if defined(C_States) && (HAS_TRACK==1)",
+ " /* restore state of tracked C objects */",
+ " c_revert((uchar *) &(now.c_state[0]));",
+ " #if (HAS_STACK==1)",
+ " c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */",
+ " #endif",
+ " #endif",
+ " return 1;",
+ "}",
+ "",
+ "void",
+ "write_root(void) /* for trail file */",
+ "{ int fd;",
+ "",
+ " if (iterative == 0 && Nr_Trails > 1)",
+ " sprintf(fnm, \"%%s%%d.%%s\", TrailFile, Nr_Trails-1, sprefix);",
+ " else",
+ " sprintf(fnm, \"%%s.%%s\", TrailFile, sprefix);",
+ "",
+ " if (cur_Root.m_vsize == 0)",
+ " { (void) unlink(fnm); /* remove possible old copy */",
+ " return; /* its the default initial state */",
+ " }",
+ "",
+ " if ((fd = creat(fnm, TMODE)) < 0)",
+ " { char *q;",
+ " if ((q = strchr(TrailFile, \'.\')))",
+ " { *q = \'\\0\'; /* strip .pml */",
+ " if (iterative == 0 && Nr_Trails-1 > 0)",
+ " sprintf(fnm, \"%%s%%d.%%s\", TrailFile, Nr_Trails-1, sprefix);",
+ " else",
+ " sprintf(fnm, \"%%s.%%s\", TrailFile, sprefix);",
+ " *q = \'.\';",
+ " fd = creat(fnm, TMODE);",
+ " }",
+ " if (fd < 0)",
+ " { cpu_printf(\"pan: cannot create %%s\\n\", fnm);",
+ " perror(\"cause\");",
+ " return;",
+ " } }",
+ "",
+ " if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))",
+ " { cpu_printf(\"pan: error writing %%s\\n\", fnm);",
+ " } else",
+ " { cpu_printf(\"pan: wrote %%s\\n\", fnm);",
+ " }",
+ " close(fd);",
+ "}",
+ "",
+ "void",
+ "set_root(void)",
+ "{ int fd;",
+ " char *q;",
+ " char MyFile[512];", /* enough to hold a reasonable pathname */
+ " char MySuffix[16];",
+ " char *ssuffix = \"rst\";",
+ " int try_core = 1;",
+ "",
+ " strcpy(MyFile, TrailFile);",
+ "try_again:",
+ " if (whichtrail > 0)",
+ " { sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, ssuffix);",
+ " fd = open(fnm, O_RDONLY, 0);",
+ " if (fd < 0 && (q = strchr(MyFile, \'.\')))",
+ " { *q = \'\\0\'; /* strip .pml */",
+ " sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, ssuffix);",
+ " *q = \'.\';",
+ " fd = open(fnm, O_RDONLY, 0);",
+ " }",
+ " } else",
+ " { sprintf(fnm, \"%%s.%%s\", MyFile, ssuffix);",
+ " fd = open(fnm, O_RDONLY, 0);",
+ " if (fd < 0 && (q = strchr(MyFile, \'.\')))",
+ " { *q = \'\\0\'; /* strip .pml */",
+ " sprintf(fnm, \"%%s.%%s\", MyFile, ssuffix);",
+ " *q = \'.\';",
+ " fd = open(fnm, O_RDONLY, 0);",
+ " } }",
+ "",
+ " if (fd < 0)",
+ " { if (try_core < NCORE)",
+ " { ssuffix = MySuffix;",
+ " sprintf(ssuffix, \"cpu%%d_rst\", try_core++);",
+ " goto try_again;",
+ " }",
+ " cpu_printf(\"no file '%%s.rst' or '%%s' (not an error)\\n\", MyFile, fnm);",
+ " } else",
+ " { if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))",
+ " { cpu_printf(\"read error %%s\\n\", fnm);",
+ " close(fd);",
+ " pan_exit(1);",
+ " }",
+ " close(fd);",
+ " (void) unpack_state(&cur_Root, -2);",
+ "#ifdef SEP_STATE",
+ " cpu_printf(\"partial trail -- last few steps only\\n\");",
+ "#endif",
+ " cpu_printf(\"restored root from '%%s'\\n\", fnm);",
+ " printf(\"=====State:=====\\n\");",
+ " { int i, j; P0 *z;",
+ " for (i = 0; i < now._nr_pr; i++)",
+ " { z = (P0 *)pptr(i);",
+ " printf(\"proc %%2d (%%s) \", i, procname[z->_t]);",
+
+ " for (j = 0; src_all[j].src; j++)",
+ " if (src_all[j].tp == (int) z->_t)",
+ " { printf(\" line %%3d \\\"%%s\\\" \",",
+ " src_all[j].src[z->_p], PanSource);",
+ " break;",
+ " }",
+ " printf(\"(state %%d)\\n\", z->_p);",
+ " c_locals(i, z->_t);",
+ " }",
+ " c_globals();",
+ " }",
+ " printf(\"================\\n\");",
+ " }",
+ "}",
+ "",
+ "#ifdef USE_DISK",
+ "unsigned long dsk_written, dsk_drained;",
+ "void mem_drain(void);",
+ "#endif",
+ "",
+ "void",
+ "m_clear_frame(SM_frame *f)", /* clear room for stats */
+ "{ int i, clr_sz = sizeof(SM_results);",
+ "",
+ " for (i = 0; i <= _NP_; i++) /* all proctypes */",
+ " { clr_sz += NrStates[i]*sizeof(uchar);",
+ " }",
+ " memset(f, 0, clr_sz);",
+ " /* caution if sizeof(SM_results) > sizeof(SM_frame) */",
+ "}",
+ "",
+ "#define TargetQ_Full(n) (m_workq[n][prfree[n]].m_vsize != 0)", /* no free slot */
+ "#define TargetQ_NotFull(n) (m_workq[n][prfree[n]].m_vsize == 0)", /* avoiding prcnt */
+ "",
+ "int",
+ "AllQueuesEmpty(void)",
+ "{ int q;",
+ "#ifndef NGQ",
+ " if (*grcnt != 0)",
+ " { return 0;",
+ " }",
+ "#endif",
+ " for (q = 0; q < NCORE; q++)",
+ " { if (prcnt[q] != 0)", /* not locked, ok if race */
+ " { return 0;",
+ " } }",
+ " return 1;",
+ "}",
+ "",
+ "void",
+ "Read_Queue(int q)",
+ "{ SM_frame *f, *of;",
+ " int remember, target_q;",
+ " SM_results *r;",
+ " double patience = 0.0;",
+ "",
+ " target_q = (q + 1) %% NCORE;",
+ "",
+ " for (;;)",
+ " { f = Get_Full_Frame(q);",
+ " if (!f) /* 1 second timeout -- and trigger for Query */",
+ " { if (someone_crashed(2))",
+ " { printf(\"cpu%%d: search terminated [code %%d]\\n\",",
+ " core_id, search_terminated?*search_terminated:-1);",
+ " sudden_stop(\"\");",
+ " pan_exit(1);",
+ " }",
+ "#ifdef TESTING",
+ " /* to profile with cc -pg and gprof pan.exe -- set handoff depth beyond maxdepth */",
+ " exit(0);",
+ "#endif",
+ " remember = *grfree;",
+ " if (core_id == 0 /* root can initiate termination */",
+ " && remote_party == 0 /* and only the original root */",
+ " && query_in_progress == 0 /* unless its already in progress */",
+ " && AllQueuesEmpty())",
+ " { f = Get_Free_Frame(target_q);",
+ " query_in_progress = 1; /* only root process can do this */",
+ " if (!f) { Uerror(\"Fatal1: no free slot\"); }",
+ " f->m_boq = QUERY; /* initiate Query */",
+ " if (verbose)",
+ " { cpu_printf(\"snd QUERY to q%%d (%%d) into slot %%d\\n\",",
+ " target_q, nstates_get + 1, prfree[target_q]-1);",
+ " }",
+ " f->m_vsize = remember + 1;",
+ " /* number will not change unless we receive more states */",
+ " } else if (patience++ > OneHour) /* one hour watchdog timer */",
+ " { cpu_printf(\"timeout -- giving up\\n\");",
+ " sudden_stop(\"queue timeout\");",
+ " pan_exit(1);",
+ " }",
+ " if (0) cpu_printf(\"timed out -- try again\\n\");",
+ " continue; ",
+ " }",
+ " patience = 0.0; /* reset watchdog */",
+ "",
+ " if (f->m_boq == QUERY)",
+ " { if (verbose)",
+ " { cpu_printf(\"got QUERY on q%%d (%%d <> %%d) from slot %%d\\n\",",
+ " q, f->m_vsize, nstates_put + 1, prfull[q]-1);",
+ " snapshot();",
+ " }",
+ " remember = f->m_vsize;",
+ " f->m_vsize = 0; /* release slot */",
+ "",
+ " if (core_id == 0 && remote_party == 0) /* original root cpu0 */",
+ " { if (query_in_progress == 1 /* didn't send more states in the interim */",
+ " && *grfree + 1 == remember) /* no action on global queue meanwhile */",
+ " { if (verbose) cpu_printf(\"Termination detected\\n\");",
+ " if (TargetQ_Full(target_q))",
+ " { if (verbose)",
+ " cpu_printf(\"warning: target q is full\\n\");",
+ " }",
+ " f = Get_Free_Frame(target_q);",
+ " if (!f) { Uerror(\"Fatal2: no free slot\"); }",
+ " m_clear_frame(f);",
+ " f->m_boq = QUIT; /* send final Quit, collect stats */",
+ " f->m_vsize = 111; /* anything non-zero will do */",
+ " if (verbose)",
+ " cpu_printf(\"put QUIT on q%%d\\n\", target_q);",
+ " } else",
+ " { if (verbose) cpu_printf(\"Stale Query\\n\");",
+ "#ifdef USE_DISK",
+ " mem_drain();",
+ "#endif",
+ " }",
+ " query_in_progress = 0;",
+ " } else",
+ " { if (TargetQ_Full(target_q))",
+ " { if (verbose)",
+ " cpu_printf(\"warning: forward query - target q full\\n\");",
+ " }",
+ " f = Get_Free_Frame(target_q);",
+ " if (verbose)",
+ " cpu_printf(\"snd QUERY response to q%%d (%%d <> %%d) in slot %%d\\n\",",
+ " target_q, remember, *grfree + 1, prfree[target_q]-1);",
+ " if (!f) { Uerror(\"Fatal4: no free slot\"); }",
+ "",
+ " if (*grfree + 1 == remember) /* no action on global queue */",
+ " { f->m_boq = QUERY; /* forward query, to root */",
+ " f->m_vsize = remember;",
+ " } else",
+ " { f->m_boq = QUERY_F; /* no match -- busy */",
+ " f->m_vsize = 112; /* anything non-zero */",
+ "#ifdef USE_DISK",
+ " if (dsk_written != dsk_drained)",
+ " { mem_drain();",
+ " }",
+ "#endif",
+ " } }",
+ " continue;",
+ " }",
+ "",
+ " if (f->m_boq == QUERY_F)",
+ " { if (verbose)",
+ " { cpu_printf(\"got QUERY_F on q%%d from slot %%d\\n\", q, prfull[q]-1);",
+ " }",
+ " f->m_vsize = 0; /* release slot */",
+ "",
+ " if (core_id == 0 && remote_party == 0) /* original root cpu0 */",
+ " { if (verbose) cpu_printf(\"No Match on Query\\n\");",
+ " query_in_progress = 0;",
+ " } else",
+ " { if (TargetQ_Full(target_q))",
+ " { if (verbose) cpu_printf(\"warning: forwarding query_f, target queue full\\n\");",
+ " }",
+ " f = Get_Free_Frame(target_q);",
+ " if (verbose) cpu_printf(\"forward QUERY_F to q%%d into slot %%d\\n\",",
+ " target_q, prfree[target_q]-1);",
+ " if (!f) { Uerror(\"Fatal5: no free slot\"); }",
+ " f->m_boq = QUERY_F; /* cannot terminate yet */",
+ " f->m_vsize = 113; /* anything non-zero */",
+ " }",
+ "#ifdef USE_DISK",
+ " if (dsk_written != dsk_drained)",
+ " { mem_drain();",
+ " }",
+ "#endif",
+ " continue;",
+ " }",
+ "",
+ " if (f->m_boq == QUIT)",
+ " { if (0) cpu_printf(\"done -- local memcnt %%g Mb\\n\", memcnt/(1048576.));",
+ " retrieve_info((SM_results *) f); /* collect and combine stats */",
+ " if (verbose)",
+ " { cpu_printf(\"received Quit\\n\");",
+ " snapshot();",
+ " }",
+ " f->m_vsize = 0; /* release incoming slot */",
+ " if (core_id != 0)",
+ " { f = Get_Free_Frame(target_q); /* new outgoing slot */",
+ " if (!f) { Uerror(\"Fatal6: no free slot\"); }",
+ " m_clear_frame(f); /* start with zeroed stats */",
+ " record_info((SM_results *) f);",
+ " f->m_boq = QUIT; /* forward combined results */",
+ " f->m_vsize = 114; /* anything non-zero */",
+ " if (verbose>1)",
+ " cpu_printf(\"fwd Results to q%%d\\n\", target_q);",
+ " }",
+ " break; /* successful termination */",
+ " }",
+ "",
+ " /* else: 0<= boq <= 255, means STATE transfer */",
+ " if (unpack_state(f, q) != 0)",
+ " { nstates_get++;",
+ " f->m_vsize = 0; /* release slot */",
+ " if (VVERBOSE) cpu_printf(\"Got state\\n\");",
+ "",
+ " if (search_terminated != NULL",
+ " && *search_terminated == 0)",
+ " { new_state(); /* explore successors */",
+ " memset((uchar *) &cur_Root, 0, sizeof(SM_frame)); /* avoid confusion */",
+ " } else",
+ " { pan_exit(0);",
+ " }",
+ " } else",
+ " { pan_exit(0);",
+ " } }",
+ " if (verbose) cpu_printf(\"done got %%d put %%d\\n\", nstates_get, nstates_put);",
+ " sleep_report();",
+ "}",
+ "",
+ "void",
+ "give_up(int unused_x)",
+ "{",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 32; /* give_up */",
+ " }",
+ " if (!writing_trail)",
+ " { was_interrupted = 1;",
+ " snapshot();",
+ " cpu_printf(\"Give Up\\n\");",
+ " sleep_report();",
+ " pan_exit(1);",
+ " } else /* we are already terminating */",
+ " { cpu_printf(\"SIGINT\\n\");",
+ " }",
+ "}",
+ "",
+ "void",
+ "check_overkill(void)",
+ "{",
+ " vmax_seen = (vmax_seen + 7)/ 8;",
+ " vmax_seen *= 8; /* round up to a multiple of 8 */",
+ "",
+ " if (core_id == 0",
+ " && !remote_party",
+ " && nstates_put > 0",
+ " && VMAX - vmax_seen > 8)",
+ " {",
+ "#ifdef BITSTATE",
+ " printf(\"cpu0: max VMAX value seen in this run: \");",
+ "#else",
+ " printf(\"cpu0: recommend recompiling with \");",
+ "#endif",
+ " printf(\"-DVMAX=%%d\\n\", vmax_seen);",
+ " }",
+ "}",
+ "",
+ "void",
+ "mem_put(int q) /* handoff state to other cpu, workq q */",
+ "{ SM_frame *f;",
+ " int i, j;",
+ "",
+ " if (vsize > VMAX)",
+ " { vsize = (vsize + 7)/8; vsize *= 8; /* round up */",
+ " printf(\"pan: recompile with -DVMAX=N with N >= %%d\\n\", vsize);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (now._nr_pr > PMAX)",
+ " { printf(\"pan: recompile with -DPMAX=N with N >= %%d\\n\", now._nr_pr);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (now._nr_qs > QMAX)",
+ " { printf(\"pan: recompile with -DQMAX=N with N >= %%d\\n\", now._nr_qs);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (vsize > vmax_seen) vmax_seen = vsize;",
+ " if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr;",
+ " if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs;",
+ "",
+ " f = Get_Free_Frame(q); /* not called in likely deadlock states */",
+ " if (!f) { Uerror(\"Fatal3: no free slot\"); }",
+ "",
+ " if (VVERBOSE) cpu_printf(\"putting state into q%%d\\n\", q);",
+ "",
+ " memcpy((uchar *) f->m_now, (uchar *) &now, vsize);",
+ " memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char));",
+ " for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+ " { if (Mask[i])",
+ " { f->m_Mask[i/8] |= (1<<j);",
+ " } }",
+ "",
+ " if (now._nr_pr > 0)",
+ " { memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT));",
+ " memcpy((uchar *) f->m_p_skip, (uchar *) proc_skip, now._nr_pr * sizeof(uchar));",
+ " }",
+ " if (now._nr_qs > 0)",
+ " { memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT));",
+ " memcpy((uchar *) f->m_q_skip, (uchar *) q_skip, now._nr_qs * sizeof(uchar));",
+ " }",
+ "#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+ " c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */",
+ "#endif",
+ "#ifdef FULL_TRAIL",
+ " f->m_stack = stack_last[core_id];",
+ "#endif",
+ " f->nr_handoffs = nr_handoffs+1;",
+ " f->m_tau = trpt->tau;",
+ " f->m_o_pm = trpt->o_pm;",
+ " f->m_boq = boq;",
+ " f->m_vsize = vsize; /* must come last - now the other cpu can see it */",
+ "",
+ " if (query_in_progress == 1)",
+ " query_in_progress = 2; /* make sure we know, if a query makes the rounds */",
+ " nstates_put++;",
+ "}",
+ "",
+ "#ifdef USE_DISK",
+ "int Dsk_W_Nr, Dsk_R_Nr;",
+ "int dsk_file = -1, dsk_read = -1;",
+ "unsigned long dsk_written, dsk_drained;",
+ "char dsk_name[512];",
+ "",
+ "#ifndef BFS_DISK",
+ "#if defined(WIN32) || defined(WIN64)",
+ " #define RFLAGS (O_RDONLY|O_BINARY)",
+ " #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)",
+ "#else",
+ " #define RFLAGS (O_RDONLY)",
+ " #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC)",
+ "#endif",
+ "#endif",
+ "",
+ "void",
+ "dsk_stats(void)",
+ "{ int i;",
+ "",
+ " if (dsk_written > 0)",
+ " { cpu_printf(\"dsk_written %%d states in %%d files\\ncpu%%d: dsk_drained %%6d states\\n\",",
+ " dsk_written, Dsk_W_Nr, core_id, dsk_drained);",
+ " close(dsk_read);",
+ " close(dsk_file);",
+ " for (i = 0; i < Dsk_W_Nr; i++)",
+ " { sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", i, core_id);",
+ " unlink(dsk_name);",
+ " } }",
+ "}",
+ "",
+ "void",
+ "mem_drain(void)",
+ "{ SM_frame *f, g;",
+ " int q = (core_id + 1) %% NCORE; /* target q */",
+ " int sz;",
+ "",
+ " if (dsk_read < 0",
+ " || dsk_written <= dsk_drained)",
+ " { return;",
+ " }",
+ "",
+ " while (dsk_written > dsk_drained",
+ " && TargetQ_NotFull(q))",
+ " { f = Get_Free_Frame(q);",
+ " if (!f) { Uerror(\"Fatal: unhandled condition\"); }",
+ "",
+ " if ((dsk_drained+1)%%MAX_DSK_FILE == 0) /* 100K states max per file */",
+ " { (void) close(dsk_read); /* close current read handle */",
+ " sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_R_Nr++, core_id);",
+ " (void) unlink(dsk_name); /* remove current file */",
+ " sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_R_Nr, core_id);",
+ " cpu_printf(\"reading %%s\\n\", dsk_name);",
+ " dsk_read = open(dsk_name, RFLAGS); /* open next file */",
+ " if (dsk_read < 0)",
+ " { Uerror(\"could not open dsk file\");",
+ " } }",
+ " if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame))",
+ " { Uerror(\"bad dsk file read\");",
+ " }",
+ " sz = g.m_vsize;",
+ " g.m_vsize = 0;",
+ " memcpy(f, &g, sizeof(SM_frame));",
+ " f->m_vsize = sz; /* last */",
+ "",
+ " dsk_drained++;",
+ " }",
+ "}",
+ "",
+ "void",
+ "mem_file(void)",
+ "{ SM_frame f;",
+ " int i, j, q = (core_id + 1) %% NCORE; /* target q */",
+ "",
+ " if (vsize > VMAX)",
+ " { printf(\"pan: recompile with -DVMAX=N with N >= %%d\\n\", vsize);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (now._nr_pr > PMAX)",
+ " { printf(\"pan: recompile with -DPMAX=N with N >= %%d\\n\", now._nr_pr);",
+ " Uerror(\"aborting\");",
+ " }",
+ " if (now._nr_qs > QMAX)",
+ " { printf(\"pan: recompile with -DQMAX=N with N >= %%d\\n\", now._nr_qs);",
+ " Uerror(\"aborting\");",
+ " }",
+ "",
+ " if (VVERBOSE) cpu_printf(\"filing state for q%%d\\n\", q);",
+ "",
+ " memcpy((uchar *) f.m_now, (uchar *) &now, vsize);",
+ " memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char));",
+ " for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+ " { if (Mask[i])",
+ " { f.m_Mask[i/8] |= (1<<j);",
+ " } }",
+ "",
+ " if (now._nr_pr > 0)",
+ " { memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT));",
+ " memcpy((uchar *)f.m_p_skip, (uchar *)proc_skip, now._nr_pr*sizeof(uchar));",
+ " }",
+ " if (now._nr_qs > 0)",
+ " { memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT));",
+ " memcpy((uchar *) f.m_q_skip, (uchar *) q_skip, now._nr_qs*sizeof(uchar));",
+ " }",
+ "#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+ " c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */",
+ "#endif",
+ "#ifdef FULL_TRAIL",
+ " f.m_stack = stack_last[core_id];",
+ "#endif",
+ " f.nr_handoffs = nr_handoffs+1;",
+ " f.m_tau = trpt->tau;",
+ " f.m_o_pm = trpt->o_pm;",
+ " f.m_boq = boq;",
+ " f.m_vsize = vsize;",
+ "",
+ " if (query_in_progress == 1)",
+ " { query_in_progress = 2;",
+ " }",
+ " if (dsk_file < 0)",
+ " { sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_W_Nr, core_id);",
+ " dsk_file = open(dsk_name, WFLAGS, 0644);",
+ " dsk_read = open(dsk_name, RFLAGS);",
+ " if (dsk_file < 0 || dsk_read < 0)",
+ " { cpu_printf(\"File: <%%s>\\n\", dsk_name);",
+ " Uerror(\"cannot open diskfile\");",
+ " }",
+ " Dsk_W_Nr++; /* nr of next file to open */",
+ " cpu_printf(\"created temporary diskfile %%s\\n\", dsk_name);",
+ " } else if ((dsk_written+1)%%MAX_DSK_FILE == 0)",
+ " { close(dsk_file); /* close write handle */",
+ " sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_W_Nr++, core_id);",
+ " dsk_file = open(dsk_name, WFLAGS, 0644);",
+ " if (dsk_file < 0)",
+ " { cpu_printf(\"File: <%%s>\\n\", dsk_name);",
+ " Uerror(\"aborting: cannot open new diskfile\");",
+ " }",
+ " cpu_printf(\"created temporary diskfile %%s\\n\", dsk_name);",
+ " }",
+ " if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame))",
+ " { Uerror(\"aborting -- disk write failed (disk full?)\");",
+ " }",
+ " nstates_put++;",
+ " dsk_written++;",
+ "}",
+ "#endif",
+ "",
+ "int",
+ "mem_hand_off(void)",
+ "{",
+ " if (search_terminated == NULL",
+ " || *search_terminated != 0) /* not a full crash check */",
+ " { pan_exit(0);",
+ " }",
+ " iam_alive(); /* on every transition of Down */",
+ "#ifdef USE_DISK",
+ " mem_drain(); /* maybe call this also on every Up */",
+ "#endif",
+ " if (depth > z_handoff /* above handoff limit */",
+ "#ifndef SAFETY",
+ " && !a_cycles /* not in liveness mode */",
+ "#endif",
+ "#if SYNC",
+ " && boq == -1 /* not mid-rv */",
+ "#endif",
+ "#ifdef VERI",
+ " && (trpt->tau&4) /* claim moves first */",
+ " && !((trpt-1)->tau&128) /* not a stutter move */",
+ "#endif",
+ " && !(trpt->tau&8)) /* not an atomic move */",
+ " { int q = (core_id + 1) %% NCORE; /* circular handoff */",
+ " #ifdef GENEROUS",
+ " if (prcnt[q] < LN_FRAMES)", /* not the best strategy */
+ " #else",
+ " if (TargetQ_NotFull(q)",
+ " && (dfs_phase2 == 0 || prcnt[core_id] > 0))", /* not locked, ok if race */
+ " #endif",
+ " { mem_put(q);", /* only 1 writer: lock-free */
+ " return 1;",
+ " }",
+ " { int rval;",
+ " #ifndef NGQ",
+ " rval = GlobalQ_HasRoom();",
+ " #else",
+ " rval = 0;",
+ " #endif",
+ " #ifdef USE_DISK",
+ " if (rval == 0)",
+ " { void mem_file(void);",
+ " mem_file();",
+ " rval = 1;",
+ " }",
+ " #endif",
+ " return rval;",
+ " }",
+ " }",
+ " return 0; /* i.e., no handoff */",
+ "}",
+ "",
+ "void",
+ "mem_put_acc(void) /* liveness mode */",
+ "{ int q = (core_id + 1) %% NCORE;",
+ "",
+ " if (search_terminated == NULL",
+ " || *search_terminated != 0)",
+ " { pan_exit(0);",
+ " }",
+ "#ifdef USE_DISK",
+ " mem_drain();",
+ "#endif",
+ " /* some tortured use of preprocessing: */",
+ "#if !defined(NGQ) || defined(USE_DISK)",
+ " if (TargetQ_Full(q))",
+ " {",
+ "#endif",
+ "#ifndef NGQ",
+ " if (GlobalQ_HasRoom())",
+ " { return;",
+ " }",
+ "#endif",
+ "#ifdef USE_DISK",
+ " mem_file();",
+ " } else",
+ "#else",
+ " #if !defined(NGQ) || defined(USE_DISK)",
+ " }",
+ " #endif",
+ "#endif",
+ " { mem_put(q);",
+ " }",
+ "}",
+ "",
+ "#if defined(WIN32) || defined(WIN64)", /* visual studio */
+ "void",
+ "init_shm(void) /* initialize shared work-queues */",
+ "{ char key[512];",
+ " int n, m;",
+ " int must_exit = 0;",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 3: allocate shared work-queues %%g Mb\\n\",",
+ " ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.));",
+ " }",
+ " for (m = 0; m < NR_QS; m++) /* last q is global 1 */",
+ " { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;",
+ " sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, m);",
+ " if (core_id == 0)", /* root process creates shared memory segments */
+ " { shmid[m] = CreateFileMapping(",
+ " INVALID_HANDLE_VALUE, /* use paging file */",
+ " NULL, /* default security */",
+ " PAGE_READWRITE, /* access permissions */",
+ " 0, /* high-order 4 bytes */",
+ " qsize, /* low-order bytes, size in bytes */",
+ " key); /* name */",
+ " } else /* worker nodes just open these segments */",
+ " { shmid[m] = OpenFileMapping(",
+ " FILE_MAP_ALL_ACCESS, /* read/write access */",
+ " FALSE, /* children do not inherit handle */",
+ " key);",
+ " }",
+ " if (shmid[m] == NULL)",
+ " { fprintf(stderr, \"cpu%%d: could not create or open shared queues\\n\",",
+ " core_id);",
+ " must_exit = 1;",
+ " break;",
+ " }",
+ " /* attach: */",
+ " shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0);",
+ " if (shared_mem[m] == NULL)",
+ " { fprintf(stderr, \"cpu%%d: cannot attach shared q%%d (%%d Mb)\\n\",",
+ " core_id, m+1, (int) (qsize/(1048576.)));",
+ " must_exit = 1;",
+ " break;",
+ " }",
+ "",
+ " memcnt += qsize;",
+ "",
+ " m_workq[m] = (SM_frame *) shared_mem[m];",
+ " if (core_id == 0)",
+ " { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;",
+ " for (n = 0; n < nframes; n++)",
+ " { m_workq[m][n].m_vsize = 0;",
+ " m_workq[m][n].m_boq = 0;",
+ " } } }",
+ "",
+ " if (must_exit)",
+ " { fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1); /* calls cleanup_shm */",
+ " }",
+ "}",
+ "",
+ "static uchar *",
+ "prep_shmid_S(size_t n) /* either sets SS or H_tab, WIN32/WIN64 */",
+ "{ char *rval;",
+ "#ifndef SEP_STATE",
+ " char key[512];",
+ "",
+ " if (verbose && core_id == 0)",
+ " {",
+ " #ifdef BITSTATE",
+ " printf(\"cpu0: step 1: allocate shared bitstate %%g Mb\\n\",",
+ " (double) n / (1048576.));",
+ " #else",
+ " printf(\"cpu0: step 1: allocate shared hastable %%g Mb\\n\",",
+ " (double) n / (1048576.));",
+ " #endif",
+ " }",
+ " #ifdef MEMLIM",
+ " if (memcnt + (double) n > memlim)",
+ " { printf(\"cpu%%d: S %%8g + %%d Kb exceeds memory limit of %%8g Mb\\n\",",
+ " core_id, memcnt/1024., n/1024, memlim/(1048576.));",
+ " printf(\"cpu%%d: insufficient memory -- aborting\\n\", core_id);",
+ " exit(1);",
+ " }",
+ " #endif",
+ "",
+ " /* make key different from queues: */",
+ " sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, NCORE+2); /* different from qs */",
+ "",
+ " if (core_id == 0) /* root */",
+ " { shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,",
+ "#ifdef WIN64",
+ " PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);",
+ "#else",
+ " PAGE_READWRITE, 0, n, key);",
+ "#endif",
+ " memcnt += (double) n;",
+ " } else /* worker */",
+ " { shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);",
+ " }",
+
+ " if (shmid_S == NULL)",
+ " {",
+ " #ifdef BITSTATE",
+ " fprintf(stderr, \"cpu%%d: cannot %%s shared bitstate\",",
+ " core_id, core_id?\"open\":\"create\");",
+ " #else",
+ " fprintf(stderr, \"cpu%%d: cannot %%s shared hashtable\",",
+ " core_id, core_id?\"open\":\"create\");",
+ " #endif",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "",
+ " rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */",
+ " if ((char *) rval == NULL)",
+ " { fprintf(stderr, \"cpu%%d: cannot attach shared bitstate or hashtable\\n\", core_id);",
+ " fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+ " pan_exit(1);",
+ " }",
+ "#else",
+ " rval = (char *) emalloc(n);",
+ "#endif",
+ " return (uchar *) rval;",
+ "}",
+ "",
+ "static uchar *",
+ "prep_state_mem(size_t n) /* WIN32/WIN64 sets memory arena for states */",
+ "{ char *rval;",
+ " char key[512];",
+ " static int cnt = 3; /* start larger than earlier ftok calls */",
+ "",
+ " if (verbose && core_id == 0)",
+ " { printf(\"cpu0: step 2+: pre-allocate memory arena %%d of %%g Mb\\n\",",
+ " cnt-3, (double) n / (1048576.));",
+ " }",
+ " #ifdef MEMLIM",
+ " if (memcnt + (double) n > memlim)",
+ " { printf(\"cpu%%d: error: M %%.0f + %%.0f exceeds memory limit of %%.0f Kb\\n\",",
+ " core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0);",
+ " return NULL;",
+ " }",
+ " #endif",
+ "",
+ " sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, NCORE+cnt); cnt++;",
+ "",
+ " if (core_id == 0)",
+ " { shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,",
+ "#ifdef WIN64",
+ " PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);",
+ "#else",
+ " PAGE_READWRITE, 0, n, key);",
+ "#endif",
+ " } else",
+ " { shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);",
+ " }",
+ " if (shmid_M == NULL)",
+ " { printf(\"cpu%%d: failed to get pool of shared memory nr %%d of size %%d\\n\",",
+ " core_id, cnt-3, n);",
+ " printf(\"pan: check './pan --' for usage details\\n\");",
+ " return NULL;",
+ " }",
+ " rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */",
+ "",
+ " if (rval == NULL)",
+ " { printf(\"cpu%%d: failed to attach pool of shared memory nr %%d of size %%d\\n\",",
+ " core_id, cnt-3, n);",
+ " return NULL;",
+ " }",
+ " return (uchar *) rval;",
+ "}",
+ "",
+ "void",
+ "init_HT(unsigned long n) /* WIN32/WIN64 version */",
+ "{ volatile char *x;",
+ " double get_mem;",
+ "#ifndef SEP_STATE",
+ " char *dc_mem_start;",
+ "#endif",
+ " if (verbose) printf(\"cpu%%d: initialization for Windows\\n\", core_id);",
+ "",
+"#ifdef SEP_STATE",
+ " #ifndef MEMLIM",
+ " if (verbose)",
+ " { printf(\"cpu0: steps 0,1: no -DMEMLIM set\\n\");",
+ " }",
+ " #else",
+ " if (verbose)",
+ " printf(\"cpu0: steps 0,1: -DMEMLIM=%%d Mb - (hashtable %%g Mb + workqueues %%g Mb)\\n\",",
+ " MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.));",
+ "#endif",
+ " get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double);",
+ " /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */",
+ " get_mem += 4 * NCORE * sizeof(void *);", /* prfree, prfull, prcnt, prmax */
+ " #ifdef FULL_TRAIL",
+ " get_mem += (NCORE) * sizeof(Stack_Tree *);",
+ " /* NCORE * stack_last */",
+ " #endif",
+ " x = (volatile char *) prep_state_mem((size_t) get_mem);",
+ " shmid_X = (void *) x;",
+ " if (x == NULL)",
+ " { printf(\"cpu0: could not allocate shared memory, see ./pan --\\n\");",
+ " exit(1);",
+ " }",
+ " search_terminated = (volatile unsigned int *) x; /* comes first */",
+ " x += sizeof(void *); /* maintain alignment */",
+ "",
+ " is_alive = (volatile double *) x;",
+ " x += NCORE * sizeof(double);",
+ "",
+ " sh_lock = (volatile int *) x;",
+ " x += CS_NR * sizeof(void *); /* allow 1 word per entry */",
+ "",
+ " grfree = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grfull = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grcnt = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grmax = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " prfree = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prfull = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prcnt = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prmax = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " gr_readmiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ " gr_writemiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ "",
+ " #ifdef FULL_TRAIL",
+ " stack_last = (volatile Stack_Tree **) x;",
+ " x += NCORE * sizeof(Stack_Tree *);",
+ " #endif",
+ "",
+ " #ifndef BITSTATE",
+ " H_tab = (struct H_el **) emalloc(n);",
+ " #endif",
+"#else",
+ " #ifndef MEMLIM",
+ " #warning MEMLIM not set", /* cannot happen */
+ " #define MEMLIM (2048)",
+ " #endif",
+ "",
+ " if (core_id == 0 && verbose)",
+ " printf(\"cpu0: step 0: -DMEMLIM=%%d Mb - (hashtable %%g Mb + workqueues %%g Mb) = %%g Mb for state storage\\n\",",
+ " MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),",
+ " (memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));",
+ " #ifndef BITSTATE",
+ " H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */",
+ " #endif",
+ " get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE;",
+ " if (get_mem <= 0)",
+ " { Uerror(\"internal error -- shared state memory\");",
+ " }",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 2: shared state memory %%g Mb\\n\",",
+ " get_mem/(1048576.));",
+ " }",
+ " x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem); /* for states */",
+ " if (x == NULL)",
+ " { printf(\"cpu%%d: insufficient memory -- aborting\\n\", core_id);",
+ " exit(1);",
+ " }",
+ "",
+ " search_terminated = (volatile unsigned int *) x; /* comes first */",
+ " x += sizeof(void *); /* maintain alignment */",
+ "",
+ " is_alive = (volatile double *) x;",
+ " x += NCORE * sizeof(double);",
+ "",
+ " sh_lock = (volatile int *) x;",
+ " x += CS_NR * sizeof(int);",
+ "",
+ " grfree = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grfull = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grcnt = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " grmax = (volatile int *) x;",
+ " x += sizeof(void *);",
+ " prfree = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prfull = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prcnt = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " prmax = (volatile int *) x;",
+ " x += NCORE * sizeof(void *);",
+ " gr_readmiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ " gr_writemiss = (volatile double *) x;",
+ " x += sizeof(double);",
+ "",
+ " #ifdef FULL_TRAIL",
+ " stack_last = (volatile Stack_Tree **) x;",
+ " x += NCORE * sizeof(Stack_Tree *);",
+ " #endif",
+ " if (((long)x)&(sizeof(void *)-1)) /* word alignment */",
+ " { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */",
+ " }",
+ "",
+ " #ifdef COLLAPSE",
+ " ncomps = (unsigned long *) x;",
+ " x += (256+2) * sizeof(unsigned long);",
+ " #endif",
+ "",
+ " dc_shared = (sh_Allocater *) x; /* in shared memory */",
+ " x += sizeof(sh_Allocater);",
+ "",
+ " if (core_id == 0) /* root only */",
+ " { dc_shared->dc_id = shmid_M;",
+ " dc_shared->dc_start = (void *) dc_mem_start;",
+ " dc_shared->dc_arena = x;",
+ " dc_shared->pattern = 1234567;",
+ " dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start);",
+ " dc_shared->nxt = NULL;",
+ " }",
+"#endif",
+ "}",
+ "",
+ "#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__)",
+ "extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit);",
+ "int",
+ "tas(volatile LONG *s)", /* atomic test and set */
+ "{ return InterlockedBitTestAndSet(s, 1);",
+ "}",
+ "#else",
+ " #error missing definition of test and set operation for this platform",
+ "#endif",
+ "",
+ "void",
+ "cleanup_shm(int val)",
+ "{ int m;",
+ " static int nibis = 0;",
+ "",
+ " if (nibis != 0)",
+ " { printf(\"cpu%%d: Redundant call to cleanup_shm(%%d)\\n\", core_id, val);",
+ " return;",
+ " } else",
+ " { nibis = 1;",
+ " }",
+ " if (search_terminated != NULL)",
+ " { *search_terminated |= 16; /* cleanup_shm */",
+ " }",
+ "",
+ " for (m = 0; m < NR_QS; m++)",
+ " { if (shmid[m] != NULL)",
+ " { UnmapViewOfFile((char *) shared_mem[m]);",
+ " CloseHandle(shmid[m]);",
+ " } }",
+ "#ifdef SEP_STATE",
+ " UnmapViewOfFile((void *) shmid_X);",
+ " CloseHandle((void *) shmid_M);",
+ "#else",
+ " #ifdef BITSTATE",
+ " if (shmid_S != NULL)",
+ " { UnmapViewOfFile(SS);",
+ " CloseHandle(shmid_S);",
+ " }",
+ " #else",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: done, %%ld Mb of shared state memory left\\n\",",
+ " dc_shared->dc_size / (long)(1048576));",
+ " }",
+ " if (shmid_S != NULL)",
+ " { UnmapViewOfFile(H_tab);",
+ " CloseHandle(shmid_S);",
+ " }",
+ " shmid_M = (void *) (dc_shared->dc_id);",
+ " UnmapViewOfFile((char *) dc_shared->dc_start);",
+ " CloseHandle(shmid_M);",
+ " #endif",
+ "#endif",
+ " /* detached from shared memory - so cannot use cpu_printf */",
+ " if (verbose)",
+ " { printf(\"cpu%%d: done -- got %%d states from queue\\n\",",
+ " core_id, nstates_get);",
+ " }",
+ "}",
+ "",
+ "void",
+ "mem_get(void)",
+ "{ SM_frame *f;",
+ " int is_parent;",
+ "",
+ "#if defined(MA) && !defined(SEP_STATE)",
+ " #error MA requires SEP_STATE in multi-core mode",
+ "#endif",
+ "#ifdef BFS",
+ " #error BFS is not supported in multi-core mode",
+ "#endif",
+ "#ifdef SC",
+ " #error SC is not supported in multi-core mode",
+ "#endif",
+ " init_shm(); /* we are single threaded when this starts */",
+ " signal(SIGINT, give_up); /* windows control-c interrupt */",
+ "",
+ " if (core_id == 0 && verbose)",
+ " { printf(\"cpu0: step 4: creating additional workers (proxy %%d)\\n\",",
+ " proxy_pid);",
+ " }",
+ "#if 0",
+ " if NCORE > 1 the child or the parent should fork N-1 more times",
+ " the parent is the only process with core_id == 0 and is_parent > 0",
+ " the others (workers) have is_parent = 0 and core_id = 1..NCORE-1",
+ "#endif",
+ " if (core_id == 0) /* root starts up the workers */",
+ " { worker_pids[0] = (DWORD) getpid(); /* for completeness */",
+ " while (++core_id < NCORE) /* first worker sees core_id = 1 */",
+ " { char cmdline[64];",
+ " STARTUPINFO si = { sizeof(si) };",
+ " PROCESS_INFORMATION pi;",
+ "",
+ " if (proxy_pid == core_id) /* always non-zero */",
+ " { sprintf(cmdline, \"pan_proxy.exe -r %%s-Q%%d -Z%%d\",",
+ " o_cmdline, getpid(), core_id);",
+ " } else",
+ " { sprintf(cmdline, \"pan.exe %%s-Q%%d -Z%%d\",",
+ " o_cmdline, getpid(), core_id);",
+ " }",
+ " if (verbose) printf(\"cpu%%d: spawn %%s\\n\", core_id, cmdline);",
+ "",
+ " is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi);",
+ " if (is_parent == 0)",
+ " { Uerror(\"fork failed\");",
+ " }",
+ " worker_pids[core_id] = pi.dwProcessId;",
+ " worker_handles[core_id] = pi.hProcess;",
+ " if (verbose)",
+ " { cpu_printf(\"created core %%d, pid %%d\\n\",",
+ " core_id, pi.dwProcessId);",
+ " }",
+ " if (proxy_pid == core_id) /* we just created the receive half */",
+ " { /* add proxy send, store pid in proxy_pid_snd */",
+ " sprintf(cmdline, \"pan_proxy.exe -s %%s-Q%%d -Z%%d -Y%%d\",",
+ " o_cmdline, getpid(), core_id, worker_pids[proxy_pid]);",
+ " if (verbose) printf(\"cpu%%d: spawn %%s\\n\", core_id, cmdline);",
+ " is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi);",
+ " if (is_parent == 0)",
+ " { Uerror(\"fork failed\");",
+ " }",
+ " proxy_pid_snd = pi.dwProcessId;",
+ " proxy_handle_snd = pi.hProcess;",
+ " if (verbose)",
+ " { cpu_printf(\"created core %%d, pid %%d (send proxy)\\n\",",
+ " core_id, pi.dwProcessId);",
+ " } } }",
+ " core_id = 0; /* reset core_id for root process */",
+ " } else /* worker */",
+ " { static char db0[16]; /* good for up to 10^6 cores */",
+ " static char db1[16];",
+ " tprefix = db0; sprefix = db1;",
+ " sprintf(tprefix, \"cpu%%d_trail\", core_id); /* avoid conflicts on file access */",
+ " sprintf(sprefix, \"cpu%%d_rst\", core_id);",
+ " memcnt = 0; /* count only additionally allocated memory */",
+ " }",
+ " if (verbose)",
+ " { cpu_printf(\"starting core_id %%d -- pid %%d\\n\", core_id, getpid());",
+ " }",
+ " if (core_id == 0 && !remote_party)",
+ " { new_state(); /* root starts the search */",
+ " if (verbose)",
+ " cpu_printf(\"done with 1st dfs, nstates %%g (put %%d states), start reading q\\n\",",
+ " nstates, nstates_put);",
+ " dfs_phase2 = 1;",
+ " }",
+ " Read_Queue(core_id); /* all cores */",
+ "",
+ " if (verbose)",
+ " { cpu_printf(\"put %%6d states into queue -- got %%6d\\n\",",
+ " nstates_put, nstates_get);",
+ " }",
+ " done = 1;",
+ " wrapup();",
+ " exit(0);",
+ "}",
+ "#endif", /* WIN32 || WIN64 */
+ "",
+ "#ifdef BITSTATE",
+ "void",
+ "init_SS(unsigned long n)",
+ "{",
+ " SS = (uchar *) prep_shmid_S((size_t) n);",
+ " init_HT(0L);", /* locks and shared memory for Stack_Tree allocations */
+ "}",
+ "#endif", /* BITSTATE */
+ "",
+ "#endif", /* NCORE>1 */
+ 0,
+};
--- /dev/null
+/***** spin: pc_zpp.c *****/
+
+/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* pc_zpp.c is only used in the PC version of Spin */
+/* it is included to avoid too great a reliance on an external cpp */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef PC
+enum cstate { PLAIN, IN_STRING, IN_QUOTE, S_COMM, COMMENT, E_COMM };
+
+#define MAXNEST 32
+#define MAXDEF 128
+#define MAXLINE 2048
+#define GENEROUS 8192
+
+#define debug(x,y) if (verbose) printf(x,y)
+
+static FILE *outpp /* = stdout */;
+
+static int if_truth[MAXNEST];
+static int printing[MAXNEST];
+static int if_depth, nr_defs, verbose = 0;
+static enum cstate state = PLAIN;
+static char Out1[GENEROUS], Out2[GENEROUS];
+
+static struct Defines {
+ int exists;
+ char *src, *trg;
+} d[MAXDEF];
+
+static int process(char *, int, char *);
+static int zpp_do(char *);
+
+extern char *emalloc(size_t); /* main.c */
+
+static int
+do_define(char *p)
+{ char *q, *r, *s;
+
+ for (q = p+strlen(p)-1; q > p; q--)
+ if (*q == '\n' || *q == '\t' || *q == ' ')
+ *q = '\0';
+ else
+ break;
+
+ q = p + strspn(p, " \t");
+ if (!(r = strchr(q, '\t')))
+ r = strchr(q, ' ');
+ if (!r) { s = ""; goto adddef; }
+ s = r + strspn(r, " \t");
+ *r = '\0';
+ if (strchr(q, '('))
+ { debug("zpp: #define with arguments %s\n", q);
+ return 0;
+ }
+ for (r = q+strlen(q)-1; r > q; r--)
+ if (*r == ' ' || *r == '\t')
+ *r = '\0';
+ else
+ break;
+ if (nr_defs >= MAXDEF)
+ { debug("zpp: too many #defines (max %d)\n", nr_defs);
+ return 0;
+ }
+ if (strcmp(q, s) != 0)
+ { int j;
+adddef: for (j = 0; j < nr_defs; j++)
+ if (!strcmp(d[j].src, q))
+ d[j].exists = 0;
+ d[nr_defs].src = emalloc(strlen(q)+1);
+ d[nr_defs].trg = emalloc(strlen(s)+1);
+ strcpy(d[nr_defs].src, q);
+ strcpy(d[nr_defs].trg, s);
+ d[nr_defs++].exists = 1;
+ }
+ return 1;
+}
+
+static int
+isvalid(int c)
+{
+ return (isalnum(c) || c == '_');
+}
+
+static char *
+apply(char *p0)
+{ char *out, *in1, *in2, *startat;
+ int i, j;
+
+ startat = in1 = Out2; strcpy(Out2, p0);
+ out = Out1; *out = '\0';
+
+ for (i = nr_defs-1; i >= 0; i--)
+ { if (!d[i].exists) continue;
+ j = (int) strlen(d[i].src);
+more: in2 = strstr(startat, d[i].src);
+ if (!in2) /* no more matches */
+ { startat = in1;
+ continue;
+ }
+ if ((in2 == in1 || !isvalid(*(in2-1)))
+ && (in2+j == '\0' || !isvalid(*(in2+j))))
+ { *in2 = '\0';
+
+ if (strlen(in1)+strlen(d[i].trg)+strlen(in2+j) >= GENEROUS)
+ {
+ printf("spin: macro expansion overflow %s -> %s ?\n",
+ d[i].src, d[i].trg);
+ return in1;
+ }
+ strcat(out, in1);
+ strcat(out, d[i].trg);
+ strcat(out, in2+j);
+ if (in1 == Out2)
+ { startat = in1 = Out1;
+ out = Out2;
+ } else
+ { startat = in1 = Out2;
+ out = Out1;
+ }
+ *out = '\0';
+ } else
+ { startat = in2+1; /* +1 not +j.. */
+ }
+ goto more; /* recursive defines */
+ }
+ return in1;
+}
+
+static char *
+do_common(char *p)
+{ char *q, *s;
+
+ q = p + strspn(p, " \t");
+ for (s = (q + strlen(q) - 1); s > q; s--)
+ if (*s == ' ' || *s == '\t' || *s == '\n')
+ *s = '\0';
+ else
+ break;
+ return q;
+}
+
+static int
+do_undefine(char *p)
+{ int i; char *q = do_common(p);
+
+ for (i = 0; i < nr_defs; i++)
+ if (!strcmp(d[i].src, q))
+ d[i].exists = 0;
+ return 1;
+}
+
+static char *
+check_ifdef(char *p)
+{ int i; char *q = do_common(p);
+
+ for (i = 0; i < nr_defs; i++)
+ if (d[i].exists
+ && !strcmp(d[i].src, q))
+ return d[i].trg;
+ return (char *) 0;
+}
+
+static int
+do_ifdef(char *p)
+{
+ if (++if_depth >= MAXNEST)
+ { debug("zpp: too deeply nested (max %d)\n", MAXNEST);
+ return 0;
+ }
+ if_truth[if_depth] = (check_ifdef(p) != (char *)0);
+ printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
+
+ return 1;
+}
+
+static int
+do_ifndef(char *p)
+{
+ if (++if_depth >= MAXNEST)
+ { debug("zpp: too deeply nested (max %d)\n", MAXNEST);
+ return 0;
+ }
+ if_truth[if_depth] = (check_ifdef(p) == (char *)0);
+ printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
+
+ return 1;
+}
+
+static int
+is_simple(char *q)
+{
+ if (!q) return 0;
+ if (strcmp(q, "0") == 0)
+ if_truth[if_depth] = 0;
+ else if (strcmp(q, "1") == 0)
+ if_truth[if_depth] = 1;
+ else
+ return 0;
+ return 1;
+}
+
+static int
+do_if(char *p)
+{ char *q = do_common(p);
+ if (++if_depth >= MAXNEST)
+ { debug("zpp: too deeply nested (max %d)\n", MAXNEST);
+ return 0;
+ }
+ if (!is_simple(q)
+ && !is_simple(check_ifdef(q)))
+ { debug("zpp: cannot handle #if %s\n", q);
+ return 0;
+ }
+ printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
+
+ return 1;
+}
+
+static int
+do_else(char *unused)
+{
+ if_truth[if_depth] = 1-if_truth[if_depth];
+ printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
+
+ return 1;
+}
+
+static int
+do_endif(char *p)
+{
+ if (--if_depth < 0)
+ { debug("zpp: unbalanced #endif %s\n", p);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+do_include(char *p)
+{ char *r, *q;
+
+ q = strchr(p, '<');
+ r = strrchr(p, '>');
+ if (!q || !r)
+ { q = strchr (p, '\"');
+ r = strrchr(p, '\"');
+ if (!q || !r || q == r)
+ { debug("zpp: malformed #include %s", p);
+ return 0;
+ } }
+ *r = '\0';
+ return zpp_do(++q);
+}
+
+static int
+in_comment(char *p)
+{ char *q = p;
+
+ for (q = p; *q != '\n' && *q != '\0'; q++)
+ switch (state) {
+ case PLAIN:
+ switch (*q) {
+ case '"': state = IN_STRING; break;
+ case '\'': state = IN_QUOTE; break;
+ case '/': state = S_COMM; break;
+ case '\\': q++; break;
+ }
+ break;
+ case IN_STRING:
+ if (*q == '"') state = PLAIN;
+ else if (*q == '\\') q++;
+ break;
+ case IN_QUOTE:
+ if (*q == '\'') state = PLAIN;
+ else if (*q == '\\') q++;
+ break;
+ case S_COMM:
+ if (*q == '*')
+ { *(q-1) = *q = ' ';
+ state = COMMENT;
+ } else if (*q != '/')
+ state = PLAIN;
+ break;
+ case COMMENT:
+ state = (*q == '*') ? E_COMM: COMMENT;
+ *q = ' ';
+ break;
+ case E_COMM:
+ if (*q == '/')
+ state = PLAIN;
+ else if (*q != '*')
+ state = COMMENT;
+ *q = ' ';
+ break;
+ }
+ if (state == S_COMM) state = PLAIN;
+ else if (state == E_COMM) state = COMMENT;
+ return (state == COMMENT);
+}
+
+static int
+zpp_do(char *fnm)
+{ char buf[2048], buf2[MAXLINE], *p; int n, on;
+ FILE *inp; int lno = 0, nw_lno = 0;
+
+ if ((inp = fopen(fnm, "r")) == NULL)
+ { fprintf(stdout, "spin: error, '%s': No such file\n", fnm);
+ return 0; /* 4.1.2 was stderr */
+ }
+ printing[0] = if_truth[0] = 1;
+ fprintf(outpp, "#line %d \"%s\"\n", lno+1, fnm);
+ while (fgets(buf, MAXLINE, inp))
+ { lno++; n = (int) strlen(buf);
+ on = 0; nw_lno = 0;
+ while (n > 2 && buf[n-2] == '\\')
+ { buf[n-2] = '\0';
+feedme: if (!fgets(buf2, MAXLINE, inp))
+ { debug("zpp: unexpected EOF ln %d\n", lno);
+ return 0; /* switch to cpp */
+ }
+ lno++;
+ if (n + (int) strlen(buf2) >= 2048)
+ { debug("zpp: line %d too long\n", lno);
+ return 0;
+ }
+ strcat(buf, buf2);
+ n = (int) strlen(buf);
+ }
+ if (in_comment(&buf[on]))
+ { buf[n-1] = '\0'; /* eat newline */
+ on = n-1; nw_lno = 1;
+ goto feedme;
+ }
+ p = buf + strspn(buf, " \t");
+ if (nw_lno && *p != '#')
+ fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
+ if (*p == '#')
+ { if (!process(p+1, lno+1, fnm))
+ return 0;
+ } else if (printing[if_depth])
+ fprintf(outpp, "%s", apply(buf));
+ }
+ fclose(inp);
+ return 1;
+}
+
+int
+try_zpp(char *fnm, char *onm)
+{ int r;
+ if ((outpp = fopen(onm, "w")) == NULL)
+ return 0;
+ r = zpp_do(fnm);
+ fclose(outpp);
+ return r; /* 1 = ok; 0 = use cpp */
+}
+
+static struct Directives {
+ int len;
+ char *directive;
+ int (*handler)(char *);
+ int interp;
+} s[] = {
+ { 6, "define", do_define, 1 },
+ { 4, "else", do_else, 0 },
+ { 5, "endif", do_endif, 0 },
+ { 5, "ifdef", do_ifdef, 0 },
+ { 6, "ifndef", do_ifndef, 0 },
+ { 2, "if", do_if, 0 },
+ { 7, "include", do_include, 1 },
+ { 8, "undefine", do_undefine, 1 },
+};
+
+static int
+process(char *q, int lno, char *fnm)
+{ char *p; int i, r;
+
+ for (p = q; *p; p++)
+ if (*p != ' ' && *p != '\t')
+ break;
+ for (i = 0; i < (int) (sizeof(s)/sizeof(struct Directives)); i++)
+ if (!strncmp(s[i].directive, p, s[i].len))
+ { if (s[i].interp
+ && !printing[if_depth])
+ return 1;
+ fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
+ r = s[i].handler(p + s[i].len);
+ if (i == 6) /* include */
+ fprintf(outpp, "#line %d \"%s\"\n", lno, fnm);
+ return r;
+ }
+
+ debug("zpp: unrecognized directive: %s", p);
+ return 0;
+}
+#endif
--- /dev/null
+/***** spin: ps_msc.c *****/
+
+/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* The Postscript generation code below was written by Gerard J. Holzmann */
+/* in June 1997. Parts of the prolog template are based on similar boiler */
+/* plate in the Tcl/Tk distribution. This code is used to support Spin's */
+/* option -M for generating a Postscript file from a simulation run. */
+
+#include "spin.h"
+#include "version.h"
+
+/* extern void free(void *); */
+
+static char *PsPre[] = {
+ "%%%%Pages: (atend)",
+ "%%%%PageOrder: Ascend",
+ "%%%%DocumentData: Clean7Bit",
+ "%%%%Orientation: Portrait",
+ "%%%%DocumentNeededResources: font Courier-Bold",
+ "%%%%EndComments",
+ "",
+ "%%%%BeginProlog",
+ "50 dict begin",
+ "",
+ "/baseline 0 def",
+ "/height 0 def",
+ "/justify 0 def",
+ "/lineLength 0 def",
+ "/spacing 0 def",
+ "/stipple 0 def",
+ "/strings 0 def",
+ "/xoffset 0 def",
+ "/yoffset 0 def",
+ "",
+ "/ISOEncode {",
+ " dup length dict begin",
+ " {1 index /FID ne {def} {pop pop} ifelse} forall",
+ " /Encoding ISOLatin1Encoding def",
+ " currentdict",
+ " end",
+ " /Temporary exch definefont",
+ "} bind def",
+ "",
+ "/AdjustColor {",
+ " CL 2 lt {",
+ " currentgray",
+ " CL 0 eq {",
+ " .5 lt {0} {1} ifelse",
+ " } if",
+ " setgray",
+ " } if",
+ "} bind def",
+ "",
+ "/DrawText {",
+ " /stipple exch def",
+ " /justify exch def",
+ " /yoffset exch def",
+ " /xoffset exch def",
+ " /spacing exch def",
+ " /strings exch def",
+ " /lineLength 0 def",
+ " strings {",
+ " stringwidth pop",
+ " dup lineLength gt {/lineLength exch def} {pop} ifelse",
+ " newpath",
+ " } forall",
+ " 0 0 moveto (TXygqPZ) false charpath",
+ " pathbbox dup /baseline exch def",
+ " exch pop exch sub /height exch def pop",
+ " newpath",
+ " translate",
+ " lineLength xoffset mul",
+ " strings length 1 sub spacing mul height add yoffset mul translate",
+ " justify lineLength mul baseline neg translate",
+ " strings {",
+ " dup stringwidth pop",
+ " justify neg mul 0 moveto",
+ " stipple {",
+ " gsave",
+ " /char (X) def",
+ " {",
+ " char 0 3 -1 roll put",
+ " currentpoint",
+ " gsave",
+ " char true charpath clip StippleText",
+ " grestore",
+ " char stringwidth translate",
+ " moveto",
+ " } forall",
+ " grestore",
+ " } {show} ifelse",
+ " 0 spacing neg translate",
+ " } forall",
+ "} bind def",
+ "%%%%EndProlog",
+ "%%%%BeginSetup",
+ "/CL 2 def",
+ "%%%%IncludeResource: font Courier-Bold",
+ "%%%%EndSetup",
+ 0,
+};
+
+static int MH = 600; /* page height - can be scaled */
+static int oMH = 600; /* page height - not scaled */
+#define MW 500 /* page width */
+#define LH 100 /* bottom margin */
+#define RH 100 /* right margin */
+#define WW 50 /* distance between process lines */
+#define HH 8 /* vertical distance between steps */
+#define PH 14 /* height of process-tag headers */
+
+static FILE *pfd;
+static char **I; /* initial procs */
+static int *D,*R; /* maps between depth and ldepth */
+static short *M; /* x location of each box at index y */
+static short *T; /* y index of match for each box at index y */
+static char **L; /* text labels */
+static char *ProcLine; /* active processes */
+static int pspno = 0; /* postscript page */
+static int ldepth = 1;
+static int maxx, TotSteps = 2*4096; /* max nr of steps, about 40 pages */
+static float Scaler = (float) 1.0;
+
+extern int ntrail, s_trail, pno, depth;
+extern Symbol *oFname;
+extern void exit(int);
+void putpages(void);
+void spitbox(int, int, int, char *);
+
+void
+putlegend(void)
+{
+ fprintf(pfd, "gsave\n");
+ fprintf(pfd, "/Courier-Bold findfont 8 scalefont ");
+ fprintf(pfd, "ISOEncode setfont\n");
+ fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n");
+ fprintf(pfd, "%d %d [\n", MW/2, LH+oMH+ 5*HH);
+ fprintf(pfd, " (%s -- %s -- MSC -- %d)\n] 10 -0.5 0.5 0 ",
+ SpinVersion, oFname?oFname->name:"", pspno);
+ fprintf(pfd, "false DrawText\ngrestore\n");
+}
+
+void
+startpage(void)
+{ int i;
+
+ pspno++;
+ fprintf(pfd, "%%%%Page: %d %d\n", pspno, pspno);
+ putlegend();
+
+ for (i = TotSteps-1; i >= 0; i--)
+ { if (!I[i]) continue;
+ spitbox(i, RH, -PH, I[i]);
+ }
+
+ fprintf(pfd, "save\n");
+ fprintf(pfd, "10 %d moveto\n", LH+oMH+5);
+ fprintf(pfd, "%d %d lineto\n", RH+MW, LH+oMH+5);
+ fprintf(pfd, "%d %d lineto\n", RH+MW, LH);
+ fprintf(pfd, "10 %d lineto\n", LH);
+ fprintf(pfd, "closepath clip newpath\n");
+ fprintf(pfd, "%f %f translate\n",
+ (float) RH, (float) LH);
+ memset(ProcLine, 0, 256*sizeof(char));
+ if (Scaler != 1.0)
+ fprintf(pfd, "%f %f scale\n", Scaler, Scaler);
+}
+
+void
+putprelude(void)
+{ char snap[256]; FILE *fd;
+
+ sprintf(snap, "%s.ps", oFname?oFname->name:"msc");
+ if (!(pfd = fopen(snap, "w")))
+ fatal("cannot create file '%s'", snap);
+
+ fprintf(pfd, "%%!PS-Adobe-2.0\n");
+ fprintf(pfd, "%%%%Creator: %s\n", SpinVersion);
+ fprintf(pfd, "%%%%Title: MSC %s\n", oFname?oFname->name:"--");
+ fprintf(pfd, "%%%%BoundingBox: 119 154 494 638\n");
+ ntimes(pfd, 0, 1, PsPre);
+
+ if (s_trail)
+ { if (ntrail)
+ sprintf(snap, "%s%d.trail", oFname?oFname->name:"msc", ntrail);
+ else
+ sprintf(snap, "%s.trail", oFname?oFname->name:"msc");
+ if (!(fd = fopen(snap, "r")))
+ { snap[strlen(snap)-2] = '\0';
+ if (!(fd = fopen(snap, "r")))
+ fatal("cannot open trail file", (char *) 0);
+ }
+ TotSteps = 1;
+ while (fgets(snap, 256, fd)) TotSteps++;
+ fclose(fd);
+ }
+ R = (int *) emalloc(TotSteps * sizeof(int));
+ D = (int *) emalloc(TotSteps * sizeof(int));
+ M = (short *) emalloc(TotSteps * sizeof(short));
+ T = (short *) emalloc(TotSteps * sizeof(short));
+ L = (char **) emalloc(TotSteps * sizeof(char *));
+ I = (char **) emalloc(TotSteps * sizeof(char *));
+ ProcLine = (char *) emalloc(1024 * sizeof(char));
+ startpage();
+}
+
+void
+putpostlude(void)
+{ putpages();
+ fprintf(pfd, "%%%%Trailer\n");
+ fprintf(pfd, "end\n");
+ fprintf(pfd, "%%%%Pages: %d\n", pspno);
+ fprintf(pfd, "%%%%EOF\n");
+ fclose(pfd);
+ /* stderr, in case user redirected output */
+ fprintf(stderr, "spin: wrote %d pages into '%s.ps'\n",
+ pspno, oFname?oFname->name:"msc");
+ exit(0);
+}
+
+void
+psline(int x0, int iy0, int x1, int iy1, float r, float g, float b, int w)
+{ int y0 = MH-iy0;
+ int y1 = MH-iy1;
+
+ if (y1 > y0) y1 -= MH;
+
+ fprintf(pfd, "gsave\n");
+ fprintf(pfd, "%d %d moveto\n", x0*WW, y0);
+ fprintf(pfd, "%d %d lineto\n", x1*WW, y1);
+ fprintf(pfd, "%d setlinewidth\n", w);
+ fprintf(pfd, "0 setlinecap\n");
+ fprintf(pfd, "1 setlinejoin\n");
+ fprintf(pfd, "%f %f %f setrgbcolor AdjustColor\n", r,g,b);
+ fprintf(pfd, "stroke\ngrestore\n");
+}
+
+void
+colbox(int x, int y, int w, int h, float r, float g, float b)
+{ fprintf(pfd, "%d %d moveto\n", x - w, y-h);
+ fprintf(pfd, "%d %d lineto\n", x + w, y-h);
+ fprintf(pfd, "%d %d lineto\n", x + w, y+h);
+ fprintf(pfd, "%d %d lineto\n", x - w, y+h);
+ fprintf(pfd, "%d %d lineto\n", x - w, y-h);
+ fprintf(pfd, "%f %f %f setrgbcolor AdjustColor\n", r,g,b);
+ fprintf(pfd, "closepath fill\n");
+}
+
+void
+putgrid(int p)
+{ int i;
+
+ for (i = p ; i >= 0; i--)
+ { if (!ProcLine[i])
+ { psline(i, 0, i, MH-1, (float) (0.4), (float) (0.4), (float) (1.0), 1);
+ ProcLine[i] = 1;
+ } }
+}
+
+void
+putarrow(int from, int to)
+{
+ T[D[from]] = D[to];
+}
+
+void
+stepnumber(int i)
+{ int y = MH-(i*HH)%MH;
+
+ fprintf(pfd, "gsave\n");
+ fprintf(pfd, "/Courier-Bold findfont 6 scalefont ");
+ fprintf(pfd, "ISOEncode setfont\n");
+ fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n");
+ fprintf(pfd, "%d %d [\n", -40, y);
+ fprintf(pfd, " (%d)\n] 10 -0.5 0.5 0 ", R[i]);
+ fprintf(pfd, "false DrawText\ngrestore\n");
+ fprintf(pfd, "%d %d moveto\n", -20, y);
+ fprintf(pfd, "%d %d lineto\n", M[i]*WW, y);
+ fprintf(pfd, "1 setlinewidth\n0 setlinecap\n1 setlinejoin\n");
+ fprintf(pfd, "0.92 0.92 0.92 setrgbcolor AdjustColor\n");
+ fprintf(pfd, "stroke\n");
+}
+
+void
+spitbox(int x, int dx, int y, char *s)
+{ float r,g,b, bw; int a; char d[256];
+
+ if (!dx)
+ { stepnumber(y);
+ putgrid(x);
+ }
+ bw = (float)2.7*(float)strlen(s);
+ colbox(x*WW+dx, MH-(y*HH)%MH, (int) (bw+1.0),
+ 5, (float) 0.,(float) 0.,(float) 0.);
+ if (s[0] == '~')
+ { switch (s[1]) {
+ case 'B': r = (float) 0.2; g = (float) 0.2; b = (float) 1.;
+ break;
+ case 'G': r = (float) 0.2; g = (float) 1.; b = (float) 0.2;
+ break;
+ case 'R':
+ default : r = (float) 1.; g = (float) 0.2; b = (float) 0.2;
+ break;
+ }
+ s += 2;
+ } else if (strchr(s, '!'))
+ { r = (float) 1.; g = (float) 1.; b = (float) 1.;
+ } else if (strchr(s, '?'))
+ { r = (float) 0.; g = (float) 1.; b = (float) 1.;
+ } else
+ { r = (float) 1.; g = (float) 1.; b = (float) 0.;
+ if (!dx
+ && sscanf(s, "%d:%250s", &a, d) == 2 /* was &d */
+ && a >= 0 && a < TotSteps)
+ { if (!I[a]
+ || strlen(I[a]) <= strlen(s))
+ I[a] = emalloc((int) strlen(s)+1);
+ strcpy(I[a], s);
+ } }
+ colbox(x*WW+dx, MH-(y*HH)%MH, (int) bw, 4, r,g,b);
+ fprintf(pfd, "gsave\n");
+ fprintf(pfd, "/Courier-Bold findfont 8 scalefont ");
+ fprintf(pfd, "ISOEncode setfont\n");
+ fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n");
+ fprintf(pfd, "%d %d [\n", x*WW+dx, MH-(y*HH)%MH);
+ fprintf(pfd, " (%s)\n] 10 -0.5 0.5 0 ", s);
+ fprintf(pfd, "false DrawText\ngrestore\n");
+}
+
+void
+putpages(void)
+{ int i, lasti=0; float nmh;
+
+ if (maxx*WW > MW-RH/2)
+ { Scaler = (float) (MW-RH/2) / (float) (maxx*WW);
+ fprintf(pfd, "%f %f scale\n", Scaler, Scaler);
+ nmh = (float) MH; nmh /= Scaler; MH = (int) nmh;
+ }
+
+ for (i = TotSteps-1; i >= 0; i--)
+ { if (!I[i]) continue;
+ spitbox(i, 0, 0, I[i]);
+ }
+ if (ldepth >= TotSteps) ldepth = TotSteps-1;
+ for (i = 0; i <= ldepth; i++)
+ { if (!M[i] && !L[i]) continue; /* no box here */
+ if (6+i*HH >= MH*pspno)
+ { fprintf(pfd, "showpage\nrestore\n"); startpage(); }
+ if (T[i] > 0) /* red arrow */
+ { int reali = i*HH;
+ int realt = T[i]*HH;
+ int topop = (reali)/MH; topop *= MH;
+ reali -= topop; realt -= topop;
+
+ if (M[i] == M[T[i]] && reali == realt)
+ /* an rv handshake */
+ psline( M[lasti], reali+2-3*HH/2,
+ M[i], reali,
+ (float) 1.,(float) 0.,(float) 0., 2);
+ else
+ psline( M[i], reali,
+ M[T[i]], realt,
+ (float) 1.,(float) 0.,(float) 0., 2);
+
+ if (realt >= MH) T[T[i]] = -i;
+
+ } else if (T[i] < 0) /* arrow from prev page */
+ { int reali = (-T[i])*HH;
+ int realt = i*HH;
+ int topop = (realt)/MH; topop *= MH;
+ reali -= topop; realt -= topop;
+
+ psline( M[-T[i]], reali,
+ M[i], realt,
+ (float) 1., (float) 0., (float) 0., 2);
+ }
+ if (L[i])
+ { spitbox(M[i], 0, i, L[i]);
+ /* free(L[i]); */
+ lasti = i;
+ }
+ }
+ fprintf(pfd, "showpage\nrestore\n");
+}
+
+void
+putbox(int x)
+{
+ if (ldepth >= TotSteps)
+ { fprintf(stderr, "max length of %d steps exceeded - ps file truncated\n",
+ TotSteps);
+ putpostlude();
+ }
+ M[ldepth] = x;
+ if (x > maxx) maxx = x;
+}
+
+void
+pstext(int x, char *s)
+{ char *tmp = emalloc((int) strlen(s)+1);
+
+ strcpy(tmp, s);
+ if (depth == 0)
+ I[x] = tmp;
+ else
+ { putbox(x);
+ if (depth >= TotSteps || ldepth >= TotSteps)
+ { fprintf(stderr, "max nr of %d steps exceeded\n",
+ TotSteps);
+ fatal("aborting", (char *) 0);
+ }
+
+ D[depth] = ldepth;
+ R[ldepth] = depth;
+ L[ldepth] = tmp;
+ ldepth += 2;
+ }
+}
+
+void
+dotag(FILE *fd, char *s)
+{ extern int columns, notabs; extern RunList *X;
+ int i = (!strncmp(s, "MSC: ", 5))?5:0;
+ int pid = s_trail ? pno : (X?X->pid:0);
+
+ if (columns == 2)
+ pstext(pid, &s[i]);
+ else
+ { if (!notabs)
+ { printf(" ");
+ for (i = 0; i <= pid; i++)
+ printf(" ");
+ }
+ fprintf(fd, "%s", s);
+ fflush(fd);
+ }
+}
--- /dev/null
+/***** spin: reprosrc.c *****/
+
+/* Copyright (c) 2002-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include <stdio.h>
+#include "spin.h"
+#include "y.tab.h"
+
+static int indent = 1;
+
+extern ProcList *rdy;
+void repro_seq(Sequence *);
+
+void
+doindent(void)
+{ int i;
+ for (i = 0; i < indent; i++)
+ printf(" ");
+}
+
+void
+repro_sub(Element *e)
+{
+ doindent();
+ switch (e->n->ntyp) {
+ case D_STEP:
+ printf("d_step {\n");
+ break;
+ case ATOMIC:
+ printf("atomic {\n");
+ break;
+ case NON_ATOMIC:
+ printf(" {\n");
+ break;
+ }
+ indent++;
+ repro_seq(e->n->sl->this);
+ indent--;
+
+ doindent();
+ printf(" };\n");
+}
+
+void
+repro_seq(Sequence *s)
+{ Element *e;
+ Symbol *v;
+ SeqList *h;
+
+ for (e = s->frst; e; e = e->nxt)
+ {
+ v = has_lab(e, 0);
+ if (v) printf("%s:\n", v->name);
+
+ if (e->n->ntyp == UNLESS)
+ { printf("/* normal */ {\n");
+ repro_seq(e->n->sl->this);
+ doindent();
+ printf("} unless {\n");
+ repro_seq(e->n->sl->nxt->this);
+ doindent();
+ printf("}; /* end unless */\n");
+ } else if (e->sub)
+ {
+ switch (e->n->ntyp) {
+ case DO: doindent(); printf("do\n"); indent++; break;
+ case IF: doindent(); printf("if\n"); indent++; break;
+ }
+
+ for (h = e->sub; h; h = h->nxt)
+ { indent--; doindent(); indent++; printf("::\n");
+ repro_seq(h->this);
+ printf("\n");
+ }
+
+ switch (e->n->ntyp) {
+ case DO: indent--; doindent(); printf("od;\n"); break;
+ case IF: indent--; doindent(); printf("fi;\n"); break;
+ }
+ } else
+ { if (e->n->ntyp == ATOMIC
+ || e->n->ntyp == D_STEP
+ || e->n->ntyp == NON_ATOMIC)
+ repro_sub(e);
+ else if (e->n->ntyp != '.'
+ && e->n->ntyp != '@'
+ && e->n->ntyp != BREAK)
+ {
+ doindent();
+ if (e->n->ntyp == C_CODE)
+ { printf("c_code ");
+ plunk_inline(stdout, e->n->sym->name, 1);
+ } else if (e->n->ntyp == 'c'
+ && e->n->lft->ntyp == C_EXPR)
+ { printf("c_expr { ");
+ plunk_expr(stdout, e->n->lft->sym->name);
+ printf("} ->\n");
+ } else
+ { comment(stdout, e->n, 0);
+ printf(";\n");
+ } }
+ }
+ if (e == s->last)
+ break;
+ }
+}
+
+void
+repro_proc(ProcList *p)
+{
+ if (!p) return;
+ if (p->nxt) repro_proc(p->nxt);
+
+ if (p->det) printf("D"); /* deterministic */
+ printf("proctype %s()", p->n->name);
+ if (p->prov)
+ { printf(" provided ");
+ comment(stdout, p->prov, 0);
+ }
+ printf("\n{\n");
+ repro_seq(p->s);
+ printf("}\n");
+}
+
+void
+repro_src(void)
+{
+ repro_proc(rdy);
+}
--- /dev/null
+/***** spin: run.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include <stdlib.h>
+#include "spin.h"
+#include "y.tab.h"
+
+extern RunList *X, *run;
+extern Symbol *Fname;
+extern Element *LastStep;
+extern int Rvous, lineno, Tval, interactive, MadeChoice;
+extern int TstOnly, verbose, s_trail, xspin, jumpsteps, depth;
+extern int nproc, nstop, no_print, like_java;
+
+static long Seed = 1;
+static int E_Check = 0, Escape_Check = 0;
+
+static int eval_sync(Element *);
+static int pc_enabled(Lextok *n);
+extern void sr_buf(int, int);
+
+void
+Srand(unsigned int s)
+{ Seed = s;
+}
+
+long
+Rand(void)
+{ /* CACM 31(10), Oct 1988 */
+ Seed = 16807*(Seed%127773) - 2836*(Seed/127773);
+ if (Seed <= 0) Seed += 2147483647;
+ return Seed;
+}
+
+Element *
+rev_escape(SeqList *e)
+{ Element *r;
+
+ if (!e)
+ return (Element *) 0;
+
+ if ((r = rev_escape(e->nxt)) != ZE) /* reversed order */
+ return r;
+
+ return eval_sub(e->this->frst);
+}
+
+Element *
+eval_sub(Element *e)
+{ Element *f, *g;
+ SeqList *z;
+ int i, j, k;
+
+ if (!e || !e->n)
+ return ZE;
+#ifdef DEBUG
+ printf("\n\teval_sub(%d %s: line %d) ",
+ e->Seqno, e->esc?"+esc":"", e->n?e->n->ln:0);
+ comment(stdout, e->n, 0);
+ printf("\n");
+#endif
+ if (e->n->ntyp == GOTO)
+ { if (Rvous) return ZE;
+ LastStep = e;
+ f = get_lab(e->n, 1);
+ cross_dsteps(e->n, f->n);
+ return f;
+ }
+ if (e->n->ntyp == UNLESS)
+ { /* escapes were distributed into sequence */
+ return eval_sub(e->sub->this->frst);
+ } else if (e->sub) /* true for IF, DO, and UNLESS */
+ { Element *has_else = ZE;
+ Element *bas_else = ZE;
+ int nr_else = 0, nr_choices = 0;
+
+ if (interactive
+ && !MadeChoice && !E_Check
+ && !Escape_Check
+ && !(e->status&(D_ATOM))
+ && depth >= jumpsteps)
+ { printf("Select stmnt (");
+ whoruns(0); printf(")\n");
+ if (nproc-nstop > 1)
+ printf("\tchoice 0: other process\n");
+ }
+ for (z = e->sub, j=0; z; z = z->nxt)
+ { j++;
+ if (interactive
+ && !MadeChoice && !E_Check
+ && !Escape_Check
+ && !(e->status&(D_ATOM))
+ && depth >= jumpsteps
+ && z->this->frst
+ && (xspin || (verbose&32) || Enabled0(z->this->frst)))
+ { if (z->this->frst->n->ntyp == ELSE)
+ { has_else = (Rvous)?ZE:z->this->frst->nxt;
+ nr_else = j;
+ continue;
+ }
+ printf("\tchoice %d: ", j);
+#if 0
+ if (z->this->frst->n)
+ printf("line %d, ", z->this->frst->n->ln);
+#endif
+ if (!Enabled0(z->this->frst))
+ printf("unexecutable, ");
+ else
+ nr_choices++;
+ comment(stdout, z->this->frst->n, 0);
+ printf("\n");
+ } }
+
+ if (nr_choices == 0 && has_else)
+ printf("\tchoice %d: (else)\n", nr_else);
+
+ if (interactive && depth >= jumpsteps
+ && !Escape_Check
+ && !(e->status&(D_ATOM))
+ && !E_Check)
+ { if (!MadeChoice)
+ { char buf[256];
+ if (xspin)
+ printf("Make Selection %d\n\n", j);
+ else
+ printf("Select [0-%d]: ", j);
+ fflush(stdout);
+ scanf("%64s", buf);
+ if (isdigit(buf[0]))
+ k = atoi(buf);
+ else
+ { if (buf[0] == 'q')
+ alldone(0);
+ k = -1;
+ }
+ } else
+ { k = MadeChoice;
+ MadeChoice = 0;
+ }
+ if (k < 1 || k > j)
+ { if (k != 0) printf("\tchoice outside range\n");
+ return ZE;
+ }
+ k--;
+ } else
+ { if (e->n && e->n->indstep >= 0)
+ k = 0; /* select 1st executable guard */
+ else
+ k = Rand()%j; /* nondeterminism */
+ }
+ has_else = ZE;
+ bas_else = ZE;
+ for (i = 0, z = e->sub; i < j+k; i++)
+ { if (z->this->frst
+ && z->this->frst->n->ntyp == ELSE)
+ { bas_else = z->this->frst;
+ has_else = (Rvous)?ZE:bas_else->nxt;
+ if (!interactive || depth < jumpsteps
+ || Escape_Check
+ || (e->status&(D_ATOM)))
+ { z = (z->nxt)?z->nxt:e->sub;
+ continue;
+ }
+ }
+ if (z->this->frst
+ && ((z->this->frst->n->ntyp == ATOMIC
+ || z->this->frst->n->ntyp == D_STEP)
+ && z->this->frst->n->sl->this->frst->n->ntyp == ELSE))
+ { bas_else = z->this->frst->n->sl->this->frst;
+ has_else = (Rvous)?ZE:bas_else->nxt;
+ if (!interactive || depth < jumpsteps
+ || Escape_Check
+ || (e->status&(D_ATOM)))
+ { z = (z->nxt)?z->nxt:e->sub;
+ continue;
+ }
+ }
+ if (i >= k)
+ { if ((f = eval_sub(z->this->frst)) != ZE)
+ return f;
+ else if (interactive && depth >= jumpsteps
+ && !(e->status&(D_ATOM)))
+ { if (!E_Check && !Escape_Check)
+ printf("\tunexecutable\n");
+ return ZE;
+ } }
+ z = (z->nxt)?z->nxt:e->sub;
+ }
+ LastStep = bas_else;
+ return has_else;
+ } else
+ { if (e->n->ntyp == ATOMIC
+ || e->n->ntyp == D_STEP)
+ { f = e->n->sl->this->frst;
+ g = e->n->sl->this->last;
+ g->nxt = e->nxt;
+ if (!(g = eval_sub(f))) /* atomic guard */
+ return ZE;
+ return g;
+ } else if (e->n->ntyp == NON_ATOMIC)
+ { f = e->n->sl->this->frst;
+ g = e->n->sl->this->last;
+ g->nxt = e->nxt; /* close it */
+ return eval_sub(f);
+ } else if (e->n->ntyp == '.')
+ { if (!Rvous) return e->nxt;
+ return eval_sub(e->nxt);
+ } else
+ { SeqList *x;
+ if (!(e->status & (D_ATOM))
+ && e->esc && verbose&32)
+ { printf("Stmnt [");
+ comment(stdout, e->n, 0);
+ printf("] has escape(s): ");
+ for (x = e->esc; x; x = x->nxt)
+ { printf("[");
+ g = x->this->frst;
+ if (g->n->ntyp == ATOMIC
+ || g->n->ntyp == NON_ATOMIC)
+ g = g->n->sl->this->frst;
+ comment(stdout, g->n, 0);
+ printf("] ");
+ }
+ printf("\n");
+ }
+#if 0
+ if (!(e->status & D_ATOM)) /* escapes don't reach inside d_steps */
+ /* 4.2.4: only the guard of a d_step can have an escape */
+#endif
+ { Escape_Check++;
+ if (like_java)
+ { if ((g = rev_escape(e->esc)) != ZE)
+ { if (verbose&4)
+ printf("\tEscape taken\n");
+ Escape_Check--;
+ return g;
+ }
+ } else
+ { for (x = e->esc; x; x = x->nxt)
+ { if ((g = eval_sub(x->this->frst)) != ZE)
+ { if (verbose&4)
+ printf("\tEscape taken\n");
+ Escape_Check--;
+ return g;
+ } } }
+ Escape_Check--;
+ }
+
+ switch (e->n->ntyp) {
+ case TIMEOUT: case RUN:
+ case PRINT: case PRINTM:
+ case C_CODE: case C_EXPR:
+ case ASGN: case ASSERT:
+ case 's': case 'r': case 'c':
+ /* toplevel statements only */
+ LastStep = e;
+ default:
+ break;
+ }
+ if (Rvous)
+ {
+ return (eval_sync(e))?e->nxt:ZE;
+ }
+ return (eval(e->n))?e->nxt:ZE;
+ }
+ }
+ return ZE; /* not reached */
+}
+
+static int
+eval_sync(Element *e)
+{ /* allow only synchronous receives
+ and related node types */
+ Lextok *now = (e)?e->n:ZN;
+
+ if (!now
+ || now->ntyp != 'r'
+ || now->val >= 2 /* no rv with a poll */
+ || !q_is_sync(now))
+ {
+ return 0;
+ }
+
+ LastStep = e;
+ return eval(now);
+}
+
+static int
+assign(Lextok *now)
+{ int t;
+
+ if (TstOnly) return 1;
+
+ switch (now->rgt->ntyp) {
+ case FULL: case NFULL:
+ case EMPTY: case NEMPTY:
+ case RUN: case LEN:
+ t = BYTE;
+ break;
+ default:
+ t = Sym_typ(now->rgt);
+ break;
+ }
+ typ_ck(Sym_typ(now->lft), t, "assignment");
+ return setval(now->lft, eval(now->rgt));
+}
+
+static int
+nonprogress(void) /* np_ */
+{ RunList *r;
+
+ for (r = run; r; r = r->nxt)
+ { if (has_lab(r->pc, 4)) /* 4=progress */
+ return 0;
+ }
+ return 1;
+}
+
+int
+eval(Lextok *now)
+{
+ if (now) {
+ lineno = now->ln;
+ Fname = now->fn;
+#ifdef DEBUG
+ printf("eval ");
+ comment(stdout, now, 0);
+ printf("\n");
+#endif
+ switch (now->ntyp) {
+ case CONST: return now->val;
+ case '!': return !eval(now->lft);
+ case UMIN: return -eval(now->lft);
+ case '~': return ~eval(now->lft);
+
+ case '/': return (eval(now->lft) / eval(now->rgt));
+ case '*': return (eval(now->lft) * eval(now->rgt));
+ case '-': return (eval(now->lft) - eval(now->rgt));
+ case '+': return (eval(now->lft) + eval(now->rgt));
+ case '%': return (eval(now->lft) % eval(now->rgt));
+ case LT: return (eval(now->lft) < eval(now->rgt));
+ case GT: return (eval(now->lft) > eval(now->rgt));
+ case '&': return (eval(now->lft) & eval(now->rgt));
+ case '^': return (eval(now->lft) ^ eval(now->rgt));
+ case '|': return (eval(now->lft) | eval(now->rgt));
+ case LE: return (eval(now->lft) <= eval(now->rgt));
+ case GE: return (eval(now->lft) >= eval(now->rgt));
+ case NE: return (eval(now->lft) != eval(now->rgt));
+ case EQ: return (eval(now->lft) == eval(now->rgt));
+ case OR: return (eval(now->lft) || eval(now->rgt));
+ case AND: return (eval(now->lft) && eval(now->rgt));
+ case LSHIFT: return (eval(now->lft) << eval(now->rgt));
+ case RSHIFT: return (eval(now->lft) >> eval(now->rgt));
+ case '?': return (eval(now->lft) ? eval(now->rgt->lft)
+ : eval(now->rgt->rgt));
+
+ case 'p': return remotevar(now); /* _p for remote reference */
+ case 'q': return remotelab(now);
+ case 'R': return qrecv(now, 0); /* test only */
+ case LEN: return qlen(now);
+ case FULL: return (qfull(now));
+ case EMPTY: return (qlen(now)==0);
+ case NFULL: return (!qfull(now));
+ case NEMPTY: return (qlen(now)>0);
+ case ENABLED: if (s_trail) return 1;
+ return pc_enabled(now->lft);
+ case EVAL: return eval(now->lft);
+ case PC_VAL: return pc_value(now->lft);
+ case NONPROGRESS: return nonprogress();
+ case NAME: return getval(now);
+
+ case TIMEOUT: return Tval;
+ case RUN: return TstOnly?1:enable(now);
+
+ case 's': return qsend(now); /* send */
+ case 'r': return qrecv(now, 1); /* receive or poll */
+ case 'c': return eval(now->lft); /* condition */
+ case PRINT: return TstOnly?1:interprint(stdout, now);
+ case PRINTM: return TstOnly?1:printm(stdout, now);
+ case ASGN: return assign(now);
+
+ case C_CODE: printf("%s:\t", now->sym->name);
+ plunk_inline(stdout, now->sym->name, 0);
+ return 1; /* uninterpreted */
+
+ case C_EXPR: printf("%s:\t", now->sym->name);
+ plunk_expr(stdout, now->sym->name);
+ printf("\n");
+ return 1; /* uninterpreted */
+
+ case ASSERT: if (TstOnly || eval(now->lft)) return 1;
+ non_fatal("assertion violated", (char *) 0);
+ printf("spin: text of failed assertion: assert(");
+ comment(stdout, now->lft, 0);
+ printf(")\n");
+ if (s_trail && !xspin) return 1;
+ wrapup(1); /* doesn't return */
+
+ case IF: case DO: case BREAK: case UNLESS: /* compound */
+ case '.': return 1; /* return label for compound */
+ case '@': return 0; /* stop state */
+ case ELSE: return 1; /* only hit here in guided trails */
+ default : printf("spin: bad node type %d (run)\n", now->ntyp);
+ if (s_trail) printf("spin: trail file doesn't match spec?\n");
+ fatal("aborting", 0);
+ }}
+ return 0;
+}
+
+int
+printm(FILE *fd, Lextok *n)
+{ extern char Buf[];
+ int j;
+
+ Buf[0] = '\0';
+ if (!no_print)
+ if (!s_trail || depth >= jumpsteps) {
+ if (n->lft->ismtyp)
+ j = n->lft->val;
+ else
+ j = eval(n->lft);
+ sr_buf(j, 1);
+ dotag(fd, Buf);
+ }
+ return 1;
+}
+
+int
+interprint(FILE *fd, Lextok *n)
+{ Lextok *tmp = n->lft;
+ char c, *s = n->sym->name;
+ int i, j; char lbuf[512]; /* matches value in sr_buf() */
+ extern char Buf[]; /* global, size 4096 */
+ char tBuf[4096]; /* match size of global Buf[] */
+
+ Buf[0] = '\0';
+ if (!no_print)
+ if (!s_trail || depth >= jumpsteps) {
+ for (i = 0; i < (int) strlen(s); i++)
+ switch (s[i]) {
+ case '\"': break; /* ignore */
+ case '\\':
+ switch(s[++i]) {
+ case 't': strcat(Buf, "\t"); break;
+ case 'n': strcat(Buf, "\n"); break;
+ default: goto onechar;
+ }
+ break;
+ case '%':
+ if ((c = s[++i]) == '%')
+ { strcat(Buf, "%"); /* literal */
+ break;
+ }
+ if (!tmp)
+ { non_fatal("too few print args %s", s);
+ break;
+ }
+ j = eval(tmp->lft);
+ tmp = tmp->rgt;
+ switch(c) {
+ case 'c': sprintf(lbuf, "%c", j); break;
+ case 'd': sprintf(lbuf, "%d", j); break;
+
+ case 'e': strcpy(tBuf, Buf); /* event name */
+ Buf[0] = '\0';
+ sr_buf(j, 1);
+ strcpy(lbuf, Buf);
+ strcpy(Buf, tBuf);
+ break;
+
+ case 'o': sprintf(lbuf, "%o", j); break;
+ case 'u': sprintf(lbuf, "%u", (unsigned) j); break;
+ case 'x': sprintf(lbuf, "%x", j); break;
+ default: non_fatal("bad print cmd: '%s'", &s[i-1]);
+ lbuf[0] = '\0'; break;
+ }
+ goto append;
+ default:
+onechar: lbuf[0] = s[i]; lbuf[1] = '\0';
+append: strcat(Buf, lbuf);
+ break;
+ }
+ dotag(fd, Buf);
+ }
+ if (strlen(Buf) >= 4096) fatal("printf string too long", 0);
+ return 1;
+}
+
+static int
+Enabled1(Lextok *n)
+{ int i; int v = verbose;
+
+ if (n)
+ switch (n->ntyp) {
+ case 'c':
+ if (has_typ(n->lft, RUN))
+ return 1; /* conservative */
+ /* else fall through */
+ default: /* side-effect free */
+ verbose = 0;
+ E_Check++;
+ i = eval(n);
+ E_Check--;
+ verbose = v;
+ return i;
+
+ case C_CODE: case C_EXPR:
+ case PRINT: case PRINTM:
+ case ASGN: case ASSERT:
+ return 1;
+
+ case 's':
+ if (q_is_sync(n))
+ { if (Rvous) return 0;
+ TstOnly = 1; verbose = 0;
+ E_Check++;
+ i = eval(n);
+ E_Check--;
+ TstOnly = 0; verbose = v;
+ return i;
+ }
+ return (!qfull(n));
+ case 'r':
+ if (q_is_sync(n))
+ return 0; /* it's never a user-choice */
+ n->ntyp = 'R'; verbose = 0;
+ E_Check++;
+ i = eval(n);
+ E_Check--;
+ n->ntyp = 'r'; verbose = v;
+ return i;
+ }
+ return 0;
+}
+
+int
+Enabled0(Element *e)
+{ SeqList *z;
+
+ if (!e || !e->n)
+ return 0;
+
+ switch (e->n->ntyp) {
+ case '@':
+ return X->pid == (nproc-nstop-1);
+ case '.':
+ return 1;
+ case GOTO:
+ if (Rvous) return 0;
+ return 1;
+ case UNLESS:
+ return Enabled0(e->sub->this->frst);
+ case ATOMIC:
+ case D_STEP:
+ case NON_ATOMIC:
+ return Enabled0(e->n->sl->this->frst);
+ }
+ if (e->sub) /* true for IF, DO, and UNLESS */
+ { for (z = e->sub; z; z = z->nxt)
+ if (Enabled0(z->this->frst))
+ return 1;
+ return 0;
+ }
+ for (z = e->esc; z; z = z->nxt)
+ { if (Enabled0(z->this->frst))
+ return 1;
+ }
+#if 0
+ printf("enabled1 ");
+ comment(stdout, e->n, 0);
+ printf(" ==> %s\n", Enabled1(e->n)?"yes":"nope");
+#endif
+ return Enabled1(e->n);
+}
+
+int
+pc_enabled(Lextok *n)
+{ int i = nproc - nstop;
+ int pid = eval(n);
+ int result = 0;
+ RunList *Y, *oX;
+
+ if (pid == X->pid)
+ fatal("used: enabled(pid=thisproc) [%s]", X->n->name);
+
+ for (Y = run; Y; Y = Y->nxt)
+ if (--i == pid)
+ { oX = X; X = Y;
+ result = Enabled0(Y->pc);
+ X = oX;
+ break;
+ }
+ return result;
+}
--- /dev/null
+/***** spin: sched.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include <stdlib.h>
+#include "spin.h"
+#include "y.tab.h"
+
+extern int verbose, s_trail, analyze, no_wrapup;
+extern char *claimproc, *eventmap, Buf[];
+extern Ordered *all_names;
+extern Symbol *Fname, *context;
+extern int lineno, nr_errs, dumptab, xspin, jumpsteps, columns;
+extern int u_sync, Elcnt, interactive, TstOnly, cutoff;
+extern short has_enabled;
+extern int limited_vis;
+
+RunList *X = (RunList *) 0;
+RunList *run = (RunList *) 0;
+RunList *LastX = (RunList *) 0; /* previous executing proc */
+ProcList *rdy = (ProcList *) 0;
+Element *LastStep = ZE;
+int nproc=0, nstop=0, Tval=0;
+int Rvous=0, depth=0, nrRdy=0, MadeChoice;
+short Have_claim=0, Skip_claim=0;
+
+static int Priority_Sum = 0;
+static void setlocals(RunList *);
+static void setparams(RunList *, ProcList *, Lextok *);
+static void talk(RunList *);
+
+void
+runnable(ProcList *p, int weight, int noparams)
+{ RunList *r = (RunList *) emalloc(sizeof(RunList));
+
+ r->n = p->n;
+ r->tn = p->tn;
+ r->pid = nproc++ - nstop + Skip_claim;
+
+ if ((verbose&4) || (verbose&32))
+ printf("Starting %s with pid %d\n",
+ p->n?p->n->name:"--", r->pid);
+
+ if (!p->s)
+ fatal("parsing error, no sequence %s",
+ p->n?p->n->name:"--");
+
+ r->pc = huntele(p->s->frst, p->s->frst->status, -1);
+ r->ps = p->s;
+
+ if (p->s->last)
+ p->s->last->status |= ENDSTATE; /* normal end state */
+
+ r->nxt = run;
+ r->prov = p->prov;
+ r->priority = weight;
+ if (noparams) setlocals(r);
+ Priority_Sum += weight;
+ run = r;
+}
+
+ProcList *
+ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov)
+ /* n=name, p=formals, s=body det=deterministic prov=provided */
+{ ProcList *r = (ProcList *) emalloc(sizeof(ProcList));
+ Lextok *fp, *fpt; int j; extern int Npars;
+
+ r->n = n;
+ r->p = p;
+ r->s = s;
+ r->prov = prov;
+ r->tn = nrRdy++;
+ if (det != 0 && det != 1)
+ { fprintf(stderr, "spin: bad value for det (cannot happen)\n");
+ }
+ r->det = (short) det;
+ r->nxt = rdy;
+ rdy = r;
+
+ for (fp = p, j = 0; fp; fp = fp->rgt)
+ for (fpt = fp->lft; fpt; fpt = fpt->rgt)
+ j++; /* count # of parameters */
+ Npars = max(Npars, j);
+
+ return rdy;
+}
+
+int
+find_maxel(Symbol *s)
+{ ProcList *p;
+
+ for (p = rdy; p; p = p->nxt)
+ if (p->n == s)
+ return p->s->maxel++;
+ return Elcnt++;
+}
+
+static void
+formdump(void)
+{ ProcList *p;
+ Lextok *f, *t;
+ int cnt;
+
+ for (p = rdy; p; p = p->nxt)
+ { if (!p->p) continue;
+ cnt = -1;
+ for (f = p->p; f; f = f->rgt) /* types */
+ for (t = f->lft; t; t = t->rgt) /* formals */
+ { if (t->ntyp != ',')
+ t->sym->Nid = cnt--; /* overload Nid */
+ else
+ t->lft->sym->Nid = cnt--;
+ }
+ }
+}
+
+void
+announce(char *w)
+{
+ if (columns)
+ { extern char Buf[];
+ extern int firstrow;
+ firstrow = 1;
+ if (columns == 2)
+ { sprintf(Buf, "%d:%s",
+ run->pid - Have_claim, run->n->name);
+ pstext(run->pid - Have_claim, Buf);
+ } else
+ printf("proc %d = %s\n",
+ run->pid - Have_claim, run->n->name);
+ return;
+ }
+
+ if (dumptab
+ || analyze
+ || s_trail
+ || !(verbose&4))
+ return;
+
+ if (w)
+ printf(" 0: proc - (%s) ", w);
+ else
+ whoruns(1);
+ printf("creates proc %2d (%s)",
+ run->pid - Have_claim,
+ run->n->name);
+ if (run->priority > 1)
+ printf(" priority %d", run->priority);
+ printf("\n");
+}
+
+#ifndef MAXP
+#define MAXP 255 /* matches max nr of processes in verifier */
+#endif
+
+int
+enable(Lextok *m)
+{ ProcList *p;
+ Symbol *s = m->sym; /* proctype name */
+ Lextok *n = m->lft; /* actual parameters */
+
+ if (m->val < 1) m->val = 1; /* minimum priority */
+ for (p = rdy; p; p = p->nxt)
+ if (strcmp(s->name, p->n->name) == 0)
+ { if (nproc-nstop >= MAXP)
+ { printf("spin: too many processes (%d max)\n", MAXP);
+ break;
+ }
+ runnable(p, m->val, 0);
+ announce((char *) 0);
+ setparams(run, p, n);
+ setlocals(run); /* after setparams */
+ return run->pid - Have_claim + Skip_claim; /* effective simu pid */
+ }
+ return 0; /* process not found */
+}
+
+void
+check_param_count(int i, Lextok *m)
+{ ProcList *p;
+ Symbol *s = m->sym; /* proctype name */
+ Lextok *f, *t; /* formal pars */
+ int cnt = 0;
+
+ for (p = rdy; p; p = p->nxt)
+ { if (strcmp(s->name, p->n->name) == 0)
+ { if (m->lft) /* actual param list */
+ { lineno = m->lft->ln;
+ Fname = m->lft->fn;
+ }
+ for (f = p->p; f; f = f->rgt) /* one type at a time */
+ for (t = f->lft; t; t = t->rgt) /* count formal params */
+ { cnt++;
+ }
+ if (i != cnt)
+ { printf("spin: saw %d parameters, expected %d\n", i, cnt);
+ non_fatal("wrong number of parameters", "");
+ }
+ break;
+ } }
+}
+
+void
+start_claim(int n)
+{ ProcList *p;
+ RunList *r, *q = (RunList *) 0;
+
+ for (p = rdy; p; p = p->nxt)
+ if (p->tn == n
+ && strcmp(p->n->name, ":never:") == 0)
+ { runnable(p, 1, 1);
+ goto found;
+ }
+ printf("spin: couldn't find claim (ignored)\n");
+ Skip_claim = 1;
+ goto done;
+found:
+ /* move claim to far end of runlist, and reassign it pid 0 */
+ if (columns == 2)
+ { depth = 0;
+ pstext(0, "0::never:");
+ for (r = run; r; r = r->nxt)
+ { if (!strcmp(r->n->name, ":never:"))
+ continue;
+ sprintf(Buf, "%d:%s",
+ r->pid+1, r->n->name);
+ pstext(r->pid+1, Buf);
+ } }
+
+ if (run->pid == 0) return; /* it is the first process started */
+
+ q = run; run = run->nxt;
+ q->pid = 0; q->nxt = (RunList *) 0; /* remove */
+done:
+ Have_claim = 1;
+ for (r = run; r; r = r->nxt)
+ { r->pid = r->pid+Have_claim; /* adjust */
+ if (!r->nxt)
+ { r->nxt = q;
+ break;
+ } }
+}
+
+int
+f_pid(char *n)
+{ RunList *r;
+ int rval = -1;
+
+ for (r = run; r; r = r->nxt)
+ if (strcmp(n, r->n->name) == 0)
+ { if (rval >= 0)
+ { printf("spin: remote ref to proctype %s, ", n);
+ printf("has more than one match: %d and %d\n",
+ rval, r->pid);
+ } else
+ rval = r->pid;
+ }
+ return rval;
+}
+
+void
+wrapup(int fini)
+{
+ limited_vis = 0;
+ if (columns)
+ { extern void putpostlude(void);
+ if (columns == 2) putpostlude();
+ if (!no_wrapup)
+ printf("-------------\nfinal state:\n-------------\n");
+ }
+ if (no_wrapup)
+ goto short_cut;
+ if (nproc != nstop)
+ { int ov = verbose;
+ printf("#processes: %d\n", nproc-nstop - Have_claim + Skip_claim);
+ verbose &= ~4;
+ dumpglobals();
+ verbose = ov;
+ verbose &= ~1; /* no more globals */
+ verbose |= 32; /* add process states */
+ for (X = run; X; X = X->nxt)
+ talk(X);
+ verbose = ov; /* restore */
+ }
+ printf("%d process%s created\n",
+ nproc - Have_claim + Skip_claim,
+ (xspin || nproc!=1)?"es":"");
+short_cut:
+ if (xspin) alldone(0); /* avoid an abort from xspin */
+ if (fini) alldone(1);
+}
+
+static char is_blocked[256];
+
+static int
+p_blocked(int p)
+{ int i, j;
+
+ is_blocked[p%256] = 1;
+ for (i = j = 0; i < nproc - nstop; i++)
+ j += is_blocked[i];
+ if (j >= nproc - nstop)
+ { memset(is_blocked, 0, 256);
+ return 1;
+ }
+ return 0;
+}
+
+static Element *
+silent_moves(Element *e)
+{ Element *f;
+
+ if (e->n)
+ switch (e->n->ntyp) {
+ case GOTO:
+ if (Rvous) break;
+ f = get_lab(e->n, 1);
+ cross_dsteps(e->n, f->n);
+ return f; /* guard against goto cycles */
+ case UNLESS:
+ return silent_moves(e->sub->this->frst);
+ case NON_ATOMIC:
+ case ATOMIC:
+ case D_STEP:
+ e->n->sl->this->last->nxt = e->nxt;
+ return silent_moves(e->n->sl->this->frst);
+ case '.':
+ return silent_moves(e->nxt);
+ }
+ return e;
+}
+
+static RunList *
+pickproc(RunList *Y)
+{ SeqList *z; Element *has_else;
+ short Choices[256];
+ int j, k, nr_else = 0;
+
+ if (nproc <= nstop+1)
+ { X = run;
+ return NULL;
+ }
+ if (!interactive || depth < jumpsteps)
+ { /* was: j = (int) Rand()%(nproc-nstop); */
+ if (Priority_Sum < nproc-nstop)
+ fatal("cannot happen - weights", (char *)0);
+ j = (int) Rand()%Priority_Sum;
+
+ while (j - X->priority >= 0)
+ { j -= X->priority;
+ Y = X;
+ X = X->nxt;
+ if (!X) { Y = NULL; X = run; }
+ }
+ } else
+ { int only_choice = -1;
+ int no_choice = 0, proc_no_ch, proc_k;
+
+ Tval = 0; /* new 4.2.6 */
+try_again: printf("Select a statement\n");
+try_more: for (X = run, k = 1; X; X = X->nxt)
+ { if (X->pid > 255) break;
+
+ Choices[X->pid] = (short) k;
+
+ if (!X->pc
+ || (X->prov && !eval(X->prov)))
+ { if (X == run)
+ Choices[X->pid] = 0;
+ continue;
+ }
+ X->pc = silent_moves(X->pc);
+ if (!X->pc->sub && X->pc->n)
+ { int unex;
+ unex = !Enabled0(X->pc);
+ if (unex)
+ no_choice++;
+ else
+ only_choice = k;
+ if (!xspin && unex && !(verbose&32))
+ { k++;
+ continue;
+ }
+ printf("\tchoice %d: ", k++);
+ p_talk(X->pc, 0);
+ if (unex)
+ printf(" unexecutable,");
+ printf(" [");
+ comment(stdout, X->pc->n, 0);
+ if (X->pc->esc) printf(" + Escape");
+ printf("]\n");
+ } else {
+ has_else = ZE;
+ proc_no_ch = no_choice;
+ proc_k = k;
+ for (z = X->pc->sub, j=0; z; z = z->nxt)
+ { Element *y = silent_moves(z->this->frst);
+ int unex;
+ if (!y) continue;
+
+ if (y->n->ntyp == ELSE)
+ { has_else = (Rvous)?ZE:y;
+ nr_else = k++;
+ continue;
+ }
+
+ unex = !Enabled0(y);
+ if (unex)
+ no_choice++;
+ else
+ only_choice = k;
+ if (!xspin && unex && !(verbose&32))
+ { k++;
+ continue;
+ }
+ printf("\tchoice %d: ", k++);
+ p_talk(X->pc, 0);
+ if (unex)
+ printf(" unexecutable,");
+ printf(" [");
+ comment(stdout, y->n, 0);
+ printf("]\n");
+ }
+ if (has_else)
+ { if (no_choice-proc_no_ch >= (k-proc_k)-1)
+ { only_choice = nr_else;
+ printf("\tchoice %d: ", nr_else);
+ p_talk(X->pc, 0);
+ printf(" [else]\n");
+ } else
+ { no_choice++;
+ printf("\tchoice %d: ", nr_else);
+ p_talk(X->pc, 0);
+ printf(" unexecutable, [else]\n");
+ } }
+ } }
+ X = run;
+ if (k - no_choice < 2 && Tval == 0)
+ { Tval = 1;
+ no_choice = 0; only_choice = -1;
+ goto try_more;
+ }
+ if (xspin)
+ printf("Make Selection %d\n\n", k-1);
+ else
+ { if (k - no_choice < 2)
+ { printf("no executable choices\n");
+ alldone(0);
+ }
+ printf("Select [1-%d]: ", k-1);
+ }
+ if (!xspin && k - no_choice == 2)
+ { printf("%d\n", only_choice);
+ j = only_choice;
+ } else
+ { char buf[256];
+ fflush(stdout);
+ scanf("%64s", buf);
+ j = -1;
+ if (isdigit(buf[0]))
+ j = atoi(buf);
+ else
+ { if (buf[0] == 'q')
+ alldone(0);
+ }
+ if (j < 1 || j >= k)
+ { printf("\tchoice is outside range\n");
+ goto try_again;
+ } }
+ MadeChoice = 0;
+ Y = NULL;
+ for (X = run; X; Y = X, X = X->nxt)
+ { if (!X->nxt
+ || X->nxt->pid > 255
+ || j < Choices[X->nxt->pid])
+ {
+ MadeChoice = 1+j-Choices[X->pid];
+ break;
+ } }
+ }
+ return Y;
+}
+
+void
+sched(void)
+{ Element *e;
+ RunList *Y = NULL; /* previous process in run queue */
+ RunList *oX;
+ int go, notbeyond = 0;
+#ifdef PC
+ int bufmax = 100;
+#endif
+ if (dumptab)
+ { formdump();
+ symdump();
+ dumplabels();
+ return;
+ }
+
+ if (has_enabled && u_sync > 0)
+ { printf("spin: error, cannot use 'enabled()' in ");
+ printf("models with synchronous channels.\n");
+ nr_errs++;
+ }
+ if (analyze)
+ { gensrc();
+ return;
+ } else if (s_trail)
+ { match_trail();
+ return;
+ }
+ if (claimproc)
+ printf("warning: never claim not used in random simulation\n");
+ if (eventmap)
+ printf("warning: trace assertion not used in random simulation\n");
+
+ X = run;
+ Y = pickproc(Y);
+
+ while (X)
+ { context = X->n;
+ if (X->pc && X->pc->n)
+ { lineno = X->pc->n->ln;
+ Fname = X->pc->n->fn;
+ }
+ if (cutoff > 0 && depth >= cutoff)
+ { printf("-------------\n");
+ printf("depth-limit (-u%d steps) reached\n", cutoff);
+ break;
+ }
+#ifdef PC
+ if (xspin && !interactive && --bufmax <= 0)
+ { int c; /* avoid buffer overflow on pc's */
+ printf("spin: type return to proceed\n");
+ fflush(stdout);
+ c = getc(stdin);
+ if (c == 'q') wrapup(0);
+ bufmax = 100;
+ }
+#endif
+ depth++; LastStep = ZE;
+ oX = X; /* a rendezvous could change it */
+ go = 1;
+ if (X->prov && X->pc
+ && !(X->pc->status & D_ATOM)
+ && !eval(X->prov))
+ { if (!xspin && ((verbose&32) || (verbose&4)))
+ { p_talk(X->pc, 1);
+ printf("\t<<Not Enabled>>\n");
+ }
+ go = 0;
+ }
+ if (go && (e = eval_sub(X->pc)))
+ { if (depth >= jumpsteps
+ && ((verbose&32) || (verbose&4)))
+ { if (X == oX)
+ if (!(e->status & D_ATOM) || (verbose&32)) /* no talking in d_steps */
+ { p_talk(X->pc, 1);
+ printf(" [");
+ if (!LastStep) LastStep = X->pc;
+ comment(stdout, LastStep->n, 0);
+ printf("]\n");
+ }
+ if (verbose&1) dumpglobals();
+ if (verbose&2) dumplocal(X);
+
+ if (!(e->status & D_ATOM))
+ if (xspin)
+ printf("\n");
+ }
+ if (oX != X
+ || (X->pc->status & (ATOM|D_ATOM))) /* new 5.0 */
+ { e = silent_moves(e);
+ notbeyond = 0;
+ }
+ oX->pc = e; LastX = X;
+
+ if (!interactive) Tval = 0;
+ memset(is_blocked, 0, 256);
+
+ if (X->pc && (X->pc->status & (ATOM|L_ATOM))
+ && (notbeyond == 0 || oX != X))
+ { if ((X->pc->status & L_ATOM))
+ notbeyond = 1;
+ continue; /* no process switch */
+ }
+ } else
+ { depth--;
+ if (oX->pc && (oX->pc->status & D_ATOM))
+ { non_fatal("stmnt in d_step blocks", (char *)0);
+ }
+ if (X->pc
+ && X->pc->n
+ && X->pc->n->ntyp == '@'
+ && X->pid == (nproc-nstop-1))
+ { if (X != run && Y != NULL)
+ Y->nxt = X->nxt;
+ else
+ run = X->nxt;
+ nstop++;
+ Priority_Sum -= X->priority;
+ if (verbose&4)
+ { whoruns(1);
+ dotag(stdout, "terminates\n");
+ }
+ LastX = X;
+ if (!interactive) Tval = 0;
+ if (nproc == nstop) break;
+ memset(is_blocked, 0, 256);
+ /* proc X is no longer in runlist */
+ X = (X->nxt) ? X->nxt : run;
+ } else
+ { if (p_blocked(X->pid))
+ { if (Tval) break;
+ Tval = 1;
+ if (depth >= jumpsteps)
+ { oX = X;
+ X = (RunList *) 0; /* to suppress indent */
+ dotag(stdout, "timeout\n");
+ X = oX;
+ } } } }
+
+ if (!run || !X) break; /* new 5.0 */
+
+ Y = pickproc(X);
+ notbeyond = 0;
+ }
+ context = ZS;
+ wrapup(0);
+}
+
+int
+complete_rendez(void)
+{ RunList *orun = X, *tmp;
+ Element *s_was = LastStep;
+ Element *e;
+ int j, ointer = interactive;
+
+ if (s_trail)
+ return 1;
+ if (orun->pc->status & D_ATOM)
+ fatal("rv-attempt in d_step sequence", (char *)0);
+ Rvous = 1;
+ interactive = 0;
+
+ j = (int) Rand()%Priority_Sum; /* randomize start point */
+ X = run;
+ while (j - X->priority >= 0)
+ { j -= X->priority;
+ X = X->nxt;
+ if (!X) X = run;
+ }
+ for (j = nproc - nstop; j > 0; j--)
+ { if (X != orun
+ && (!X->prov || eval(X->prov))
+ && (e = eval_sub(X->pc)))
+ { if (TstOnly)
+ { X = orun;
+ Rvous = 0;
+ goto out;
+ }
+ if ((verbose&32) || (verbose&4))
+ { tmp = orun; orun = X; X = tmp;
+ if (!s_was) s_was = X->pc;
+ p_talk(s_was, 1);
+ printf(" [");
+ comment(stdout, s_was->n, 0);
+ printf("]\n");
+ tmp = orun; orun = X; X = tmp;
+ if (!LastStep) LastStep = X->pc;
+ p_talk(LastStep, 1);
+ printf(" [");
+ comment(stdout, LastStep->n, 0);
+ printf("]\n");
+ }
+ Rvous = 0; /* before silent_moves */
+ X->pc = silent_moves(e);
+out: interactive = ointer;
+ return 1;
+ }
+
+ X = X->nxt;
+ if (!X) X = run;
+ }
+ Rvous = 0;
+ X = orun;
+ interactive = ointer;
+ return 0;
+}
+
+/***** Runtime - Local Variables *****/
+
+static void
+addsymbol(RunList *r, Symbol *s)
+{ Symbol *t;
+ int i;
+
+ for (t = r->symtab; t; t = t->next)
+ if (strcmp(t->name, s->name) == 0)
+ return; /* it's already there */
+
+ t = (Symbol *) emalloc(sizeof(Symbol));
+ t->name = s->name;
+ t->type = s->type;
+ t->hidden = s->hidden;
+ t->nbits = s->nbits;
+ t->nel = s->nel;
+ t->ini = s->ini;
+ t->setat = depth;
+ t->context = r->n;
+ if (s->type != STRUCT)
+ { if (s->val) /* if already initialized, copy info */
+ { t->val = (int *) emalloc(s->nel*sizeof(int));
+ for (i = 0; i < s->nel; i++)
+ t->val[i] = s->val[i];
+ } else
+ (void) checkvar(t, 0); /* initialize it */
+ } else
+ { if (s->Sval)
+ fatal("saw preinitialized struct %s", s->name);
+ t->Slst = s->Slst;
+ t->Snm = s->Snm;
+ t->owner = s->owner;
+ /* t->context = r->n; */
+ }
+ t->next = r->symtab; /* add it */
+ r->symtab = t;
+}
+
+static void
+setlocals(RunList *r)
+{ Ordered *walk;
+ Symbol *sp;
+ RunList *oX = X;
+
+ X = r;
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (sp
+ && sp->context
+ && strcmp(sp->context->name, r->n->name) == 0
+ && sp->Nid >= 0
+ && (sp->type == UNSIGNED
+ || sp->type == BIT
+ || sp->type == MTYPE
+ || sp->type == BYTE
+ || sp->type == CHAN
+ || sp->type == SHORT
+ || sp->type == INT
+ || sp->type == STRUCT))
+ { if (!findloc(sp))
+ non_fatal("setlocals: cannot happen '%s'",
+ sp->name);
+ }
+ }
+ X = oX;
+}
+
+static void
+oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p)
+{ int k; int at, ft;
+ RunList *oX = X;
+
+ if (!a)
+ fatal("missing actual parameters: '%s'", p->n->name);
+ if (t->sym->nel != 1)
+ fatal("array in parameter list, %s", t->sym->name);
+ k = eval(a->lft);
+
+ at = Sym_typ(a->lft);
+ X = r; /* switch context */
+ ft = Sym_typ(t);
+
+ if (at != ft && (at == CHAN || ft == CHAN))
+ { char buf[256], tag1[64], tag2[64];
+ (void) sputtype(tag1, ft);
+ (void) sputtype(tag2, at);
+ sprintf(buf, "type-clash in params of %s(..), (%s<-> %s)",
+ p->n->name, tag1, tag2);
+ non_fatal("%s", buf);
+ }
+ t->ntyp = NAME;
+ addsymbol(r, t->sym);
+ (void) setval(t, k);
+
+ X = oX;
+}
+
+static void
+setparams(RunList *r, ProcList *p, Lextok *q)
+{ Lextok *f, *a; /* formal and actual pars */
+ Lextok *t; /* list of pars of 1 type */
+
+ if (q)
+ { lineno = q->ln;
+ Fname = q->fn;
+ }
+ for (f = p->p, a = q; f; f = f->rgt) /* one type at a time */
+ for (t = f->lft; t; t = t->rgt, a = (a)?a->rgt:a)
+ { if (t->ntyp != ',')
+ oneparam(r, t, a, p); /* plain var */
+ else
+ oneparam(r, t->lft, a, p); /* expanded struct */
+ }
+}
+
+Symbol *
+findloc(Symbol *s)
+{ Symbol *r;
+
+ if (!X)
+ { /* fatal("error, cannot eval '%s' (no proc)", s->name); */
+ return ZS;
+ }
+ for (r = X->symtab; r; r = r->next)
+ if (strcmp(r->name, s->name) == 0)
+ break;
+ if (!r)
+ { addsymbol(X, s);
+ r = X->symtab;
+ }
+ return r;
+}
+
+int
+in_bound(Symbol *r, int n)
+{
+ if (!r) return 0;
+
+ if (n >= r->nel || n < 0)
+ { printf("spin: indexing %s[%d] - size is %d\n",
+ r->name, n, r->nel);
+ non_fatal("indexing array \'%s\'", r->name);
+ return 0;
+ }
+ return 1;
+}
+
+int
+getlocal(Lextok *sn)
+{ Symbol *r, *s = sn->sym;
+ int n = eval(sn->lft);
+
+ r = findloc(s);
+ if (r && r->type == STRUCT)
+ return Rval_struct(sn, r, 1); /* 1 = check init */
+ if (in_bound(r, n))
+ return cast_val(r->type, r->val[n], r->nbits);
+ return 0;
+}
+
+int
+setlocal(Lextok *p, int m)
+{ Symbol *r = findloc(p->sym);
+ int n = eval(p->lft);
+
+ if (in_bound(r, n))
+ { if (r->type == STRUCT)
+ (void) Lval_struct(p, r, 1, m); /* 1 = check init */
+ else
+ {
+#if 0
+ if (r->nbits > 0)
+ m = (m & ((1<<r->nbits)-1));
+ r->val[n] = m;
+#else
+ r->val[n] = cast_val(r->type, m, r->nbits);
+#endif
+ r->setat = depth;
+ } }
+
+ return 1;
+}
+
+void
+whoruns(int lnr)
+{ if (!X) return;
+
+ if (lnr) printf("%3d: ", depth);
+ printf("proc ");
+ if (Have_claim && X->pid == 0)
+ printf(" -");
+ else
+ printf("%2d", X->pid - Have_claim);
+ printf(" (%s) ", X->n->name);
+}
+
+static void
+talk(RunList *r)
+{
+ if ((verbose&32) || (verbose&4))
+ { p_talk(r->pc, 1);
+ printf("\n");
+ if (verbose&1) dumpglobals();
+ if (verbose&2) dumplocal(r);
+ }
+}
+
+void
+p_talk(Element *e, int lnr)
+{ static int lastnever = -1;
+ int newnever = -1;
+
+ if (e && e->n)
+ newnever = e->n->ln;
+
+ if (Have_claim && X && X->pid == 0
+ && lastnever != newnever && e)
+ { if (xspin)
+ { printf("MSC: ~G line %d\n", newnever);
+#if 0
+ printf("%3d: proc - (NEVER) line %d \"never\" ",
+ depth, newnever);
+ printf("(state 0)\t[printf('MSC: never\\\\n')]\n");
+ } else
+ { printf("%3d: proc - (NEVER) line %d \"never\"\n",
+ depth, newnever);
+#endif
+ }
+ lastnever = newnever;
+ }
+
+ whoruns(lnr);
+ if (e)
+ { printf("line %3d %s (state %d)",
+ e->n?e->n->ln:-1,
+ e->n?e->n->fn->name:"-",
+ e->seqno);
+ if (!xspin
+ && ((e->status&ENDSTATE) || has_lab(e, 2))) /* 2=end */
+ { printf(" <valid end state>");
+ }
+ }
+}
+
+int
+remotelab(Lextok *n)
+{ int i;
+
+ lineno = n->ln;
+ Fname = n->fn;
+ if (n->sym->type != 0 && n->sym->type != LABEL)
+ { printf("spin: error, type: %d\n", n->sym->type);
+ fatal("not a labelname: '%s'", n->sym->name);
+ }
+ if (n->indstep >= 0)
+ { fatal("remote ref to label '%s' inside d_step",
+ n->sym->name);
+ }
+ if ((i = find_lab(n->sym, n->lft->sym, 1)) == 0)
+ fatal("unknown labelname: %s", n->sym->name);
+ return i;
+}
+
+int
+remotevar(Lextok *n)
+{ int prno, i, added=0;
+ RunList *Y, *oX;
+ Lextok *onl;
+ Symbol *os;
+
+ lineno = n->ln;
+ Fname = n->fn;
+
+ if (!n->lft->lft)
+ prno = f_pid(n->lft->sym->name);
+ else
+ { prno = eval(n->lft->lft); /* pid - can cause recursive call */
+#if 0
+ if (n->lft->lft->ntyp == CONST) /* user-guessed pid */
+#endif
+ { prno += Have_claim;
+ added = Have_claim;
+ } }
+
+ if (prno < 0)
+ return 0; /* non-existing process */
+#if 0
+ i = nproc - nstop;
+ for (Y = run; Y; Y = Y->nxt)
+ { --i;
+ printf(" %s: i=%d, prno=%d, ->pid=%d\n", Y->n->name, i, prno, Y->pid);
+ }
+#endif
+ i = nproc - nstop;
+ for (Y = run; Y; Y = Y->nxt)
+ if (--i == prno)
+ { if (strcmp(Y->n->name, n->lft->sym->name) != 0)
+ { printf("spin: remote reference error on '%s[%d]'\n",
+ n->lft->sym->name, prno-added);
+ non_fatal("refers to wrong proctype '%s'", Y->n->name);
+ }
+ if (strcmp(n->sym->name, "_p") == 0)
+ { if (Y->pc)
+ return Y->pc->seqno;
+ /* harmless, can only happen with -t */
+ return 0;
+ }
+#if 1
+ /* new 4.0 allow remote variables */
+ oX = X;
+ X = Y;
+
+ onl = n->lft;
+ n->lft = n->rgt;
+
+ os = n->sym;
+ n->sym = findloc(n->sym);
+
+ i = getval(n);
+
+ n->sym = os;
+ n->lft = onl;
+ X = oX;
+ return i;
+#else
+ break;
+#endif
+ }
+ printf("remote ref: %s[%d] ", n->lft->sym->name, prno-added);
+ non_fatal("%s not found", n->sym->name);
+ printf("have only:\n");
+ i = nproc - nstop - 1;
+ for (Y = run; Y; Y = Y->nxt, i--)
+ if (!strcmp(Y->n->name, n->lft->sym->name))
+ printf("\t%d\t%s\n", i, Y->n->name);
+
+ return 0;
+}
--- /dev/null
+/***** spin: spin.h *****/
+
+/* Copyright (c) 1989-2007 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#ifndef SEEN_SPIN_H
+#define SEEN_SPIN_H
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#ifndef PC
+#include <memory.h>
+#endif
+
+typedef struct Lextok {
+ unsigned short ntyp; /* node type */
+ short ismtyp; /* CONST derived from MTYP */
+ int val; /* value attribute */
+ int ln; /* line number */
+ int indstep; /* part of d_step sequence */
+ struct Symbol *fn; /* file name */
+ struct Symbol *sym; /* symbol reference */
+ struct Sequence *sq; /* sequence */
+ struct SeqList *sl; /* sequence list */
+ struct Lextok *lft, *rgt; /* children in parse tree */
+} Lextok;
+
+typedef struct Slicer {
+ Lextok *n; /* global var, usable as slice criterion */
+ short code; /* type of use: DEREF_USE or normal USE */
+ short used; /* set when handled */
+ struct Slicer *nxt; /* linked list */
+} Slicer;
+
+typedef struct Access {
+ struct Symbol *who; /* proctype name of accessor */
+ struct Symbol *what; /* proctype name of accessed */
+ int cnt, typ; /* parameter nr and, e.g., 's' or 'r' */
+ struct Access *lnk; /* linked list */
+} Access;
+
+typedef struct Symbol {
+ char *name;
+ int Nid; /* unique number for the name */
+ unsigned short type; /* bit,short,.., chan,struct */
+ unsigned char hidden; /* bit-flags:
+ 1=hide, 2=show,
+ 4=bit-equiv, 8=byte-equiv,
+ 16=formal par, 32=inline par,
+ 64=treat as if local; 128=read at least once
+ */
+ unsigned char colnr; /* for use with xspin during simulation */
+ int nbits; /* optional width specifier */
+ int nel; /* 1 if scalar, >1 if array */
+ int setat; /* last depth value changed */
+ int *val; /* runtime value(s), initl 0 */
+ Lextok **Sval; /* values for structures */
+
+ int xu; /* exclusive r or w by 1 pid */
+ struct Symbol *xup[2]; /* xr or xs proctype */
+ struct Access *access;/* e.g., senders and receives of chan */
+ Lextok *ini; /* initial value, or chan-def */
+ Lextok *Slst; /* template for structure if struct */
+ struct Symbol *Snm; /* name of the defining struct */
+ struct Symbol *owner; /* set for names of subfields in typedefs */
+ struct Symbol *context; /* 0 if global, or procname */
+ struct Symbol *next; /* linked list */
+} Symbol;
+
+typedef struct Ordered { /* links all names in Symbol table */
+ struct Symbol *entry;
+ struct Ordered *next;
+} Ordered;
+
+typedef struct Queue {
+ short qid; /* runtime q index */
+ int qlen; /* nr messages stored */
+ int nslots, nflds; /* capacity, flds/slot */
+ int setat; /* last depth value changed */
+ int *fld_width; /* type of each field */
+ int *contents; /* the values stored */
+ int *stepnr; /* depth when each msg was sent */
+ struct Queue *nxt; /* linked list */
+} Queue;
+
+typedef struct FSM_state { /* used in pangen5.c - dataflow */
+ int from; /* state number */
+ int seen; /* used for dfs */
+ int in; /* nr of incoming edges */
+ int cr; /* has reachable 1-relevant successor */
+ int scratch;
+ unsigned long *dom, *mod; /* to mark dominant nodes */
+ struct FSM_trans *t; /* outgoing edges */
+ struct FSM_trans *p; /* incoming edges, predecessors */
+ struct FSM_state *nxt; /* linked list of all states */
+} FSM_state;
+
+typedef struct FSM_trans { /* used in pangen5.c - dataflow */
+ int to;
+ short relevant; /* when sliced */
+ short round; /* ditto: iteration when marked */
+ struct FSM_use *Val[2]; /* 0=reads, 1=writes */
+ struct Element *step;
+ struct FSM_trans *nxt;
+} FSM_trans;
+
+typedef struct FSM_use { /* used in pangen5.c - dataflow */
+ Lextok *n;
+ Symbol *var;
+ int special;
+ struct FSM_use *nxt;
+} FSM_use;
+
+typedef struct Element {
+ Lextok *n; /* defines the type & contents */
+ int Seqno; /* identifies this el within system */
+ int seqno; /* identifies this el within a proc */
+ int merge; /* set by -O if step can be merged */
+ int merge_start;
+ int merge_single;
+ short merge_in; /* nr of incoming edges */
+ short merge_mark; /* state was generated in merge sequence */
+ unsigned int status; /* used by analyzer generator */
+ struct FSM_use *dead; /* optional dead variable list */
+ struct SeqList *sub; /* subsequences, for compounds */
+ struct SeqList *esc; /* zero or more escape sequences */
+ struct Element *Nxt; /* linked list - for global lookup */
+ struct Element *nxt; /* linked list - program structure */
+} Element;
+
+typedef struct Sequence {
+ Element *frst;
+ Element *last; /* links onto continuations */
+ Element *extent; /* last element in original */
+ int maxel; /* 1+largest id in sequence */
+} Sequence;
+
+typedef struct SeqList {
+ Sequence *this; /* one sequence */
+ struct SeqList *nxt; /* linked list */
+} SeqList;
+
+typedef struct Label {
+ Symbol *s;
+ Symbol *c;
+ Element *e;
+ int visible; /* label referenced in claim (slice relevant) */
+ struct Label *nxt;
+} Label;
+
+typedef struct Lbreak {
+ Symbol *l;
+ struct Lbreak *nxt;
+} Lbreak;
+
+typedef struct RunList {
+ Symbol *n; /* name */
+ int tn; /* ordinal of type */
+ int pid; /* process id */
+ int priority; /* for simulations only */
+ Element *pc; /* current stmnt */
+ Sequence *ps; /* used by analyzer generator */
+ Lextok *prov; /* provided clause */
+ Symbol *symtab; /* local variables */
+ struct RunList *nxt; /* linked list */
+} RunList;
+
+typedef struct ProcList {
+ Symbol *n; /* name */
+ Lextok *p; /* parameters */
+ Sequence *s; /* body */
+ Lextok *prov; /* provided clause */
+ short tn; /* ordinal number */
+ unsigned char det; /* deterministic */
+ unsigned char unsafe; /* contains global var inits */
+ struct ProcList *nxt; /* linked list */
+} ProcList;
+
+typedef Lextok *Lexptr;
+
+#define YYSTYPE Lexptr
+
+#define ZN (Lextok *)0
+#define ZS (Symbol *)0
+#define ZE (Element *)0
+
+#define DONE 1 /* status bits of elements */
+#define ATOM 2 /* part of an atomic chain */
+#define L_ATOM 4 /* last element in a chain */
+#define I_GLOB 8 /* inherited global ref */
+#define DONE2 16 /* used in putcode and main*/
+#define D_ATOM 32 /* deterministic atomic */
+#define ENDSTATE 64 /* normal endstate */
+#define CHECK2 128 /* status bits for remote ref check */
+#define CHECK3 256 /* status bits for atomic jump check */
+
+#define Nhash 255 /* slots in symbol hash-table */
+
+#define XR 1 /* non-shared receive-only */
+#define XS 2 /* non-shared send-only */
+#define XX 4 /* overrides XR or XS tag */
+
+#define CODE_FRAG 2 /* auto-numbered code-fragment */
+#define CODE_DECL 4 /* auto-numbered c_decl */
+#define PREDEF 3 /* predefined name: _p, _last */
+
+#define UNSIGNED 5 /* val defines width in bits */
+#define BIT 1 /* also equal to width in bits */
+#define BYTE 8 /* ditto */
+#define SHORT 16 /* ditto */
+#define INT 32 /* ditto */
+#define CHAN 64 /* not */
+#define STRUCT 128 /* user defined structure name */
+
+#define SOMETHINGBIG 65536
+#define RATHERSMALL 512
+
+#ifndef max
+#define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif
+
+enum { INIV, PUTV, LOGV }; /* for pangen[14].c */
+
+/***** prototype definitions *****/
+Element *eval_sub(Element *);
+Element *get_lab(Lextok *, int);
+Element *huntele(Element *, int, int);
+Element *huntstart(Element *);
+Element *target(Element *);
+
+Lextok *do_unless(Lextok *, Lextok *);
+Lextok *expand(Lextok *, int);
+Lextok *getuname(Symbol *);
+Lextok *mk_explicit(Lextok *, int, int);
+Lextok *nn(Lextok *, int, Lextok *, Lextok *);
+Lextok *rem_lab(Symbol *, Lextok *, Symbol *);
+Lextok *rem_var(Symbol *, Lextok *, Symbol *, Lextok *);
+Lextok *tail_add(Lextok *, Lextok *);
+
+ProcList *ready(Symbol *, Lextok *, Sequence *, int, Lextok *);
+
+SeqList *seqlist(Sequence *, SeqList *);
+Sequence *close_seq(int);
+
+Symbol *break_dest(void);
+Symbol *findloc(Symbol *);
+Symbol *has_lab(Element *, int);
+Symbol *lookup(char *);
+Symbol *prep_inline(Symbol *, Lextok *);
+
+char *emalloc(size_t);
+long Rand(void);
+
+int any_oper(Lextok *, int);
+int any_undo(Lextok *);
+int c_add_sv(FILE *);
+int cast_val(int, int, int);
+int checkvar(Symbol *, int);
+int Cnt_flds(Lextok *);
+int cnt_mpars(Lextok *);
+int complete_rendez(void);
+int enable(Lextok *);
+int Enabled0(Element *);
+int eval(Lextok *);
+int find_lab(Symbol *, Symbol *, int);
+int find_maxel(Symbol *);
+int full_name(FILE *, Lextok *, Symbol *, int);
+int getlocal(Lextok *);
+int getval(Lextok *);
+int glob_inline(char *);
+int has_typ(Lextok *, int);
+int in_bound(Symbol *, int);
+int interprint(FILE *, Lextok *);
+int printm(FILE *, Lextok *);
+int ismtype(char *);
+int isproctype(char *);
+int isutype(char *);
+int Lval_struct(Lextok *, Symbol *, int, int);
+int main(int, char **);
+int pc_value(Lextok *);
+int proper_enabler(Lextok *);
+int putcode(FILE *, Sequence *, Element *, int, int, int);
+int q_is_sync(Lextok *);
+int qlen(Lextok *);
+int qfull(Lextok *);
+int qmake(Symbol *);
+int qrecv(Lextok *, int);
+int qsend(Lextok *);
+int remotelab(Lextok *);
+int remotevar(Lextok *);
+int Rval_struct(Lextok *, Symbol *, int);
+int setlocal(Lextok *, int);
+int setval(Lextok *, int);
+int sputtype(char *, int);
+int Sym_typ(Lextok *);
+int tl_main(int, char *[]);
+int Width_set(int *, int, Lextok *);
+int yyparse(void);
+int yywrap(void);
+int yylex(void);
+
+void AST_track(Lextok *, int);
+void add_seq(Lextok *);
+void alldone(int);
+void announce(char *);
+void c_state(Symbol *, Symbol *, Symbol *);
+void c_add_def(FILE *);
+void c_add_loc(FILE *, char *);
+void c_add_locinit(FILE *, int, char *);
+void c_add_use(FILE *);
+void c_chandump(FILE *);
+void c_preview(void);
+void c_struct(FILE *, char *, Symbol *);
+void c_track(Symbol *, Symbol *, Symbol *);
+void c_var(FILE *, char *, Symbol *);
+void c_wrapper(FILE *);
+void chanaccess(void);
+void check_param_count(int, Lextok *);
+void checkrun(Symbol *, int);
+void comment(FILE *, Lextok *, int);
+void cross_dsteps(Lextok *, Lextok *);
+void doq(Symbol *, int, RunList *);
+void dotag(FILE *, char *);
+void do_locinits(FILE *);
+void do_var(FILE *, int, char *, Symbol *, char *, char *, char *);
+void dump_struct(Symbol *, char *, RunList *);
+void dumpclaims(FILE *, int, char *);
+void dumpglobals(void);
+void dumplabels(void);
+void dumplocal(RunList *);
+void dumpsrc(int, int);
+void fatal(char *, char *);
+void fix_dest(Symbol *, Symbol *);
+void genaddproc(void);
+void genaddqueue(void);
+void gencodetable(FILE *);
+void genheader(void);
+void genother(void);
+void gensrc(void);
+void gensvmap(void);
+void genunio(void);
+void ini_struct(Symbol *);
+void loose_ends(void);
+void make_atomic(Sequence *, int);
+void match_trail(void);
+void no_side_effects(char *);
+void nochan_manip(Lextok *, Lextok *, int);
+void non_fatal(char *, char *);
+void ntimes(FILE *, int, int, char *c[]);
+void open_seq(int);
+void p_talk(Element *, int);
+void pickup_inline(Symbol *, Lextok *);
+void plunk_c_decls(FILE *);
+void plunk_c_fcts(FILE *);
+void plunk_expr(FILE *, char *);
+void plunk_inline(FILE *, char *, int);
+void prehint(Symbol *);
+void preruse(FILE *, Lextok *);
+void prune_opts(Lextok *);
+void pstext(int, char *);
+void pushbreak(void);
+void putname(FILE *, char *, Lextok *, int, char *);
+void putremote(FILE *, Lextok *, int);
+void putskip(int);
+void putsrc(Element *);
+void putstmnt(FILE *, Lextok *, int);
+void putunames(FILE *);
+void rem_Seq(void);
+void runnable(ProcList *, int, int);
+void sched(void);
+void setaccess(Symbol *, Symbol *, int, int);
+void set_lab(Symbol *, Element *);
+void setmtype(Lextok *);
+void setpname(Lextok *);
+void setptype(Lextok *, int, Lextok *);
+void setuname(Lextok *);
+void setutype(Lextok *, Symbol *, Lextok *);
+void setxus(Lextok *, int);
+void Srand(unsigned);
+void start_claim(int);
+void struct_name(Lextok *, Symbol *, int, char *);
+void symdump(void);
+void symvar(Symbol *);
+void trackchanuse(Lextok *, Lextok *, int);
+void trackvar(Lextok *, Lextok *);
+void trackrun(Lextok *);
+void trapwonly(Lextok * /* , char * */); /* spin.y and main.c */
+void typ2c(Symbol *);
+void typ_ck(int, int, char *);
+void undostmnt(Lextok *, int);
+void unrem_Seq(void);
+void unskip(int);
+void varcheck(Element *, Element *);
+void whoruns(int);
+void wrapup(int);
+void yyerror(char *, ...);
+#endif
--- /dev/null
+/***** spin: spin.y *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+%{
+#include "spin.h"
+#include <stdarg.h>
+
+#define YYDEBUG 0
+#define Stop nn(ZN,'@',ZN,ZN)
+
+extern Symbol *context, *owner;
+extern int u_sync, u_async, dumptab;
+extern short has_sorted, has_random, has_enabled, has_pcvalue, has_np;
+extern short has_code, has_state, has_io;
+extern void count_runs(Lextok *);
+extern void no_internals(Lextok *);
+extern void any_runs(Lextok *);
+extern void validref(Lextok *, Lextok *);
+extern char yytext[];
+
+int Mpars = 0; /* max nr of message parameters */
+int Expand_Ok = 0, realread = 1, IArgs = 0, NamesNotAdded = 0;
+char *claimproc = (char *) 0;
+char *eventmap = (char *) 0;
+
+static int Embedded = 0, inEventMap = 0, has_ini = 0;
+
+%}
+
+%token ASSERT PRINT PRINTM
+%token C_CODE C_DECL C_EXPR C_STATE C_TRACK
+%token RUN LEN ENABLED EVAL PC_VAL
+%token TYPEDEF MTYPE INLINE LABEL OF
+%token GOTO BREAK ELSE SEMI
+%token IF FI DO OD SEP
+%token ATOMIC NON_ATOMIC D_STEP UNLESS
+%token TIMEOUT NONPROGRESS
+%token ACTIVE PROCTYPE D_PROCTYPE
+%token HIDDEN SHOW ISLOCAL
+%token PRIORITY PROVIDED
+%token FULL EMPTY NFULL NEMPTY
+%token CONST TYPE XU /* val */
+%token NAME UNAME PNAME INAME /* sym */
+%token STRING CLAIM TRACE INIT /* sym */
+
+%right ASGN
+%left SND O_SND RCV R_RCV /* SND doubles as boolean negation */
+%left OR
+%left AND
+%left '|'
+%left '^'
+%left '&'
+%left EQ NE
+%left GT LT GE LE
+%left LSHIFT RSHIFT
+%left '+' '-'
+%left '*' '/' '%'
+%left INCR DECR
+%right '~' UMIN NEG
+%left DOT
+%%
+
+/** PROMELA Grammar Rules **/
+
+program : units { yytext[0] = '\0'; }
+ ;
+
+units : unit
+ | units unit
+ ;
+
+unit : proc /* proctype { } */
+ | init /* init { } */
+ | claim /* never claim */
+ | events /* event assertions */
+ | one_decl /* variables, chans */
+ | utype /* user defined types */
+ | c_fcts /* c functions etc. */
+ | ns /* named sequence */
+ | SEMI /* optional separator */
+ | error
+ ;
+
+proc : inst /* optional instantiator */
+ proctype NAME {
+ setptype($3, PROCTYPE, ZN);
+ setpname($3);
+ context = $3->sym;
+ context->ini = $2; /* linenr and file */
+ Expand_Ok++; /* expand struct names in decl */
+ has_ini = 0;
+ }
+ '(' decl ')' { Expand_Ok--;
+ if (has_ini)
+ fatal("initializer in parameter list", (char *) 0);
+ }
+ Opt_priority
+ Opt_enabler
+ body { ProcList *rl;
+ rl = ready($3->sym, $6, $11->sq, $2->val, $10);
+ if ($1 != ZN && $1->val > 0)
+ { int j;
+ for (j = 0; j < $1->val; j++)
+ runnable(rl, $9?$9->val:1, 1);
+ announce(":root:");
+ if (dumptab) $3->sym->ini = $1;
+ }
+ if (rl && has_ini == 1) /* global initializations, unsafe */
+ { /* printf("proctype %s has initialized data\n",
+ $3->sym->name);
+ */
+ rl->unsafe = 1;
+ }
+ context = ZS;
+ }
+ ;
+
+proctype: PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 0; }
+ | D_PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; }
+ ;
+
+inst : /* empty */ { $$ = ZN; }
+ | ACTIVE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; }
+ | ACTIVE '[' CONST ']' {
+ $$ = nn(ZN,CONST,ZN,ZN); $$->val = $3->val;
+ if ($3->val > 255)
+ non_fatal("max nr of processes is 255\n", "");
+ }
+ | ACTIVE '[' NAME ']' {
+ $$ = nn(ZN,CONST,ZN,ZN);
+ $$->val = 0;
+ if (!$3->sym->type)
+ non_fatal("undeclared variable %s",
+ $3->sym->name);
+ else if ($3->sym->ini->ntyp != CONST)
+ non_fatal("need constant initializer for %s\n",
+ $3->sym->name);
+ else
+ $$->val = $3->sym->ini->val;
+ }
+ ;
+
+init : INIT { context = $1->sym; }
+ Opt_priority
+ body { ProcList *rl;
+ rl = ready(context, ZN, $4->sq, 0, ZN);
+ runnable(rl, $3?$3->val:1, 1);
+ announce(":root:");
+ context = ZS;
+ }
+ ;
+
+claim : CLAIM { context = $1->sym;
+ if (claimproc)
+ non_fatal("claim %s redefined", claimproc);
+ claimproc = $1->sym->name;
+ }
+ body { (void) ready($1->sym, ZN, $3->sq, 0, ZN);
+ context = ZS;
+ }
+ ;
+
+events : TRACE { context = $1->sym;
+ if (eventmap)
+ non_fatal("trace %s redefined", eventmap);
+ eventmap = $1->sym->name;
+ inEventMap++;
+ }
+ body { (void) ready($1->sym, ZN, $3->sq, 0, ZN);
+ context = ZS;
+ inEventMap--;
+ }
+ ;
+
+utype : TYPEDEF NAME { if (context)
+ fatal("typedef %s must be global",
+ $2->sym->name);
+ owner = $2->sym;
+ }
+ '{' decl_lst '}' { setuname($5); owner = ZS; }
+ ;
+
+nm : NAME { $$ = $1; }
+ | INAME { $$ = $1;
+ if (IArgs)
+ fatal("invalid use of '%s'", $1->sym->name);
+ }
+ ;
+
+ns : INLINE nm '(' { NamesNotAdded++; }
+ args ')' { prep_inline($2->sym, $5);
+ NamesNotAdded--;
+ }
+ ;
+
+c_fcts : ccode { /* leaves pseudo-inlines with sym of
+ * type CODE_FRAG or CODE_DECL in global context
+ */
+ }
+ | cstate
+ ;
+
+cstate : C_STATE STRING STRING {
+ c_state($2->sym, $3->sym, ZS);
+ has_code = has_state = 1;
+ }
+ | C_TRACK STRING STRING {
+ c_track($2->sym, $3->sym, ZS);
+ has_code = has_state = 1;
+ }
+ | C_STATE STRING STRING STRING {
+ c_state($2->sym, $3->sym, $4->sym);
+ has_code = has_state = 1;
+ }
+ | C_TRACK STRING STRING STRING {
+ c_track($2->sym, $3->sym, $4->sym);
+ has_code = has_state = 1;
+ }
+ ;
+
+ccode : C_CODE { Symbol *s;
+ NamesNotAdded++;
+ s = prep_inline(ZS, ZN);
+ NamesNotAdded--;
+ $$ = nn(ZN, C_CODE, ZN, ZN);
+ $$->sym = s;
+ has_code = 1;
+ }
+ | C_DECL { Symbol *s;
+ NamesNotAdded++;
+ s = prep_inline(ZS, ZN);
+ NamesNotAdded--;
+ s->type = CODE_DECL;
+ $$ = nn(ZN, C_CODE, ZN, ZN);
+ $$->sym = s;
+ has_code = 1;
+ }
+ ;
+cexpr : C_EXPR { Symbol *s;
+ NamesNotAdded++;
+ s = prep_inline(ZS, ZN);
+ NamesNotAdded--;
+ $$ = nn(ZN, C_EXPR, ZN, ZN);
+ $$->sym = s;
+ no_side_effects(s->name);
+ has_code = 1;
+ }
+ ;
+
+body : '{' { open_seq(1); }
+ sequence OS { add_seq(Stop); }
+ '}' { $$->sq = close_seq(0); }
+ ;
+
+sequence: step { if ($1) add_seq($1); }
+ | sequence MS step { if ($3) add_seq($3); }
+ ;
+
+step : one_decl { $$ = ZN; }
+ | XU vref_lst { setxus($2, $1->val); $$ = ZN; }
+ | NAME ':' one_decl { fatal("label preceding declaration,", (char *)0); }
+ | NAME ':' XU { fatal("label predecing xr/xs claim,", 0); }
+ | stmnt { $$ = $1; }
+ | stmnt UNLESS stmnt { $$ = do_unless($1, $3); }
+ ;
+
+vis : /* empty */ { $$ = ZN; }
+ | HIDDEN { $$ = $1; }
+ | SHOW { $$ = $1; }
+ | ISLOCAL { $$ = $1; }
+ ;
+
+asgn: /* empty */
+ | ASGN
+ ;
+
+one_decl: vis TYPE var_list { setptype($3, $2->val, $1); $$ = $3; }
+ | vis UNAME var_list { setutype($3, $2->sym, $1);
+ $$ = expand($3, Expand_Ok);
+ }
+ | vis TYPE asgn '{' nlst '}' {
+ if ($2->val != MTYPE)
+ fatal("malformed declaration", 0);
+ setmtype($5);
+ if ($1)
+ non_fatal("cannot %s mtype (ignored)",
+ $1->sym->name);
+ if (context != ZS)
+ fatal("mtype declaration must be global", 0);
+ }
+ ;
+
+decl_lst: one_decl { $$ = nn(ZN, ',', $1, ZN); }
+ | one_decl SEMI
+ decl_lst { $$ = nn(ZN, ',', $1, $3); }
+ ;
+
+decl : /* empty */ { $$ = ZN; }
+ | decl_lst { $$ = $1; }
+ ;
+
+vref_lst: varref { $$ = nn($1, XU, $1, ZN); }
+ | varref ',' vref_lst { $$ = nn($1, XU, $1, $3); }
+ ;
+
+var_list: ivar { $$ = nn($1, TYPE, ZN, ZN); }
+ | ivar ',' var_list { $$ = nn($1, TYPE, ZN, $3); }
+ ;
+
+ivar : vardcl { $$ = $1;
+ $1->sym->ini = nn(ZN,CONST,ZN,ZN);
+ $1->sym->ini->val = 0;
+ }
+ | vardcl ASGN expr { $1->sym->ini = $3; $$ = $1;
+ trackvar($1,$3);
+ if ($3->ntyp == CONST
+ || ($3->ntyp == NAME && $3->sym->context))
+ { has_ini = 2; /* local init */
+ } else
+ { has_ini = 1; /* possibly global */
+ }
+ }
+ | vardcl ASGN ch_init { $1->sym->ini = $3;
+ $$ = $1; has_ini = 1;
+ }
+ ;
+
+ch_init : '[' CONST ']' OF
+ '{' typ_list '}' { if ($2->val) u_async++;
+ else u_sync++;
+ { int i = cnt_mpars($6);
+ Mpars = max(Mpars, i);
+ }
+ $$ = nn(ZN, CHAN, ZN, $6);
+ $$->val = $2->val;
+ }
+ ;
+
+vardcl : NAME { $1->sym->nel = 1; $$ = $1; }
+ | NAME ':' CONST { $1->sym->nbits = $3->val;
+ if ($3->val >= 8*sizeof(long))
+ { non_fatal("width-field %s too large",
+ $1->sym->name);
+ $3->val = 8*sizeof(long)-1;
+ }
+ $1->sym->nel = 1; $$ = $1;
+ }
+ | NAME '[' CONST ']' { $1->sym->nel = $3->val; $$ = $1; }
+ ;
+
+varref : cmpnd { $$ = mk_explicit($1, Expand_Ok, NAME); }
+ ;
+
+pfld : NAME { $$ = nn($1, NAME, ZN, ZN); }
+ | NAME { owner = ZS; }
+ '[' expr ']' { $$ = nn($1, NAME, $4, ZN); }
+ ;
+
+cmpnd : pfld { Embedded++;
+ if ($1->sym->type == STRUCT)
+ owner = $1->sym->Snm;
+ }
+ sfld { $$ = $1; $$->rgt = $3;
+ if ($3 && $1->sym->type != STRUCT)
+ $1->sym->type = STRUCT;
+ Embedded--;
+ if (!Embedded && !NamesNotAdded
+ && !$1->sym->type)
+ non_fatal("undeclared variable: %s",
+ $1->sym->name);
+ if ($3) validref($1, $3->lft);
+ owner = ZS;
+ }
+ ;
+
+sfld : /* empty */ { $$ = ZN; }
+ | '.' cmpnd %prec DOT { $$ = nn(ZN, '.', $2, ZN); }
+ ;
+
+stmnt : Special { $$ = $1; }
+ | Stmnt { $$ = $1;
+ if (inEventMap)
+ non_fatal("not an event", (char *)0);
+ }
+ ;
+
+Special : varref RCV { Expand_Ok++; }
+ rargs { Expand_Ok--; has_io++;
+ $$ = nn($1, 'r', $1, $4);
+ trackchanuse($4, ZN, 'R');
+ }
+ | varref SND { Expand_Ok++; }
+ margs { Expand_Ok--; has_io++;
+ $$ = nn($1, 's', $1, $4);
+ $$->val=0; trackchanuse($4, ZN, 'S');
+ any_runs($4);
+ }
+ | IF options FI { $$ = nn($1, IF, ZN, ZN);
+ $$->sl = $2->sl;
+ prune_opts($$);
+ }
+ | DO { pushbreak(); }
+ options OD { $$ = nn($1, DO, ZN, ZN);
+ $$->sl = $3->sl;
+ prune_opts($$);
+ }
+ | BREAK { $$ = nn(ZN, GOTO, ZN, ZN);
+ $$->sym = break_dest();
+ }
+ | GOTO NAME { $$ = nn($2, GOTO, ZN, ZN);
+ if ($2->sym->type != 0
+ && $2->sym->type != LABEL) {
+ non_fatal("bad label-name %s",
+ $2->sym->name);
+ }
+ $2->sym->type = LABEL;
+ }
+ | NAME ':' stmnt { $$ = nn($1, ':',$3, ZN);
+ if ($1->sym->type != 0
+ && $1->sym->type != LABEL) {
+ non_fatal("bad label-name %s",
+ $1->sym->name);
+ }
+ $1->sym->type = LABEL;
+ }
+ ;
+
+Stmnt : varref ASGN expr { $$ = nn($1, ASGN, $1, $3);
+ trackvar($1, $3);
+ nochan_manip($1, $3, 0);
+ no_internals($1);
+ }
+ | varref INCR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1;
+ $$ = nn(ZN, '+', $1, $$);
+ $$ = nn($1, ASGN, $1, $$);
+ trackvar($1, $1);
+ no_internals($1);
+ if ($1->sym->type == CHAN)
+ fatal("arithmetic on chan", (char *)0);
+ }
+ | varref DECR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1;
+ $$ = nn(ZN, '-', $1, $$);
+ $$ = nn($1, ASGN, $1, $$);
+ trackvar($1, $1);
+ no_internals($1);
+ if ($1->sym->type == CHAN)
+ fatal("arithmetic on chan id's", (char *)0);
+ }
+ | PRINT '(' STRING { realread = 0; }
+ prargs ')' { $$ = nn($3, PRINT, $5, ZN); realread = 1; }
+ | PRINTM '(' varref ')' { $$ = nn(ZN, PRINTM, $3, ZN); }
+ | PRINTM '(' CONST ')' { $$ = nn(ZN, PRINTM, $3, ZN); }
+ | ASSERT full_expr { $$ = nn(ZN, ASSERT, $2, ZN); AST_track($2, 0); }
+ | ccode { $$ = $1; }
+ | varref R_RCV { Expand_Ok++; }
+ rargs { Expand_Ok--; has_io++;
+ $$ = nn($1, 'r', $1, $4);
+ $$->val = has_random = 1;
+ trackchanuse($4, ZN, 'R');
+ }
+ | varref RCV { Expand_Ok++; }
+ LT rargs GT { Expand_Ok--; has_io++;
+ $$ = nn($1, 'r', $1, $5);
+ $$->val = 2; /* fifo poll */
+ trackchanuse($5, ZN, 'R');
+ }
+ | varref R_RCV { Expand_Ok++; }
+ LT rargs GT { Expand_Ok--; has_io++; /* rrcv poll */
+ $$ = nn($1, 'r', $1, $5);
+ $$->val = 3; has_random = 1;
+ trackchanuse($5, ZN, 'R');
+ }
+ | varref O_SND { Expand_Ok++; }
+ margs { Expand_Ok--; has_io++;
+ $$ = nn($1, 's', $1, $4);
+ $$->val = has_sorted = 1;
+ trackchanuse($4, ZN, 'S');
+ any_runs($4);
+ }
+ | full_expr { $$ = nn(ZN, 'c', $1, ZN); count_runs($$); }
+ | ELSE { $$ = nn(ZN,ELSE,ZN,ZN);
+ }
+ | ATOMIC '{' { open_seq(0); }
+ sequence OS '}' { $$ = nn($1, ATOMIC, ZN, ZN);
+ $$->sl = seqlist(close_seq(3), 0);
+ make_atomic($$->sl->this, 0);
+ }
+ | D_STEP '{' { open_seq(0); rem_Seq(); }
+ sequence OS '}' { $$ = nn($1, D_STEP, ZN, ZN);
+ $$->sl = seqlist(close_seq(4), 0);
+ make_atomic($$->sl->this, D_ATOM);
+ unrem_Seq();
+ }
+ | '{' { open_seq(0); }
+ sequence OS '}' { $$ = nn(ZN, NON_ATOMIC, ZN, ZN);
+ $$->sl = seqlist(close_seq(5), 0);
+ }
+ | INAME { IArgs++; }
+ '(' args ')' { pickup_inline($1->sym, $4); IArgs--; }
+ Stmnt { $$ = $7; }
+ ;
+
+options : option { $$->sl = seqlist($1->sq, 0); }
+ | option options { $$->sl = seqlist($1->sq, $2->sl); }
+ ;
+
+option : SEP { open_seq(0); }
+ sequence OS { $$ = nn(ZN,0,ZN,ZN);
+ $$->sq = close_seq(6); }
+ ;
+
+OS : /* empty */
+ | SEMI { /* redundant semi at end of sequence */ }
+ ;
+
+MS : SEMI { /* at least one semi-colon */ }
+ | MS SEMI { /* but more are okay too */ }
+ ;
+
+aname : NAME { $$ = $1; }
+ | PNAME { $$ = $1; }
+ ;
+
+expr : '(' expr ')' { $$ = $2; }
+ | expr '+' expr { $$ = nn(ZN, '+', $1, $3); }
+ | expr '-' expr { $$ = nn(ZN, '-', $1, $3); }
+ | expr '*' expr { $$ = nn(ZN, '*', $1, $3); }
+ | expr '/' expr { $$ = nn(ZN, '/', $1, $3); }
+ | expr '%' expr { $$ = nn(ZN, '%', $1, $3); }
+ | expr '&' expr { $$ = nn(ZN, '&', $1, $3); }
+ | expr '^' expr { $$ = nn(ZN, '^', $1, $3); }
+ | expr '|' expr { $$ = nn(ZN, '|', $1, $3); }
+ | expr GT expr { $$ = nn(ZN, GT, $1, $3); }
+ | expr LT expr { $$ = nn(ZN, LT, $1, $3); }
+ | expr GE expr { $$ = nn(ZN, GE, $1, $3); }
+ | expr LE expr { $$ = nn(ZN, LE, $1, $3); }
+ | expr EQ expr { $$ = nn(ZN, EQ, $1, $3); }
+ | expr NE expr { $$ = nn(ZN, NE, $1, $3); }
+ | expr AND expr { $$ = nn(ZN, AND, $1, $3); }
+ | expr OR expr { $$ = nn(ZN, OR, $1, $3); }
+ | expr LSHIFT expr { $$ = nn(ZN, LSHIFT,$1, $3); }
+ | expr RSHIFT expr { $$ = nn(ZN, RSHIFT,$1, $3); }
+ | '~' expr { $$ = nn(ZN, '~', $2, ZN); }
+ | '-' expr %prec UMIN { $$ = nn(ZN, UMIN, $2, ZN); }
+ | SND expr %prec NEG { $$ = nn(ZN, '!', $2, ZN); }
+
+ | '(' expr SEMI expr ':' expr ')' {
+ $$ = nn(ZN, OR, $4, $6);
+ $$ = nn(ZN, '?', $2, $$);
+ }
+
+ | RUN aname { Expand_Ok++;
+ if (!context)
+ fatal("used 'run' outside proctype",
+ (char *) 0);
+ }
+ '(' args ')'
+ Opt_priority { Expand_Ok--;
+ $$ = nn($2, RUN, $5, ZN);
+ $$->val = ($7) ? $7->val : 1;
+ trackchanuse($5, $2, 'A'); trackrun($$);
+ }
+ | LEN '(' varref ')' { $$ = nn($3, LEN, $3, ZN); }
+ | ENABLED '(' expr ')' { $$ = nn(ZN, ENABLED, $3, ZN);
+ has_enabled++;
+ }
+ | varref RCV { Expand_Ok++; }
+ '[' rargs ']' { Expand_Ok--; has_io++;
+ $$ = nn($1, 'R', $1, $5);
+ }
+ | varref R_RCV { Expand_Ok++; }
+ '[' rargs ']' { Expand_Ok--; has_io++;
+ $$ = nn($1, 'R', $1, $5);
+ $$->val = has_random = 1;
+ }
+ | varref { $$ = $1; trapwonly($1 /*, "varref" */); }
+ | cexpr { $$ = $1; }
+ | CONST { $$ = nn(ZN,CONST,ZN,ZN);
+ $$->ismtyp = $1->ismtyp;
+ $$->val = $1->val;
+ }
+ | TIMEOUT { $$ = nn(ZN,TIMEOUT, ZN, ZN); }
+ | NONPROGRESS { $$ = nn(ZN,NONPROGRESS, ZN, ZN);
+ has_np++;
+ }
+ | PC_VAL '(' expr ')' { $$ = nn(ZN, PC_VAL, $3, ZN);
+ has_pcvalue++;
+ }
+ | PNAME '[' expr ']' '@' NAME
+ { $$ = rem_lab($1->sym, $3, $6->sym); }
+ | PNAME '[' expr ']' ':' pfld
+ { $$ = rem_var($1->sym, $3, $6->sym, $6->lft); }
+ | PNAME '@' NAME { $$ = rem_lab($1->sym, ZN, $3->sym); }
+ | PNAME ':' pfld { $$ = rem_var($1->sym, ZN, $3->sym, $3->lft); }
+ ;
+
+Opt_priority: /* none */ { $$ = ZN; }
+ | PRIORITY CONST { $$ = $2; }
+ ;
+
+full_expr: expr { $$ = $1; }
+ | Expr { $$ = $1; }
+ ;
+
+Opt_enabler: /* none */ { $$ = ZN; }
+ | PROVIDED '(' full_expr ')' { if (!proper_enabler($3))
+ { non_fatal("invalid PROVIDED clause",
+ (char *)0);
+ $$ = ZN;
+ } else
+ $$ = $3;
+ }
+ | PROVIDED error { $$ = ZN;
+ non_fatal("usage: provided ( ..expr.. )",
+ (char *)0);
+ }
+ ;
+
+ /* an Expr cannot be negated - to protect Probe expressions */
+Expr : Probe { $$ = $1; }
+ | '(' Expr ')' { $$ = $2; }
+ | Expr AND Expr { $$ = nn(ZN, AND, $1, $3); }
+ | Expr AND expr { $$ = nn(ZN, AND, $1, $3); }
+ | Expr OR Expr { $$ = nn(ZN, OR, $1, $3); }
+ | Expr OR expr { $$ = nn(ZN, OR, $1, $3); }
+ | expr AND Expr { $$ = nn(ZN, AND, $1, $3); }
+ | expr OR Expr { $$ = nn(ZN, OR, $1, $3); }
+ ;
+
+Probe : FULL '(' varref ')' { $$ = nn($3, FULL, $3, ZN); }
+ | NFULL '(' varref ')' { $$ = nn($3, NFULL, $3, ZN); }
+ | EMPTY '(' varref ')' { $$ = nn($3, EMPTY, $3, ZN); }
+ | NEMPTY '(' varref ')' { $$ = nn($3,NEMPTY, $3, ZN); }
+ ;
+
+basetype: TYPE { $$->sym = ZS;
+ $$->val = $1->val;
+ if ($$->val == UNSIGNED)
+ fatal("unsigned cannot be used as mesg type", 0);
+ }
+ | UNAME { $$->sym = $1->sym;
+ $$->val = STRUCT;
+ }
+ | error /* e.g., unsigned ':' const */
+ ;
+
+typ_list: basetype { $$ = nn($1, $1->val, ZN, ZN); }
+ | basetype ',' typ_list { $$ = nn($1, $1->val, ZN, $3); }
+ ;
+
+args : /* empty */ { $$ = ZN; }
+ | arg { $$ = $1; }
+ ;
+
+prargs : /* empty */ { $$ = ZN; }
+ | ',' arg { $$ = $2; }
+ ;
+
+margs : arg { $$ = $1; }
+ | expr '(' arg ')' { if ($1->ntyp == ',')
+ $$ = tail_add($1, $3);
+ else
+ $$ = nn(ZN, ',', $1, $3);
+ }
+ ;
+
+arg : expr { if ($1->ntyp == ',')
+ $$ = $1;
+ else
+ $$ = nn(ZN, ',', $1, ZN);
+ }
+ | expr ',' arg { if ($1->ntyp == ',')
+ $$ = tail_add($1, $3);
+ else
+ $$ = nn(ZN, ',', $1, $3);
+ }
+ ;
+
+rarg : varref { $$ = $1; trackvar($1, $1);
+ trapwonly($1 /*, "rarg" */); }
+ | EVAL '(' expr ')' { $$ = nn(ZN,EVAL,$3,ZN);
+ trapwonly($1 /*, "eval rarg" */); }
+ | CONST { $$ = nn(ZN,CONST,ZN,ZN);
+ $$->ismtyp = $1->ismtyp;
+ $$->val = $1->val;
+ }
+ | '-' CONST %prec UMIN { $$ = nn(ZN,CONST,ZN,ZN);
+ $$->val = - ($2->val);
+ }
+ ;
+
+rargs : rarg { if ($1->ntyp == ',')
+ $$ = $1;
+ else
+ $$ = nn(ZN, ',', $1, ZN);
+ }
+ | rarg ',' rargs { if ($1->ntyp == ',')
+ $$ = tail_add($1, $3);
+ else
+ $$ = nn(ZN, ',', $1, $3);
+ }
+ | rarg '(' rargs ')' { if ($1->ntyp == ',')
+ $$ = tail_add($1, $3);
+ else
+ $$ = nn(ZN, ',', $1, $3);
+ }
+ | '(' rargs ')' { $$ = $2; }
+ ;
+
+nlst : NAME { $$ = nn($1, NAME, ZN, ZN);
+ $$ = nn(ZN, ',', $$, ZN); }
+ | nlst NAME { $$ = nn($2, NAME, ZN, ZN);
+ $$ = nn(ZN, ',', $$, $1);
+ }
+ | nlst ',' { $$ = $1; /* commas optional */ }
+ ;
+%%
+
+void
+yyerror(char *fmt, ...)
+{
+ non_fatal(fmt, (char *) 0);
+}
--- /dev/null
+/***** spin: spinlex.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include <stdlib.h>
+#include "spin.h"
+#include "y.tab.h"
+
+#define MAXINL 16 /* max recursion depth inline fcts */
+#define MAXPAR 32 /* max params to an inline call */
+#define MAXLEN 512 /* max len of an actual parameter text */
+
+typedef struct IType {
+ Symbol *nm; /* name of the type */
+ Lextok *cn; /* contents */
+ Lextok *params; /* formal pars if any */
+ char **anms; /* literal text for actual pars */
+ char *prec; /* precondition for c_code or c_expr */
+ int dln, cln; /* def and call linenr */
+ Symbol *dfn, *cfn; /* def and call filename */
+ struct IType *nxt; /* linked list */
+} IType;
+
+typedef struct C_Added {
+ Symbol *s;
+ Symbol *t;
+ Symbol *ival;
+ struct C_Added *nxt;
+} C_Added;
+
+extern RunList *X;
+extern ProcList *rdy;
+extern Symbol *Fname;
+extern Symbol *context, *owner;
+extern YYSTYPE yylval;
+extern short has_last, has_code;
+extern int verbose, IArgs, hastrack, separate;
+
+short has_stack = 0;
+int lineno = 1;
+char yytext[2048];
+FILE *yyin, *yyout;
+
+static C_Added *c_added, *c_tracked;
+static IType *Inline_stub[MAXINL];
+static char *ReDiRect;
+static char *Inliner[MAXINL], IArg_cont[MAXPAR][MAXLEN];
+static unsigned char in_comment=0;
+static int IArgno = 0, Inlining = -1;
+static int check_name(char *);
+
+#if 1
+#define Token(y) { if (in_comment) goto again; \
+ yylval = nn(ZN,0,ZN,ZN); return y; }
+
+#define ValToken(x, y) { if (in_comment) goto again; \
+ yylval = nn(ZN,0,ZN,ZN); yylval->val = x; return y; }
+
+#define SymToken(x, y) { if (in_comment) goto again; \
+ yylval = nn(ZN,0,ZN,ZN); yylval->sym = x; return y; }
+#else
+#define Token(y) { yylval = nn(ZN,0,ZN,ZN); \
+ if (!in_comment) return y; else goto again; }
+
+#define ValToken(x, y) { yylval = nn(ZN,0,ZN,ZN); yylval->val = x; \
+ if (!in_comment) return y; else goto again; }
+
+#define SymToken(x, y) { yylval = nn(ZN,0,ZN,ZN); yylval->sym = x; \
+ if (!in_comment) return y; else goto again; }
+#endif
+
+static int getinline(void);
+static void uninline(void);
+
+#if 1
+#define Getchar() ((Inlining<0)?getc(yyin):getinline())
+#define Ungetch(c) {if (Inlining<0) ungetc(c,yyin); else uninline(); }
+
+#else
+
+static int
+Getchar(void)
+{ int c;
+ if (Inlining<0)
+ c = getc(yyin);
+ else
+ c = getinline();
+ if (0)
+ { printf("<%c:%d>[%d] ", c, c, Inlining);
+ }
+ return c;
+}
+
+static void
+Ungetch(int c)
+{
+ if (Inlining<0)
+ ungetc(c,yyin);
+ else
+ uninline();
+#if 1
+ printf("<bs>");
+#endif
+}
+#endif
+
+static int
+notquote(int c)
+{ return (c != '\"' && c != '\n');
+}
+
+int
+isalnum_(int c)
+{ return (isalnum(c) || c == '_');
+}
+
+static int
+isalpha_(int c)
+{ return isalpha(c); /* could be macro */
+}
+
+static int
+isdigit_(int c)
+{ return isdigit(c); /* could be macro */
+}
+
+static void
+getword(int first, int (*tst)(int))
+{ int i=0, c;
+
+ yytext[i++]= (char) first;
+ while (tst(c = Getchar()))
+ { yytext[i++] = (char) c;
+ if (c == '\\')
+ { c = Getchar();
+ yytext[i++] = (char) c; /* no tst */
+ } }
+ yytext[i] = '\0';
+ Ungetch(c);
+}
+
+static int
+follow(int tok, int ifyes, int ifno)
+{ int c;
+
+ if ((c = Getchar()) == tok)
+ return ifyes;
+ Ungetch(c);
+
+ return ifno;
+}
+
+static IType *seqnames;
+
+static void
+def_inline(Symbol *s, int ln, char *ptr, char *prc, Lextok *nms)
+{ IType *tmp;
+ char *nw = (char *) emalloc(strlen(ptr)+1);
+ strcpy(nw, ptr);
+
+ for (tmp = seqnames; tmp; tmp = tmp->nxt)
+ if (!strcmp(s->name, tmp->nm->name))
+ { non_fatal("procedure name %s redefined",
+ tmp->nm->name);
+ tmp->cn = (Lextok *) nw;
+ tmp->params = nms;
+ tmp->dln = ln;
+ tmp->dfn = Fname;
+ return;
+ }
+ tmp = (IType *) emalloc(sizeof(IType));
+ tmp->nm = s;
+ tmp->cn = (Lextok *) nw;
+ tmp->params = nms;
+ if (strlen(prc) > 0)
+ { tmp->prec = (char *) emalloc(strlen(prc)+1);
+ strcpy(tmp->prec, prc);
+ }
+ tmp->dln = ln;
+ tmp->dfn = Fname;
+ tmp->nxt = seqnames;
+ seqnames = tmp;
+}
+
+void
+gencodetable(FILE *fd)
+{ IType *tmp;
+ char *q;
+ int cnt;
+
+ if (separate == 2) return;
+
+ fprintf(fd, "struct {\n");
+ fprintf(fd, " char *c; char *t;\n");
+ fprintf(fd, "} code_lookup[] = {\n");
+
+ if (has_code)
+ for (tmp = seqnames; tmp; tmp = tmp->nxt)
+ if (tmp->nm->type == CODE_FRAG
+ || tmp->nm->type == CODE_DECL)
+ { fprintf(fd, "\t{ \"%s\", ",
+ tmp->nm->name);
+ q = (char *) tmp->cn;
+
+ while (*q == '\n' || *q == '\r' || *q == '\\')
+ q++;
+
+ fprintf(fd, "\"");
+ cnt = 0;
+ while (*q && cnt < 1024) /* pangen1.h allows 2048 */
+ { switch (*q) {
+ case '"':
+ fprintf(fd, "\\\"");
+ break;
+ case '%':
+ fprintf(fd, "%%");
+ break;
+ case '\n':
+ fprintf(fd, "\\n");
+ break;
+ default:
+ putc(*q, fd);
+ break;
+ }
+ q++; cnt++;
+ }
+ if (*q) fprintf(fd, "...");
+ fprintf(fd, "\"");
+ fprintf(fd, " },\n");
+ }
+
+ fprintf(fd, " { (char *) 0, \"\" }\n");
+ fprintf(fd, "};\n");
+}
+
+static int
+iseqname(char *t)
+{ IType *tmp;
+
+ for (tmp = seqnames; tmp; tmp = tmp->nxt)
+ { if (!strcmp(t, tmp->nm->name))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+getinline(void)
+{ int c;
+
+ if (ReDiRect)
+ { c = *ReDiRect++;
+ if (c == '\0')
+ { ReDiRect = (char *) 0;
+ c = *Inliner[Inlining]++;
+ }
+ } else
+ c = *Inliner[Inlining]++;
+
+ if (c == '\0')
+ { lineno = Inline_stub[Inlining]->cln;
+ Fname = Inline_stub[Inlining]->cfn;
+ Inlining--;
+#if 0
+ if (verbose&32)
+ printf("spin: line %d, done inlining %s\n",
+ lineno, Inline_stub[Inlining+1]->nm->name);
+#endif
+ return Getchar();
+ }
+ return c;
+}
+
+static void
+uninline(void)
+{
+ if (ReDiRect)
+ ReDiRect--;
+ else
+ Inliner[Inlining]--;
+}
+
+IType *
+find_inline(char *s)
+{ IType *tmp;
+
+ for (tmp = seqnames; tmp; tmp = tmp->nxt)
+ if (!strcmp(s, tmp->nm->name))
+ break;
+ if (!tmp)
+ fatal("cannot happen, missing inline def %s", s);
+
+ return tmp;
+}
+
+void
+c_state(Symbol *s, Symbol *t, Symbol *ival) /* name, scope, ival */
+{ C_Added *r;
+
+ r = (C_Added *) emalloc(sizeof(C_Added));
+ r->s = s; /* pointer to a data object */
+ r->t = t; /* size of object, or "global", or "local proctype_name" */
+ r->ival = ival;
+ r->nxt = c_added;
+ c_added = r;
+}
+
+void
+c_track(Symbol *s, Symbol *t, Symbol *stackonly) /* name, size */
+{ C_Added *r;
+
+ r = (C_Added *) emalloc(sizeof(C_Added));
+ r->s = s;
+ r->t = t;
+ r->ival = stackonly; /* abuse of name */
+ r->nxt = c_tracked;
+ c_tracked = r;
+
+ if (stackonly != ZS)
+ { if (strcmp(stackonly->name, "\"Matched\"") == 0)
+ r->ival = ZS; /* the default */
+ else if (strcmp(stackonly->name, "\"UnMatched\"") != 0
+ && strcmp(stackonly->name, "\"unMatched\"") != 0
+ && strcmp(stackonly->name, "\"StackOnly\"") != 0)
+ non_fatal("expecting '[Un]Matched', saw %s", stackonly->name);
+ else
+ has_stack = 1; /* unmatched stack */
+ }
+}
+
+char *
+jump_etc(char *op)
+{ char *p = op;
+
+ /* kludgy - try to get the type separated from the name */
+
+ while (*p == ' ' || *p == '\t')
+ p++; /* initial white space */
+ while (*p != ' ' && *p != '\t')
+ p++; /* type name */
+ while (*p == ' ' || *p == '\t')
+ p++; /* white space */
+ while (*p == '*')
+ p++; /* decorations */
+ while (*p == ' ' || *p == '\t')
+ p++; /* white space */
+
+ if (*p == '\0')
+ fatal("c_state format (%s)", op);
+
+ if (strchr(p, '[')
+ && !strchr(p, '{'))
+ { non_fatal("array initialization error, c_state (%s)", p);
+ return (char *) 0;
+ }
+
+ return p;
+}
+
+void
+c_add_globinit(FILE *fd)
+{ C_Added *r;
+ char *p, *q;
+
+ fprintf(fd, "void\nglobinit(void)\n{\n");
+ for (r = c_added; r; r = r->nxt)
+ { if (r->ival == ZS)
+ continue;
+
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0)
+ { for (q = r->ival->name; *q; q++)
+ { if (*q == '\"')
+ *q = ' ';
+ if (*q == '\\')
+ *q++ = ' '; /* skip over the next */
+ }
+ p = jump_etc(r->s->name); /* e.g., "int **q" */
+ if (p)
+ fprintf(fd, " now.%s = %s;\n", p, r->ival->name);
+
+ } else
+ if (strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0)
+ { for (q = r->ival->name; *q; q++)
+ { if (*q == '\"')
+ *q = ' ';
+ if (*q == '\\')
+ *q++ = ' '; /* skip over the next */
+ }
+ p = jump_etc(r->s->name); /* e.g., "int **q" */
+ if (p)
+ fprintf(fd, " %s = %s;\n", p, r->ival->name); /* no now. prefix */
+
+ } }
+ fprintf(fd, "}\n");
+}
+
+void
+c_add_locinit(FILE *fd, int tpnr, char *pnm)
+{ C_Added *r;
+ char *p, *q, *s;
+ int frst = 1;
+
+ fprintf(fd, "void\nlocinit%d(int h)\n{\n", tpnr);
+ for (r = c_added; r; r = r->nxt)
+ if (r->ival != ZS
+ && strncmp(r->t->name, " Local", strlen(" Local")) == 0)
+ { for (q = r->ival->name; *q; q++)
+ if (*q == '\"')
+ *q = ' ';
+
+ p = jump_etc(r->s->name); /* e.g., "int **q" */
+
+ q = r->t->name + strlen(" Local");
+ while (*q == ' ' || *q == '\t')
+ q++; /* process name */
+
+ s = (char *) emalloc(strlen(q)+1);
+ strcpy(s, q);
+
+ q = &s[strlen(s)-1];
+ while (*q == ' ' || *q == '\t')
+ *q-- = '\0';
+
+ if (strcmp(pnm, s) != 0)
+ continue;
+
+ if (frst)
+ { fprintf(fd, "\tuchar *this = pptr(h);\n");
+ frst = 0;
+ }
+
+ if (p)
+ fprintf(fd, " ((P%d *)this)->%s = %s;\n",
+ tpnr, p, r->ival->name);
+
+ }
+ fprintf(fd, "}\n");
+}
+
+/* tracking:
+ 1. for non-global and non-local c_state decls: add up all the sizes in c_added
+ 2. add a global char array of that size into now
+ 3. generate a routine that memcpy's the required values into that array
+ 4. generate a call to that routine
+ */
+
+void
+c_preview(void)
+{ C_Added *r;
+
+ hastrack = 0;
+ if (c_tracked)
+ hastrack = 1;
+ else
+ for (r = c_added; r; r = r->nxt)
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0
+ && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0
+ && strncmp(r->t->name, " Local", strlen(" Local")) != 0)
+ { hastrack = 1; /* c_state variant now obsolete */
+ break;
+ }
+}
+
+int
+c_add_sv(FILE *fd) /* 1+2 -- called in pangen1.c */
+{ C_Added *r;
+ int cnt = 0;
+
+ if (!c_added && !c_tracked) return 0;
+
+ for (r = c_added; r; r = r->nxt) /* pickup global decls */
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0)
+ fprintf(fd, " %s;\n", r->s->name);
+
+ for (r = c_added; r; r = r->nxt)
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0
+ && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0
+ && strncmp(r->t->name, " Local", strlen(" Local")) != 0)
+ { cnt++; /* obsolete use */
+ }
+
+ for (r = c_tracked; r; r = r->nxt)
+ cnt++; /* preferred use */
+
+ if (cnt == 0) return 0;
+
+ cnt = 0;
+ fprintf(fd, " uchar c_state[");
+ for (r = c_added; r; r = r->nxt)
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0
+ && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0
+ && strncmp(r->t->name, " Local", strlen(" Local")) != 0)
+ { fprintf(fd, "%ssizeof(%s)",
+ (cnt==0)?"":"+", r->t->name);
+ cnt++;
+ }
+
+ for (r = c_tracked; r; r = r->nxt)
+ { if (r->ival != ZS) continue;
+
+ fprintf(fd, "%s%s",
+ (cnt==0)?"":"+", r->t->name);
+ cnt++;
+ }
+
+ if (cnt == 0) fprintf(fd, "4"); /* now redundant */
+ fprintf(fd, "];\n");
+ return 1;
+}
+
+void
+c_stack_size(FILE *fd)
+{ C_Added *r;
+ int cnt = 0;
+
+ for (r = c_tracked; r; r = r->nxt)
+ if (r->ival != ZS)
+ { fprintf(fd, "%s%s",
+ (cnt==0)?"":"+", r->t->name);
+ cnt++;
+ }
+ if (cnt == 0)
+ { fprintf(fd, "WS");
+ }
+}
+
+void
+c_add_stack(FILE *fd)
+{ C_Added *r;
+ int cnt = 0;
+
+ if ((!c_added && !c_tracked) || !has_stack)
+ { return;
+ }
+
+ for (r = c_tracked; r; r = r->nxt)
+ if (r->ival != ZS)
+ { cnt++;
+ }
+
+ if (cnt > 0)
+ { fprintf(fd, " uchar c_stack[StackSize];\n");
+ }
+}
+
+void
+c_add_hidden(FILE *fd)
+{ C_Added *r;
+
+ for (r = c_added; r; r = r->nxt) /* pickup hidden decls */
+ if (strncmp(r->t->name, "\"Hidden\"", strlen("\"Hidden\"")) == 0)
+ { r->s->name[strlen(r->s->name)-1] = ' ';
+ fprintf(fd, "%s; /* Hidden */\n", &r->s->name[1]);
+ r->s->name[strlen(r->s->name)-1] = '"';
+ }
+ /* called before c_add_def - quotes are still there */
+}
+
+void
+c_add_loc(FILE *fd, char *s) /* state vector entries for proctype s */
+{ C_Added *r;
+ static char buf[1024];
+ char *p;
+
+ if (!c_added) return;
+
+ strcpy(buf, s);
+ strcat(buf, " ");
+ for (r = c_added; r; r = r->nxt) /* pickup local decls */
+ if (strncmp(r->t->name, " Local", strlen(" Local")) == 0)
+ { p = r->t->name + strlen(" Local");
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (strcmp(p, buf) == 0)
+ fprintf(fd, " %s;\n", r->s->name);
+ }
+}
+void
+c_add_def(FILE *fd) /* 3 - called in plunk_c_fcts() */
+{ C_Added *r;
+
+ fprintf(fd, "#if defined(C_States) && defined(HAS_TRACK)\n");
+ for (r = c_added; r; r = r->nxt)
+ { r->s->name[strlen(r->s->name)-1] = ' ';
+ r->s->name[0] = ' '; /* remove the "s */
+
+ r->t->name[strlen(r->t->name)-1] = ' ';
+ r->t->name[0] = ' ';
+
+ if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0
+ || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0
+ || strncmp(r->t->name, " Local", strlen(" Local")) == 0)
+ continue;
+
+ if (strchr(r->s->name, '&'))
+ fatal("dereferencing state object: %s", r->s->name);
+
+ fprintf(fd, "extern %s %s;\n", r->t->name, r->s->name);
+ }
+ for (r = c_tracked; r; r = r->nxt)
+ { r->s->name[strlen(r->s->name)-1] = ' ';
+ r->s->name[0] = ' '; /* remove " */
+
+ r->t->name[strlen(r->t->name)-1] = ' ';
+ r->t->name[0] = ' ';
+ }
+
+ if (separate == 2)
+ { fprintf(fd, "#endif\n");
+ return;
+ }
+
+ if (has_stack)
+ { fprintf(fd, "int cpu_printf(const char *, ...);\n");
+ fprintf(fd, "void\nc_stack(uchar *p_t_r)\n{\n");
+ fprintf(fd, "#ifdef VERBOSE\n");
+ fprintf(fd, " cpu_printf(\"c_stack %%u\\n\", p_t_r);\n");
+ fprintf(fd, "#endif\n");
+ for (r = c_tracked; r; r = r->nxt)
+ { if (r->ival == ZS) continue;
+
+ fprintf(fd, "\tif(%s)\n", r->s->name);
+ fprintf(fd, "\t\tmemcpy(p_t_r, %s, %s);\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\telse\n");
+ fprintf(fd, "\t\tmemset(p_t_r, 0, %s);\n",
+ r->t->name);
+ fprintf(fd, "\tp_t_r += %s;\n", r->t->name);
+ }
+ fprintf(fd, "}\n\n");
+ }
+
+ fprintf(fd, "void\nc_update(uchar *p_t_r)\n{\n");
+ fprintf(fd, "#ifdef VERBOSE\n");
+ fprintf(fd, " printf(\"c_update %%u\\n\", p_t_r);\n");
+ fprintf(fd, "#endif\n");
+ for (r = c_added; r; r = r->nxt)
+ { if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0
+ || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0
+ || strncmp(r->t->name, " Local", strlen(" Local")) == 0)
+ continue;
+
+ fprintf(fd, "\tmemcpy(p_t_r, &%s, sizeof(%s));\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\tp_t_r += sizeof(%s);\n", r->t->name);
+ }
+
+ for (r = c_tracked; r; r = r->nxt)
+ { if (r->ival) continue;
+
+ fprintf(fd, "\tif(%s)\n", r->s->name);
+ fprintf(fd, "\t\tmemcpy(p_t_r, %s, %s);\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\telse\n");
+ fprintf(fd, "\t\tmemset(p_t_r, 0, %s);\n",
+ r->t->name);
+ fprintf(fd, "\tp_t_r += %s;\n", r->t->name);
+ }
+
+ fprintf(fd, "}\n");
+
+ if (has_stack)
+ { fprintf(fd, "void\nc_unstack(uchar *p_t_r)\n{\n");
+ fprintf(fd, "#ifdef VERBOSE\n");
+ fprintf(fd, " cpu_printf(\"c_unstack %%u\\n\", p_t_r);\n");
+ fprintf(fd, "#endif\n");
+ for (r = c_tracked; r; r = r->nxt)
+ { if (r->ival == ZS) continue;
+
+ fprintf(fd, "\tif(%s)\n", r->s->name);
+ fprintf(fd, "\t\tmemcpy(%s, p_t_r, %s);\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\tp_t_r += %s;\n", r->t->name);
+ }
+ fprintf(fd, "}\n");
+ }
+
+ fprintf(fd, "void\nc_revert(uchar *p_t_r)\n{\n");
+ fprintf(fd, "#ifdef VERBOSE\n");
+ fprintf(fd, " printf(\"c_revert %%u\\n\", p_t_r);\n");
+ fprintf(fd, "#endif\n");
+ for (r = c_added; r; r = r->nxt)
+ { if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0
+ || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0
+ || strncmp(r->t->name, " Local", strlen(" Local")) == 0)
+ continue;
+
+ fprintf(fd, "\tmemcpy(&%s, p_t_r, sizeof(%s));\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\tp_t_r += sizeof(%s);\n", r->t->name);
+ }
+ for (r = c_tracked; r; r = r->nxt)
+ { if (r->ival != ZS) continue;
+
+ fprintf(fd, "\tif(%s)\n", r->s->name);
+ fprintf(fd, "\t\tmemcpy(%s, p_t_r, %s);\n",
+ r->s->name, r->t->name);
+ fprintf(fd, "\tp_t_r += %s;\n", r->t->name);
+ }
+
+ fprintf(fd, "}\n");
+ fprintf(fd, "#endif\n");
+}
+
+void
+plunk_reverse(FILE *fd, IType *p, int matchthis)
+{ char *y, *z;
+
+ if (!p) return;
+ plunk_reverse(fd, p->nxt, matchthis);
+
+ if (!p->nm->context
+ && p->nm->type == matchthis)
+ { fprintf(fd, "\n/* start of %s */\n", p->nm->name);
+ z = (char *) p->cn;
+ while (*z == '\n' || *z == '\r' || *z == '\\')
+ z++;
+ /* e.g.: \#include "..." */
+
+ y = z;
+ while ((y = strstr(y, "\\#")) != NULL)
+ { *y = '\n'; y++;
+ }
+
+ fprintf(fd, "%s\n", z);
+ fprintf(fd, "\n/* end of %s */\n", p->nm->name);
+ }
+}
+
+void
+plunk_c_decls(FILE *fd)
+{
+ plunk_reverse(fd, seqnames, CODE_DECL);
+}
+
+void
+plunk_c_fcts(FILE *fd)
+{
+ if (separate == 2 && hastrack)
+ { c_add_def(fd);
+ return;
+ }
+
+ c_add_hidden(fd);
+ plunk_reverse(fd, seqnames, CODE_FRAG);
+
+ if (c_added || c_tracked) /* enables calls to c_revert and c_update */
+ fprintf(fd, "#define C_States 1\n");
+ else
+ fprintf(fd, "#undef C_States\n");
+
+ if (hastrack)
+ c_add_def(fd);
+
+ c_add_globinit(fd);
+ do_locinits(fd);
+}
+
+static void
+check_inline(IType *tmp)
+{ char buf[128];
+ ProcList *p;
+
+ if (!X) return;
+
+ for (p = rdy; p; p = p->nxt)
+ { if (strcmp(p->n->name, X->n->name) == 0)
+ continue;
+ sprintf(buf, "P%s->", p->n->name);
+ if (strstr((char *)tmp->cn, buf))
+ { printf("spin: in proctype %s, ref to object in proctype %s\n",
+ X->n->name, p->n->name);
+ fatal("invalid variable ref in '%s'", tmp->nm->name);
+ } }
+}
+
+void
+plunk_expr(FILE *fd, char *s)
+{ IType *tmp;
+
+ tmp = find_inline(s);
+ check_inline(tmp);
+
+ fprintf(fd, "%s", (char *) tmp->cn);
+}
+
+void
+preruse(FILE *fd, Lextok *n) /* check a condition for c_expr with preconditions */
+{ IType *tmp;
+
+ if (!n) return;
+ if (n->ntyp == C_EXPR)
+ { tmp = find_inline(n->sym->name);
+ if (tmp->prec)
+ { fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec);
+ fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;");
+ fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec);
+ fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec);
+ fprintf(fd, "_m = 3; goto P999; } } \n\t\t");
+ }
+ } else
+ { preruse(fd, n->rgt);
+ preruse(fd, n->lft);
+ }
+}
+
+int
+glob_inline(char *s)
+{ IType *tmp;
+ char *bdy;
+
+ tmp = find_inline(s);
+ bdy = (char *) tmp->cn;
+ return (strstr(bdy, "now.") /* global ref or */
+ || strchr(bdy, '(') > bdy); /* possible C-function call */
+}
+
+void
+plunk_inline(FILE *fd, char *s, int how) /* c_code with precondition */
+{ IType *tmp;
+
+ tmp = find_inline(s);
+ check_inline(tmp);
+
+ fprintf(fd, "{ ");
+ if (how && tmp->prec)
+ { fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec);
+ fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;");
+ fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec);
+ fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec);
+ fprintf(fd, "_m = 3; goto P999; } } ");
+ }
+ fprintf(fd, "%s", (char *) tmp->cn);
+ fprintf(fd, " }\n");
+}
+
+void
+no_side_effects(char *s)
+{ IType *tmp;
+ char *t;
+
+ /* could still defeat this check via hidden
+ * side effects in function calls,
+ * but this will catch at least some cases
+ */
+
+ tmp = find_inline(s);
+ t = (char *) tmp->cn;
+
+ if (strchr(t, ';')
+ || strstr(t, "++")
+ || strstr(t, "--"))
+ {
+bad: lineno = tmp->dln;
+ Fname = tmp->dfn;
+ non_fatal("c_expr %s has side-effects", s);
+ return;
+ }
+ while ((t = strchr(t, '=')) != NULL)
+ { if (*(t-1) == '!'
+ || *(t-1) == '>'
+ || *(t-1) == '<')
+ { t++;
+ continue;
+ }
+ t++;
+ if (*t != '=')
+ goto bad;
+ t++;
+ }
+}
+
+void
+pickup_inline(Symbol *t, Lextok *apars)
+{ IType *tmp; Lextok *p, *q; int j;
+
+ tmp = find_inline(t->name);
+
+ if (++Inlining >= MAXINL)
+ fatal("inline fcts too deeply nested", 0);
+ tmp->cln = lineno; /* remember calling point */
+ tmp->cfn = Fname; /* and filename */
+
+ for (p = apars, q = tmp->params, j = 0; p && q; p = p->rgt, q = q->rgt)
+ j++; /* count them */
+ if (p || q)
+ fatal("wrong nr of params on call of '%s'", t->name);
+
+ tmp->anms = (char **) emalloc(j * sizeof(char *));
+ for (p = apars, j = 0; p; p = p->rgt, j++)
+ { tmp->anms[j] = (char *) emalloc(strlen(IArg_cont[j])+1);
+ strcpy(tmp->anms[j], IArg_cont[j]);
+ }
+
+ lineno = tmp->dln; /* linenr of def */
+ Fname = tmp->dfn; /* filename of same */
+ Inliner[Inlining] = (char *)tmp->cn;
+ Inline_stub[Inlining] = tmp;
+#if 0
+ if (verbose&32)
+ printf("spin: line %d, file %s, inlining '%s' (from line %d, file %s)\n",
+ tmp->cln, tmp->cfn->name, t->name, tmp->dln, tmp->dfn->name);
+#endif
+ for (j = 0; j < Inlining; j++)
+ if (Inline_stub[j] == Inline_stub[Inlining])
+ fatal("cyclic inline attempt on: %s", t->name);
+}
+
+static void
+do_directive(int first)
+{ int c = first; /* handles lines starting with pound */
+
+ getword(c, isalpha_);
+
+ if (strcmp(yytext, "#ident") == 0)
+ goto done;
+
+ if ((c = Getchar()) != ' ')
+ fatal("malformed preprocessor directive - # .", 0);
+
+ if (!isdigit_(c = Getchar()))
+ fatal("malformed preprocessor directive - # .lineno", 0);
+
+ getword(c, isdigit_);
+ lineno = atoi(yytext); /* pickup the line number */
+
+ if ((c = Getchar()) == '\n')
+ return; /* no filename */
+
+ if (c != ' ')
+ fatal("malformed preprocessor directive - .fname", 0);
+
+ if ((c = Getchar()) != '\"')
+ fatal("malformed preprocessor directive - .fname", 0);
+
+ getword(c, notquote);
+ if (Getchar() != '\"')
+ fatal("malformed preprocessor directive - fname.", 0);
+
+ strcat(yytext, "\"");
+ Fname = lookup(yytext);
+done:
+ while (Getchar() != '\n')
+ ;
+}
+
+void
+precondition(char *q)
+{ int c, nest = 1;
+
+ for (;;)
+ { c = Getchar();
+ *q++ = c;
+ switch (c) {
+ case '\n':
+ lineno++;
+ break;
+ case '[':
+ nest++;
+ break;
+ case ']':
+ if (--nest <= 0)
+ { *--q = '\0';
+ return;
+ }
+ break;
+ }
+ }
+ fatal("cannot happen", (char *) 0); /* unreachable */
+}
+
+
+Symbol *
+prep_inline(Symbol *s, Lextok *nms)
+{ int c, nest = 1, dln, firstchar, cnr;
+ char *p;
+ Lextok *t;
+ static char Buf1[SOMETHINGBIG], Buf2[RATHERSMALL];
+ static int c_code = 1;
+
+ for (t = nms; t; t = t->rgt)
+ if (t->lft)
+ { if (t->lft->ntyp != NAME)
+ fatal("bad param to inline %s", s?s->name:"--");
+ t->lft->sym->hidden |= 32;
+ }
+
+ if (!s) /* C_Code fragment */
+ { s = (Symbol *) emalloc(sizeof(Symbol));
+ s->name = (char *) emalloc(strlen("c_code")+26);
+ sprintf(s->name, "c_code%d", c_code++);
+ s->context = context;
+ s->type = CODE_FRAG;
+ } else
+ s->type = PREDEF;
+
+ p = &Buf1[0];
+ Buf2[0] = '\0';
+ for (;;)
+ { c = Getchar();
+ switch (c) {
+ case '[':
+ if (s->type != CODE_FRAG)
+ goto bad;
+ precondition(&Buf2[0]); /* e.g., c_code [p] { r = p-r; } */
+ continue;
+ case '{':
+ break;
+ case '\n':
+ lineno++;
+ /* fall through */
+ case ' ': case '\t': case '\f': case '\r':
+ continue;
+ default :
+ printf("spin: saw char '%c'\n", c);
+bad: fatal("bad inline: %s", s->name);
+ }
+ break;
+ }
+ dln = lineno;
+ if (s->type == CODE_FRAG)
+ { if (verbose&32)
+ sprintf(Buf1, "\t/* line %d %s */\n\t\t",
+ lineno, Fname->name);
+ else
+ strcpy(Buf1, "");
+ } else
+ sprintf(Buf1, "\n#line %d %s\n{", lineno, Fname->name);
+ p += strlen(Buf1);
+ firstchar = 1;
+
+ cnr = 1; /* not zero */
+more:
+ *p++ = c = Getchar();
+ if (p - Buf1 >= SOMETHINGBIG)
+ fatal("inline text too long", 0);
+ switch (c) {
+ case '\n':
+ lineno++;
+ cnr = 0;
+ break;
+ case '{':
+ cnr++;
+ nest++;
+ break;
+ case '}':
+ cnr++;
+ if (--nest <= 0)
+ { *p = '\0';
+ if (s->type == CODE_FRAG)
+ *--p = '\0'; /* remove trailing '}' */
+ def_inline(s, dln, &Buf1[0], &Buf2[0], nms);
+ if (firstchar)
+ printf("%3d: %s, warning: empty inline definition (%s)\n",
+ dln, Fname->name, s->name);
+ return s; /* normal return */
+ }
+ break;
+ case '#':
+ if (cnr == 0)
+ { p--;
+ do_directive(c); /* reads to newline */
+ break;
+ } /* else fall through */
+ default:
+ firstchar = 0;
+ case '\t':
+ case ' ':
+ case '\f':
+ cnr++;
+ break;
+ }
+ goto more;
+}
+
+static int
+lex(void)
+{ int c;
+
+again:
+ c = Getchar();
+ yytext[0] = (char) c;
+ yytext[1] = '\0';
+ switch (c) {
+ case '\n': /* newline */
+ lineno++;
+ case '\r': /* carriage return */
+ goto again;
+
+ case ' ': case '\t': case '\f': /* white space */
+ goto again;
+
+ case '#': /* preprocessor directive */
+ if (in_comment) goto again;
+ do_directive(c);
+ goto again;
+
+ case '\"':
+ getword(c, notquote);
+ if (Getchar() != '\"')
+ fatal("string not terminated", yytext);
+ strcat(yytext, "\"");
+ SymToken(lookup(yytext), STRING)
+
+ case '\'': /* new 3.0.9 */
+ c = Getchar();
+ if (c == '\\')
+ { c = Getchar();
+ if (c == 'n') c = '\n';
+ else if (c == 'r') c = '\r';
+ else if (c == 't') c = '\t';
+ else if (c == 'f') c = '\f';
+ }
+ if (Getchar() != '\'' && !in_comment)
+ fatal("character quote missing: %s", yytext);
+ ValToken(c, CONST)
+
+ default:
+ break;
+ }
+
+ if (isdigit_(c))
+ { getword(c, isdigit_);
+ ValToken(atoi(yytext), CONST)
+ }
+
+ if (isalpha_(c) || c == '_')
+ { getword(c, isalnum_);
+ if (!in_comment)
+ { c = check_name(yytext);
+ if (c) return c;
+ /* else fall through */
+ }
+ goto again;
+ }
+
+ switch (c) {
+ case '/': c = follow('*', 0, '/');
+ if (!c) { in_comment = 1; goto again; }
+ break;
+ case '*': c = follow('/', 0, '*');
+ if (!c) { in_comment = 0; goto again; }
+ break;
+ case ':': c = follow(':', SEP, ':'); break;
+ case '-': c = follow('>', SEMI, follow('-', DECR, '-')); break;
+ case '+': c = follow('+', INCR, '+'); break;
+ case '<': c = follow('<', LSHIFT, follow('=', LE, LT)); break;
+ case '>': c = follow('>', RSHIFT, follow('=', GE, GT)); break;
+ case '=': c = follow('=', EQ, ASGN); break;
+ case '!': c = follow('=', NE, follow('!', O_SND, SND)); break;
+ case '?': c = follow('?', R_RCV, RCV); break;
+ case '&': c = follow('&', AND, '&'); break;
+ case '|': c = follow('|', OR, '|'); break;
+ case ';': c = SEMI; break;
+ default : break;
+ }
+ Token(c)
+}
+
+static struct {
+ char *s; int tok; int val; char *sym;
+} Names[] = {
+ {"active", ACTIVE, 0, 0},
+ {"assert", ASSERT, 0, 0},
+ {"atomic", ATOMIC, 0, 0},
+ {"bit", TYPE, BIT, 0},
+ {"bool", TYPE, BIT, 0},
+ {"break", BREAK, 0, 0},
+ {"byte", TYPE, BYTE, 0},
+ {"c_code", C_CODE, 0, 0},
+ {"c_decl", C_DECL, 0, 0},
+ {"c_expr", C_EXPR, 0, 0},
+ {"c_state", C_STATE, 0, 0},
+ {"c_track", C_TRACK, 0, 0},
+ {"D_proctype", D_PROCTYPE, 0, 0},
+ {"do", DO, 0, 0},
+ {"chan", TYPE, CHAN, 0},
+ {"else", ELSE, 0, 0},
+ {"empty", EMPTY, 0, 0},
+ {"enabled", ENABLED, 0, 0},
+ {"eval", EVAL, 0, 0},
+ {"false", CONST, 0, 0},
+ {"fi", FI, 0, 0},
+ {"full", FULL, 0, 0},
+ {"goto", GOTO, 0, 0},
+ {"hidden", HIDDEN, 0, ":hide:"},
+ {"if", IF, 0, 0},
+ {"init", INIT, 0, ":init:"},
+ {"int", TYPE, INT, 0},
+ {"len", LEN, 0, 0},
+ {"local", ISLOCAL, 0, ":local:"},
+ {"mtype", TYPE, MTYPE, 0},
+ {"nempty", NEMPTY, 0, 0},
+ {"never", CLAIM, 0, ":never:"},
+ {"nfull", NFULL, 0, 0},
+ {"notrace", TRACE, 0, ":notrace:"},
+ {"np_", NONPROGRESS, 0, 0},
+ {"od", OD, 0, 0},
+ {"of", OF, 0, 0},
+ {"pc_value", PC_VAL, 0, 0},
+ {"pid", TYPE, BYTE, 0},
+ {"printf", PRINT, 0, 0},
+ {"printm", PRINTM, 0, 0},
+ {"priority", PRIORITY, 0, 0},
+ {"proctype", PROCTYPE, 0, 0},
+ {"provided", PROVIDED, 0, 0},
+ {"run", RUN, 0, 0},
+ {"d_step", D_STEP, 0, 0},
+ {"inline", INLINE, 0, 0},
+ {"short", TYPE, SHORT, 0},
+ {"skip", CONST, 1, 0},
+ {"timeout", TIMEOUT, 0, 0},
+ {"trace", TRACE, 0, ":trace:"},
+ {"true", CONST, 1, 0},
+ {"show", SHOW, 0, ":show:"},
+ {"typedef", TYPEDEF, 0, 0},
+ {"unless", UNLESS, 0, 0},
+ {"unsigned", TYPE, UNSIGNED, 0},
+ {"xr", XU, XR, 0},
+ {"xs", XU, XS, 0},
+ {0, 0, 0, 0},
+};
+
+static int
+check_name(char *s)
+{ int i;
+
+ yylval = nn(ZN, 0, ZN, ZN);
+ for (i = 0; Names[i].s; i++)
+ if (strcmp(s, Names[i].s) == 0)
+ { yylval->val = Names[i].val;
+ if (Names[i].sym)
+ yylval->sym = lookup(Names[i].sym);
+ return Names[i].tok;
+ }
+
+ if ((yylval->val = ismtype(s)) != 0)
+ { yylval->ismtyp = 1;
+ return CONST;
+ }
+
+ if (strcmp(s, "_last") == 0)
+ has_last++;
+
+ if (Inlining >= 0 && !ReDiRect)
+ { Lextok *tt, *t = Inline_stub[Inlining]->params;
+
+ for (i = 0; t; t = t->rgt, i++) /* formal pars */
+ if (!strcmp(s, t->lft->sym->name) /* varname matches formal */
+ && strcmp(s, Inline_stub[Inlining]->anms[i]) != 0) /* actual pars */
+ {
+#if 0
+ if (verbose&32)
+ printf("\tline %d, replace %s in call of '%s' with %s\n",
+ lineno, s,
+ Inline_stub[Inlining]->nm->name,
+ Inline_stub[Inlining]->anms[i]);
+#endif
+ for (tt = Inline_stub[Inlining]->params; tt; tt = tt->rgt)
+ if (!strcmp(Inline_stub[Inlining]->anms[i],
+ tt->lft->sym->name))
+ { /* would be cyclic if not caught */
+ printf("spin: line %d replacement value: %s\n",
+ lineno, tt->lft->sym->name);
+wrong: fatal("formal par of %s contains replacement value",
+ Inline_stub[Inlining]->nm->name);
+ yylval->ntyp = tt->lft->ntyp;
+ yylval->sym = lookup(tt->lft->sym->name);
+ return NAME;
+ }
+
+ /* check for occurrence of param as field of struct */
+ { char *ptr = Inline_stub[Inlining]->anms[i];
+ while ((ptr = strstr(ptr, s)) != NULL)
+ { if (*(ptr-1) == '.'
+ || *(ptr+strlen(s)) == '.')
+ { goto wrong;
+ }
+ ptr++;
+ } }
+ ReDiRect = Inline_stub[Inlining]->anms[i];
+ return 0;
+ } }
+
+ yylval->sym = lookup(s); /* symbol table */
+ if (isutype(s))
+ return UNAME;
+ if (isproctype(s))
+ return PNAME;
+ if (iseqname(s))
+ return INAME;
+
+ return NAME;
+}
+
+int
+yylex(void)
+{ static int last = 0;
+ static int hold = 0;
+ int c;
+ /*
+ * repair two common syntax mistakes with
+ * semi-colons before or after a '}'
+ */
+ if (hold)
+ { c = hold;
+ hold = 0;
+ } else
+ { c = lex();
+ if (last == ELSE
+ && c != SEMI
+ && c != FI)
+ { hold = c;
+ last = 0;
+ return SEMI;
+ }
+ if (last == '}'
+ && c != PROCTYPE
+ && c != INIT
+ && c != CLAIM
+ && c != SEP
+ && c != FI
+ && c != OD
+ && c != '}'
+ && c != UNLESS
+ && c != SEMI
+ && c != EOF)
+ { hold = c;
+ last = 0;
+ return SEMI; /* insert ';' */
+ }
+ if (c == SEMI)
+ { /* if context, we're not in a typedef
+ * because they're global.
+ * if owner, we're at the end of a ref
+ * to a struct field -- prevent that the
+ * lookahead is interpreted as a field of
+ * the same struct...
+ */
+ if (context) owner = ZS;
+ hold = lex(); /* look ahead */
+ if (hold == '}'
+ || hold == SEMI)
+ { c = hold; /* omit ';' */
+ hold = 0;
+ }
+ }
+ }
+ last = c;
+
+ if (IArgs)
+ { static int IArg_nst = 0;
+
+ if (strcmp(yytext, ",") == 0)
+ { IArg_cont[++IArgno][0] = '\0';
+ } else if (strcmp(yytext, "(") == 0)
+ { if (IArg_nst++ == 0)
+ { IArgno = 0;
+ IArg_cont[0][0] = '\0';
+ } else
+ strcat(IArg_cont[IArgno], yytext);
+ } else if (strcmp(yytext, ")") == 0)
+ { if (--IArg_nst > 0)
+ strcat(IArg_cont[IArgno], yytext);
+ } else if (c == CONST && yytext[0] == '\'')
+ { sprintf(yytext, "'%c'", yylval->val);
+ strcat(IArg_cont[IArgno], yytext);
+ } else if (c == CONST)
+ { sprintf(yytext, "%d", yylval->val);
+ strcat(IArg_cont[IArgno], yytext);
+ } else
+ {
+ switch (c) {
+ case SEP: strcpy(yytext, "::"); break;
+ case SEMI: strcpy(yytext, ";"); break;
+ case DECR: strcpy(yytext, "--"); break;
+ case INCR: strcpy(yytext, "++"); break;
+ case LSHIFT: strcpy(yytext, "<<"); break;
+ case RSHIFT: strcpy(yytext, ">>"); break;
+ case LE: strcpy(yytext, "<="); break;
+ case LT: strcpy(yytext, "<"); break;
+ case GE: strcpy(yytext, ">="); break;
+ case GT: strcpy(yytext, ">"); break;
+ case EQ: strcpy(yytext, "=="); break;
+ case ASGN: strcpy(yytext, "="); break;
+ case NE: strcpy(yytext, "!="); break;
+ case R_RCV: strcpy(yytext, "??"); break;
+ case RCV: strcpy(yytext, "?"); break;
+ case O_SND: strcpy(yytext, "!!"); break;
+ case SND: strcpy(yytext, "!"); break;
+ case AND: strcpy(yytext, "&&"); break;
+ case OR: strcpy(yytext, "||"); break;
+ }
+ strcat(IArg_cont[IArgno], yytext);
+ }
+ }
+ return c;
+}
--- /dev/null
+/***** spin: structs.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+typedef struct UType {
+ Symbol *nm; /* name of the type */
+ Lextok *cn; /* contents */
+ struct UType *nxt; /* linked list */
+} UType;
+
+extern Symbol *Fname;
+extern int lineno, depth, Expand_Ok, has_hidden;
+
+Symbol *owner;
+
+static UType *Unames = 0;
+static UType *Pnames = 0;
+
+static Lextok *cpnn(Lextok *, int, int, int);
+extern void sr_mesg(FILE *, int, int);
+
+void
+setuname(Lextok *n)
+{ UType *tmp;
+
+ if (!owner)
+ fatal("illegal reference inside typedef", (char *) 0);
+
+ for (tmp = Unames; tmp; tmp = tmp->nxt)
+ if (!strcmp(owner->name, tmp->nm->name))
+ { non_fatal("typename %s was defined before",
+ tmp->nm->name);
+ return;
+ }
+
+ tmp = (UType *) emalloc(sizeof(UType));
+ tmp->nm = owner;
+ tmp->cn = n;
+ tmp->nxt = Unames;
+ Unames = tmp;
+}
+
+static void
+putUname(FILE *fd, UType *tmp)
+{ Lextok *fp, *tl;
+
+ if (!tmp) return;
+ putUname(fd, tmp->nxt); /* postorder */
+ fprintf(fd, "struct %s { /* user defined type */\n",
+ tmp->nm->name);
+ for (fp = tmp->cn; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ typ2c(tl->sym);
+ fprintf(fd, "};\n");
+}
+
+void
+putunames(FILE *fd)
+{
+ putUname(fd, Unames);
+}
+
+int
+isutype(char *t)
+{ UType *tmp;
+
+ for (tmp = Unames; tmp; tmp = tmp->nxt)
+ { if (!strcmp(t, tmp->nm->name))
+ return 1;
+ }
+ return 0;
+}
+
+Lextok *
+getuname(Symbol *t)
+{ UType *tmp;
+
+ for (tmp = Unames; tmp; tmp = tmp->nxt)
+ { if (!strcmp(t->name, tmp->nm->name))
+ return tmp->cn;
+ }
+ fatal("%s is not a typename", t->name);
+ return (Lextok *)0;
+}
+
+void
+setutype(Lextok *p, Symbol *t, Lextok *vis) /* user-defined types */
+{ int oln = lineno;
+ Symbol *ofn = Fname;
+ Lextok *m, *n;
+
+ m = getuname(t);
+ for (n = p; n; n = n->rgt)
+ { lineno = n->ln;
+ Fname = n->fn;
+ if (n->sym->type)
+ non_fatal("redeclaration of '%s'", n->sym->name);
+
+ if (n->sym->nbits > 0)
+ non_fatal("(%s) only an unsigned can have width-field",
+ n->sym->name);
+
+ if (Expand_Ok)
+ n->sym->hidden |= (4|8|16); /* formal par */
+
+ if (vis)
+ { if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0)
+ { n->sym->hidden |= 1;
+ has_hidden++;
+ } else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0)
+ n->sym->hidden |= 2;
+ else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0)
+ n->sym->hidden |= 64;
+ }
+ n->sym->type = STRUCT; /* classification */
+ n->sym->Slst = m; /* structure itself */
+ n->sym->Snm = t; /* name of typedef */
+ n->sym->Nid = 0; /* this is no chan */
+ n->sym->hidden |= 4;
+ if (n->sym->nel <= 0)
+ non_fatal("bad array size for '%s'", n->sym->name);
+ }
+ lineno = oln;
+ Fname = ofn;
+}
+
+static Symbol *
+do_same(Lextok *n, Symbol *v, int xinit)
+{ Lextok *tmp, *fp, *tl;
+ int ix = eval(n->lft);
+ int oln = lineno;
+ Symbol *ofn = Fname;
+
+ lineno = n->ln;
+ Fname = n->fn;
+
+ /* n->sym->type == STRUCT
+ * index: n->lft
+ * subfields: n->rgt
+ * structure template: n->sym->Slst
+ * runtime values: n->sym->Sval
+ */
+ if (xinit) ini_struct(v); /* once, at top level */
+
+ if (ix >= v->nel || ix < 0)
+ { printf("spin: indexing %s[%d] - size is %d\n",
+ v->name, ix, v->nel);
+ fatal("indexing error \'%s\'", v->name);
+ }
+ if (!n->rgt || !n->rgt->lft)
+ { non_fatal("no subfields %s", v->name); /* i.e., wants all */
+ lineno = oln; Fname = ofn;
+ return ZS;
+ }
+
+ if (n->rgt->ntyp != '.')
+ { printf("bad subfield type %d\n", n->rgt->ntyp);
+ alldone(1);
+ }
+
+ tmp = n->rgt->lft;
+ if (tmp->ntyp != NAME && tmp->ntyp != TYPE)
+ { printf("bad subfield entry %d\n", tmp->ntyp);
+ alldone(1);
+ }
+ for (fp = v->Sval[ix]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ if (!strcmp(tl->sym->name, tmp->sym->name))
+ { lineno = oln; Fname = ofn;
+ return tl->sym;
+ }
+ fatal("cannot locate subfield %s", tmp->sym->name);
+ return ZS;
+}
+
+int
+Rval_struct(Lextok *n, Symbol *v, int xinit) /* n varref, v valref */
+{ Symbol *tl;
+ Lextok *tmp;
+ int ix;
+
+ if (!n || !(tl = do_same(n, v, xinit)))
+ return 0;
+
+ tmp = n->rgt->lft;
+ if (tmp->sym->type == STRUCT)
+ { return Rval_struct(tmp, tl, 0);
+ } else if (tmp->rgt)
+ fatal("non-zero 'rgt' on non-structure", 0);
+
+ ix = eval(tmp->lft);
+ if (ix >= tl->nel || ix < 0)
+ fatal("indexing error \'%s\'", tl->name);
+
+ return cast_val(tl->type, tl->val[ix], tl->nbits);
+}
+
+int
+Lval_struct(Lextok *n, Symbol *v, int xinit, int a) /* a = assigned value */
+{ Symbol *tl;
+ Lextok *tmp;
+ int ix;
+
+ if (!(tl = do_same(n, v, xinit)))
+ return 1;
+
+ tmp = n->rgt->lft;
+ if (tmp->sym->type == STRUCT)
+ return Lval_struct(tmp, tl, 0, a);
+ else if (tmp->rgt)
+ fatal("non-zero 'rgt' on non-structure", 0);
+
+ ix = eval(tmp->lft);
+ if (ix >= tl->nel || ix < 0)
+ fatal("indexing error \'%s\'", tl->name);
+
+ if (tl->nbits > 0)
+ a = (a & ((1<<tl->nbits)-1));
+
+ if (a != tl->val[ix])
+ { tl->val[ix] = a;
+ tl->setat = depth;
+ }
+ return 1;
+}
+
+int
+Cnt_flds(Lextok *m)
+{ Lextok *fp, *tl, *n;
+ int cnt = 0;
+
+ if (m->ntyp == ',')
+ { n = m;
+ goto is_lst;
+ }
+ if (!m->sym || m->ntyp != STRUCT)
+ return 1;
+
+ n = getuname(m->sym);
+is_lst:
+ for (fp = n; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ { if (tl->sym->nel != 1)
+ fatal("array of structures in param list, %s",
+ tl->sym->name);
+ cnt += Cnt_flds(tl->sym->Slst);
+ } else
+ cnt += tl->sym->nel;
+ }
+ return cnt;
+}
+
+int
+Sym_typ(Lextok *t)
+{ Symbol *s = t->sym;
+
+ if (!s) return 0;
+
+ if (s->type != STRUCT)
+ return s->type;
+
+ if (!t->rgt
+ || t->rgt->ntyp != '.' /* gh: had ! in wrong place */
+ || !t->rgt->lft)
+ return STRUCT; /* not a field reference */
+
+ return Sym_typ(t->rgt->lft);
+}
+
+int
+Width_set(int *wdth, int i, Lextok *n)
+{ Lextok *fp, *tl;
+ int j = i, k;
+
+ for (fp = n; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ j = Width_set(wdth, j, tl->sym->Slst);
+ else
+ { for (k = 0; k < tl->sym->nel; k++, j++)
+ wdth[j] = tl->sym->type;
+ } }
+ return j;
+}
+
+void
+ini_struct(Symbol *s)
+{ int i; Lextok *fp, *tl;
+
+ if (s->type != STRUCT) /* last step */
+ { (void) checkvar(s, 0);
+ return;
+ }
+ if (s->Sval == (Lextok **) 0)
+ { s->Sval = (Lextok **) emalloc(s->nel * sizeof(Lextok *));
+ for (i = 0; i < s->nel; i++)
+ { s->Sval[i] = cpnn(s->Slst, 1, 1, 1);
+
+ for (fp = s->Sval[i]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ ini_struct(tl->sym);
+ } }
+}
+
+static Lextok *
+cpnn(Lextok *s, int L, int R, int S)
+{ Lextok *d; extern int Nid;
+
+ if (!s) return ZN;
+
+ d = (Lextok *) emalloc(sizeof(Lextok));
+ d->ntyp = s->ntyp;
+ d->val = s->val;
+ d->ln = s->ln;
+ d->fn = s->fn;
+ d->sym = s->sym;
+ if (L) d->lft = cpnn(s->lft, 1, 1, S);
+ if (R) d->rgt = cpnn(s->rgt, 1, 1, S);
+
+ if (S && s->sym)
+ { d->sym = (Symbol *) emalloc(sizeof(Symbol));
+ memcpy(d->sym, s->sym, sizeof(Symbol));
+ if (d->sym->type == CHAN)
+ d->sym->Nid = ++Nid;
+ }
+ if (s->sq || s->sl)
+ fatal("cannot happen cpnn", (char *) 0);
+
+ return d;
+}
+
+int
+full_name(FILE *fd, Lextok *n, Symbol *v, int xinit)
+{ Symbol *tl;
+ Lextok *tmp;
+ int hiddenarrays = 0;
+
+ fprintf(fd, "%s", v->name);
+
+ if (!n || !(tl = do_same(n, v, xinit)))
+ return 0;
+ tmp = n->rgt->lft;
+
+ if (tmp->sym->type == STRUCT)
+ { fprintf(fd, ".");
+ hiddenarrays = full_name(fd, tmp, tl, 0);
+ goto out;
+ }
+ fprintf(fd, ".%s", tl->name);
+out: if (tmp->sym->nel > 1)
+ { fprintf(fd, "[%d]", eval(tmp->lft));
+ hiddenarrays = 1;
+ }
+ return hiddenarrays;
+}
+
+void
+validref(Lextok *p, Lextok *c)
+{ Lextok *fp, *tl;
+ char lbuf[512];
+
+ for (fp = p->sym->Slst; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ if (strcmp(tl->sym->name, c->sym->name) == 0)
+ return;
+
+ sprintf(lbuf, "no field '%s' defined in structure '%s'\n",
+ c->sym->name, p->sym->name);
+ non_fatal(lbuf, (char *) 0);
+}
+
+void
+struct_name(Lextok *n, Symbol *v, int xinit, char *buf)
+{ Symbol *tl;
+ Lextok *tmp;
+ char lbuf[512];
+
+ if (!n || !(tl = do_same(n, v, xinit)))
+ return;
+ tmp = n->rgt->lft;
+ if (tmp->sym->type == STRUCT)
+ { strcat(buf, ".");
+ struct_name(tmp, tl, 0, buf);
+ return;
+ }
+ sprintf(lbuf, ".%s", tl->name);
+ strcat(buf, lbuf);
+ if (tmp->sym->nel > 1)
+ { sprintf(lbuf, "[%d]", eval(tmp->lft));
+ strcat(buf, lbuf);
+ }
+}
+
+void
+walk2_struct(char *s, Symbol *z)
+{ Lextok *fp, *tl;
+ char eprefix[128];
+ int ix;
+ extern void Done_case(char *, Symbol *);
+
+ ini_struct(z);
+ if (z->nel == 1)
+ sprintf(eprefix, "%s%s.", s, z->name);
+ for (ix = 0; ix < z->nel; ix++)
+ { if (z->nel > 1)
+ sprintf(eprefix, "%s%s[%d].", s, z->name, ix);
+ for (fp = z->Sval[ix]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ walk2_struct(eprefix, tl->sym);
+ else if (tl->sym->type == CHAN)
+ Done_case(eprefix, tl->sym);
+ } }
+}
+
+void
+walk_struct(FILE *ofd, int dowhat, char *s, Symbol *z, char *a, char *b, char *c)
+{ Lextok *fp, *tl;
+ char eprefix[128];
+ int ix;
+
+ ini_struct(z);
+ if (z->nel == 1)
+ sprintf(eprefix, "%s%s.", s, z->name);
+ for (ix = 0; ix < z->nel; ix++)
+ { if (z->nel > 1)
+ sprintf(eprefix, "%s%s[%d].", s, z->name, ix);
+ for (fp = z->Sval[ix]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ walk_struct(ofd, dowhat, eprefix, tl->sym, a,b,c);
+ else
+ do_var(ofd, dowhat, eprefix, tl->sym, a,b,c);
+ } }
+}
+
+void
+c_struct(FILE *fd, char *ipref, Symbol *z)
+{ Lextok *fp, *tl;
+ char pref[256], eprefix[300];
+ int ix;
+
+ ini_struct(z);
+
+ for (ix = 0; ix < z->nel; ix++)
+ for (fp = z->Sval[ix]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { strcpy(eprefix, ipref);
+ if (z->nel > 1)
+ { /* insert index before last '.' */
+ eprefix[strlen(eprefix)-1] = '\0';
+ sprintf(pref, "[ %d ].", ix);
+ strcat(eprefix, pref);
+ }
+ if (tl->sym->type == STRUCT)
+ { strcat(eprefix, tl->sym->name);
+ strcat(eprefix, ".");
+ c_struct(fd, eprefix, tl->sym);
+ } else
+ c_var(fd, eprefix, tl->sym);
+ }
+}
+
+void
+dump_struct(Symbol *z, char *prefix, RunList *r)
+{ Lextok *fp, *tl;
+ char eprefix[256];
+ int ix, jx;
+
+ ini_struct(z);
+
+ for (ix = 0; ix < z->nel; ix++)
+ { if (z->nel > 1)
+ sprintf(eprefix, "%s[%d]", prefix, ix);
+ else
+ strcpy(eprefix, prefix);
+
+ for (fp = z->Sval[ix]; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ { char pref[300];
+ strcpy(pref, eprefix);
+ strcat(pref, ".");
+ strcat(pref, tl->sym->name);
+ dump_struct(tl->sym, pref, r);
+ } else
+ for (jx = 0; jx < tl->sym->nel; jx++)
+ { if (tl->sym->type == CHAN)
+ doq(tl->sym, jx, r);
+ else
+ { printf("\t\t");
+ if (r)
+ printf("%s(%d):", r->n->name, r->pid);
+ printf("%s.%s", eprefix, tl->sym->name);
+ if (tl->sym->nel > 1)
+ printf("[%d]", jx);
+ printf(" = ");
+ sr_mesg(stdout, tl->sym->val[jx],
+ tl->sym->type == MTYPE);
+ printf("\n");
+ } } }
+ }
+}
+
+static int
+retrieve(Lextok **targ, int i, int want, Lextok *n, int Ntyp)
+{ Lextok *fp, *tl;
+ int j = i, k;
+
+ for (fp = n; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ { j = retrieve(targ, j, want, tl->sym->Slst, Ntyp);
+ if (j < 0)
+ { Lextok *x = cpnn(tl, 1, 0, 0);
+ x->rgt = nn(ZN, '.', (*targ), ZN);
+ (*targ) = x;
+ return -1;
+ }
+ } else
+ { for (k = 0; k < tl->sym->nel; k++, j++)
+ { if (j == want)
+ { *targ = cpnn(tl, 1, 0, 0);
+ (*targ)->lft = nn(ZN, CONST, ZN, ZN);
+ (*targ)->lft->val = k;
+ if (Ntyp)
+ (*targ)->ntyp = (short) Ntyp;
+ return -1;
+ }
+ } } }
+ return j;
+}
+
+static int
+is_explicit(Lextok *n)
+{
+ if (!n) return 0;
+ if (!n->sym) fatal("unexpected - no symbol", 0);
+ if (n->sym->type != STRUCT) return 1;
+ if (!n->rgt) return 0;
+ if (n->rgt->ntyp != '.')
+ { lineno = n->ln;
+ Fname = n->fn;
+ printf("ntyp %d\n", n->rgt->ntyp);
+ fatal("unexpected %s, no '.'", n->sym->name);
+ }
+ return is_explicit(n->rgt->lft);
+}
+
+Lextok *
+expand(Lextok *n, int Ok)
+ /* turn rgt-lnked list of struct nms, into ',' list of flds */
+{ Lextok *x = ZN, *y;
+
+ if (!Ok) return n;
+
+ while (n)
+ { y = mk_explicit(n, 1, 0);
+ if (x)
+ (void) tail_add(x, y);
+ else
+ x = y;
+
+ n = n->rgt;
+ }
+ return x;
+}
+
+Lextok *
+mk_explicit(Lextok *n, int Ok, int Ntyp)
+ /* produce a single ',' list of fields */
+{ Lextok *bld = ZN, *x;
+ int i, cnt; extern int IArgs;
+
+ if (n->sym->type != STRUCT
+ || is_explicit(n))
+ return n;
+
+ if (n->rgt
+ && n->rgt->ntyp == '.'
+ && n->rgt->lft
+ && n->rgt->lft->sym
+ && n->rgt->lft->sym->type == STRUCT)
+ { Lextok *y;
+ bld = mk_explicit(n->rgt->lft, Ok, Ntyp);
+ for (x = bld; x; x = x->rgt)
+ { y = cpnn(n, 1, 0, 0);
+ y->rgt = nn(ZN, '.', x->lft, ZN);
+ x->lft = y;
+ }
+
+ return bld;
+ }
+
+ if (!Ok || !n->sym->Slst)
+ { if (IArgs) return n;
+ printf("spin: saw '");
+ comment(stdout, n, 0);
+ printf("'\n");
+ fatal("incomplete structure ref '%s'", n->sym->name);
+ }
+
+ cnt = Cnt_flds(n->sym->Slst);
+ for (i = cnt-1; i >= 0; i--)
+ { bld = nn(ZN, ',', ZN, bld);
+ if (retrieve(&(bld->lft), 0, i, n->sym->Slst, Ntyp) >= 0)
+ { printf("cannot retrieve field %d\n", i);
+ fatal("bad structure %s", n->sym->name);
+ }
+ x = cpnn(n, 1, 0, 0);
+ x->rgt = nn(ZN, '.', bld->lft, ZN);
+ bld->lft = x;
+ }
+ return bld;
+}
+
+Lextok *
+tail_add(Lextok *a, Lextok *b)
+{ Lextok *t;
+
+ for (t = a; t->rgt; t = t->rgt)
+ if (t->ntyp != ',')
+ fatal("unexpected type - tail_add", 0);
+ t->rgt = b;
+ return a;
+}
+
+void
+setpname(Lextok *n)
+{ UType *tmp;
+
+ for (tmp = Pnames; tmp; tmp = tmp->nxt)
+ if (!strcmp(n->sym->name, tmp->nm->name))
+ { non_fatal("proctype %s redefined",
+ n->sym->name);
+ return;
+ }
+ tmp = (UType *) emalloc(sizeof(UType));
+ tmp->nm = n->sym;
+ tmp->nxt = Pnames;
+ Pnames = tmp;
+}
+
+int
+isproctype(char *t)
+{ UType *tmp;
+
+ for (tmp = Pnames; tmp; tmp = tmp->nxt)
+ { if (!strcmp(t, tmp->nm->name))
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/***** spin: sym.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern Symbol *Fname, *owner;
+extern int lineno, depth, verbose, NamesNotAdded, deadvar, has_hidden;
+extern short has_xu;
+
+Symbol *context = ZS;
+Ordered *all_names = (Ordered *)0;
+int Nid = 0;
+
+Lextok *Mtype = (Lextok *) 0;
+
+static Ordered *last_name = (Ordered *)0;
+static Symbol *symtab[Nhash+1];
+static Lextok *runstmnts = ZN;
+
+static int
+samename(Symbol *a, Symbol *b)
+{
+ if (!a && !b) return 1;
+ if (!a || !b) return 0;
+ return !strcmp(a->name, b->name);
+}
+
+int
+hash(char *s)
+{ int h=0;
+
+ while (*s)
+ { h += *s++;
+ h <<= 1;
+ if (h&(Nhash+1))
+ h |= 1;
+ }
+ return h&Nhash;
+}
+
+Symbol *
+lookup(char *s)
+{ Symbol *sp; Ordered *no;
+ int h = hash(s);
+
+ for (sp = symtab[h]; sp; sp = sp->next)
+ if (strcmp(sp->name, s) == 0
+ && samename(sp->context, context)
+ && samename(sp->owner, owner))
+ return sp; /* found */
+
+ if (context) /* in proctype */
+ for (sp = symtab[h]; sp; sp = sp->next)
+ if (strcmp(sp->name, s) == 0
+ && !sp->context
+ && samename(sp->owner, owner))
+ return sp; /* global */
+
+ sp = (Symbol *) emalloc(sizeof(Symbol));
+ sp->name = (char *) emalloc(strlen(s) + 1);
+ strcpy(sp->name, s);
+ sp->nel = 1;
+ sp->setat = depth;
+ sp->context = context;
+ sp->owner = owner; /* if fld in struct */
+
+ if (NamesNotAdded == 0)
+ { sp->next = symtab[h];
+ symtab[h] = sp;
+ no = (Ordered *) emalloc(sizeof(Ordered));
+ no->entry = sp;
+ if (!last_name)
+ last_name = all_names = no;
+ else
+ { last_name->next = no;
+ last_name = no;
+ } }
+
+ return sp;
+}
+
+void
+trackvar(Lextok *n, Lextok *m)
+{ Symbol *sp = n->sym;
+
+ if (!sp) return; /* a structure list */
+ switch (m->ntyp) {
+ case NAME:
+ if (m->sym->type != BIT)
+ { sp->hidden |= 4;
+ if (m->sym->type != BYTE)
+ sp->hidden |= 8;
+ }
+ break;
+ case CONST:
+ if (m->val != 0 && m->val != 1)
+ sp->hidden |= 4;
+ if (m->val < 0 || m->val > 256)
+ sp->hidden |= 8; /* ditto byte-equiv */
+ break;
+ default: /* unknown */
+ sp->hidden |= (4|8); /* not known bit-equiv */
+ }
+}
+
+void
+trackrun(Lextok *n)
+{
+ runstmnts = nn(ZN, 0, n, runstmnts);
+}
+
+void
+checkrun(Symbol *parnm, int posno)
+{ Lextok *n, *now, *v; int i, m;
+ int res = 0; char buf[16], buf2[16];
+
+ for (n = runstmnts; n; n = n->rgt)
+ { now = n->lft;
+ if (now->sym != parnm->context)
+ continue;
+ for (v = now->lft, i = 0; v; v = v->rgt, i++)
+ if (i == posno)
+ { m = v->lft->ntyp;
+ if (m == CONST)
+ { m = v->lft->val;
+ if (m != 0 && m != 1)
+ res |= 4;
+ if (m < 0 || m > 256)
+ res |= 8;
+ } else if (m == NAME)
+ { m = v->lft->sym->type;
+ if (m != BIT)
+ { res |= 4;
+ if (m != BYTE)
+ res |= 8;
+ }
+ } else
+ res |= (4|8); /* unknown */
+ break;
+ } }
+ if (!(res&4) || !(res&8))
+ { if (!(verbose&32)) return;
+ strcpy(buf2, (!(res&4))?"bit":"byte");
+ sputtype(buf, parnm->type);
+ i = (int) strlen(buf);
+ while (i > 0 && buf[--i] == ' ') buf[i] = '\0';
+ if (i == 0 || strcmp(buf, buf2) == 0) return;
+ prehint(parnm);
+ printf("proctype %s, '%s %s' could be declared",
+ parnm->context?parnm->context->name:"", buf, parnm->name);
+ printf(" '%s %s'\n", buf2, parnm->name);
+ }
+}
+
+void
+trackchanuse(Lextok *m, Lextok *w, int t)
+{ Lextok *n = m; int cnt = 1;
+ while (n)
+ { if (n->lft
+ && n->lft->sym
+ && n->lft->sym->type == CHAN)
+ setaccess(n->lft->sym, w?w->sym:ZS, cnt, t);
+ n = n->rgt; cnt++;
+ }
+}
+
+void
+setptype(Lextok *n, int t, Lextok *vis) /* predefined types */
+{ int oln = lineno, cnt = 1; extern int Expand_Ok;
+
+ while (n)
+ { if (n->sym->type && !(n->sym->hidden&32))
+ { lineno = n->ln; Fname = n->fn;
+ non_fatal("redeclaration of '%s'", n->sym->name);
+ lineno = oln;
+ }
+ n->sym->type = (short) t;
+
+ if (Expand_Ok)
+ { n->sym->hidden |= (4|8|16); /* formal par */
+ if (t == CHAN)
+ setaccess(n->sym, ZS, cnt, 'F');
+ }
+ if (t == UNSIGNED)
+ { if (n->sym->nbits < 0 || n->sym->nbits >= 32)
+ fatal("(%s) has invalid width-field", n->sym->name);
+ if (n->sym->nbits == 0)
+ { n->sym->nbits = 16;
+ non_fatal("unsigned without width-field", 0);
+ }
+ } else if (n->sym->nbits > 0)
+ { non_fatal("(%s) only an unsigned can have width-field",
+ n->sym->name);
+ }
+ if (vis)
+ { if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0)
+ { n->sym->hidden |= 1;
+ has_hidden++;
+ if (t == BIT)
+ fatal("bit variable (%s) cannot be hidden",
+ n->sym->name);
+ } else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0)
+ { n->sym->hidden |= 2;
+ } else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0)
+ { n->sym->hidden |= 64;
+ }
+ }
+ if (t == CHAN)
+ n->sym->Nid = ++Nid;
+ else
+ { n->sym->Nid = 0;
+ if (n->sym->ini
+ && n->sym->ini->ntyp == CHAN)
+ { Fname = n->fn;
+ lineno = n->ln;
+ fatal("chan initializer for non-channel %s",
+ n->sym->name);
+ }
+ }
+ if (n->sym->nel <= 0)
+ { lineno = n->ln; Fname = n->fn;
+ non_fatal("bad array size for '%s'", n->sym->name);
+ lineno = oln;
+ }
+ n = n->rgt; cnt++;
+ }
+}
+
+static void
+setonexu(Symbol *sp, int t)
+{
+ sp->xu |= t;
+ if (t == XR || t == XS)
+ { if (sp->xup[t-1]
+ && strcmp(sp->xup[t-1]->name, context->name))
+ { printf("error: x[rs] claims from %s and %s\n",
+ sp->xup[t-1]->name, context->name);
+ non_fatal("conflicting claims on chan '%s'",
+ sp->name);
+ }
+ sp->xup[t-1] = context;
+ }
+}
+
+static void
+setallxu(Lextok *n, int t)
+{ Lextok *fp, *tl;
+
+ for (fp = n; fp; fp = fp->rgt)
+ for (tl = fp->lft; tl; tl = tl->rgt)
+ { if (tl->sym->type == STRUCT)
+ setallxu(tl->sym->Slst, t);
+ else if (tl->sym->type == CHAN)
+ setonexu(tl->sym, t);
+ }
+}
+
+Lextok *Xu_List = (Lextok *) 0;
+
+void
+setxus(Lextok *p, int t)
+{ Lextok *m, *n;
+
+ has_xu = 1;
+ if (!context)
+ { lineno = p->ln;
+ Fname = p->fn;
+ fatal("non-local x[rs] assertion", (char *)0);
+ }
+ for (m = p; m; m = m->rgt)
+ { Lextok *Xu_new = (Lextok *) emalloc(sizeof(Lextok));
+ Xu_new->val = t;
+ Xu_new->lft = m->lft;
+ Xu_new->sym = context;
+ Xu_new->rgt = Xu_List;
+ Xu_List = Xu_new;
+
+ n = m->lft;
+ if (n->sym->type == STRUCT)
+ setallxu(n->sym->Slst, t);
+ else if (n->sym->type == CHAN)
+ setonexu(n->sym, t);
+ else
+ { int oln = lineno;
+ lineno = n->ln; Fname = n->fn;
+ non_fatal("xr or xs of non-chan '%s'",
+ n->sym->name);
+ lineno = oln;
+ }
+ }
+}
+
+void
+setmtype(Lextok *m)
+{ Lextok *n;
+ int cnt, oln = lineno;
+
+ if (m) { lineno = m->ln; Fname = m->fn; }
+
+ if (!Mtype)
+ Mtype = m;
+ else
+ { for (n = Mtype; n->rgt; n = n->rgt)
+ ;
+ n->rgt = m; /* concatenate */
+ }
+
+ for (n = Mtype, cnt = 1; n; n = n->rgt, cnt++) /* syntax check */
+ { if (!n->lft || !n->lft->sym
+ || n->lft->ntyp != NAME
+ || n->lft->lft) /* indexed variable */
+ fatal("bad mtype definition", (char *)0);
+
+ /* label the name */
+ if (n->lft->sym->type != MTYPE)
+ { n->lft->sym->hidden |= 128; /* is used */
+ n->lft->sym->type = MTYPE;
+ n->lft->sym->ini = nn(ZN,CONST,ZN,ZN);
+ n->lft->sym->ini->val = cnt;
+ } else if (n->lft->sym->ini->val != cnt)
+ non_fatal("name %s appears twice in mtype declaration",
+ n->lft->sym->name);
+ }
+ lineno = oln;
+ if (cnt > 256)
+ fatal("too many mtype elements (>255)", (char *)0);
+}
+
+int
+ismtype(char *str) /* name to number */
+{ Lextok *n;
+ int cnt = 1;
+
+ for (n = Mtype; n; n = n->rgt)
+ { if (strcmp(str, n->lft->sym->name) == 0)
+ return cnt;
+ cnt++;
+ }
+ return 0;
+}
+
+int
+sputtype(char *foo, int m)
+{
+ switch (m) {
+ case UNSIGNED: strcpy(foo, "unsigned "); break;
+ case BIT: strcpy(foo, "bit "); break;
+ case BYTE: strcpy(foo, "byte "); break;
+ case CHAN: strcpy(foo, "chan "); break;
+ case SHORT: strcpy(foo, "short "); break;
+ case INT: strcpy(foo, "int "); break;
+ case MTYPE: strcpy(foo, "mtype "); break;
+ case STRUCT: strcpy(foo, "struct"); break;
+ case PROCTYPE: strcpy(foo, "proctype"); break;
+ case LABEL: strcpy(foo, "label "); return 0;
+ default: strcpy(foo, "value "); return 0;
+ }
+ return 1;
+}
+
+
+static int
+puttype(int m)
+{ char buf[128];
+
+ if (sputtype(buf, m))
+ { printf("%s", buf);
+ return 1;
+ }
+ return 0;
+}
+
+void
+symvar(Symbol *sp)
+{ Lextok *m;
+
+ if (!puttype(sp->type))
+ return;
+
+ printf("\t");
+ if (sp->owner) printf("%s.", sp->owner->name);
+ printf("%s", sp->name);
+ if (sp->nel > 1) printf("[%d]", sp->nel);
+
+ if (sp->type == CHAN)
+ printf("\t%d", (sp->ini)?sp->ini->val:0);
+ else if (sp->type == STRUCT) /* Frank Weil, 2.9.8 */
+ printf("\t%s", sp->Snm->name);
+ else
+ printf("\t%d", eval(sp->ini));
+
+ if (sp->owner)
+ printf("\t<:struct-field:>");
+ else
+ if (!sp->context)
+ printf("\t<:global:>");
+ else
+ printf("\t<%s>", sp->context->name);
+
+ if (sp->Nid < 0) /* formal parameter */
+ printf("\t<parameter %d>", -(sp->Nid));
+ else
+ printf("\t<variable>");
+ if (sp->type == CHAN && sp->ini)
+ { int i;
+ for (m = sp->ini->rgt, i = 0; m; m = m->rgt)
+ i++;
+ printf("\t%d\t", i);
+ for (m = sp->ini->rgt; m; m = m->rgt)
+ { if (m->ntyp == STRUCT)
+ printf("struct %s", m->sym->name);
+ else
+ (void) puttype(m->ntyp);
+ if (m->rgt) printf("\t");
+ }
+ }
+ printf("\n");
+}
+
+void
+symdump(void)
+{ Ordered *walk;
+
+ for (walk = all_names; walk; walk = walk->next)
+ symvar(walk->entry);
+}
+
+void
+chname(Symbol *sp)
+{ printf("chan ");
+ if (sp->context) printf("%s-", sp->context->name);
+ if (sp->owner) printf("%s.", sp->owner->name);
+ printf("%s", sp->name);
+ if (sp->nel > 1) printf("[%d]", sp->nel);
+ printf("\t");
+}
+
+static struct X {
+ int typ; char *nm;
+} xx[] = {
+ { 'A', "exported as run parameter" },
+ { 'F', "imported as proctype parameter" },
+ { 'L', "used as l-value in asgnmnt" },
+ { 'V', "used as r-value in asgnmnt" },
+ { 'P', "polled in receive stmnt" },
+ { 'R', "used as parameter in receive stmnt" },
+ { 'S', "used as parameter in send stmnt" },
+ { 'r', "received from" },
+ { 's', "sent to" },
+};
+
+static void
+chan_check(Symbol *sp)
+{ Access *a; int i, b=0, d;
+
+ if (verbose&1) goto report; /* -C -g */
+
+ for (a = sp->access; a; a = a->lnk)
+ if (a->typ == 'r')
+ b |= 1;
+ else if (a->typ == 's')
+ b |= 2;
+ if (b == 3 || (sp->hidden&16)) /* balanced or formal par */
+ return;
+report:
+ chname(sp);
+ for (i = d = 0; i < (int) (sizeof(xx)/sizeof(struct X)); i++)
+ { b = 0;
+ for (a = sp->access; a; a = a->lnk)
+ if (a->typ == xx[i].typ) b++;
+ if (b == 0) continue; d++;
+ printf("\n\t%s by: ", xx[i].nm);
+ for (a = sp->access; a; a = a->lnk)
+ if (a->typ == xx[i].typ)
+ { printf("%s", a->who->name);
+ if (a->what) printf(" to %s", a->what->name);
+ if (a->cnt) printf(" par %d", a->cnt);
+ if (--b > 0) printf(", ");
+ }
+ }
+ printf("%s\n", (!d)?"\n\tnever used under this name":"");
+}
+
+void
+chanaccess(void)
+{ Ordered *walk;
+ char buf[128];
+ extern int Caccess, separate;
+ extern short has_code;
+
+ for (walk = all_names; walk; walk = walk->next)
+ { if (!walk->entry->owner)
+ switch (walk->entry->type) {
+ case CHAN:
+ if (Caccess) chan_check(walk->entry);
+ break;
+ case MTYPE:
+ case BIT:
+ case BYTE:
+ case SHORT:
+ case INT:
+ case UNSIGNED:
+ if ((walk->entry->hidden&128)) /* was: 32 */
+ continue;
+
+ if (!separate
+ && !walk->entry->context
+ && !has_code
+ && deadvar)
+ walk->entry->hidden |= 1; /* auto-hide */
+
+ if (!(verbose&32) || has_code) continue;
+
+ printf("spin: warning, %s, ", Fname->name);
+ sputtype(buf, walk->entry->type);
+ if (walk->entry->context)
+ printf("proctype %s",
+ walk->entry->context->name);
+ else
+ printf("global");
+ printf(", '%s%s' variable is never used\n",
+ buf, walk->entry->name);
+ } }
+}
--- /dev/null
+/***** tl_spin: tl.h *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include <stdio.h>
+#include <string.h>
+
+typedef struct Symbol {
+ char *name;
+ struct Symbol *next; /* linked list, symbol table */
+} Symbol;
+
+typedef struct Node {
+ short ntyp; /* node type */
+ struct Symbol *sym;
+ struct Node *lft; /* tree */
+ struct Node *rgt; /* tree */
+ struct Node *nxt; /* if linked list */
+} Node;
+
+typedef struct Graph {
+ Symbol *name;
+ Symbol *incoming;
+ Symbol *outgoing;
+ Symbol *oldstring;
+ Symbol *nxtstring;
+ Node *New;
+ Node *Old;
+ Node *Other;
+ Node *Next;
+ unsigned char isred[64], isgrn[64];
+ unsigned char redcnt, grncnt;
+ unsigned char reachable;
+ struct Graph *nxt;
+} Graph;
+
+typedef struct Mapping {
+ char *from;
+ Graph *to;
+ struct Mapping *nxt;
+} Mapping;
+
+enum {
+ ALWAYS=257,
+ AND, /* 258 */
+ EQUIV, /* 259 */
+ EVENTUALLY, /* 260 */
+ FALSE, /* 261 */
+ IMPLIES, /* 262 */
+ NOT, /* 263 */
+ OR, /* 264 */
+ PREDICATE, /* 265 */
+ TRUE, /* 266 */
+ U_OPER, /* 267 */
+ V_OPER /* 268 */
+#ifdef NXT
+ , NEXT /* 269 */
+#endif
+};
+
+Node *Canonical(Node *);
+Node *canonical(Node *);
+Node *cached(Node *);
+Node *dupnode(Node *);
+Node *getnode(Node *);
+Node *in_cache(Node *);
+Node *push_negation(Node *);
+Node *right_linked(Node *);
+Node *tl_nn(int, Node *, Node *);
+
+Symbol *tl_lookup(char *);
+Symbol *getsym(Symbol *);
+Symbol *DoDump(Node *);
+
+extern char *emalloc(size_t); /* in main.c */
+
+int anywhere(int, Node *, Node *);
+int dump_cond(Node *, Node *, int);
+int hash(char *); /* in sym.c */
+int isalnum_(int); /* in spinlex.c */
+int isequal(Node *, Node *);
+int tl_Getchar(void);
+
+void *tl_emalloc(int);
+void a_stats(void);
+void addtrans(Graph *, char *, Node *, char *);
+void cache_stats(void);
+void dump(Node *);
+void exit(int);
+void Fatal(char *, char *);
+void fatal(char *, char *);
+void fsm_print(void);
+void releasenode(int, Node *);
+void tfree(void *);
+void tl_explain(int);
+void tl_UnGetchar(void);
+void tl_parse(void);
+void tl_yyerror(char *);
+void trans(Node *);
+
+#define ZN (Node *)0
+#define ZS (Symbol *)0
+#define Nhash 255 /* must match size in spin.h */
+#define True tl_nn(TRUE, ZN, ZN)
+#define False tl_nn(FALSE, ZN, ZN)
+#define Not(a) push_negation(tl_nn(NOT, a, ZN))
+#define rewrite(n) canonical(right_linked(n))
+
+typedef Node *Nodeptr;
+#define YYSTYPE Nodeptr
+
+#define Debug(x) { if (tl_verbose) printf(x); }
+#define Debug2(x,y) { if (tl_verbose) printf(x,y); }
+#define Dump(x) { if (tl_verbose) dump(x); }
+#define Explain(x) { if (tl_verbose) tl_explain(x); }
+
+#define Assert(x, y) { if (!(x)) { tl_explain(y); \
+ Fatal(": assertion failed\n",(char *)0); } }
--- /dev/null
+/***** tl_spin: tl_buchi.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+extern int tl_verbose, tl_clutter, Total, Max_Red;
+
+FILE *tl_out; /* if standalone: = stdout; */
+
+typedef struct Transition {
+ Symbol *name;
+ Node *cond;
+ int redundant, merged, marked;
+ struct Transition *nxt;
+} Transition;
+
+typedef struct State {
+ Symbol *name;
+ Transition *trans;
+ Graph *colors;
+ unsigned char redundant;
+ unsigned char accepting;
+ unsigned char reachable;
+ struct State *nxt;
+} State;
+
+static State *never = (State *) 0;
+static int hitsall;
+
+static int
+sametrans(Transition *s, Transition *t)
+{
+ if (strcmp(s->name->name, t->name->name) != 0)
+ return 0;
+ return isequal(s->cond, t->cond);
+}
+
+static Node *
+Prune(Node *p)
+{
+ if (p)
+ switch (p->ntyp) {
+ case PREDICATE:
+ case NOT:
+ case FALSE:
+ case TRUE:
+#ifdef NXT
+ case NEXT:
+#endif
+ return p;
+ case OR:
+ p->lft = Prune(p->lft);
+ if (!p->lft)
+ { releasenode(1, p->rgt);
+ return ZN;
+ }
+ p->rgt = Prune(p->rgt);
+ if (!p->rgt)
+ { releasenode(1, p->lft);
+ return ZN;
+ }
+ return p;
+ case AND:
+ p->lft = Prune(p->lft);
+ if (!p->lft)
+ return Prune(p->rgt);
+ p->rgt = Prune(p->rgt);
+ if (!p->rgt)
+ return p->lft;
+ return p;
+ }
+ releasenode(1, p);
+ return ZN;
+}
+
+static State *
+findstate(char *nm)
+{ State *b;
+ for (b = never; b; b = b->nxt)
+ if (!strcmp(b->name->name, nm))
+ return b;
+ if (strcmp(nm, "accept_all"))
+ { if (strncmp(nm, "accept", 6))
+ { int i; char altnm[64];
+ for (i = 0; i < 64; i++)
+ if (nm[i] == '_')
+ break;
+ if (i >= 64)
+ Fatal("name too long %s", nm);
+ sprintf(altnm, "accept%s", &nm[i]);
+ return findstate(altnm);
+ }
+ /* Fatal("buchi: no state %s", nm); */
+ }
+ return (State *) 0;
+}
+
+static void
+Dfs(State *b)
+{ Transition *t;
+
+ if (!b || b->reachable) return;
+ b->reachable = 1;
+
+ if (b->redundant)
+ printf("/* redundant state %s */\n",
+ b->name->name);
+ for (t = b->trans; t; t = t->nxt)
+ { if (!t->redundant)
+ { Dfs(findstate(t->name->name));
+ if (!hitsall
+ && strcmp(t->name->name, "accept_all") == 0)
+ hitsall = 1;
+ }
+ }
+}
+
+void
+retarget(char *from, char *to)
+{ State *b;
+ Transition *t;
+ Symbol *To = tl_lookup(to);
+
+ if (tl_verbose) printf("replace %s with %s\n", from, to);
+
+ for (b = never; b; b = b->nxt)
+ { if (!strcmp(b->name->name, from))
+ b->redundant = 1;
+ else
+ for (t = b->trans; t; t = t->nxt)
+ { if (!strcmp(t->name->name, from))
+ t->name = To;
+ } }
+}
+
+#ifdef NXT
+static Node *
+nonxt(Node *n)
+{
+ switch (n->ntyp) {
+ case U_OPER:
+ case V_OPER:
+ case NEXT:
+ return ZN;
+ case OR:
+ n->lft = nonxt(n->lft);
+ n->rgt = nonxt(n->rgt);
+ if (!n->lft || !n->rgt)
+ return True;
+ return n;
+ case AND:
+ n->lft = nonxt(n->lft);
+ n->rgt = nonxt(n->rgt);
+ if (!n->lft)
+ { if (!n->rgt)
+ n = ZN;
+ else
+ n = n->rgt;
+ } else if (!n->rgt)
+ n = n->lft;
+ return n;
+ }
+ return n;
+}
+#endif
+
+static Node *
+combination(Node *s, Node *t)
+{ Node *nc;
+#ifdef NXT
+ Node *a = nonxt(s);
+ Node *b = nonxt(t);
+
+ if (tl_verbose)
+ { printf("\tnonxtA: "); dump(a);
+ printf("\n\tnonxtB: "); dump(b);
+ printf("\n");
+ }
+ /* if there's only a X(f), its equivalent to true */
+ if (!a || !b)
+ nc = True;
+ else
+ nc = tl_nn(OR, a, b);
+#else
+ nc = tl_nn(OR, s, t);
+#endif
+ if (tl_verbose)
+ { printf("\tcombo: "); dump(nc);
+ printf("\n");
+ }
+ return nc;
+}
+
+Node *
+unclutter(Node *n, char *snm)
+{ Node *t, *s, *v, *u;
+ Symbol *w;
+
+ /* check only simple cases like !q && q */
+ for (t = n; t; t = t->rgt)
+ { if (t->rgt)
+ { if (t->ntyp != AND || !t->lft)
+ return n;
+ if (t->lft->ntyp != PREDICATE
+#ifdef NXT
+ && t->lft->ntyp != NEXT
+#endif
+ && t->lft->ntyp != NOT)
+ return n;
+ } else
+ { if (t->ntyp != PREDICATE
+#ifdef NXT
+ && t->ntyp != NEXT
+#endif
+ && t->ntyp != NOT)
+ return n;
+ }
+ }
+
+ for (t = n; t; t = t->rgt)
+ { if (t->rgt)
+ v = t->lft;
+ else
+ v = t;
+ if (v->ntyp == NOT
+ && v->lft->ntyp == PREDICATE)
+ { w = v->lft->sym;
+ for (s = n; s; s = s->rgt)
+ { if (s == t) continue;
+ if (s->rgt)
+ u = s->lft;
+ else
+ u = s;
+ if (u->ntyp == PREDICATE
+ && strcmp(u->sym->name, w->name) == 0)
+ { if (tl_verbose)
+ { printf("BINGO %s:\t", snm);
+ dump(n);
+ printf("\n");
+ }
+ return False;
+ }
+ }
+ } }
+ return n;
+}
+
+static void
+clutter(void)
+{ State *p;
+ Transition *s;
+
+ for (p = never; p; p = p->nxt)
+ for (s = p->trans; s; s = s->nxt)
+ { s->cond = unclutter(s->cond, p->name->name);
+ if (s->cond
+ && s->cond->ntyp == FALSE)
+ { if (s != p->trans
+ || s->nxt)
+ s->redundant = 1;
+ }
+ }
+}
+
+static void
+showtrans(State *a)
+{ Transition *s;
+
+ for (s = a->trans; s; s = s->nxt)
+ { printf("%s ", s->name?s->name->name:"-");
+ dump(s->cond);
+ printf(" %d %d %d\n", s->redundant, s->merged, s->marked);
+ }
+}
+
+static int
+mergetrans(void)
+{ State *b;
+ Transition *s, *t;
+ Node *nc; int cnt = 0;
+
+ for (b = never; b; b = b->nxt)
+ { if (!b->reachable) continue;
+
+ for (s = b->trans; s; s = s->nxt)
+ { if (s->redundant) continue;
+
+ for (t = s->nxt; t; t = t->nxt)
+ if (!t->redundant
+ && !strcmp(s->name->name, t->name->name))
+ { if (tl_verbose)
+ { printf("===\nstate %s, trans to %s redundant\n",
+ b->name->name, s->name->name);
+ showtrans(b);
+ printf(" conditions ");
+ dump(s->cond); printf(" <-> ");
+ dump(t->cond); printf("\n");
+ }
+
+ if (!s->cond) /* same as T */
+ { releasenode(1, t->cond); /* T or t */
+ nc = True;
+ } else if (!t->cond)
+ { releasenode(1, s->cond);
+ nc = True;
+ } else
+ { nc = combination(s->cond, t->cond);
+ }
+ t->cond = rewrite(nc);
+ t->merged = 1;
+ s->redundant = 1;
+ cnt++;
+ break;
+ } } }
+ return cnt;
+}
+
+static int
+all_trans_match(State *a, State *b)
+{ Transition *s, *t;
+ int found, result = 0;
+
+ if (a->accepting != b->accepting)
+ goto done;
+
+ for (s = a->trans; s; s = s->nxt)
+ { if (s->redundant) continue;
+ found = 0;
+ for (t = b->trans; t; t = t->nxt)
+ { if (t->redundant) continue;
+ if (sametrans(s, t))
+ { found = 1;
+ t->marked = 1;
+ break;
+ } }
+ if (!found)
+ goto done;
+ }
+ for (s = b->trans; s; s = s->nxt)
+ { if (s->redundant || s->marked) continue;
+ found = 0;
+ for (t = a->trans; t; t = t->nxt)
+ { if (t->redundant) continue;
+ if (sametrans(s, t))
+ { found = 1;
+ break;
+ } }
+ if (!found)
+ goto done;
+ }
+ result = 1;
+done:
+ for (s = b->trans; s; s = s->nxt)
+ s->marked = 0;
+ return result;
+}
+
+#ifndef NO_OPT
+#define BUCKY
+#endif
+
+#ifdef BUCKY
+static int
+all_bucky(State *a, State *b)
+{ Transition *s, *t;
+ int found, result = 0;
+
+ for (s = a->trans; s; s = s->nxt)
+ { if (s->redundant) continue;
+ found = 0;
+ for (t = b->trans; t; t = t->nxt)
+ { if (t->redundant) continue;
+
+ if (isequal(s->cond, t->cond))
+ { if (strcmp(s->name->name, b->name->name) == 0
+ && strcmp(t->name->name, a->name->name) == 0)
+ { found = 1; /* they point to each other */
+ t->marked = 1;
+ break;
+ }
+ if (strcmp(s->name->name, t->name->name) == 0
+ && strcmp(s->name->name, "accept_all") == 0)
+ { found = 1;
+ t->marked = 1;
+ break;
+ /* same exit from which there is no return */
+ }
+ }
+ }
+ if (!found)
+ goto done;
+ }
+ for (s = b->trans; s; s = s->nxt)
+ { if (s->redundant || s->marked) continue;
+ found = 0;
+ for (t = a->trans; t; t = t->nxt)
+ { if (t->redundant) continue;
+
+ if (isequal(s->cond, t->cond))
+ { if (strcmp(s->name->name, a->name->name) == 0
+ && strcmp(t->name->name, b->name->name) == 0)
+ { found = 1;
+ t->marked = 1;
+ break;
+ }
+ if (strcmp(s->name->name, t->name->name) == 0
+ && strcmp(s->name->name, "accept_all") == 0)
+ { found = 1;
+ t->marked = 1;
+ break;
+ }
+ }
+ }
+ if (!found)
+ goto done;
+ }
+ result = 1;
+done:
+ for (s = b->trans; s; s = s->nxt)
+ s->marked = 0;
+ return result;
+}
+
+static int
+buckyballs(void)
+{ State *a, *b, *c, *d;
+ int m, cnt=0;
+
+ do {
+ m = 0; cnt++;
+ for (a = never; a; a = a->nxt)
+ { if (!a->reachable) continue;
+
+ if (a->redundant) continue;
+
+ for (b = a->nxt; b; b = b->nxt)
+ { if (!b->reachable) continue;
+
+ if (b->redundant) continue;
+
+ if (all_bucky(a, b))
+ { m++;
+ if (tl_verbose)
+ { printf("%s bucky match %s\n",
+ a->name->name, b->name->name);
+ }
+
+ if (a->accepting && !b->accepting)
+ { if (strcmp(b->name->name, "T0_init") == 0)
+ { c = a; d = b;
+ b->accepting = 1;
+ } else
+ { c = b; d = a;
+ }
+ } else
+ { c = a; d = b;
+ }
+
+ retarget(c->name->name, d->name->name);
+ if (!strncmp(c->name->name, "accept", 6)
+ && Max_Red == 0)
+ { char buf[64];
+ sprintf(buf, "T0%s", &(c->name->name[6]));
+ retarget(buf, d->name->name);
+ }
+ break;
+ }
+ } }
+ } while (m && cnt < 10);
+ return cnt-1;
+}
+#endif
+
+static int
+mergestates(int v)
+{ State *a, *b;
+ int m, cnt=0;
+
+ if (tl_verbose)
+ return 0;
+
+ do {
+ m = 0; cnt++;
+ for (a = never; a; a = a->nxt)
+ { if (v && !a->reachable) continue;
+
+ if (a->redundant) continue; /* 3.3.10 */
+
+ for (b = a->nxt; b; b = b->nxt)
+ { if (v && !b->reachable) continue;
+
+ if (b->redundant) continue; /* 3.3.10 */
+
+ if (all_trans_match(a, b))
+ { m++;
+ if (tl_verbose)
+ { printf("%d: state %s equals state %s\n",
+ cnt, a->name->name, b->name->name);
+ showtrans(a);
+ printf("==\n");
+ showtrans(b);
+ }
+ retarget(a->name->name, b->name->name);
+ if (!strncmp(a->name->name, "accept", 6)
+ && Max_Red == 0)
+ { char buf[64];
+ sprintf(buf, "T0%s", &(a->name->name[6]));
+ retarget(buf, b->name->name);
+ }
+ break;
+ }
+#if 0
+ else if (tl_verbose)
+ { printf("\n%d: state %s differs from state %s [%d,%d]\n",
+ cnt, a->name->name, b->name->name,
+ a->accepting, b->accepting);
+ showtrans(a);
+ printf("==\n");
+ showtrans(b);
+ printf("\n");
+ }
+#endif
+ } }
+ } while (m && cnt < 10);
+ return cnt-1;
+}
+
+static int tcnt;
+
+static void
+rev_trans(Transition *t) /* print transitions in reverse order... */
+{
+ if (!t) return;
+ rev_trans(t->nxt);
+
+ if (t->redundant && !tl_verbose) return;
+ fprintf(tl_out, "\t:: (");
+ if (dump_cond(t->cond, t->cond, 1))
+ fprintf(tl_out, "1");
+ fprintf(tl_out, ") -> goto %s\n", t->name->name);
+ tcnt++;
+}
+
+static void
+printstate(State *b)
+{
+ if (!b || (!tl_verbose && !b->reachable)) return;
+
+ b->reachable = 0; /* print only once */
+ fprintf(tl_out, "%s:\n", b->name->name);
+
+ if (tl_verbose)
+ { fprintf(tl_out, " /* ");
+ dump(b->colors->Other);
+ fprintf(tl_out, " */\n");
+ }
+
+ if (strncmp(b->name->name, "accept", 6) == 0
+ && Max_Red == 0)
+ fprintf(tl_out, "T0%s:\n", &(b->name->name[6]));
+
+ fprintf(tl_out, "\tif\n");
+ tcnt = 0;
+ rev_trans(b->trans);
+ if (!tcnt) fprintf(tl_out, "\t:: false\n");
+ fprintf(tl_out, "\tfi;\n");
+ Total++;
+}
+
+void
+addtrans(Graph *col, char *from, Node *op, char *to)
+{ State *b;
+ Transition *t;
+
+ t = (Transition *) tl_emalloc(sizeof(Transition));
+ t->name = tl_lookup(to);
+ t->cond = Prune(dupnode(op));
+
+ if (tl_verbose)
+ { printf("\n%s <<\t", from); dump(op);
+ printf("\n\t"); dump(t->cond);
+ printf(">> %s\n", t->name->name);
+ }
+ if (t->cond) t->cond = rewrite(t->cond);
+
+ for (b = never; b; b = b->nxt)
+ if (!strcmp(b->name->name, from))
+ { t->nxt = b->trans;
+ b->trans = t;
+ return;
+ }
+ b = (State *) tl_emalloc(sizeof(State));
+ b->name = tl_lookup(from);
+ b->colors = col;
+ b->trans = t;
+ if (!strncmp(from, "accept", 6))
+ b->accepting = 1;
+ b->nxt = never;
+ never = b;
+}
+
+static void
+clr_reach(void)
+{ State *p;
+ for (p = never; p; p = p->nxt)
+ p->reachable = 0;
+ hitsall = 0;
+}
+
+void
+fsm_print(void)
+{ State *b; int cnt1, cnt2=0;
+ extern void put_uform(void);
+
+ if (tl_clutter) clutter();
+
+ b = findstate("T0_init");
+ if (b && (Max_Red == 0))
+ b->accepting = 1;
+
+ mergestates(0);
+ b = findstate("T0_init");
+
+ fprintf(tl_out, "never { /* ");
+ put_uform();
+ fprintf(tl_out, " */\n");
+
+ do {
+ clr_reach();
+ Dfs(b);
+ cnt1 = mergetrans();
+ cnt2 = mergestates(1);
+ if (tl_verbose)
+ printf("/* >>%d,%d<< */\n", cnt1, cnt2);
+ } while (cnt2 > 0);
+
+#ifdef BUCKY
+ buckyballs();
+ clr_reach();
+ Dfs(b);
+#endif
+ if (b && b->accepting)
+ fprintf(tl_out, "accept_init:\n");
+
+ if (!b && !never)
+ { fprintf(tl_out, " 0 /* false */;\n");
+ } else
+ { printstate(b); /* init state must be first */
+ for (b = never; b; b = b->nxt)
+ printstate(b);
+ }
+ if (hitsall)
+ fprintf(tl_out, "accept_all:\n skip\n");
+ fprintf(tl_out, "}\n");
+}
--- /dev/null
+/***** tl_spin: tl_cache.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+typedef struct Cache {
+ Node *before;
+ Node *after;
+ int same;
+ struct Cache *nxt;
+} Cache;
+
+static Cache *stored = (Cache *) 0;
+static unsigned long Caches, CacheHits;
+
+static int ismatch(Node *, Node *);
+extern void fatal(char *, char *);
+int sameform(Node *, Node *);
+
+#if 0
+void
+cache_dump(void)
+{ Cache *d; int nr=0;
+
+ printf("\nCACHE DUMP:\n");
+ for (d = stored; d; d = d->nxt, nr++)
+ { if (d->same) continue;
+ printf("B%3d: ", nr); dump(d->before); printf("\n");
+ printf("A%3d: ", nr); dump(d->after); printf("\n");
+ }
+ printf("============\n");
+}
+#endif
+
+Node *
+in_cache(Node *n)
+{ Cache *d; int nr=0;
+
+ for (d = stored; d; d = d->nxt, nr++)
+ if (isequal(d->before, n))
+ { CacheHits++;
+ if (d->same && ismatch(n, d->before)) return n;
+ return dupnode(d->after);
+ }
+ return ZN;
+}
+
+Node *
+cached(Node *n)
+{ Cache *d;
+ Node *m;
+
+ if (!n) return n;
+ if ((m = in_cache(n)) != ZN)
+ return m;
+
+ Caches++;
+ d = (Cache *) tl_emalloc(sizeof(Cache));
+ d->before = dupnode(n);
+ d->after = Canonical(n); /* n is released */
+
+ if (ismatch(d->before, d->after))
+ { d->same = 1;
+ releasenode(1, d->after);
+ d->after = d->before;
+ }
+ d->nxt = stored;
+ stored = d;
+ return dupnode(d->after);
+}
+
+void
+cache_stats(void)
+{
+ printf("cache stores : %9ld\n", Caches);
+ printf("cache hits : %9ld\n", CacheHits);
+}
+
+void
+releasenode(int all_levels, Node *n)
+{
+ if (!n) return;
+
+ if (all_levels)
+ { releasenode(1, n->lft);
+ n->lft = ZN;
+ releasenode(1, n->rgt);
+ n->rgt = ZN;
+ }
+ tfree((void *) n);
+}
+
+Node *
+tl_nn(int t, Node *ll, Node *rl)
+{ Node *n = (Node *) tl_emalloc(sizeof(Node));
+
+ n->ntyp = (short) t;
+ n->lft = ll;
+ n->rgt = rl;
+
+ return n;
+}
+
+Node *
+getnode(Node *p)
+{ Node *n;
+
+ if (!p) return p;
+
+ n = (Node *) tl_emalloc(sizeof(Node));
+ n->ntyp = p->ntyp;
+ n->sym = p->sym; /* same name */
+ n->lft = p->lft;
+ n->rgt = p->rgt;
+
+ return n;
+}
+
+Node *
+dupnode(Node *n)
+{ Node *d;
+
+ if (!n) return n;
+ d = getnode(n);
+ d->lft = dupnode(n->lft);
+ d->rgt = dupnode(n->rgt);
+ return d;
+}
+
+int
+one_lft(int ntyp, Node *x, Node *in)
+{
+ if (!x) return 1;
+ if (!in) return 0;
+
+ if (sameform(x, in))
+ return 1;
+
+ if (in->ntyp != ntyp)
+ return 0;
+
+ if (one_lft(ntyp, x, in->lft))
+ return 1;
+
+ return one_lft(ntyp, x, in->rgt);
+}
+
+int
+all_lfts(int ntyp, Node *from, Node *in)
+{
+ if (!from) return 1;
+
+ if (from->ntyp != ntyp)
+ return one_lft(ntyp, from, in);
+
+ if (!one_lft(ntyp, from->lft, in))
+ return 0;
+
+ return all_lfts(ntyp, from->rgt, in);
+}
+
+int
+sametrees(int ntyp, Node *a, Node *b)
+{ /* toplevel is an AND or OR */
+ /* both trees are right-linked, but the leafs */
+ /* can be in different places in the two trees */
+
+ if (!all_lfts(ntyp, a, b))
+ return 0;
+
+ return all_lfts(ntyp, b, a);
+}
+
+int /* a better isequal() */
+sameform(Node *a, Node *b)
+{
+ if (!a && !b) return 1;
+ if (!a || !b) return 0;
+ if (a->ntyp != b->ntyp) return 0;
+
+ if (a->sym
+ && b->sym
+ && strcmp(a->sym->name, b->sym->name) != 0)
+ return 0;
+
+ switch (a->ntyp) {
+ case TRUE:
+ case FALSE:
+ return 1;
+ case PREDICATE:
+ if (!a->sym || !b->sym) fatal("sameform...", (char *) 0);
+ return !strcmp(a->sym->name, b->sym->name);
+
+ case NOT:
+#ifdef NXT
+ case NEXT:
+#endif
+ return sameform(a->lft, b->lft);
+ case U_OPER:
+ case V_OPER:
+ if (!sameform(a->lft, b->lft))
+ return 0;
+ if (!sameform(a->rgt, b->rgt))
+ return 0;
+ return 1;
+
+ case AND:
+ case OR: /* the hard case */
+ return sametrees(a->ntyp, a, b);
+
+ default:
+ printf("type: %d\n", a->ntyp);
+ fatal("cannot happen, sameform", (char *) 0);
+ }
+
+ return 0;
+}
+
+int
+isequal(Node *a, Node *b)
+{
+ if (!a && !b)
+ return 1;
+
+ if (!a || !b)
+ { if (!a)
+ { if (b->ntyp == TRUE)
+ return 1;
+ } else
+ { if (a->ntyp == TRUE)
+ return 1;
+ }
+ return 0;
+ }
+ if (a->ntyp != b->ntyp)
+ return 0;
+
+ if (a->sym
+ && b->sym
+ && strcmp(a->sym->name, b->sym->name) != 0)
+ return 0;
+
+ if (isequal(a->lft, b->lft)
+ && isequal(a->rgt, b->rgt))
+ return 1;
+
+ return sameform(a, b);
+}
+
+static int
+ismatch(Node *a, Node *b)
+{
+ if (!a && !b) return 1;
+ if (!a || !b) return 0;
+ if (a->ntyp != b->ntyp) return 0;
+
+ if (a->sym
+ && b->sym
+ && strcmp(a->sym->name, b->sym->name) != 0)
+ return 0;
+
+ if (ismatch(a->lft, b->lft)
+ && ismatch(a->rgt, b->rgt))
+ return 1;
+
+ return 0;
+}
+
+int
+any_term(Node *srch, Node *in)
+{
+ if (!in) return 0;
+
+ if (in->ntyp == AND)
+ return any_term(srch, in->lft) ||
+ any_term(srch, in->rgt);
+
+ return isequal(in, srch);
+}
+
+int
+any_and(Node *srch, Node *in)
+{
+ if (!in) return 0;
+
+ if (srch->ntyp == AND)
+ return any_and(srch->lft, in) &&
+ any_and(srch->rgt, in);
+
+ return any_term(srch, in);
+}
+
+int
+any_lor(Node *srch, Node *in)
+{
+ if (!in) return 0;
+
+ if (in->ntyp == OR)
+ return any_lor(srch, in->lft) ||
+ any_lor(srch, in->rgt);
+
+ return isequal(in, srch);
+}
+
+int
+anywhere(int tok, Node *srch, Node *in)
+{
+ if (!in) return 0;
+
+ switch (tok) {
+ case AND: return any_and(srch, in);
+ case OR: return any_lor(srch, in);
+ case 0: return any_term(srch, in);
+ }
+ fatal("cannot happen, anywhere", (char *) 0);
+ return 0;
+}
--- /dev/null
+/***** tl_spin: tl_lex.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include "tl.h"
+
+static Symbol *symtab[Nhash+1];
+static int tl_lex(void);
+
+extern YYSTYPE tl_yylval;
+extern char yytext[];
+
+#define Token(y) tl_yylval = tl_nn(y,ZN,ZN); return y
+
+static void
+tl_getword(int first, int (*tst)(int))
+{ int i=0; char c;
+
+ yytext[i++] = (char ) first;
+ while (tst(c = tl_Getchar()))
+ yytext[i++] = c;
+ yytext[i] = '\0';
+ tl_UnGetchar();
+}
+
+static int
+tl_follow(int tok, int ifyes, int ifno)
+{ int c;
+ char buf[32];
+ extern int tl_yychar;
+
+ if ((c = tl_Getchar()) == tok)
+ return ifyes;
+ tl_UnGetchar();
+ tl_yychar = c;
+ sprintf(buf, "expected '%c'", tok);
+ tl_yyerror(buf); /* no return from here */
+ return ifno;
+}
+
+int
+tl_yylex(void)
+{ int c = tl_lex();
+#if 0
+ printf("c = %d\n", c);
+#endif
+ return c;
+}
+
+static int
+tl_lex(void)
+{ int c;
+
+ do {
+ c = tl_Getchar();
+ yytext[0] = (char ) c;
+ yytext[1] = '\0';
+
+ if (c <= 0)
+ { Token(';');
+ }
+
+ } while (c == ' '); /* '\t' is removed in tl_main.c */
+
+ if (islower(c))
+ { tl_getword(c, isalnum_);
+ if (strcmp("true", yytext) == 0)
+ { Token(TRUE);
+ }
+ if (strcmp("false", yytext) == 0)
+ { Token(FALSE);
+ }
+ tl_yylval = tl_nn(PREDICATE,ZN,ZN);
+ tl_yylval->sym = tl_lookup(yytext);
+ return PREDICATE;
+ }
+ if (c == '<')
+ { c = tl_Getchar();
+ if (c == '>')
+ { Token(EVENTUALLY);
+ }
+ if (c != '-')
+ { tl_UnGetchar();
+ tl_yyerror("expected '<>' or '<->'");
+ }
+ c = tl_Getchar();
+ if (c == '>')
+ { Token(EQUIV);
+ }
+ tl_UnGetchar();
+ tl_yyerror("expected '<->'");
+ }
+
+ switch (c) {
+ case '/' : c = tl_follow('\\', AND, '/'); break;
+ case '\\': c = tl_follow('/', OR, '\\'); break;
+ case '&' : c = tl_follow('&', AND, '&'); break;
+ case '|' : c = tl_follow('|', OR, '|'); break;
+ case '[' : c = tl_follow(']', ALWAYS, '['); break;
+ case '-' : c = tl_follow('>', IMPLIES, '-'); break;
+ case '!' : c = NOT; break;
+ case 'U' : c = U_OPER; break;
+ case 'V' : c = V_OPER; break;
+#ifdef NXT
+ case 'X' : c = NEXT; break;
+#endif
+ default : break;
+ }
+ Token(c);
+}
+
+Symbol *
+tl_lookup(char *s)
+{ Symbol *sp;
+ int h = hash(s);
+
+ for (sp = symtab[h]; sp; sp = sp->next)
+ if (strcmp(sp->name, s) == 0)
+ return sp;
+
+ sp = (Symbol *) tl_emalloc(sizeof(Symbol));
+ sp->name = (char *) tl_emalloc((int) strlen(s) + 1);
+ strcpy(sp->name, s);
+ sp->next = symtab[h];
+ symtab[h] = sp;
+
+ return sp;
+}
+
+Symbol *
+getsym(Symbol *s)
+{ Symbol *n = (Symbol *) tl_emalloc(sizeof(Symbol));
+
+ n->name = s->name;
+ return n;
+}
--- /dev/null
+/***** tl_spin: tl_main.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+extern FILE *tl_out;
+
+int newstates = 0; /* debugging only */
+int tl_errs = 0;
+int tl_verbose = 0;
+int tl_terse = 0;
+int tl_clutter = 0;
+unsigned long All_Mem = 0;
+
+static char uform[4096];
+static int hasuform=0, cnt=0;
+
+extern void cache_stats(void);
+extern void a_stats(void);
+
+int
+tl_Getchar(void)
+{
+ if (cnt < hasuform)
+ return uform[cnt++];
+ cnt++;
+ return -1;
+}
+
+void
+tl_balanced(void)
+{ int i;
+ int k = 0;
+
+ for (i = 0; i < hasuform; i++)
+ { if (uform[i] == '(')
+ { k++;
+ } else if (uform[i] == ')')
+ { k--;
+ } }
+ if (k != 0)
+ { tl_errs++;
+ tl_yyerror("parentheses not balanced");
+ }
+}
+
+void
+put_uform(void)
+{
+ fprintf(tl_out, "%s", uform);
+}
+
+void
+tl_UnGetchar(void)
+{
+ if (cnt > 0) cnt--;
+}
+
+static void
+tl_stats(void)
+{ extern int Stack_mx;
+ printf("total memory used: %9ld\n", All_Mem);
+ printf("largest stack sze: %9d\n", Stack_mx);
+ cache_stats();
+ a_stats();
+}
+
+int
+tl_main(int argc, char *argv[])
+{ int i;
+ extern int verbose, xspin;
+ tl_verbose = verbose;
+ tl_clutter = 1-xspin; /* use -X -f to turn off uncluttering */
+
+ while (argc > 1 && argv[1][0] == '-')
+ { switch (argv[1][1]) {
+ case 'd': newstates = 1; /* debugging mode */
+ break;
+ case 'f': argc--; argv++;
+ for (i = 0; i < argv[1][i]; i++)
+ { if (argv[1][i] == '\t'
+ || argv[1][i] == '\"'
+ || argv[1][i] == '\n')
+ argv[1][i] = ' ';
+ }
+ strcpy(uform, argv[1]);
+ hasuform = (int) strlen(uform);
+ break;
+ case 'v': tl_verbose++;
+ break;
+ case 'n': tl_terse = 1;
+ break;
+ default : printf("spin -f: saw '-%c'\n", argv[1][1]);
+ goto nogood;
+ }
+ argc--; argv++;
+ }
+ if (hasuform == 0)
+ {
+nogood: printf("usage:\tspin [-v] [-n] -f formula\n");
+ printf(" -v verbose translation\n");
+ printf(" -n normalize tl formula and exit\n");
+ exit(1);
+ }
+ tl_balanced();
+
+ if (tl_errs == 0)
+ tl_parse();
+
+ if (tl_verbose) tl_stats();
+ return tl_errs;
+}
+
+#define Binop(a) \
+ fprintf(tl_out, "("); \
+ dump(n->lft); \
+ fprintf(tl_out, a); \
+ dump(n->rgt); \
+ fprintf(tl_out, ")")
+
+void
+dump(Node *n)
+{
+ if (!n) return;
+
+ switch(n->ntyp) {
+ case OR: Binop(" || "); break;
+ case AND: Binop(" && "); break;
+ case U_OPER: Binop(" U "); break;
+ case V_OPER: Binop(" V "); break;
+#ifdef NXT
+ case NEXT:
+ fprintf(tl_out, "X");
+ fprintf(tl_out, " (");
+ dump(n->lft);
+ fprintf(tl_out, ")");
+ break;
+#endif
+ case NOT:
+ fprintf(tl_out, "!");
+ fprintf(tl_out, " (");
+ dump(n->lft);
+ fprintf(tl_out, ")");
+ break;
+ case FALSE:
+ fprintf(tl_out, "false");
+ break;
+ case TRUE:
+ fprintf(tl_out, "true");
+ break;
+ case PREDICATE:
+ fprintf(tl_out, "(%s)", n->sym->name);
+ break;
+ case -1:
+ fprintf(tl_out, " D ");
+ break;
+ default:
+ printf("Unknown token: ");
+ tl_explain(n->ntyp);
+ break;
+ }
+}
+
+void
+tl_explain(int n)
+{
+ switch (n) {
+ case ALWAYS: printf("[]"); break;
+ case EVENTUALLY: printf("<>"); break;
+ case IMPLIES: printf("->"); break;
+ case EQUIV: printf("<->"); break;
+ case PREDICATE: printf("predicate"); break;
+ case OR: printf("||"); break;
+ case AND: printf("&&"); break;
+ case NOT: printf("!"); break;
+ case U_OPER: printf("U"); break;
+ case V_OPER: printf("V"); break;
+#ifdef NXT
+ case NEXT: printf("X"); break;
+#endif
+ case TRUE: printf("true"); break;
+ case FALSE: printf("false"); break;
+ case ';': printf("end of formula"); break;
+ default: printf("%c", n); break;
+ }
+}
+
+static void
+tl_non_fatal(char *s1, char *s2)
+{ extern int tl_yychar;
+ int i;
+
+ printf("tl_spin: ");
+ if (s2)
+ printf(s1, s2);
+ else
+ printf(s1);
+ if (tl_yychar != -1 && tl_yychar != 0)
+ { printf(", saw '");
+ tl_explain(tl_yychar);
+ printf("'");
+ }
+ printf("\ntl_spin: %s\n---------", uform);
+ for (i = 0; i < cnt; i++)
+ printf("-");
+ printf("^\n");
+ fflush(stdout);
+ tl_errs++;
+}
+
+void
+tl_yyerror(char *s1)
+{
+ Fatal(s1, (char *) 0);
+}
+
+void
+Fatal(char *s1, char *s2)
+{
+ tl_non_fatal(s1, s2);
+ /* tl_stats(); */
+ exit(1);
+}
--- /dev/null
+/***** tl_spin: tl_mem.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+#if 1
+#define log(e, u, d) event[e][(int) u] += (long) d;
+#else
+#define log(e, u, d)
+#endif
+
+#define A_LARGE 80
+#define A_USER 0x55000000
+#define NOTOOBIG 32768
+
+#define POOL 0
+#define ALLOC 1
+#define FREE 2
+#define NREVENT 3
+
+extern unsigned long All_Mem;
+extern int tl_verbose;
+
+union M {
+ long size;
+ union M *link;
+};
+
+static union M *freelist[A_LARGE];
+static long req[A_LARGE];
+static long event[NREVENT][A_LARGE];
+
+void *
+tl_emalloc(int U)
+{ union M *m;
+ long r, u;
+ void *rp;
+
+ u = (long) ((U-1)/sizeof(union M) + 2);
+
+ if (u >= A_LARGE)
+ { log(ALLOC, 0, 1);
+ if (tl_verbose)
+ printf("tl_spin: memalloc %ld bytes\n", u);
+ m = (union M *) emalloc((int) u*sizeof(union M));
+ All_Mem += (unsigned long) u*sizeof(union M);
+ } else
+ { if (!freelist[u])
+ { r = req[u] += req[u] ? req[u] : 1;
+ if (r >= NOTOOBIG)
+ r = req[u] = NOTOOBIG;
+ log(POOL, u, r);
+ freelist[u] = (union M *)
+ emalloc((int) r*u*sizeof(union M));
+ All_Mem += (unsigned long) r*u*sizeof(union M);
+ m = freelist[u] + (r-2)*u;
+ for ( ; m >= freelist[u]; m -= u)
+ m->link = m+u;
+ }
+ log(ALLOC, u, 1);
+ m = freelist[u];
+ freelist[u] = m->link;
+ }
+ m->size = (u|A_USER);
+
+ for (r = 1; r < u; )
+ (&m->size)[r++] = 0;
+
+ rp = (void *) (m+1);
+ memset(rp, 0, U);
+ return rp;
+}
+
+void
+tfree(void *v)
+{ union M *m = (union M *) v;
+ long u;
+
+ --m;
+ if ((m->size&0xFF000000) != A_USER)
+ Fatal("releasing a free block", (char *)0);
+
+ u = (m->size &= 0xFFFFFF);
+ if (u >= A_LARGE)
+ { log(FREE, 0, 1);
+ /* free(m); */
+ } else
+ { log(FREE, u, 1);
+ m->link = freelist[u];
+ freelist[u] = m;
+ }
+}
+
+void
+a_stats(void)
+{ long p, a, f;
+ int i;
+
+ printf(" size\t pool\tallocs\t frees\n");
+ for (i = 0; i < A_LARGE; i++)
+ { p = event[POOL][i];
+ a = event[ALLOC][i];
+ f = event[FREE][i];
+
+ if(p|a|f)
+ printf("%5d\t%6ld\t%6ld\t%6ld\n",
+ i, p, a, f);
+ }
+}
--- /dev/null
+/***** tl_spin: tl_parse.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+extern int tl_yylex(void);
+extern int tl_verbose, tl_errs;
+
+int tl_yychar = 0;
+YYSTYPE tl_yylval;
+
+static Node *tl_formula(void);
+static Node *tl_factor(void);
+static Node *tl_level(int);
+
+static int prec[2][4] = {
+ { U_OPER, V_OPER, 0, 0 }, /* left associative */
+ { OR, AND, IMPLIES, EQUIV, }, /* left associative */
+};
+
+static Node *
+tl_factor(void)
+{ Node *ptr = ZN;
+
+ switch (tl_yychar) {
+ case '(':
+ ptr = tl_formula();
+ if (tl_yychar != ')')
+ tl_yyerror("expected ')'");
+ tl_yychar = tl_yylex();
+ break;
+ case NOT:
+ ptr = tl_yylval;
+ tl_yychar = tl_yylex();
+ ptr->lft = tl_factor();
+ ptr = push_negation(ptr);
+ break;
+ case ALWAYS:
+ tl_yychar = tl_yylex();
+
+ ptr = tl_factor();
+#ifndef NO_OPT
+ if (ptr->ntyp == FALSE
+ || ptr->ntyp == TRUE)
+ break; /* [] false == false */
+
+ if (ptr->ntyp == V_OPER)
+ { if (ptr->lft->ntyp == FALSE)
+ break; /* [][]p = []p */
+
+ ptr = ptr->rgt; /* [] (p V q) = [] q */
+ }
+#endif
+ ptr = tl_nn(V_OPER, False, ptr);
+ break;
+#ifdef NXT
+ case NEXT:
+ tl_yychar = tl_yylex();
+ ptr = tl_factor();
+ if (ptr->ntyp == TRUE)
+ break; /* X true = true */
+ ptr = tl_nn(NEXT, ptr, ZN);
+ break;
+#endif
+ case EVENTUALLY:
+ tl_yychar = tl_yylex();
+
+ ptr = tl_factor();
+#ifndef NO_OPT
+ if (ptr->ntyp == TRUE
+ || ptr->ntyp == FALSE)
+ break; /* <> true == true */
+
+ if (ptr->ntyp == U_OPER
+ && ptr->lft->ntyp == TRUE)
+ break; /* <><>p = <>p */
+
+ if (ptr->ntyp == U_OPER)
+ { /* <> (p U q) = <> q */
+ ptr = ptr->rgt;
+ /* fall thru */
+ }
+#endif
+ ptr = tl_nn(U_OPER, True, ptr);
+
+ break;
+ case PREDICATE:
+ ptr = tl_yylval;
+ tl_yychar = tl_yylex();
+ break;
+ case TRUE:
+ case FALSE:
+ ptr = tl_yylval;
+ tl_yychar = tl_yylex();
+ break;
+ }
+ if (!ptr) tl_yyerror("expected predicate");
+#if 0
+ printf("factor: ");
+ tl_explain(ptr->ntyp);
+ printf("\n");
+#endif
+ return ptr;
+}
+
+static Node *
+bin_simpler(Node *ptr)
+{ Node *a, *b;
+
+ if (ptr)
+ switch (ptr->ntyp) {
+ case U_OPER:
+#ifndef NO_OPT
+ if (ptr->rgt->ntyp == TRUE
+ || ptr->rgt->ntyp == FALSE
+ || ptr->lft->ntyp == FALSE)
+ { ptr = ptr->rgt;
+ break;
+ }
+ if (isequal(ptr->lft, ptr->rgt))
+ { /* p U p = p */
+ ptr = ptr->rgt;
+ break;
+ }
+ if (ptr->lft->ntyp == U_OPER
+ && isequal(ptr->lft->lft, ptr->rgt))
+ { /* (p U q) U p = (q U p) */
+ ptr->lft = ptr->lft->rgt;
+ break;
+ }
+ if (ptr->rgt->ntyp == U_OPER
+ && ptr->rgt->lft->ntyp == TRUE)
+ { /* p U (T U q) = (T U q) */
+ ptr = ptr->rgt;
+ break;
+ }
+#ifdef NXT
+ /* X p U X q == X (p U q) */
+ if (ptr->rgt->ntyp == NEXT
+ && ptr->lft->ntyp == NEXT)
+ { ptr = tl_nn(NEXT,
+ tl_nn(U_OPER,
+ ptr->lft->lft,
+ ptr->rgt->lft), ZN);
+ }
+#endif
+#endif
+ break;
+ case V_OPER:
+#ifndef NO_OPT
+ if (ptr->rgt->ntyp == FALSE
+ || ptr->rgt->ntyp == TRUE
+ || ptr->lft->ntyp == TRUE)
+ { ptr = ptr->rgt;
+ break;
+ }
+ if (isequal(ptr->lft, ptr->rgt))
+ { /* p V p = p */
+ ptr = ptr->rgt;
+ break;
+ }
+ /* F V (p V q) == F V q */
+ if (ptr->lft->ntyp == FALSE
+ && ptr->rgt->ntyp == V_OPER)
+ { ptr->rgt = ptr->rgt->rgt;
+ break;
+ }
+ /* p V (F V q) == F V q */
+ if (ptr->rgt->ntyp == V_OPER
+ && ptr->rgt->lft->ntyp == FALSE)
+ { ptr->lft = False;
+ ptr->rgt = ptr->rgt->rgt;
+ break;
+ }
+#endif
+ break;
+ case IMPLIES:
+#ifndef NO_OPT
+ if (isequal(ptr->lft, ptr->rgt))
+ { ptr = True;
+ break;
+ }
+#endif
+ ptr = tl_nn(OR, Not(ptr->lft), ptr->rgt);
+ ptr = rewrite(ptr);
+ break;
+ case EQUIV:
+#ifndef NO_OPT
+ if (isequal(ptr->lft, ptr->rgt))
+ { ptr = True;
+ break;
+ }
+#endif
+ a = rewrite(tl_nn(AND,
+ dupnode(ptr->lft),
+ dupnode(ptr->rgt)));
+ b = rewrite(tl_nn(AND,
+ Not(ptr->lft),
+ Not(ptr->rgt)));
+ ptr = tl_nn(OR, a, b);
+ ptr = rewrite(ptr);
+ break;
+ case AND:
+#ifndef NO_OPT
+ /* p && (q U p) = p */
+ if (ptr->rgt->ntyp == U_OPER
+ && isequal(ptr->rgt->rgt, ptr->lft))
+ { ptr = ptr->lft;
+ break;
+ }
+ if (ptr->lft->ntyp == U_OPER
+ && isequal(ptr->lft->rgt, ptr->rgt))
+ { ptr = ptr->rgt;
+ break;
+ }
+
+ /* p && (q V p) == q V p */
+ if (ptr->rgt->ntyp == V_OPER
+ && isequal(ptr->rgt->rgt, ptr->lft))
+ { ptr = ptr->rgt;
+ break;
+ }
+ if (ptr->lft->ntyp == V_OPER
+ && isequal(ptr->lft->rgt, ptr->rgt))
+ { ptr = ptr->lft;
+ break;
+ }
+
+ /* (p U q) && (r U q) = (p && r) U q*/
+ if (ptr->rgt->ntyp == U_OPER
+ && ptr->lft->ntyp == U_OPER
+ && isequal(ptr->rgt->rgt, ptr->lft->rgt))
+ { ptr = tl_nn(U_OPER,
+ tl_nn(AND, ptr->lft->lft, ptr->rgt->lft),
+ ptr->lft->rgt);
+ break;
+ }
+
+ /* (p V q) && (p V r) = p V (q && r) */
+ if (ptr->rgt->ntyp == V_OPER
+ && ptr->lft->ntyp == V_OPER
+ && isequal(ptr->rgt->lft, ptr->lft->lft))
+ { ptr = tl_nn(V_OPER,
+ ptr->rgt->lft,
+ tl_nn(AND, ptr->lft->rgt, ptr->rgt->rgt));
+ break;
+ }
+#ifdef NXT
+ /* X p && X q == X (p && q) */
+ if (ptr->rgt->ntyp == NEXT
+ && ptr->lft->ntyp == NEXT)
+ { ptr = tl_nn(NEXT,
+ tl_nn(AND,
+ ptr->rgt->lft,
+ ptr->lft->lft), ZN);
+ break;
+ }
+#endif
+
+ if (isequal(ptr->lft, ptr->rgt) /* (p && p) == p */
+ || ptr->rgt->ntyp == FALSE /* (p && F) == F */
+ || ptr->lft->ntyp == TRUE) /* (T && p) == p */
+ { ptr = ptr->rgt;
+ break;
+ }
+ if (ptr->rgt->ntyp == TRUE /* (p && T) == p */
+ || ptr->lft->ntyp == FALSE) /* (F && p) == F */
+ { ptr = ptr->lft;
+ break;
+ }
+
+ /* (p V q) && (r U q) == p V q */
+ if (ptr->rgt->ntyp == U_OPER
+ && ptr->lft->ntyp == V_OPER
+ && isequal(ptr->lft->rgt, ptr->rgt->rgt))
+ { ptr = ptr->lft;
+ break;
+ }
+#endif
+ break;
+
+ case OR:
+#ifndef NO_OPT
+ /* p || (q U p) == q U p */
+ if (ptr->rgt->ntyp == U_OPER
+ && isequal(ptr->rgt->rgt, ptr->lft))
+ { ptr = ptr->rgt;
+ break;
+ }
+
+ /* p || (q V p) == p */
+ if (ptr->rgt->ntyp == V_OPER
+ && isequal(ptr->rgt->rgt, ptr->lft))
+ { ptr = ptr->lft;
+ break;
+ }
+
+ /* (p U q) || (p U r) = p U (q || r) */
+ if (ptr->rgt->ntyp == U_OPER
+ && ptr->lft->ntyp == U_OPER
+ && isequal(ptr->rgt->lft, ptr->lft->lft))
+ { ptr = tl_nn(U_OPER,
+ ptr->rgt->lft,
+ tl_nn(OR, ptr->lft->rgt, ptr->rgt->rgt));
+ break;
+ }
+
+ if (isequal(ptr->lft, ptr->rgt) /* (p || p) == p */
+ || ptr->rgt->ntyp == FALSE /* (p || F) == p */
+ || ptr->lft->ntyp == TRUE) /* (T || p) == T */
+ { ptr = ptr->lft;
+ break;
+ }
+ if (ptr->rgt->ntyp == TRUE /* (p || T) == T */
+ || ptr->lft->ntyp == FALSE) /* (F || p) == p */
+ { ptr = ptr->rgt;
+ break;
+ }
+
+ /* (p V q) || (r V q) = (p || r) V q */
+ if (ptr->rgt->ntyp == V_OPER
+ && ptr->lft->ntyp == V_OPER
+ && isequal(ptr->lft->rgt, ptr->rgt->rgt))
+ { ptr = tl_nn(V_OPER,
+ tl_nn(OR, ptr->lft->lft, ptr->rgt->lft),
+ ptr->rgt->rgt);
+ break;
+ }
+
+ /* (p V q) || (r U q) == r U q */
+ if (ptr->rgt->ntyp == U_OPER
+ && ptr->lft->ntyp == V_OPER
+ && isequal(ptr->lft->rgt, ptr->rgt->rgt))
+ { ptr = ptr->rgt;
+ break;
+ }
+#endif
+ break;
+ }
+ return ptr;
+}
+
+static Node *
+tl_level(int nr)
+{ int i; Node *ptr = ZN;
+
+ if (nr < 0)
+ return tl_factor();
+
+ ptr = tl_level(nr-1);
+again:
+ for (i = 0; i < 4; i++)
+ if (tl_yychar == prec[nr][i])
+ { tl_yychar = tl_yylex();
+ ptr = tl_nn(prec[nr][i],
+ ptr, tl_level(nr-1));
+ ptr = bin_simpler(ptr);
+ goto again;
+ }
+ if (!ptr) tl_yyerror("syntax error");
+#if 0
+ printf("level %d: ", nr);
+ tl_explain(ptr->ntyp);
+ printf("\n");
+#endif
+ return ptr;
+}
+
+static Node *
+tl_formula(void)
+{ tl_yychar = tl_yylex();
+ return tl_level(1); /* 2 precedence levels, 1 and 0 */
+}
+
+void
+tl_parse(void)
+{ Node *n = tl_formula();
+ if (tl_verbose)
+ { printf("formula: ");
+ dump(n);
+ printf("\n");
+ }
+ if (tl_Getchar() != -1)
+ { tl_yyerror("syntax error");
+ tl_errs++;
+ return;
+ }
+ trans(n);
+}
--- /dev/null
+/***** tl_spin: tl_rewrt.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+extern int tl_verbose;
+
+static Node *can = ZN;
+
+Node *
+right_linked(Node *n)
+{
+ if (!n) return n;
+
+ if (n->ntyp == AND || n->ntyp == OR)
+ while (n->lft && n->lft->ntyp == n->ntyp)
+ { Node *tmp = n->lft;
+ n->lft = tmp->rgt;
+ tmp->rgt = n;
+ n = tmp;
+ }
+
+ n->lft = right_linked(n->lft);
+ n->rgt = right_linked(n->rgt);
+
+ return n;
+}
+
+Node *
+canonical(Node *n)
+{ Node *m; /* assumes input is right_linked */
+
+ if (!n) return n;
+ if ((m = in_cache(n)) != ZN)
+ return m;
+
+ n->rgt = canonical(n->rgt);
+ n->lft = canonical(n->lft);
+
+ return cached(n);
+}
+
+Node *
+push_negation(Node *n)
+{ Node *m;
+
+ Assert(n->ntyp == NOT, n->ntyp);
+
+ switch (n->lft->ntyp) {
+ case TRUE:
+ Debug("!true => false\n");
+ releasenode(0, n->lft);
+ n->lft = ZN;
+ n->ntyp = FALSE;
+ break;
+ case FALSE:
+ Debug("!false => true\n");
+ releasenode(0, n->lft);
+ n->lft = ZN;
+ n->ntyp = TRUE;
+ break;
+ case NOT:
+ Debug("!!p => p\n");
+ m = n->lft->lft;
+ releasenode(0, n->lft);
+ n->lft = ZN;
+ releasenode(0, n);
+ n = m;
+ break;
+ case V_OPER:
+ Debug("!(p V q) => (!p U !q)\n");
+ n->ntyp = U_OPER;
+ goto same;
+ case U_OPER:
+ Debug("!(p U q) => (!p V !q)\n");
+ n->ntyp = V_OPER;
+ goto same;
+#ifdef NXT
+ case NEXT:
+ Debug("!X -> X!\n");
+ n->ntyp = NEXT;
+ n->lft->ntyp = NOT;
+ n->lft = push_negation(n->lft);
+ break;
+#endif
+ case AND:
+ Debug("!(p && q) => !p || !q\n");
+ n->ntyp = OR;
+ goto same;
+ case OR:
+ Debug("!(p || q) => !p && !q\n");
+ n->ntyp = AND;
+
+same: m = n->lft->rgt;
+ n->lft->rgt = ZN;
+
+ n->rgt = Not(m);
+ n->lft->ntyp = NOT;
+ m = n->lft;
+ n->lft = push_negation(m);
+ break;
+ }
+
+ return rewrite(n);
+}
+
+static void
+addcan(int tok, Node *n)
+{ Node *m, *prev = ZN;
+ Node **ptr;
+ Node *N;
+ Symbol *s, *t; int cmp;
+
+ if (!n) return;
+
+ if (n->ntyp == tok)
+ { addcan(tok, n->rgt);
+ addcan(tok, n->lft);
+ return;
+ }
+
+ N = dupnode(n);
+ if (!can)
+ { can = N;
+ return;
+ }
+
+ s = DoDump(N);
+ if (can->ntyp != tok) /* only one element in list so far */
+ { ptr = &can;
+ goto insert;
+ }
+
+ /* there are at least 2 elements in list */
+ prev = ZN;
+ for (m = can; m->ntyp == tok && m->rgt; prev = m, m = m->rgt)
+ { t = DoDump(m->lft);
+ if (t != ZS)
+ cmp = strcmp(s->name, t->name);
+ else
+ cmp = 0;
+ if (cmp == 0) /* duplicate */
+ return;
+ if (cmp < 0)
+ { if (!prev)
+ { can = tl_nn(tok, N, can);
+ return;
+ } else
+ { ptr = &(prev->rgt);
+ goto insert;
+ } } }
+
+ /* new entry goes at the end of the list */
+ ptr = &(prev->rgt);
+insert:
+ t = DoDump(*ptr);
+ cmp = strcmp(s->name, t->name);
+ if (cmp == 0) /* duplicate */
+ return;
+ if (cmp < 0)
+ *ptr = tl_nn(tok, N, *ptr);
+ else
+ *ptr = tl_nn(tok, *ptr, N);
+}
+
+static void
+marknode(int tok, Node *m)
+{
+ if (m->ntyp != tok)
+ { releasenode(0, m->rgt);
+ m->rgt = ZN;
+ }
+ m->ntyp = -1;
+}
+
+Node *
+Canonical(Node *n)
+{ Node *m, *p, *k1, *k2, *prev, *dflt = ZN;
+ int tok;
+
+ if (!n) return n;
+
+ tok = n->ntyp;
+ if (tok != AND && tok != OR)
+ return n;
+
+ can = ZN;
+ addcan(tok, n);
+#if 0
+ Debug("\nA0: "); Dump(can);
+ Debug("\nA1: "); Dump(n); Debug("\n");
+#endif
+ releasenode(1, n);
+
+ /* mark redundant nodes */
+ if (tok == AND)
+ { for (m = can; m; m = (m->ntyp == AND) ? m->rgt : ZN)
+ { k1 = (m->ntyp == AND) ? m->lft : m;
+ if (k1->ntyp == TRUE)
+ { marknode(AND, m);
+ dflt = True;
+ continue;
+ }
+ if (k1->ntyp == FALSE)
+ { releasenode(1, can);
+ can = False;
+ goto out;
+ } }
+ for (m = can; m; m = (m->ntyp == AND) ? m->rgt : ZN)
+ for (p = can; p; p = (p->ntyp == AND) ? p->rgt : ZN)
+ { if (p == m
+ || p->ntyp == -1
+ || m->ntyp == -1)
+ continue;
+ k1 = (m->ntyp == AND) ? m->lft : m;
+ k2 = (p->ntyp == AND) ? p->lft : p;
+
+ if (isequal(k1, k2))
+ { marknode(AND, p);
+ continue;
+ }
+ if (anywhere(OR, k1, k2))
+ { marknode(AND, p);
+ continue;
+ }
+ } }
+ if (tok == OR)
+ { for (m = can; m; m = (m->ntyp == OR) ? m->rgt : ZN)
+ { k1 = (m->ntyp == OR) ? m->lft : m;
+ if (k1->ntyp == FALSE)
+ { marknode(OR, m);
+ dflt = False;
+ continue;
+ }
+ if (k1->ntyp == TRUE)
+ { releasenode(1, can);
+ can = True;
+ goto out;
+ } }
+ for (m = can; m; m = (m->ntyp == OR) ? m->rgt : ZN)
+ for (p = can; p; p = (p->ntyp == OR) ? p->rgt : ZN)
+ { if (p == m
+ || p->ntyp == -1
+ || m->ntyp == -1)
+ continue;
+ k1 = (m->ntyp == OR) ? m->lft : m;
+ k2 = (p->ntyp == OR) ? p->lft : p;
+
+ if (isequal(k1, k2))
+ { marknode(OR, p);
+ continue;
+ }
+ if (anywhere(AND, k1, k2))
+ { marknode(OR, p);
+ continue;
+ }
+ } }
+ for (m = can, prev = ZN; m; ) /* remove marked nodes */
+ { if (m->ntyp == -1)
+ { k2 = m->rgt;
+ releasenode(0, m);
+ if (!prev)
+ { m = can = can->rgt;
+ } else
+ { m = prev->rgt = k2;
+ /* if deleted the last node in a chain */
+ if (!prev->rgt && prev->lft
+ && (prev->ntyp == AND || prev->ntyp == OR))
+ { k1 = prev->lft;
+ prev->ntyp = prev->lft->ntyp;
+ prev->sym = prev->lft->sym;
+ prev->rgt = prev->lft->rgt;
+ prev->lft = prev->lft->lft;
+ releasenode(0, k1);
+ }
+ }
+ continue;
+ }
+ prev = m;
+ m = m->rgt;
+ }
+out:
+#if 0
+ Debug("A2: "); Dump(can); Debug("\n");
+#endif
+ if (!can)
+ { if (!dflt)
+ fatal("cannot happen, Canonical", (char *) 0);
+ return dflt;
+ }
+
+ return can;
+}
--- /dev/null
+/***** tl_spin: tl_trans.c *****/
+
+/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */
+/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */
+
+#include "tl.h"
+
+extern FILE *tl_out;
+extern int tl_errs, tl_verbose, tl_terse, newstates;
+
+int Stack_mx=0, Max_Red=0, Total=0;
+
+static Mapping *Mapped = (Mapping *) 0;
+static Graph *Nodes_Set = (Graph *) 0;
+static Graph *Nodes_Stack = (Graph *) 0;
+
+static char dumpbuf[2048];
+static int Red_cnt = 0;
+static int Lab_cnt = 0;
+static int Base = 0;
+static int Stack_sz = 0;
+
+static Graph *findgraph(char *);
+static Graph *pop_stack(void);
+static Node *Duplicate(Node *);
+static Node *flatten(Node *);
+static Symbol *catSlist(Symbol *, Symbol *);
+static Symbol *dupSlist(Symbol *);
+static char *newname(void);
+static int choueka(Graph *, int);
+static int not_new(Graph *);
+static int set_prefix(char *, int, Graph *);
+static void Addout(char *, char *);
+static void fsm_trans(Graph *, int, char *);
+static void mkbuchi(void);
+static void expand_g(Graph *);
+static void fixinit(Node *);
+static void liveness(Node *);
+static void mk_grn(Node *);
+static void mk_red(Node *);
+static void ng(Symbol *, Symbol *, Node *, Node *, Node *);
+static void push_stack(Graph *);
+static void sdump(Node *);
+
+static void
+dump_graph(Graph *g)
+{ Node *n1;
+
+ printf("\n\tnew:\t");
+ for (n1 = g->New; n1; n1 = n1->nxt)
+ { dump(n1); printf(", "); }
+ printf("\n\told:\t");
+ for (n1 = g->Old; n1; n1 = n1->nxt)
+ { dump(n1); printf(", "); }
+ printf("\n\tnxt:\t");
+ for (n1 = g->Next; n1; n1 = n1->nxt)
+ { dump(n1); printf(", "); }
+ printf("\n\tother:\t");
+ for (n1 = g->Other; n1; n1 = n1->nxt)
+ { dump(n1); printf(", "); }
+ printf("\n");
+}
+
+static void
+push_stack(Graph *g)
+{
+ if (!g) return;
+
+ g->nxt = Nodes_Stack;
+ Nodes_Stack = g;
+ if (tl_verbose)
+ { Symbol *z;
+ printf("\nPush %s, from ", g->name->name);
+ for (z = g->incoming; z; z = z->next)
+ printf("%s, ", z->name);
+ dump_graph(g);
+ }
+ Stack_sz++;
+ if (Stack_sz > Stack_mx) Stack_mx = Stack_sz;
+}
+
+static Graph *
+pop_stack(void)
+{ Graph *g = Nodes_Stack;
+
+ if (g) Nodes_Stack = g->nxt;
+
+ Stack_sz--;
+
+ return g;
+}
+
+static char *
+newname(void)
+{ static int cnt = 0;
+ static char buf[32];
+ sprintf(buf, "S%d", cnt++);
+ return buf;
+}
+
+static int
+has_clause(int tok, Graph *p, Node *n)
+{ Node *q, *qq;
+
+ switch (n->ntyp) {
+ case AND:
+ return has_clause(tok, p, n->lft) &&
+ has_clause(tok, p, n->rgt);
+ case OR:
+ return has_clause(tok, p, n->lft) ||
+ has_clause(tok, p, n->rgt);
+ }
+
+ for (q = p->Other; q; q = q->nxt)
+ { qq = right_linked(q);
+ if (anywhere(tok, n, qq))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+mk_grn(Node *n)
+{ Graph *p;
+
+ n = right_linked(n);
+more:
+ for (p = Nodes_Set; p; p = p->nxt)
+ if (p->outgoing
+ && has_clause(AND, p, n))
+ { p->isgrn[p->grncnt++] =
+ (unsigned char) Red_cnt;
+ Lab_cnt++;
+ }
+
+ if (n->ntyp == U_OPER) /* 3.4.0 */
+ { n = n->rgt;
+ goto more;
+ }
+}
+
+static void
+mk_red(Node *n)
+{ Graph *p;
+
+ n = right_linked(n);
+ for (p = Nodes_Set; p; p = p->nxt)
+ { if (p->outgoing
+ && has_clause(0, p, n))
+ { if (p->redcnt >= 63)
+ Fatal("too many Untils", (char *)0);
+ p->isred[p->redcnt++] =
+ (unsigned char) Red_cnt;
+ Lab_cnt++; Max_Red = Red_cnt;
+ } }
+}
+
+static void
+liveness(Node *n)
+{
+ if (n)
+ switch (n->ntyp) {
+#ifdef NXT
+ case NEXT:
+ liveness(n->lft);
+ break;
+#endif
+ case U_OPER:
+ Red_cnt++;
+ mk_red(n);
+ mk_grn(n->rgt);
+ /* fall through */
+ case V_OPER:
+ case OR: case AND:
+ liveness(n->lft);
+ liveness(n->rgt);
+ break;
+ }
+}
+
+static Graph *
+findgraph(char *nm)
+{ Graph *p;
+ Mapping *m;
+
+ for (p = Nodes_Set; p; p = p->nxt)
+ if (!strcmp(p->name->name, nm))
+ return p;
+ for (m = Mapped; m; m = m->nxt)
+ if (strcmp(m->from, nm) == 0)
+ return m->to;
+
+ printf("warning: node %s not found\n", nm);
+ return (Graph *) 0;
+}
+
+static void
+Addout(char *to, char *from)
+{ Graph *p = findgraph(from);
+ Symbol *s;
+
+ if (!p) return;
+ s = getsym(tl_lookup(to));
+ s->next = p->outgoing;
+ p->outgoing = s;
+}
+
+#ifdef NXT
+int
+only_nxt(Node *n)
+{
+ switch (n->ntyp) {
+ case NEXT:
+ return 1;
+ case OR:
+ case AND:
+ return only_nxt(n->rgt) && only_nxt(n->lft);
+ default:
+ return 0;
+ }
+}
+#endif
+
+int
+dump_cond(Node *pp, Node *r, int first)
+{ Node *q;
+ int frst = first;
+
+ if (!pp) return frst;
+
+ q = dupnode(pp);
+ q = rewrite(q);
+
+ if (q->ntyp == PREDICATE
+ || q->ntyp == NOT
+#ifndef NXT
+ || q->ntyp == OR
+#endif
+ || q->ntyp == FALSE)
+ { if (!frst) fprintf(tl_out, " && ");
+ dump(q);
+ frst = 0;
+#ifdef NXT
+ } else if (q->ntyp == OR)
+ { if (!frst) fprintf(tl_out, " && ");
+ fprintf(tl_out, "((");
+ frst = dump_cond(q->lft, r, 1);
+
+ if (!frst)
+ fprintf(tl_out, ") || (");
+ else
+ { if (only_nxt(q->lft))
+ { fprintf(tl_out, "1))");
+ return 0;
+ }
+ }
+
+ frst = dump_cond(q->rgt, r, 1);
+
+ if (frst)
+ { if (only_nxt(q->rgt))
+ fprintf(tl_out, "1");
+ else
+ fprintf(tl_out, "0");
+ frst = 0;
+ }
+
+ fprintf(tl_out, "))");
+#endif
+ } else if (q->ntyp == V_OPER
+ && !anywhere(AND, q->rgt, r))
+ { frst = dump_cond(q->rgt, r, frst);
+ } else if (q->ntyp == AND)
+ {
+ frst = dump_cond(q->lft, r, frst);
+ frst = dump_cond(q->rgt, r, frst);
+ }
+
+ return frst;
+}
+
+static int
+choueka(Graph *p, int count)
+{ int j, k, incr_cnt = 0;
+
+ for (j = count; j <= Max_Red; j++) /* for each acceptance class */
+ { int delta = 0;
+
+ /* is state p labeled Grn-j OR not Red-j ? */
+
+ for (k = 0; k < (int) p->grncnt; k++)
+ if (p->isgrn[k] == j)
+ { delta = 1;
+ break;
+ }
+ if (delta)
+ { incr_cnt++;
+ continue;
+ }
+ for (k = 0; k < (int) p->redcnt; k++)
+ if (p->isred[k] == j)
+ { delta = 1;
+ break;
+ }
+
+ if (delta) break;
+
+ incr_cnt++;
+ }
+ return incr_cnt;
+}
+
+static int
+set_prefix(char *pref, int count, Graph *r2)
+{ int incr_cnt = 0; /* acceptance class 'count' */
+
+ if (Lab_cnt == 0
+ || Max_Red == 0)
+ sprintf(pref, "accept"); /* new */
+ else if (count >= Max_Red)
+ sprintf(pref, "T0"); /* cycle */
+ else
+ { incr_cnt = choueka(r2, count+1);
+ if (incr_cnt + count >= Max_Red)
+ sprintf(pref, "accept"); /* last hop */
+ else
+ sprintf(pref, "T%d", count+incr_cnt);
+ }
+ return incr_cnt;
+}
+
+static void
+fsm_trans(Graph *p, int count, char *curnm)
+{ Graph *r;
+ Symbol *s;
+ char prefix[128], nwnm[256];
+
+ if (!p->outgoing)
+ addtrans(p, curnm, False, "accept_all");
+
+ for (s = p->outgoing; s; s = s->next)
+ { r = findgraph(s->name);
+ if (!r) continue;
+ if (r->outgoing)
+ { (void) set_prefix(prefix, count, r);
+ sprintf(nwnm, "%s_%s", prefix, s->name);
+ } else
+ strcpy(nwnm, "accept_all");
+
+ if (tl_verbose)
+ { printf("maxred=%d, count=%d, curnm=%s, nwnm=%s ",
+ Max_Red, count, curnm, nwnm);
+ printf("(greencnt=%d,%d, redcnt=%d,%d)\n",
+ r->grncnt, r->isgrn[0],
+ r->redcnt, r->isred[0]);
+ }
+ addtrans(p, curnm, r->Old, nwnm);
+ }
+}
+
+static void
+mkbuchi(void)
+{ Graph *p;
+ int k;
+ char curnm[64];
+
+ for (k = 0; k <= Max_Red; k++)
+ for (p = Nodes_Set; p; p = p->nxt)
+ { if (!p->outgoing)
+ continue;
+ if (k != 0
+ && !strcmp(p->name->name, "init")
+ && Max_Red != 0)
+ continue;
+
+ if (k == Max_Red
+ && strcmp(p->name->name, "init") != 0)
+ strcpy(curnm, "accept_");
+ else
+ sprintf(curnm, "T%d_", k);
+
+ strcat(curnm, p->name->name);
+
+ fsm_trans(p, k, curnm);
+ }
+ fsm_print();
+}
+
+static Symbol *
+dupSlist(Symbol *s)
+{ Symbol *p1, *p2, *p3, *d = ZS;
+
+ for (p1 = s; p1; p1 = p1->next)
+ { for (p3 = d; p3; p3 = p3->next)
+ { if (!strcmp(p3->name, p1->name))
+ break;
+ }
+ if (p3) continue; /* a duplicate */
+
+ p2 = getsym(p1);
+ p2->next = d;
+ d = p2;
+ }
+ return d;
+}
+
+static Symbol *
+catSlist(Symbol *a, Symbol *b)
+{ Symbol *p1, *p2, *p3, *tmp;
+
+ /* remove duplicates from b */
+ for (p1 = a; p1; p1 = p1->next)
+ { p3 = ZS;
+ for (p2 = b; p2; p2 = p2->next)
+ { if (strcmp(p1->name, p2->name))
+ { p3 = p2;
+ continue;
+ }
+ tmp = p2->next;
+ tfree((void *) p2);
+ if (p3)
+ p3->next = tmp;
+ else
+ b = tmp;
+ } }
+ if (!a) return b;
+ if (!b) return a;
+ if (!b->next)
+ { b->next = a;
+ return b;
+ }
+ /* find end of list */
+ for (p1 = a; p1->next; p1 = p1->next)
+ ;
+ p1->next = b;
+ return a;
+}
+
+static void
+fixinit(Node *orig)
+{ Graph *p1, *g;
+ Symbol *q1, *q2 = ZS;
+
+ ng(tl_lookup("init"), ZS, ZN, ZN, ZN);
+ p1 = pop_stack();
+ p1->nxt = Nodes_Set;
+ p1->Other = p1->Old = orig;
+ Nodes_Set = p1;
+
+ for (g = Nodes_Set; g; g = g->nxt)
+ { for (q1 = g->incoming; q1; q1 = q2)
+ { q2 = q1->next;
+ Addout(g->name->name, q1->name);
+ tfree((void *) q1);
+ }
+ g->incoming = ZS;
+ }
+}
+
+static Node *
+flatten(Node *p)
+{ Node *q, *r, *z = ZN;
+
+ for (q = p; q; q = q->nxt)
+ { r = dupnode(q);
+ if (z)
+ z = tl_nn(AND, r, z);
+ else
+ z = r;
+ }
+ if (!z) return z;
+ z = rewrite(z);
+ return z;
+}
+
+static Node *
+Duplicate(Node *n)
+{ Node *n1, *n2, *lst = ZN, *d = ZN;
+
+ for (n1 = n; n1; n1 = n1->nxt)
+ { n2 = dupnode(n1);
+ if (lst)
+ { lst->nxt = n2;
+ lst = n2;
+ } else
+ d = lst = n2;
+ }
+ return d;
+}
+
+static void
+ng(Symbol *s, Symbol *in, Node *isnew, Node *isold, Node *next)
+{ Graph *g = (Graph *) tl_emalloc(sizeof(Graph));
+
+ if (s) g->name = s;
+ else g->name = tl_lookup(newname());
+
+ if (in) g->incoming = dupSlist(in);
+ if (isnew) g->New = flatten(isnew);
+ if (isold) g->Old = Duplicate(isold);
+ if (next) g->Next = flatten(next);
+
+ push_stack(g);
+}
+
+static void
+sdump(Node *n)
+{
+ switch (n->ntyp) {
+ case PREDICATE: strcat(dumpbuf, n->sym->name);
+ break;
+ case U_OPER: strcat(dumpbuf, "U");
+ goto common2;
+ case V_OPER: strcat(dumpbuf, "V");
+ goto common2;
+ case OR: strcat(dumpbuf, "|");
+ goto common2;
+ case AND: strcat(dumpbuf, "&");
+common2: sdump(n->rgt);
+common1: sdump(n->lft);
+ break;
+#ifdef NXT
+ case NEXT: strcat(dumpbuf, "X");
+ goto common1;
+#endif
+ case NOT: strcat(dumpbuf, "!");
+ goto common1;
+ case TRUE: strcat(dumpbuf, "T");
+ break;
+ case FALSE: strcat(dumpbuf, "F");
+ break;
+ default: strcat(dumpbuf, "?");
+ break;
+ }
+}
+
+Symbol *
+DoDump(Node *n)
+{
+ if (!n) return ZS;
+
+ if (n->ntyp == PREDICATE)
+ return n->sym;
+
+ dumpbuf[0] = '\0';
+ sdump(n);
+ return tl_lookup(dumpbuf);
+}
+
+static int
+not_new(Graph *g)
+{ Graph *q1; Node *tmp, *n1, *n2;
+ Mapping *map;
+
+ tmp = flatten(g->Old); /* duplicate, collapse, normalize */
+ g->Other = g->Old; /* non normalized full version */
+ g->Old = tmp;
+
+ g->oldstring = DoDump(g->Old);
+
+ tmp = flatten(g->Next);
+ g->nxtstring = DoDump(tmp);
+
+ if (tl_verbose) dump_graph(g);
+
+ Debug2("\tformula-old: [%s]\n", g->oldstring?g->oldstring->name:"true");
+ Debug2("\tformula-nxt: [%s]\n", g->nxtstring?g->nxtstring->name:"true");
+ for (q1 = Nodes_Set; q1; q1 = q1->nxt)
+ { Debug2(" compare old to: %s", q1->name->name);
+ Debug2(" [%s]", q1->oldstring?q1->oldstring->name:"true");
+
+ Debug2(" compare nxt to: %s", q1->name->name);
+ Debug2(" [%s]", q1->nxtstring?q1->nxtstring->name:"true");
+
+ if (q1->oldstring != g->oldstring
+ || q1->nxtstring != g->nxtstring)
+ { Debug(" => different\n");
+ continue;
+ }
+ Debug(" => match\n");
+
+ if (g->incoming)
+ q1->incoming = catSlist(g->incoming, q1->incoming);
+
+ /* check if there's anything in g->Other that needs
+ adding to q1->Other
+ */
+ for (n2 = g->Other; n2; n2 = n2->nxt)
+ { for (n1 = q1->Other; n1; n1 = n1->nxt)
+ if (isequal(n1, n2))
+ break;
+ if (!n1)
+ { Node *n3 = dupnode(n2);
+ /* don't mess up n2->nxt */
+ n3->nxt = q1->Other;
+ q1->Other = n3;
+ } }
+
+ map = (Mapping *) tl_emalloc(sizeof(Mapping));
+ map->from = g->name->name;
+ map->to = q1;
+ map->nxt = Mapped;
+ Mapped = map;
+
+ for (n1 = g->Other; n1; n1 = n2)
+ { n2 = n1->nxt;
+ releasenode(1, n1);
+ }
+ for (n1 = g->Old; n1; n1 = n2)
+ { n2 = n1->nxt;
+ releasenode(1, n1);
+ }
+ for (n1 = g->Next; n1; n1 = n2)
+ { n2 = n1->nxt;
+ releasenode(1, n1);
+ }
+ return 1;
+ }
+
+ if (newstates) tl_verbose=1;
+ Debug2(" New Node %s [", g->name->name);
+ for (n1 = g->Old; n1; n1 = n1->nxt)
+ { Dump(n1); Debug(", "); }
+ Debug2("] nr %d\n", Base);
+ if (newstates) tl_verbose=0;
+
+ Base++;
+ g->nxt = Nodes_Set;
+ Nodes_Set = g;
+
+ return 0;
+}
+
+static void
+expand_g(Graph *g)
+{ Node *now, *n1, *n2, *nx;
+ int can_release;
+
+ if (!g->New)
+ { Debug2("\nDone with %s", g->name->name);
+ if (tl_verbose) dump_graph(g);
+ if (not_new(g))
+ { if (tl_verbose) printf("\tIs Not New\n");
+ return;
+ }
+ if (g->Next)
+ { Debug(" Has Next [");
+ for (n1 = g->Next; n1; n1 = n1->nxt)
+ { Dump(n1); Debug(", "); }
+ Debug("]\n");
+
+ ng(ZS, getsym(g->name), g->Next, ZN, ZN);
+ }
+ return;
+ }
+
+ if (tl_verbose)
+ { Symbol *z;
+ printf("\nExpand %s, from ", g->name->name);
+ for (z = g->incoming; z; z = z->next)
+ printf("%s, ", z->name);
+ printf("\n\thandle:\t"); Explain(g->New->ntyp);
+ dump_graph(g);
+ }
+
+ if (g->New->ntyp == AND)
+ { if (g->New->nxt)
+ { n2 = g->New->rgt;
+ while (n2->nxt)
+ n2 = n2->nxt;
+ n2->nxt = g->New->nxt;
+ }
+ n1 = n2 = g->New->lft;
+ while (n2->nxt)
+ n2 = n2->nxt;
+ n2->nxt = g->New->rgt;
+
+ releasenode(0, g->New);
+
+ g->New = n1;
+ push_stack(g);
+ return;
+ }
+
+ can_release = 0; /* unless it need not go into Old */
+ now = g->New;
+ g->New = g->New->nxt;
+ now->nxt = ZN;
+
+ if (now->ntyp != TRUE)
+ { if (g->Old)
+ { for (n1 = g->Old; n1->nxt; n1 = n1->nxt)
+ if (isequal(now, n1))
+ { can_release = 1;
+ goto out;
+ }
+ n1->nxt = now;
+ } else
+ g->Old = now;
+ }
+out:
+ switch (now->ntyp) {
+ case FALSE:
+ push_stack(g);
+ break;
+ case TRUE:
+ releasenode(1, now);
+ push_stack(g);
+ break;
+ case PREDICATE:
+ case NOT:
+ if (can_release) releasenode(1, now);
+ push_stack(g);
+ break;
+ case V_OPER:
+ Assert(now->rgt != ZN, now->ntyp);
+ Assert(now->lft != ZN, now->ntyp);
+ Assert(now->rgt->nxt == ZN, now->ntyp);
+ Assert(now->lft->nxt == ZN, now->ntyp);
+ n1 = now->rgt;
+ n1->nxt = g->New;
+
+ if (can_release)
+ nx = now;
+ else
+ nx = getnode(now); /* now also appears in Old */
+ nx->nxt = g->Next;
+
+ n2 = now->lft;
+ n2->nxt = getnode(now->rgt);
+ n2->nxt->nxt = g->New;
+ g->New = flatten(n2);
+ push_stack(g);
+ ng(ZS, g->incoming, n1, g->Old, nx);
+ break;
+
+ case U_OPER:
+ Assert(now->rgt->nxt == ZN, now->ntyp);
+ Assert(now->lft->nxt == ZN, now->ntyp);
+ n1 = now->lft;
+
+ if (can_release)
+ nx = now;
+ else
+ nx = getnode(now); /* now also appears in Old */
+ nx->nxt = g->Next;
+
+ n2 = now->rgt;
+ n2->nxt = g->New;
+
+ goto common;
+
+#ifdef NXT
+ case NEXT:
+ Assert(now->lft != ZN, now->ntyp);
+ nx = dupnode(now->lft);
+ nx->nxt = g->Next;
+ g->Next = nx;
+ if (can_release) releasenode(0, now);
+ push_stack(g);
+ break;
+#endif
+
+ case OR:
+ Assert(now->rgt->nxt == ZN, now->ntyp);
+ Assert(now->lft->nxt == ZN, now->ntyp);
+ n1 = now->lft;
+ nx = g->Next;
+
+ n2 = now->rgt;
+ n2->nxt = g->New;
+common:
+ n1->nxt = g->New;
+
+ ng(ZS, g->incoming, n1, g->Old, nx);
+ g->New = flatten(n2);
+
+ if (can_release) releasenode(1, now);
+
+ push_stack(g);
+ break;
+ }
+}
+
+Node *
+twocases(Node *p)
+{ Node *q;
+ /* 1: ([]p1 && []p2) == [](p1 && p2) */
+ /* 2: (<>p1 || <>p2) == <>(p1 || p2) */
+
+ if (!p) return p;
+
+ switch(p->ntyp) {
+ case AND:
+ case OR:
+ case U_OPER:
+ case V_OPER:
+ p->lft = twocases(p->lft);
+ p->rgt = twocases(p->rgt);
+ break;
+#ifdef NXT
+ case NEXT:
+#endif
+ case NOT:
+ p->lft = twocases(p->lft);
+ break;
+
+ default:
+ break;
+ }
+ if (p->ntyp == AND /* 1 */
+ && p->lft->ntyp == V_OPER
+ && p->lft->lft->ntyp == FALSE
+ && p->rgt->ntyp == V_OPER
+ && p->rgt->lft->ntyp == FALSE)
+ { q = tl_nn(V_OPER, False,
+ tl_nn(AND, p->lft->rgt, p->rgt->rgt));
+ } else
+ if (p->ntyp == OR /* 2 */
+ && p->lft->ntyp == U_OPER
+ && p->lft->lft->ntyp == TRUE
+ && p->rgt->ntyp == U_OPER
+ && p->rgt->lft->ntyp == TRUE)
+ { q = tl_nn(U_OPER, True,
+ tl_nn(OR, p->lft->rgt, p->rgt->rgt));
+ } else
+ q = p;
+ return q;
+}
+
+void
+trans(Node *p)
+{ Node *op;
+ Graph *g;
+
+ if (!p || tl_errs) return;
+
+ p = twocases(p);
+
+ if (tl_verbose || tl_terse)
+ { fprintf(tl_out, "\t/* Normlzd: ");
+ dump(p);
+ fprintf(tl_out, " */\n");
+ }
+ if (tl_terse)
+ return;
+
+ op = dupnode(p);
+
+ ng(ZS, getsym(tl_lookup("init")), p, ZN, ZN);
+ while ((g = Nodes_Stack) != (Graph *) 0)
+ { Nodes_Stack = g->nxt;
+ expand_g(g);
+ }
+ if (newstates)
+ return;
+
+ fixinit(p);
+ liveness(flatten(op)); /* was: liveness(op); */
+
+ mkbuchi();
+ if (tl_verbose)
+ { printf("/*\n");
+ printf(" * %d states in Streett automaton\n", Base);
+ printf(" * %d Streett acceptance conditions\n", Max_Red);
+ printf(" * %d Buchi states\n", Total);
+ printf(" */\n");
+ }
+}
--- /dev/null
+/***** spin: vars.c *****/
+
+/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
+/* All Rights Reserved. This software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code. Permission is given to distribute this code provided that */
+/* this introductory message is not removed and no monies are exchanged. */
+/* Software written by Gerard J. Holzmann. For tool documentation see: */
+/* http://spinroot.com/ */
+/* Send all bug-reports and/or questions to: bugs@spinroot.com */
+
+#include "spin.h"
+#include "y.tab.h"
+
+extern Ordered *all_names;
+extern RunList *X, *LastX;
+extern Symbol *Fname;
+extern char Buf[];
+extern int lineno, depth, verbose, xspin, limited_vis;
+extern int analyze, jumpsteps, nproc, nstop, columns;
+extern short no_arrays, Have_claim;
+extern void sr_mesg(FILE *, int, int);
+extern void sr_buf(int, int);
+
+static int getglobal(Lextok *);
+static int setglobal(Lextok *, int);
+static int maxcolnr = 1;
+
+int
+getval(Lextok *sn)
+{ Symbol *s = sn->sym;
+
+ if (strcmp(s->name, "_") == 0)
+ { non_fatal("attempt to read value of '_'", 0);
+ return 0;
+ }
+ if (strcmp(s->name, "_last") == 0)
+ return (LastX)?LastX->pid:0;
+ if (strcmp(s->name, "_p") == 0)
+ return (X && X->pc)?X->pc->seqno:0;
+ if (strcmp(s->name, "_pid") == 0)
+ { if (!X) return 0;
+ return X->pid - Have_claim;
+ }
+ if (strcmp(s->name, "_nr_pr") == 0)
+ return nproc-nstop; /* new 3.3.10 */
+
+ if (s->context && s->type)
+ return getlocal(sn);
+
+ if (!s->type) /* not declared locally */
+ { s = lookup(s->name); /* try global */
+ sn->sym = s; /* fix it */
+ }
+ return getglobal(sn);
+}
+
+int
+setval(Lextok *v, int n)
+{
+ if (v->sym->context && v->sym->type)
+ return setlocal(v, n);
+ if (!v->sym->type)
+ v->sym = lookup(v->sym->name);
+ return setglobal(v, n);
+}
+
+void
+rm_selfrefs(Symbol *s, Lextok *i)
+{
+ if (!i) return;
+
+ if (i->ntyp == NAME
+ && strcmp(i->sym->name, s->name) == 0
+ && ( (!i->sym->context && !s->context)
+ || ( i->sym->context && s->context
+ && strcmp(i->sym->context->name, s->context->name) == 0)))
+ { lineno = i->ln;
+ Fname = i->fn;
+ non_fatal("self-reference initializing '%s'", s->name);
+ i->ntyp = CONST;
+ i->val = 0;
+ } else
+ { rm_selfrefs(s, i->lft);
+ rm_selfrefs(s, i->rgt);
+ }
+}
+
+int
+checkvar(Symbol *s, int n)
+{ int i, oln = lineno; /* calls on eval() change it */
+ Symbol *ofnm = Fname;
+
+ if (!in_bound(s, n))
+ return 0;
+
+ if (s->type == 0)
+ { non_fatal("undecl var %s (assuming int)", s->name);
+ s->type = INT;
+ }
+ /* not a STRUCT */
+ if (s->val == (int *) 0) /* uninitialized */
+ { s->val = (int *) emalloc(s->nel*sizeof(int));
+ for (i = 0; i < s->nel; i++)
+ { if (s->type != CHAN)
+ { rm_selfrefs(s, s->ini);
+ s->val[i] = eval(s->ini);
+ } else if (!analyze)
+ s->val[i] = qmake(s);
+ } }
+ lineno = oln;
+ Fname = ofnm;
+ return 1;
+}
+
+static int
+getglobal(Lextok *sn)
+{ Symbol *s = sn->sym;
+ int i, n = eval(sn->lft);
+
+ if (s->type == 0 && X && (i = find_lab(s, X->n, 0)))
+ { printf("findlab through getglobal on %s\n", s->name);
+ return i; /* can this happen? */
+ }
+ if (s->type == STRUCT)
+ return Rval_struct(sn, s, 1); /* 1 = check init */
+ if (checkvar(s, n))
+ return cast_val(s->type, s->val[n], s->nbits);
+ return 0;
+}
+
+int
+cast_val(int t, int v, int w)
+{ int i=0; short s=0; unsigned int u=0;
+
+ if (t == PREDEF || t == INT || t == CHAN) i = v; /* predef means _ */
+ else if (t == SHORT) s = (short) v;
+ else if (t == BYTE || t == MTYPE) u = (unsigned char)v;
+ else if (t == BIT) u = (unsigned char)(v&1);
+ else if (t == UNSIGNED)
+ { if (w == 0)
+ fatal("cannot happen, cast_val", (char *)0);
+ /* u = (unsigned)(v& ((1<<w)-1)); problem when w=32 */
+ u = (unsigned)(v& (~0u>>(8*sizeof(unsigned)-w))); /* doug */
+ }
+
+ if (v != i+s+ (int) u)
+ { char buf[64]; sprintf(buf, "%d->%d (%d)", v, i+s+u, t);
+ non_fatal("value (%s) truncated in assignment", buf);
+ }
+ return (int)(i+s+u);
+}
+
+static int
+setglobal(Lextok *v, int m)
+{
+ if (v->sym->type == STRUCT)
+ (void) Lval_struct(v, v->sym, 1, m);
+ else
+ { int n = eval(v->lft);
+ if (checkvar(v->sym, n))
+ { int oval = v->sym->val[n];
+ int nval = cast_val(v->sym->type, m, v->sym->nbits);
+ v->sym->val[n] = nval;
+ if (oval != nval)
+ { v->sym->setat = depth;
+ } } }
+ return 1;
+}
+
+void
+dumpclaims(FILE *fd, int pid, char *s)
+{ extern Lextok *Xu_List; extern int Pid;
+ extern short terse;
+ Lextok *m; int cnt = 0; int oPid = Pid;
+
+ for (m = Xu_List; m; m = m->rgt)
+ if (strcmp(m->sym->name, s) == 0)
+ { cnt=1;
+ break;
+ }
+ if (cnt == 0) return;
+
+ Pid = pid;
+ fprintf(fd, "#ifndef XUSAFE\n");
+ for (m = Xu_List; m; m = m->rgt)
+ { if (strcmp(m->sym->name, s) != 0)
+ continue;
+ no_arrays = 1;
+ putname(fd, "\t\tsetq_claim(", m->lft, 0, "");
+ no_arrays = 0;
+ fprintf(fd, ", %d, ", m->val);
+ terse = 1;
+ putname(fd, "\"", m->lft, 0, "\", h, ");
+ terse = 0;
+ fprintf(fd, "\"%s\");\n", s);
+ }
+ fprintf(fd, "#endif\n");
+ Pid = oPid;
+}
+
+void
+dumpglobals(void)
+{ Ordered *walk;
+ static Lextok *dummy = ZN;
+ Symbol *sp;
+ int j;
+
+ if (!dummy)
+ dummy = nn(ZN, NAME, nn(ZN,CONST,ZN,ZN), ZN);
+
+ for (walk = all_names; walk; walk = walk->next)
+ { sp = walk->entry;
+ if (!sp->type || sp->context || sp->owner
+ || sp->type == PROCTYPE || sp->type == PREDEF
+ || sp->type == CODE_FRAG || sp->type == CODE_DECL
+ || (sp->type == MTYPE && ismtype(sp->name)))
+ continue;
+
+ if (sp->type == STRUCT)
+ { if ((verbose&4) && !(verbose&64)
+ && (sp->setat < depth
+ && jumpsteps != depth))
+ { continue;
+ }
+ dump_struct(sp, sp->name, 0);
+ continue;
+ }
+ for (j = 0; j < sp->nel; j++)
+ { int prefetch;
+ if (sp->type == CHAN)
+ { doq(sp, j, 0);
+ continue;
+ }
+ if ((verbose&4) && !(verbose&64)
+ && (sp->setat < depth
+ && jumpsteps != depth))
+ { continue;
+ }
+
+ dummy->sym = sp;
+ dummy->lft->val = j;
+ /* in case of cast_val warnings, do this first: */
+ prefetch = getglobal(dummy);
+ printf("\t\t%s", sp->name);
+ if (sp->nel > 1) printf("[%d]", j);
+ printf(" = ");
+ sr_mesg(stdout, prefetch,
+ sp->type == MTYPE);
+ printf("\n");
+ if (limited_vis && (sp->hidden&2))
+ { int colpos;
+ Buf[0] = '\0';
+ if (!xspin)
+ { if (columns == 2)
+ sprintf(Buf, "~G%s = ", sp->name);
+ else
+ sprintf(Buf, "%s = ", sp->name);
+ }
+ sr_buf(prefetch, sp->type == MTYPE);
+ if (sp->colnr == 0)
+ { sp->colnr = maxcolnr;
+ maxcolnr = 1+(maxcolnr%10);
+ }
+ colpos = nproc+sp->colnr-1;
+ if (columns == 2)
+ { pstext(colpos, Buf);
+ continue;
+ }
+ if (!xspin)
+ { printf("\t\t%s\n", Buf);
+ continue;
+ }
+ printf("MSC: ~G %s %s\n", sp->name, Buf);
+ printf("%3d:\tproc %3d (TRACK) line 1 \"var\" ",
+ depth, colpos);
+ printf("(state 0)\t[printf('MSC: globvar\\\\n')]\n");
+ printf("\t\t%s", sp->name);
+ if (sp->nel > 1) printf("[%d]", j);
+ printf(" = %s\n", Buf);
+ } } }
+}
+
+void
+dumplocal(RunList *r)
+{ static Lextok *dummy = ZN;
+ Symbol *z, *s;
+ int i;
+
+ if (!r) return;
+
+ s = r->symtab;
+
+ if (!dummy)
+ dummy = nn(ZN, NAME, nn(ZN,CONST,ZN,ZN), ZN);
+
+ for (z = s; z; z = z->next)
+ { if (z->type == STRUCT)
+ { dump_struct(z, z->name, r);
+ continue;
+ }
+ for (i = 0; i < z->nel; i++)
+ { if (z->type == CHAN)
+ { doq(z, i, r);
+ continue;
+ }
+ if ((verbose&4) && !(verbose&64)
+ && (z->setat < depth
+ && jumpsteps != depth))
+ continue;
+
+ dummy->sym = z;
+ dummy->lft->val = i;
+
+ printf("\t\t%s(%d):%s",
+ r->n->name, r->pid, z->name);
+ if (z->nel > 1) printf("[%d]", i);
+ printf(" = ");
+ sr_mesg(stdout, getval(dummy), z->type == MTYPE);
+ printf("\n");
+ if (limited_vis && (z->hidden&2))
+ { int colpos;
+ Buf[0] = '\0';
+ if (!xspin)
+ { if (columns == 2)
+ sprintf(Buf, "~G%s(%d):%s = ",
+ r->n->name, r->pid, z->name);
+ else
+ sprintf(Buf, "%s(%d):%s = ",
+ r->n->name, r->pid, z->name);
+ }
+ sr_buf(getval(dummy), z->type==MTYPE);
+ if (z->colnr == 0)
+ { z->colnr = maxcolnr;
+ maxcolnr = 1+(maxcolnr%10);
+ }
+ colpos = nproc+z->colnr-1;
+ if (columns == 2)
+ { pstext(colpos, Buf);
+ continue;
+ }
+ if (!xspin)
+ { printf("\t\t%s\n", Buf);
+ continue;
+ }
+ printf("MSC: ~G %s(%d):%s %s\n",
+ r->n->name, r->pid, z->name, Buf);
+
+ printf("%3d:\tproc %3d (TRACK) line 1 \"var\" ",
+ depth, colpos);
+ printf("(state 0)\t[printf('MSC: locvar\\\\n')]\n");
+ printf("\t\t%s(%d):%s",
+ r->n->name, r->pid, z->name);
+ if (z->nel > 1) printf("[%d]", i);
+ printf(" = %s\n", Buf);
+ } } }
+}
--- /dev/null
+#define SpinVersion "Spin Version 5.1.6 -- 9 May 2008"
--- /dev/null
+Terminals which are not used
+
+ MTYPE
+ LABEL
+ NON_ATOMIC
+
+
+Grammar
+
+ 0 $accept: program $end
+
+ 1 program: units
+
+ 2 units: unit
+ 3 | units unit
+
+ 4 unit: proc
+ 5 | init
+ 6 | claim
+ 7 | events
+ 8 | one_decl
+ 9 | utype
+ 10 | c_fcts
+ 11 | ns
+ 12 | SEMI
+ 13 | error
+
+ 14 @1: /* empty */
+
+ 15 @2: /* empty */
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' @2 Opt_priority Opt_enabler body
+
+ 17 proctype: PROCTYPE
+ 18 | D_PROCTYPE
+
+ 19 inst: /* empty */
+ 20 | ACTIVE
+ 21 | ACTIVE '[' CONST ']'
+ 22 | ACTIVE '[' NAME ']'
+
+ 23 @3: /* empty */
+
+ 24 init: INIT @3 Opt_priority body
+
+ 25 @4: /* empty */
+
+ 26 claim: CLAIM @4 body
+
+ 27 @5: /* empty */
+
+ 28 events: TRACE @5 body
+
+ 29 @6: /* empty */
+
+ 30 utype: TYPEDEF NAME @6 '{' decl_lst '}'
+
+ 31 nm: NAME
+ 32 | INAME
+
+ 33 @7: /* empty */
+
+ 34 ns: INLINE nm '(' @7 args ')'
+
+ 35 c_fcts: ccode
+ 36 | cstate
+
+ 37 cstate: C_STATE STRING STRING
+ 38 | C_TRACK STRING STRING
+ 39 | C_STATE STRING STRING STRING
+ 40 | C_TRACK STRING STRING STRING
+
+ 41 ccode: C_CODE
+ 42 | C_DECL
+
+ 43 cexpr: C_EXPR
+
+ 44 @8: /* empty */
+
+ 45 @9: /* empty */
+
+ 46 body: '{' @8 sequence OS @9 '}'
+
+ 47 sequence: step
+ 48 | sequence MS step
+
+ 49 step: one_decl
+ 50 | XU vref_lst
+ 51 | NAME ':' one_decl
+ 52 | NAME ':' XU
+ 53 | stmnt
+ 54 | stmnt UNLESS stmnt
+
+ 55 vis: /* empty */
+ 56 | HIDDEN
+ 57 | SHOW
+ 58 | ISLOCAL
+
+ 59 asgn: /* empty */
+ 60 | ASGN
+
+ 61 one_decl: vis TYPE var_list
+ 62 | vis UNAME var_list
+ 63 | vis TYPE asgn '{' nlst '}'
+
+ 64 decl_lst: one_decl
+ 65 | one_decl SEMI decl_lst
+
+ 66 decl: /* empty */
+ 67 | decl_lst
+
+ 68 vref_lst: varref
+ 69 | varref ',' vref_lst
+
+ 70 var_list: ivar
+ 71 | ivar ',' var_list
+
+ 72 ivar: vardcl
+ 73 | vardcl ASGN expr
+ 74 | vardcl ASGN ch_init
+
+ 75 ch_init: '[' CONST ']' OF '{' typ_list '}'
+
+ 76 vardcl: NAME
+ 77 | NAME ':' CONST
+ 78 | NAME '[' CONST ']'
+
+ 79 varref: cmpnd
+
+ 80 pfld: NAME
+
+ 81 @10: /* empty */
+
+ 82 pfld: NAME @10 '[' expr ']'
+
+ 83 @11: /* empty */
+
+ 84 cmpnd: pfld @11 sfld
+
+ 85 sfld: /* empty */
+ 86 | '.' cmpnd
+
+ 87 stmnt: Special
+ 88 | Stmnt
+
+ 89 @12: /* empty */
+
+ 90 Special: varref RCV @12 rargs
+
+ 91 @13: /* empty */
+
+ 92 Special: varref SND @13 margs
+ 93 | IF options FI
+
+ 94 @14: /* empty */
+
+ 95 Special: DO @14 options OD
+ 96 | BREAK
+ 97 | GOTO NAME
+ 98 | NAME ':' stmnt
+
+ 99 Stmnt: varref ASGN expr
+ 100 | varref INCR
+ 101 | varref DECR
+
+ 102 @15: /* empty */
+
+ 103 Stmnt: PRINT '(' STRING @15 prargs ')'
+ 104 | PRINTM '(' varref ')'
+ 105 | PRINTM '(' CONST ')'
+ 106 | ASSERT full_expr
+ 107 | ccode
+
+ 108 @16: /* empty */
+
+ 109 Stmnt: varref R_RCV @16 rargs
+
+ 110 @17: /* empty */
+
+ 111 Stmnt: varref RCV @17 LT rargs GT
+
+ 112 @18: /* empty */
+
+ 113 Stmnt: varref R_RCV @18 LT rargs GT
+
+ 114 @19: /* empty */
+
+ 115 Stmnt: varref O_SND @19 margs
+ 116 | full_expr
+ 117 | ELSE
+
+ 118 @20: /* empty */
+
+ 119 Stmnt: ATOMIC '{' @20 sequence OS '}'
+
+ 120 @21: /* empty */
+
+ 121 Stmnt: D_STEP '{' @21 sequence OS '}'
+
+ 122 @22: /* empty */
+
+ 123 Stmnt: '{' @22 sequence OS '}'
+
+ 124 @23: /* empty */
+
+ 125 @24: /* empty */
+
+ 126 Stmnt: INAME @23 '(' args ')' @24 Stmnt
+
+ 127 options: option
+ 128 | option options
+
+ 129 @25: /* empty */
+
+ 130 option: SEP @25 sequence OS
+
+ 131 OS: /* empty */
+ 132 | SEMI
+
+ 133 MS: SEMI
+ 134 | MS SEMI
+
+ 135 aname: NAME
+ 136 | PNAME
+
+ 137 expr: '(' expr ')'
+ 138 | expr '+' expr
+ 139 | expr '-' expr
+ 140 | expr '*' expr
+ 141 | expr '/' expr
+ 142 | expr '%' expr
+ 143 | expr '&' expr
+ 144 | expr '^' expr
+ 145 | expr '|' expr
+ 146 | expr GT expr
+ 147 | expr LT expr
+ 148 | expr GE expr
+ 149 | expr LE expr
+ 150 | expr EQ expr
+ 151 | expr NE expr
+ 152 | expr AND expr
+ 153 | expr OR expr
+ 154 | expr LSHIFT expr
+ 155 | expr RSHIFT expr
+ 156 | '~' expr
+ 157 | '-' expr
+ 158 | SND expr
+ 159 | '(' expr SEMI expr ':' expr ')'
+
+ 160 @26: /* empty */
+
+ 161 expr: RUN aname @26 '(' args ')' Opt_priority
+ 162 | LEN '(' varref ')'
+ 163 | ENABLED '(' expr ')'
+
+ 164 @27: /* empty */
+
+ 165 expr: varref RCV @27 '[' rargs ']'
+
+ 166 @28: /* empty */
+
+ 167 expr: varref R_RCV @28 '[' rargs ']'
+ 168 | varref
+ 169 | cexpr
+ 170 | CONST
+ 171 | TIMEOUT
+ 172 | NONPROGRESS
+ 173 | PC_VAL '(' expr ')'
+ 174 | PNAME '[' expr ']' '@' NAME
+ 175 | PNAME '[' expr ']' ':' pfld
+ 176 | PNAME '@' NAME
+ 177 | PNAME ':' pfld
+
+ 178 Opt_priority: /* empty */
+ 179 | PRIORITY CONST
+
+ 180 full_expr: expr
+ 181 | Expr
+
+ 182 Opt_enabler: /* empty */
+ 183 | PROVIDED '(' full_expr ')'
+ 184 | PROVIDED error
+
+ 185 Expr: Probe
+ 186 | '(' Expr ')'
+ 187 | Expr AND Expr
+ 188 | Expr AND expr
+ 189 | Expr OR Expr
+ 190 | Expr OR expr
+ 191 | expr AND Expr
+ 192 | expr OR Expr
+
+ 193 Probe: FULL '(' varref ')'
+ 194 | NFULL '(' varref ')'
+ 195 | EMPTY '(' varref ')'
+ 196 | NEMPTY '(' varref ')'
+
+ 197 basetype: TYPE
+ 198 | UNAME
+ 199 | error
+
+ 200 typ_list: basetype
+ 201 | basetype ',' typ_list
+
+ 202 args: /* empty */
+ 203 | arg
+
+ 204 prargs: /* empty */
+ 205 | ',' arg
+
+ 206 margs: arg
+ 207 | expr '(' arg ')'
+
+ 208 arg: expr
+ 209 | expr ',' arg
+
+ 210 rarg: varref
+ 211 | EVAL '(' expr ')'
+ 212 | CONST
+ 213 | '-' CONST
+
+ 214 rargs: rarg
+ 215 | rarg ',' rargs
+ 216 | rarg '(' rargs ')'
+ 217 | '(' rargs ')'
+
+ 218 nlst: NAME
+ 219 | nlst NAME
+ 220 | nlst ','
+
+
+Terminals, with rules where they appear
+
+$end (0) 0
+'%' (37) 142
+'&' (38) 143
+'(' (40) 16 34 103 104 105 126 137 159 161 162 163 173 183 186 193
+ 194 195 196 207 211 216 217
+')' (41) 16 34 103 104 105 126 137 159 161 162 163 173 183 186 193
+ 194 195 196 207 211 216 217
+'*' (42) 140
+'+' (43) 138
+',' (44) 69 71 201 205 209 215 220
+'-' (45) 139 157 213
+'.' (46) 86
+'/' (47) 141
+':' (58) 51 52 77 98 159 175 177
+'@' (64) 174 176
+'[' (91) 21 22 75 78 82 165 167 174 175
+']' (93) 21 22 75 78 82 165 167 174 175
+'^' (94) 144
+'{' (123) 30 46 63 75 119 121 123
+'|' (124) 145
+'}' (125) 30 46 63 75 119 121 123
+'~' (126) 156
+error (256) 13 184 199
+ASSERT (258) 106
+PRINT (259) 103
+PRINTM (260) 104 105
+C_CODE (261) 41
+C_DECL (262) 42
+C_EXPR (263) 43
+C_STATE (264) 37 39
+C_TRACK (265) 38 40
+RUN (266) 161
+LEN (267) 162
+ENABLED (268) 163
+EVAL (269) 211
+PC_VAL (270) 173
+TYPEDEF (271) 30
+MTYPE (272)
+INLINE (273) 34
+LABEL (274)
+OF (275) 75
+GOTO (276) 97
+BREAK (277) 96
+ELSE (278) 117
+SEMI (279) 12 65 132 133 134 159
+IF (280) 93
+FI (281) 93
+DO (282) 95
+OD (283) 95
+SEP (284) 130
+ATOMIC (285) 119
+NON_ATOMIC (286)
+D_STEP (287) 121
+UNLESS (288) 54
+TIMEOUT (289) 171
+NONPROGRESS (290) 172
+ACTIVE (291) 20 21 22
+PROCTYPE (292) 17
+D_PROCTYPE (293) 18
+HIDDEN (294) 56
+SHOW (295) 57
+ISLOCAL (296) 58
+PRIORITY (297) 179
+PROVIDED (298) 183 184
+FULL (299) 193
+EMPTY (300) 195
+NFULL (301) 194
+NEMPTY (302) 196
+CONST (303) 21 75 77 78 105 170 179 212 213
+TYPE (304) 61 63 197
+XU (305) 50 52
+NAME (306) 16 22 30 31 51 52 76 77 78 80 82 97 98 135 174 176 218 219
+UNAME (307) 62 198
+PNAME (308) 136 174 175 176 177
+INAME (309) 32 126
+STRING (310) 37 38 39 40 103
+CLAIM (311) 26
+TRACE (312) 28
+INIT (313) 24
+ASGN (314) 60 73 74 99
+R_RCV (315) 109 113 167
+RCV (316) 90 111 165
+O_SND (317) 115
+SND (318) 92 158
+OR (319) 153 189 190 192
+AND (320) 152 187 188 191
+NE (321) 151
+EQ (322) 150
+LE (323) 149
+GE (324) 148
+LT (325) 111 113 147
+GT (326) 111 113 146
+RSHIFT (327) 155
+LSHIFT (328) 154
+DECR (329) 101
+INCR (330) 100
+NEG (331)
+UMIN (332)
+DOT (333)
+
+
+Nonterminals, with rules where they appear
+
+$accept (98)
+ on left: 0
+program (99)
+ on left: 1, on right: 0
+units (100)
+ on left: 2 3, on right: 1 3
+unit (101)
+ on left: 4 5 6 7 8 9 10 11 12 13, on right: 2 3
+proc (102)
+ on left: 16, on right: 4
+@1 (103)
+ on left: 14, on right: 16
+@2 (104)
+ on left: 15, on right: 16
+proctype (105)
+ on left: 17 18, on right: 16
+inst (106)
+ on left: 19 20 21 22, on right: 16
+init (107)
+ on left: 24, on right: 5
+@3 (108)
+ on left: 23, on right: 24
+claim (109)
+ on left: 26, on right: 6
+@4 (110)
+ on left: 25, on right: 26
+events (111)
+ on left: 28, on right: 7
+@5 (112)
+ on left: 27, on right: 28
+utype (113)
+ on left: 30, on right: 9
+@6 (114)
+ on left: 29, on right: 30
+nm (115)
+ on left: 31 32, on right: 34
+ns (116)
+ on left: 34, on right: 11
+@7 (117)
+ on left: 33, on right: 34
+c_fcts (118)
+ on left: 35 36, on right: 10
+cstate (119)
+ on left: 37 38 39 40, on right: 36
+ccode (120)
+ on left: 41 42, on right: 35 107
+cexpr (121)
+ on left: 43, on right: 169
+body (122)
+ on left: 46, on right: 16 24 26 28
+@8 (123)
+ on left: 44, on right: 46
+@9 (124)
+ on left: 45, on right: 46
+sequence (125)
+ on left: 47 48, on right: 46 48 119 121 123 130
+step (126)
+ on left: 49 50 51 52 53 54, on right: 47 48
+vis (127)
+ on left: 55 56 57 58, on right: 61 62 63
+asgn (128)
+ on left: 59 60, on right: 63
+one_decl (129)
+ on left: 61 62 63, on right: 8 49 51 64 65
+decl_lst (130)
+ on left: 64 65, on right: 30 65 67
+decl (131)
+ on left: 66 67, on right: 16
+vref_lst (132)
+ on left: 68 69, on right: 50 69
+var_list (133)
+ on left: 70 71, on right: 61 62 71
+ivar (134)
+ on left: 72 73 74, on right: 70 71
+ch_init (135)
+ on left: 75, on right: 74
+vardcl (136)
+ on left: 76 77 78, on right: 72 73 74
+varref (137)
+ on left: 79, on right: 68 69 90 92 99 100 101 104 109 111 113 115
+ 162 165 167 168 193 194 195 196 210
+pfld (138)
+ on left: 80 82, on right: 84 175 177
+@10 (139)
+ on left: 81, on right: 82
+cmpnd (140)
+ on left: 84, on right: 79 86
+@11 (141)
+ on left: 83, on right: 84
+sfld (142)
+ on left: 85 86, on right: 84
+stmnt (143)
+ on left: 87 88, on right: 53 54 98
+Special (144)
+ on left: 90 92 93 95 96 97 98, on right: 87
+@12 (145)
+ on left: 89, on right: 90
+@13 (146)
+ on left: 91, on right: 92
+@14 (147)
+ on left: 94, on right: 95
+Stmnt (148)
+ on left: 99 100 101 103 104 105 106 107 109 111 113 115 116 117
+ 119 121 123 126, on right: 88 126
+@15 (149)
+ on left: 102, on right: 103
+@16 (150)
+ on left: 108, on right: 109
+@17 (151)
+ on left: 110, on right: 111
+@18 (152)
+ on left: 112, on right: 113
+@19 (153)
+ on left: 114, on right: 115
+@20 (154)
+ on left: 118, on right: 119
+@21 (155)
+ on left: 120, on right: 121
+@22 (156)
+ on left: 122, on right: 123
+@23 (157)
+ on left: 124, on right: 126
+@24 (158)
+ on left: 125, on right: 126
+options (159)
+ on left: 127 128, on right: 93 95 128
+option (160)
+ on left: 130, on right: 127 128
+@25 (161)
+ on left: 129, on right: 130
+OS (162)
+ on left: 131 132, on right: 46 119 121 123 130
+MS (163)
+ on left: 133 134, on right: 48 134
+aname (164)
+ on left: 135 136, on right: 161
+expr (165)
+ on left: 137 138 139 140 141 142 143 144 145 146 147 148 149 150
+ 151 152 153 154 155 156 157 158 159 161 162 163 165 167 168 169
+ 170 171 172 173 174 175 176 177, on right: 73 82 99 137 138 139
+ 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
+ 156 157 158 159 163 173 174 175 180 188 190 191 192 207 208 209
+ 211
+@26 (166)
+ on left: 160, on right: 161
+@27 (167)
+ on left: 164, on right: 165
+@28 (168)
+ on left: 166, on right: 167
+Opt_priority (169)
+ on left: 178 179, on right: 16 24 161
+full_expr (170)
+ on left: 180 181, on right: 106 116 183
+Opt_enabler (171)
+ on left: 182 183 184, on right: 16
+Expr (172)
+ on left: 185 186 187 188 189 190 191 192, on right: 181 186 187
+ 188 189 190 191 192
+Probe (173)
+ on left: 193 194 195 196, on right: 185
+basetype (174)
+ on left: 197 198 199, on right: 200 201
+typ_list (175)
+ on left: 200 201, on right: 75 201
+args (176)
+ on left: 202 203, on right: 34 126 161
+prargs (177)
+ on left: 204 205, on right: 103
+margs (178)
+ on left: 206 207, on right: 92 115
+arg (179)
+ on left: 208 209, on right: 203 205 206 207 209
+rarg (180)
+ on left: 210 211 212 213, on right: 214 215 216
+rargs (181)
+ on left: 214 215 216 217, on right: 90 109 111 113 165 167 215
+ 216 217
+nlst (182)
+ on left: 218 219 220, on right: 63 219 220
+
+
+state 0
+
+ 0 $accept: . program $end
+
+ error shift, and go to state 1
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_STATE shift, and go to state 4
+ C_TRACK shift, and go to state 5
+ TYPEDEF shift, and go to state 6
+ INLINE shift, and go to state 7
+ SEMI shift, and go to state 8
+ ACTIVE shift, and go to state 9
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ CLAIM shift, and go to state 13
+ TRACE shift, and go to state 14
+ INIT shift, and go to state 15
+
+ PROCTYPE reduce using rule 19 (inst)
+ D_PROCTYPE reduce using rule 19 (inst)
+ TYPE reduce using rule 55 (vis)
+ UNAME reduce using rule 55 (vis)
+
+ program go to state 16
+ units go to state 17
+ unit go to state 18
+ proc go to state 19
+ inst go to state 20
+ init go to state 21
+ claim go to state 22
+ events go to state 23
+ utype go to state 24
+ ns go to state 25
+ c_fcts go to state 26
+ cstate go to state 27
+ ccode go to state 28
+ vis go to state 29
+ one_decl go to state 30
+
+
+state 1
+
+ 13 unit: error .
+
+ $default reduce using rule 13 (unit)
+
+
+state 2
+
+ 41 ccode: C_CODE .
+
+ $default reduce using rule 41 (ccode)
+
+
+state 3
+
+ 42 ccode: C_DECL .
+
+ $default reduce using rule 42 (ccode)
+
+
+state 4
+
+ 37 cstate: C_STATE . STRING STRING
+ 39 | C_STATE . STRING STRING STRING
+
+ STRING shift, and go to state 31
+
+
+state 5
+
+ 38 cstate: C_TRACK . STRING STRING
+ 40 | C_TRACK . STRING STRING STRING
+
+ STRING shift, and go to state 32
+
+
+state 6
+
+ 30 utype: TYPEDEF . NAME @6 '{' decl_lst '}'
+
+ NAME shift, and go to state 33
+
+
+state 7
+
+ 34 ns: INLINE . nm '(' @7 args ')'
+
+ NAME shift, and go to state 34
+ INAME shift, and go to state 35
+
+ nm go to state 36
+
+
+state 8
+
+ 12 unit: SEMI .
+
+ $default reduce using rule 12 (unit)
+
+
+state 9
+
+ 20 inst: ACTIVE .
+ 21 | ACTIVE . '[' CONST ']'
+ 22 | ACTIVE . '[' NAME ']'
+
+ '[' shift, and go to state 37
+
+ $default reduce using rule 20 (inst)
+
+
+state 10
+
+ 56 vis: HIDDEN .
+
+ $default reduce using rule 56 (vis)
+
+
+state 11
+
+ 57 vis: SHOW .
+
+ $default reduce using rule 57 (vis)
+
+
+state 12
+
+ 58 vis: ISLOCAL .
+
+ $default reduce using rule 58 (vis)
+
+
+state 13
+
+ 26 claim: CLAIM . @4 body
+
+ $default reduce using rule 25 (@4)
+
+ @4 go to state 38
+
+
+state 14
+
+ 28 events: TRACE . @5 body
+
+ $default reduce using rule 27 (@5)
+
+ @5 go to state 39
+
+
+state 15
+
+ 24 init: INIT . @3 Opt_priority body
+
+ $default reduce using rule 23 (@3)
+
+ @3 go to state 40
+
+
+state 16
+
+ 0 $accept: program . $end
+
+ $end shift, and go to state 41
+
+
+state 17
+
+ 1 program: units .
+ 3 units: units . unit
+
+ error shift, and go to state 1
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_STATE shift, and go to state 4
+ C_TRACK shift, and go to state 5
+ TYPEDEF shift, and go to state 6
+ INLINE shift, and go to state 7
+ SEMI shift, and go to state 8
+ ACTIVE shift, and go to state 9
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ CLAIM shift, and go to state 13
+ TRACE shift, and go to state 14
+ INIT shift, and go to state 15
+
+ $end reduce using rule 1 (program)
+ PROCTYPE reduce using rule 19 (inst)
+ D_PROCTYPE reduce using rule 19 (inst)
+ TYPE reduce using rule 55 (vis)
+ UNAME reduce using rule 55 (vis)
+
+ unit go to state 42
+ proc go to state 19
+ inst go to state 20
+ init go to state 21
+ claim go to state 22
+ events go to state 23
+ utype go to state 24
+ ns go to state 25
+ c_fcts go to state 26
+ cstate go to state 27
+ ccode go to state 28
+ vis go to state 29
+ one_decl go to state 30
+
+
+state 18
+
+ 2 units: unit .
+
+ $default reduce using rule 2 (units)
+
+
+state 19
+
+ 4 unit: proc .
+
+ $default reduce using rule 4 (unit)
+
+
+state 20
+
+ 16 proc: inst . proctype NAME @1 '(' decl ')' @2 Opt_priority Opt_enabler body
+
+ PROCTYPE shift, and go to state 43
+ D_PROCTYPE shift, and go to state 44
+
+ proctype go to state 45
+
+
+state 21
+
+ 5 unit: init .
+
+ $default reduce using rule 5 (unit)
+
+
+state 22
+
+ 6 unit: claim .
+
+ $default reduce using rule 6 (unit)
+
+
+state 23
+
+ 7 unit: events .
+
+ $default reduce using rule 7 (unit)
+
+
+state 24
+
+ 9 unit: utype .
+
+ $default reduce using rule 9 (unit)
+
+
+state 25
+
+ 11 unit: ns .
+
+ $default reduce using rule 11 (unit)
+
+
+state 26
+
+ 10 unit: c_fcts .
+
+ $default reduce using rule 10 (unit)
+
+
+state 27
+
+ 36 c_fcts: cstate .
+
+ $default reduce using rule 36 (c_fcts)
+
+
+state 28
+
+ 35 c_fcts: ccode .
+
+ $default reduce using rule 35 (c_fcts)
+
+
+state 29
+
+ 61 one_decl: vis . TYPE var_list
+ 62 | vis . UNAME var_list
+ 63 | vis . TYPE asgn '{' nlst '}'
+
+ TYPE shift, and go to state 46
+ UNAME shift, and go to state 47
+
+
+state 30
+
+ 8 unit: one_decl .
+
+ $default reduce using rule 8 (unit)
+
+
+state 31
+
+ 37 cstate: C_STATE STRING . STRING
+ 39 | C_STATE STRING . STRING STRING
+
+ STRING shift, and go to state 48
+
+
+state 32
+
+ 38 cstate: C_TRACK STRING . STRING
+ 40 | C_TRACK STRING . STRING STRING
+
+ STRING shift, and go to state 49
+
+
+state 33
+
+ 30 utype: TYPEDEF NAME . @6 '{' decl_lst '}'
+
+ $default reduce using rule 29 (@6)
+
+ @6 go to state 50
+
+
+state 34
+
+ 31 nm: NAME .
+
+ $default reduce using rule 31 (nm)
+
+
+state 35
+
+ 32 nm: INAME .
+
+ $default reduce using rule 32 (nm)
+
+
+state 36
+
+ 34 ns: INLINE nm . '(' @7 args ')'
+
+ '(' shift, and go to state 51
+
+
+state 37
+
+ 21 inst: ACTIVE '[' . CONST ']'
+ 22 | ACTIVE '[' . NAME ']'
+
+ CONST shift, and go to state 52
+ NAME shift, and go to state 53
+
+
+state 38
+
+ 26 claim: CLAIM @4 . body
+
+ '{' shift, and go to state 54
+
+ body go to state 55
+
+
+state 39
+
+ 28 events: TRACE @5 . body
+
+ '{' shift, and go to state 54
+
+ body go to state 56
+
+
+state 40
+
+ 24 init: INIT @3 . Opt_priority body
+
+ PRIORITY shift, and go to state 57
+
+ $default reduce using rule 178 (Opt_priority)
+
+ Opt_priority go to state 58
+
+
+state 41
+
+ 0 $accept: program $end .
+
+ $default accept
+
+
+state 42
+
+ 3 units: units unit .
+
+ $default reduce using rule 3 (units)
+
+
+state 43
+
+ 17 proctype: PROCTYPE .
+
+ $default reduce using rule 17 (proctype)
+
+
+state 44
+
+ 18 proctype: D_PROCTYPE .
+
+ $default reduce using rule 18 (proctype)
+
+
+state 45
+
+ 16 proc: inst proctype . NAME @1 '(' decl ')' @2 Opt_priority Opt_enabler body
+
+ NAME shift, and go to state 59
+
+
+state 46
+
+ 61 one_decl: vis TYPE . var_list
+ 63 | vis TYPE . asgn '{' nlst '}'
+
+ NAME shift, and go to state 60
+ ASGN shift, and go to state 61
+
+ $default reduce using rule 59 (asgn)
+
+ asgn go to state 62
+ var_list go to state 63
+ ivar go to state 64
+ vardcl go to state 65
+
+
+state 47
+
+ 62 one_decl: vis UNAME . var_list
+
+ NAME shift, and go to state 60
+
+ var_list go to state 66
+ ivar go to state 64
+ vardcl go to state 65
+
+
+state 48
+
+ 37 cstate: C_STATE STRING STRING .
+ 39 | C_STATE STRING STRING . STRING
+
+ STRING shift, and go to state 67
+
+ $default reduce using rule 37 (cstate)
+
+
+state 49
+
+ 38 cstate: C_TRACK STRING STRING .
+ 40 | C_TRACK STRING STRING . STRING
+
+ STRING shift, and go to state 68
+
+ $default reduce using rule 38 (cstate)
+
+
+state 50
+
+ 30 utype: TYPEDEF NAME @6 . '{' decl_lst '}'
+
+ '{' shift, and go to state 69
+
+
+state 51
+
+ 34 ns: INLINE nm '(' . @7 args ')'
+
+ $default reduce using rule 33 (@7)
+
+ @7 go to state 70
+
+
+state 52
+
+ 21 inst: ACTIVE '[' CONST . ']'
+
+ ']' shift, and go to state 71
+
+
+state 53
+
+ 22 inst: ACTIVE '[' NAME . ']'
+
+ ']' shift, and go to state 72
+
+
+state 54
+
+ 46 body: '{' . @8 sequence OS @9 '}'
+
+ $default reduce using rule 44 (@8)
+
+ @8 go to state 73
+
+
+state 55
+
+ 26 claim: CLAIM @4 body .
+
+ $default reduce using rule 26 (claim)
+
+
+state 56
+
+ 28 events: TRACE @5 body .
+
+ $default reduce using rule 28 (events)
+
+
+state 57
+
+ 179 Opt_priority: PRIORITY . CONST
+
+ CONST shift, and go to state 74
+
+
+state 58
+
+ 24 init: INIT @3 Opt_priority . body
+
+ '{' shift, and go to state 54
+
+ body go to state 75
+
+
+state 59
+
+ 16 proc: inst proctype NAME . @1 '(' decl ')' @2 Opt_priority Opt_enabler body
+
+ $default reduce using rule 14 (@1)
+
+ @1 go to state 76
+
+
+state 60
+
+ 76 vardcl: NAME .
+ 77 | NAME . ':' CONST
+ 78 | NAME . '[' CONST ']'
+
+ '[' shift, and go to state 77
+ ':' shift, and go to state 78
+
+ $default reduce using rule 76 (vardcl)
+
+
+state 61
+
+ 60 asgn: ASGN .
+
+ $default reduce using rule 60 (asgn)
+
+
+state 62
+
+ 63 one_decl: vis TYPE asgn . '{' nlst '}'
+
+ '{' shift, and go to state 79
+
+
+state 63
+
+ 61 one_decl: vis TYPE var_list .
+
+ $default reduce using rule 61 (one_decl)
+
+
+state 64
+
+ 70 var_list: ivar .
+ 71 | ivar . ',' var_list
+
+ ',' shift, and go to state 80
+
+ $default reduce using rule 70 (var_list)
+
+
+state 65
+
+ 72 ivar: vardcl .
+ 73 | vardcl . ASGN expr
+ 74 | vardcl . ASGN ch_init
+
+ ASGN shift, and go to state 81
+
+ $default reduce using rule 72 (ivar)
+
+
+state 66
+
+ 62 one_decl: vis UNAME var_list .
+
+ $default reduce using rule 62 (one_decl)
+
+
+state 67
+
+ 39 cstate: C_STATE STRING STRING STRING .
+
+ $default reduce using rule 39 (cstate)
+
+
+state 68
+
+ 40 cstate: C_TRACK STRING STRING STRING .
+
+ $default reduce using rule 40 (cstate)
+
+
+state 69
+
+ 30 utype: TYPEDEF NAME @6 '{' . decl_lst '}'
+
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+
+ $default reduce using rule 55 (vis)
+
+ vis go to state 29
+ one_decl go to state 82
+ decl_lst go to state 83
+
+
+state 70
+
+ 34 ns: INLINE nm '(' @7 . args ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ $default reduce using rule 202 (args)
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ args go to state 103
+ arg go to state 104
+
+
+state 71
+
+ 21 inst: ACTIVE '[' CONST ']' .
+
+ $default reduce using rule 21 (inst)
+
+
+state 72
+
+ 22 inst: ACTIVE '[' NAME ']' .
+
+ $default reduce using rule 22 (inst)
+
+
+state 73
+
+ 46 body: '{' @8 . sequence OS @9 '}'
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ sequence go to state 125
+ step go to state 126
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 74
+
+ 179 Opt_priority: PRIORITY CONST .
+
+ $default reduce using rule 179 (Opt_priority)
+
+
+state 75
+
+ 24 init: INIT @3 Opt_priority body .
+
+ $default reduce using rule 24 (init)
+
+
+state 76
+
+ 16 proc: inst proctype NAME @1 . '(' decl ')' @2 Opt_priority Opt_enabler body
+
+ '(' shift, and go to state 136
+
+
+state 77
+
+ 78 vardcl: NAME '[' . CONST ']'
+
+ CONST shift, and go to state 137
+
+
+state 78
+
+ 77 vardcl: NAME ':' . CONST
+
+ CONST shift, and go to state 138
+
+
+state 79
+
+ 63 one_decl: vis TYPE asgn '{' . nlst '}'
+
+ NAME shift, and go to state 139
+
+ nlst go to state 140
+
+
+state 80
+
+ 71 var_list: ivar ',' . var_list
+
+ NAME shift, and go to state 60
+
+ var_list go to state 141
+ ivar go to state 64
+ vardcl go to state 65
+
+
+state 81
+
+ 73 ivar: vardcl ASGN . expr
+ 74 | vardcl ASGN . ch_init
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+ '[' shift, and go to state 142
+
+ cexpr go to state 98
+ ch_init go to state 143
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 144
+
+
+state 82
+
+ 64 decl_lst: one_decl .
+ 65 | one_decl . SEMI decl_lst
+
+ SEMI shift, and go to state 145
+
+ $default reduce using rule 64 (decl_lst)
+
+
+state 83
+
+ 30 utype: TYPEDEF NAME @6 '{' decl_lst . '}'
+
+ '}' shift, and go to state 146
+
+
+state 84
+
+ 43 cexpr: C_EXPR .
+
+ $default reduce using rule 43 (cexpr)
+
+
+state 85
+
+ 161 expr: RUN . aname @26 '(' args ')' Opt_priority
+
+ NAME shift, and go to state 147
+ PNAME shift, and go to state 148
+
+ aname go to state 149
+
+
+state 86
+
+ 162 expr: LEN . '(' varref ')'
+
+ '(' shift, and go to state 150
+
+
+state 87
+
+ 163 expr: ENABLED . '(' expr ')'
+
+ '(' shift, and go to state 151
+
+
+state 88
+
+ 173 expr: PC_VAL . '(' expr ')'
+
+ '(' shift, and go to state 152
+
+
+state 89
+
+ 171 expr: TIMEOUT .
+
+ $default reduce using rule 171 (expr)
+
+
+state 90
+
+ 172 expr: NONPROGRESS .
+
+ $default reduce using rule 172 (expr)
+
+
+state 91
+
+ 170 expr: CONST .
+
+ $default reduce using rule 170 (expr)
+
+
+state 92
+
+ 80 pfld: NAME .
+ 82 | NAME . @10 '[' expr ']'
+
+ '[' reduce using rule 81 (@10)
+ $default reduce using rule 80 (pfld)
+
+ @10 go to state 153
+
+
+state 93
+
+ 174 expr: PNAME . '[' expr ']' '@' NAME
+ 175 | PNAME . '[' expr ']' ':' pfld
+ 176 | PNAME . '@' NAME
+ 177 | PNAME . ':' pfld
+
+ '[' shift, and go to state 154
+ ':' shift, and go to state 155
+ '@' shift, and go to state 156
+
+
+state 94
+
+ 158 expr: SND . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 157
+
+
+state 95
+
+ 157 expr: '-' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 158
+
+
+state 96
+
+ 156 expr: '~' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 159
+
+
+state 97
+
+ 137 expr: '(' . expr ')'
+ 159 | '(' . expr SEMI expr ':' expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 160
+
+
+state 98
+
+ 169 expr: cexpr .
+
+ $default reduce using rule 169 (expr)
+
+
+state 99
+
+ 165 expr: varref . RCV @27 '[' rargs ']'
+ 167 | varref . R_RCV @28 '[' rargs ']'
+ 168 | varref .
+
+ R_RCV shift, and go to state 161
+ RCV shift, and go to state 162
+
+ $default reduce using rule 168 (expr)
+
+
+state 100
+
+ 84 cmpnd: pfld . @11 sfld
+
+ $default reduce using rule 83 (@11)
+
+ @11 go to state 163
+
+
+state 101
+
+ 79 varref: cmpnd .
+
+ $default reduce using rule 79 (varref)
+
+
+state 102
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 208 arg: expr .
+ 209 | expr . ',' arg
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ',' shift, and go to state 182
+
+ $default reduce using rule 208 (arg)
+
+
+state 103
+
+ 34 ns: INLINE nm '(' @7 args . ')'
+
+ ')' shift, and go to state 183
+
+
+state 104
+
+ 203 args: arg .
+
+ $default reduce using rule 203 (args)
+
+
+state 105
+
+ 106 Stmnt: ASSERT . full_expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 132
+ full_expr go to state 184
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 106
+
+ 103 Stmnt: PRINT . '(' STRING @15 prargs ')'
+
+ '(' shift, and go to state 185
+
+
+state 107
+
+ 104 Stmnt: PRINTM . '(' varref ')'
+ 105 | PRINTM . '(' CONST ')'
+
+ '(' shift, and go to state 186
+
+
+state 108
+
+ 97 Special: GOTO . NAME
+
+ NAME shift, and go to state 187
+
+
+state 109
+
+ 96 Special: BREAK .
+
+ $default reduce using rule 96 (Special)
+
+
+state 110
+
+ 117 Stmnt: ELSE .
+
+ $default reduce using rule 117 (Stmnt)
+
+
+state 111
+
+ 93 Special: IF . options FI
+
+ SEP shift, and go to state 188
+
+ options go to state 189
+ option go to state 190
+
+
+state 112
+
+ 95 Special: DO . @14 options OD
+
+ $default reduce using rule 94 (@14)
+
+ @14 go to state 191
+
+
+state 113
+
+ 119 Stmnt: ATOMIC . '{' @20 sequence OS '}'
+
+ '{' shift, and go to state 192
+
+
+state 114
+
+ 121 Stmnt: D_STEP . '{' @21 sequence OS '}'
+
+ '{' shift, and go to state 193
+
+
+state 115
+
+ 193 Probe: FULL . '(' varref ')'
+
+ '(' shift, and go to state 194
+
+
+state 116
+
+ 195 Probe: EMPTY . '(' varref ')'
+
+ '(' shift, and go to state 195
+
+
+state 117
+
+ 194 Probe: NFULL . '(' varref ')'
+
+ '(' shift, and go to state 196
+
+
+state 118
+
+ 196 Probe: NEMPTY . '(' varref ')'
+
+ '(' shift, and go to state 197
+
+
+state 119
+
+ 50 step: XU . vref_lst
+
+ NAME shift, and go to state 92
+
+ vref_lst go to state 198
+ varref go to state 199
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 120
+
+ 51 step: NAME . ':' one_decl
+ 52 | NAME . ':' XU
+ 80 pfld: NAME .
+ 82 | NAME . @10 '[' expr ']'
+ 98 Special: NAME . ':' stmnt
+
+ ':' shift, and go to state 200
+
+ '[' reduce using rule 81 (@10)
+ $default reduce using rule 80 (pfld)
+
+ @10 go to state 153
+
+
+state 121
+
+ 126 Stmnt: INAME . @23 '(' args ')' @24 Stmnt
+
+ $default reduce using rule 124 (@23)
+
+ @23 go to state 201
+
+
+state 122
+
+ 137 expr: '(' . expr ')'
+ 159 | '(' . expr SEMI expr ':' expr ')'
+ 186 Expr: '(' . Expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 202
+ Expr go to state 203
+ Probe go to state 135
+
+
+state 123
+
+ 123 Stmnt: '{' . @22 sequence OS '}'
+
+ $default reduce using rule 122 (@22)
+
+ @22 go to state 204
+
+
+state 124
+
+ 107 Stmnt: ccode .
+
+ $default reduce using rule 107 (Stmnt)
+
+
+state 125
+
+ 46 body: '{' @8 sequence . OS @9 '}'
+ 48 sequence: sequence . MS step
+
+ SEMI shift, and go to state 205
+
+ $default reduce using rule 131 (OS)
+
+ OS go to state 206
+ MS go to state 207
+
+
+state 126
+
+ 47 sequence: step .
+
+ $default reduce using rule 47 (sequence)
+
+
+state 127
+
+ 49 step: one_decl .
+
+ $default reduce using rule 49 (step)
+
+
+state 128
+
+ 90 Special: varref . RCV @12 rargs
+ 92 | varref . SND @13 margs
+ 99 Stmnt: varref . ASGN expr
+ 100 | varref . INCR
+ 101 | varref . DECR
+ 109 | varref . R_RCV @16 rargs
+ 111 | varref . RCV @17 LT rargs GT
+ 113 | varref . R_RCV @18 LT rargs GT
+ 115 | varref . O_SND @19 margs
+ 165 expr: varref . RCV @27 '[' rargs ']'
+ 167 | varref . R_RCV @28 '[' rargs ']'
+ 168 | varref .
+
+ ASGN shift, and go to state 208
+ R_RCV shift, and go to state 209
+ RCV shift, and go to state 210
+ O_SND shift, and go to state 211
+ SND shift, and go to state 212
+ DECR shift, and go to state 213
+ INCR shift, and go to state 214
+
+ $default reduce using rule 168 (expr)
+
+
+state 129
+
+ 53 step: stmnt .
+ 54 | stmnt . UNLESS stmnt
+
+ UNLESS shift, and go to state 215
+
+ $default reduce using rule 53 (step)
+
+
+state 130
+
+ 87 stmnt: Special .
+
+ $default reduce using rule 87 (stmnt)
+
+
+state 131
+
+ 88 stmnt: Stmnt .
+
+ $default reduce using rule 88 (stmnt)
+
+
+state 132
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 180 full_expr: expr .
+ 191 Expr: expr . AND Expr
+ 192 | expr . OR Expr
+
+ OR shift, and go to state 216
+ AND shift, and go to state 217
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 180 (full_expr)
+
+
+state 133
+
+ 116 Stmnt: full_expr .
+
+ $default reduce using rule 116 (Stmnt)
+
+
+state 134
+
+ 181 full_expr: Expr .
+ 187 Expr: Expr . AND Expr
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 190 | Expr . OR expr
+
+ OR shift, and go to state 218
+ AND shift, and go to state 219
+
+ $default reduce using rule 181 (full_expr)
+
+
+state 135
+
+ 185 Expr: Probe .
+
+ $default reduce using rule 185 (Expr)
+
+
+state 136
+
+ 16 proc: inst proctype NAME @1 '(' . decl ')' @2 Opt_priority Opt_enabler body
+
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+
+ ')' reduce using rule 66 (decl)
+ $default reduce using rule 55 (vis)
+
+ vis go to state 29
+ one_decl go to state 82
+ decl_lst go to state 220
+ decl go to state 221
+
+
+state 137
+
+ 78 vardcl: NAME '[' CONST . ']'
+
+ ']' shift, and go to state 222
+
+
+state 138
+
+ 77 vardcl: NAME ':' CONST .
+
+ $default reduce using rule 77 (vardcl)
+
+
+state 139
+
+ 218 nlst: NAME .
+
+ $default reduce using rule 218 (nlst)
+
+
+state 140
+
+ 63 one_decl: vis TYPE asgn '{' nlst . '}'
+ 219 nlst: nlst . NAME
+ 220 | nlst . ','
+
+ NAME shift, and go to state 223
+ '}' shift, and go to state 224
+ ',' shift, and go to state 225
+
+
+state 141
+
+ 71 var_list: ivar ',' var_list .
+
+ $default reduce using rule 71 (var_list)
+
+
+state 142
+
+ 75 ch_init: '[' . CONST ']' OF '{' typ_list '}'
+
+ CONST shift, and go to state 226
+
+
+state 143
+
+ 74 ivar: vardcl ASGN ch_init .
+
+ $default reduce using rule 74 (ivar)
+
+
+state 144
+
+ 73 ivar: vardcl ASGN expr .
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 73 (ivar)
+
+
+state 145
+
+ 65 decl_lst: one_decl SEMI . decl_lst
+
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+
+ $default reduce using rule 55 (vis)
+
+ vis go to state 29
+ one_decl go to state 82
+ decl_lst go to state 227
+
+
+state 146
+
+ 30 utype: TYPEDEF NAME @6 '{' decl_lst '}' .
+
+ $default reduce using rule 30 (utype)
+
+
+state 147
+
+ 135 aname: NAME .
+
+ $default reduce using rule 135 (aname)
+
+
+state 148
+
+ 136 aname: PNAME .
+
+ $default reduce using rule 136 (aname)
+
+
+state 149
+
+ 161 expr: RUN aname . @26 '(' args ')' Opt_priority
+
+ $default reduce using rule 160 (@26)
+
+ @26 go to state 228
+
+
+state 150
+
+ 162 expr: LEN '(' . varref ')'
+
+ NAME shift, and go to state 92
+
+ varref go to state 229
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 151
+
+ 163 expr: ENABLED '(' . expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 230
+
+
+state 152
+
+ 173 expr: PC_VAL '(' . expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 231
+
+
+state 153
+
+ 82 pfld: NAME @10 . '[' expr ']'
+
+ '[' shift, and go to state 232
+
+
+state 154
+
+ 174 expr: PNAME '[' . expr ']' '@' NAME
+ 175 | PNAME '[' . expr ']' ':' pfld
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 233
+
+
+state 155
+
+ 177 expr: PNAME ':' . pfld
+
+ NAME shift, and go to state 92
+
+ pfld go to state 234
+
+
+state 156
+
+ 176 expr: PNAME '@' . NAME
+
+ NAME shift, and go to state 235
+
+
+state 157
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 158 | SND expr .
+
+ $default reduce using rule 158 (expr)
+
+
+state 158
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 157 | '-' expr .
+
+ $default reduce using rule 157 (expr)
+
+
+state 159
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 156 | '~' expr .
+
+ $default reduce using rule 156 (expr)
+
+
+state 160
+
+ 137 expr: '(' expr . ')'
+ 138 | expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 159 | '(' expr . SEMI expr ':' expr ')'
+
+ SEMI shift, and go to state 236
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 237
+
+
+state 161
+
+ 167 expr: varref R_RCV . @28 '[' rargs ']'
+
+ $default reduce using rule 166 (@28)
+
+ @28 go to state 238
+
+
+state 162
+
+ 165 expr: varref RCV . @27 '[' rargs ']'
+
+ $default reduce using rule 164 (@27)
+
+ @27 go to state 239
+
+
+state 163
+
+ 84 cmpnd: pfld @11 . sfld
+
+ '.' shift, and go to state 240
+
+ $default reduce using rule 85 (sfld)
+
+ sfld go to state 241
+
+
+state 164
+
+ 153 expr: expr OR . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 242
+
+
+state 165
+
+ 152 expr: expr AND . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 243
+
+
+state 166
+
+ 145 expr: expr '|' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 244
+
+
+state 167
+
+ 144 expr: expr '^' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 245
+
+
+state 168
+
+ 143 expr: expr '&' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 246
+
+
+state 169
+
+ 151 expr: expr NE . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 247
+
+
+state 170
+
+ 150 expr: expr EQ . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 248
+
+
+state 171
+
+ 149 expr: expr LE . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 249
+
+
+state 172
+
+ 148 expr: expr GE . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 250
+
+
+state 173
+
+ 147 expr: expr LT . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 251
+
+
+state 174
+
+ 146 expr: expr GT . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 252
+
+
+state 175
+
+ 155 expr: expr RSHIFT . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 253
+
+
+state 176
+
+ 154 expr: expr LSHIFT . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 254
+
+
+state 177
+
+ 138 expr: expr '+' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 255
+
+
+state 178
+
+ 139 expr: expr '-' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 256
+
+
+state 179
+
+ 140 expr: expr '*' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 257
+
+
+state 180
+
+ 141 expr: expr '/' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 258
+
+
+state 181
+
+ 142 expr: expr '%' . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 259
+
+
+state 182
+
+ 209 arg: expr ',' . arg
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ arg go to state 260
+
+
+state 183
+
+ 34 ns: INLINE nm '(' @7 args ')' .
+
+ $default reduce using rule 34 (ns)
+
+
+state 184
+
+ 106 Stmnt: ASSERT full_expr .
+
+ $default reduce using rule 106 (Stmnt)
+
+
+state 185
+
+ 103 Stmnt: PRINT '(' . STRING @15 prargs ')'
+
+ STRING shift, and go to state 261
+
+
+state 186
+
+ 104 Stmnt: PRINTM '(' . varref ')'
+ 105 | PRINTM '(' . CONST ')'
+
+ CONST shift, and go to state 262
+ NAME shift, and go to state 92
+
+ varref go to state 263
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 187
+
+ 97 Special: GOTO NAME .
+
+ $default reduce using rule 97 (Special)
+
+
+state 188
+
+ 130 option: SEP . @25 sequence OS
+
+ $default reduce using rule 129 (@25)
+
+ @25 go to state 264
+
+
+state 189
+
+ 93 Special: IF options . FI
+
+ FI shift, and go to state 265
+
+
+state 190
+
+ 127 options: option .
+ 128 | option . options
+
+ SEP shift, and go to state 188
+
+ $default reduce using rule 127 (options)
+
+ options go to state 266
+ option go to state 190
+
+
+state 191
+
+ 95 Special: DO @14 . options OD
+
+ SEP shift, and go to state 188
+
+ options go to state 267
+ option go to state 190
+
+
+state 192
+
+ 119 Stmnt: ATOMIC '{' . @20 sequence OS '}'
+
+ $default reduce using rule 118 (@20)
+
+ @20 go to state 268
+
+
+state 193
+
+ 121 Stmnt: D_STEP '{' . @21 sequence OS '}'
+
+ $default reduce using rule 120 (@21)
+
+ @21 go to state 269
+
+
+state 194
+
+ 193 Probe: FULL '(' . varref ')'
+
+ NAME shift, and go to state 92
+
+ varref go to state 270
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 195
+
+ 195 Probe: EMPTY '(' . varref ')'
+
+ NAME shift, and go to state 92
+
+ varref go to state 271
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 196
+
+ 194 Probe: NFULL '(' . varref ')'
+
+ NAME shift, and go to state 92
+
+ varref go to state 272
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 197
+
+ 196 Probe: NEMPTY '(' . varref ')'
+
+ NAME shift, and go to state 92
+
+ varref go to state 273
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 198
+
+ 50 step: XU vref_lst .
+
+ $default reduce using rule 50 (step)
+
+
+state 199
+
+ 68 vref_lst: varref .
+ 69 | varref . ',' vref_lst
+
+ ',' shift, and go to state 274
+
+ $default reduce using rule 68 (vref_lst)
+
+
+state 200
+
+ 51 step: NAME ':' . one_decl
+ 52 | NAME ':' . XU
+ 98 Special: NAME ':' . stmnt
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 275
+ NAME shift, and go to state 276
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ vis go to state 29
+ one_decl go to state 277
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 278
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 201
+
+ 126 Stmnt: INAME @23 . '(' args ')' @24 Stmnt
+
+ '(' shift, and go to state 279
+
+
+state 202
+
+ 137 expr: '(' expr . ')'
+ 138 | expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 159 | '(' expr . SEMI expr ':' expr ')'
+ 191 Expr: expr . AND Expr
+ 192 | expr . OR Expr
+
+ SEMI shift, and go to state 236
+ OR shift, and go to state 216
+ AND shift, and go to state 217
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 237
+
+
+state 203
+
+ 186 Expr: '(' Expr . ')'
+ 187 | Expr . AND Expr
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 190 | Expr . OR expr
+
+ OR shift, and go to state 218
+ AND shift, and go to state 219
+ ')' shift, and go to state 280
+
+
+state 204
+
+ 123 Stmnt: '{' @22 . sequence OS '}'
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ sequence go to state 281
+ step go to state 126
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 205
+
+ 132 OS: SEMI .
+ 133 MS: SEMI .
+
+ FI reduce using rule 132 (OS)
+ OD reduce using rule 132 (OS)
+ SEP reduce using rule 132 (OS)
+ '}' reduce using rule 132 (OS)
+ $default reduce using rule 133 (MS)
+
+
+state 206
+
+ 46 body: '{' @8 sequence OS . @9 '}'
+
+ $default reduce using rule 45 (@9)
+
+ @9 go to state 282
+
+
+state 207
+
+ 48 sequence: sequence MS . step
+ 134 MS: MS . SEMI
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ SEMI shift, and go to state 283
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ step go to state 284
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 208
+
+ 99 Stmnt: varref ASGN . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 285
+
+
+state 209
+
+ 109 Stmnt: varref R_RCV . @16 rargs
+ 113 | varref R_RCV . @18 LT rargs GT
+ 167 expr: varref R_RCV . @28 '[' rargs ']'
+
+ LT reduce using rule 112 (@18)
+ '[' reduce using rule 166 (@28)
+ $default reduce using rule 108 (@16)
+
+ @16 go to state 286
+ @18 go to state 287
+ @28 go to state 238
+
+
+state 210
+
+ 90 Special: varref RCV . @12 rargs
+ 111 Stmnt: varref RCV . @17 LT rargs GT
+ 165 expr: varref RCV . @27 '[' rargs ']'
+
+ LT reduce using rule 110 (@17)
+ '[' reduce using rule 164 (@27)
+ $default reduce using rule 89 (@12)
+
+ @12 go to state 288
+ @17 go to state 289
+ @27 go to state 239
+
+
+state 211
+
+ 115 Stmnt: varref O_SND . @19 margs
+
+ $default reduce using rule 114 (@19)
+
+ @19 go to state 290
+
+
+state 212
+
+ 92 Special: varref SND . @13 margs
+
+ $default reduce using rule 91 (@13)
+
+ @13 go to state 291
+
+
+state 213
+
+ 101 Stmnt: varref DECR .
+
+ $default reduce using rule 101 (Stmnt)
+
+
+state 214
+
+ 100 Stmnt: varref INCR .
+
+ $default reduce using rule 100 (Stmnt)
+
+
+state 215
+
+ 54 step: stmnt UNLESS . stmnt
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 276
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ ccode go to state 124
+ cexpr go to state 98
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 292
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 216
+
+ 153 expr: expr OR . expr
+ 192 Expr: expr OR . Expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 293
+ Expr go to state 294
+ Probe go to state 135
+
+
+state 217
+
+ 152 expr: expr AND . expr
+ 191 Expr: expr AND . Expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 295
+ Expr go to state 296
+ Probe go to state 135
+
+
+state 218
+
+ 189 Expr: Expr OR . Expr
+ 190 | Expr OR . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 297
+ Expr go to state 298
+ Probe go to state 135
+
+
+state 219
+
+ 187 Expr: Expr AND . Expr
+ 188 | Expr AND . expr
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 299
+ Expr go to state 300
+ Probe go to state 135
+
+
+state 220
+
+ 67 decl: decl_lst .
+
+ $default reduce using rule 67 (decl)
+
+
+state 221
+
+ 16 proc: inst proctype NAME @1 '(' decl . ')' @2 Opt_priority Opt_enabler body
+
+ ')' shift, and go to state 301
+
+
+state 222
+
+ 78 vardcl: NAME '[' CONST ']' .
+
+ $default reduce using rule 78 (vardcl)
+
+
+state 223
+
+ 219 nlst: nlst NAME .
+
+ $default reduce using rule 219 (nlst)
+
+
+state 224
+
+ 63 one_decl: vis TYPE asgn '{' nlst '}' .
+
+ $default reduce using rule 63 (one_decl)
+
+
+state 225
+
+ 220 nlst: nlst ',' .
+
+ $default reduce using rule 220 (nlst)
+
+
+state 226
+
+ 75 ch_init: '[' CONST . ']' OF '{' typ_list '}'
+
+ ']' shift, and go to state 302
+
+
+state 227
+
+ 65 decl_lst: one_decl SEMI decl_lst .
+
+ $default reduce using rule 65 (decl_lst)
+
+
+state 228
+
+ 161 expr: RUN aname @26 . '(' args ')' Opt_priority
+
+ '(' shift, and go to state 303
+
+
+state 229
+
+ 162 expr: LEN '(' varref . ')'
+
+ ')' shift, and go to state 304
+
+
+state 230
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 163 | ENABLED '(' expr . ')'
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 305
+
+
+state 231
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 173 | PC_VAL '(' expr . ')'
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 306
+
+
+state 232
+
+ 82 pfld: NAME @10 '[' . expr ']'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 307
+
+
+state 233
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 174 | PNAME '[' expr . ']' '@' NAME
+ 175 | PNAME '[' expr . ']' ':' pfld
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ']' shift, and go to state 308
+
+
+state 234
+
+ 177 expr: PNAME ':' pfld .
+
+ $default reduce using rule 177 (expr)
+
+
+state 235
+
+ 176 expr: PNAME '@' NAME .
+
+ $default reduce using rule 176 (expr)
+
+
+state 236
+
+ 159 expr: '(' expr SEMI . expr ':' expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 309
+
+
+state 237
+
+ 137 expr: '(' expr ')' .
+
+ $default reduce using rule 137 (expr)
+
+
+state 238
+
+ 167 expr: varref R_RCV @28 . '[' rargs ']'
+
+ '[' shift, and go to state 310
+
+
+state 239
+
+ 165 expr: varref RCV @27 . '[' rargs ']'
+
+ '[' shift, and go to state 311
+
+
+state 240
+
+ 86 sfld: '.' . cmpnd
+
+ NAME shift, and go to state 92
+
+ pfld go to state 100
+ cmpnd go to state 312
+
+
+state 241
+
+ 84 cmpnd: pfld @11 sfld .
+
+ $default reduce using rule 84 (cmpnd)
+
+
+state 242
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 153 | expr OR expr .
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 153 (expr)
+
+
+state 243
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 152 | expr AND expr .
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 152 (expr)
+
+
+state 244
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 145 | expr '|' expr .
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 145 (expr)
+
+
+state 245
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 144 | expr '^' expr .
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 144 (expr)
+
+
+state 246
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 143 | expr '&' expr .
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 143 (expr)
+
+
+state 247
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 151 | expr NE expr .
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 151 (expr)
+
+
+state 248
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 150 | expr EQ expr .
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 150 (expr)
+
+
+state 249
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 149 | expr LE expr .
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 149 (expr)
+
+
+state 250
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 148 | expr GE expr .
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 148 (expr)
+
+
+state 251
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 147 | expr LT expr .
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 147 (expr)
+
+
+state 252
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 146 | expr GT expr .
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 146 (expr)
+
+
+state 253
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 155 | expr RSHIFT expr .
+
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 155 (expr)
+
+
+state 254
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 154 | expr LSHIFT expr .
+ 155 | expr . RSHIFT expr
+
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 154 (expr)
+
+
+state 255
+
+ 138 expr: expr . '+' expr
+ 138 | expr '+' expr .
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 138 (expr)
+
+
+state 256
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 139 | expr '-' expr .
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 139 (expr)
+
+
+state 257
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 140 | expr '*' expr .
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ $default reduce using rule 140 (expr)
+
+
+state 258
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 141 | expr '/' expr .
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ $default reduce using rule 141 (expr)
+
+
+state 259
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 142 | expr '%' expr .
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ $default reduce using rule 142 (expr)
+
+
+state 260
+
+ 209 arg: expr ',' arg .
+
+ $default reduce using rule 209 (arg)
+
+
+state 261
+
+ 103 Stmnt: PRINT '(' STRING . @15 prargs ')'
+
+ $default reduce using rule 102 (@15)
+
+ @15 go to state 313
+
+
+state 262
+
+ 105 Stmnt: PRINTM '(' CONST . ')'
+
+ ')' shift, and go to state 314
+
+
+state 263
+
+ 104 Stmnt: PRINTM '(' varref . ')'
+
+ ')' shift, and go to state 315
+
+
+state 264
+
+ 130 option: SEP @25 . sequence OS
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ sequence go to state 316
+ step go to state 126
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 265
+
+ 93 Special: IF options FI .
+
+ $default reduce using rule 93 (Special)
+
+
+state 266
+
+ 128 options: option options .
+
+ $default reduce using rule 128 (options)
+
+
+state 267
+
+ 95 Special: DO @14 options . OD
+
+ OD shift, and go to state 317
+
+
+state 268
+
+ 119 Stmnt: ATOMIC '{' @20 . sequence OS '}'
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ sequence go to state 318
+ step go to state 126
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 269
+
+ 121 Stmnt: D_STEP '{' @21 . sequence OS '}'
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ HIDDEN shift, and go to state 10
+ SHOW shift, and go to state 11
+ ISLOCAL shift, and go to state 12
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ XU shift, and go to state 119
+ NAME shift, and go to state 120
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ $default reduce using rule 55 (vis)
+
+ ccode go to state 124
+ cexpr go to state 98
+ sequence go to state 319
+ step go to state 126
+ vis go to state 29
+ one_decl go to state 127
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 129
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 270
+
+ 193 Probe: FULL '(' varref . ')'
+
+ ')' shift, and go to state 320
+
+
+state 271
+
+ 195 Probe: EMPTY '(' varref . ')'
+
+ ')' shift, and go to state 321
+
+
+state 272
+
+ 194 Probe: NFULL '(' varref . ')'
+
+ ')' shift, and go to state 322
+
+
+state 273
+
+ 196 Probe: NEMPTY '(' varref . ')'
+
+ ')' shift, and go to state 323
+
+
+state 274
+
+ 69 vref_lst: varref ',' . vref_lst
+
+ NAME shift, and go to state 92
+
+ vref_lst go to state 324
+ varref go to state 199
+ pfld go to state 100
+ cmpnd go to state 101
+
+
+state 275
+
+ 52 step: NAME ':' XU .
+
+ $default reduce using rule 52 (step)
+
+
+state 276
+
+ 80 pfld: NAME .
+ 82 | NAME . @10 '[' expr ']'
+ 98 Special: NAME . ':' stmnt
+
+ ':' shift, and go to state 325
+
+ '[' reduce using rule 81 (@10)
+ $default reduce using rule 80 (pfld)
+
+ @10 go to state 153
+
+
+state 277
+
+ 51 step: NAME ':' one_decl .
+
+ $default reduce using rule 51 (step)
+
+
+state 278
+
+ 98 Special: NAME ':' stmnt .
+
+ $default reduce using rule 98 (Special)
+
+
+state 279
+
+ 126 Stmnt: INAME @23 '(' . args ')' @24 Stmnt
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ $default reduce using rule 202 (args)
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ args go to state 326
+ arg go to state 104
+
+
+state 280
+
+ 186 Expr: '(' Expr ')' .
+
+ $default reduce using rule 186 (Expr)
+
+
+state 281
+
+ 48 sequence: sequence . MS step
+ 123 Stmnt: '{' @22 sequence . OS '}'
+
+ SEMI shift, and go to state 205
+
+ $default reduce using rule 131 (OS)
+
+ OS go to state 327
+ MS go to state 207
+
+
+state 282
+
+ 46 body: '{' @8 sequence OS @9 . '}'
+
+ '}' shift, and go to state 328
+
+
+state 283
+
+ 134 MS: MS SEMI .
+
+ $default reduce using rule 134 (MS)
+
+
+state 284
+
+ 48 sequence: sequence MS step .
+
+ $default reduce using rule 48 (sequence)
+
+
+state 285
+
+ 99 Stmnt: varref ASGN expr .
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 99 (Stmnt)
+
+
+state 286
+
+ 109 Stmnt: varref R_RCV @16 . rargs
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 335
+
+
+state 287
+
+ 113 Stmnt: varref R_RCV @18 . LT rargs GT
+
+ LT shift, and go to state 336
+
+
+state 288
+
+ 90 Special: varref RCV @12 . rargs
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 337
+
+
+state 289
+
+ 111 Stmnt: varref RCV @17 . LT rargs GT
+
+ LT shift, and go to state 338
+
+
+state 290
+
+ 115 Stmnt: varref O_SND @19 . margs
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 339
+ margs go to state 340
+ arg go to state 341
+
+
+state 291
+
+ 92 Special: varref SND @13 . margs
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 339
+ margs go to state 342
+ arg go to state 341
+
+
+state 292
+
+ 54 step: stmnt UNLESS stmnt .
+
+ $default reduce using rule 54 (step)
+
+
+state 293
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 153 | expr OR expr .
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 191 Expr: expr . AND Expr
+ 192 | expr . OR Expr
+
+ AND shift, and go to state 217
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 153 (expr)
+
+
+state 294
+
+ 187 Expr: Expr . AND Expr
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 190 | Expr . OR expr
+ 192 | expr OR Expr .
+
+ AND shift, and go to state 219
+
+ $default reduce using rule 192 (Expr)
+
+
+state 295
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 152 | expr AND expr .
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 191 Expr: expr . AND Expr
+ 192 | expr . OR Expr
+
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 152 (expr)
+
+
+state 296
+
+ 187 Expr: Expr . AND Expr
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 190 | Expr . OR expr
+ 191 | expr AND Expr .
+
+ $default reduce using rule 191 (Expr)
+
+
+state 297
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 190 Expr: Expr OR expr .
+ 191 | expr . AND Expr
+ 192 | expr . OR Expr
+
+ AND shift, and go to state 217
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 190 (Expr)
+
+
+state 298
+
+ 187 Expr: Expr . AND Expr
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 189 | Expr OR Expr .
+ 190 | Expr . OR expr
+
+ AND shift, and go to state 219
+
+ $default reduce using rule 189 (Expr)
+
+
+state 299
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 188 Expr: Expr AND expr .
+ 191 | expr . AND Expr
+ 192 | expr . OR Expr
+
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+
+ $default reduce using rule 188 (Expr)
+
+
+state 300
+
+ 187 Expr: Expr . AND Expr
+ 187 | Expr AND Expr .
+ 188 | Expr . AND expr
+ 189 | Expr . OR Expr
+ 190 | Expr . OR expr
+
+ $default reduce using rule 187 (Expr)
+
+
+state 301
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' . @2 Opt_priority Opt_enabler body
+
+ $default reduce using rule 15 (@2)
+
+ @2 go to state 343
+
+
+state 302
+
+ 75 ch_init: '[' CONST ']' . OF '{' typ_list '}'
+
+ OF shift, and go to state 344
+
+
+state 303
+
+ 161 expr: RUN aname @26 '(' . args ')' Opt_priority
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ $default reduce using rule 202 (args)
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ args go to state 345
+ arg go to state 104
+
+
+state 304
+
+ 162 expr: LEN '(' varref ')' .
+
+ $default reduce using rule 162 (expr)
+
+
+state 305
+
+ 163 expr: ENABLED '(' expr ')' .
+
+ $default reduce using rule 163 (expr)
+
+
+state 306
+
+ 173 expr: PC_VAL '(' expr ')' .
+
+ $default reduce using rule 173 (expr)
+
+
+state 307
+
+ 82 pfld: NAME @10 '[' expr . ']'
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ']' shift, and go to state 346
+
+
+state 308
+
+ 174 expr: PNAME '[' expr ']' . '@' NAME
+ 175 | PNAME '[' expr ']' . ':' pfld
+
+ ':' shift, and go to state 347
+ '@' shift, and go to state 348
+
+
+state 309
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 159 | '(' expr SEMI expr . ':' expr ')'
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ':' shift, and go to state 349
+
+
+state 310
+
+ 167 expr: varref R_RCV @28 '[' . rargs ']'
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 350
+
+
+state 311
+
+ 165 expr: varref RCV @27 '[' . rargs ']'
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 351
+
+
+state 312
+
+ 86 sfld: '.' cmpnd .
+
+ $default reduce using rule 86 (sfld)
+
+
+state 313
+
+ 103 Stmnt: PRINT '(' STRING @15 . prargs ')'
+
+ ',' shift, and go to state 352
+
+ $default reduce using rule 204 (prargs)
+
+ prargs go to state 353
+
+
+state 314
+
+ 105 Stmnt: PRINTM '(' CONST ')' .
+
+ $default reduce using rule 105 (Stmnt)
+
+
+state 315
+
+ 104 Stmnt: PRINTM '(' varref ')' .
+
+ $default reduce using rule 104 (Stmnt)
+
+
+state 316
+
+ 48 sequence: sequence . MS step
+ 130 option: SEP @25 sequence . OS
+
+ SEMI shift, and go to state 205
+
+ $default reduce using rule 131 (OS)
+
+ OS go to state 354
+ MS go to state 207
+
+
+state 317
+
+ 95 Special: DO @14 options OD .
+
+ $default reduce using rule 95 (Special)
+
+
+state 318
+
+ 48 sequence: sequence . MS step
+ 119 Stmnt: ATOMIC '{' @20 sequence . OS '}'
+
+ SEMI shift, and go to state 205
+
+ $default reduce using rule 131 (OS)
+
+ OS go to state 355
+ MS go to state 207
+
+
+state 319
+
+ 48 sequence: sequence . MS step
+ 121 Stmnt: D_STEP '{' @21 sequence . OS '}'
+
+ SEMI shift, and go to state 205
+
+ $default reduce using rule 131 (OS)
+
+ OS go to state 356
+ MS go to state 207
+
+
+state 320
+
+ 193 Probe: FULL '(' varref ')' .
+
+ $default reduce using rule 193 (Probe)
+
+
+state 321
+
+ 195 Probe: EMPTY '(' varref ')' .
+
+ $default reduce using rule 195 (Probe)
+
+
+state 322
+
+ 194 Probe: NFULL '(' varref ')' .
+
+ $default reduce using rule 194 (Probe)
+
+
+state 323
+
+ 196 Probe: NEMPTY '(' varref ')' .
+
+ $default reduce using rule 196 (Probe)
+
+
+state 324
+
+ 69 vref_lst: varref ',' vref_lst .
+
+ $default reduce using rule 69 (vref_lst)
+
+
+state 325
+
+ 98 Special: NAME ':' . stmnt
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ GOTO shift, and go to state 108
+ BREAK shift, and go to state 109
+ ELSE shift, and go to state 110
+ IF shift, and go to state 111
+ DO shift, and go to state 112
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 276
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ ccode go to state 124
+ cexpr go to state 98
+ varref go to state 128
+ pfld go to state 100
+ cmpnd go to state 101
+ stmnt go to state 278
+ Special go to state 130
+ Stmnt go to state 131
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 326
+
+ 126 Stmnt: INAME @23 '(' args . ')' @24 Stmnt
+
+ ')' shift, and go to state 357
+
+
+state 327
+
+ 123 Stmnt: '{' @22 sequence OS . '}'
+
+ '}' shift, and go to state 358
+
+
+state 328
+
+ 46 body: '{' @8 sequence OS @9 '}' .
+
+ $default reduce using rule 46 (body)
+
+
+state 329
+
+ 211 rarg: EVAL . '(' expr ')'
+
+ '(' shift, and go to state 359
+
+
+state 330
+
+ 212 rarg: CONST .
+
+ $default reduce using rule 212 (rarg)
+
+
+state 331
+
+ 213 rarg: '-' . CONST
+
+ CONST shift, and go to state 360
+
+
+state 332
+
+ 217 rargs: '(' . rargs ')'
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 361
+
+
+state 333
+
+ 210 rarg: varref .
+
+ $default reduce using rule 210 (rarg)
+
+
+state 334
+
+ 214 rargs: rarg .
+ 215 | rarg . ',' rargs
+ 216 | rarg . '(' rargs ')'
+
+ '(' shift, and go to state 362
+ ',' shift, and go to state 363
+
+ $default reduce using rule 214 (rargs)
+
+
+state 335
+
+ 109 Stmnt: varref R_RCV @16 rargs .
+
+ $default reduce using rule 109 (Stmnt)
+
+
+state 336
+
+ 113 Stmnt: varref R_RCV @18 LT . rargs GT
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 364
+
+
+state 337
+
+ 90 Special: varref RCV @12 rargs .
+
+ $default reduce using rule 90 (Special)
+
+
+state 338
+
+ 111 Stmnt: varref RCV @17 LT . rargs GT
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 365
+
+
+state 339
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 207 margs: expr . '(' arg ')'
+ 208 arg: expr .
+ 209 | expr . ',' arg
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ '(' shift, and go to state 366
+ ',' shift, and go to state 182
+
+ $default reduce using rule 208 (arg)
+
+
+state 340
+
+ 115 Stmnt: varref O_SND @19 margs .
+
+ $default reduce using rule 115 (Stmnt)
+
+
+state 341
+
+ 206 margs: arg .
+
+ $default reduce using rule 206 (margs)
+
+
+state 342
+
+ 92 Special: varref SND @13 margs .
+
+ $default reduce using rule 92 (Special)
+
+
+state 343
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' @2 . Opt_priority Opt_enabler body
+
+ PRIORITY shift, and go to state 57
+
+ $default reduce using rule 178 (Opt_priority)
+
+ Opt_priority go to state 367
+
+
+state 344
+
+ 75 ch_init: '[' CONST ']' OF . '{' typ_list '}'
+
+ '{' shift, and go to state 368
+
+
+state 345
+
+ 161 expr: RUN aname @26 '(' args . ')' Opt_priority
+
+ ')' shift, and go to state 369
+
+
+state 346
+
+ 82 pfld: NAME @10 '[' expr ']' .
+
+ $default reduce using rule 82 (pfld)
+
+
+state 347
+
+ 175 expr: PNAME '[' expr ']' ':' . pfld
+
+ NAME shift, and go to state 92
+
+ pfld go to state 370
+
+
+state 348
+
+ 174 expr: PNAME '[' expr ']' '@' . NAME
+
+ NAME shift, and go to state 371
+
+
+state 349
+
+ 159 expr: '(' expr SEMI expr ':' . expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 372
+
+
+state 350
+
+ 167 expr: varref R_RCV @28 '[' rargs . ']'
+
+ ']' shift, and go to state 373
+
+
+state 351
+
+ 165 expr: varref RCV @27 '[' rargs . ']'
+
+ ']' shift, and go to state 374
+
+
+state 352
+
+ 205 prargs: ',' . arg
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ arg go to state 375
+
+
+state 353
+
+ 103 Stmnt: PRINT '(' STRING @15 prargs . ')'
+
+ ')' shift, and go to state 376
+
+
+state 354
+
+ 130 option: SEP @25 sequence OS .
+
+ $default reduce using rule 130 (option)
+
+
+state 355
+
+ 119 Stmnt: ATOMIC '{' @20 sequence OS . '}'
+
+ '}' shift, and go to state 377
+
+
+state 356
+
+ 121 Stmnt: D_STEP '{' @21 sequence OS . '}'
+
+ '}' shift, and go to state 378
+
+
+state 357
+
+ 126 Stmnt: INAME @23 '(' args ')' . @24 Stmnt
+
+ $default reduce using rule 125 (@24)
+
+ @24 go to state 379
+
+
+state 358
+
+ 123 Stmnt: '{' @22 sequence OS '}' .
+
+ $default reduce using rule 123 (Stmnt)
+
+
+state 359
+
+ 211 rarg: EVAL '(' . expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 380
+
+
+state 360
+
+ 213 rarg: '-' CONST .
+
+ $default reduce using rule 213 (rarg)
+
+
+state 361
+
+ 217 rargs: '(' rargs . ')'
+
+ ')' shift, and go to state 381
+
+
+state 362
+
+ 216 rargs: rarg '(' . rargs ')'
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 382
+
+
+state 363
+
+ 215 rargs: rarg ',' . rargs
+
+ EVAL shift, and go to state 329
+ CONST shift, and go to state 330
+ NAME shift, and go to state 92
+ '-' shift, and go to state 331
+ '(' shift, and go to state 332
+
+ varref go to state 333
+ pfld go to state 100
+ cmpnd go to state 101
+ rarg go to state 334
+ rargs go to state 383
+
+
+state 364
+
+ 113 Stmnt: varref R_RCV @18 LT rargs . GT
+
+ GT shift, and go to state 384
+
+
+state 365
+
+ 111 Stmnt: varref RCV @17 LT rargs . GT
+
+ GT shift, and go to state 385
+
+
+state 366
+
+ 207 margs: expr '(' . arg ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 97
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 102
+ arg go to state 386
+
+
+state 367
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' @2 Opt_priority . Opt_enabler body
+
+ PROVIDED shift, and go to state 387
+
+ $default reduce using rule 182 (Opt_enabler)
+
+ Opt_enabler go to state 388
+
+
+state 368
+
+ 75 ch_init: '[' CONST ']' OF '{' . typ_list '}'
+
+ error shift, and go to state 389
+ TYPE shift, and go to state 390
+ UNAME shift, and go to state 391
+
+ basetype go to state 392
+ typ_list go to state 393
+
+
+state 369
+
+ 161 expr: RUN aname @26 '(' args ')' . Opt_priority
+
+ PRIORITY shift, and go to state 57
+
+ $default reduce using rule 178 (Opt_priority)
+
+ Opt_priority go to state 394
+
+
+state 370
+
+ 175 expr: PNAME '[' expr ']' ':' pfld .
+
+ $default reduce using rule 175 (expr)
+
+
+state 371
+
+ 174 expr: PNAME '[' expr ']' '@' NAME .
+
+ $default reduce using rule 174 (expr)
+
+
+state 372
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 159 | '(' expr SEMI expr ':' expr . ')'
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 395
+
+
+state 373
+
+ 167 expr: varref R_RCV @28 '[' rargs ']' .
+
+ $default reduce using rule 167 (expr)
+
+
+state 374
+
+ 165 expr: varref RCV @27 '[' rargs ']' .
+
+ $default reduce using rule 165 (expr)
+
+
+state 375
+
+ 205 prargs: ',' arg .
+
+ $default reduce using rule 205 (prargs)
+
+
+state 376
+
+ 103 Stmnt: PRINT '(' STRING @15 prargs ')' .
+
+ $default reduce using rule 103 (Stmnt)
+
+
+state 377
+
+ 119 Stmnt: ATOMIC '{' @20 sequence OS '}' .
+
+ $default reduce using rule 119 (Stmnt)
+
+
+state 378
+
+ 121 Stmnt: D_STEP '{' @21 sequence OS '}' .
+
+ $default reduce using rule 121 (Stmnt)
+
+
+state 379
+
+ 126 Stmnt: INAME @23 '(' args ')' @24 . Stmnt
+
+ ASSERT shift, and go to state 105
+ PRINT shift, and go to state 106
+ PRINTM shift, and go to state 107
+ C_CODE shift, and go to state 2
+ C_DECL shift, and go to state 3
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ ELSE shift, and go to state 110
+ ATOMIC shift, and go to state 113
+ D_STEP shift, and go to state 114
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ INAME shift, and go to state 121
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+ '{' shift, and go to state 123
+
+ ccode go to state 124
+ cexpr go to state 98
+ varref go to state 396
+ pfld go to state 100
+ cmpnd go to state 101
+ Stmnt go to state 397
+ expr go to state 132
+ full_expr go to state 133
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 380
+
+ 138 expr: expr . '+' expr
+ 139 | expr . '-' expr
+ 140 | expr . '*' expr
+ 141 | expr . '/' expr
+ 142 | expr . '%' expr
+ 143 | expr . '&' expr
+ 144 | expr . '^' expr
+ 145 | expr . '|' expr
+ 146 | expr . GT expr
+ 147 | expr . LT expr
+ 148 | expr . GE expr
+ 149 | expr . LE expr
+ 150 | expr . EQ expr
+ 151 | expr . NE expr
+ 152 | expr . AND expr
+ 153 | expr . OR expr
+ 154 | expr . LSHIFT expr
+ 155 | expr . RSHIFT expr
+ 211 rarg: EVAL '(' expr . ')'
+
+ OR shift, and go to state 164
+ AND shift, and go to state 165
+ '|' shift, and go to state 166
+ '^' shift, and go to state 167
+ '&' shift, and go to state 168
+ NE shift, and go to state 169
+ EQ shift, and go to state 170
+ LE shift, and go to state 171
+ GE shift, and go to state 172
+ LT shift, and go to state 173
+ GT shift, and go to state 174
+ RSHIFT shift, and go to state 175
+ LSHIFT shift, and go to state 176
+ '+' shift, and go to state 177
+ '-' shift, and go to state 178
+ '*' shift, and go to state 179
+ '/' shift, and go to state 180
+ '%' shift, and go to state 181
+ ')' shift, and go to state 398
+
+
+state 381
+
+ 217 rargs: '(' rargs ')' .
+
+ $default reduce using rule 217 (rargs)
+
+
+state 382
+
+ 216 rargs: rarg '(' rargs . ')'
+
+ ')' shift, and go to state 399
+
+
+state 383
+
+ 215 rargs: rarg ',' rargs .
+
+ $default reduce using rule 215 (rargs)
+
+
+state 384
+
+ 113 Stmnt: varref R_RCV @18 LT rargs GT .
+
+ $default reduce using rule 113 (Stmnt)
+
+
+state 385
+
+ 111 Stmnt: varref RCV @17 LT rargs GT .
+
+ $default reduce using rule 111 (Stmnt)
+
+
+state 386
+
+ 207 margs: expr '(' arg . ')'
+
+ ')' shift, and go to state 400
+
+
+state 387
+
+ 183 Opt_enabler: PROVIDED . '(' full_expr ')'
+ 184 | PROVIDED . error
+
+ error shift, and go to state 401
+ '(' shift, and go to state 402
+
+
+state 388
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' @2 Opt_priority Opt_enabler . body
+
+ '{' shift, and go to state 54
+
+ body go to state 403
+
+
+state 389
+
+ 199 basetype: error .
+
+ $default reduce using rule 199 (basetype)
+
+
+state 390
+
+ 197 basetype: TYPE .
+
+ $default reduce using rule 197 (basetype)
+
+
+state 391
+
+ 198 basetype: UNAME .
+
+ $default reduce using rule 198 (basetype)
+
+
+state 392
+
+ 200 typ_list: basetype .
+ 201 | basetype . ',' typ_list
+
+ ',' shift, and go to state 404
+
+ $default reduce using rule 200 (typ_list)
+
+
+state 393
+
+ 75 ch_init: '[' CONST ']' OF '{' typ_list . '}'
+
+ '}' shift, and go to state 405
+
+
+state 394
+
+ 161 expr: RUN aname @26 '(' args ')' Opt_priority .
+
+ $default reduce using rule 161 (expr)
+
+
+state 395
+
+ 159 expr: '(' expr SEMI expr ':' expr ')' .
+
+ $default reduce using rule 159 (expr)
+
+
+state 396
+
+ 99 Stmnt: varref . ASGN expr
+ 100 | varref . INCR
+ 101 | varref . DECR
+ 109 | varref . R_RCV @16 rargs
+ 111 | varref . RCV @17 LT rargs GT
+ 113 | varref . R_RCV @18 LT rargs GT
+ 115 | varref . O_SND @19 margs
+ 165 expr: varref . RCV @27 '[' rargs ']'
+ 167 | varref . R_RCV @28 '[' rargs ']'
+ 168 | varref .
+
+ ASGN shift, and go to state 208
+ R_RCV shift, and go to state 209
+ RCV shift, and go to state 406
+ O_SND shift, and go to state 211
+ DECR shift, and go to state 213
+ INCR shift, and go to state 214
+
+ $default reduce using rule 168 (expr)
+
+
+state 397
+
+ 126 Stmnt: INAME @23 '(' args ')' @24 Stmnt .
+
+ $default reduce using rule 126 (Stmnt)
+
+
+state 398
+
+ 211 rarg: EVAL '(' expr ')' .
+
+ $default reduce using rule 211 (rarg)
+
+
+state 399
+
+ 216 rargs: rarg '(' rargs ')' .
+
+ $default reduce using rule 216 (rargs)
+
+
+state 400
+
+ 207 margs: expr '(' arg ')' .
+
+ $default reduce using rule 207 (margs)
+
+
+state 401
+
+ 184 Opt_enabler: PROVIDED error .
+
+ $default reduce using rule 184 (Opt_enabler)
+
+
+state 402
+
+ 183 Opt_enabler: PROVIDED '(' . full_expr ')'
+
+ C_EXPR shift, and go to state 84
+ RUN shift, and go to state 85
+ LEN shift, and go to state 86
+ ENABLED shift, and go to state 87
+ PC_VAL shift, and go to state 88
+ TIMEOUT shift, and go to state 89
+ NONPROGRESS shift, and go to state 90
+ FULL shift, and go to state 115
+ EMPTY shift, and go to state 116
+ NFULL shift, and go to state 117
+ NEMPTY shift, and go to state 118
+ CONST shift, and go to state 91
+ NAME shift, and go to state 92
+ PNAME shift, and go to state 93
+ SND shift, and go to state 94
+ '-' shift, and go to state 95
+ '~' shift, and go to state 96
+ '(' shift, and go to state 122
+
+ cexpr go to state 98
+ varref go to state 99
+ pfld go to state 100
+ cmpnd go to state 101
+ expr go to state 132
+ full_expr go to state 407
+ Expr go to state 134
+ Probe go to state 135
+
+
+state 403
+
+ 16 proc: inst proctype NAME @1 '(' decl ')' @2 Opt_priority Opt_enabler body .
+
+ $default reduce using rule 16 (proc)
+
+
+state 404
+
+ 201 typ_list: basetype ',' . typ_list
+
+ error shift, and go to state 389
+ TYPE shift, and go to state 390
+ UNAME shift, and go to state 391
+
+ basetype go to state 392
+ typ_list go to state 408
+
+
+state 405
+
+ 75 ch_init: '[' CONST ']' OF '{' typ_list '}' .
+
+ $default reduce using rule 75 (ch_init)
+
+
+state 406
+
+ 111 Stmnt: varref RCV . @17 LT rargs GT
+ 165 expr: varref RCV . @27 '[' rargs ']'
+
+ '[' reduce using rule 164 (@27)
+ $default reduce using rule 110 (@17)
+
+ @17 go to state 289
+ @27 go to state 239
+
+
+state 407
+
+ 183 Opt_enabler: PROVIDED '(' full_expr . ')'
+
+ ')' shift, and go to state 409
+
+
+state 408
+
+ 201 typ_list: basetype ',' typ_list .
+
+ $default reduce using rule 201 (typ_list)
+
+
+state 409
+
+ 183 Opt_enabler: PROVIDED '(' full_expr ')' .
+
+ $default reduce using rule 183 (Opt_enabler)
--- /dev/null
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ASSERT = 258,
+ PRINT = 259,
+ PRINTM = 260,
+ C_CODE = 261,
+ C_DECL = 262,
+ C_EXPR = 263,
+ C_STATE = 264,
+ C_TRACK = 265,
+ RUN = 266,
+ LEN = 267,
+ ENABLED = 268,
+ EVAL = 269,
+ PC_VAL = 270,
+ TYPEDEF = 271,
+ MTYPE = 272,
+ INLINE = 273,
+ LABEL = 274,
+ OF = 275,
+ GOTO = 276,
+ BREAK = 277,
+ ELSE = 278,
+ SEMI = 279,
+ IF = 280,
+ FI = 281,
+ DO = 282,
+ OD = 283,
+ SEP = 284,
+ ATOMIC = 285,
+ NON_ATOMIC = 286,
+ D_STEP = 287,
+ UNLESS = 288,
+ TIMEOUT = 289,
+ NONPROGRESS = 290,
+ ACTIVE = 291,
+ PROCTYPE = 292,
+ D_PROCTYPE = 293,
+ HIDDEN = 294,
+ SHOW = 295,
+ ISLOCAL = 296,
+ PRIORITY = 297,
+ PROVIDED = 298,
+ FULL = 299,
+ EMPTY = 300,
+ NFULL = 301,
+ NEMPTY = 302,
+ CONST = 303,
+ TYPE = 304,
+ XU = 305,
+ NAME = 306,
+ UNAME = 307,
+ PNAME = 308,
+ INAME = 309,
+ STRING = 310,
+ CLAIM = 311,
+ TRACE = 312,
+ INIT = 313,
+ ASGN = 314,
+ R_RCV = 315,
+ RCV = 316,
+ O_SND = 317,
+ SND = 318,
+ OR = 319,
+ AND = 320,
+ NE = 321,
+ EQ = 322,
+ LE = 323,
+ GE = 324,
+ LT = 325,
+ GT = 326,
+ RSHIFT = 327,
+ LSHIFT = 328,
+ DECR = 329,
+ INCR = 330,
+ NEG = 331,
+ UMIN = 332,
+ DOT = 333
+ };
+#endif
+/* Tokens. */
+#define ASSERT 258
+#define PRINT 259
+#define PRINTM 260
+#define C_CODE 261
+#define C_DECL 262
+#define C_EXPR 263
+#define C_STATE 264
+#define C_TRACK 265
+#define RUN 266
+#define LEN 267
+#define ENABLED 268
+#define EVAL 269
+#define PC_VAL 270
+#define TYPEDEF 271
+#define MTYPE 272
+#define INLINE 273
+#define LABEL 274
+#define OF 275
+#define GOTO 276
+#define BREAK 277
+#define ELSE 278
+#define SEMI 279
+#define IF 280
+#define FI 281
+#define DO 282
+#define OD 283
+#define SEP 284
+#define ATOMIC 285
+#define NON_ATOMIC 286
+#define D_STEP 287
+#define UNLESS 288
+#define TIMEOUT 289
+#define NONPROGRESS 290
+#define ACTIVE 291
+#define PROCTYPE 292
+#define D_PROCTYPE 293
+#define HIDDEN 294
+#define SHOW 295
+#define ISLOCAL 296
+#define PRIORITY 297
+#define PROVIDED 298
+#define FULL 299
+#define EMPTY 300
+#define NFULL 301
+#define NEMPTY 302
+#define CONST 303
+#define TYPE 304
+#define XU 305
+#define NAME 306
+#define UNAME 307
+#define PNAME 308
+#define INAME 309
+#define STRING 310
+#define CLAIM 311
+#define TRACE 312
+#define INIT 313
+#define ASGN 314
+#define R_RCV 315
+#define RCV 316
+#define O_SND 317
+#define SND 318
+#define OR 319
+#define AND 320
+#define NE 321
+#define EQ 322
+#define LE 323
+#define GE 324
+#define LT 325
+#define GT 326
+#define RSHIFT 327
+#define LSHIFT 328
+#define DECR 329
+#define INCR 330
+#define NEG 331
+#define UMIN 332
+#define DOT 333
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
--- /dev/null
+-- Updated for SPIN Version 5.0 --- October 2007 ---
+
+Perform tests test0 to test5 in the order listed, and
+make sure you get the same results.
+The next four tests are to assess the effect of
+partial order reductions. In exhaustive mode, they
+may not all be executable within the bounds of your
+system - with reduction turned on, though, they should
+all run as specified.
+The last test checks the use of execution priorities
+during random simulations.
+
+The file called 'pathfinder' illustrates the use of
+'provided' clauses (using as inspiration the bug in
+the control software of the Mars pathfinder that
+spotted an otherwise perfect mission in July 1997)
+
+ You can always check valid options of spin
+ by typing (at command prompt $):
+ $ spin --
+
+ Similarly, you can check valid options of
+ the compiled verifiers by typing:
+ $ pan --
+
+test 0 check that spin exists, is executable, and is
+ the version that you expect
+
+ $ spin -V
+ Spin Version 5.0 -- 26 October 2007
+
+test 1 check that you can run a simulation
+
+ $ spin hello
+ passed first test!
+ 1 process created
+
+ or without the default indenting of output:
+
+ $ spin -T hello
+ passed first test!
+ 1 process created
+
+test 2 a basic reachability check (safety)
+
+ $ spin -a loops # generate c-verifier
+ $ cc -DNOREDUCE -o pan pan.c # no reduction (test)
+ $ ./pan # default run
+ hint: this search is more efficient if pan.c is compiled -DSAFETY
+
+ (Spin Version 5.0 -- 26 October 2007)
+
+ Full statespace search for:
+ never-claim - (none specified)
+ assertion violations +
+ acceptance cycles - (not selected)
+ invalid endstates +
+
+ State-vector 12 byte, depth reached 11, errors: 0
+ 15 states, stored
+ 4 states, matched
+ 19 transitions (= stored+matched)
+ 0 atomic steps
+ hash conflicts: 0 (resolved)
+
+ 2.501 memory usage (Mbyte)
+
+ unreached in proctype loop
+ line 12, state 12, "-end-"
+ (1 of 12 states)
+
+ pan: elapsed time 0 seconds
+
+test 3 cycle detection check (liveness):
+
+ $ ./pan -a # search for acceptance cycles
+ pan: acceptance cycle (at depth 0)
+ pan: wrote loops.trail
+ (Spin Version 5.0 -- 26 October 2007)
+ Warning: Search not completed
+
+ Full statespace search for:
+ never-claim - (none specified)
+ assertion violations +
+> acceptance cycles + (fairness disabled)
+ invalid endstates +
+
+ State-vector 12 byte, depth reached 11, errors: 1
+ 13 states, stored (15 visited)
+ 2 states, matched
+ 17 transitions (= visited+matched)
+ 0 atomic steps
+ hash conflicts: 0 (resolved)
+
+ 2.501 memory usage (Mbyte)
+
+ pan: elapsed time 0.015 seconds
+ pan: rate 1000 states/second
+
+test 4 guided simulation check (playback the error trail found in test 3)
+
+ $ spin -t -p loops # guided simulation for the error-cycle
+ Starting loop with pid 0
+ <<<<<START OF CYCLE>>>>>
+ 1: proc 0 (loop) line 5 "loops" (state 1) [a = ((a+1)%3)]
+ 2: proc 0 (loop) line 7 "loops" (state 2) [b = (2*a)]
+ 3: proc 0 (loop) line 7 "loops" (state 3) [(1)]
+ 4: proc 0 (loop) line 10 "loops" (state 8) [b = (b-1)]
+ 5: proc 0 (loop) line 5 "loops" (state 1) [a = ((a+1)%3)]
+ 6: proc 0 (loop) line 7 "loops" (state 2) [b = (2*a)]
+ 7: proc 0 (loop) line 7 "loops" (state 3) [(1)]
+ 8: proc 0 (loop) line 10 "loops" (state 8) [b = (b-1)]
+ 9: proc 0 (loop) line 5 "loops" (state 1) [a = ((a+1)%3)]
+ 10: proc 0 (loop) line 8 "loops" (state 4) [b = (2*a)]
+ 11: proc 0 (loop) line 8 "loops" (state 5) [(1)]
+ spin: line 10 "loops", Error: value (-1->255 (8)) truncated in assignment
+ 12: proc 0 (loop) line 10 "loops" (state 8) [b = (b-1)]
+ spin: trail ends after 12 steps
+ #processes: 1
+ 12: proc 0 (loop) line 4 "loops" (state 9)
+ 1 process created
+
+test 5 try to find a cycle that avoids the progress labels (there are none)
+
+ $ cc -DNP -DNOREDUCE -o pan pan.c # add support for non-progress
+ $ ./pan -l # search for non-progress cycles
+
+ (Spin Version 5.0 -- 26 October 2007)
+
+ Full statespace search for:
+ never claim +
+ assertion violations + (if within scope of claim)
+ non-progress cycles + (fairness disabled)
+ invalid end states - (disabled by never claim)
+
+ State-vector 16 byte, depth reached 23, errors: 0
+ 27 states, stored (39 visited)
+ 28 states, matched
+ 67 transitions (= visited+matched)
+ 0 atomic steps
+ hash conflicts: 0 (resolved)
+
+ 2.501 memory usage (Mbyte)
+
+ unreached in proctype loop
+ line 12, state 12, "-end-"
+ (1 of 12 states)
+
+ pan: elapsed time 0 seconds
+
+test 6: check partial order reduction algorithm -- first disable it
+
+ $ spin -a leader (or snoopy, pftp, sort)
+ $ cc -DSAFETY -DNOREDUCE -DNOCLAIM -o pan pan.c # safety only
+ $ ./pan -c0 -n # exhaustive, -c0 = ignore errors
+ (Spin Version 5.0 -- 26 October 2007)
+
+ Full statespace search for:
+ never claim - (not selected)
+ assertion violations +
+ cycle checks - (disabled by -DSAFETY)
+ invalid end states +
+
+ State-vector 196 byte, depth reached 108, errors: 0
+ 15779 states, stored
+ 42402 states, matched
+ 58181 transitions (= stored+matched)
+ 12 atomic steps
+ hash conflicts: 440 (resolved)
+
+ Stats on memory usage (in Megabytes):
+ 3.010 equivalent memory usage for states (stored*(State-vector + overhead))
+ 2.731 actual memory usage for states (compression: 90.73%)
+ state-vector as stored = 177 byte + 4 byte overhead
+ 2.000 memory used for hash table (-w19)
+ 0.267 memory used for DFS stack (-m10000)
+ 0.094 memory lost to fragmentation
+ 4.904 total actual memory usage
+
+ pan: elapsed time 0.125 seconds
+ pan: rate 126232 states/second
+
+test 6b: now leave p.o. reduction enabled (i.e., do not disable it)
+
+ $ cc -DSAFETY -DNOCLAIM -o pan pan.c # safety only, reduced search
+ $ ./pan -c0 -n # -n = no dead code listing
+
+ (Spin Version 5.0 -- 26 October 2007)
+ + Partial Order Reduction
+
+ Full statespace search for:
+ never claim - (not selected)
+ assertion violations +
+ cycle checks - (disabled by -DSAFETY)
+ invalid end states +
+
+ State-vector 196 byte, depth reached 108, errors: 0
+ 97 states, stored
+ 0 states, matched
+ 97 transitions (= stored+matched)
+ 12 atomic steps
+ hash conflicts: 0 (resolved)
+
+ 2.501 memory usage (Mbyte)
+
+ pan: elapsed time 0 seconds
+
+ If compiled as above, the results should match the results from the table below.
+ The numbers in the first two columns of the table should match precisely.
+ The numbers in the third column should match approximately (how well it matches
+ depends only on the properties of the C-compiler and the speed of the hardware
+ you use to run the tests.)
+ The first line for each test is for the exhaustive run, the second line is for
+ the default run using partial order reduction.
+ The times given are for a 2.4GHz dual-core Intel PC, with 2 GB of memory.
+
+ States Stored Transitions Memory Used Time (s)
+leader
+ S= 15779 T= 58181 M= 4.904 Mb t= 0.125
+ S= 97 T= 97 M= 2.501 Mb t= <0.1
+
+snoopy
+ S= 61619 T= 211398 M= 8.03 Mb t= 0.328
+ S= 9343 T= 12706 M= 3.38 Mb t= 0.03
+
+pftp
+ S= 144813 T= 397948 M= 18.97 Mb t= 0.79
+ S= 47356 T= 64970 M= 8.07 Mb t= 0.14
+
+sort
+ S= 107713 T= 512419 M= 18.87 Mb t= 1.08
+ S= 135 T= 135 M= 2.50 Mb t= <0.1
+
+
+test 7 check random number generator
+
+ $ spin -p -g -u10000 priorities # runs 10000 steps
+ ....
+ depth-limit (-u10000 steps) reached
+ #processes: 5
+ a[0] = 0
+ a[1] = 334
+ a[2] = 677
+ a[3] = 994
+ a[4] = 1327
+ 10000: proc 4 (A) line 11 "priorities" (state 3)
+ 10000: proc 3 (A) line 12 "priorities" (state 2)
+ 10000: proc 2 (A) line 14 "priorities" (state 4)
+ 10000: proc 1 (A) line 11 "priorities" (state 3)
+ 10000: proc 0 (:init:) line 22 "priorities" (state 6) <valid end state>
+ 5 processes created
+
+ The numbers in the array should tend to the ratio
+ 1:2:3:4 if the random number generator works properly.
+ array index 0 remains unused (it's the pid of the init
+ process)
+
+test 8 test the generation of never claims from LTL formulae:
+
+ $ spin -f "[] ( p U ( <> q ))"
+
+ never { /* [] ( p U ( <> q )) */
+ T0_init:
+ if
+ :: ((q)) -> goto accept_S9
+ :: (1) -> goto T0_init
+ fi;
+ accept_S9:
+ if
+ :: (1) -> goto T0_init
+ fi;
+ }
+
+test 9 read a never claim from a file
+
+ $ spin -a -N leader.ltl leader # the claim is in file leader.ltl
+ $ cc -o pan pan.c # the default compilation
+ $ ./pan -a # search for acceptance cycles
+ warning: for p.o. reduction to be valid the never claim must be stutter-invariant
+ (never claims generated from LTL formulae are stutter-invariant)
+
+ (Spin Version 5.0 -- 26 October 2007)
+ + Partial Order Reduction
+
+ Full statespace search for:
+ never claim +
+ assertion violations + (if within scope of claim)
+ acceptance cycles + (fairness disabled)
+ invalid end states - (disabled by never claim)
+
+ State-vector 204 byte, depth reached 205, errors: 0
+ 181 states, stored (360 visited)
+ 251 states, matched
+ 611 transitions (= visited+matched)
+ 24 atomic steps
+ hash conflicts: 0 (resolved)
+
+ 2.501 memory usage (Mbyte)
+
+ unreached in proctype node
+ line 53, state 28, "out!two,nr"
+ (1 of 49 states)
+ unreached in proctype :init:
+ (0 of 11 states)
+
+ pan: elapsed time 0 seconds
+
+end of tests
--- /dev/null
+/*
+ * a simple example of the use of inline's
+ * (requires Spin version 3.2 or later)
+ *
+ */
+
+mtype = { msg0, msg1, ack0, ack1 };
+
+chan sender = [1] of { mtype };
+chan receiver = [1] of { mtype };
+
+inline phase(msg, good_ack, bad_ack)
+{
+ do
+ :: sender?good_ack -> break
+ :: sender?bad_ack
+ :: timeout ->
+ if
+ :: receiver!msg;
+ :: skip /* lose message */
+ fi;
+ od
+}
+
+inline recv(cur_msg, cur_ack, lst_msg, lst_ack)
+{
+ do
+ :: receiver?cur_msg -> sender!cur_ack; break /* accept */
+ :: receiver?lst_msg -> sender!lst_ack
+ od;
+}
+
+active proctype Sender()
+{
+ do
+ :: phase(msg1, ack1, ack0);
+ phase(msg0, ack0, ack1)
+ od
+}
+
+active proctype Receiver()
+{
+ do
+ :: recv(msg1, ack1, msg0, ack0);
+ recv(msg0, ack0, msg1, ack1)
+ od
+}
--- /dev/null
+/*
+ The Sieve of Erathostenes
+ Prints all prime numbers up to PRIME_MAX
+*/
+#define PRIME_MAX 100
+
+chan count = [0] of { int };
+byte tries = 2;
+
+proctype sieve(chan c; int prime)
+{ int i, haschild;
+
+end: do
+ :: c?i ->
+ if
+ :: (i%prime) ->
+ if
+ :: !haschild ->
+ /* found a new prime */
+ printf("MSC: %d\n", i);
+ haschild++;
+ chan child = [0] of { int };
+ run sieve(child, i);
+ :: else ->
+ child!i
+ fi;
+ :: else
+ /* i is divisible by prime */
+ fi
+ od
+}
+
+init
+{
+ run sieve(count, 2);
+ do
+ :: (tries < PRIME_MAX) -> count!tries; tries++
+ :: (tries >= PRIME_MAX) -> break
+ od
+}
--- /dev/null
+/*
+ The Sieve of Eratosthenes (c. 276-196 BC)
+ Prints all prime numbers up to MAX
+*/
+#define MAX 25
+
+mtype = { number, eof };
+
+chan root = [0] of { mtype, int };
+
+proctype sieve(chan c; int prime)
+{ chan child = [0] of { mtype, int };
+ bool haschild;
+ int n;
+
+ printf("MSC: %d is prime\n", prime);
+end: do
+ :: c?number(n) ->
+ if
+ :: (n%prime) == 0 ->
+ printf("MSC: %d = %d*%d\n", n, prime, n/prime)
+ :: else ->
+ if
+ :: !haschild -> /* new prime */
+ haschild = true;
+ run sieve(child, n);
+ :: else ->
+ child!number(n)
+ fi;
+ fi
+ :: c?eof(0) ->
+ break
+ od;
+ if
+ :: haschild ->
+ child!eof(0)
+ :: else
+ fi
+}
+
+init
+{ int n = 2;
+
+ run sieve(root, n);
+ do
+ :: (n < MAX) -> n++; root!number(n)
+ :: (n >= MAX) -> root!eof(0); break
+ od
+}
--- /dev/null
+# To unbundle, sh this file
+echo ex.readme 1>&2
+sed 's/.//' >ex.readme <<'//GO.SYSIN DD ex.readme'
+-The 12 files in this bundle are the PROMELA
+-files of all exercises and examples discussed
+-in the document Doc/Exercises.ps from the
+-SPIN distribution.
+//GO.SYSIN DD ex.readme
+echo ex.1a 1>&2
+sed 's/.//' >ex.1a <<'//GO.SYSIN DD ex.1a'
+-init {
+- byte i = 0;
+- do
+- :: i = i+1
+- od
+-}
+//GO.SYSIN DD ex.1a
+echo ex.1b 1>&2
+sed 's/.//' >ex.1b <<'//GO.SYSIN DD ex.1b'
+-init {
+- chan dummy = [20] of { byte };
+- do
+- :: dummy!85
+- :: dummy!170
+- od
+-}
+//GO.SYSIN DD ex.1b
+echo ex.1c 1>&2
+sed 's/.//' >ex.1c <<'//GO.SYSIN DD ex.1c'
+-#define N 26
+-
+-int a;
+-byte b;
+-
+-init {
+- do
+- :: atomic { (b < N) ->
+- if
+- :: a = a + (1<<b)
+- :: skip
+- fi;
+- b=b+1 }
+- od
+-}
+//GO.SYSIN DD ex.1c
+echo ex.2 1>&2
+sed 's/.//' >ex.2 <<'//GO.SYSIN DD ex.2'
+-#define MAX 8
+-proctype A(chan in, out)
+-{ byte mt; /* message data */
+- bit vr;
+-S1: mt = (mt+1)%MAX;
+- out!mt,1;
+- goto S2;
+-S2: in?vr;
+- if
+- :: (vr == 1) -> goto S1
+- :: (vr == 0) -> goto S3
+- :: printf("AERROR1\n") -> goto S5
+- fi;
+-S3: out!mt,1;
+- goto S4;
+-S4: in?vr;
+- if
+- :: goto S1
+- :: printf("AERROR2\n"); goto S5
+- fi;
+-S5: out!mt,0;
+- goto S4
+-}
+-proctype B(chan in, out)
+-{ byte mr, lmr;
+- bit ar;
+- goto S2; /* initial state */
+-S1: assert(mr == (lmr+1)%MAX);
+- lmr = mr;
+- out!1;
+- goto S2;
+-S2: in?mr,ar;
+- if
+- :: (ar == 1) -> goto S1
+- :: (ar == 0) -> goto S3
+- :: printf("ERROR1\n"); goto S5
+- fi;
+-S3: out!1;
+- goto S2;
+-S4: in?mr,ar;
+- if
+- :: goto S1
+- :: printf("ERROR2\n"); goto S5
+- fi;
+-S5: out!0;
+- goto S4
+-}
+-init {
+- chan a2b = [2] of { bit };
+- chan b2a = [2] of { byte, bit };
+- atomic {
+- run A(a2b, b2a);
+- run B(b2a, a2b)
+- }
+-}
+//GO.SYSIN DD ex.2
+echo ex.3 1>&2
+sed 's/.//' >ex.3 <<'//GO.SYSIN DD ex.3'
+-mtype = { a, b, c, d, e, i, l, m, n, q, r, u, v };
+-
+-chan dce = [0] of { mtype };
+-chan dte = [0] of { mtype };
+-
+-active proctype dte_proc()
+-{
+-state01:
+- do
+- :: dce!b -> /* state21 */ dce!a
+- :: dce!i -> /* state14 */
+- if
+- :: dte?m -> goto state19
+- :: dce!a
+- fi
+- :: dte?m -> goto state18
+- :: dte?u -> goto state08
+- :: dce!d -> break
+- od;
+-
+- /* state02: */
+- if
+- :: dte?v
+- :: dte?u -> goto state15
+- :: dte?m -> goto state19
+- fi;
+-state03:
+- dce!e;
+- /* state04: */
+- if
+- :: dte?m -> goto state19
+- :: dce!c
+- fi;
+- /* state05: */
+- if
+- :: dte?m -> goto state19
+- :: dte?r
+- fi;
+- /* state6A: */
+- if
+- :: dte?m -> goto state19
+- :: dte?q
+- fi;
+-state07:
+- if
+- :: dte?m -> goto state19
+- :: dte?r
+- fi;
+- /* state6B: */
+- if /* non-deterministic select */
+- :: dte?q -> goto state07
+- :: dte?q
+- :: dte?m -> goto state19
+- fi;
+- /* state10: */
+- if
+- :: dte?m -> goto state19
+- :: dte?r
+- fi;
+-state6C:
+- if
+- :: dte?m -> goto state19
+- :: dte?l
+- fi;
+- /* state11: */
+- if
+- :: dte?m -> goto state19
+- :: dte?n
+- fi;
+- /* state12: */
+- if
+- :: dte?m -> goto state19
+- :: dce!b -> goto state16
+- fi;
+-
+-state15:
+- if
+- :: dte?v -> goto state03
+- :: dte?m -> goto state19
+- fi;
+-state08:
+- if
+- :: dce!c
+- :: dce!d -> goto state15
+- :: dte?m -> goto state19
+- fi;
+- /* state09: */
+- if
+- :: dte?m -> goto state19
+- :: dte?q
+- fi;
+- /* state10B: */
+- if
+- :: dte?r -> goto state6C
+- :: dte?m -> goto state19
+- fi;
+-state18:
+- if
+- :: dte?l -> goto state01
+- :: dte?m -> goto state19
+- fi;
+-state16:
+- dte?m;
+- /* state17: */
+- dte?l;
+- /* state21: */
+- dce!a; goto state01;
+-state19:
+- dce!b;
+- /* state20: */
+- dte?l;
+- /* state21: */
+- dce!a; goto state01
+-}
+-
+-active proctype dce_proc()
+-{
+-state01:
+- do
+- :: dce?b -> /* state21 */ dce?a
+- :: dce?i -> /* state14 */
+- if
+- :: dce?b -> goto state16
+- :: dce?a
+- fi
+- :: dte!m -> goto state18
+- :: dte!u -> goto state08
+- :: dce?d -> break
+- od;
+-
+- /* state02: */
+- if
+- :: dte!v
+- :: dte!u -> goto state15
+- :: dce?b -> goto state16
+- fi;
+-state03:
+- dce?e;
+- /* state04: */
+- if
+- :: dce?b -> goto state16
+- :: dce?c
+- fi;
+- /* state05: */
+- if
+- :: dce?b -> goto state16
+- :: dte!r
+- fi;
+- /* state6A: */
+- if
+- :: dce?b -> goto state16
+- :: dte!q
+- fi;
+-state07:
+- if
+- :: dce?b -> goto state16
+- :: dte!r
+- fi;
+- /* state6B: */
+- if /* non-deterministic select */
+- :: dte!q -> goto state07
+- :: dte!q
+- :: dce?b -> goto state16
+- fi;
+- /* state10: */
+- if
+- :: dce?b -> goto state16
+- :: dte!r
+- fi;
+-state6C:
+- if
+- :: dce?b -> goto state16
+- :: dte!l
+- fi;
+- /* state11: */
+- if
+- :: dce?b -> goto state16
+- :: dte!n
+- fi;
+- /* state12: */
+- if
+- :: dce?b -> goto state16
+- :: dte!m -> goto state19
+- fi;
+-
+-state15:
+- if
+- :: dte!v -> goto state03
+- :: dce?b -> goto state16
+- fi;
+-state08:
+- if
+- :: dce?c
+- :: dce?d -> goto state15
+- :: dce?b -> goto state16
+- fi;
+- /* state09: */
+- if
+- :: dce?b -> goto state16
+- :: dte!q
+- fi;
+- /* state10B: */
+- if
+- :: dte!r -> goto state6C
+- :: dce?b -> goto state16
+- fi;
+-state18:
+- if
+- :: dte!l -> goto state01
+- :: dce?b -> goto state16
+- fi;
+-state16:
+- dte!m;
+- /* state17: */
+- dte!l;
+- /* state21: */
+- dce?a; goto state01;
+-state19:
+- dce?b;
+- /* state20: */
+- dte!l;
+- /* state21: */
+- dce?a; goto state01
+-}
+//GO.SYSIN DD ex.3
+echo ex.4b 1>&2
+sed 's/.//' >ex.4b <<'//GO.SYSIN DD ex.4b'
+-#define true 1
+-#define false 0
+-
+-bool flag[2];
+-bool turn;
+-
+-active [2] proctype user()
+-{ flag[_pid] = true;
+- turn = _pid;
+- (flag[1-_pid] == false || turn == 1-_pid);
+-crit: skip; /* critical section */
+- flag[_pid] = false
+-}
+//GO.SYSIN DD ex.4b
+echo ex.4c 1>&2
+sed 's/.//' >ex.4c <<'//GO.SYSIN DD ex.4c'
+-byte in;
+-byte x, y, z;
+-active [2] proctype user() /* file ex.4c */
+-{ byte me = _pid+1; /* me is 1 or 2 */
+-L1: x = me;
+-L2: if
+- :: (y != 0 && y != me) -> goto L1 /* try again */
+- :: (y == 0 || y == me)
+- fi;
+-L3: z = me;
+-L4: if
+- :: (x != me) -> goto L1 /* try again */
+- :: (x == me)
+- fi;
+-L5: y = me;
+-L6: if
+- :: (z != me) -> goto L1 /* try again */
+- :: (z == me)
+- fi;
+-L7: /* success */
+- in = in+1;
+- assert(in == 1);
+- in = in - 1;
+- goto L1
+-}
+//GO.SYSIN DD ex.4c
+echo ex.5a 1>&2
+sed 's/.//' >ex.5a <<'//GO.SYSIN DD ex.5a'
+-#define Place byte /* assume < 256 tokens per place */
+-
+-Place p1, p2, p3;
+-Place p4, p5, p6;
+-#define inp1(x) (x>0) -> x=x-1
+-#define inp2(x,y) (x>0&&y>0) -> x = x-1; y=y-1
+-#define out1(x) x=x+1
+-#define out2(x,y) x=x+1; y=y+1
+-init
+-{ p1 = 1; p4 = 1; /* initial marking */
+- do
+-/*t1*/ :: atomic { inp1(p1) -> out1(p2) }
+-/*t2*/ :: atomic { inp2(p2,p4) -> out1(p3) }
+-/*t3*/ :: atomic { inp1(p3) -> out2(p1,p4) }
+-/*t4*/ :: atomic { inp1(p4) -> out1(p5) }
+-/*t5*/ :: atomic { inp2(p1,p5) -> out1(p6) }
+-/*t6*/ :: atomic { inp1(p6) -> out2(p4,p1) }
+- od
+-}
+//GO.SYSIN DD ex.5a
+echo ex.5b 1>&2
+sed 's/.//' >ex.5b <<'//GO.SYSIN DD ex.5b'
+-#define Place byte /* assume < 256 tokens per place */
+-
+-Place P1, P2, P4, P5, RC, CC, RD, CD;
+-Place p1, p2, p4, p5, rc, cc, rd, cd;
+-
+-#define inp1(x) (x>0) -> x=x-1
+-#define inp2(x,y) (x>0&&y>0) -> x = x-1; y=y-1
+-#define out1(x) x=x+1
+-#define out2(x,y) x=x+1; y=y+1
+-
+-init /* file ex.5b */
+-{ P1 = 1; p1 = 1; /* initial marking */
+- do
+- :: atomic { inp1(P1) -> out2(rc,P2) } /* DC */
+- :: atomic { inp2(P2,CC) -> out1(P4) } /* CA */
+- :: atomic { inp1(P4) -> out2(P5,rd) } /* DD */
+- :: atomic { inp2(P5,CD) -> out1(P1) } /* FD */
+- :: atomic { inp2(P1,RC) -> out2(P4,cc) } /* AC */
+- :: atomic { inp2(P4,RD) -> out2(P1,cd) } /* AD */
+- :: atomic { inp2(P5,RD) -> out1(P1) } /* DA */
+-
+- :: atomic { inp1(p1) -> out2(RC,p2) } /* dc */
+- :: atomic { inp2(p2,cc) -> out1(p4) } /* ca */
+- :: atomic { inp1(p4) -> out2(p5,RD) } /* dd */
+- :: atomic { inp2(p5,cd) -> out1(p1) } /* fd */
+- :: atomic { inp2(p1,rc) -> out2(p4,CC) } /* ac */
+- :: atomic { inp2(p4,rd) -> out2(p1,CD) } /* ad */
+- :: atomic { inp2(p5,rd) -> out1(p1) } /* da */
+- od
+-}
+//GO.SYSIN DD ex.5b
+echo ex.6 1>&2
+sed 's/.//' >ex.6 <<'//GO.SYSIN DD ex.6'
+-#if 0
+- Cambridge Ring Protocol [Needham'82]
+- basic protocol - no LTL properties
+-#endif
+-
+-#define LOSS 1
+-#define RELAXED 1
+-
+-#if RELAXED==1
+-#define stimeout empty(sender)
+-#define rtimeout empty(recv)
+-#else
+-#define stimeout timeout
+-#define rtimeout timeout
+-#endif
+-
+-#define QSZ 6 /* length of message queues */
+-
+- mtype = {
+- RDY, NOTRDY, DATA, NODATA, RESET
+- };
+- chan sender = [QSZ] of { mtype, byte };
+- chan recv = [QSZ] of { mtype, byte };
+-
+-active proctype Sender()
+-{ short n = -1; byte t,m;
+-
+- xr sender;
+- xs recv;
+-
+-I: /* Idle State */
+- do
+-#if LOSS==1
+- :: sender?_,_ -> progress2: skip
+-#endif
+- :: sender?RESET,0 ->
+-ackreset: recv!RESET,0; /* stay in idle */
+- n = -1;
+- goto I
+-
+- /* E-rep: protocol error */
+-
+- :: sender?RDY,m -> /* E-exp */
+- assert(m == (n+1)%4);
+-advance: n = (n+1)%4;
+- if
+-/* buffer */ :: atomic {
+- printf("MSC: NEW\n");
+- recv!DATA,n;
+- goto E
+- }
+-/* no buffer */ :: recv!NODATA,n;
+- goto N
+- fi
+-
+- :: sender?NOTRDY,m -> /* N-exp */
+-expected: assert(m == (n+1)%4);
+- goto I
+-
+- /* Timeout */
+- /* ignored (p.154, in [Needham'82]) */
+-
+- :: goto reset
+-
+- od;
+-
+-E: /* Essential element sent, ack expected */
+- do
+-#if LOSS==1
+- :: sender?_,_ -> progress0: skip
+-#endif
+- :: sender?RESET,0 ->
+- goto ackreset
+-
+- :: sender?RDY,m ->
+- if
+- :: (m == n) -> /* E-rep */
+- goto retrans
+- :: (m == (n+1)%4) -> /* E-exp */
+- goto advance
+- fi
+-
+- :: sender?NOTRDY,m -> /* N-exp */
+- goto expected
+-
+- /* Timeout */
+- :: stimeout ->
+- printf("MSC: STO\n");
+-retrans: recv!DATA,n /* retransmit */
+-
+- :: goto reset
+-
+- od;
+-
+-
+-N: /* Non-essential element sent */
+- do
+-#if LOSS==1
+- :: sender?_,_ -> progress1: skip
+-#endif
+- :: sender?RESET,0 ->
+- goto ackreset
+-
+- :: sender?RDY,m -> /* E-rep */
+- assert(m == n) /* E-exp: protocol error */
+- -> recv!NODATA,n /* retransmit and stay in N */
+-
+- :: recv!DATA,n; /* buffer ready event */
+- goto E
+-
+- :: goto reset
+-
+- /* Timeout */
+- /* ignored (p.154, in [Needham'82]) */
+- od;
+-
+-reset: recv!RESET,0;
+- do
+-#if LOSS==1
+- :: sender?_,_ -> progress3: skip
+-#endif
+- :: sender?t,m ->
+- if
+- :: t == RESET -> n = -1; goto I
+- :: else /* ignored, p. 152 */
+- fi
+- :: timeout -> /* a real timeout */
+- goto reset
+- od
+-}
+-
+-active proctype Receiver()
+-{ byte t, n, m, Nexp;
+-
+- xr recv;
+- xs sender;
+-
+-I: /* Idle State */
+- do
+-#if LOSS==1
+- :: recv?_,_ -> progress2: skip
+-#endif
+- :: recv?RESET,0 ->
+-ackreset: sender!RESET,0; /* stay in idle */
+- n = 0; Nexp = 0;
+- goto I
+-
+- :: atomic { recv?DATA(m) -> /* E-exp */
+- assert(m == n);
+-advance: printf("MSC: EXP\n");
+- n = (n+1)%4;
+- assert(m == Nexp);
+- Nexp = (m+1)%4;
+- if
+- :: sender!RDY,n; goto E
+- :: sender!NOTRDY,n; goto N
+- fi
+- }
+-
+- :: recv?NODATA(m) -> /* N-exp */
+- assert(m == n)
+-
+- /* Timeout: ignored */
+-
+- /* only receiver can initiate transfer; p. 156 */
+- :: empty(recv) -> sender!RDY,n; goto E
+-
+- :: goto reset
+- od;
+-
+-E:
+- do
+-#if LOSS==1
+- :: recv?_,_ -> progress1: skip
+-#endif
+- :: recv?RESET,0 ->
+- goto ackreset
+-
+- :: atomic { recv?DATA(m) ->
+- if
+- :: ((m+1)%4 == n) -> /* E-rep */
+- printf("MSC: REP\n");
+- goto retrans
+- :: (m == n) -> /* E-exp */
+- goto advance
+- fi
+- }
+-
+- :: recv?NODATA(m) -> /* N-exp */
+- assert(m == n);
+- goto I
+-
+- :: rtimeout ->
+- printf("MSC: RTO\n");
+-retrans: sender!RDY,n;
+- goto E
+-
+- :: goto reset
+-
+- od;
+-
+-N:
+- do
+-#if LOSS==1
+- :: recv?_,_ -> progress0: skip
+-#endif
+- :: recv?RESET,0 ->
+- goto ackreset
+-
+- :: recv?DATA(m) -> /* E-rep */
+- assert((m+1)%4 == n); /* E-exp and N-exp: protocol error */
+- printf("MSC: REP\n");
+- sender!NOTRDY,n /* retransmit and stay in N */
+-
+- :: sender!RDY,n -> /* buffer ready event */
+- goto E
+-
+- /* Timeout: ignored */
+-
+- :: goto reset
+-
+- od;
+-
+-progress:
+-reset: sender!RESET,0;
+- do
+-#if LOSS==1
+- :: recv?_,_ -> progress3: skip
+-#endif
+- :: recv?t,m ->
+- if
+- :: t == RESET -> n = 0; Nexp = 0; goto I
+- :: else /* ignored, p. 152 */
+- fi
+- :: timeout -> /* a real timeout */
+- goto reset
+- od
+-}
+//GO.SYSIN DD ex.6
+echo ex.7 1>&2
+sed 's/.//' >ex.7 <<'//GO.SYSIN DD ex.7'
+-mtype = { Wakeme, Running };
+-
+-bit lk, sleep_q;
+-bit r_lock, r_want;
+-mtype State = Running;
+-
+-active proctype client()
+-{
+-sleep: /* sleep routine */
+- atomic { (lk == 0) -> lk = 1 }; /* spinlock(&lk) */
+- do /* while r->lock */
+- :: (r_lock == 1) -> /* r->lock == 1 */
+- r_want = 1;
+- State = Wakeme;
+- lk = 0; /* freelock(&lk) */
+- (State == Running); /* wait for wakeup */
+- :: else -> /* r->lock == 0 */
+- break
+- od;
+-progress:
+- assert(r_lock == 0); /* should still be true */
+- r_lock = 1; /* consumed resource */
+- lk = 0; /* freelock(&lk) */
+- goto sleep
+-}
+-
+-active proctype server() /* interrupt routine */
+-{
+-wakeup: /* wakeup routine */
+- r_lock = 0; /* r->lock = 0 */
+- (lk == 0); /* waitlock(&lk) */
+- if
+- :: r_want -> /* someone is sleeping */
+- atomic { /* spinlock on sleep queue */
+- (sleep_q == 0) -> sleep_q = 1
+- };
+- r_want = 0;
+-#ifdef PROPOSED_FIX
+- (lk == 0); /* waitlock(&lk) */
+-#endif
+- if
+- :: (State == Wakeme) ->
+- State = Running;
+- :: else ->
+- fi;
+- sleep_q = 0
+- :: else ->
+- fi;
+- goto wakeup
+-}
+//GO.SYSIN DD ex.7
+echo ex.8 1>&2
+sed 's/.//' >ex.8 <<'//GO.SYSIN DD ex.8'
+-/* Dolev, Klawe & Rodeh for leader election in unidirectional ring
+- * `An O(n log n) unidirectional distributed algorithm for extrema
+- * finding in a circle,' J. of Algs, Vol 3. (1982), pp. 245-260
+- */
+-
+-#define elected (nr_leaders > 0)
+-#define noLeader (nr_leaders == 0)
+-#define oneLeader (nr_leaders == 1)
+-
+-/* properties:
+- * ![] noLeader
+- * <> elected
+- * <>[]oneLeader
+- * [] (noLeader U oneLeader)
+- */
+-
+-#define N 7 /* nr of processes (use 5 for demos) */
+-#define I 3 /* node given the smallest number */
+-#define L 14 /* size of buffer (>= 2*N) */
+-
+-mtype = { one, two, winner };
+-chan q[N] = [L] of { mtype, byte};
+-
+-byte nr_leaders = 0;
+-
+-proctype node (chan in, out; byte mynumber)
+-{ bit Active = 1, know_winner = 0;
+- byte nr, maximum = mynumber, neighbourR;
+-
+- xr in;
+- xs out;
+-
+- printf("MSC: %d\n", mynumber);
+- out!one(mynumber);
+-end: do
+- :: in?one(nr) ->
+- if
+- :: Active ->
+- if
+- :: nr != maximum ->
+- out!two(nr);
+- neighbourR = nr
+- :: else ->
+- /* Raynal p.39: max is greatest number */
+- assert(nr == N);
+- know_winner = 1;
+- out!winner,nr;
+- fi
+- :: else ->
+- out!one(nr)
+- fi
+-
+- :: in?two(nr) ->
+- if
+- :: Active ->
+- if
+- :: neighbourR > nr && neighbourR > maximum ->
+- maximum = neighbourR;
+- out!one(neighbourR)
+- :: else ->
+- Active = 0
+- fi
+- :: else ->
+- out!two(nr)
+- fi
+- :: in?winner,nr ->
+- if
+- :: nr != mynumber ->
+- printf("MSC: LOST\n");
+- :: else ->
+- printf("MSC: LEADER\n");
+- nr_leaders++;
+- assert(nr_leaders == 1)
+- fi;
+- if
+- :: know_winner
+- :: else -> out!winner,nr
+- fi;
+- break
+- od
+-}
+-
+-init {
+- byte proc;
+- atomic {
+- proc = 1;
+- do
+- :: proc <= N ->
+- run node (q[proc-1], q[proc%N], (N+I-proc)%N+1);
+- proc++
+- :: proc > N ->
+- break
+- od
+- }
+-}
+-
+-#if 0
+-/* !(<>[]oneLeader) */
+-
+-never {
+-T0:
+- if
+- :: skip -> goto T0
+- :: !oneLeader -> goto accept
+- fi;
+-accept:
+- if
+- :: skip -> goto T0
+- fi
+-}
+-#endif
+//GO.SYSIN DD ex.8
+echo ex.9 1>&2
+sed 's/.//' >ex.9 <<'//GO.SYSIN DD ex.9'
+-#define MaxSeq 3
+-#define MaxSeq_plus_1 4
+-#define inc(x) x = (x + 1) % (MaxSeq_plus_1)
+-
+-chan q[2] = [MaxSeq] of { byte, byte };
+-
+-active [2] proctype p5()
+-{ byte NextFrame, AckExp, FrameExp,
+- r, s, nbuf, i;
+- chan in, out;
+-
+- in = q[_pid];
+- out = q[1-_pid];
+-
+- xr in;
+- xs out;
+-
+- do
+- :: nbuf < MaxSeq ->
+- nbuf++;
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq_plus_1);
+- inc(NextFrame)
+- :: in?r,s ->
+- if
+- :: r == FrameExp ->
+- inc(FrameExp)
+- :: else
+- fi;
+- do
+- :: ((AckExp <= s) && (s < NextFrame))
+- || ((AckExp <= s) && (NextFrame < AckExp))
+- || ((s < NextFrame) && (NextFrame < AckExp)) ->
+- nbuf--;
+- inc(AckExp)
+- :: else ->
+- break
+- od
+- :: timeout ->
+- NextFrame = AckExp;
+- i = 1;
+- do
+- :: i <= nbuf ->
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq_plus_1);
+- inc(NextFrame);
+- i++
+- :: else ->
+- break
+- od
+- od
+-}
+//GO.SYSIN DD ex.9
+echo ex.9b 1>&2
+sed 's/.//' >ex.9b <<'//GO.SYSIN DD ex.9b'
+-#define MaxSeq 3
+-#define MaxSeq_plus_1 4
+-#define inc(x) x = (x + 1) % (MaxSeq + 1)
+-
+-chan q[2] = [MaxSeq] of { byte, byte };
+-
+-active [2] proctype p5()
+-{ byte NextFrame, AckExp, FrameExp,
+- r, s, nbuf, i;
+- chan in, out;
+-
+- d_step {
+- in = q[_pid];
+- out = q[1-_pid]
+- };
+- xr in;
+- xs out;
+-
+- do
+- :: atomic { nbuf < MaxSeq ->
+- nbuf++;
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+- printf("MSC: nbuf: %d\n", nbuf);
+- inc(NextFrame)
+- }
+- :: atomic { in?r,s ->
+- if
+- :: r == FrameExp ->
+- printf("MSC: accept %d\n", r);
+- inc(FrameExp)
+- :: else
+- -> printf("MSC: reject\n")
+- fi
+- };
+- d_step {
+- do
+- :: ((AckExp <= s) && (s < NextFrame))
+- || ((AckExp <= s) && (NextFrame < AckExp))
+- || ((s < NextFrame) && (NextFrame < AckExp)) ->
+- nbuf--;
+- printf("MSC: nbuf: %d\n", nbuf);
+- inc(AckExp)
+- :: else ->
+- printf("MSC: %d %d %d\n", AckExp, s, NextFrame);
+- break
+- od; skip
+- }
+- :: timeout ->
+- d_step {
+- NextFrame = AckExp;
+- printf("MSC: timeout\n");
+- i = 1;
+- do
+- :: i <= nbuf ->
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+- inc(NextFrame);
+- i++
+- :: else ->
+- break
+- od; i = 0
+- }
+- od
+-}
+//GO.SYSIN DD ex.9b
+echo ex.9c 1>&2
+sed 's/.//' >ex.9c <<'//GO.SYSIN DD ex.9c'
+-#define MaxSeq 3
+-#define MaxSeq_plus_1 4
+-#define inc(x) x = (x + 1) % (MaxSeq + 1)
+-
+-#define CHECKIT
+-
+-#ifdef CHECKIT
+- mtype = { red, white, blue }; /* Wolper's test */
+- chan source = [0] of { mtype };
+- chan q[2] = [MaxSeq] of { mtype, byte, byte };
+- mtype rcvd = white;
+- mtype sent = white;
+-#else
+- chan q[2] = [MaxSeq] of { byte, byte };
+-#endif
+-
+-active [2] proctype p5()
+-{ byte NextFrame, AckExp, FrameExp,
+- r, s, nbuf, i;
+- chan in, out;
+-#ifdef CHECKIT
+- mtype ball;
+- byte frames[MaxSeq_plus_1];
+-#endif
+-
+- d_step {
+- in = q[_pid];
+- out = q[1-_pid]
+- };
+-
+- xr in;
+- xs out;
+-
+- do
+- :: atomic {
+- nbuf < MaxSeq ->
+- nbuf++;
+-#ifdef CHECKIT
+- if
+- :: _pid%2 -> source?ball
+- :: else
+- fi;
+- frames[NextFrame] = ball;
+- out!ball, NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+- if
+- :: _pid%2 -> sent = ball;
+- :: else
+- fi;
+-#else
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+-#endif
+-#ifdef VERBOSE
+- printf("MSC: nbuf: %d\n", nbuf);
+-#endif
+- inc(NextFrame)
+- }
+-#ifdef CHECKIT
+- :: atomic { in?ball,r,s ->
+-#else
+- :: atomic { in?r,s ->
+-#endif
+- if
+- :: r == FrameExp ->
+-#ifdef VERBOSE
+- printf("MSC: accept %d\n", r);
+-#endif
+-#ifdef CHECKIT
+- if
+- :: _pid%2
+- :: else -> rcvd = ball
+- fi;
+-#endif
+- inc(FrameExp)
+- :: else
+-#ifdef VERBOSE
+- -> printf("MSC: reject\n")
+-#endif
+- fi
+- };
+- d_step {
+- do
+- :: ((AckExp <= s) && (s < NextFrame))
+- || ((AckExp <= s) && (NextFrame < AckExp))
+- || ((s < NextFrame) && (NextFrame < AckExp)) ->
+- nbuf--;
+-#ifdef VERBOSE
+- printf("MSC: nbuf: %d\n", nbuf);
+-#endif
+- inc(AckExp)
+- :: else ->
+-#ifdef VERBOSE
+- printf("MSC: %d %d %d\n", AckExp, s, NextFrame);
+-#endif
+- break
+- od;
+- skip
+- }
+- :: timeout ->
+- d_step {
+- NextFrame = AckExp;
+-#ifdef VERBOSE
+- printf("MSC: timeout\n");
+-#endif
+- i = 1;
+- do
+- :: i <= nbuf ->
+-#ifdef CHECKIT
+- if
+- :: _pid%2 -> ball = frames[NextFrame]
+- :: else
+- fi;
+- out!ball, NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+-#else
+- out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);
+-#endif
+- inc(NextFrame);
+- i++
+- :: else ->
+- break
+- od;
+- i = 0
+- }
+- od
+-}
+-#ifdef CHECKIT
+-active proctype Source()
+-{
+- do
+- :: source!white
+- :: source!red -> break
+- od;
+- do
+- :: source!white
+- :: source!blue -> break
+- od;
+-end: do
+- :: source!white
+- od
+-}
+-
+-#define sw (sent == white)
+-#define sr (sent == red)
+-#define sb (sent == blue)
+-
+-#define rw (rcvd == white)
+-#define rr (rcvd == red)
+-#define rb (rcvd == blue)
+-
+-#define LTL 3
+-#if LTL==1
+-
+-never { /* ![](sr -> <> rr) */
+- /* the never claim is generated by
+- spin -f "![](sr -> <> rr)"
+- and then edited a little for readability
+- the same is true for all others below
+- */
+- do
+- :: 1
+- :: !rr && sr -> goto accept
+- od;
+-accept:
+- if
+- :: !rr -> goto accept
+- fi
+-}
+-
+-#endif
+-#if LTL==2
+-
+-never { /* !rr U rb */
+- do
+- :: !rr
+- :: rb -> break /* move to implicit 2nd state */
+- od
+-}
+-
+-#endif
+-#if LTL==3
+-
+-never { /* (![](sr -> <> rr)) || (!rr U rb) */
+-
+- if
+- :: 1 -> goto T0_S5
+- :: !rr && sr -> goto accept_S8
+- :: !rr -> goto T0_S2
+- :: rb -> goto accept_all
+- fi;
+-accept_S8:
+- if
+- :: !rr -> goto T0_S8
+- fi;
+-T0_S2:
+- if
+- :: !rr -> goto T0_S2
+- :: rb -> goto accept_all
+- fi;
+-T0_S8:
+- if
+- :: !rr -> goto accept_S8
+- fi;
+-T0_S5:
+- if
+- :: 1 -> goto T0_S5
+- :: !rr && sr -> goto accept_S8
+- fi;
+-accept_all:
+- skip
+-}
+-#endif
+-
+-#endif
+//GO.SYSIN DD ex.9c
--- /dev/null
+init {
+ printf("passed first test!\n")
+}
--- /dev/null
+/* Dolev, Klawe & Rodeh for leader election in unidirectional ring
+ * `An O(n log n) unidirectional distributed algorithm for extrema
+ * finding in a circle,' J. of Algs, Vol 3. (1982), pp. 245-260
+ */
+
+#define N 5 /* nr of processes (use 5 for demos) */
+#define I 3 /* node given the smallest number */
+#define L 10 /* size of buffer (>= 2*N) */
+
+mtype = { one, two, winner };
+chan q[N] = [L] of { mtype, byte};
+
+byte nr_leaders = 0;
+
+proctype node (chan in, out; byte mynumber)
+{ bit Active = 1, know_winner = 0;
+ byte nr, maximum = mynumber, neighbourR;
+
+ xr in;
+ xs out;
+
+ printf("MSC: %d\n", mynumber);
+ out!one(mynumber);
+end: do
+ :: in?one(nr) ->
+ if
+ :: Active ->
+ if
+ :: nr != maximum ->
+ out!two(nr);
+ neighbourR = nr
+ :: else ->
+ /* Raynal p.39: max is greatest number */
+ assert(nr == N);
+ know_winner = 1;
+ out!winner,nr;
+ fi
+ :: else ->
+ out!one(nr)
+ fi
+
+ :: in?two(nr) ->
+ if
+ :: Active ->
+ if
+ :: neighbourR > nr && neighbourR > maximum ->
+ maximum = neighbourR;
+ out!one(neighbourR)
+ :: else ->
+ Active = 0
+ fi
+ :: else ->
+ out!two(nr)
+ fi
+ :: in?winner,nr ->
+ if
+ :: nr != mynumber ->
+ printf("MSC: LOST\n");
+ :: else ->
+ printf("MSC: LEADER\n");
+ nr_leaders++;
+ assert(nr_leaders == 1)
+ fi;
+ if
+ :: know_winner
+ :: else -> out!winner,nr
+ fi;
+ break
+ od
+}
+
+init {
+ byte proc;
+ atomic {
+ proc = 1;
+ do
+ :: proc <= N ->
+ run node (q[proc-1], q[proc%N], (N+I-proc)%N+1);
+ proc++
+ :: proc > N ->
+ break
+ od
+ }
+}
+
--- /dev/null
+#define elected (nr_leaders > 0)
+#define noLeader (nr_leaders == 0)
+#define oneLeader (nr_leaders == 1)
+
+ /*
+ * Formula As Typed: <>[]oneLeader
+ * The Never Claim Below Corresponds
+ * To The Negated Formula !(<>[]oneLeader)
+ * (formalizing violations of the original)
+ */
+
+never { /* !(<>[]oneLeader) */
+T0_init:
+ if
+ :: (1) -> goto T0_init
+ :: (! ((oneLeader))) -> goto accept_S5
+ fi;
+accept_S5:
+ if
+ :: (1) -> goto T0_init
+ fi;
+accept_all:
+ skip
+}
+
+#ifdef NOTES
+Some other properties:
+ ![] noLeader
+ <> elected
+ [] (noLeader U oneLeader)
+
+
+
+
+
+
+#endif
+#ifdef RESULT
+warning: for p.o. reduction to be valid the never claim must be stutter-closed
+(never claims generated from LTL formulae are stutter-closed)
+(Spin Version 3.1.2 -- 14 March 1998)
+ + Partial Order Reduction
+
+Full statespace search for:
+ never-claim +
+ assertion violations + (if within scope of claim)
+ acceptance cycles + (fairness disabled)
+ invalid endstates - (disabled by never-claim)
+
+State-vector 200 byte, depth reached 233, errors: 0
+ 202 states, stored (402 visited)
+ 281 states, matched
+ 683 transitions (= visited+matched)
+ 36 atomic steps
+hash conflicts: 0 (resolved)
+(max size 2^19 states)
+
+2.542 memory usage (Mbyte)
+
+unreached in proctype node
+ line 105, state 28, "out!two,nr"
+ (1 of 49 states)
+unreached in proctype :init:
+ (0 of 11 states)
+
+real 0.13
+user 0.04
+sys 0.09
+
+#endif
--- /dev/null
+/* Dolev, Klawe & Rodeh for leader election in unidirectional ring
+ * `An O(n log n) unidirectional distributed algorithm for extrema
+ * finding in a circle,' J. of Algs, Vol 3. (1982), pp. 245-260
+
+ * Randomized initialization added -- Feb 17, 1997
+ */
+
+#define elected (nr_leaders > 0)
+#define noLeader (nr_leaders == 0)
+#define oneLeader (nr_leaders == 1)
+
+/* sample properties:
+ * ![] noLeader
+ * <> elected
+ * <>[]oneLeader
+ * [] (noLeader U oneLeader)
+ */
+
+byte nr_leaders = 0;
+
+#define N 5 /* number of processes in the ring */
+#define L 10 /* 2xN */
+byte I;
+
+mtype = { one, two, winner };
+chan q[N] = [L] of { mtype, byte};
+
+proctype node (chan in, out; byte mynumber)
+{ bit Active = 1, know_winner = 0;
+ byte nr, maximum = mynumber, neighbourR;
+
+ xr in;
+ xs out;
+
+ printf("MSC: %d\n", mynumber);
+ out!one(mynumber);
+end: do
+ :: in?one(nr) ->
+ if
+ :: Active ->
+ if
+ :: nr != maximum ->
+ out!two(nr);
+ neighbourR = nr
+ :: else ->
+ know_winner = 1;
+ out!winner,nr;
+ fi
+ :: else ->
+ out!one(nr)
+ fi
+
+ :: in?two(nr) ->
+ if
+ :: Active ->
+ if
+ :: neighbourR > nr && neighbourR > maximum ->
+ maximum = neighbourR;
+ out!one(neighbourR)
+ :: else ->
+ Active = 0
+ fi
+ :: else ->
+ out!two(nr)
+ fi
+ :: in?winner,nr ->
+ if
+ :: nr != mynumber ->
+ printf("MSC: LOST\n");
+ :: else ->
+ printf("MSC: LEADER\n");
+ nr_leaders++;
+ assert(nr_leaders == 1)
+ fi;
+ if
+ :: know_winner
+ :: else -> out!winner,nr
+ fi;
+ break
+ od
+}
+
+init {
+ byte proc;
+ byte Ini[6]; /* N<=6 randomize the process numbers */
+ atomic {
+
+ I = 1; /* pick a number to be assigned 1..N */
+ do
+ :: I <= N ->
+ if /* non-deterministic choice */
+ :: Ini[0] == 0 && N >= 1 -> Ini[0] = I
+ :: Ini[1] == 0 && N >= 2 -> Ini[1] = I
+ :: Ini[2] == 0 && N >= 3 -> Ini[2] = I
+ :: Ini[3] == 0 && N >= 4 -> Ini[3] = I
+ :: Ini[4] == 0 && N >= 5 -> Ini[4] = I
+ :: Ini[5] == 0 && N >= 6 -> Ini[5] = I /* works for up to N=6 */
+ fi;
+ I++
+ :: I > N -> /* assigned all numbers 1..N */
+ break
+ od;
+
+ proc = 1;
+ do
+ :: proc <= N ->
+ run node (q[proc-1], q[proc%N], Ini[proc-1]);
+ proc++
+ :: proc > N ->
+ break
+ od
+ }
+}
+
+#if 0
+
+/* !(<>[]oneLeader) -- a sample LTL property */
+
+never {
+T0:
+ if
+ :: skip -> goto T0
+ :: !oneLeader -> goto accept
+ fi;
+accept:
+ if
+ :: skip -> goto T0
+ fi
+}
+#endif
--- /dev/null
+/* Dolev, Klawe & Rodeh for leader election in unidirectional ring
+ * `An O(n log n) unidirectional distributed algorithm for extrema
+ * finding in a circle,' J. of Algs, Vol 3. (1982), pp. 245-260
+ */
+
+#define N 5 /* nr of processes (use 5 for demos) */
+#define I 3 /* node given the smallest number */
+#define L 10 /* size of buffer (>= 2*N) */
+
+mtype = { one, two, winner };
+chan q[N] = [L] of { mtype, byte};
+
+byte nr_leaders = 0;
+
+notrace {
+ do
+ :: q[0]?one,_
+ :: q[0]?two,_ -> break
+ od;
+ do
+ :: q[0]?two,_
+ :: q[0]?winner,_ -> break
+ od
+}
+
+proctype node (chan in, out; byte mynumber)
+{ bit Active = 1, know_winner = 0;
+ byte nr, maximum = mynumber, neighbourR;
+
+ xr in;
+ xs out;
+
+ printf("MSC: %d\n", mynumber);
+ out!one(mynumber);
+end: do
+ :: in?one(nr) ->
+ if
+ :: Active ->
+ if
+ :: nr != maximum ->
+ out!two(nr);
+ neighbourR = nr
+ :: else ->
+ /* Raynal p.39: max is greatest number */
+ assert(nr == N);
+ know_winner = 1;
+ out!winner,nr;
+ fi
+ :: else ->
+ out!one(nr)
+ fi
+
+ :: in?two(nr) ->
+ if
+ :: Active ->
+ if
+ :: neighbourR > nr && neighbourR > maximum ->
+ maximum = neighbourR;
+ out!one(neighbourR)
+ :: else ->
+ Active = 0
+ fi
+ :: else ->
+ out!two(nr)
+ fi
+ :: in?winner,nr ->
+ if
+ :: nr != mynumber ->
+ printf("MSC: LOST\n");
+ :: else ->
+ printf("MSC: LEADER\n");
+ nr_leaders++;
+ assert(nr_leaders == 1)
+ fi;
+ if
+ :: know_winner
+ :: else -> out!winner,nr
+ fi;
+ break
+ od
+}
+
+init {
+ byte proc;
+ atomic {
+ proc = 1;
+ do
+ :: proc <= N ->
+ run node (q[proc-1], q[proc%N], (N+I-proc)%N+1);
+ proc++
+ :: proc > N ->
+ break
+ od
+ }
+}
+
--- /dev/null
+active proctype loop()
+{ byte a, b;
+
+ do
+ :: a = (a+1)%3;
+ if
+ :: b = 2*a; skip
+ :: b = 2*a; accept: skip
+ fi;
+progress: b--
+ od
+}
--- /dev/null
+/*
+ * Model of cell-phone handoff strategy in a mobile network.
+ * A translation from the pi-calculus description of this
+ * model presented in:
+ * Fredrik Orava and Joachim Parrow, 'An algebraic verification
+ * of a mobile network,' Formal aspects of computing, 4:497-543 (1992).
+ * For more information on this model, email: joachim@it.kth.se
+ *
+ * See also the simplified version of this model in mobile2
+ *
+ * The ltl property definition for this version is in mobile1.ltl
+ *
+ * to perform the verification with xspin, simply use the ltl property
+ * manager, which will load the above .ltl file by default.
+ * to perform the verificaion from a Unix command line, type:
+ * $ spin -a -N mobile1.ltl mobile1
+ * $ cc -o pan pan.c
+ * $ pan -a
+ */
+
+mtype = { data, ho_cmd, ho_com, ho_acc, ho_fail, ch_rel, white, red, blue };
+
+chan in = [1] of { mtype };
+chan out = [1] of { mtype };
+
+byte a_id, p_id; /* ids of processes refered to in the property */
+
+proctype CC(chan fa, fp, l) /* communication controller */
+{ chan m_old, m_new, x;
+ mtype v;
+
+ do
+ :: in?v ->
+ printf("MSC: DATA\n");
+ fa!data; fa!v
+ :: l?m_new ->
+ fa!ho_cmd; fa!m_new;
+ printf("MSC: HAND-OFF\n");
+ if
+ :: fp?ho_com ->
+ printf("MSC: CH_REL\n");
+ fa!ch_rel; fa?m_old;
+ l!m_old;
+ x = fa; fa = fp; fp = x
+ :: fa?ho_fail ->
+ printf("MSC: FAIL\n");
+ l!m_new
+ fi
+ od
+}
+
+proctype HC(chan l, m) /* handover controller */
+{
+ do
+ :: l!m; l?m
+ od
+}
+
+proctype MSC(chan fa, fp, m) /* mobile switching center */
+{ chan l = [0] of { chan };
+
+ atomic {
+ run HC(l, m);
+ run CC(fa, fp, l)
+ }
+}
+
+proctype BS(chan f, m; bit how) /* base station */
+{ chan v;
+
+ if
+ :: how -> goto Active
+ :: else -> goto Passive
+ fi;
+
+Active:
+ printf("MSC: ACTIVE\n");
+ do
+ :: f?data -> f?v; m!data; m!v
+ :: f?ho_cmd -> /* handover command */
+progress: f?v; m!ho_cmd; m!v;
+ if
+ :: f?ch_rel ->
+ f!m;
+ goto Passive
+ :: m?ho_fail ->
+ printf("MSC: FAILURE\n");
+ f!ho_fail
+ fi
+ od;
+
+Passive:
+ printf("MSC: PASSIVE\n");
+ m?ho_acc -> f!ho_com;
+ goto Active
+}
+
+proctype MS(chan m) /* mobile station */
+{ chan m_new;
+ mtype v;
+
+ do
+ :: m?data -> m?v; out!v
+ :: m?ho_cmd; m?m_new;
+ if
+ :: m_new!ho_acc; m = m_new
+ :: m!ho_fail
+ fi
+ od
+}
+
+proctype P(chan fa, fp)
+{ chan m = [0] of { mtype };
+
+ atomic {
+ run MSC(fa, fp, m);
+ p_id = run BS(fp, m, 0) /* passive base station */
+ }
+}
+
+proctype Q(chan fa)
+{ chan m = [0] of { mtype }; /* mixed use as mtype/chan */
+
+ atomic {
+ a_id = run BS(fa, m, 1); /* active base station */
+ run MS(m)
+ }
+}
+
+active proctype System()
+{ chan fa = [0] of { mtype }; /* mixed use as mtype/chan */
+ chan fp = [0] of { mtype }; /* mixed use as mtype/chan */
+
+ atomic {
+ run P(fa, fp);
+ run Q(fa)
+ }
+}
+
+active proctype top()
+{
+ do
+ :: in!red; in!white; in!blue
+ od
+}
+active proctype bot()
+{
+ do /* deadlock on loss or duplication */
+ :: out?red; out?white; out?blue
+ od
+}
--- /dev/null
+#define p in?[red]
+#define q out?[red]
+#define r (BS[a_id]@progress || BS[p_id]@progress)
+
+ /*
+ * Formula As Typed: (![]<>(r)) -> [](<>p -> <>q)
+ * The Never Claim Below Corresponds
+ * To The Negated Formula !((![]<>(r)) -> [](<>p -> <>q))
+ * (formalizing violations of the original)
+ */
+
+never { /* !((![]<>(r)) -> [](<>p -> <>q)) */
+T0_init:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S17
+ :: (! ((q)) && (p)) -> goto T0_S44
+ :: (! ((q))) -> goto T0_S58
+ :: (! ((r))) -> goto T0_S91
+ :: (1) -> goto T0_init
+ fi;
+accept_S8:
+ if
+ :: (! ((q)) && ! ((r))) -> goto accept_S8
+ fi;
+T0_S17:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S17
+ fi;
+T0_S44:
+ if
+ :: (! ((q)) && ! ((r))) -> goto accept_S8
+ :: (! ((q))) -> goto T0_S44
+ fi;
+T0_S58:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S17
+ :: (! ((q)) && (p)) -> goto T0_S44
+ :: (! ((q))) -> goto T0_S58
+ fi;
+T0_S91:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S17
+ :: (! ((r))) -> goto T0_S91
+ fi;
+}
+
+#ifdef NOTES
+Use Load to open a file or a template.
+
+
+#endif
+#ifdef RESULT
+warning: for p.o. reduction to be valid the never claim must be stutter-closed
+(never claims generated from LTL formulae are stutter-closed)
+(Spin Version 3.4.0 -- 7 August 2000)
+ + Partial Order Reduction
+
+Full statespace search for:
+ never-claim +
+ assertion violations + (if within scope of claim)
+ acceptance cycles + (fairness disabled)
+ invalid endstates - (disabled by never-claim)
+
+State-vector 128 byte, depth reached 1833, errors: 0
+ 44455 states, stored (48719 visited)
+ 137737 states, matched
+ 186456 transitions (= visited+matched)
+ 241 atomic steps
+hash conflicts: 8962 (resolved)
+(max size 2^19 states)
+
+Stats on memory usage (in Megabytes):
+6.046 equivalent memory usage for states (stored*(State-vector + overhead))
+3.379 actual memory usage for states (compression: 55.89%)
+ State-vector as stored = 68 byte + 8 byte overhead
+2.097 memory used for hash-table (-w19)
+0.240 memory used for DFS stack (-m10000)
+5.819 total actual memory usage
+
+unreached in proctype CC
+ line 49, state 25, "-end-"
+ (1 of 25 states)
+unreached in proctype HC
+ line 56, state 6, "-end-"
+ (1 of 6 states)
+unreached in proctype MSC
+ (0 of 4 states)
+unreached in proctype BS
+ line 95, state 31, "-end-"
+ (1 of 31 states)
+unreached in proctype MS
+ line 108, state 14, "-end-"
+ (1 of 14 states)
+unreached in proctype P
+ (0 of 4 states)
+unreached in proctype Q
+ (0 of 4 states)
+unreached in proctype System
+ (0 of 4 states)
+unreached in proctype top
+ line 143, state 7, "-end-"
+ (1 of 7 states)
+unreached in proctype bot
+ line 149, state 7, "-end-"
+ (1 of 7 states)
+5.1u 0.1s 5r ./pan -X -m10000 -w19 -a ...
+
+#endif
--- /dev/null
+/*
+ * Simplified model of cell-phone handoff strategy in a mobile network.
+ * A translation from the pi-calculus description of this
+ * model presented in:
+ * Fredrik Orava and Joachim Parrow, 'An algebraic verification
+ * of a mobile network,' Formal aspects of computing, 4:497-543 (1992).
+ * For more information on this model, email: joachim@it.kth.se
+ *
+ * This version exploits some Promela features to reduce the number
+ * of processes -- which looks better in simulations, and reduces
+ * complexity (by about 60%) in verification.
+ *
+ * See also the more literal version of this model in mobile1.
+ *
+ * The ltl property definition for this version is in mobile2.ltl
+ *
+ * to perform the verification with xspin, simply use the ltl property
+ * manager, which will load the above .ltl file by default.
+ * to perform the verificaion from a Unix command line, type:
+ * $ spin -a -N mobile2.ltl mobile2
+ * $ cc -o pan pan.c
+ * $ pan -a
+ */
+
+mtype = { data, ho_cmd, ho_com, ho_acc, ho_fail, ch_rel, white, red, blue };
+
+chan in = [1] of { mtype };
+chan out = [1] of { mtype };
+chan fa = [0] of { chan };
+chan fp = [0] of { chan };
+chan m1 = [0] of { chan };
+chan m2 = [0] of { chan };
+chan l = [0] of { chan };
+
+byte a_id, p_id; /* ids of processes refered to in the property */
+
+proctype CC() /* communication controller */
+{ chan m_old, m_new, x;
+ mtype v;
+
+ do
+ :: in?v ->
+ printf("MSC: DATA\n");
+ fa!data; fa!v
+ :: l?m_new ->
+ fa!ho_cmd; fa!m_new;
+ printf("MSC: HAND-OFF\n");
+ if
+ :: fp?ho_com ->
+ printf("MSC: CH_REL\n");
+ fa!ch_rel; fa?m_old;
+ l!m_old;
+ x = fa; fa = fp; fp = x
+ :: fa?ho_fail ->
+ printf("MSC: FAIL\n");
+ l!m_new
+ fi
+ od
+}
+
+proctype HC(chan m) /* handover controller */
+{
+ do
+ :: l!m; l?m
+ od
+}
+
+proctype BS(chan f, m; bit how) /* base station */
+{ chan v;
+
+ if
+ :: how -> goto Active
+ :: else -> goto Passive
+ fi;
+
+Active:
+ printf("MSC: ACTIVE\n");
+ do
+ :: f?data -> f?v; m!data; m!v
+ :: f?ho_cmd -> /* handover command */
+progress: f?v; m!ho_cmd; m!v;
+ if
+ :: f?ch_rel ->
+ f!m;
+ goto Passive
+ :: m?ho_fail ->
+ printf("MSC: FAILURE\n");
+ f!ho_fail
+ fi
+ od;
+
+Passive:
+ printf("MSC: PASSIVE\n");
+ m?ho_acc -> f!ho_com;
+ goto Active
+}
+
+proctype MS(chan m) /* mobile station */
+{ chan m_new;
+ mtype v;
+
+ do
+ :: m?data -> m?v; out!v
+ :: m?ho_cmd; m?m_new;
+ if
+ :: m_new!ho_acc; m = m_new
+ :: m!ho_fail
+ fi
+ od
+}
+
+active proctype System()
+{
+ atomic {
+ run HC(m1);
+ run CC();
+ p_id = run BS(fp, m1, 0); /* passive base station */
+ a_id = run BS(fa, m2, 1); /* active base station */
+ run MS(m2)
+ }
+
+end: do
+ :: in!red; in!white; in!blue
+ od
+}
+
+active proctype Out()
+{
+end: do /* deadlocks if order is disturbed */
+ :: out?red; out?white; out?blue
+ od
+}
--- /dev/null
+#define p in?[red]
+#define q out?[red]
+#define r (BS[a_id]@progress || BS[p_id]@progress)
+
+ /*
+ * Formula As Typed: (![]<>(r)) -> [](<>p -> <>q)
+ * The Never Claim Below Corresponds
+ * To The Negated Formula !((![]<>(r)) -> [](<>p -> <>q))
+ * (formalizing violations of the original)
+ */
+
+never { /* !((![]<>(r)) -> [](<>p -> <>q)) */
+T0_init:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S13
+ :: (! ((q)) && (p)) -> goto T0_S26
+ :: (! ((q))) -> goto T0_S32
+ :: (! ((r))) -> goto T0_S44
+ :: (1) -> goto T0_init
+ fi;
+accept_S8:
+ if
+ :: (! ((q)) && ! ((r))) -> goto T0_S8
+ fi;
+T0_S8:
+ if
+ :: (! ((q)) && ! ((r))) -> goto accept_S8
+ fi;
+T0_S13:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S13
+ fi;
+T0_S26:
+ if
+ :: (! ((q)) && ! ((r))) -> goto accept_S8
+ :: (! ((q))) -> goto T0_S26
+ fi;
+T0_S32:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S13
+ :: (! ((q)) && (p)) -> goto T0_S26
+ :: (! ((q))) -> goto T0_S32
+ fi;
+T0_S44:
+ if
+ :: (! ((q)) && ! ((r)) && (p)) -> goto accept_S8
+ :: (! ((q)) && ! ((r))) -> goto T0_S13
+ :: (! ((r))) -> goto T0_S44
+ fi;
+accept_all:
+ skip
+}
+
+#ifdef NOTES
+Use Load to open a file or a template.
+#endif
+#ifdef RESULT
+warning: for p.o. reduction to be valid the never claim must be stutter-closed
+(never claims generated from LTL formulae are stutter-closed)
+(Spin Version 3.2.4 -- 16 October 1998)
+ + Partial Order Reduction
+
+Full statespace search for:
+ never-claim +
+ assertion violations + (if within scope of claim)
+ acceptance cycles + (fairness disabled)
+ invalid endstates - (disabled by never-claim)
+
+State-vector 96 byte, depth reached 1944, errors: 0
+ 16191 states, stored (18994 visited)
+ 46781 states, matched
+ 65775 transitions (= visited+matched)
+ 16 atomic steps
+hash conflicts: 1713 (resolved)
+(max size 2^19 states)
+
+Stats on memory usage (in Megabytes):
+1.684 equivalent memory usage for states (stored*(State-vector + overhead))
+0.998 actual memory usage for states (compression: 59.24%)
+ State-vector as stored = 54 byte + 8 byte overhead
+2.097 memory used for hash-table (-w19)
+0.240 memory used for DFS stack (-m10000)
+3.464 total actual memory usage
+
+unreached in proctype CC
+ line 28, state 25, "-end-"
+ (1 of 25 states)
+unreached in proctype HC
+ line 35, state 6, "-end-"
+ (1 of 6 states)
+unreached in proctype BS
+ line 65, state 31, "-end-"
+ (1 of 31 states)
+unreached in proctype MS
+ line 78, state 14, "-end-"
+ (1 of 14 states)
+unreached in proctype System
+ line 98, state 13, "-end-"
+ (1 of 13 states)
+unreached in proctype Out
+ line 105, state 7, "-end-"
+ (1 of 7 states)
+
+#endif
--- /dev/null
+/*
+ * Models the Pathfinder scheduling algorithm and explains the
+ * cause of the recurring reset problem during the mission on Mars
+ *
+ * there is a high priority process, that consumes
+ * data produced by a low priority process.
+ * data consumption and production happens under
+ * the protection of a mutex lock
+ * the mutex lock conflicts with the scheduling priorities
+ * which can deadlock the system if high() starts up
+ * while low() has the lock set
+ * there are 12 reachable states in the full (non-reduced)
+ * state space -- two of which are deadlock states
+ * partial order reduction cannot be used here because of
+ * the 'provided' clause that models the process priorities
+ */
+
+mtype = { free, busy, idle, waiting, running };
+
+show mtype h_state = idle;
+show mtype l_state = idle;
+show mtype mutex = free;
+
+active proctype high() /* can run at any time */
+{
+end: do
+ :: h_state = waiting;
+ atomic { mutex == free -> mutex = busy };
+ h_state = running;
+
+ /* critical section - consume data */
+
+ atomic { h_state = idle; mutex = free }
+ od
+}
+
+active proctype low() provided (h_state == idle) /* scheduling rule */
+{
+end: do
+ :: l_state = waiting;
+ atomic { mutex == free -> mutex = busy};
+ l_state = running;
+
+ /* critical section - produce data */
+
+ atomic { l_state = idle; mutex = free }
+ od
+
+}
+
--- /dev/null
+/* Peterson's solution to the mutual exclusion problem - 1981 */
+
+bool turn, flag[2];
+byte ncrit;
+
+active [2] proctype user()
+{
+ assert(_pid == 0 || _pid == 1);
+again:
+ flag[_pid] = 1;
+ turn = _pid;
+ (flag[1 - _pid] == 0 || turn == 1 - _pid);
+
+ ncrit++;
+ assert(ncrit == 1); /* critical section */
+ ncrit--;
+
+ flag[_pid] = 0;
+ goto again
+}
--- /dev/null
+/* Peterson's algorithm for N processes - see Lynch's book, p. 284 */
+
+#ifndef N
+ #define N 3 /* nr of processes */
+#endif
+
+byte turn[N], flag[N];
+
+byte ncrit; /* auxiliary var to check mutual exclusion */
+
+active [N] proctype user()
+{ byte j, k;
+ /* without never claims, _pid's are: 0 .. N-1 */
+again:
+ k = 0; /* count max N-1 rounds of competition */
+ do
+ :: k < N-1 ->
+ flag[_pid] = k;
+ turn[k] = _pid;
+
+ j = 0; /* for all j != _pid */
+ do
+ :: j == _pid ->
+ j++
+ :: j != _pid ->
+ if
+ :: j < N ->
+ (flag[j] < k || turn[k] != _pid);
+ j++
+ :: j >= N ->
+ break
+ fi
+ od;
+ k++
+ :: else -> /* survived all N-1 rounds */
+ break
+ od;
+
+ ncrit++;
+ assert(ncrit == 1); /* critical section */
+ ncrit--;
+
+ flag[_pid] = 0;
+ goto again
+}
--- /dev/null
+/*
+ * PROMELA Validation Model
+ * FLOW CONTROL LAYER VALIDATION
+ */
+
+#define LOSS 1 /* message loss */
+#define DUPS 0 /* duplicate msgs */
+#define QSZ 2 /* queue size */
+
+mtype = {
+ red, white, blue,
+ abort, accept, ack, sync_ack, close, connect,
+ create, data, eof, open, reject, sync, transfer,
+ FATAL, NON_FATAL, COMPLETE
+ }
+
+chan ses_to_flow[2] = [QSZ] of { byte, byte };
+chan flow_to_ses[2] = [QSZ] of { byte, byte };
+chan dll_to_flow[2] = [QSZ] of { byte, byte };
+
+/*
+ * Flow Control Layer Validation Model
+ */
+
+#define true 1
+#define false 0
+
+#define M 4 /* range sequence numbers */
+#define W 2 /* window size: M/2 */
+
+proctype fc(chan s2f, f2d, f2s, d2f)
+{ bool busy[M]; /* outstanding messages */
+ byte q; /* seq# oldest unacked msg */
+ byte m; /* seq# last msg received */
+ byte s; /* seq# next msg to send */
+ byte window; /* nr of outstanding msgs */
+ byte type; /* msg type */
+ bit received[M]; /* receiver housekeeping */
+ bit x; /* scratch variable */
+ byte p; /* seq# of last msg acked */
+ byte I_buf[M], O_buf[M]; /* message buffers */
+
+ xr s2f;
+ xs f2d;
+ xs f2s;
+ xr d2f;
+
+ /* sender part */
+end: do
+ :: atomic {
+ (window < W && nempty(s2f)
+ && nfull(f2d)) ->
+ s2f?type,x;
+ window = window + 1;
+ busy[s] = true;
+ O_buf[s] = type;
+ f2d!type,s;
+ if
+ :: (type != sync) ->
+ s = (s+1)%M
+ :: (type == sync) ->
+ window = 0;
+ s = M;
+ do
+ :: (s > 0) ->
+ s = s-1;
+ busy[s] = false
+ :: (s == 0) ->
+ break
+ od
+ fi
+ }
+ :: atomic {
+ (window > 0 && busy[q] == false) ->
+ window = window - 1;
+ q = (q+1)%M
+ }
+#if DUPS
+ :: atomic {
+ (nfull(f2d) && window > 0 && busy[q] == true) ->
+ f2d!O_buf[q],q
+ }
+#endif
+ :: atomic {
+ (timeout && nfull(f2d) && window > 0 && busy[q] == true) ->
+ f2d!O_buf[q],q
+ }
+ /* receiver part */
+#if LOSS
+ :: d2f?type,m /* lose any message */
+#endif
+ :: d2f?type,m ->
+ if
+ :: atomic {
+ (type == ack) ->
+ busy[m] = false
+ }
+ :: atomic {
+ (type == sync) ->
+ m = 0;
+ do
+ :: (m < M) ->
+ received[m] = 0;
+ m = m+1
+ :: (m == M) ->
+ break
+ od
+ }; f2d!sync_ack,0
+ :: (type == sync_ack) ->
+ f2s!sync_ack,0
+ :: (type != ack && type != sync && type != sync_ack)->
+ if
+ :: atomic {
+ (received[m] == true) ->
+ x = ((0<p-m && p-m<=W)
+ || (0<p-m+M && p-m+M<=W)) };
+ if
+ :: (x) -> f2d!ack,m
+ :: (!x) /* else skip */
+ fi
+ :: atomic {
+ (received[m] == false) ->
+ I_buf[m] = type;
+ received[m] = true;
+ received[(m-W+M)%M] = false
+ }
+ fi
+ fi
+ :: /* atomic { */
+ (received[p] == true && nfull(f2s) && nfull(f2d)) ->
+ f2s!I_buf[p],0;
+ f2d!ack,p;
+ p = (p+1)%M
+ /* } */
+ od
+}
+
+proctype upper_s(chan s2f, f2s0)
+{ byte s_state;
+ byte type, toggle;
+
+ xs s2f;
+ xr f2s0;
+
+ s2f!sync,toggle;
+ do
+ :: f2s0?sync_ack,type ->
+ if
+ :: (type != toggle)
+ :: (type == toggle) -> break
+ fi
+ :: timeout ->
+ s2f!sync,toggle
+ od;
+ toggle = 1 - toggle;
+
+end: do
+ :: s2f!white,0
+ :: atomic {
+ (s_state == 0 && nfull(s2f)) ->
+ s2f!red,0 ->
+ s_state = 1
+ }
+ :: atomic {
+ (s_state == 1 && nfull(s2f)) ->
+ s2f!blue,0 ->
+ s_state = 2
+ }
+ od
+}
+
+proctype upper_r(chan f2s1)
+{ byte r_state;
+
+ xr f2s1;
+
+ do
+ :: f2s1?white,0
+ :: f2s1?red,0 -> break
+ :: f2s1?blue,0 -> assert(0)
+ od;
+
+ do
+ :: f2s1?white,0
+ :: f2s1?red,0 -> assert(0)
+ :: f2s1?blue,0 -> goto end
+ od;
+end:
+ do
+ :: f2s1?white,0
+ :: f2s1?red,0 -> assert(0)
+ :: f2s1?blue,0 -> assert(0)
+ od
+}
+
+init
+{
+ atomic {
+ run fc(ses_to_flow[0], dll_to_flow[1], flow_to_ses[0], dll_to_flow[0]);
+ run fc(ses_to_flow[1], dll_to_flow[0], flow_to_ses[1], dll_to_flow[1]);
+ run upper_s(ses_to_flow[0], flow_to_ses[0]);
+ run upper_r(flow_to_ses[1])
+ }
+}
--- /dev/null
+/* test execution priorities
+ run this as:
+ spin -p -g priorities
+ requires Spin Version 2.5 or later
+*/
+
+int a[5];
+
+proctype A()
+{
+ do
+ :: printf("%d\n", _pid); a[_pid]++
+ od
+}
+
+init {
+ atomic {
+ run A() priority 1;
+ run A() priority 2;
+ run A() priority 3;
+ run A() priority 4;
+} }
--- /dev/null
+/* snooping cache algorithm
+ */
+#define QSZ 2
+
+mtype = { r, w, raw,
+ RD, WR, RX,
+ MX, MXdone,
+ req0, req1,
+ CtoB, BtoC,
+ grant, done
+ };
+
+chan tocpu0 = [QSZ] of { mtype };
+chan fromcpu0 = [QSZ] of { mtype };
+chan tobus0 = [QSZ] of { mtype };
+chan frombus0 = [QSZ] of { mtype };
+chan grant0 = [QSZ] of { mtype };
+
+chan tocpu1 = [QSZ] of { mtype };
+chan fromcpu1 = [QSZ] of { mtype };
+chan tobus1 = [QSZ] of { mtype };
+chan frombus1 = [QSZ] of { mtype };
+chan grant1 = [QSZ] of { mtype };
+
+chan claim0 = [QSZ] of { mtype };
+chan claim1 = [QSZ] of { mtype };
+chan release0 = [QSZ] of { mtype };
+chan release1 = [QSZ] of { mtype };
+
+#define W 1
+#define R 2
+#define X 3
+
+proctype cpu0()
+{
+ xs fromcpu0;
+ xr tocpu0;
+ do
+ :: fromcpu0!r -> tocpu0?done
+ :: fromcpu0!w -> tocpu0?done
+ :: fromcpu0!raw -> tocpu0?done
+ od
+}
+
+proctype cpu1()
+{
+ xs fromcpu1;
+ xr tocpu1;
+ do
+ :: fromcpu1!r -> tocpu1?done
+ :: fromcpu1!w -> tocpu1?done
+ :: fromcpu1!raw -> tocpu1?done
+ od
+}
+
+proctype cache0()
+{ byte state = X;
+ byte which;
+
+ xr frombus0;
+ xr fromcpu0;
+ xs tocpu0;
+ xs tobus0;
+ xr grant0;
+ xs claim0;
+ xs release0;
+resume:
+ do
+ :: frombus0?RD ->
+ if
+ :: (state == W) -> state = R; tobus0!CtoB
+ :: (state != W) -> tobus0!done
+ fi
+ :: frombus0?MX -> state = X; tobus0!MXdone
+ :: frombus0?RX ->
+ if
+ :: (state == W) -> state = X; tobus0!CtoB
+ :: (state == R) -> state = X; tobus0!done
+ :: (state == X) -> tobus0!done
+ fi
+
+ :: fromcpu0?r ->
+ if
+ :: (state != X) -> tocpu0!done
+ :: (state == X) -> which = RD; goto buscycle
+ fi
+ :: fromcpu0?w ->
+ if
+ :: (state == W) -> tocpu0!done
+ :: (state != W) -> which = MX; goto buscycle
+ fi
+ :: fromcpu0?raw ->
+ if
+ :: (state == W) -> tocpu0!done
+ :: (state != W) -> which = RX; goto buscycle
+ fi
+ od;
+buscycle:
+ claim0!req0;
+ do
+ :: frombus0?RD ->
+ if
+ :: (state == W) -> state = R; tobus0!CtoB
+ :: (state != W) -> tobus0!done
+ fi
+ :: frombus0?MX -> state = X; tobus0!MXdone
+ :: frombus0?RX ->
+ if
+ :: (state == W) -> state = X; tobus0!CtoB
+ :: (state == R) -> state = X; tobus0!done
+ :: (state == X) -> tobus0!done
+ fi
+ :: grant0?grant ->
+ if
+ :: (which == RD) -> state = R
+ :: (which == MX) -> state = W
+ :: (which == RX) -> state = W
+ fi;
+ tocpu0!done;
+ break
+ od;
+ release0!done;
+
+ if
+ :: (which == RD) -> tobus0!RD -> frombus0?BtoC
+ :: (which == MX) -> tobus0!MX -> frombus0?done
+ :: (which == RX) -> tobus0!RX -> frombus0?BtoC
+ fi;
+ goto resume
+}
+
+proctype cache1()
+{ byte state = X;
+ byte which;
+
+ xr frombus1;
+ xr fromcpu1;
+ xs tobus1;
+ xs tocpu1;
+ xr grant1;
+ xs claim1;
+ xs release1;
+resume:
+ do
+ :: frombus1?RD ->
+ if
+ :: (state == W) -> state = R; tobus1!CtoB
+ :: (state != W) -> tobus1!done
+ fi
+ :: frombus1?MX -> state = X; tobus1!MXdone
+ :: frombus1?RX ->
+ if
+ :: (state == W) -> state = X; tobus1!CtoB
+ :: (state == R) -> state = X; tobus1!done
+ :: (state == X) -> tobus1!done
+ fi
+
+ :: fromcpu1?r ->
+ if
+ :: (state != X) -> tocpu1!done
+ :: (state == X) -> which = RD; goto buscycle
+ fi
+ :: fromcpu1?w ->
+ if
+ :: (state == W) -> tocpu1!done
+ :: (state != W) -> which = MX; goto buscycle
+ fi
+ :: fromcpu1?raw ->
+ if
+ :: (state == W) -> tocpu1!done
+ :: (state != W) -> which = RX; goto buscycle
+ fi
+ od;
+buscycle:
+ claim1!req1;
+ do
+ :: frombus1?RD ->
+ if
+ :: (state == W) -> state = R; tobus1!CtoB
+ :: (state != W) -> tobus1!done
+ fi
+ :: frombus1?MX -> state = X; tobus1!MXdone
+ :: frombus1?RX ->
+ if
+ :: (state == W) -> state = X; tobus1!CtoB
+ :: (state == R) -> state = X; tobus1!done
+ :: (state == X) -> tobus1!done
+ fi
+ :: grant1?grant ->
+ if
+ :: (which == RD) -> state = R
+ :: (which == MX) -> state = W
+ :: (which == RX) -> state = W
+ fi;
+ tocpu1!done;
+ break
+ od;
+ release1!done;
+
+ if
+ :: (which == RD) -> tobus1!RD -> frombus1?BtoC
+ :: (which == MX) -> tobus1!MX -> frombus1?done
+ :: (which == RX) -> tobus1!RX -> frombus1?BtoC
+ fi;
+ goto resume
+}
+
+proctype busarbiter()
+{
+ xs grant0;
+ xs grant1;
+ xr claim0;
+ xr claim1;
+ xr release0;
+ xr release1;
+
+ do
+ :: claim0?req0 -> grant0!grant; release0?done
+ :: claim1?req1 -> grant1!grant; release1?done
+ od
+}
+
+proctype bus() /* models real bus + main memory */
+{
+ xs frombus1;
+ xs frombus0;
+ xr tobus0;
+ xr tobus1;
+
+ do
+ :: tobus0?CtoB -> frombus1!BtoC
+ :: tobus1?CtoB -> frombus0!BtoC
+
+ :: tobus0?done -> /* M -> B */ frombus1!BtoC
+ :: tobus1?done -> /* M -> B */ frombus0!BtoC
+
+ :: tobus0?MXdone -> /* B -> M */ frombus1!done
+ :: tobus1?MXdone -> /* B -> M */ frombus0!done
+
+ :: tobus0?RD -> frombus1!RD
+ :: tobus1?RD -> frombus0!RD
+
+ :: tobus0?MX -> frombus1!MX
+ :: tobus1?MX -> frombus0!MX
+
+ :: tobus0?RX -> frombus1!RX
+ :: tobus1?RX -> frombus0!RX
+ od
+}
+
+init {
+ atomic {
+ run cpu0(); run cpu1();
+ run cache0(); run cache1();
+ run bus(); run busarbiter()
+ }
+}
--- /dev/null
+/*
+ * A program to sort concurrently N "random" numbers
+ * The reduced space and time should be linear in the
+ * number of processes, and can be reduced when the length of
+ * the buffer queues is increased.
+ * In full search it should be exponential.
+ */
+
+#define N 7 /* Number of Proc */
+#define L 10 /* Size of buffer queues */
+#define RANDOM (seed * 3 + 14) % 100 /* Calculate "random" number */
+
+chan q[N] = [L] of {byte};
+
+proctype left(chan out) /* leftmost process, generates random numbers */
+{ byte counter, seed;
+
+ xs out;
+
+ counter = 0; seed = 15;
+ do
+ :: out!seed -> /* output value to the right */
+ counter = counter + 1;
+ if
+ :: counter == N -> break
+ :: counter != N -> skip
+ fi;
+ seed = RANDOM /* next "random" number */
+ od
+}
+
+proctype middle(chan in, out; byte procnum)
+{ byte counter, myval, nextval;
+
+ xs out;
+ xr in;
+
+ counter = N - procnum;
+ in?myval; /* get first value from the left */
+ do
+ :: counter > 0 ->
+ in?nextval; /* upon receipt of a new value */
+ if
+ :: nextval >= myval -> out!nextval
+ :: nextval < myval ->
+ out!myval;
+ myval=nextval /* send bigger, hold smaller */
+ fi;
+ counter = counter - 1
+ :: counter == 0 -> break
+ od
+}
+
+proctype right(chan in) /* rightmost channel */
+{ byte biggest;
+
+ xr in;
+
+ in?biggest /* accepts only one value, which is the biggest */
+}
+
+init {
+ byte proc=1;
+
+ atomic {
+ run left ( q[0] );
+ do
+ :: proc < N ->
+ run middle ( q[proc-1] , q[proc], proc );
+ proc = proc+1
+ :: proc == N -> break
+ od;
+ run right ( q[N-1] )
+ }
+}
--- /dev/null
+/*
+ Example of property-based slicing.
+ Try: spin -A wordcount
+ Requires Spin version 3.4 or later
+ */
+
+chan STDIN;
+int c, nl, nw, nc;
+
+init {
+ bool inword = false;
+
+ do
+ :: STDIN?c ->
+ if
+ :: c == -1 -> break /* EOF */
+ :: c == '\n' -> nc++; nl++
+ :: else -> nc++
+ fi;
+ if
+ :: c == ' ' || c == '\t' || c == '\n' ->
+ inword = false
+ :: else ->
+ if
+ :: !inword ->
+ nw++; inword = true
+ :: else /* do nothing */
+ fi
+ fi
+ od;
+ assert(nc >= nl);
+ printf("%d\t%d\t%d\n", nl, nw, nc)
+}
--- /dev/null
+#!/bin/sh
+# the next line restarts using wish \
+exec wish c:/cygwin/bin/xspin -- $*
+
+# cd ;# enable to cd to home directory by default
+
+# on PCs:
+# adjust the first argument to wish above with the name and
+# location of this tcl/tk file on your system, if different.
+#
+# Cygwin:
+# if you use cygwin, do not refer to the file as /usr/bin/xspin
+# /usr/bin is a symbolic link to /bin, which really
+# lives in c:/cygwin, hence the contortions above
+#
+# on Unix/Linux/Solaris systems
+# replace the first line with something like
+# #!/usr/bin/wish -f
+# using the pathname for the wish executable on your system
+
+#======================================================================#
+# Tcl/Tk Spin Controller, written by Gerard J. Holzmann, 1995-2005. #
+# See the README.html file for full installation notes. #
+# http://spinroot.com/spin/whatispin.html #
+#======================================================================#
+set xversion "5.1.0 -- 24 April 2008"
+
+# -- Xspin Installation Notes (see also README.html):
+
+# 1. On Unix systems: change the first line of this file to point to the wish
+# executable you want to use (e.g., wish4.2 or /usr/local/bin/wish8.0)
+# ==> be careful, the pathname should be 30 characters or less
+#
+# 2. If you use another C compiler than gcc, change the set CC line below
+#
+# 3. Browse the configurable options below if you would like to
+# change or adjust the visual appearance of the GUI
+#
+# 4. If you run on a PC, and have an ancient version of tcl/tk,
+# you must set the values fo Unix, CMD, and Go32 by hand below
+# => with Tcl/Tk 7.5/4.1 or later, this happens automatically
+
+# set CC "cc -w -Wl -woff,84" ;# ANSI-C compiler, suppress warnings
+# set CC "cl -w -nologo" ;# Visual Studio C/C++ compiler, prefered on PCs
+ set CC "gcc -w" ;# standard gcc compiler - no warnings
+ set CC0 "gcc"
+
+# set CPP "cpp" ;# the normal default C preprocessor
+ set CPP "gcc -E -x c" ;# c preprocessor, assuming we have gcc
+
+ set SPIN "spin" ;# use a full path-name if necessary, e.g. c:/cygwin/bin/spin.exe
+ set DOT "dot" ;# optional, graph layout interface
+ ;# no prob if dot is not available
+ set BG "white" ;# default background color for text
+ set FG "black" ;# default foreground color for text
+ set CMD "" ;# default is empty, and adjusted below
+ set Unix 1 ;# default is Unix, but this is adjusted below
+ set Ptype "color" ;# printer-type: mono, color, or gray
+ set NT 0 ;# scratch variable, ignore
+
+ set debug_on 0
+ if {$debug_on} {
+ toplevel .debug ; #debugging window
+ text .debug.txt -width 80 -height 60 -relief raised -bd 2 \
+ -yscrollcommand ".debug.scroll set"
+ scrollbar .debug.scroll -command ".debug.txt yview"
+ pack .debug.scroll -side right -fill y
+ pack .debug.txt -side left
+ }
+ proc debug {txt} {
+ global debug_on
+ if {$debug_on} {
+ catch { .debug.txt insert end "\n$txt" }
+ } }
+
+ if [info exists tcl_platform] {
+ set sys $tcl_platform(platform)
+# if {$sys == "macintosh"} {
+# ... no adjustments needed? ...
+# }
+ if {[string match windows $sys]} {
+ set Unix 0 ;# means Windows95/98/2000/NT/XP
+
+# if {[auto_execok cl] != ""} {
+# set CC "cl -w -nologo" ;# Visual Studio compiler, PCs
+# set CC0 "cl"
+# }
+
+ if {$tcl_platform(os) == "Windows 95"} {
+ set CMD "command.com" ;# Windows95
+ } else {
+ set CMD "cmd"
+ set NT 1
+ } } }
+
+#-- GUI configuration options - by Leszek Holenderski <lhol@win.tue.nl>
+#-- basic text size:
+ set HelvBig -Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*
+# mscs:
+ if {$NT} { ;# on a windows nt machine
+ set SmallFont "-*-Courier-Bold-R-Normal--*-110-*"
+ set BigFont "-*-Courier-Bold-R-Normal--*-110-*"
+ } else {
+ set SmallFont "-*-Courier-Bold-R-Normal--*-80-*"
+ set BigFont "-*-Courier-Bold-R-Normal--*-80-*"
+ }
+
+# Some visual aspects of Xspin GUI can be configured by the user.
+# On PCs, the values of configuration options that are hard-coded
+# into this script can be modified (see below). On Unix, in addition to
+# (or rather, instead of) the manual modification, the values can be set in
+# an Xspin resource file ($HOME/.xspinrc) and/or in the X11 default resource
+# file (usually, $HOME/.Xdefaults).
+
+# Since the same option can be specified in several places, options can have
+# several, possibly inconsistent, values. The value which takes effect is
+# determined by the order in which options are scanned. The values specified
+# later in the order have higher priority. First, hard-coded options are
+# scanned, then options specified in .Xdefaults, and finally options
+# specified in .xspinrc.
+
+# When setting configuration options in an .xspinrc file, convert the
+# settings below to the format of an X11 resource file. For example,
+#
+# # width of scrollbars (any Tk dimension, default 15 pixels)
+# option add *Scrollbar.width 13 startupFile
+#
+# should be converted to
+#
+# ! width of scrollbars (any Tk dimension, default 15 pixels)
+# *Scrollbar.width 13
+# In .Xdefaults file, configuration options should be preceded by the
+# application's name, so the above option should be converted to
+#
+# ! width of scrollbars (any Tk dimension, default 15 pixels)
+# xspin*Scrollbar.width 13
+# side on which side scrollbars are put (left or right, default=right)
+
+option add *Scrollbar.side left startupFile
+
+#--- sizes
+ # width of scrollbars (any Tk dimension, default 15 pixels)
+ option add *Scrollbar.width 13 startupFile
+ # width of borders (in pixels, typically 1 or 2, default 2)
+ option add *borderWidth 1 startupFile
+ # initial width of the input/log area (in characters, default 80)
+ option add *Input*Text.width 72 startupFile
+ option add *Log*Text.width 72 startupFile
+ # initial height of the input area (in lines, default 24)
+ option add *Input*Text.height 20 startupFile
+ # initial height of the log area (in lines, default 24)
+ option add *Log*Text.height 5 startupFile
+ # size of the handle used to adjust the height of the log area
+ # (in pixels, default 0)
+ option add *Handle.width 10 startupFile
+ option add *Handle.height 10 startupFile
+#--- colors
+ # colors for the input/log area
+ option add *Input*Text.background white startupFile
+ option add *Input*Text.foreground black startupFile
+ option add *Log*Text.background gray95 startupFile
+ option add *Log*Text.foreground black startupFile
+ # colors for the editable/read-only area
+ option add *Entry.background white startupFile
+ option add *Edit*Text.background white startupFile
+ option add *Edit*Text.foreground black startupFile
+ # colors for the editable/read-only area
+ option add *Read*Text.background gray95 startupFile
+ option add *Read*Text.foreground black startupFile
+#--- fonts
+ # fonts for the input/log area (default is system dependent,
+ # usually -*-Courier-Medium-R-Normal--*-120-*)
+option add *Input*Text.font -*-Helvetica-Medium-R-Normal--*-120-* startupFile
+#option add *Input*Text.font -schumacher-clean-medium-r-normal--*-120-*-60-* startupFile
+#option add *Log*Text.font -schumacher-clean-medium-r-normal--*-120-*-60-* startupFile
+
+#--- widgets
+ # simple texts (dialogs which present read-only texts, such as help)
+ option add *SimpleText.Text.width 60
+ option add *SimpleText.Text.height 30
+ option add *SimpleText.Text.background white
+
+ # sections (decorated frames for grouping related buttons)
+ option add *Section*Title.font -*-Helvetica-Bold-R-Normal--*-100-* startupFile
+
+#option add *Section*Checkbutton.font -*-Helvetica-Medium-R-Normal--*-100-* startupFile
+#option add *Section*Radiobutton.font -*-Helvetica-Medium-R-Normal--*-100-* startupFile
+#option add *Section*Label.font -*-Helvetica-Medium-R-Normal--*-100-* startupFile
+
+################ end of configurable parameters #######################
+
+wm title . "SPIN CONTROL $xversion"
+wm iconname . "SPIN"
+wm geometry . +41+50
+wm minsize . 400 200
+
+set Fname ""
+set firstime 1
+set notignored 0
+
+#### seed the advanced parameters settings
+
+set e(0) "Physical Memory Available (in Mbytes): "
+set ival(0) 128
+set expl(0) "explain"
+
+set e(1) "Estimated State Space Size (states x 10^3): "
+set ival(1) 500
+set expl(1) "explain"
+
+set e(2) "Maximum Search Depth (steps): "
+set ival(2) 10000
+set expl(2) "explain"
+
+set e(7) "Nr of hash-functions in Bitstate mode: "
+set ival(7) 2
+set expl(7) "explain"
+
+set e(3) "Extra Compile-Time Directives (Optional): "
+set ival(3) ""
+set expl(3) "Choose"
+
+set e(4) "Extra Run-Time Options (Optional): "
+set ival(4) ""
+set expl(4) "Choose"
+
+set e(5) "Extra Verifier Generation Options: "
+set ival(5) ""
+set expl(5) "Choose"
+
+set ival(6) 1 ;# which error is reported, default is 1st
+
+
+# allow no more than one entry per selection
+catch { tk_listboxSingleSelect Listbox }
+
+proc msg_file {f nowarn} {
+ set msg ""
+ set ef [open $f r]
+
+ while {[gets $ef line] > -1} {
+ if {$nowarn} {
+ if {[string first "warning" $line] < 0} {
+ set msg "$msg\n$line"
+ }
+ } else {
+ set msg "$msg\n$line"
+ } }
+ close $ef
+ return $msg
+}
+
+ scan $tk_version "%d.%d" tk_major tk_minor
+
+ set version ""
+
+ if {[auto_execok $SPIN] == "" \
+ || [auto_execok $SPIN] == 0} {
+ set version "Error: No executable $SPIN found..."
+ } else {
+ if {$Unix} {
+ set version [exec $SPIN -V]
+ } else {
+ catch { exec $SPIN -V >&pan.tmp } err
+ if {$err == ""} {
+ set version [msg_file pan.tmp 1]
+ } else {
+ puts "error: $err"
+ puts "is there a $SPIN and a go32.exe?"
+ exit
+ } }
+ if {[string first "Spin Version 5" $version] < 0 \
+ && [string first "Spin Version 4" $version] < 0 \
+ && [string first "Spin Version 3" $version] < 0 } {
+ set version "Spin Version not recognized: $version"
+ }
+ }
+
+frame .menu
+ # top level menu bar
+ menubutton .menu.file -text "File.." \
+ -relief raised -menu .menu.file.m
+ menubutton .menu.run -text "Run.." -fg red \
+ -relief raised -menu .menu.run.m
+ menubutton .menu.edit -text "Edit.." \
+ -relief raised -menu .menu.edit.m
+ menubutton .menu.view -text "View.." \
+ -relief raised -menu .menu.view.m
+ label .menu.title -text "SPIN DESIGN VERIFICATION" -fg blue
+
+ set lno 1
+ label .menu.lno -text "Line#:" -relief raised
+ entry .menu.ent -width 6 -textvariable lno \
+ -relief sunken -background white -foreground black
+ bind .menu.ent <Return> {
+ .inp.t tag remove hilite 0.0 end
+ .inp.t tag add hilite $lno.0 $lno.1000
+ .inp.t tag configure hilite -background $FG -foreground $BG
+ .inp.t yview -pickplace [expr $lno-4]
+ }
+
+ label .menu.fnd1 -text "Find:" -relief raised
+ entry .menu.fnd2 -width 8 -textvariable pat \
+ -relief sunken -background white -foreground black
+ bind .menu.fnd2 <Return> {
+ .inp.t tag remove hilite 0.0 end
+ forAllMatches .inp.t $pat
+ }
+
+ menubutton .menu.help -text "Help" -relief raised \
+ -menu .menu.help.m
+
+ pack append .menu \
+ .menu.file {left frame w} \
+ .menu.edit {left frame w} \
+ .menu.view {left frame w} \
+ .menu.run {left frame w} \
+ .menu.help {left frame w} \
+ .menu.title {left frame c expand} \
+ .menu.fnd2 {right frame e} \
+ .menu.fnd1 {right frame e} \
+ .menu.ent {right frame e} \
+ .menu.lno {right frame e}
+
+set loglines 5
+frame .log
+ text .log.t -bd 2 -height $loglines -background $FG -foreground $BG
+frame .log.b
+ button .log.b.pl -text "+" \
+ -command {incr loglines 1; .log.t configure -height $loglines}
+ button .log.b.mn -text "-" \
+ -command {incr loglines -1; .log.t configure -height $loglines}
+ pack append .log.b .log.b.pl {top}
+ pack append .log.b .log.b.mn {top}
+ pack append .log .log.b {left filly}
+ pack append .log .log.t {right expand fill}
+
+proc dopaste {} {
+ global FG BG
+ set from [.inp.t index insert]
+ tk_textPaste .inp.t
+ set upto [.inp.t index insert]
+ .inp.t tag add sel $from $upto
+# .inp.t tag configure hilite -background $FG -foreground $BG
+}
+
+#- fetch the value of user-defined configuration options
+
+proc fetchOption {name default args} {
+
+ set class Dummy
+ set fullName $name
+
+ # class encoded in name ?
+ switch -glob -- $name *.* {
+ set list [split $name .]
+ switch [llength $list] 2 {} default { error "wrong option \"$name\" }
+ set class [lindex $list 0]
+ set name [lindex $list 1]
+ }
+
+ # create a unique dummy frame of requested class and get the option's value
+ set dummy .0
+ while {[winfo exists $dummy]} { append dummy 0 }
+ frame $dummy -class $class
+ set value [option get $dummy $name $class]
+ destroy $dummy
+
+ # option not defined ?
+ switch -- $value "" { return $default }
+
+ # check a restriction on option's value
+ switch [llength $args] {
+ 0 { # no restriction
+ }
+ 1 { # restriction is given as a list of allowed values
+ switch -- [lsearch -exact [lindex $args 0] $value] -1 {
+ set msg "wrong value \"$value\" of option \"$fullName\"\
+ (should be one of $args)"
+ return -code error -errorinfo $msg $msg
+ }
+ }
+ 2 { # restriction is given as a range (min and max)
+ set min [lindex $args 0]
+ set max [lindex $args 1]
+ if {$value < $min} { set $value $min }
+ if {$value > $max} { set $value $max }
+ }
+ default {
+ error "internal error in fetchOption: wrong restriction \"$args\""
+ }
+ }
+
+ return $value
+}
+
+# width of borders
+set BD [fetchOption borderWidth 1 0 4]
+#option add *Text.highlightThickness $BD startupFile
+
+# scrollbar's side
+set scrollbarSide [fetchOption Scrollbar.side right {left right}]
+
+frame .inp
+ # view of spin input
+ scrollbar .inp.s -command ".inp.t yview"
+ text .inp.t -bd 2 -font $HelvBig -yscrollcommand ".inp.s set" -wrap word
+
+ pack .inp.s -side $scrollbarSide -fill y
+ pack append .inp \
+ .inp.t {left expand fill}
+
+ menu .inp.t.edit -tearoff 0
+ .inp.t.edit add command -label "Cut" \
+ -command {tk_textCopy .inp.t; tk_textCut .inp.t}
+ .inp.t.edit add command -label "Copy" \
+ -command {tk_textCopy .inp.t}
+ .inp.t.edit add command -label "Paste" \
+ -command {dopaste}
+
+ bind .inp.t <ButtonPress-3> {
+ tk_popup .inp.t.edit %X %Y
+ }
+ bind .inp.t <ButtonRelease-1> { setlno }
+
+
+set l_typ 0; # used by both simulator and validator
+set lt_typ 0; # used by ltl panel
+set ol_typ -1; # remembers setting last used in compilation
+set m_typ 2; # used by simulator
+
+menu .menu.file.m
+ .menu.file.m add command -label "New" \
+ -command ".inp.t delete 0.0 end"
+# .menu.file.m add command -label "UnSelect" \
+# -command ".inp.t tag remove hilite 0.0 end;\
+# .inp.t tag remove Rev 0.0 end;\
+# .inp.t tag remove sel 0.0 end"
+ .menu.file.m add command -label "ReOpen" -command "open_spec"
+ .menu.file.m add command -label "Open.." -command "open_spec 0"
+ .menu.file.m add command -label "Save As.." -command "save_spec 0"
+ .menu.file.m add command -label "Save" -command "save_spec"
+ .menu.file.m add command -label "Quit" \
+ -command "cleanup 1; destroy .; exit"
+
+menu .menu.help.m
+ .menu.help.m add command -label "About Spin" \
+ -command "aboutspin"
+ .menu.help.m add separator
+ .menu.help.m add command -label "Promela Usage" \
+ -command "promela"
+ .menu.help.m add command -label "Xspin Usage" \
+ -command "helper"
+ .menu.help.m add command -label "Simulation" \
+ -command "roadmap1"
+ .menu.help.m add command -label "Verification" \
+ -command "roadmap2"
+ .menu.help.m add command -label "LTL Formulae" \
+ -command "roadmap4"
+ .menu.help.m add command -label "Spin Automata View" \
+ -command "roadmap5"
+ .menu.help.m add command -label "Reducing Complexity" \
+ -command "roadmap3"
+
+menu .menu.run.m
+ .menu.run.m add command -label "Run Syntax Check" \
+ -command {syntax_check "-a -v" "Syntax Check"}
+ .menu.run.m add command -label "Run Slicing Algorithm" \
+ -command {syntax_check "-A" "Property-Specific Slicing"}
+ .menu.run.m add separator
+ .menu.run.m add command -label "Set Simulation Parameters.." \
+ -command simulation
+ .menu.run.m add command -label "(Re)Run Simulation" \
+ -command Rewind -state disabled
+ .menu.run.m add separator
+ .menu.run.m add command -label "Set Verification Parameters.." \
+ -command "basicval"
+ .menu.run.m add command -label "(Re)Run Verification" \
+ -command {runval "0"} -state disabled
+ .menu.run.m add separator
+ .menu.run.m add command -label "LTL Property manager.." \
+ -command call_tl
+ .menu.run.m add separator
+ .menu.run.m add command -label "View Spin Automaton for each Proctype.." \
+ -command fsmview
+
+
+menu .menu.edit.m
+ .menu.edit.m add command -label "Cut" \
+ -command {tk_textCopy .inp.t; tk_textCut .inp.t}
+ .menu.edit.m add command -label "Copy" \
+ -command {tk_textCopy .inp.t}
+ .menu.edit.m add command -label "Paste" \
+ -command {tk_textPaste .inp.t}
+
+set FSz 110
+
+menu .menu.view.m
+ .menu.view.m add command -label "Larger" \
+ -command {
+ incr FSz 10
+ set HelvBig "-Adobe-Helvetica-Medium-R-Normal--*-$FSz-*-*-*-*-*-*"
+ .inp.t configure -font $HelvBig
+ }
+ .menu.view.m add command -label "Default text size" \
+ -command {
+ set FSz 110
+ set HelvBig "-Adobe-Helvetica-Medium-R-Normal--*-$FSz-*-*-*-*-*-*"
+ .inp.t configure -font $HelvBig
+ }
+ .menu.view.m add command -label "Smaller" \
+ -command {
+ incr FSz -10
+ set HelvBig "-Adobe-Helvetica-Medium-R-Normal--*-$FSz-*-*-*-*-*-*"
+ .inp.t configure -font $HelvBig
+ }
+ .menu.view.m add separator
+ .menu.view.m add command -label "Clear Selections" \
+ -command ".inp.t tag remove hilite 0.0 end;\
+ .inp.t tag remove Rev 0.0 end;\
+ .inp.t tag remove sel 0.0 end"
+
+proc setlno {} {
+ scan [.inp.t index insert] "%d.%d" lno cno
+ .menu.ent delete 0 end
+ .menu.ent insert end $lno
+ .inp.t tag remove hilite $lno.0 $lno.end ;# or else cursor is invis
+ update
+}
+
+proc add_log {{y ""}} {
+
+ if {$y == "\n"} { return }
+ .log.t insert end "\n$y"
+ .log.t yview -pickplace end
+}
+
+proc cleanup {how} {
+ global Unix
+ if {$Unix == 0 && $how == 1} {
+ add_log "removing temporary files, please wait.."; update
+ }
+ rmfile "pan.h pan.c pan.t pan.m pan.b pan.tmp pan.ltl"
+ rmfile "pan.oin pan.pre pan.out pan.exe pan.otl"
+ rmfile "pan_in pan_in.trail trail.out pan"
+ rmfile "_tmp1_ _tmp2_ pan.o pan.obj pan.exe"
+ if {$Unix == 0 && $how == 1} { add_log "done.." }
+}
+
+
+pack append . \
+ .log {bot frame w fillx} \
+ .inp {bot frame w expand fill} \
+ .menu {top fillx}
+
+# simulation parameters - initial settings
+ set fvars 1
+ set msc 1; set svars 1
+ set rvars 1
+ set stop 0; set tsc 0
+ set seed "1"; # random sumulation
+ set jumpsteps "0"; # guided simulation
+
+ set s_typ 0
+ # meaning s_type values:
+ # 0 - Random Simulation (using seed)
+ # 1 - Guided Simulation (using pan.trail)
+ # 2 - Interactive Simulation
+
+ set whichsim 0
+ # meaning of whichsim values:
+ # 0 - use pan_in.tra(il)
+ # 1 - use user specified file
+
+tkwait visibility .log
+add_log "SPIN LOG:"
+add_log " $version"
+add_log "Xspin Version $xversion"
+add_log "TclTk Version [info tclversion]/$tk_version\n"
+
+ if {$Unix == 0} {
+ if {[auto_execok $CC0] == "" \
+ || [auto_execok $CC0] == 0} {
+ set m "Error: no C compiler found: $CC"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+ }}
+
+.inp.t configure -background $BG -foreground $FG
+
+# process execution barchart
+
+set Data(0) 0
+set Name(0) "-"
+set n 0
+set bar_handle 0
+set PlaceBar ""
+
+proc stopbar {} {
+ global Data Name n PlaceBar
+ for {set i 0} {$i <= $n} {incr i} {
+ set Data($i) 0
+ set Name($i) ""
+ }
+ set n 0
+ set PlaceBar [wm geometry .bar]
+ set k [string first "\+" $PlaceBar]
+ if {$k > 0} {
+ set PlaceBar [string range $PlaceBar $k end]
+ }
+ catch { destroy .bar }
+}
+
+proc growbar {v} {
+ global n Data
+ set Data($n) $v
+ incr n
+ catch { fillbar }
+}
+
+proc shrinkbar {} {
+ global n
+ incr n -1
+ set Data($n) 0
+ catch { fillbar }
+}
+
+proc stepbar {v nm} {
+ global n Data Name
+
+ if {$v >= 0} {
+ if { [info exists Data($v)] } {
+ incr Data($v)
+ } else {
+ set Data($v) 1
+ }
+ if {$v >= $n} {
+ set n [expr $v+1]
+ }
+ if { [string length $nm] > 4} {
+ set Name($v) [string range $nm 0 4]
+ } else {
+ set Name($v) $nm
+ }
+ catch { fillbar }
+ }
+}
+
+proc adjustbar {v w} {
+ global Data
+
+ set Data($v) $w
+ catch { fillbar }
+}
+
+proc startbar {tl} {
+ global n Data bar_handle Ptype PlaceBar
+
+ catch { destroy .bar }
+ toplevel .bar
+ wm minsize .bar 200 200
+ wm title .bar "$tl"
+
+ set maxy [expr [winfo screenheight .] - 200]
+ if {$PlaceBar == ""} {
+ set PlaceBar "+[expr [winfo rootx .]+150]+$maxy"
+ }
+ wm geometry .bar $PlaceBar
+
+ set bar_handle [canvas .bar.c -height 410 -width 410 -relief raised]
+ frame .bar.buts
+
+ button .bar.buts.s1 -text "Save in: panbar.ps" \
+ -command ".bar.c postscript -file panbar.ps -colormode $Ptype"
+
+ button .bar.buts.b -text " Close " -command "stopbar"
+ pack .bar.buts.b .bar.buts.s1 -side right
+ pack append .bar .bar.c {top expand fill} .bar.buts {bot frame e}
+}
+
+proc fillbar {} {
+ global n Data Name
+
+ .bar.c delete grid
+ .bar.c delete data
+ set sum 0
+ for {set i 0} {$i < $n} {incr i} {
+ if { [info exists Data($i)] } {
+ incr sum $Data($i)
+ } else {
+ set Data($i) 0
+ set Name($i) "-"
+ }
+ }
+ for {set i 0} {$i < $n} {incr i} {
+ .bar.c create line \
+ $i 0 \
+ $i 100 \
+ -fill #222222 -tags grid
+ .bar.c create text $i 105 \
+ -text $i -tags grid
+ .bar.c create text $i 110 \
+ -text "$Name($i)" \
+ -fill blue -tags grid
+
+ if { [info exists Data($i)] } {
+ set y [expr ($Data($i)*100)/$sum]
+ .bar.c create line \
+ $i 100 \
+ $i [expr 100-$y] \
+ -width 35 -fill red -tags data
+ if {$y > 6} {
+ set nrcol "yellow"
+ } else {
+ set nrcol "red"
+ }
+ .bar.c create text $i 95 \
+ -text "$Data($i)" \
+ -fill $nrcol -tags grid
+ }
+ }
+
+ .bar.c create text [expr ($n)/2.0] -15 -text "Percentage of $sum System Steps" \
+ -anchor c -justify center -tags grid
+ .bar.c create text [expr ($n)/2.0] -8 -text "Executed Per Process ($n total)" \
+ -anchor c -justify center -tags grid
+ .bar.c scale all 0 0 55 3
+ if {$n <= 5} {
+ .bar.c move all 100 60
+ } else {
+ .bar.c move all 50 60
+ }
+}
+
+proc barscales {} {
+ global bar_handle
+
+ catch {
+ button .bar.buts.b4 -text "Larger" \
+ -command "$bar_handle scale all 0 0 1.1 1.1"
+ button .bar.buts.b5 -text "Smaller" \
+ -command "$bar_handle scale all 0 0 0.9 0.9"
+ pack append .bar.buts \
+ .bar.buts.b4 {right padx 5} \
+ .bar.buts.b5 {right padx 5}
+ }
+}
+
+# Files and Generic Boxes
+
+set file ""
+set boxnr 0
+
+proc rmfile {f} {
+ global Unix CMD tk_major tk_minor
+
+ set err ""
+ catch { eval file delete -force $f } err
+ if {$err == "" } { return }
+
+ if {$Unix} {
+ catch {exec rm -f $f}
+ } else {
+ set n [llength $f]
+ for {set i 0} {$i < $n} {incr i} {
+ set g [lindex $f $i]
+ add_log "rm $g"
+ if {$tk_major >= 4 && $tk_minor >= 2} {
+ catch {exec $CMD /c del $g}
+ } else {
+ catch {exec $CMD >&@stdout /c del $g}
+ }
+ } }
+}
+
+proc mvfile {f g} {
+ global Unix CMD tk_major tk_minor
+
+ set err ""
+ catch { file rename -force $f $g } err
+ if {$err == "" } { return }
+
+ if {$Unix} {
+ catch {exec mv $f $g}
+ } else {
+ if {$tk_major >= 4 && $tk_minor >= 2} {
+ catch {exec $CMD /c move $f $g}
+ } else {
+ catch {exec $CMD >&@stdout /c move $f $g}
+ }
+ }
+}
+
+proc cpfile {f g} {
+ global Unix CMD tk_major tk_minor
+
+ set err ""
+ catch { file copy -force $f $g } err
+ if {$err == "" } { return }
+
+ if {$Unix} {
+ catch {exec cp $f $g}
+ } else {
+ if {$tk_major >= 4 && $tk_minor >= 2} {
+ catch {exec $CMD /c copy $f $g}
+ } else {
+ catch {exec $CMD >&@stdout /c copy $f $g}
+ } }
+}
+
+proc cmpfile {f g} {
+ global Unix
+
+ set err ""
+ if {$Unix} {
+ catch {exec cmp $f $g} err
+ } else {
+ if {[file exists $f] == 0 \
+ || [file exists $g] == 0} {
+ return "error"
+ }
+ set fd1 [open $f r]
+ set fd2 [open $g r]
+ while {1} {
+ set n1 [gets $fd1 line1]
+ set n2 [gets $fd2 line2]
+ if {$n1 != $n2 \
+ || [string compare $line1 $line2] != 0} {
+ set err "files differ"
+ break
+ }
+ if {$n1 < 0} { break }
+ }
+ close $fd1
+ close $fd2
+ }
+ return $err
+}
+
+proc file_ok {f} {
+ if {[file exists $f]} {
+ if {![file isfile $f] || ![file writable $f]} {
+ set m "error: file $f is not writable"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+ return 0
+ } }
+ return 1
+}
+
+proc mkpan_in {} {
+ global HasNever
+ set fd [open pan_in w]
+
+ fconfigure $fd -translation lf
+ puts $fd [.inp.t get 0.0 end] nonewline
+
+ if {$HasNever != ""} {
+ if [catch {set fdn [open $HasNever r]} errmsg] {
+ add_log $errmsg
+ catch { tk_messageBox -icon info -message $errmsg }
+ } else {
+ while {[gets $fdn line] > -1} {
+ puts $fd $line
+ }
+ catch "close $fdn"
+ } }
+ catch "flush $fd"
+ catch "close $fd"
+}
+
+proc no_ltlchange {} {
+
+ if {![file exists pan.ltl]} {
+ return 1
+ }
+ if {![file exists pan.otl]} {
+ cpfile pan.ltl pan.otl
+ return 0 ; first time
+ }
+ set err [cmpfile pan.ltl pan.otl]
+ if {[string length $err] > 0} {
+ cpfile pan.ltl pan.otl
+ return 0 ;# different
+ }
+ return 1 ;# unchanged
+}
+
+proc no_change {} {
+ global nv_typ
+
+ mkpan_in ;# keep this up to date
+ if {![file exists pan.oin]} {
+ cpfile pan_in pan.oin
+ return 0 ; first time
+ }
+ set err [cmpfile pan_in pan.oin]
+ if {[string length $err] > 0} {
+ cpfile pan_in pan.oin
+ return 0 ;# different
+ }
+ if {$nv_typ == 0} {
+ return 1
+ }
+ return [no_ltlchange] ;# unchanged
+}
+
+proc mk_exec {} {
+ global Unix CC SPIN notignored
+
+ set nochange [no_change]
+ if {$nochange == 1 && [file exists "pan"]} {
+ add_log "<no recompilation needed>"
+ return 1
+ }
+
+ add_log "<compiling executable>"
+ catch {
+ warner "Compiling" "Please wait until compilation of the \
+executable produced by spin completes." 300
+ }
+ add_log "$SPIN -a pan_in"
+
+ catch {exec $SPIN -a pan_in >&pan.tmp}
+ set errmsg [msg_file pan.tmp 1]
+
+ if {[string length $errmsg]>0} {
+ add_log "$errmsg"
+ catch { tk_messageBox -icon info -message $errmsg }
+ add_log "<stopped compilation attempt>"
+ catch { destroy .warn }
+ return 0
+ }
+
+ add_log "$CC -o pan -D_POSIX_SOURCE pan.c"; update
+ if {$Unix} {
+ catch { eval exec $CC -o pan -D_POSIX_SOURCE pan.c >pan.tmp 2>pan.err} errmsg
+ } else {
+ catch { eval exec $CC -o pan -D_POSIX_SOURCE pan.c >pan.tmp 2>pan.err}
+ }
+ set errmsg [msg_file pan.tmp 0]
+ set errmsg "$errmsg \n [msg_file pan.err 0]"
+
+ if {[string length $errmsg]>0 && $notignored} {
+ add_log "$errmsg"
+ catch { tk_messageBox -icon info -message $errmsg }
+ catch { destroy .warn }
+ return 0
+ }
+ add_log "<compilation complete>"
+ catch {destroy .warn}
+ return 1
+}
+
+set PlaceWarn "+20+20"
+
+proc warner {banner msg w} {
+ global PlaceWarn
+
+ catch {destroy .warn}
+ toplevel .warn
+
+ wm title .warn "$banner"
+ wm iconname .warn "Info"
+ set k [string first "\+" $PlaceWarn]
+ if {$k > 0} {
+ set PlaceWarn [string range $PlaceWarn $k end]
+ }
+ wm geometry .warn $PlaceWarn
+
+ message .warn.t -width $w -text $msg
+ button .warn.ok -text "Ok" \
+ -command "set PlaceWarn [wm geometry .warn]; destroy .warn"
+
+ pack append .warn .warn.t {top expand fill}
+ pack append .warn .warn.ok {bottom}
+
+ update
+}
+
+proc dosave {} {
+ global Fname xversion
+
+ if {[file_ok $Fname]==0} return
+ set fd [open $Fname w]
+ add_log "<saved spec in $Fname>"
+ puts $fd "[.inp.t get 0.0 end]" nonewline
+ catch "flush $fd"
+ catch "close $fd"
+ wm title . "SPIN CONTROL $xversion -- File: $Fname"
+}
+
+proc save_spec {{curr 1}} {
+#-
+#- Save the input area into a file.
+#-
+#- If 'curr' is true then we save to the current file. Otherwise, a file
+#- selection dialog is presented. If a file is selected (note that the
+#- dialog can be canceled) then we try to write to it.
+#-
+
+ global Fname
+
+ if $curr {
+ switch -- $Fname "" {
+ add_log "no file to save to, try \"Save as ...\""
+ return
+ }
+ writeoutfile .inp.t $Fname
+ } else {
+ # try to use the predefined file selection dialog
+ switch [info commands tk_getSaveFile] "" {
+ # some old version of Tk so use our own file selection dialog
+ set fileselect "FileSelect save"
+ } default {
+ set fileselect "tk_getSaveFile"
+ }
+
+ # get the file (return if the file selection dialog canceled)
+ switch -- [set file [eval $fileselect]] "" return
+
+ # write the file and update Fname if the file written successfully
+ if [writeoutfile .inp.t $file] {
+ set Fname $file
+ }
+ }
+}
+
+proc consider_it {} {
+ global file Fname xversion
+
+ if {[file isdirectory $file]} then {
+ cd $file
+ fillerup ""
+ add_log "cd $file"
+ } else {
+ if {![file isfile $file]} {
+ set file ""
+ } else {
+ readinfile .inp.t $file
+
+ rmfile pan_in.trail
+ cpfile $file.trail pan_in.trail
+
+ set dir [pwd]
+ set Fname $file
+ wm title . "SPIN CONTROL $xversion -- File: $Fname"
+ destroy .b
+ } }
+}
+
+#----------------------------------------------------------------------
+# Improvements - by Leszek Holenderski <lhol@win.tue.nl>
+# GUI configuration and File Selection dialogs
+#----------------------------------------------------------------------
+
+# predefined priorities for options stored in the option data base are
+# widgetDefault 20
+# startupFile 40
+# userDefault 60
+# interactive 80
+
+# most of frames are used for layout purposes so they should be invisible
+option add *Frame.borderWidth 0 interactive
+
+proc try_with {xspinrc} {
+
+ if ![file exists $xspinrc] return
+
+ if ![file isfile $xspinrc] {
+ puts "xspin warning: the resource file \"$xspinrc\" exists but is not\
+ a normal file"
+ return
+ }
+ if ![file readable $xspinrc] {
+ puts "xspin warning: the resource file \"$xspinrc\" exists but is not\
+ readable"
+ return
+ }
+ if [catch {option readfile $xspinrc userDefault} result] {
+ puts "xspin warning: some problems when trying to load the resource\
+ file \"$xspinrc\"\n$result"
+ return
+ }
+}
+
+if [info exists env(HOME)] {
+ if $Unix {
+ try_with [file join $env(HOME) .xspinrc]
+ } else {
+ try_with [file join $env(HOME) xspinrc]
+ }
+}
+
+proc FileSelect {mode {title ""}} {
+ switch -- $mode open - save {} default { set mode open }
+
+ switch $mode {
+ open {
+ set title Open
+ set okButtonText Open
+ }
+ save {
+ set title Save
+ set okButtonText Save
+ }
+ }
+
+ set w .fileselect
+ upvar #0 $w this
+
+ if [winfo exists $w] {
+ wm title $w $title
+ $this(okButton) config -text $okButtonText
+ catch {wm geom $w $this(geom)}
+ wm deiconify $w
+ } else {
+ toplevel $w -class Fileselect
+ wm title $w $title
+ # the minimal size is given in characters and lines (setgrid is on)
+ wm minsize $w 14 7
+
+ # layout frames
+ pack [set f [frame $w.f]] -padx 5 -pady 5 -fill both -expand yes
+ pack [set buttons [frame $f.b]] -side bottom -fill x
+ pack [set name [frame $f.n]] -side bottom -fill x -pady 5
+ pack [set path [frame $f.p]] -side top -fill x
+ pack [set files [frame $f.f]] -side top -fill both -expand yes
+
+ # create ok/cancel buttons
+ set okButton [button $buttons.ok -text $okButtonText \
+ -command "FileSelect.Close $w 1"]
+ pack $okButton -side right
+
+ set cancelButton [button $buttons.cancel -text Cancel \
+ -command "FileSelect.Close $w 0"]
+ pack $cancelButton -side left
+
+ MakeSameWidth "$okButton $cancelButton"
+
+ # create path button
+ set pathButton $path.path
+ global $w|currDir
+ set pathMenu [tk_optionMenu $pathButton $w|currDir ""]
+ pack $pathButton -side top
+
+ # create the list of files
+ global scrollbarSide
+
+ set fileList $files.l
+ set scrollbar $files.s
+ pack [scrollbar $files.s -command "$fileList yview"] \
+ -side $scrollbarSide -fill y
+ pack [listbox $fileList -yscrollcommand "$files.s set" -selectmode single -setgrid on] \
+ -side $scrollbarSide -expand yes -fill both
+
+ bind $fileList <ButtonPress-1> "FileSelect.Selected $w %x %y"
+ bind $fileList <Double-ButtonPress-1> "FileSelect.Chosen $w %x %y"
+
+ set fileEntry $name.e
+ pack [label $name.l -text File:] -side left
+ pack [entry $fileEntry] -side right -expand yes -fill x
+
+ set this(okButton) $okButton
+ set this(pathButton) $pathButton
+ set this(pathMenu) $pathMenu
+ set this(fileList) $fileList
+ set this(fileEntry) $fileEntry
+
+ foreach widget "$okButton $cancelButton $pathButton $fileList $scrollbar" {
+ $widget config -highlightthickness 0
+ }
+
+ wm protocol $w WM_DELETE_WINDOW [$cancelButton cget -command]
+ }
+
+ # fill in the list of files
+ if ![info exists this(path)] { set this(path) [pwd] }
+ FileSelect.cd $w $this(path)
+
+ # make the dialog modal (set a grab and claim the focus)
+
+ set oldFocus [focus]
+ set oldGrab [grab current $w]
+ if {$oldGrab != ""} { set grabStatus [grab status $oldGrab] }
+ grab $w
+ focus $this(fileEntry)
+
+ # make the contents of file entry selected (for easy deletion)
+ $this(fileEntry) select from 0
+ $this(fileEntry) select to end
+
+ # Wait for the user to respond, then restore the focus and return the
+ # contents of file entry.
+ # Restore the focus before deleting the window, since otherwise the
+ # window manager may take the focus away so we can't redirect it.
+ # Finally, restore any grab that was in effect.
+
+ global $w|response
+ tkwait variable $w|response
+ catch {focus $oldFocus}
+ grab release $w
+ set this(geom) [wm geom $w]
+ wm withdraw $w
+ if {$oldGrab != ""} {
+ if {$grabStatus == "global"} {
+ grab -global $oldGrab
+ } else {
+ grab $oldGrab
+ }
+ }
+ return [set $w|response]
+}
+
+proc CompareNoCase {s1 s2} {
+ return [string compare [string tolower $s1] [string tolower $s2]]
+}
+
+proc FileSelect.LoadFiles {w} {
+ upvar #0 $w this
+
+ # split all names in the current directory into dirs and files
+ set dirs ""
+ set files ""
+ set filter ""
+ if [info exists this(filter)] { set filter $this(filter) }
+ switch -- $filter "" { set filter * }
+ foreach f [lsort -command CompareNoCase [glob -nocomplain .* *]] {
+ if [file isdir $f] {
+ # exclude the '.' and '..' directory
+ switch -- $f . - .. continue
+ lappend dirs $f
+ }
+ if [file isfile $f] {
+ # filter files
+ switch -glob -- $f $filter { lappend files $f }
+ }
+ }
+
+ # Fill in the file list
+ $this(fileList) delete 0 end
+ foreach d $dirs {
+ # append directory mark to the name (tricky)
+ set d [string trimright [file join $d a] a]
+ $this(fileList) insert end $d
+ }
+ foreach f $files { $this(fileList) insert end $f }
+}
+
+proc FileSelect.LoadPath {w} {
+ upvar #0 $w this
+
+ # convert path to list
+ set dirs [file split $this(path)]
+
+ # compute prefix paths
+ set path ""
+ set paths ""
+ foreach d $dirs {
+ set path [file join $path $d]
+ lappend paths $path
+ }
+
+ # reverse dirs and paths
+ set rev_dirs ""
+ foreach d $dirs { set rev_dirs [concat [list $d] $rev_dirs] }
+ set rev_paths ""
+ foreach p $paths { set rev_paths [concat [list $p] $rev_paths] }
+
+ # update the path menubutton
+ global $w|currDir
+ set $w|currDir [lindex $rev_dirs 0]
+
+ # fill in the path menu
+ $this(pathMenu) delete 0 end
+ foreach d [lrange $rev_dirs 1 end] p [lrange $rev_paths 1 end] {
+ $this(pathMenu) add command -label $d -command "FileSelect.cd $w $p"
+ }
+}
+
+proc FileSelect.Selected {w x y} {
+ upvar #0 $w this
+
+ # determine the selected list element
+ set elem [$this(fileList) get [$this(fileList) index @$x,$y]]
+ switch -- $elem "" return
+
+ # directories cannot be selected (they can only be chosen)
+ if [file isdir $elem] return
+
+ $this(fileEntry) delete 0 end
+ $this(fileEntry) insert end $elem
+}
+
+proc FileSelect.Chosen {w x y} {
+ upvar #0 $w this
+
+ # determine the selected list element
+ set elem [$this(fileList) get [$this(fileList) index @$x,$y]]
+ switch -- $elem "" return
+
+ # if directory then cd, otherwise close the dialog
+ if [file isdir $elem] { FileSelect.cd $w $elem } { FileSelect.Close $w 1 }
+}
+
+proc FileSelect.Close {w {ok 1}} {
+ # trigger the end of dialog
+ upvar #0 $w this $w|response response
+ if $ok {
+ set response [file join $this(path) [$this(fileEntry) get]]
+ } else {
+ set response ""
+ }
+}
+
+proc FileSelect.cd {w dir} {
+ upvar #0 $w this
+
+ if [catch {cd $dir} errmsg] {
+ puts "xspin warning: $errmsg"
+ return
+ }
+
+ set this(path) [pwd]
+ FileSelect.LoadFiles $w
+ FileSelect.LoadPath $w
+}
+
+proc open_spec {{curr 1}} {
+ global Fname
+
+ if $curr {
+ switch -- $Fname "" {
+ add_log "no file to reopen, try \"Open ...\""
+ return
+ }
+ readinfile .inp.t $Fname
+ } else {
+ # try to use the predefined file selection dialog
+ switch [info commands tk_getOpenFile] "" {
+ # some old version of Tk so use our own file selection dialog
+ set fileselect "FileSelect open"
+ } default {
+ set fileselect "tk_getOpenFile"
+ }
+ set init_dir [pwd]
+ # get the file (return if the file selection dialog canceled)
+ switch -- [set file [eval $fileselect -initialdir { $init_dir } ]] "" return
+
+ # load the file and update some items if the file loaded successfully
+ if [readinfile .inp.t $file] {
+ rmfile pan_in.trail
+ cpfile $file.trail pan_in.trail
+ set Fname $file
+ set_path $Fname
+ }
+ }
+}
+
+
+proc set_path {Fname} {
+ #cd to directory in file
+ set fullpath [split $Fname /]
+ set nlen [llength $fullpath]
+ set fullpath [lrange $fullpath 0 [expr $nlen - 2]] ;# get rid of filename
+ set wd [join $fullpath /] ;#put path back together
+ catch {cd $wd}
+}
+
+set defname ""
+
+proc loaddefault_tl {} {
+ global Fname defname
+
+ if {$defname ==""} {
+ set file2 "$Fname.ltl"
+ } else {
+ set file2 $defname
+ }
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+ }
+ if {![file exists $file2]} {
+ .tl.notes.t insert end "Use Load to open a file or a template."
+ return
+ }
+ catch {
+ .tl.notes.title configure -text "Notes \[file $file2]:"
+ }
+ readinfile .tl.never.t $file2
+ catch { extract_defs }
+}
+
+set suffix "ltl"
+
+proc browse_tl {} {
+ global defname suffix
+
+ set suffix "ltl"
+
+ catch {destroy .b}
+ toplevel .b
+ wm iconname .b "Load"
+
+ frame .b.top
+ frame .b.bot
+ scrollbar .b.top.scroll -command ".b.top.list yview"
+ listbox .b.top.list -yscroll ".b.top.scroll set" -relief raised
+
+ button .b.zap -text "Cancel" -command "destroy .b"
+ button .b.all -text "Show All Files" \
+ -command {
+ set suffix ""
+ fillerup ""
+ destroy .b.all
+ }
+ message .b.bot.msg -text "Dir: "
+ entry .b.bot.entry -textvariable dir -relief sunken -width 20
+ pack append .b.top \
+ .b.top.scroll {right filly} \
+ .b.top.list {left expand fill}
+ pack append .b.bot \
+ .b.bot.msg {left} \
+ .b.bot.entry {left}
+ pack append .b \
+ .b.top {top fillx} \
+ .b.all {left} \
+ .b.zap {left} \
+ .b.bot {left}
+
+ bind .b.bot.entry <Return> {
+ set nd [.b.bot.entry get]
+ if {[file isdirectory $nd]} {
+ cd $nd
+ fillerup $suffix
+ add_log "cd $nd"
+ }
+ }
+
+ fillerup $suffix
+ bind .b.top.list <Double-Button-1> {
+ set file2 [selection get]
+ if {[string first "---" $file2] >= 0} {
+ # ignore
+ } elseif {[string first "Invariance" $file2] >= 0} {
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+
+ .tl.main.e1 insert end "\[\] (p)"
+ .tl.notes.t insert end "'p' is invariantly true throughout
+an execution"
+ .tl.notes.title configure \
+ -text "Notes \[template $file2]:"
+ do_ltl
+ destroy .b
+ }
+ } elseif {[string first "Response" $file2] >= 0} {
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+
+ .tl.main.e1 insert end "\[\] ((p) -> (<> (q)))"
+ .tl.notes.t insert end "if 'p' is true in at least one state,
+then sometime thereafter 'q' must also
+become true at least once."
+ .tl.notes.title configure \
+ -text "Notes \[template $file2]:"
+ do_ltl
+ destroy .b
+ }
+ } elseif {[string first "Precedence" $file2] >= 0} {
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+
+ .tl.main.e1 insert end "\[\] ((p) -> ((q) U (r)))"
+ .tl.notes.t insert end "'p' is a trigger, or 'enabling' condition that
+determines when this requirement becomes applicable
+'r' is the fullfillment of the requirement, and
+'q' is a condition that must remain true in the interim."
+ .tl.notes.title configure \
+ -text "Notes \[template $file2]:"
+ do_ltl
+ destroy .b
+ }
+ } elseif {[string first "Objective" $file2] >= 0} {
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+
+ .tl.main.e1 insert end "\[\] ((p) -> <>((q) || (r)))"
+ .tl.notes.t insert end "'p' is a trigger, or 'enabling' condition that
+determines when this requirement becomes applicable
+'q' is the fullfillment of the requirement, and
+'r' is a discharging condition that terminates the
+applicability of the requirement."
+
+ .tl.notes.title configure \
+ -text "Notes \[template $file2]:"
+ do_ltl
+ destroy .b
+ }
+ } elseif {[file isdirectory $file2]} then {
+ cd $file2
+ fillerup $suffix
+ add_log "cd $file2"
+ } else {
+ if {![file isfile $file2]} {
+ set file2 ""
+ } else {
+ catch {
+ .tl.main.e1 delete 0 end
+ .tl.macros.t delete 0.0 end
+ .tl.notes.t delete 0.0 end
+ .tl.never.t delete 0.0 end
+ .tl.results.t delete 0.0 end
+ .tl.notes.title configure \
+ -text "Notes \[file $file2]:"
+ }
+ readinfile .tl.never.t $file2
+ set defname $file2
+ catch { extract_defs }
+ set dir [pwd]
+ destroy .b
+ }
+ }
+ }
+}
+
+proc reopen {} {
+ global Fname
+
+ catch {readinfile .inp.t $Fname} ermsg
+ if {[string length $ermsg]<=1} { return }
+ add_log $ermsg
+ catch { tk_messageBox -icon info -message $ermsg }
+}
+
+proc check_xsp {f} {
+ set ff ${f}.xsp
+ if [catch {set fd [open $ff r]} errmsg] {
+ # add_log "no file $ff"
+ return
+ }
+ add_log "reading $ff file"
+ update
+ while {[gets $fd line] > -1} {
+ if {[string first "X:" $line] == 0} {
+ set C [string range $line 3 end]
+ add_log "X: $C"
+ update
+ catch { eval exec $C } errmsg
+ if {$errmsg != ""} { add_log $errmsg }
+ }
+ if {[string first "L:" $line] == 0} {
+ set N [string range $line 3 end]
+ catch {.log.t configure -height $N -bg black -fg gold}
+ }
+ }
+}
+
+proc writeoutfile {from to} {
+
+ if ![file_ok $to] { return 0 }
+
+ if [catch {set fd [open $to w]} errmsg] {
+ add_log $errmsg
+ catch { tk_messageBox -icon info -message $ermsg }
+ return 0
+ }
+
+ add_log "<saved spec in $to>"
+ puts $fd [string trimright [$from get 0.0 end] "\n"]
+# puts -nonewline $fd [$from get 0.0 end]
+ close $fd
+ return 1
+}
+
+proc readinfile {into from} {
+
+ if [catch {set fd [open $from r]} errmsg] {
+ add_log $errmsg
+ catch { tk_messageBox -icon info -message $ermsg }
+ return 0
+ }
+
+ $into delete 0.0 end
+ while {[gets $fd line] > -1} { $into insert end $line\n }
+ catch { close $fd }
+ add_log "<open $from>"
+
+ global LastGenerate
+ set LastGenerate "" ;# used in Validation.tcl
+ return 1
+}
+
+proc fillerup {suffix} {
+ wm title .b [pwd]
+ .b.top.list delete 0 end
+
+ set dotdot 0
+ set l {}
+ catch { if {$suffix != ""} {
+ set l [lsort [glob *.$suffix]]
+ } else {
+ set l [lsort [glob *]]
+ } }
+ foreach i $l {
+ .b.top.list insert end $i
+ if {$i == ".."} { set dotdot 1 }
+ }
+ if {$dotdot == 0} {
+ .b.top.list insert 0 ".."
+ }
+ if {$suffix != ""} {
+ .b.top.list insert 0 "------files:--------"
+ .b.top.list insert 0 "Objective(p,q,r)"
+ .b.top.list insert 0 "Precedence(p,q,r)"
+ .b.top.list insert 0 "Response(p,q)"
+ .b.top.list insert 0 "Invariance(p)"
+ .b.top.list insert 0 "-----templates:-----"
+ }
+}
+
+proc gotoline {} {
+ global BG FG
+
+ catch { destroy .ln }
+ toplevel .ln
+ wm title .ln "Goto Line"
+ wm iconname .ln "Goto"
+
+ label .ln.lab -text "Enter line number:"
+ entry .ln.ent -width 20 -relief sunken -textvariable lno
+ pack append .ln \
+ .ln.lab {left padx 1m} \
+ .ln.ent {right expand}
+ bind .ln.ent <Return> {
+ .inp.t tag remove hilite 0.0 end
+ .inp.t tag add hilite $lno.0 $lno.1000
+ .inp.t tag configure hilite \
+ -background $FG -foreground $BG
+ .inp.t yview -pickplace [expr $lno-1]
+ }
+ focus .ln.ent
+}
+
+proc savebox {b} {
+ set fname [.c$b.f.e1 get]
+ if {[file_ok $fname]==0} return
+ set fd [open $fname w]
+ add_log "<saved output in $fname>"
+ puts $fd "[.c$b.z.t get 0.0 end]" nonewline
+ catch "flush $fd"
+ catch "close $fd"
+}
+
+proc doplace {a b} {
+ global PlaceBox
+ set PlaceBox($a) [wm geometry .c$b]
+ set k [string first "\+" $PlaceBox($a)]
+ if {$k > 0} {
+ set PlaceBox($a) [string range $PlaceBox($a) $k end]
+ }
+}
+
+proc mkbox {n m p {wd 60} {ht 10} {xp 200} {yp 200}} {
+ global boxnr FG BG PlaceBox HelvBig
+ global ind old_ss old_insert new_insert;# for search capability
+
+ incr boxnr
+
+ toplevel .c$boxnr
+ wm title .c$boxnr $n
+ wm iconname .c$boxnr $m
+
+ if {[info exists PlaceBox($n)] == 0} {
+ set PlaceBox($n) "+$xp+$yp"
+ }
+ wm geometry .c$boxnr $PlaceBox($n)
+
+ #initialize search parameters
+ set ind 1.0
+ set old_ss ""
+ set old_insert ""
+ set new_insert ""
+ frame .c$boxnr.d ;# search bar
+ label .c$boxnr.d.l -text "Search for: "
+ entry .c$boxnr.d.e -width 15
+ bind .c$boxnr.d.e <KeyPress-Return> "search_sim .c$boxnr.z.t .c$boxnr.d.e .c$boxnr"
+ button .c$boxnr.d.b -text "Find" -command "search_sim .c$boxnr.z.t .c$boxnr.d.e .c$boxnr"
+
+ pack .c$boxnr.d -side top -fill x
+ pack .c$boxnr.d.b -side right
+ pack .c$boxnr.d.e -side right
+ pack .c$boxnr.d.l -side right
+
+ frame .c$boxnr.z
+
+ text .c$boxnr.z.t -relief raised -bd 2 -font $HelvBig \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".c$boxnr.z.s set" \
+ -setgrid 1 -width $wd -height $ht -wrap word
+ bind .c$boxnr.z.t <ButtonRelease-1> "+update idletasks; update_insert .c$boxnr.z.t"
+ scrollbar .c$boxnr.z.s \
+ -command ".c$boxnr.z.t yview"
+ pack append .c$boxnr.z \
+ .c$boxnr.z.s {left filly} \
+ .c$boxnr.z.t {left expand fill}
+
+ button .c$boxnr.b -text "Close" \
+ -command "doplace {$n} {$boxnr}; destroy .c$boxnr"
+ button .c$boxnr.c -text "Clear" \
+ -command ".c$boxnr.z.t delete 0.0 end"
+ pack append .c$boxnr \
+ .c$boxnr.z {top expand fill} \
+ .c$boxnr.b {right padx 5} \
+ .c$boxnr.c {right padx 5}
+
+ if {[string length $p]>0} {
+ frame .c$boxnr.f -relief sunken
+ button .c$boxnr.f.e0 -text "Save in: " \
+ -command "savebox $boxnr"
+ entry .c$boxnr.f.e1 -relief flat -width 10
+ .c$boxnr.f.e1 insert end $p
+ pack append .c$boxnr.f \
+ .c$boxnr.f.e0 {left padx 5} \
+ .c$boxnr.f.e1 {left}
+ pack append .c$boxnr \
+ .c$boxnr.f {right padx 5}
+ }
+
+ tkwait visibility .c$boxnr
+ raise .c$boxnr
+ return $boxnr
+}
+proc update_insert {t} {
+ global new_insert
+ update idletasks
+ set new_insert [$t index insert]
+}
+
+proc search_sim {t e b} {
+ global ind old_ss old_insert new_insert
+
+ set ss [$e get]
+
+ if {$ss == ""} {
+ return
+ }
+
+ #if user has selected use that lin.char as 'ind'. otherwise use end of last word found
+ #set new_insert [$t index insert]
+ if {$new_insert != $old_insert} {
+ set ind $new_insert ;# this is where we will start searching
+ set old_insert $new_insert ;# update select location
+ }
+ set cur_ind $ind
+ set ind [$t search $ss $ind]
+
+ set old_ss $ss
+ if {$ind != ""} {
+ $t yview -pickplace $ind
+ $t tag configure hilite -foreground black -background white
+ $t tag delete hilite
+ set split_ind [split $ind "."]
+ set char [lindex $split_ind 1]
+ set char [incr char [string length $ss]]
+ set indstart $ind
+ set indend ""
+ append indend [lindex $split_ind 0] "." $char
+ $t tag add hilite $indstart $indend
+ $t tag configure hilite -foreground white -background black
+ set ind $indend
+ } else {
+ # set ind 1.0
+ catch { tk_messageBox -icon info -message "no match for $ss" }
+ set ind $cur_ind ;# restore ind to last good value
+ raise $b
+ }
+
+}
+
+# Tcl/Tk book, page 219
+proc forAllMatches {w pattern} {
+ global BG FG lno
+
+ scan [$w index end] %d numLines
+ for {set i 1} {$i < $numLines} { incr i} {
+ $w mark set last $i.0
+ if {[regexp -indices $pattern \
+ [$w get last "last lineend"] indices]} {
+ $w mark set first \
+ "last + [lindex $indices 0] chars"
+ $w mark set last "last + 1 chars \
+ + [lindex $indices 1] chars"
+ .inp.t tag add hilite $i.0 $i.end
+ .inp.t tag configure hilite \
+ -background $FG -foreground $BG
+# .inp.t yview -pickplace [expr $i-1]
+ } }
+
+# move to the next line that matches
+
+ for {set i [expr $lno+1]} {$i < $numLines} { incr i} {
+ $w mark set last $i.0
+ if {[regexp -indices $pattern \
+ [$w get last "last lineend"] indices]} {
+ $w mark set first \
+ "last + [lindex $indices 0] chars"
+ $w mark set last "last + 1 chars \
+ + [lindex $indices 1] chars"
+ .inp.t yview -pickplace [expr $i-5]
+ set lno $i
+ return
+ } }
+ for {set i 1} {$i <= $lno} { incr i} {
+ $w mark set last $i.0
+ if {[regexp -indices $pattern \
+ [$w get last "last lineend"] indices]} {
+ $w mark set first \
+ "last + [lindex $indices 0] chars"
+ $w mark set last "last + 1 chars \
+ + [lindex $indices 1] chars"
+ .inp.t yview -pickplace [expr $i-5]
+ set lno $i
+ return
+ } }
+ catch {
+ tk_messageBox -icon info -message "No Match of \"$pattern\""
+ }
+}
+
+set noprep 0
+
+proc hasWord {pattern} {
+ global SPIN noprep
+ set err ""
+ if {![file exists pan.pre] && $noprep == 0} {
+ add_log "$SPIN -Z pan_in ;# preprocess input $pattern"
+ catch {exec $SPIN -Z pan_in >&pan.tmp} err
+ # leaves output in pan.pre
+ add_log "<done preprocess>"
+ }
+
+ if {[string length $err] == 0 && $noprep == 0} {
+ set fd [open pan.pre r]
+ while {[gets $fd line] > -1} {
+ if {[regexp -indices $pattern $line indices]} {
+ catch "close $fd"
+ return 1
+ } }
+ catch "close $fd"
+ return 0
+ }
+
+ # else, there were errors, just ignore the include files:
+ set noprep 1
+ add_log "$err"
+ scan [.inp.t index end] %d numLines
+ for {set i 1} {$i < $numLines} { incr i} {
+ .inp.t mark set last $i.0
+ if {[regexp -indices $pattern \
+ [.inp.t get last "last lineend"] indices]} {
+ return 1
+ }
+ }
+ return 0
+}
+
+# FSM view option
+
+set nodeX(0) 0
+set nodeY(0) 0
+set Label(0) 0
+set Transit(0) {}
+set TLabel(0) 0
+set Lab2Node(0) 0
+set Dist(0) 0
+set State(0) 0
+set Edges(0) {}
+set New 0
+set MaxDist 0
+set Maxx 0
+set Maxy 0
+set Minx 50
+set Miny 50
+set reached_end 0
+set Scale 1
+
+set cnr 0
+set x 0
+set y 0
+
+proc fsmview {} {
+ global Unix
+
+ add_log "<fsm view option>"
+
+ if {[mk_exec] != 1} { return }
+
+ catch {destroy .z}
+ toplevel .z
+
+ wm title .z "Double-Click Proc"
+
+ listbox .z.list -setgrid 1
+ button .z.b -text "Cancel" -command "destroy .z"
+
+ pack append .z \
+ .z.list {top expand fill} \
+ .z.b {right padx 5}
+
+ if {$Unix} {
+ add_log "./pan -d # find proc names"; update
+ set fd [open "|./pan -d" w+]
+ } else {
+ add_log "pan -d # find proc names"; update
+ catch { eval exec pan -d >&pan.tmp } err
+ if {$err != ""} {
+ add_log "error: $err"
+ }
+ set fd [open pan.tmp r]
+ }
+ while {[gets $fd line] > -1} {
+ if {[string first "proctype" $line] >= 0 } {
+ .z.list insert end \
+ [string trim [string range $line 9 end]]
+ } }
+ catch { close $fd }
+
+ bind .z.list <Double-Button-1> {
+ set pfind [selection get]
+ catch { destroy .z }
+ findfsm $pfind
+ }
+ focus .z.list
+}
+
+proc findfsm {pfind} {
+ global Unix New Dist State Edges Label
+ global Transit MaxDist reached_end TLabel DOT
+
+ if {[mk_exec] != 1} { return }
+
+ set src 0; set trn 0; set trg 0
+ set Want 0; set MaxDist 0; set startstate ""
+
+ catch { foreach el [array names State] { unset State($el) } }
+ catch { foreach el [array names Edges] { unset Edges($el) } }
+ catch { foreach el [array names Dist] { unset Dist($el) } }
+
+ if {$Unix} {
+ add_log "./pan -d # compute fsm"; update
+ set fd [open "|./pan -d" w+]
+ } else {
+ add_log "pan -d # compute fsm"; update
+ catch { eval exec pan -d >&pan.tmp }
+ set fd [open pan.tmp r]
+ }
+ while {[gets $fd line] > -1} {
+ set k [string first "proctype" $line]
+ if { $k >= 0 } {
+ if { $Want == 1 } {
+ break
+ }
+ incr k 9
+ set pname [string range $line $k end]
+
+ if { [string first $pfind $line] >= 0 \
+ && [string length $pfind] == [string length $pname]} {
+ set Want 1
+ set reached_end 0
+ set nsrc "$pname:0"
+ set Dist($nsrc) 0
+ set Label($nsrc) "end"
+ set Edges($nsrc) {}
+ }
+ } elseif { $Want == 1 \
+ && [string first "statement" $line] < 0
+ && [string first "state" $line] >= 0} {
+ scan $line " state %d -(tr %d)-> state %d" \
+ src trn trg
+ if {$trg == 0} { set reached_end 1 }
+
+ set nsrc "$pname:$src"
+ set ntrg "$pname:$trg"
+
+ if {$startstate == ""} { set startstate $nsrc }
+
+ set k [string first "line" $line]
+ if { $k > 0 } {
+ set m [string first "=>" $line]
+ incr m -1
+ set lbl [string range $line $k $m]
+ incr m 3
+ set action [string range $line $m end]
+ } else {
+ set lbl "line 0"
+ set action "line 0"
+ }
+ set Label($nsrc) $lbl
+ if { [info exists Dist($nsrc)] == 0 } {
+ set Dist($nsrc) 0
+ set Edges($nsrc) {}
+ }
+ if { [info exists Dist($ntrg)] == 0 } {
+ set Dist($ntrg) [expr $Dist($nsrc) + 1]
+ set Edges($ntrg) {}
+ if {$Dist($ntrg) > $MaxDist } {
+ set MaxDist $Dist($ntrg)
+ }
+ } else {
+ if { [expr $Dist($nsrc) + 1] < $Dist($ntrg) } {
+ set Dist($ntrg) [expr $Dist($nsrc) + 1]
+ if {$Dist($ntrg) > $MaxDist } {
+ set MaxDist $Dist($ntrg)
+ }
+ } }
+if {0 \
+&& [auto_execok $DOT] != 0 \
+&& [auto_execok $DOT] != ""} {
+ set z1 [string first "\[" $action]
+ set z2 [string last "\]" $action]
+ if {$z1 > 0 && $z2 > $z1} {
+ incr z1 -1; incr z2
+ set a1 [string range $action 0 $z1]
+ set a2 [string range $action $z2 end]
+ set action "$a1$a2"
+ }
+ set kkk "$nsrc;$trg:$action"
+ lappend Edges($nsrc) "$kkk"
+ lappend Edges($kkk) $ntrg
+ lappend Transit($nsrc) "$lbl"
+ lappend Transit($kkk) ""
+ set Dist($kkk) $Dist($ntrg)
+ set Label($kkk) "T3"
+} else {
+ lappend Edges($nsrc) $ntrg
+ lappend Transit($nsrc) $action
+}
+ }
+ }
+ if { $Want == 1 } {
+ dograph $pfind $startstate
+ } else {
+ add_log "sorry, $pfind not found..."
+ catch {
+ tk_messageBox -icon info -message "$pfind not found..."
+ }
+ }
+ catch "close $fd"
+ add_log "<done>"
+ update
+}
+
+proc plunknode {el prefix} {
+ global State Label TLabel
+ global x y
+
+ set pn [string range $el $prefix end]
+ set State($el) [mkNode "$Label($el)" $pn $x $y]
+
+ if { $x > 250 } {
+ set x [expr $x - 250]
+ set x [expr 250 - $x]
+ } else {
+ set x [expr 250 - $x]
+ incr x 75
+ set x [expr 250 + $x]
+ }
+}
+
+proc dograph {n s} {
+ global Dist Edges Label Transit MaxDist State ELabel
+ global cnr lcnr reached_end x y Unix DOT
+ set count -1
+
+ set lcnr [mkcanvas "FSM $n" $n 300 300 0]
+ set prefix [string length $n]
+ incr prefix
+ set y 0
+
+ while {$count < $MaxDist} {
+ incr count
+ incr y 50
+ set x 250
+ foreach el [array names Dist] {
+ if { [ string first $n $el ] >= 0 \
+ && $Dist($el) == $count \
+ && $el != "$n:0" } {
+ plunknode $el $prefix
+ } }
+ }
+ if {$reached_end == 1} {
+ # force end state at the bottom
+ incr y 50
+ set x 250
+ plunknode "$n:0" $prefix
+ }
+
+ foreach el [array names Edges] {
+ if { [ string first $n $el ] >= 0 } {
+ for {set i 0} { [lindex $Edges($el) $i] != "" } {incr i} {
+ set ntrg [lindex $Edges($el) $i]
+ set lbl [lindex $Transit($el) $i]
+ mkEdge $lbl $State($el) $State($ntrg)
+ set ELabel($el,$ntrg) "$lbl"
+ }
+ } }
+ addscales $lcnr
+
+ .f$cnr.c itemconfigure $State($s) -outline red; update
+
+ if {[auto_execok $DOT] != 0 \
+ && [auto_execok $DOT] != ""} {
+ dodot $n
+# button .f$lcnr.b66 -text "Redraw with Dot" -command "dodot $n"
+# pack append .f$lcnr .f$lcnr.b66 {right padx 5}
+ }
+ update
+}
+
+proc mkNode {n t x y} {
+ # tcl book p. 213
+ global cnr NodeWidth NodeHeight HelvBig
+ global nodeX nodeY New TLabel Lab2Node
+
+ if {[string first ";" $t] > 0} {
+ set New [.f$cnr.c create rectangle [expr $x-1] [expr $y-1] \
+ [expr $x+1] [expr $y+1] \
+ -outline white \
+ -fill white \
+ -tags node]
+ set z [string first ":" $t]; incr z
+ set t [string range $t $z end]
+ set Lab [.f$cnr.c create text $x $y -font $HelvBig -text $n -tags node]
+ } else {
+ set New [.f$cnr.c create oval [expr $x-10] [expr $y-10] \
+ [expr $x+10] [expr $y+10] \
+ -outline black \
+ -fill white \
+ -tags node]
+ set Lab [.f$cnr.c create text $x $y -font $HelvBig -text $n -tags node]
+
+ .f$cnr.c bind $Lab <Any-Enter> "
+ .f$cnr.c itemconfigure {$Lab} -fill black -text {$t}
+ if {[string first \"end\" $n] < 0 } { set_src {$t} }
+ "
+ .f$cnr.c bind $Lab <Any-Leave> "
+ .f$cnr.c itemconfigure {$Lab} -fill black -text {$n}
+ "
+ }
+ set nodeX($New) $x
+ set nodeY($New) $y
+ set TLabel($New) $Lab
+ set Lab2Node($Lab) $New
+ set NodeWidth($New) 10
+ set NodeHeight($New) 10
+
+ update
+ return $New
+}
+
+proc set_src {n} {
+ set where 0
+ scan $n "line %d" where
+ .inp.t tag remove hilite 0.0 end
+ src_line $where
+}
+
+proc mkEdge {L a b} {
+ global cnr Xrem Yrem TransLabel HelvBig
+ global nodeX nodeY edgeHead edgeTail
+
+ if { $nodeY($b) > $nodeY($a) } {
+ set ydiff -11
+ } elseif { $nodeY($b) < $nodeY($a) } {
+ set ydiff 11
+ } else {
+ set ydiff 0
+ }
+ if { $nodeX($b) > $nodeX($a) } {
+ set xdiff -6
+ } elseif { $nodeX($b) < $nodeX($a) } {
+ set xdiff 6
+ } else {
+ set xdiff 0
+ }
+ set edge [.f$cnr.c create line \
+ $nodeX($a) $nodeY($a) \
+ [expr $nodeX($b) + $xdiff] \
+ [expr $nodeY($b) + $ydiff] \
+ -arrow both -arrowshape {4 3 3} ]
+ .f$cnr.c lower $edge
+ lappend edgeHead($a) $edge
+ lappend edgeTail($b) $edge
+
+ set Xrem($edge) $xdiff
+ set Yrem($edge) $ydiff
+
+ set midX [expr $nodeX($a) + $nodeX($b)]
+ set midX [expr $midX / 2 ]
+ set midY [expr $nodeY($a) + $nodeY($b)]
+ set midY [expr $midY / 2 ]
+
+ set TransLabel($a,$b) \
+ [.f$cnr.c create text $midX $midY -font $HelvBig -tags texttag]
+
+ .f$cnr.c bind $edge <Button-1> "
+ .f$cnr.c coords $TransLabel($a,$b) \[.f$cnr.c canvasx %x\] \[.f$cnr.c canvasy %y\]
+ .f$cnr.c itemconfigure $TransLabel($a,$b) \
+ -fill black -text {$L} -font $HelvBig
+ "
+ .f$cnr.c bind $edge <ButtonRelease-1> "
+ .f$cnr.c itemconfigure $TransLabel($a,$b) \
+ -fill black -text {}
+ "
+}
+
+proc moveNode {cnr node mx my together} {
+ global edgeHead edgeTail TLabel NodeWidth NodeHeight
+ global nodeX nodeY Lab2Node
+ global Xrem Yrem Scale
+
+ set x [.f$cnr.c coords $node]
+ if {[llength $x] == 2} { set node $Lab2Node($node) }
+
+ set mx [.f$cnr.c canvasx $mx]
+ set my [.f$cnr.c canvasy $my]
+
+ set wx $NodeWidth($node)
+ set wy $NodeHeight($node)
+
+ .f$cnr.c coords $node \
+ [expr $mx-$wx] [expr $my-$wy] \
+ [expr $mx+$wx] [expr $my+$wy]
+ .f$cnr.c coords $TLabel($node) $mx $my
+
+ set nodeX($node) $mx
+ set nodeY($node) $my
+ if {$together == 0} { return }
+ catch {
+ foreach edge $edgeHead($node) {
+ set ec [.f$cnr.c coords $edge]
+ set nx [expr $nodeX($node) + $Xrem($edge)*$Scale]
+ set ny [expr $nodeY($node) + $Yrem($edge)*$Scale]
+ .f$cnr.c coords $edge \
+ $nx $ny \
+ [lindex $ec 2] [lindex $ec 3]
+ }}
+ catch {
+ foreach edge $edgeTail($node) {
+ set ec [.f$cnr.c coords $edge]
+ set nx [expr $nodeX($node) + $Xrem($edge)*$Scale]
+ set ny [expr $nodeY($node) + $Yrem($edge)*$Scale]
+ .f$cnr.c coords $edge \
+ [lindex $ec 0] [lindex $ec 1] \
+ $nx $ny
+ }}
+}
+
+set PlaceCanvas(msc) ""
+
+proc mkcanvas {n m geox geoy placed} {
+ global cnr tk_version
+ global Chandle Sticky
+ global FG BG Ptype PlaceCanvas
+
+ incr cnr
+ toplevel .f$cnr
+ wm title .f$cnr "$n"
+ wm iconname .f$cnr $m
+ if {$placed} {
+ if {$PlaceCanvas($m) == ""} {
+ set PlaceCanvas($m) "+$geox+$geoy"
+ }
+ set k [string first "\+" $PlaceCanvas($m)]
+ if {$k > 0} {
+ set PlaceCanvas($m) [string range $PlaceCanvas($m) $k end]
+ }
+ wm geometry .f$cnr $PlaceCanvas($m)
+ }
+ wm minsize .f$cnr 100 100
+
+ if {[string first "4." $tk_version] == 0 \
+ || [string first "8." $tk_version] == 0} {
+ set cv [canvas .f$cnr.c -relief raised -bd 2\
+ -scrollregion {-15c -5c 30c 40c} \
+ -background $BG \
+ -xscrollcommand ".f$cnr.hscroll set" \
+ -yscrollcommand ".f$cnr.vscroll set"]
+ scrollbar .f$cnr.vscroll -relief sunken \
+ -command ".f$cnr.c yview"
+ scrollbar .f$cnr.hscroll -relief sunken -orient horiz \
+ -command ".f$cnr.c xview"
+ } else {
+ set cv [canvas .f$cnr.c -relief raised -bd 2\
+ -scrollregion {-15c -5c 30c 40c} \
+ -background $BG \
+ -xscroll ".f$cnr.hscroll set" \
+ -yscroll ".f$cnr.vscroll set"]
+ scrollbar .f$cnr.vscroll -relief sunken \
+ -command ".f$cnr.c yview"
+ scrollbar .f$cnr.hscroll -relief sunken -orient horiz \
+ -command ".f$cnr.c xview"
+ }
+ set Chandle($cnr) $cv
+ #-width 500 -height 500
+
+ button .f$cnr.b1 -text "Close" \
+ -command "set PlaceCanvas($m) [wm geometry .f$cnr]; destroy .f$cnr"
+ button .f$cnr.b2 -text "Save in: $m.ps" \
+ -command "$cv postscript -file $m.ps -colormode $Ptype"
+
+ pack append .f$cnr \
+ .f$cnr.vscroll {right filly} \
+ .f$cnr.hscroll {bottom fillx} \
+ .f$cnr.c {top expand fill}
+
+ if {$m == "msc"} {
+ set Sticky($cnr) 0
+ checkbutton .f$cnr.b6 -text "Preserve" \
+ -variable Sticky($cnr) \
+ -relief flat
+ pack append .f$cnr \
+ .f$cnr.b6 { right padx 5}
+ }
+ pack append .f$cnr \
+ .f$cnr.b1 {right padx 5} \
+ .f$cnr.b2 {right padx 5}
+
+ bind $cv <2> "$cv scan mark %x %y" ;# Geert Janssen, TUE
+ bind $cv <B2-Motion> "$cv scan dragto %x %y"
+
+ .f$cnr.c bind node <B1-Motion> "
+ moveNode $cnr \[.f$cnr.c find withtag current] %x %y 1
+ "
+
+# .f$cnr.c bind node <B2-Motion> "
+# moveNode $cnr \[.f$cnr.c find withtag current] %x %y 1
+# "
+
+ tkwait visibility .f$cnr
+ return $cnr
+}
+
+proc addscales {p} {
+ global Chandle Scale
+
+ catch {
+ set cv $Chandle($p)
+ button .f$p.b4 -text "Larger" \
+ -command "$cv scale all 0 0 1.1 1.1; set Scale [expr $Scale*1.1]"
+ button .f$p.b5 -text "Smaller" \
+ -command "$cv scale all 0 0 0.9 0.9; set Scale [expr $Scale*0.9]"
+ pack append .f$p \
+ .f$p.b4 {right padx 5} \
+ .f$p.b5 {right padx 5}
+ }
+}
+
+proc dodot {n} {
+ global Edges edgeHead edgeTail NodeWidth NodeHeight Maxx Maxy
+ global State ELabel TransLabel Unix cnr lcnr Xrem Yrem DOT
+
+ add_log "<dot graph layout...>"
+ set fd [open "|$DOT" w+]
+
+ puts $fd "digraph dodot {"
+
+ foreach el [array names Edges] {
+ if { [ string first $n $el ] >= 0 } {
+ for {set i 0} { [lindex $Edges($el) $i] != "" } {incr i} {
+ set ntrg [lindex $Edges($el) $i]
+ puts $fd " \"$el\" -> \"$ntrg\"; "
+ }
+ }}
+
+ puts $fd "}"
+ flush $fd
+ set ends ""
+ set nodot 1
+ while {[gets $fd line] > -1} {
+ if {[string first "\}" $line] >= 0} {
+ break;
+ }
+ set dd [string length $line]; incr dd -1
+ while {[string range $line $dd $dd] == "\\"} {
+ gets $fd more
+ set line "[string range $line 0 [expr $dd-1]]$more"
+ set dd [string length $line]; incr dd -1
+ }
+ if {[string first " -> " $line] >= 0} {
+ set a [string first "\[pos=\"" $line]; incr a 8
+ set b [string first "\"\];" $line]; incr b -1
+ set b2 [string first "->" $line]
+ set line1 [string range $line 0 [expr $a - 9]]
+ set src [string range $line1 0 [expr $b2 - 1]]
+ set src [string trim $src " \""]
+ set trg [string range $line1 [expr $b2 + 3] [expr $a - 1]]
+ set trg [string trim $trg " \""]
+ set tp [string range $line [expr $a-2] [expr $a-2]]
+ set line [string range $line $a $b]
+ set k [split $line " "]
+ set j [llength $k]
+ set j2 [trunc [expr $j/2]]
+ set coords ".f$cnr.c create line "
+ set spline "-smooth 1"
+ set nl $ELabel($src,$trg)
+ if {$tp == "e"} {
+ set ends "last"
+ for {set i 1} {$i < $j} {incr i} {
+ scan [lindex $k $i] "%d,%d" x y
+ set coords " $coords[expr 50 + $x] [expr 50 + $Maxy - $y] "
+ if {$i == $j2} {
+ .f$cnr.c coords \
+ $TransLabel($State($src),$State($trg)) \
+ [expr 50 + $x] [expr 50 + $Maxy - $y]
+ .f$cnr.c itemconfigure \
+ $TransLabel($State($src),$State($trg)) \
+ -fill black -text "$nl"
+ }
+ }
+ scan [lindex $k 0] "%d,%d" x y
+ set coords " $coords[expr 50 + $x] [expr 50 + $Maxy - $y] "
+ } else {
+ set ends "first"
+ for {set i 0} {$i < $j} {incr i} {
+ scan [lindex $k $i] "%d,%d" x y
+ set coords " $coords[expr 50 + $x] [expr 50 + $Maxy - $y] "
+ if {$i == $j2} {
+ .f$cnr.c coords \
+ $TransLabel($State($src),$State($trg)) \
+ [expr 50 + $x] [expr 50 + $Maxy - $y]
+ .f$cnr.c itemconfigure \
+ $TransLabel($State($src),$State($trg)) \
+ -fill black -text "$nl"
+ } } }
+ set coords "$coords -arrow $ends $spline -tags connector"
+
+ set ne [eval $coords]
+ set Xrem($ne) 10
+ set Yrem($ne) 10
+
+ continue
+ }
+ if {[string first "node " $line] >= 0 \
+ || [string first "\{" $line] >= 0} {
+ continue
+ }
+ if {[string first "graph " $line] >= 0} {
+ set a [string first "\"" $line]; incr a
+ set b [string last "\"" $line]; incr b -1
+ set line [string range $line $a $b]
+ set k [split $line ","]
+ if {[llength $k] == 4} {
+ set Maxx [lindex $k 2]
+ set Maxy [lindex $k 3]
+ } else {
+ set Maxx [lindex $k 0]
+ set Maxy [lindex $k 1]
+ }
+ set nodot 0
+ } else { ;# a node
+ set a [string first "\[" $line]; incr a
+ set b [string last "\]" $line]; incr b -1
+ set line1 [string range $line 0 [expr $a - 2]]
+ set line [string range $line $a $b]
+ set nn [string trim $line1 " \""]
+
+ set a [string first "pos=" $line]; incr a 5
+ set b [string first "\"" [string range $line $a end]]
+ set b [expr $a + $b - 1]
+ set line1 [string range $line $a $b]
+ set k [split $line1 ","]
+ set x [lindex $k 0]
+ set y [lindex $k 1]
+
+ set a [string first "width=" $line]; incr a 7
+ set b [string first "\"" [string range $line $a end]]
+ set b [expr $a + $b - 1]
+ set w [expr 75 * [string range $line $a $b]]
+
+ set a [string first "height=" $line]; incr a 8
+ set b [string first "\"" [string range $line $a end]]
+ set b [expr $a + $b - 1]
+ set h [expr 75 * [string range $line $a $b]]
+
+ catch {
+ set NodeWidth($State($nn)) [expr $w/2]
+ set NodeHeight($State($nn)) [expr $h/2]
+ moveNode $lcnr $State($nn) \
+ [expr 50 + $x] [expr 50 + $Maxy - $y] 0
+ } err
+#puts $err
+ }
+ }
+ catch { close $fd }
+
+ if {$nodot} {
+ add_log "<cannot find dot>"
+ catch {
+ tk_messageBox -icon info -message "<cannot find dot>"
+ }
+ return
+ }
+
+ foreach el [array names Edges] {
+ if { [ string first $n $el ] >= 0 } {
+ catch {
+ foreach edge $edgeHead($State($el)) {
+ .f$cnr.c delete $edge
+ }
+ unset edgeHead($State($el))
+ unset edgeTail($State($el))
+ }
+ } }
+ .f$cnr.c bind node <B1-Motion> {} ;# no moving
+ .f$cnr.c bind node <B2-Motion> {}
+ catch { destroy .f$lcnr.b6 }
+# button .f$lcnr.b6 -text "No Labels" \
+# -command ".f$lcnr.c delete texttag; destroy .f$lcnr.b6"
+ button .f$lcnr.b6 -text "No Labels" \
+ -command "hide_automata_labels .f$lcnr.b6 .f$cnr.c"
+ pack append .f$lcnr .f$lcnr.b6 {right padx 5}
+}
+
+proc hide_automata_labels {b c} {
+ $b configure -text "Add Labels"
+ $c itemconfigure texttag -fill white
+ $b configure -command "show_automata_labels $b $c"
+}
+
+proc show_automata_labels {b c} {
+ $b configure -text "No Labels"
+ $c itemconfigure texttag -fill black
+ $b configure -command "hide_automata_labels $b $c"
+}
+
+proc trunc {p} {
+ set foo [string first "." $p]
+ if {$foo >= 0} {
+ incr foo -1
+ set p [string range $p 0 $foo]
+ }
+ return $p
+}
+
+# Help menus
+
+proc aboutspin {} {
+ global FG BG HelvBig version xversion
+
+ catch {destroy .h}
+ toplevel .h
+
+ wm title .h "About SPIN"
+ wm iconname .h "About"
+ message .h.t -width 600 -background $BG -foreground $FG -font $HelvBig \
+ -text " $version
+Xspin Version $xversion
+
+Spin is an on-the-fly LTL model checking system
+for proving properties of asynchronous software
+system designs, first distributed in 1991.
+
+The master sources for the latest version of
+this software can always be found via:
+
+http://spinroot.com/spin/whatispin.html
+
+For help: spin_list@spinroot.com
+
+The Spin sources are (c) 1990-2004 Bell Labs,
+Lucent Technologies, Murray Hill, NJ, USA.
+All rights are reserved. This software is for
+educational and research purposes only.
+No guarantee whatsoever is expressed or implied
+by the distribution of this code.
+"
+ button .h.b -text "Ok" -command {destroy .h}
+ pack append .h .h.t {top expand fill}
+ pack append .h .h.b {top}
+}
+
+proc promela {} {
+ global FG BG HelvBig
+
+ catch {destroy .h}
+ toplevel .h
+
+ wm title .h "Promela URL"
+ wm iconname .h "Promela"
+ message .h.t -width 600 -background $BG -foreground $FG -font $HelvBig \
+ -text "All Promela references are available online:
+
+http://spinroot.com/spin/Man/index.html
+
+"
+ button .h.b -text "Ok" -command {destroy .h}
+ pack append .h .h.t {top expand fill}
+ pack append .h .h.b {top}
+}
+
+proc helper {} {
+ global FG BG HelvBig
+
+ catch {destroy .h}
+ toplevel .h
+
+ wm title .h "Help with Xspin"
+ wm iconname .h "Help"
+ message .h.t -background $BG -foreground $FG -font $HelvBig \
+ -text "\
+Spin Version Controller - (c) 1993-2004 Bell Laboratories
+
+Enter a Promela model into the main text window, or 'Open'
+one via the File Menu (e.g., from Spin's Test directory).
+Once loaded, you can revert to the stored version of the file
+with option ReOpen. Select Clear to empty the text window.
+
+In the log, just below the text-window, background
+commands are printed that Xspin generates.
+Outputs from Simulation and Verification runs always
+appear in separate windows.
+
+All run-time options are available through the Run menu.
+A typical way of working with Xspin is to use:
+
+- First a Syntax Check to get hints and warnings
+- Random Simulation for further debugging
+- Add the properties to be verified (assertions, never claims)
+- Perform a Slicing Check to check for redundancy
+- Perform Verification for a correctness proof
+- Guided Simulation to inspect errors reported by
+ the Verification option
+
+Clicking Button-1 in the main window updates the
+Line number display at the top of the window -- as a
+simple way of finding out at what line you are.
+
+You can also use another editor to update the
+specifications outside Xspin, and use the ReOpen
+command from the File menu to refresh the Xspin
+edit buffer before starting each new simulation or
+verification run."
+ button .h.b -text "Ok" -command {destroy .h}
+ pack append .h .h.t {top expand fill}
+ pack append .h .h.b {top}
+}
+
+# LTL interface
+
+set formula ""
+set tl_stat 0
+
+proc put_template {s} {
+ .tl.main.e1 delete 0 end
+ .tl.main.e1 insert end "$s"
+}
+
+set PlaceTL "+100+1"
+
+proc call_tl {} { ;# expanded interface
+ global formula tl_stat nv_typ an_typ cp_typ
+ global FG BG Fname firstime PlaceTL
+
+ catch {destroy .tl}
+ toplevel .tl
+
+ set k [string first "\+" $PlaceTL]
+ if {$k > 0} {
+ set PlaceTL [string range $PlaceTL $k end]
+ }
+
+ wm title .tl "Linear Time Temporal Logic Formulae"
+ wm iconname .tl "LTL"
+ wm geometry .tl $PlaceTL
+
+ frame .tl.main
+ entry .tl.main.e1 -relief sunken \
+ -background $BG -foreground $FG
+ label .tl.main.e2 -text "Formula: "
+
+ frame .tl.op
+ set alw {\[\] }
+ set eve {\<\> }
+ pack append .tl.op [label .tl.op.s0 -text "Operators: " \
+ -relief flat] {left}
+ pack append .tl.op [button .tl.op.always -width 1 -text "\[\]" \
+ -command ".tl.main.e1 insert insert \"$alw \""] {left}
+ pack append .tl.op [button .tl.op.event -width 1 -text "\<\>" \
+ -command ".tl.main.e1 insert insert \"$eve \""] {left}
+ pack append .tl.op [button .tl.op.until -width 1 -text "U" \
+ -command ".tl.main.e1 insert insert \" U \""] {left}
+ pack append .tl.op [button .tl.op.impl -width 1 -text "->" \
+ -command ".tl.main.e1 insert insert \" -> \""] {left}
+ pack append .tl.op [button .tl.op.and -width 1 -text "and" \
+ -command ".tl.main.e1 insert insert \" && \""] {left}
+ pack append .tl.op [button .tl.op.or -width 1 -text "or" \
+ -command ".tl.main.e1 insert insert \" || \""] {left}
+ pack append .tl.op [button .tl.op.not -width 1 -text "not" \
+ -command ".tl.main.e1 insert insert \" ! \""] {left}
+
+ frame .tl.b -relief ridge -borderwidth 4
+ label .tl.b.s0 -text "Property holds for: "
+ radiobutton .tl.b.s1 -text "All Executions (desired behavior)" \
+ -variable tl_stat -value 0
+ radiobutton .tl.b.s2 -text "No Executions (error behavior)" \
+ -variable tl_stat -value 1
+ pack append .tl.b \
+ .tl.b.s0 {left} \
+ .tl.b.s1 {left} \
+ .tl.b.s2 {left}
+
+ .tl.main.e1 insert end $formula
+
+ button .tl.main.file -text "Load..." \
+ -command "browse_tl"
+
+ bind .tl.main.e1 <Return> { do_ltl }
+
+ pack append .tl.main \
+ .tl.main.e2 {top left}\
+ .tl.main.e1 {top left expand fill} \
+ .tl.main.file {top right}
+
+ pack append .tl .tl.main {top fillx frame e}
+ pack append .tl .tl.op {top frame w}
+ pack append .tl .tl.b {top fillx frame w}
+
+ frame .tl.macros -relief ridge -borderwidth 4
+ label .tl.macros.title -text "Symbol Definitions:" -relief flat
+ scrollbar .tl.macros.s -relief flat \
+ -command ".tl.macros.t yview"
+ text .tl.macros.t -height 4 -relief raised -bd 2 \
+ -yscrollcommand ".tl.macros.s set" \
+ -background $BG -foreground $FG \
+ -setgrid 1 \
+ -wrap word
+
+ pack append .tl.macros \
+ .tl.macros.title {top frame w} \
+ .tl.macros.s {left filly} \
+ .tl.macros.t {left expand fill}
+
+ frame .tl.notes -relief ridge -borderwidth 4
+ label .tl.notes.title -text "Notes: " -relief flat
+ scrollbar .tl.notes.s -relief flat \
+ -command ".tl.notes.t yview"
+ text .tl.notes.t -height 4 -relief raised -bd 2 \
+ -yscrollcommand ".tl.notes.s set" \
+ -background $BG -foreground $FG \
+ -setgrid 1 \
+ -wrap word
+ pack append .tl.notes \
+ .tl.notes.title {top fillx frame w} \
+ .tl.notes.s {left filly} \
+ .tl.notes.t {left expand fill}
+
+ frame .tl.never
+ frame .tl.never.top
+ label .tl.never.top.title -text "Never Claim:"\
+ -relief flat
+ button .tl.never.top.gc -text "Generate" \
+ -command "do_ltl"
+ pack append .tl.never.top \
+ .tl.never.top.gc {right}\
+ .tl.never.top.title {left}
+
+ scrollbar .tl.never.s -relief flat \
+ -command ".tl.never.t yview"
+ text .tl.never.t -height 8 -relief raised -bd 2 \
+ -yscrollcommand ".tl.never.s set" \
+ -setgrid 1 \
+ -wrap word
+ pack append .tl.never \
+ .tl.never.top {top fillx frame w} \
+ .tl.never.s {left filly} \
+ .tl.never.t {left expand fill}
+
+ frame .tl.results
+ frame .tl.results.top
+
+ button .tl.results.top.svp -text "Run Verification" \
+ -command "do_ltl; basicval2"
+ label .tl.results.top.title -text "Verification Result:"\
+ -relief flat
+ pack append .tl.results.top \
+ .tl.results.top.svp {right}\
+ .tl.results.top.title {left}
+
+ scrollbar .tl.results.s -relief flat \
+ -command ".tl.results.t yview"
+ text .tl.results.t -height 7 -relief raised -bd 2 \
+ -yscrollcommand ".tl.results.s set" \
+ -setgrid 1 \
+ -wrap word
+ pack append .tl.results \
+ .tl.results.top {top fillx frame w} \
+ .tl.results.s {left filly} \
+ .tl.results.t {left expand fill}
+
+ pack append .tl \
+ .tl.notes {top expand fill} \
+ .tl.macros {top expand fill} \
+ .tl.never {top expand fill} \
+ .tl.results {top expand fill} \
+
+ pack append .tl [button .tl.sv -text "Save As.." \
+ -command "save_tl"] {right}
+ pack append .tl [button .tl.exit -text "Close" \
+ -command "set PlaceTL [wm geometry .tl]; destroy .tl"] {right}
+
+ pack append .tl [button .tl.help -text "Help" -fg red \
+ -command "roadmap4"] {left}
+ pack append .tl [button .tl.clear -text "Clear" \
+ -command ".tl.main.e1 delete 0 end; .tl.never.t delete 0.0 end"] {left}
+
+ loaddefault_tl
+ focus .tl.main.e1
+}
+
+proc purge_nvr {foo} {
+ set j [llength $foo]; incr j -1
+ for {set i $j} {$i >= 0} {incr i -1} {
+ set k [lindex $foo $i]
+ set kk [expr $k+1]
+ .tl.never.t delete $k.0 $kk.0
+ }
+}
+
+proc grab_nvr {inp target} {
+
+ set pattern $inp
+ scan [.tl.never.t index end] %d numLines
+ set foo {}
+ set yes 0
+
+ for {set i 1} {$i < $numLines} { incr i} {
+ .tl.never.t mark set last $i.0
+ set have [.tl.never.t get last "last lineend + 1 chars"]
+ if {[regexp -indices $pattern $have indices]} {
+ lappend foo $i
+ set yes [expr 1 - $yes]
+ if {$yes} {
+ set pattern "#endif"
+ } else {
+ set pattern $inp
+ }
+ }
+ if {$yes && [string first $inp $have] != 0} {
+ $target insert end "$have"
+ lappend foo $i
+ }
+ }
+ purge_nvr $foo
+}
+
+proc extract_defs {} {
+ global tl_stat
+
+ set pattern "#define "
+ scan [.tl.never.t index end] %d numLines
+ set foo {}
+ set tl_stat 1
+ for {set i 1} {$i < $numLines} { incr i} {
+ .tl.never.t mark set last $i.0
+ set have [.tl.never.t get last "last lineend + 1 chars"]
+ if {[regexp -indices $pattern $have indices]} {
+ .tl.macros.t insert end "$have"
+ lappend foo $i
+ }
+ set have [.tl.never.t get last "last lineend"]
+ set k [string first "Formula As Typed: " $have]
+ if {$k > 0} {
+ set ff [string range $have [expr $k+18] end]
+ .tl.main.e1 insert end $ff
+ }
+ if {[string first "To The Negated Formula " $have] > 0} {
+ set tl_stat 0
+ }
+ }
+ purge_nvr $foo
+
+ grab_nvr "#ifdef NOTES" .tl.notes.t
+ grab_nvr "#ifdef RESULT" .tl.results.t
+}
+
+proc inspect_ltl {} {
+ global formula
+ set formula "[.tl.main.e1 get]"
+
+ set x $formula
+ regsub -all {\&\&} "$x" " " y; set x $y
+ regsub -all {\|\|} "$x" " " y; set x $y
+ regsub -all {\/\\} "$x" " " y; set x $y
+ regsub -all {\\\/} "$x" " " y; set x $y
+ regsub -all {\!} "$x" " " y; set x $y
+ regsub -all {<->} "$x" " " y; set x $y
+ regsub -all {\->} "$x" " " y; set x $y
+ regsub -all {\[\]} "$x" " " y; set x $y
+ regsub -all {\<\>} "$x" " " y; set x $y
+ regsub -all {[()]} "$x" " " y; set x $y
+ regsub -all {\ \ *} "$x" " " y; set x $y
+ regsub -all { U} "$x" " " y; set x $y
+ regsub -all { V} "$x" " " y; set x $y
+ regsub -all { X} "$x" " " y; set x $y
+
+ set predefs " np_ true false "
+
+ set k [split $x " "]
+ set j [llength $k]
+ set line [.tl.macros.t get 0.0 end]
+ for {set i 0} {$i < $j} {incr i} {
+ if {[string length [lindex $k $i]] > 0 \
+ && [string first " [lindex $k $i] " $predefs] < 0} {
+ set pattern "#define [lindex $k $i]"
+ if {[string first $pattern $line] < 0} {
+ catch {
+ .tl.macros.t insert end "$pattern\t?\n"
+ }
+ set line [.tl.macros.t get 0.0 end]
+ } } }
+}
+
+proc do_ltl {} {
+ global formula tl_stat SPIN tk_major tk_minor
+
+ set formula "[.tl.main.e1 get]"
+ .tl.never.t delete 0.0 end
+ update
+
+ catch { inspect_ltl }
+
+ set MostSystems 1 ;# change to 0 only if there are problems
+ ;# see below
+
+ if {$tl_stat == 0} {
+ add_log "$SPIN -f \"!( $formula )\""
+ if {$MostSystems} {
+ catch {exec $SPIN -f "!($formula)" >&pan.ltl} err
+ } else {
+ # this variant was needed on some older systems,
+ # but it causes problems on some of the newer ones...
+ catch {exec $SPIN -f \"!($formula)\" >&pan.ltl} err
+ }
+ } else {
+ add_log "$SPIN -f \"( $formula )\""
+ if {$MostSystems} {
+ catch {exec $SPIN -f "($formula)" >&pan.ltl} err
+ } else {
+ # see above
+ catch {exec $SPIN -f \"($formula)\" >&pan.ltl} err
+ }
+ }
+ set lno 0
+
+ if {$err != ""} {
+ add_log "<error: $err>"
+ add_log "hint: check the Help Button for syntax rules"
+ } else {
+ .tl.never.t insert end \
+ " /*\n"
+ .tl.never.t insert end \
+ " * Formula As Typed: $formula\n"
+ incr lno 2
+ if {$tl_stat == 0} {
+ .tl.never.t insert end \
+ " * The Never Claim Below Corresponds\n"
+ .tl.never.t insert end \
+ " * To The Negated Formula !($formula)\n"
+ .tl.never.t insert end \
+ " * (formalizing violations of the original)\n"
+ incr lno 3
+ }
+ .tl.never.t insert end \
+ " */\n\n"
+ incr lno 2
+ }
+ catch {
+ set fd [open pan.ltl r]
+ while {[gets $fd line] > -1} {
+ .tl.never.t insert end "$line\n"
+ }
+ close $fd
+ }
+ rmfile pan.ltl
+}
+
+proc dump_tl {bb} {
+
+ if {$bb != ""} {
+ set fnm $bb
+ } else {
+ set fnm [.sv_tl.ent get]
+ }
+
+ if {[file_ok $fnm]==0} return
+ set fd [open $fnm w]
+ add_log "<save claim in $fnm>"
+ catch { puts $fd "[.tl.macros.t get 0.0 end]" nonewline }
+
+ puts $fd "[.tl.never.t get 0.0 end]" nonewline
+
+ catch { puts $fd "#ifdef NOTES"
+ puts $fd "[.tl.notes.t get 0.0 end]" nonewline
+ puts $fd "#endif"
+ }
+ catch { puts $fd "#ifdef RESULT"
+ puts $fd "[.tl.results.t get 0.0 end]" nonewline
+ puts $fd "#endif"
+ }
+
+ catch "flush $fd"
+ catch "close $fd"
+ catch "destroy .sv_tl"
+ catch "focus .tl.main.e1"
+}
+
+proc save_tl {} {
+ global Fname PlaceWarn
+ catch {destroy .sv_tl}
+ toplevel .sv_tl
+
+ wm title .sv_tl "Save Claim"
+ wm iconname .sv_tl "Save"
+ wm geometry .sv_tl $PlaceWarn
+
+ label .sv_tl.msg -text "Name for LTL File: " -relief flat
+ entry .sv_tl.ent -width 6 -relief sunken -textvariable fnm
+ button .sv_tl.b1 -text "Ok" -command { dump_tl "" }
+ button .sv_tl.b2 -text "Cancel" -command "destroy .sv_tl"
+ bind .sv_tl.ent <Return> { dump_tl "" }
+
+ set fnm [.sv_tl.ent get]
+ if {$fnm == ""} {
+ .sv_tl.ent insert end "$Fname.ltl"
+ }
+
+ pack append .sv_tl \
+ .sv_tl.msg {top frame w} \
+ .sv_tl.ent {top frame e expand fill} \
+ .sv_tl.b1 {right frame e} \
+ .sv_tl.b2 {right frame e}
+ focus .sv_tl.ent
+}
+
+proc add_tl {} {
+ global BG FG HelvBig PlaceWarn
+ catch {destroy .warn}
+ toplevel .warn
+ set k [string first "\+" $PlaceWarn]
+ if {$k > 0} {
+ set PlaceWarn [string range $PlaceWarn $k end]
+ }
+
+ wm title .warn "Accept"
+ wm iconname .warn "Accept"
+ wm geometry .warn $PlaceWarn
+
+ message .warn.t -width 300 \
+ -background $BG -foreground $FG -font $HelvBig \
+ -text " \
+Instructions:
+
+1. Save the Never Claim in a file, \
+for instance a file called 'never', \
+using the <Save> button.
+
+2. Insert the line
+
+#include \"never\"
+
+(the name of the file with the claim) \
+at the end of the main specification.
+
+3. Insert macro definitions (#define's) for all \
+propositional symbols used in the formula.
+
+For instance, with LTL formula
+'[] p -> <> q' add the macro defs:
+
+#define p (cnt == 1)
+#define q (cnt > 1)
+
+These macros must be defined just above the line \
+with the #include \"never\" directive
+
+4. Perform the verification, and make sure that \
+the box 'Apply Never Claim' is checked in the \
+Verification Panel (or else the claim is ignored).
+You can have a library of claim files that you can \
+choose from for verification, by changing only the \
+name of the include file.
+
+5. Never claims have no effect during simulation runs.
+
+6. See the HELP->LTL menu for more information.
+
+"
+ button .warn.b -text "Ok" \
+ -command {set PlaceWarn [wm geometry .warn]; destroy .warn}
+ pack append .warn .warn.t {top expand fill}
+ pack append .warn .warn.b {right frame e}
+}
+
+proc roadmap4 {} {
+ global FG BG
+
+ catch {destroy .h}
+ toplevel .h
+
+ wm title .h "LTL Help"
+ wm iconname .h "Help"
+ frame .h.z
+ scrollbar .h.z.s -command ".h.z.t yview"
+ text .h.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".h.z.s set" \
+ -setgrid 1 -width 60 -height 30 -wrap word
+ pack append .h.z \
+ .h.z.s {left filly} \
+ .h.z.t {left expand fill}
+ .h.z.t insert end "GUIDELINES:
+You can load an LTL template or a previously saved LTL
+formula from a file via the LOAD button on the upper
+right of the LTL Property Manager panel.
+
+Define a new LTL formula using lowercase names for the
+propositional symbols, for instance:
+ [] (p U q)
+The formula expresses either a positive (desired) or a
+negative (undesired) property of the model. A positive
+property is negated automatically by the translator to
+convert it in a never claim (which expresses the
+corresponding negative property (the undesired behavior
+that is claimed 'never' to occur).
+
+When you type a <Return> or hit the <Generate> button,
+the formula is translated into an equivalent never-claim.
+
+You must add a macro-definition for each propositional
+symbol used in the LTL formula. Insert these definitions
+in the symbols window of the LTL panel, they will be
+remembered together with the annotations and verification
+result if the formula is saved in an .ltl file.
+Enclose the symbol definitions in braces, to secure correct
+operator precedence, for instance:
+
+#define p (a > b)
+#define q (len(q) < 5)
+
+Valid temporal logic operators are:
+ \[\] Always (no space between \[ and \])
+ <> Eventually (no space between < and >)
+ U (Strong) Until
+ V The Dual of Until: (p V q) == !(!p U !q)
+
+ All operators are left-associative (incl. U and V).
+
+Boolean Operators:
+ && Logical And (alternative form: /\\, no spaces)
+ ! Logical Negation
+ || Logical Or (alternative form: \\/, no spaces)
+ -> Logical Implication
+ <-> Logical Equivalence
+
+Boolean Predicates:
+ true, false
+ any name that starts with a lowercase letter
+
+Examples:
+ \[\] p
+ !( <> !q )
+ p U q
+ p U (\[\] (q U r))
+
+Generic properties/Templates:
+ Invariance: \[\] p
+ Response: p -> \<\> q
+ Precedence: p -> (q U r)
+ Objective: p -> \<\> (q || r)
+
+ Each of the above 4 generic types of properties
+ can (and will generally have to) be prefixed by
+ temporal operators such as
+ \[\], \<\>, \[\]\<\>, \<\>\[\]
+ The last (objective) property can be read to mean
+ that 'p' is a trigger, or 'enabling' condition that
+ determines when the requirement becomes applicable
+ (e.g. the sending of a new data message); then 'q'
+ can be the fullfillment of the requirement (e.g.
+ the arrival of the matching acknowledgement), and
+ 'r' could be a discharging condition that voids the
+ applicability of the check (an abort condition).
+"
+ button .h.b -text "Ok" -command {destroy .h}
+ pack append .h .h.z {top expand fill}
+ pack append .h .h.b {top}
+ .h.z.t configure -state disabled
+ .h.z.t yview -pickplace 1.0
+ focus .h.z.t
+}
+
+
+
+# Specific Help
+
+proc roadmap1 {} {
+ global FG BG
+
+ catch {destroy .road1}
+ toplevel .road1
+
+ wm title .road1 "Help with Simulation"
+ wm iconname .road1 "SimHelp"
+ frame .road1.z
+ scrollbar .road1.z.s -command ".road1.z.t yview"
+ text .road1.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road1.z.s set" \
+ -setgrid 1 -width 60 -height 30 -wrap word
+ pack append .road1.z \
+ .road1.z.s {left filly} \
+ .road1.z.t {left expand fill}
+ .road1.z.t insert end "GUIDELINES:
+0. Always use a Syntax Check before running simulations.\
+It shakes out the more obvious flaws in the model.
+
+1. Random simulation option is used to debug a model.\
+Other than assert statements, no correctness requirements\
+are checked during simulation runs. All nondeterministic\
+decisions are resolved randomly. You can of course still\
+force a selection to go into a specific direction by \
+modifying the model.\
+A random run is repeated precisely if the Seed Value\
+for the random number generator is kept the same.
+
+2. Interactive simulation can be used to force the\
+execution towards a known point. The user is prompted\
+at every point in the execution where a nondeterministic\
+choice has to be resolved.
+
+3. A guided simulation is used to follow an error-trail that was\
+produced by the verifier. If the trail gets to be thousands of execution\
+steps long, this can be time-consuming. \
+You can skip the initial portion of such a long trail by typing\
+a number in the 'Steps Skipped' box in the Simulation Panel .
+
+4. The options in the Simulations Panel allow you to enable or\
+disable types of displays that are maintained during simulation\
+runs. Usually, it is not necessary to look at all possible display panels.\
+Experiment to see which displays you find most useful.
+
+5. To track the value changes of Selected variables, in the\
+Message Sequence Chart and in the Variable Values Panel, add a prefix\
+'show ' to the declaration of the selected variables in the Promela\
+specification, e.g. use 'show byte cnt;' instead of 'byte cnt;'"
+
+ button .road1.b -text "Ok" -command {destroy .road1}
+ pack append .road1 .road1.z {top expand fill}
+ pack append .road1 .road1.b {top}
+ .road1.z.t configure -state disabled
+ .road1.z.t yview -pickplace 1.0
+ focus .road1.z.t
+}
+
+proc roadmap2a {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification Parameters"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 18 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "Physical Memory Available:
+Set this number to amount of physical (not virtual)
+memory in your system, in MegaBytes, and leave it there for all runs.
+
+When the limit is reached, the verification is stopped to
+avoid trashing.
+
+The number entered here is the number of MegaBytes directly
+(not a power of two, as in previous versions of xspin).
+
+If an exhaustive verification cannot be completed due to
+lack of memory, use compression, or switch to a
+supertrace/bitstate run under basic options."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+proc roadmap2b {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 15 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "Estimated State Space Size:
+This parameter is used to calculate the size of the
+hash-table. It results in a selection of a numeric argument
+for the -w flag of the verifier. Setting it too high may
+cause an out-of-memory error with zero states reached
+(meaning that the verification could not be started).
+Setting it too low can cause inefficiencies due to
+hash collisions.
+
+In Bitstate runs begin with the default estimate for
+this parameter. After a run completes successfully,
+double the estimate, and see if the number of reached
+stated changes much. Continue to do this until
+it stops changing, or until you overflow the memory
+bound and run out of memory.
+
+The closest power of two is taken by xspin to set the
+true number used for the number of reachable states.
+(The selected power of two is visible as the number that
+follow the -w flag in the run-line that xspin generates).
+
+To set a specific -w parameter, use the Extra Run-Time Option
+Field. If, for instance, -w32 is specified there, it will
+override the value computed from the Estimated State Space Size."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+proc roadmap2c {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 20 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "Maximum Search Depth:
+This number determines the size of the depth-first
+search stack that is used during the verification.
+The stack uses memory, so a larger number increases
+the memory requirements, and a lower number decreases
+it. In a tight spot, when there seems not to be
+sufficient memory for the search depth needed, you
+can reduce the estimated state space size to free some
+more memory for the stack.
+
+If you hit the maximum search depth during a verification
+(noted as 'Search not completed' or 'Search Truncated'
+in the verification output) without finding an error,
+double the search depth parameter and repeat the run."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap2k {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 10 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "Number of hash functions:
+This number determines how many bits are set per
+state when in Bitstate verification mode. The default is 2.
+At the end of a Bitstate verification run, the verifier
+can issue a recommendation for a different setting of
+this flag (which is the -k flag), it a change can be
+predicted to lead to better coverage."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap2d {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 26 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "GENERAL GUIDELINES:
+=> When just starting out, it is safe to leave all parameters in the\
+Verification Options Panel set at their initial value and to simply\
+push the Run button in the Basic Options Panel for a default\
+exhaustive verification.\
+If you run out of memory, adjust the parameter settings as \
+indicated under the 'explain' options.
+
+=> If an error is found, first try to reduce the search depth to \
+find a shorter equivalent. Once you're content with the length,\
+move on to a guided simulation to inspect the error-trail in detail.\
+Optionally, use the Find Shortest Trail option, but note that this\
+can increase runtime and memory use. So: do not use this option until\
+you are certain that an error exists -- leave it turned off by default).\
+If you are verifying a Safety Property, try the Breadth-First Search\
+mode to find the shortest counter-example. It may run out of memory\
+sooner than the default depth-first search mode, but it often works.
+
+=> It is always safe to leave the Partial Order Reduction option enabled.\
+Turn it off only for debugging purposes, or when warned to do so by the \
+verifier itself. The Profiling option gathers statistics about the \
+verification hot-spots in the model.
+
+=> If you save all error-trails, you have to copy the one you are\
+interested in inspecting with a guided simulation onto the file\
+pan_in.trail manually (outside xspin) after the run completes.\
+The trails are numbered in order of discovery."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap2e {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 25 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "BASIC GUIDELINES:
+When just starting out, it is safe to leave all parameters\
+at their initial value and to push the Run button for a\
+default exhaustive verification.\
+If you run out of memory, adjust the parameter settings\
+under Advanced Options.
+
+=> Safety properties are properties of single states (like\
+deadlocks = invalid endstates, or assertion violations).
+
+=> Liveness properties are properties of sequences of\
+states (typically: infinite sequences, i.e., cycles).\
+There are two types of cycles: non-progress (not passing\
+through any state marked with a 'progress' label) and\
+acceptance (passing infinitely often through a state\
+marked with an 'accept' label).
+
+=> Use the Weak Fairness option only when really necessary,\
+to avoid unwated error reports. It can increase the CPU-time\
+requirements by a factor roughly equal to twice the number of\
+active processes.
+
+=> It is safe to leave the Reduced Search option enabled.\
+Turn it off only for debugging purposes, or when warned to do so by the \
+verifier itself. The Profiling option gathers statistics about the \
+verification hot-spots in the model.
+
+=> You can apply a Never Claim even when checking Safety Properties\
+when you want to restrict the search to the sequences that are\
+matched by the claim (a user-guided search pruning technique)."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap2f {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 15 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "GUIDELINES:
+This will run a verification for the specific LTL property\
+that was defined in the LTL options panel that brought you\
+here. The claim was placed in a separate .ltl\
+file, not included in the main specification.\
+(It will be picked up in the verification automatically.)\
+The separate .ltl file combines the notes, formula,\
+macros, results, etc., for easier tracking.
+
+On a first run, leave all choices at their initial\
+value and push the Run button for a default verification.\
+If you run out of memory, adjust the parameter settings\
+under Advanced Options.
+
+Use the Weak Fairness option only when really necessary,\
+to avoid unwated error reports. It can increase the CPU-time\
+requirements by a factor roughly equal to twice the number of\
+active processes."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap2 {} {
+ global FG BG
+
+ catch {destroy .road2}
+ toplevel .road2
+
+ wm title .road2 "Help with Verification"
+ wm iconname .road2 "ValHelp"
+ frame .road2.z
+ scrollbar .road2.z.s -command ".road2.z.t yview"
+ text .road2.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road2.z.s set" \
+ -setgrid 1 -width 60 -height 20 -wrap word
+ pack append .road2.z \
+ .road2.z.s {left filly} \
+ .road2.z.t {left expand fill}
+ .road2.z.t insert end "GUIDELINES:
+When just starting out, it is safe to leave all
+verification parameters set at their initial values
+and to Run a default verification.
+If you run out of memory, or encounter other problems,
+look at the specific help options in the verification
+settings panel.
+One parameter is important to set correctly right from
+the start: the physical memory size of your system.
+It is by default set to 64 Mbytes. Set it once to the
+true amount of physical memory on your system, in Megabytes,
+and never change it again (unless you buy more physical
+memory for your machine of course).
+You can find this parameter under advanced options in the
+verification parameters panel.
+Bitstate/Supertrace verifications are approximate, and
+only used for models too large to verify exhaustively.
+This option allows you to get at least an approximate
+answer to the correctness of models that could otherwise
+not be verified by a finite state system."
+
+ button .road2.b -text "Ok" -command {destroy .road2}
+ pack append .road2 .road2.z {top expand fill}
+ pack append .road2 .road2.b {top}
+ .road2.z.t configure -state disabled
+ .road2.z.t yview -pickplace 1.0
+ focus .road2.z.t
+}
+
+proc roadmap3 {} {
+ global FG BG
+
+ catch {destroy .road3}
+ toplevel .road3
+
+ wm title .road3 "Reducing Complexity"
+ wm iconname .road3 "CompHelp"
+ frame .road3.z
+ scrollbar .road3.z.s -command ".road3.z.t yview"
+ text .road3.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road3.z.s set" \
+ -setgrid 1 -width 60 -height 30 -wrap word
+ pack append .road3.z \
+ .road3.z.s {left filly} \
+ .road3.z.t {left expand fill}
+ .road3.z.t insert end "
+When a verification cannot be completed because of\
+computational complexity; here are some strategies that\
+can be applied to combat this problem.
+
+0. Run the Slicing Algorithm (in the Run Menu) to find\
+potential redundancy in your model for the stated properties.
+
+1. Try to make the model more general, more abstract.\
+Remember that you are constructing a verification model and not\
+an implementation. SPIN's strength is in proving properties of\
+*interactions* in a distributed system (the implicit assumptions\
+that processes make about each other) -- it's strength is not in\
+proving things about local *computations*, data dependencies etc.
+
+2. Remove everything that is not directly related to the property\
+you are trying to prove: redundant computations, redundant data. \
+*Avoid counters*; avoid incrementing variables that are used for\
+only book-keeping purposes.
+The Syntax Check option will warn about the gravest offenses.
+
+3. Asynchronous channels are a significant source of complexity in\
+verification. Use a synchronous channel where possible. Reduce the\
+number of slots in asynchronous channels to a minimum (use 2, or 3\
+slots to get started).
+
+4. Look for processes that merely transfer messages. Consider if\
+you can remove processes that only copy incoming messages from\
+one channel into another, by letting the sender generate the\
+final message right away. If the intermediate process makes\
+choices (e.g., to delete or duplicate, etc.), let the sender\
+make that choice, rather than the intermediate process.
+
+5. Combine local computations into atomic, or d_step, sequences.
+
+6. Avoid leaving scratch data around in variables. You can reduce\
+the number of states by, for instance, resetting local variables\
+that are used inside atomic sequences to zero at the end of those\
+sequences; so that the scratch values aren't visible outside the\
+sequence. Alternatively: introduce some extra global 'hidden'\
+variables for these purposes (see the WhatsNew.html document).
+Use the predefined variable \"_\" as a write-only scratch variable\
+wherever possible.
+
+7. If possible to do so: combine the behavior of two processes into\
+a single one. Generalize behavior; focus on coordination aspects\
+(i.e., the interfaces between processes, rather than the local\
+computation inside processes).
+
+8. Try to exploit the partial order reduction strategies.\
+Use the xr and xs assertions (see WhatsNew.html); avoid sharing\
+channels between multiple receivers or multiple senders.\
+Avoid merging independent data-streams into a single shared channel."
+
+ button .road3.b -text "Ok" -command {destroy .road3}
+ pack append .road3 .road3.z {top expand fill}
+ pack append .road3 .road3.b {top}
+ .road3.z.t configure -state disabled
+ .road3.z.t yview -pickplace 1.0
+ focus .road3.z.t
+}
+
+proc roadmap5 {} {
+ global FG BG
+
+ catch {destroy .road5}
+ toplevel .road5
+
+ wm title .road5 "Spin Automata"
+ wm iconname .road5 "FsmHelp"
+ frame .road5.z
+ scrollbar .road5.z.s -command ".road5.z.t yview"
+ text .road5.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road5.z.s set" \
+ -setgrid 1 -width 60 -height 30 -wrap word
+ pack append .road5.z \
+ .road5.z.s {left filly} \
+ .road5.z.t {left expand fill}
+ .road5.z.t insert end "
+The Spin Automata view option give a view of the
+structure of the automata models that Spin uses in
+the verification process.
+Each proctype is represented by a unique automaton.
+
+Chosing this option (in the Run menu) will cause Spin to
+first generate a verifier, compile it, and then run it
+(as pan -d) to obtain a description of the proctype
+names and the corresponding automata.
+
+After selecting (double-clicking) the proctype name desired,
+the graph will be produced. The default graph layout
+algorithm is small and a self-contained part of Xspin,
+but also rather crude. Be on guard, therefore, for edges
+that overlap (a typical case, for instance, is a backedge
+that hides behind a series of forward edges. Use DOT
+(see the README.html file on Spin) when possible for much
+better graph layout.
+
+In the default layout, the following button actions are
+defined (the first one is not needed when using DOT):
+
+1. Moving Nodes: either Button-1 or Button-2.
+2. Displaying Edge Labels: hold Button-1 down on the edge.
+3. Cross-References: Move the cursor over a Node to see the
+ corresponding line in the Promela source, in the main
+ Xspin window.
+
+If labels look bad -- try changing the font definitions at
+the start of the xspin.tcl file (hints are given there).
+"
+ button .road5.b -text "Ok" -command {destroy .road5}
+ pack append .road5 .road5.z {top expand fill}
+ pack append .road5 .road5.b {top}
+ .road5.z.t configure -state disabled
+ .road5.z.t yview -pickplace 1.0
+ focus .road5.z.t
+}
+
+proc roadmap6 {} {
+ global FG BG
+
+ catch {destroy .road6}
+ toplevel .road6
+
+ wm title .road6 "Optional Compiler Directives"
+ wm iconname .road6 "Optional"
+ frame .road6.z
+ scrollbar .road6.z.s -command ".road6.z.t yview"
+ text .road6.z.t -relief raised -bd 2 \
+ -background $BG -foreground $FG \
+ -yscrollcommand ".road6.z.s set" \
+ -setgrid 1 -width 80 -height 30 -wrap word
+ pack append .road6.z \
+ .road6.z.s {left filly} \
+ .road6.z.t {left expand fill}
+ .road6.z.t insert end "
+ Use only when prompted:
+
+NFAIR=N size memory used for enforcing weak fairness (default N=2)
+VECTORSZ=N allocates memory (in bytes) for state vector (default N=1024)
+
+ Related to partial order reduction:
+
+CTL limit p.o.reduction to subset consistent with branching time logic
+GLOB_ALPHA consider process death a global action
+XUSAFE disable validity checks of xr/xs assertions
+
+ Speedups:
+
+NOBOUNDCHECK don't check array bound violations
+NOCOMP don't compress states with fullstate storage (uses more memory)
+NOSTUTTER disable stuttering rules (warning: changes semantics)
+
+ Memory saving (slower):
+
+MA=N use a minimized DFA encoding for state vectors up to N bytes
+
+ Experimental:
+
+BCOMP when in BITSTATE mode, computes hash over compressed state-vector
+NIBIS apply a small optimization of partial order reduction
+NOVSZ risky - removes 4 bytes from state vector - its length field.
+PRINTF enables printfs during verification runs
+RANDSTORE=N in BITSTATE mode, -DRANDSTORE=33 lowers prob of storing a state to 33%
+W_XPT=N with MA, write checkpoint files every multiple of N states stored
+R_XPT with MA, restart run from last checkpoint file written
+
+ Debugging:
+
+SDUMP with CHECK: adds ascii dumps of state vectors
+SVDUMP add run option -pN to write N-byte state vectors into file sv_dump
+
+ Already set by the other xspin options:
+
+BITSTATE use supertrace/bitstate instead of exhaustive exploration
+HC use hash-compact instead of exhaustive exploration
+COLLAPSE collapses state vector size by up to 80% to 90%
+MEMCNT=N set upperbound of 2^N bytes to memory that can be allocated
+MEMLIM=N set upperbound of N Mbytes to memory that can be allocated
+NOCLAIM exclude never claim from the verification, if present
+NOFAIR disable the code for weak-fairness (is faster)
+NOREDUCE disables the partial order reduction algorithm
+NP enable non-progress cycle detection (option -l, replacing -a),
+PEG add complexity profiling (transition counts)
+REACH guarantee absence of errors within the -m depth-limit
+SAFETY optimize for the case where no cycle detection is needed
+VAR_RANGES compute the effective value range of byte variables
+CHECK generate debugging information (see also below)
+VERBOSE elaborate debugging printouts
+"
+ button .road6.b -text "Ok" -command {destroy .road6}
+ pack append .road6 .road6.z {top expand fill}
+ pack append .road6 .road6.b {top}
+ .road6.z.t configure -state disabled
+ .road6.z.t yview -pickplace 1.0
+ focus .road6.z.t
+}
+
+
+# simulation options panel
+
+set s_options ""
+set v_options ""
+set a_options ""
+set c_options ""
+
+set Blue "blue"; #"yellow"
+set Yellow "yellow"; #"red"
+set White "white"; #"yellow"
+set Red "red"; #"yellow"
+set Green "green"; #"green"
+
+set fd 0
+set Depth 0
+set Seq(0) 0
+set Sdbox 0
+set Spbox(0) 0
+set sbox 0
+
+set simruns 0
+set stepper 0
+set stepped 0
+set VERBOSE 0
+set SYMBOLIC 0
+set howmany 0
+set Choice(1) 0
+set Sticky(0) 0
+set SparseMsc 1
+set showvars 1
+set vv 1
+set qv 1
+set gvars 1
+set lvars 0
+set hide_q1 ""
+set hide_q2 ""
+set hide_q3 ""
+set PlaceSim "+200+100"
+
+proc simulation_old {} {
+ global s_typ l_typ showvars qv PlaceSim
+ global fvars gvars lvars SparseMsc HelvBig
+ global msc ebc tsc vv svars rvars seed jumpsteps
+ global hide_q1 hide_q2 hide_q3
+
+ catch { .menu.run.m entryconfigure 5 -state normal }
+
+ catch {destroy .s}
+ toplevel .s
+ set k [string first "\+" $PlaceSim]
+ if {$k > 0} {
+ set PlaceSim [string range $PlaceSim $k end]
+ }
+
+ wm title .s "Simulation Options"
+ wm iconname .s "SIM"
+ wm geometry .s $PlaceSim
+
+ frame .s.opt -relief flat
+
+ mkpan_in
+
+ frame .s.opt.mode -relief raised -borderwidth 1m
+ label .s.opt.mode.fld0 \
+ -font $HelvBig \
+ -text "Display Mode" \
+ -relief sunken -borderwidth 1m
+
+ checkbutton .s.opt.mode.fld4b -text "Time Sequence Panel - with:" \
+ -variable tsc \
+ -relief flat
+ frame .s.opt.mode.flds
+ radiobutton .s.opt.mode.flds.fld3 \
+ -text " Interleaved Steps" \
+ -variable m_typ -value 2 \
+ -relief flat
+ radiobutton .s.opt.mode.flds.fld1 \
+ -text " One Window per Process" \
+ -variable m_typ -value 0 \
+ -relief flat
+ radiobutton .s.opt.mode.flds.fld2 \
+ -text " One Trace per Process" \
+ -variable m_typ -value 1 \
+ -relief flat
+ frame .s.opt.mode.flds.fld0 -width 15
+ pack append .s.opt.mode.flds \
+ .s.opt.mode.flds.fld0 {left frame w}\
+ .s.opt.mode.flds.fld3 {top frame w}\
+ .s.opt.mode.flds.fld1 {top frame w}\
+ .s.opt.mode.flds.fld2 {top frame w}
+
+ checkbutton .s.opt.mode.fld4a -text "MSC Panel - with:" \
+ -variable msc \
+ -relief flat
+ frame .s.opt.mode.steps
+ radiobutton .s.opt.mode.steps.fld5 -text " Step Number Labels" \
+ -variable SYMBOLIC -value 0 \
+ -relief flat
+ radiobutton .s.opt.mode.steps.fld6 -text " Source Text Labels" \
+ -variable SYMBOLIC -value 1 \
+ -relief flat
+ radiobutton .s.opt.mode.steps.fld7 -text " Normal Spacing" \
+ -variable SparseMsc -value 1 \
+ -relief flat
+ radiobutton .s.opt.mode.steps.fld8 -text " Condensed Spacing" \
+ -variable SparseMsc -value 0 \
+ -relief flat
+ frame .s.opt.mode.steps.fld0 -width 15
+ pack append .s.opt.mode.steps \
+ .s.opt.mode.steps.fld0 {left frame w}\
+ .s.opt.mode.steps.fld5 {top frame w}\
+ .s.opt.mode.steps.fld6 {top frame w}\
+ .s.opt.mode.steps.fld7 {top frame w}\
+ .s.opt.mode.steps.fld8 {top frame w}
+
+ checkbutton .s.opt.mode.fld4c -text "Execution Bar Panel" \
+ -variable ebc \
+ -relief flat
+ checkbutton .s.opt.mode.fld4d -text "Data Values Panel" \
+ -variable vv \
+ -relief flat
+
+ frame .s.opt.mode.vars
+
+ checkbutton .s.opt.mode.vars.fld4c -text " Track Buffered Channels" \
+ -variable qv \
+ -relief flat
+ checkbutton .s.opt.mode.vars.fld4d -text " Track Global Variables" \
+ -variable gvars \
+ -relief flat
+ checkbutton .s.opt.mode.vars.fld4e -text " Track Local Variables" \
+ -variable lvars \
+ -relief flat
+
+ checkbutton .s.opt.mode.vars.fld4f \
+ -text " Display vars marked 'show' in MSC" \
+ -variable showvars \
+ -relief flat
+ frame .s.opt.mode.vars.fld0 -width 15
+ pack append .s.opt.mode.vars \
+ .s.opt.mode.vars.fld0 {left frame w}\
+ .s.opt.mode.vars.fld4c {top frame w}\
+ .s.opt.mode.vars.fld4d {top frame w}\
+ .s.opt.mode.vars.fld4e {top frame w}\
+ .s.opt.mode.vars.fld4f {top frame w}
+
+ pack append .s.opt.mode .s.opt.mode.fld0 {top pady 4 frame w fillx}
+
+ pack append .s.opt.mode .s.opt.mode.fld4a {top pady 4 frame w}
+ pack append .s.opt.mode .s.opt.mode.steps {top frame w}
+
+ pack append .s.opt.mode .s.opt.mode.fld4b {top pady 4 frame w}
+ pack append .s.opt.mode .s.opt.mode.flds {top frame w}
+
+ pack append .s.opt.mode .s.opt.mode.fld4d {top pady 4 frame w}
+ pack append .s.opt.mode .s.opt.mode.vars {top frame w}
+
+ pack append .s.opt.mode .s.opt.mode.fld4c {top pady 4 frame w}
+
+ pack append .s.opt .s.opt.mode {left frame n}
+
+ frame .s.opt.mesg -relief raised -borderwidth 1m
+ label .s.opt.mesg.loss0 \
+ -font $HelvBig \
+ -text "A Full Queue" \
+ -relief sunken -borderwidth 1m
+ radiobutton .s.opt.mesg.loss1 -text "Blocks New Msgs" \
+ -variable l_typ -value 0 \
+ -relief flat
+ radiobutton .s.opt.mesg.loss2 -text "Loses New Msgs" \
+ -variable l_typ -value 1 \
+ -relief flat
+ pack append .s.opt.mesg .s.opt.mesg.loss0 {top pady 4 frame w fillx}
+ pack append .s.opt.mesg .s.opt.mesg.loss1 {top pady 4 frame w}
+ pack append .s.opt.mesg .s.opt.mesg.loss2 {top pady 4 frame w}
+
+ frame .s.opt.hide -relief raised -borderwidth 1m
+ label .s.opt.hide.txt \
+ -font $HelvBig \
+ -text "Hide Queues in MSC" \
+ -relief sunken -borderwidth 1m
+ pack append .s.opt.hide .s.opt.hide.txt {top pady 4 frame w fillx }
+
+ for {set i 1} {$i < 4} {incr i} {
+ frame .s.opt.hide.q$i
+ label .s.opt.hide.q$i.qno \
+ -font $HelvBig \
+ -text "Queue nr:"
+ entry .s.opt.hide.q$i.entry \
+ -relief sunken -width 8
+ pack append .s.opt.hide.q$i .s.opt.hide.q$i.qno {left pady 4 frame n }
+ pack append .s.opt.hide.q$i .s.opt.hide.q$i.entry {left pady 4 frame n}
+ pack append .s.opt.hide .s.opt.hide.q$i {top pady 4 frame w fillx}
+ }
+ frame .s.opt.lframe -relief raised -borderwidth 1m
+ label .s.opt.lframe.tl \
+ -font $HelvBig \
+ -text "Simulation Style" \
+ -relief sunken -borderwidth 1m
+ radiobutton .s.opt.lframe.is -text "Interactive" \
+ -variable s_typ -value 2 \
+ -relief flat
+ radiobutton .s.opt.lframe.gs -text "Guided (using pan.trail)" \
+ -variable s_typ -value 1 \
+ -relief flat
+ frame .s.opt.lframe.b
+ entry .s.opt.lframe.b.entry -relief sunken -width 8
+ label .s.opt.lframe.b.label \
+ -font $HelvBig \
+ -text "Steps Skipped"
+ pack append .s.opt.lframe.b \
+ .s.opt.lframe.b.label {left} \
+ .s.opt.lframe.b.entry {left}
+
+ radiobutton .s.opt.lframe.rs -text "Random (using seed)" \
+ -variable s_typ -value 0 \
+ -relief flat
+ frame .s.opt.lframe.s
+ entry .s.opt.lframe.s.entry -relief sunken -width 8
+ label .s.opt.lframe.s.label \
+ -font $HelvBig \
+ -text "Seed Value"
+ pack append .s.opt.lframe.s \
+ .s.opt.lframe.s.label {left} \
+ .s.opt.lframe.s.entry {left}
+
+ pack append .s.opt.lframe .s.opt.lframe.tl {top pady 4 frame w fillx} \
+ .s.opt.lframe.rs {top pady 4 frame w} \
+ .s.opt.lframe.s {top pady 4 frame e} \
+ .s.opt.lframe.gs {top pady 4 frame w} \
+ .s.opt.lframe.b {top pady 4 frame e} \
+ .s.opt.lframe.is {top pady 4 frame w}
+
+ pack append .s.opt .s.opt.lframe {top frame n}
+ pack append .s.opt .s.opt.mesg {top frame n fillx}
+ pack append .s.opt .s.opt.hide {top frame n expand fillx filly}
+
+ pack append .s .s.opt { top frame n }
+
+ pack append .s [button .s.rewind -text "Start" \
+ -command "Rewind" ] {right frame e}
+ pack append .s [button .s.exit -text "Cancel" \
+ -command "Stopsim" ] {right frame e}
+ pack append .s [button .s.help -text "Help" -fg red \
+ -command "roadmap1" ] {right frame e}
+
+ .s.opt.lframe.s.entry insert end $seed
+ .s.opt.lframe.b.entry insert end $jumpsteps
+
+ .s.opt.hide.q1.entry insert end $hide_q1
+ .s.opt.hide.q2.entry insert end $hide_q2
+ .s.opt.hide.q3.entry insert end $hide_q3
+
+ raise .s
+}
+
+
+proc simulation {} {
+ global s_typ l_typ showvars qv PlaceSim
+ global fvars gvars lvars SparseMsc HelvBig
+ global msc ebc tsc vv svars rvars seed jumpsteps
+ global hide_q1 hide_q2 hide_q3
+ global whichsim
+
+ catch { .menu.run.m entryconfigure 5 -state normal }
+
+ catch {destroy .s}
+ toplevel .s
+ catch {rmfile pan_in9999999.trail}
+ debug {about to remove pan_in9999999.trail}
+ rmfile pan_in9999999.trail
+ set k [string first "\+" $PlaceSim]
+ if {$k > 0} {
+ set PlaceSim [string range $PlaceSim $k end]
+ }
+
+ wm title .s "Simulation Options"
+ wm iconname .s "SIM"
+ wm geometry .s $PlaceSim
+
+ mkpan_in
+
+ # lower frame with 'start', 'cancel' and 'help' buttons
+ frame .s.l -relief flat
+ pack .s.l -side bottom -fill both
+
+ pack [button .s.l.rewind -text " Start " \
+ -command "Rewind" ] -side right -fill y -padx 4
+ pack [button .s.l.exit -text "Cancel" \
+ -command "
+ Stopsim;
+ catch {rmfile pan_in9999999.trail}
+ "
+ ] -side right -fill y -padx 4
+ pack [button .s.l.help -text " Help " -fg red \
+ -command "roadmap1" ] -side right -fill y -padx 4
+
+ # upper frame with modes and options
+ frame .s.u -relief flat
+ pack .s.u -side top -fill both
+
+ frame .s.u.mode -relief raised -borderwidth 1m
+ pack .s.u.mode -side left -fill both -expand 1
+
+ frame .s.u.mode.fdis -relief flat
+ pack .s.u.mode.fdis -side top -fill x -expand 1
+
+ label .s.u.mode.fdis.fld0 \
+ -font $HelvBig \
+ -text "Display Mode" \
+ -relief sunken -borderwidth 1m
+ pack .s.u.mode.fdis.fld0 -side top -fill x
+
+#MSC Panel
+
+ frame .s.u.mode.fmsc -relief flat
+ pack .s.u.mode.fmsc -side top -fill x
+
+ checkbutton .s.u.mode.fmsc.fld4a -text "MSC Panel - with:" \
+ -variable msc \
+ -relief flat
+ pack .s.u.mode.fmsc.fld4a -side left
+
+ frame .s.u.mode.msc -relief flat
+ pack .s.u.mode.msc -side top -fill x
+
+ frame .s.u.mode.msc.lab -relief flat
+ pack .s.u.mode.msc.lab -side top -fill x
+
+ frame .s.u.mode.msc.lab.dummy -width 18 -relief flat
+ pack .s.u.mode.msc.lab.dummy -side left -fill y
+
+ frame .s.u.mode.msc.lab.radios -relief flat
+ pack .s.u.mode.msc.lab.radios -side left -fill x
+
+ frame .s.u.mode.msc.lab.radios.fnum -relief flat
+ pack .s.u.mode.msc.lab.radios.fnum -side top -fill x
+
+ radiobutton .s.u.mode.msc.lab.radios.fnum.fld5 \
+ -text " Step Number Labels" \
+ -variable SYMBOLIC -value 0 \
+ -relief flat
+ pack .s.u.mode.msc.lab.radios.fnum.fld5 -side left
+
+ frame .s.u.mode.msc.lab.radios.ftext -relief flat
+ pack .s.u.mode.msc.lab.radios.ftext -side top -fill x
+
+ radiobutton .s.u.mode.msc.lab.radios.ftext.fld6 \
+ -text " Source Text Labels" \
+ -variable SYMBOLIC -value 1 \
+ -relief flat
+ pack .s.u.mode.msc.lab.radios.ftext.fld6 -side left
+
+ frame .s.u.mode.msc.lab.bracket
+ pack .s.u.mode.msc.lab.bracket -side left -fill y
+
+ canvas .s.u.mode.msc.lab.bracket.c -width 10 -height 40
+ pack .s.u.mode.msc.lab.bracket.c -side top
+ .s.u.mode.msc.lab.bracket.c create line 5 15 10 15 10 38 5 38
+
+ frame .s.u.mode.msc.space -relief flat
+ pack .s.u.mode.msc.space -side top -fill x
+
+ frame .s.u.mode.msc.space.dummy -width 18 -relief flat
+ pack .s.u.mode.msc.space.dummy -side left -fill y
+
+ frame .s.u.mode.msc.space.radios -relief flat
+ pack .s.u.mode.msc.space.radios -side left -fill x
+
+ frame .s.u.mode.msc.space.radios.fnorm -relief flat
+ pack .s.u.mode.msc.space.radios.fnorm -side top -fill x
+
+ radiobutton .s.u.mode.msc.space.radios.fnorm.fld7 \
+ -text " Normal Spacing" \
+ -variable SparseMsc -value 1 \
+ -relief flat
+ pack .s.u.mode.msc.space.radios.fnorm.fld7 -side left
+
+ frame .s.u.mode.msc.space.radios.fcond -relief flat
+ pack .s.u.mode.msc.space.radios.fcond -side top -fill x
+
+ radiobutton .s.u.mode.msc.space.radios.fcond.fld8 \
+ -text " Condensed Spacing" \
+ -variable SparseMsc -value 0 \
+ -relief flat
+ pack .s.u.mode.msc.space.radios.fcond.fld8 -side left
+
+ frame .s.u.mode.msc.space.bracket
+ pack .s.u.mode.msc.space.bracket -side left -fill y
+
+ canvas .s.u.mode.msc.space.bracket.c -width 10 -height 40
+ pack .s.u.mode.msc.space.bracket.c -side top
+ .s.u.mode.msc.space.bracket.c create line 5 15 10 15 10 38 5 38
+
+# Time Sequence Panel
+
+ frame .s.u.mode.ftsp -relief flat
+ pack .s.u.mode.ftsp -side top -fill x
+
+ checkbutton .s.u.mode.ftsp.fld4b \
+ -text "Time Sequence Panel - with:" \
+ -variable tsc \
+ -relief flat
+ pack .s.u.mode.ftsp.fld4b -side left
+
+ frame .s.u.mode.tsp
+ pack .s.u.mode.tsp -side top -fill x
+
+ frame .s.u.mode.tsp.proc
+ pack .s.u.mode.tsp.proc -side top -fill x
+
+ frame .s.u.mode.tsp.proc.dummy -width 18
+ pack .s.u.mode.tsp.proc.dummy -side left -fill y
+
+ frame .s.u.mode.tsp.proc.radios -relief flat
+ pack .s.u.mode.tsp.proc.radios -side left -fill y
+
+ frame .s.u.mode.tsp.proc.radios.is
+ pack .s.u.mode.tsp.proc.radios.is -side top -fill x
+
+ radiobutton .s.u.mode.tsp.proc.radios.is.fld3 \
+ -text " Interleaved Steps" \
+ -variable m_typ -value 2 \
+ -relief flat
+ pack .s.u.mode.tsp.proc.radios.is.fld3 -side left
+
+ frame .s.u.mode.tsp.proc.radios.1win -relief flat
+ pack .s.u.mode.tsp.proc.radios.1win -side top -fill x
+
+ radiobutton .s.u.mode.tsp.proc.radios.1win.fld1 \
+ -text " One Window per Process" \
+ -variable m_typ -value 0 \
+ -relief flat
+ pack .s.u.mode.tsp.proc.radios.1win.fld1 -side left
+
+ frame .s.u.mode.tsp.proc.radios.1trace -relief flat
+ pack .s.u.mode.tsp.proc.radios.1trace -side top -fill x
+
+ radiobutton .s.u.mode.tsp.proc.radios.1trace.fld2 \
+ -text " One Trace per Process" \
+ -variable m_typ -value 1 \
+ -relief flat
+ pack .s.u.mode.tsp.proc.radios.1trace.fld2 -side left
+
+ frame .s.u.mode.tsp.proc.bracket
+ pack .s.u.mode.tsp.proc.bracket -side left -fill y
+
+ set y 13
+ canvas .s.u.mode.tsp.proc.bracket.c -width 10 -height 66
+ pack .s.u.mode.tsp.proc.bracket.c -side top
+ .s.u.mode.tsp.proc.bracket.c create line 5 [expr 0 + $y] \
+ 10 [expr 0 + $y] \
+ 10 [expr 25 + $y] \
+ 5 [expr 25 + $y] \
+ 10 [expr 25 + $y] \
+ 10 [expr 50 + $y] \
+ 5 [expr 50 + $y]
+
+ frame .s.u.mode.fdvp -relief flat
+ pack .s.u.mode.fdvp -side top -fill x
+
+ checkbutton .s.u.mode.fdvp.fld4d -text "Data Values Panel" \
+ -variable vv \
+ -relief flat
+ pack .s.u.mode.fdvp.fld4d -side left
+
+ frame .s.u.mode.vars
+ pack .s.u.mode.vars -side top -fill x
+
+ frame .s.u.mode.vars.dummy -width 18
+ pack .s.u.mode.vars.dummy -side left -fill y
+
+ frame .s.u.mode.vars.chks -relief flat
+ pack .s.u.mode.vars.chks -side left -fill y
+
+ frame .s.u.mode.vars.chks.ftbc
+ pack .s.u.mode.vars.chks.ftbc -side top -fill x
+
+ checkbutton .s.u.mode.vars.chks.ftbc.fld4c -text " Track Buffered Channels" \
+ -variable qv \
+ -relief flat
+ pack .s.u.mode.vars.chks.ftbc.fld4c -side left
+
+ frame .s.u.mode.vars.chks.ftgv
+ pack .s.u.mode.vars.chks.ftgv -side top -fill x
+
+ checkbutton .s.u.mode.vars.chks.ftgv.fld4d -text " Track Global Variables" \
+ -variable gvars \
+ -relief flat
+ pack .s.u.mode.vars.chks.ftgv.fld4d -side left
+
+ frame .s.u.mode.vars.chks.ftlv
+ pack .s.u.mode.vars.chks.ftlv -side top -fill x
+
+ checkbutton .s.u.mode.vars.chks.ftlv.fld4e -text " Track Local Variables" \
+ -variable lvars \
+ -relief flat
+ pack .s.u.mode.vars.chks.ftlv.fld4e -side left
+
+ frame .s.u.mode.vars.chks.fshow
+ pack .s.u.mode.vars.chks.fshow -side top -fill x
+
+ checkbutton .s.u.mode.vars.chks.fshow.fld4f \
+ -text " Display vars marked 'show' in MSC" \
+ -variable showvars \
+ -relief flat
+
+ pack .s.u.mode.vars.chks.fshow.fld4f -side left
+
+ frame .s.u.mode.fexecbar -relief flat
+ pack .s.u.mode.fexecbar -side top -fill x
+
+ checkbutton .s.u.mode.fexecbar.fld4c -text "Execution Bar Panel" \
+ -variable ebc \
+ -relief flat
+ pack .s.u.mode.fexecbar.fld4c -side left
+
+#Right upper frame
+ frame .s.u.r -relief flat
+ pack .s.u.r -side right -fill y -expand 1
+
+#Simulation Style
+ frame .s.u.r.sim -relief raised -borderwidth 1m
+ pack .s.u.r.sim -side top -fill both -expand 1
+
+ frame .s.u.r.sim.flab -relief sunken
+ pack .s.u.r.sim.flab -side top -fill x
+
+ label .s.u.r.sim.flab.tl \
+ -font $HelvBig \
+ -text "Simulation Style" \
+ -relief sunken -borderwidth 1m
+ pack .s.u.r.sim.flab.tl -side top -fill x
+
+ frame .s.u.r.sim.random
+ pack .s.u.r.sim.random -side top -fill x
+
+ radiobutton .s.u.r.sim.random.rs -text "Random (using seed)" \
+ -variable s_typ -value 0 \
+ -relief flat \
+ -command "enable_disable_sub_buttons"
+ pack .s.u.r.sim.random.rs -side left
+
+ frame .s.u.r.sim.seedvalue
+ pack .s.u.r.sim.seedvalue -side top -fill x
+
+ frame .s.u.r.sim.seedvalue.dummy -width 18
+ pack .s.u.r.sim.seedvalue.dummy -side left -fill y
+
+ label .s.u.r.sim.seedvalue.label \
+ -font $HelvBig \
+ -text "Seed Value"
+ pack .s.u.r.sim.seedvalue.label -side left
+
+ entry .s.u.r.sim.seedvalue.entry -relief sunken -width 8
+ pack .s.u.r.sim.seedvalue.entry -side left
+
+ frame .s.u.r.sim.guided
+ pack .s.u.r.sim.guided -side top -fill x
+
+ radiobutton .s.u.r.sim.guided.gs -text "Guided" \
+ -variable s_typ -value 1 \
+ -relief flat \
+ -command "enable_disable_sub_buttons"
+ pack .s.u.r.sim.guided.gs -side left
+
+ frame .s.u.r.sim.guided_type
+ pack .s.u.r.sim.guided_type -side top -fill x
+
+ frame .s.u.r.sim.guided_type.dummy -width 18
+ pack .s.u.r.sim.guided_type.dummy -side left -fill y
+
+ frame .s.u.r.sim.guided_type.radios
+ pack .s.u.r.sim.guided_type.radios -side left
+
+ frame .s.u.r.sim.guided_type.radios.pan_trail
+ pack .s.u.r.sim.guided_type.radios.pan_trail -side top -fill x
+
+ radiobutton .s.u.r.sim.guided_type.radios.pan_trail.rb \
+ -text "Using pan_in.trail" \
+ -variable whichsim -value 0 \
+ -relief flat
+ pack .s.u.r.sim.guided_type.radios.pan_trail.rb -side left
+
+ frame .s.u.r.sim.guided_type.radios.trail_other
+ pack .s.u.r.sim.guided_type.radios.trail_other -side top -fill x
+
+ radiobutton .s.u.r.sim.guided_type.radios.trail_other.rb \
+ -text "Use" \
+ -variable whichsim -value 1 \
+ -relief flat
+ pack .s.u.r.sim.guided_type.radios.trail_other.rb -side left
+
+ entry .s.u.r.sim.guided_type.radios.trail_other.entry \
+ -width 20
+ pack .s.u.r.sim.guided_type.radios.trail_other.entry -side left
+
+ button .s.u.r.sim.guided_type.radios.trail_other.button -text "Browse" \
+ -command select_trail_file
+ pack .s.u.r.sim.guided_type.radios.trail_other.button -side left
+
+ frame .s.u.r.sim.skipstep
+ pack .s.u.r.sim.skipstep -side top -fill x
+
+ label .s.u.r.sim.skipstep.label \
+ -font $HelvBig \
+ -text "Steps Skipped"
+ pack .s.u.r.sim.skipstep.label -side left
+
+ entry .s.u.r.sim.skipstep.entry -relief sunken -width 8
+ pack .s.u.r.sim.skipstep.entry -side left
+
+ frame .s.u.r.sim.interactive
+ pack .s.u.r.sim.interactive -side top -fill x
+
+ radiobutton .s.u.r.sim.interactive.is -text "Interactive" \
+ -variable s_typ -value 2 \
+ -relief flat \
+ -command "enable_disable_sub_buttons"
+ pack .s.u.r.sim.interactive.is -side left
+
+#A Full Queue
+ frame .s.u.r.fq -relief raised -borderwidth 1m
+ pack .s.u.r.fq -side top -fill both -expand 1
+
+ frame .s.u.r.fq.label -relief sunken
+ pack .s.u.r.fq.label -side top -fill x
+
+ label .s.u.r.fq.label.loss0 \
+ -font $HelvBig \
+ -text "A Full Queue" \
+ -relief sunken -borderwidth 1m
+ pack .s.u.r.fq.label.loss0 -side top -fill x
+
+ frame .s.u.r.fq.block
+ pack .s.u.r.fq.block -side top -fill x
+
+ radiobutton .s.u.r.fq.block.loss1 -text "Blocks New Msgs" \
+ -variable l_typ -value 0 \
+ -relief flat
+ pack .s.u.r.fq.block.loss1 -side left
+
+ frame .s.u.r.fq.lose
+ pack .s.u.r.fq.lose -side top -fill x
+
+ radiobutton .s.u.r.fq.lose.loss2 -text "Loses New Msgs" \
+ -variable l_typ -value 1 \
+ -relief flat
+ pack .s.u.r.fq.lose.loss2 -side left
+
+#Hide Queues in MSC
+ frame .s.u.r.hq -relief raised -borderwidth 1m
+ pack .s.u.r.hq -side top -fill both -expand 1
+
+ frame .s.u.r.hq.flabel -relief sunken
+ pack .s.u.r.hq.flabel -side top -fill x
+
+ label .s.u.r.hq.flabel.txt \
+ -font $HelvBig \
+ -text "Hide Queues in MSC" \
+ -relief sunken -borderwidth 1m
+ pack .s.u.r.hq.flabel.txt -side top -fill x
+
+ for {set i 1} {$i < 4} {incr i} {
+ frame .s.u.r.hq.q$i
+ pack .s.u.r.hq.q$i -side top -fill x
+ label .s.u.r.hq.q$i.qno \
+ -font $HelvBig \
+ -text "Queue nr:"
+ pack .s.u.r.hq.q$i.qno -side left
+ entry .s.u.r.hq.q$i.entry \
+ -relief sunken -width 8
+ pack .s.u.r.hq.q$i.entry -side left
+ }
+
+ .s.u.r.sim.seedvalue.entry insert end $seed
+ .s.u.r.sim.skipstep.entry insert end $jumpsteps
+
+ .s.u.r.hq.q1.entry insert end $hide_q1
+ .s.u.r.hq.q2.entry insert end $hide_q2
+ .s.u.r.hq.q3.entry insert end $hide_q3
+ enable_disable_sub_buttons
+
+ tkwait visibility .s
+ raise .s
+}
+
+proc enable_disable_sub_buttons {} {
+ global s_typ
+ switch -regexp $s_typ {
+ 0|2 { .s.u.r.sim.guided_type.radios.pan_trail.rb configure -state disabled
+ .s.u.r.sim.guided_type.radios.trail_other.rb configure -state disabled
+ .s.u.r.sim.guided_type.radios.trail_other.button configure -state disabled
+ }
+ 1 { .s.u.r.sim.guided_type.radios.pan_trail.rb configure -state normal
+ .s.u.r.sim.guided_type.radios.trail_other.rb configure -state normal
+ .s.u.r.sim.guided_type.radios.trail_other.button configure -state normal
+ .s.u.r.sim.guided_type.radios.pan_trail.rb select
+ }
+
+ }
+}
+
+proc select_trail_file {} {
+ global Trail_filename
+ .s.u.r.sim.guided_type.radios.trail_other.rb select
+ # try to use the predefined file selection dialog
+ switch [info commands tk_getOpenFile] "" {
+ # some old version of Tk so use our own file selection dialog
+ set fileselect "FileSelect open"
+ } default {
+ set fileselect "tk_getOpenFile"
+ }
+ set init_dir [pwd]
+ # get the file (return if the file selection dialog canceled)
+ switch -- [set file [eval $fileselect -initialdir { { $init_dir } } ]] "" return
+ .s.u.r.sim.guided_type.radios.trail_other.entry insert end $file
+ raise .s
+
+}
+
+proc bld_s_options {} {
+ global fvars gvars lvars svars qv
+ global rvars l_typ showvars vv
+ global s_typ seed jumpsteps s_options
+ global hide_q1 hide_q2 hide_q3 ival whichsim trail_file trail_num
+
+ set s_options "-X -p -v $ival(5)"
+
+ if {$showvars && $gvars == 0 && $lvars == 0} {
+ catch { tk_messageBox -icon info \
+ -message "Display variables marked 'show' selected, \
+ but no local or global vars are being tracked"
+ } }
+ if {$showvars==1} { set s_options [format "%s -Y" $s_options] }
+ if {$s_typ==2} { set s_options [format "%s -i" $s_options] }
+ if {$vv && $gvars} { set s_options [format "%s -g" $s_options] }
+ if {$vv && $lvars} { set s_options [format "%s -l" $s_options] }
+ if {$svars} { set s_options [format "%s -s" $s_options] }
+ if {$rvars} { set s_options [format "%s -r" $s_options] }
+ if {$l_typ} { set s_options [format "%s -m" $s_options] }
+ if {$hide_q1 != ""} { set s_options [format "%s -q%s" $s_options $hide_q1] }
+ if {$hide_q2 != ""} { set s_options [format "%s -q%s" $s_options $hide_q2] }
+ if {$hide_q3 != ""} { set s_options [format "%s -q%s" $s_options $hide_q3] }
+ if {$s_typ==1} then {
+ set trail_num ""
+ #Guided
+ if {$whichsim == 1} {
+ #using user specified file
+ if ![file exists $trail_file] {
+ catch { tk_messageBox -icon info \
+ -message "Trail file $trail_file does not exist."
+ }
+ return 0
+ }
+ # see if file is in current directory. if not, copy to
+ # pan_in9999999.trail in current directory
+ set ind [string last "\/" $trail_file]
+ if {$ind > -1} {
+ if {[pwd] != [string range $trail_file 0 [expr $ind - 1]]} {
+ cpfile $trail_file pan_in9999999.trail
+ set trail_file "pan_in9999999.trail"
+ } else {
+ #strip off path
+ set trail_file [string range $trail_file \
+ [expr $ind + 1] \
+ [expr [string length $trail_file] - 1]]
+ }
+ }
+ #see if it's a 'pan_in<#>.trail' file
+ set is_pan_in_trail_file 0
+ if {[string range $trail_file 0 5] == "pan_in"} {
+ set l [string length $trail_file]
+
+ if {[string range $trail_file \
+ [expr $l-6] [expr $l-1]] == ".trail"} {
+ set num [string range $trail_file 6 [expr $l-7]]
+ if [string is integer $num] {
+ set trail_num $num
+ set is_pan_in_trail_file 1
+ } } }
+ if !($is_pan_in_trail_file) {
+ # not a 'pan_in<#>.trail' file - copy file to pan_in9999999.trail
+ # in current directory
+ cpfile $trail_file pan_in9999999.trail
+ if [file exists pan_in9999999.trail] {
+ set trail_num 9999999
+ } else {
+ catch {tk_messageBox -icon info \
+ -message "Unable to create input file in $pwd \
+ check write permissions."
+ }
+ return 0
+ }
+ }
+ } else {
+ if {![file exists pan_in.trail] && ![file exists pan_in.tra]} {
+ catch { tk_messageBox -icon info \
+ -message "Trail file \'pan_in.tra(il)\' does not exist."
+ }
+ return 0
+ }
+ }
+
+ set s_options [format "%s -t%s" $s_options $trail_num]
+ } else {
+ if {[string length $seed] > 0} {
+ set s_options [format "%s -n%s" $s_options $seed]
+ } }
+ if {$s_typ!=2} then {
+ if {[string length $jumpsteps] > 0} {
+ set s_options [format "%s -j%s" $s_options $jumpsteps]
+ } }
+ return 1
+}
+
+proc Stopsim {} {
+ global stop dbox2 Sticky PlaceSim PlaceCanvas
+ global stepper stepped howmany fd
+
+ set stop 1
+ set stepped 0
+ set stepper 0
+ add_log "<stop simulation>"
+ if {[winfo exists .s]} {
+ set PlaceSim [wm geometry .s]
+ destroy .s
+ }
+ catch {set howmany 0}
+ catch {stopbar}
+ catch { if {$Sticky($dbox2) == 0} {
+ set PlaceCanvas(msc) [wm geometry .f$dbox2]
+ destroy .f$dbox2
+ } }
+ catch {
+ puts $fd "q"
+ flush $fd
+ }
+ update
+}
+
+proc Step_forw {} {
+ global stepper stepped sbox simruns PlaceSim
+
+ set stepped 1
+ set stepper 1
+ if {$simruns == 0} {
+ if {[winfo exists .s]} {
+ set PlaceSim [wm geometry .s]
+ destroy .s
+ }
+ runsim
+ } else {
+ catch { .c$sbox.run configure \
+ -text "Run" -command "Runsim" }
+ }
+}
+
+proc Rewind {} {
+ global Depth s_typ whichsim trail_file
+ global Sdbox Spbox
+ global seed jumpsteps simruns
+ global hide_q1 hide_q2 hide_q3 trail_file
+
+ catch { set jumpsteps [.s.u.r.sim.skipstep.entry get] }
+ catch { set hide_q1 [.s.u.r.hq.q1.entry get] }
+ catch { set hide_q2 [.s.u.r.hq.q2.entry get] }
+ catch { set hide_q3 [.s.u.r.hq.q3.entry get] }
+
+ if {$s_typ == 0} {
+ catch { set seed [.s.u.r.sim.seedvalue.entry get] }
+ }
+ if {$s_typ == 1} {
+ #Guided
+ set Depth 0
+ catch {
+ foreach el [array names Spbox] {
+ set Sdbox $Spbox($el)
+ .c$Sdbox.z.t tag remove Rev 1.0 end
+ } }
+ if {$whichsim == 1} {
+ set trail_file ""
+ catch {set trail_file [.s.u.r.sim.guided_type.radios.trail_other.entry get]}
+ }
+ }
+
+ set simruns 0
+
+ Step_forw
+}
+
+proc Runsim {} {
+ global stepper stepped sbox
+
+ catch { .c$sbox.run configure \
+ -text "Suspend" -command "Step_forw" }
+ set stepper 1
+ set stepped 0
+}
+
+proc BreakPoint {} {
+ global stepped sbox
+
+ set stepped 1
+ catch { .c$sbox.run configure \
+ -text "BreakPoint" -command "Runsim" }
+}
+
+proc runsim {} {
+ global Unix SPIN tk_major
+ global s_options s_typ dbox2
+ global stepper stepped
+ global simruns m_typ
+ global gvars lvars
+ global fd stop Depth Seq
+ global Sdbox Spbox pbox howmany Choice
+ global sbox VERBOSE SYMBOLIC msc ebc vv tsc
+ global Blue Yellow White Red Green
+ global SmallFont BigFont Sticky SparseMsc
+ global FG BG qv gvars lvars PlaceBox
+ global dbox Vvbox
+ global whichsim trail_num
+
+ set simruns 1
+ set Vvbox 0
+ set pno 0
+ set Varnm("") ""
+ set Queues("") ""
+ set Depth 0
+ set Seq(0) 0
+ set Pstp 1
+ set Seenpno 1
+ set Banner "Select"
+
+# catch { unset Spbox(0) }
+ catch {
+ foreach el [array names pbox] {
+ catch { destroy .c$pbox($el) }
+ catch { unset pbox($el) }
+ }
+ foreach el [array names Spbox] {
+ catch { destroy .c$Spbox($el) }
+ catch { unset Spbox($el) }
+ }
+ }
+ if ![bld_s_options] {
+ return
+ }
+
+ add_log "<starting simulation>"
+ add_log "$SPIN $s_options pan_in"
+ update
+ set s_options [format "%s pan_in" $s_options]
+
+ mkpan_in
+
+ set sbox [mkbox "Simulation Output" "SimOut" "sim.out" 71 11 100 100]
+
+ pack append .c$sbox [button .c$sbox.stepf -text "Single Step" \
+ -command "Step_forw" ] {left frame w}
+ pack append .c$sbox [button .c$sbox.run -text "Run" \
+ -command "Runsim" ] {left frame w}
+
+ .c$sbox.b configure -text "Cancel" -command "Stopsim"
+
+ raise .c$sbox
+
+ set YSZ 12
+ set XSZ 84
+ set YNR 60
+ set NPR 10
+ set SMX 250
+ set Easy 1
+ set HAS 0
+ set HAS_CYCLE 0
+ set dontwait 0
+ set notexecutable 0
+ set lastexecutable 0
+
+ if {$m_typ == 2} {
+ if {$tsc} {
+ set pbox(0) \
+ [mkbox "Time Sequence" "Sequence" "seq.out" 80 10 100 325]
+ set dbox $pbox(0)
+ } }
+ if {$msc} {
+ if {[hasWord "!!"] || [hasWord "\\?\\?"]} {
+ set Easy 0
+ }
+
+ set maxx [expr [winfo screenwidth .] - 400] ;# button widths
+ set maxh [expr [winfo screenheight .] - (5+120)] ;# borders+buttons
+ set dbox2 \
+ [mkcanvas "Sequence Chart" "msc" $maxx 5 1]
+ .f$dbox2.c configure -height $maxh \
+ -scrollregion "[expr -$XSZ/2] 0 \
+ [expr $NPR*$XSZ] [expr 100+$SMX*$YSZ]"
+
+ raise .f$dbox2
+ }
+
+ raise .c$sbox
+
+ set stop 0
+ set good_trail 0
+ if {$s_typ == 1} {
+ if $whichsim {
+ set filen "pan_in${trail_num}.trail"
+ if [file exists $filen] {
+ set good_trail 1
+ }
+ } else {
+ if {[file exists pan_in.trail] || [file exists pan_in.tra]} {
+ set good_trail 1
+ }
+ }
+ if $good_trail {
+ catch { .c$sbox.z.t insert end "preparing trail, please wait..." }
+ update
+ rmfile trail.out
+ catch {eval exec $SPIN $s_options >&trail.out} errmsg
+ } else {
+ set errmsg "error: no trail file for guided simulation"
+ return
+ }
+ if {[string length $errmsg]>0} {
+ add_log "$errmsg"
+ catch {
+ tk_messageBox -icon info -message $errmsg
+ }
+ catch {
+ set fd [open trail.out r]
+ while {[gets $fd line] > -1} {
+ add_log "$line"
+ }
+ close $fd
+ }
+ Stopsim
+ catch { destroy .c$sbox }
+ catch { destroy .c$dbox }
+ set simruns 0
+ update
+ return
+ }
+ set fd [open trail.out r]
+ catch { .c$sbox.z.t insert end "done\n" }
+ } else {
+ update
+ set fd [open "|$SPIN $s_options" r+]
+ catch "flush $fd"
+ update
+ }
+
+ if {$s_typ == 2} {
+ Runsim
+ }
+
+ if {$ebc} { startbar "Xspin Bar Chart" }
+
+ set pstp -1
+ set bailout 0
+ set realstring ""
+
+ update
+ raise .c$sbox
+ lower .
+
+ while {$stop == 0 && [eof $fd] == 0} {
+ if {$bailout == 0 && [gets $fd line] > -1} {
+ set pln 0
+ set syntax 0
+ set isvar 0
+ set pname ""
+ set i 0
+ set VERBOSE 0
+ set Fnm "pan_in"
+
+ raise .c$sbox
+
+ if {$Unix == 0} {
+ if {[string first "processes created" $line] > 0} {
+ set bailout 1
+ } }
+ if {[string first "type return to proceed" $line] > 0} {
+ puts $fd ""
+ flush $fd
+ update
+ continue
+ }
+
+ set i [string first "<merge" $line]
+ if {$i > 0} {
+ set line [string range $line 0 $i]
+ }
+
+ set lastpstp $pstp
+ set pmtch [scan $line \
+ "%d: proc %d (%s line %d \"%s\" " \
+ pstp pno pname pln Fnm]
+ incr pmtch -1
+ set i [string first "\[" $line]
+ if {$i > 0} {
+ set i [expr $i + 1]
+ set j [string length $line]
+ set j [expr $j - 2]
+ set stmnt [string range $line $i $j]
+ } else {
+ set stmnt "-"
+ }
+ if {$pmtch != 4} {
+ set pmtch [scan $line \
+ " proc %d (%s line %d \"%s\" " \
+ pno pname pln Fnm]
+ }
+ if {$pmtch != 4} {
+ if {[string first "spin: line" $line] == 0 } {
+ scan $line "spin: line %d \"%s\" " pln Fnm
+ if {[string first "pan_in" $Fnm] >= 0} {
+ .inp.t tag add Rev $pln.0 $pln.end
+ .inp.t tag configure Rev \
+ -background $FG -foreground $BG
+ .inp.t yview -pickplace $pln.0
+ }
+ if {[string first "assertion viol" $line] < 0} {
+ set syntax 1
+ }
+ }
+ if {[string first "Error: " $line] >= 0 \
+ || [string first "warning: " $line] >= 0 } {
+ set syntax 1
+ }
+ }
+ if {$pmtch != 4 && $syntax == 0} {
+ set pmtch [scan $line \
+ "%d: proc - (%s line %d \"%s\" " \
+ pstp pname pln Fnm]
+ if { $pmtch == 4 } {
+ set pno -1
+ }
+ }
+ # set Fnm [string trim $Fnm "\""]
+ set pname [string trim $pname "()"]
+
+ if {[string first "TRACK" $pname] >= 0} {
+ set nwcol([expr $pno+1]) 1
+ } elseif {[string length $pname] > 0} {
+ if {[info exists nwcol([expr $pno+1])] \
+ && $nwcol([expr $pno+1])} {
+ unset Plabel($pno)
+##
+ set TMP1 [expr ($pno + 1)*$XSZ]
+ set TMP2 [expr $Pstp*$YSZ]
+ .f$dbox2.c create line \
+ [expr $TMP1 - 20] $TMP2 \
+ [expr $TMP1 + 20] $TMP2 \
+ -width 2 \
+ -fill $Red
+ incr TMP2 4
+ .f$dbox2.c create line \
+ [expr $TMP1 - 20] $TMP2 \
+ [expr $TMP1 + 20] $TMP2 \
+ -width 2 \
+ -fill $Red
+##
+ }
+ set nwcol([expr $pno+1]) 0
+ }
+ if {$pmtch == 4 && $syntax == 0} {
+ if {$ebc} {
+ if {[string first "values:" $line] < 0} {
+ stepbar $pno $pname
+ } }
+ if {$m_typ == 1 && $tsc} {
+ if { [info exists pbox($pno)] == 0 } {
+ set pbox($pno) [mkbox \
+ "Proc $pno ($pname)" \
+ "Proc$pno" "proc.$pno.out" \
+ 60 10 \
+ [expr 100+$pno*25] \
+ [expr 325+$pno*35] ]
+ }
+ set dbox $pbox($pno)
+ } elseif {$m_typ == 0 && $tsc} {
+ if { [info exists Spbox($pno)] == 0 } {
+ set Spbox($pno) \
+ [mkbox "$pname (proc $pno)" \
+ "$pname" "" \
+ 60 10 \
+ [expr 100+$pno*25] \
+ [expr 325+$pno*35] ]
+ readinfile .c$Spbox($pno).z.t "pan_in"
+ }
+ set Sdbox $Spbox($pno)
+ }
+ } elseif { [string first "..." $line] > 0 && \
+ [regexp "^\\\t*MSC: (.*)" $line] == 0 } {
+ set $line ""
+ set syntax 1
+ set pln 0
+ } elseif {$s_typ == 2 \
+ && [string first "Select " $line] == 0 } {
+ set Banner $line
+ set pln 0
+ set notexecutable 0
+ set lastexecutable 0
+ set has_timeout 0
+ } elseif {$s_typ == 2 \
+ && [string first "choice" $line] >= 0 } {
+ scan $line " choice %d" howmany
+ set NN [string first ":" $line]; incr NN 2
+ set Choice($howmany) [string range $line $NN end]
+ if {[string first "timeout" $Choice($howmany)] > 0} {
+ set has_timeout 1
+ }
+ if {[string first "unexecutable," $line] >= 0} {
+ incr notexecutable
+ } else {
+ set lastexecutable $howmany
+ }
+ set pln 0
+ } elseif {$s_typ == 2 \
+ && [string first "Make Selection" $line] >= 0 } {
+ scan $line "Make Selection %d" howmany
+ if {$notexecutable == $howmany-1 && $has_timeout == 0} {
+ set howmany $lastexecutable
+ add_log "selected: $howmany (forced)"
+ catch {
+ foreach el [array names Choice] {
+ unset Choice($el)
+ } }
+ } else {
+ pickoption $Banner
+ add_log "selected: $howmany"
+ }
+ puts $fd $howmany
+ catch "flush $fd"
+ set dontwait 1
+ set pln 0
+ } elseif { [regexp "^\\\t*MSC: (.*)" $line mch rstr] != 0 } {
+ if {$realstring != ""} {
+ set realstring "$realstring $rstr"
+ } else {
+ set realstring $rstr
+ }
+ # picked up in next cycle
+ } elseif { [string first "processes" $line] > 0 \
+ || [string first "timeout" $line] == 0 \
+ || [string first "=s==" $line] > 0 \
+ || [string first "=r==" $line] > 0 } {
+
+ if { $m_typ == 1 && $tsc} {
+ set dbox $pbox(0)
+ } elseif { $m_typ == 0 && $tsc} {
+ if { [info exists Spbox($pno)] == 0 } {
+ set Spbox($pno) \
+ [mkbox "$pname (proc $pno)" \
+ "$pname" "" \
+ 60 10 \
+ [expr 100+$pno*25] \
+ [expr 325+$pno*35] ]
+ readinfile .c$Spbox($pno).z.t "pan_in"
+ }
+ set Sdbox $Spbox($pno)
+ }
+ set pln 0; # prevent tag update
+ } elseif {$syntax == 0 && [string first " = " $line] > 0 } {
+ set isvar [string first "=" $line]
+ set isvar [expr $isvar + 1]
+ set varvl [string range $line $isvar end]
+ set isvar [expr $isvar - 2]
+ set varnm [string range $line 0 $isvar]
+ set varnm [string trim $varnm " "]
+ set Varnm($varnm) $varvl
+ set isvar 1
+ } elseif { [scan $line " %s %d " varnm qnr] == 2} {
+ if {$syntax == 0 && [string compare $varnm "queue"] == 0} {
+ set isvar [string last ":" $line]
+ set isvar [expr $isvar + 1]
+ set varvl [string range $line $isvar end]
+ set XX [string first "(" $line]
+ set YY [string last ")" $line]
+ set ZZ [string range $line $XX $YY]
+ set Queues($qnr) $varvl
+ if {[info exists Alias($qnr)]} {
+ if {[string first $ZZ $Alias($qnr)] < 0} {
+ set Alias($qnr) "$Alias($qnr), $ZZ"
+ }
+ } else {
+ set Alias($qnr) $ZZ
+ }
+ set isvar 1
+ }
+ } elseif {[string length $line] == 0} {
+ if {$dontwait == 0} { set stepper 0 }
+ set pln 0
+ set Depth [expr $Depth + 1]
+ set Seq($Depth) [tell $fd]
+ set dontwait 0
+ }
+
+
+ if {$syntax == 0} {
+ if {[string first "terminates" $line] > 0} {
+ set pln -1
+ set stmnt "<stop>"
+ }
+##NEW
+ if {$pln > 0 && [string first "pan_in" $Fnm] >= 0} {
+ .inp.t tag remove hilite 0.0 end
+ src_line $pln
+ }
+##END
+ if {$m_typ == 0} {
+ if {$pln > 0 && [string first "pan_in" $Fnm] >= 0} {
+ catch {
+ .c$Sdbox.z.t yview -pickplace $pln.0
+ .c$Sdbox.z.t tag remove Rev 1.0 end
+ .c$Sdbox.z.t tag add Rev $pln.0 $pln.end
+ .c$Sdbox.z.t tag configure Rev \
+ -background $FG -foreground $BG
+ } }
+ } elseif {$m_typ == 1} {
+ if { [info exists pbox($pno)] == 0 } {
+ set pbox($pno) [mkbox \
+ "Proc $pno ($pname)" \
+ "Proc$pno" "proc.$pno.out" \
+ 60 10 \
+ [expr 100+$pno*25] \
+ [expr 325+$pno*35] ]
+ }
+ set dbox $pbox($pno)
+ catch {
+ .c$dbox.z.t yview -pickplace end
+ .c$dbox.z.t insert end "$line\n"
+ }
+ } elseif {$m_typ == 2 && $pln != 0 \
+ && [string first "unexecutable, " $line] < 0} {
+ catch { .c$dbox.z.t yview -pickplace end }
+ catch { .c$dbox.z.t insert end "$pno:$pln" }
+ for {set i $pno} {$i > 0} {incr i -1} {
+ catch { .c$dbox.z.t insert end "\t|" }
+ }
+ catch { .c$dbox.z.t insert end "\t|>$stmnt\n" }
+ }
+ if {$msc && $pln != 0} {
+ set Mcont "--"
+ set HAS 0
+ if { [scan $stmnt "values: %d!%d" inq inp1] == 2 \
+ || [scan $stmnt "values: %d!%s" inq inp2] == 2 } {
+ set HAS [string first "!" $stmnt]
+ incr HAS
+ set Mcont [string range $stmnt $HAS end]
+ set HAS 1
+ } elseif { [scan $stmnt "values: %d?%d" inq inp1] == 2 \
+ || [scan $stmnt "values: %d?%s" inq inp2] == 2 } {
+ set HAS [string first "?" $stmnt]
+ incr HAS
+ set Mcont [string range $stmnt $HAS end]
+ set HAS 2
+ } elseif { [string first "-" $stmnt] == 0} {
+ set HAS 3
+ if {$HAS_CYCLE} {
+ set stmnt [format "Cycle>>"]
+ } else {
+ set stmnt [format "<waiting>"]
+ }
+ } elseif { [string first "<stop>" $stmnt] == 0} {
+ set HAS 3
+ set stmnt [format "<stopped>"]
+ }
+ if {$pno+1 > $Seenpno} { set Seenpno [expr $pno+1] }
+ set XLOC [expr (1+$pno)*$XSZ]
+ set YLOC [expr $Pstp*$YSZ]
+ if {[string first "printf('MSC: " $stmnt] == 0} {
+ set VERBOSE 1
+ set stmnt $realstring
+ if {[string first "BREAK" $realstring] >= 0} {
+ BreakPoint
+ }
+ set realstring ""
+ } else {
+ set VERBOSE 0
+ }
+ catch {
+ if {$VERBOSE \
+ || $HAS != 0 \
+ || [info exists R($pstp,$pno)]} {
+
+ if { $SparseMsc == 1 \
+ || [info exists Plabel($pno)] == 0 \
+ || ([info exists R($pstp,$pno)] == 0 \
+ && ($HAS != 1 \
+ || [info exists HasBox($YLOC,[expr 1+$pno])])) } {
+ incr Pstp
+ for {set i 1} \
+ {$Pstp > 1 && $i <= $Seenpno} \
+ {incr i} {
+ if {[info exists HasBox($YLOC,$i)]} {
+ continue
+ }
+ set TMP1 [expr $i*$XSZ]
+ set lncol $Blue
+ set lnwdt 1
+ catch {
+ if {$nwcol($i)} {
+ set lncol "gray"
+ set lnwdt 15
+ } }
+ .f$dbox2.c create line \
+ $TMP1 $YLOC $TMP1 \
+ [expr $YLOC+$YSZ] \
+ -width $lnwdt \
+ -fill $lncol
+ }
+ if {[info exists HasBox($YLOC,[expr 1+$pno])]} {
+ set YLOC [expr $Pstp*$YSZ]
+ } }
+ if {$HAS == 1 || $HAS == 2} {
+ set stmnt [string range $stmnt 8 end]
+ }
+ if { [info exists Plabel($pno)] == 0} {
+ set Plabel($pno) 0
+ if {$SparseMsc == 0} {
+ set HasBox($YLOC,[expr 1+$pno]) 1
+ }
+ .f$dbox2.c create rectangle \
+ [expr $XLOC-20] $YLOC \
+ [expr $XLOC+20] \
+ [expr $YLOC+$YSZ] \
+ -outline $Red -fill $Yellow
+
+ if {$pname != "TRACK"} {
+ .f$dbox2.c create text $XLOC \
+ [expr $YLOC+$YSZ/2] \
+ -font $SmallFont \
+ -text "$pname:$pno"
+ } else {
+ .f$dbox2.c create text $XLOC \
+ [expr $YLOC+$YSZ/2] \
+ -font $SmallFont \
+ -text "<show>"
+ }
+
+ set YLOC [expr $Pstp*$YSZ]
+ incr Pstp
+ for {set i 1} \
+ {$Pstp > 1 && $i <= $Seenpno} \
+ {incr i} {
+ set TMP1 [expr $i*$XSZ]
+ set lncol $Blue
+ set lnwdt 1
+ catch {
+ if {$nwcol($i)} {
+ set lncol "gray"
+ set lnwdt 15
+ } }
+ .f$dbox2.c create line \
+ $TMP1 $YLOC $TMP1 \
+ [expr $YLOC+$YSZ] \
+ -width $lnwdt \
+ -fill $lncol
+ }
+ }
+ if {(1+$pno) > $NPR} {
+ set NPR [expr $pno+2]
+ .f$dbox2.c configure \
+ -scrollregion \
+ "[expr -$XSZ/2] 0 \
+ [expr $NPR*$XSZ] [expr $SMX*$YSZ]"
+ }
+ if {$Pstp > $SMX-2} {
+ set SMX [expr 2*$SMX]
+ .f$dbox2.c configure \
+ -scrollregion \
+ "[expr -$XSZ/2] 0 \
+ [expr $NPR*$XSZ] [expr $SMX*$YSZ]"
+ }
+
+ if { [info exists R($pstp,$pno)] == 0 } {
+ if {$VERBOSE == 1} {
+ if {[string first "~W " $stmnt] == 0} {
+ set BoxFil $White
+ set stmnt [string range $stmnt 3 end]
+ } else { if {[string first "~G " $stmnt] == 0} {
+ set BoxFil $Green
+ set stmnt [string range $stmnt 3 end]
+ } else { if {[string first "~R " $stmnt] == 0} {
+ set BoxFil $Red
+ set stmnt [string range $stmnt 3 end]
+ } else { if {[string first "~B " $stmnt] == 0} {
+ set BoxFil $Blue
+ set stmnt [string range $stmnt 3 end]
+ } else { set BoxFil $Yellow } } } }
+ set BoxLab $stmnt
+ if {[string first "line " $stmnt] == 0} {
+ scan $stmnt "line %d" pln
+ set Fnm "pan_in" ;# not necessarily right...
+ }
+ } else {
+ set BoxLab $pstp
+ set BoxFil $White
+ }
+ if {$SparseMsc == 0} {
+ set HasBox($YLOC,[expr 1+$pno]) 1
+ }
+ set R($pstp,$pno) \
+ [.f$dbox2.c create rectangle \
+ [expr $XLOC-20] $YLOC \
+ [expr $XLOC+20] \
+ [expr $YLOC+$YSZ] \
+ -outline $Blue -fill $BoxFil]
+ set T($pstp,$pno) \
+ [.f$dbox2.c create text \
+ $XLOC \
+ [expr $YLOC+$YSZ/2] \
+ -font $SmallFont \
+ -text $BoxLab]
+ #if {$Pstp > $YNR-2} {
+ # .f$dbox2.c yview \
+ # [expr ($Pstp-$YNR)]
+ #}
+ }
+ if { $HAS == 3 } {
+ .f$dbox2.c itemconfigure \
+ $R($pstp,$pno) \
+ -outline $Red -fill $Yellow
+ }
+
+ if {$SYMBOLIC} {
+ .f$dbox2.c itemconfigure $T($pstp,$pno) \
+ -font $SmallFont -text "$stmnt"
+ } else {
+ if {$VERBOSE == 0 } {
+ .f$dbox2.c bind $T($pstp,$pno) <Any-Enter> "
+ .f$dbox2.c itemconfigure $T($pstp,$pno) \
+ -font $BigFont -text {$stmnt}
+ .inp.t tag remove hilite 0.0 end
+ if {[string first "pan_in" $Fnm] >= 0} {
+ src_line $pln
+ }
+ "
+ .f$dbox2.c bind $T($pstp,$pno) <Any-Leave> "
+ .f$dbox2.c itemconfigure $T($pstp,$pno) \
+ -font $SmallFont -text {$pstp}
+ "
+ } else {
+ .f$dbox2.c bind $T($pstp,$pno) <Any-Enter> "
+ .inp.t tag remove hilite 0.0 end
+ if {[string first "pan_in" $Fnm] >= 0} {
+ src_line $pln
+ }
+ "
+ }
+ }
+ }
+
+ set YLOC [expr $YLOC+$YSZ/2]
+ if {$HAS == 1} {
+ if { [info exists Q_add($inq)] == 0 } {
+ set Q_add($inq) 0
+ set Q_del($inq) 0
+ }
+ set Slot $Q_add($inq)
+ incr Q_add($inq) 1
+
+ set Mesg_y($inq,$Slot) $YLOC
+ set Mesg_x($inq,$Slot) $XLOC
+ set Q_val($inq,$Slot) $Mcont
+
+ set Rem($inq,$Slot) \
+ [.f$dbox2.c create text \
+ [expr $XLOC-40] $YLOC \
+ -font $SmallFont -text $stmnt]
+ } elseif { $HAS == 2 } {
+ if {$Easy} {
+ set Slot $Q_del($inq)
+ incr Q_del($inq) 1
+ } else {
+ for {set Slot $Q_del($inq)} \
+ {$Slot < $Q_add($inq)} \
+ {incr Slot} {
+ if {$Q_val($inq,$Slot) == "_X_"} {
+ incr Q_del($inq) 1
+ } else {
+ break
+ } }
+
+ for {set Slot $Q_del($inq)} \
+ {$Slot < $Q_add($inq)} \
+ {incr Slot} {
+ if {$Mcont == $Q_val($inq,$Slot)} {
+ set Q_val($inq,$Slot) "_X_"
+ break
+ } }
+ }
+ if {$Slot >= $Q_add($inq)} {
+ add_log "<<error: cannot match $stmnt>>"
+ } else {
+ set TMP1 $Mesg_x($inq,$Slot)
+ set TMP2 $Mesg_y($inq,$Slot)
+ if {$XLOC < $TMP1} {
+ set Delta -20
+ } else {
+ set Delta 20
+ }
+ .f$dbox2.c create line \
+ [expr $TMP1+$Delta] $TMP2 \
+ [expr $XLOC-$Delta] $YLOC \
+ -fill $Red -width 2 \
+ -arrow last -arrowshape {5 5 5}
+
+ if {$SparseMsc == 0} {
+ set TMP3 5
+ } else {
+ set TMP3 0
+ }
+
+ .f$dbox2.c coords $Rem($inq,$Slot) \
+ [expr ($TMP1 + $XLOC)/2] \
+ [expr ($TMP2 + $YLOC)/2 - $TMP3]
+ .f$dbox2.c raise $Rem($inq,$Slot)
+ }
+ } }
+ }
+ if {$pln == 0 && ($gvars || $lvars || $qv)} {
+ if {$Vvbox == 0} {
+ if {$vv} { set Vvbox [mkbox "Data Values" \
+ "Vars" "var.out" 71 19 100 350] }
+ } else {
+ catch { .c$Vvbox.z.t delete 0.0 end }
+ }
+ if {$vv} {
+ if {$gvars || $lvars} {
+ raise .c$Vvbox
+ foreach el [lsort [array names Varnm]] {
+ if {[string length $Varnm($el)] > 0} {
+ catch { .c$Vvbox.z.t insert \
+ end "$el = $Varnm($el)\n" }
+ } }
+ }
+ if {$qv} {
+ foreach el [lsort [array names Queues]] {
+ catch {
+ .c$Vvbox.z.t insert end "queue $el ($Alias($el))\n"
+ .c$Vvbox.z.t insert end " $Queues($el)\n"
+ } }
+ } }
+ }
+ } else {
+ set stepper 0
+ }
+ if {$isvar == 0} {
+ if {$syntax == 1} {
+ if {[string first "..." $line] < 0} {
+ add_log "$line"
+ catch { .c$sbox.z.t insert end "$line\n" }
+ catch { .c$sbox.z.t yview -pickplace end }
+ }
+ } else {
+ if {[string length $line] > 0} {
+ catch { .c$sbox.z.t insert end "$line\n" }
+ catch { .c$sbox.z.t yview -pickplace end }
+ }
+ if {$m_typ == 2 && \
+ [string first "START OF CYCLE" $line] > 0} {
+ catch { .c$dbox.z.t yview -pickplace end }
+ catch { .c$dbox.z.t insert end "$line\n" }
+ catch {
+ set XLOC [expr $Seenpno*$XSZ+$XSZ/2]
+ set YLOC [expr $Pstp*$YSZ+$YSZ/2]
+
+ .f$dbox2.c create text \
+ [expr $XLOC+$XSZ] $YLOC \
+ -font $SmallFont \
+ -text "Cycle/Waiting" \
+ -fill $Red
+
+ .f$dbox2.c create line \
+ $XLOC $YLOC \
+ [expr $XLOC+$XSZ/2] $YLOC \
+ -fill $Red \
+ -arrow first -arrowshape {5 5 5}
+ }
+ set HAS_CYCLE [expr $YLOC+1]
+ }
+ if {$m_typ == 2 && $HAS == 3 && $HAS_CYCLE != 0} {
+ catch {
+ set YLOC [expr $Pstp*$YSZ+$YSZ/2]
+ set XLOC0 [expr $pno*$XSZ+$XSZ]
+ set XLOC [expr $Seenpno*$XSZ+$XSZ]
+ .f$dbox2.c create line \
+ $XLOC0 [expr $YLOC-$YSZ/2] \
+ $XLOC0 $YLOC \
+ -fill $Red
+ .f$dbox2.c create line \
+ $XLOC0 $YLOC $XLOC $YLOC \
+ -fill $Red
+
+ set XLOC [expr $Seenpno*$XSZ+$XSZ]
+
+ .f$dbox2.c create line \
+ $XLOC $YLOC $XLOC \
+ [expr $HAS_CYCLE-1] \
+ -fill $Red
+ } } } }
+ # mystery update:
+ if {$tk_major >= 4 || $m_typ != 1} {
+ update ;# tk 3.x can crash on this
+ }
+
+ if {$syntax == 0 \
+ && $stop == 0 \
+ && $stepped == 1 \
+ && $stepper == 0 \
+ && $dontwait == 0} {
+ update ;# here it is harmless also with tk 3.x
+ tkwait variable stepper
+ }
+ } else {
+ if {$s_typ == 0 || $s_typ == 2} {
+ add_log "<at end of run>"
+ } else {
+ add_log "<at end of trail>"
+ }
+ catch { addscales $dbox2 }
+ if {$ebc} { barscales }
+ update
+ tkwait variable stepper
+ }
+ }
+ # end of guided trail
+
+ while {$stepper == 1} {
+ tkwait variable stepper
+ }
+ teardown
+
+ catch "close $fd"
+ add_log "<done>"
+
+ update
+}
+
+proc teardown {} {
+ global m_typ pbox sbox dbox Spbox Vvbox
+ global simruns stop stepped stepper howmany
+
+ set simruns 0
+ set stop 1
+ set stepped 0
+ set stepper 0
+ catch { set howmany 0 }
+ catch {
+ if { $m_typ == 1 } {
+ foreach el [array names pbox] {
+ catch { destroy .c$pbox($el) }
+ catch { unset pbox($el) }
+ }
+ } elseif { $m_typ == 0 } {
+ foreach el [array names Spbox] {
+ catch { destroy .c$Spbox($el) }
+ catch { unset Spbox($el) }
+ } }
+ }
+ if {[winfo exists .c$sbox]} {
+ set x "Simulation Output"
+ set PlaceBox($x) [wm geometry .c$sbox]
+ set k [string first "\+" $PlaceBox($x)]
+ if {$k > 0} {
+ set PlaceBox($x) [string range $PlaceBox($x) $k end]
+ }
+ destroy .c$sbox
+ }
+ catch { destroy .c$dbox }
+ if {[winfo exists .c$Vvbox]} {
+ set x "Data Values"
+ set PlaceBox($x) [wm geometry .c$Vvbox]
+ set k [string first "\+" $PlaceBox($x)]
+ if {$k > 0} {
+ set PlaceBox($x) [string range $PlaceBox($x) $k end]
+ }
+ destroy .c$Vvbox
+ }
+}
+
+set PlaceMenu "+150+150"
+
+proc pickoption {nm} {
+ global howmany Choice PlaceMenu
+
+ catch {destroy .prompt}
+ toplevel .prompt
+ wm title .prompt "Select"
+ wm iconname .prompt "Select"
+ wm geometry .prompt $PlaceMenu
+
+ text .prompt.t -relief raised -bd 2 \
+ -width [string length $nm] -height 1 \
+ -setgrid 1
+ pack append .prompt .prompt.t { top expand fillx }
+ .prompt.t insert end "$nm"
+ for {set i 0} {$i <= $howmany} {incr i} {
+ if {[info exists Choice($i)] \
+ && $Choice($i) != 0 \
+ && [string first "outside range" $Choice($i)] < 0 \
+ && [string first "unexecutable," $Choice($i)] <= 0} {
+ pack append .prompt \
+ [button .prompt.b$i -text "$i: $Choice($i)" \
+ -anchor w \
+ -command "set howmany $i" ] \
+ {top expand fillx}
+
+ set j [string first "line " $Choice($i)]
+ if {$j > 0} {
+ set k [string range $Choice($i) $j end]
+ scan $k "line %d" k
+ bind .prompt.b$i <Enter> "report $k"
+ bind .prompt.b$i <Leave> "report 0"
+ bind .prompt.b$i <ButtonRelease-1> "set howmany $i"
+ } } }
+ tkwait variable howmany
+ set PlaceMenu [wm geometry .prompt]
+ set k [string first "\+" $PlaceMenu]
+ if {$k > 0} {
+ set PlaceMenu [string range $PlaceMenu $k end]
+ }
+ catch { foreach el [array names Choice] { unset Choice($el) } }
+ destroy .prompt
+}
+
+proc report {n} {
+ .inp.t tag remove hilite 0.0 end
+ if {$n > 0} { src_line $n }
+}
+
+# validation options panel
+
+set an_typ -1; set cp_typ 0; set cyc_typ 0
+set as_typ -1; set ie_typ 1; set ebc 0
+set ct_typ 0; set et_typ 1
+set st_typ 0; set se_typ 0; set bf_typ 0
+set oct_typ -1; # remembers last setting used for compilation
+set nv_typ 1
+set po_typ -1; set cm_typ 0; set vb_typ 0
+set pr_typ 0; set where 0
+set vr_typ 0; set xu_typ -1
+set ur_typ 1; set vbox 0
+set killed 0; set Job_Done 0; set tcnt 0
+set waitwhat "none"
+set not_warned_yet 1
+
+set LastGenerate ""
+set LastCompile ""
+set NextCompile ""
+
+proc syntax_check {a T} {
+ global SPIN BG FG
+
+ mkpan_in
+ add_log "$SPIN $a pan_in"
+ catch {exec $SPIN $a pan_in >&pan.tmp} err ;# added -v
+ set cnt 0
+ set maxln 50
+ set ef [open pan.tmp r]
+ .inp.t tag remove hilite 0.0 end
+ .inp.t tag remove sel 0.0 end
+ set pln 0
+ set allmsg ""
+ while {[gets $ef line] > -1} {
+ add_log "$line"
+ set allmsg "$allmsg\n$line"
+ if {[string first "spin: line" $line] >= 0} {
+ scan $line "spin: line %d" pln
+ src_line $pln
+ }
+ if {[string first "spin: warning, line" $line] >= 0} {
+ scan $line "spin: warning, line %d" pln
+ src_line $pln
+ }
+ incr cnt
+ }
+ close $ef
+ if {$cnt == 0} { add_log "no syntax errors" } else {
+ warner $T "$allmsg" 800
+ }
+ update
+}
+
+proc prescan {} {
+ global an_typ cp_typ nv_typ po_typ
+ global xu_typ as_typ ie_typ
+
+ mkpan_in
+
+ if {$an_typ == -1} {
+ set an_typ 0
+ set nv_typ [hasWord "never"]
+ if {[hasWord "accept.*:"]} {
+ set an_typ 1
+ set cp_typ 2
+ } elseif {[hasWord "progress.*:"]} {
+ set an_typ 1
+ set cp_typ 1
+ }
+ }
+ if {$po_typ == -1} {
+ if {[hasWord "_last"] \
+ || [hasWord "provided.*\\("] \
+ || [hasWord "enabled\\("]} {
+ set po_typ 0
+ } else {
+ set po_typ 1
+ }
+ }
+ if {$xu_typ == -1} {
+ if {[hasWord "xr"] || [hasWord "xs"]} {
+ set xu_typ 1
+ } else {
+ set xu_typ 0
+ }
+ }
+ if {$as_typ == -1} {
+ if {$an_typ == 0} {
+ set as_typ [hasWord "assert"]
+ set ie_typ 1
+ } else {
+ set as_typ 0
+ set ie_typ 0
+ }
+ }
+}
+
+proc basicval2 {} {
+ global e ival expl HelvBig PlaceSim
+ global an_typ cp_typ nv_typ firstime
+ global cyc_typ ct_typ lt_typ
+ global et_typ st_typ se_typ bf_typ stop
+ global vb_typ pr_typ vr_typ ur_typ xu_typ
+
+ set nv_typ 1
+ set an_typ 1
+ set cp_typ 2
+
+ dump_tl "pan.ltl"
+
+ catch { .menu.run.m entryconfigure 8 -state normal }
+ catch { .tl.results.top.rv configure -state normal }
+ set stop 0
+ set firstime 0
+ set lt_typ 1
+
+ catch {destroy .v}
+ toplevel .v
+
+ set k [string first "\+" $PlaceSim]
+ if {$k > 0} {
+ set PlaceSim [string range $PlaceSim $k end]
+ }
+
+ wm title .v "LTL Verification Options"
+ wm iconname .v "VAL"
+ wm geometry .v $PlaceSim
+
+ prescan
+
+ frame .v.correct -relief flat -borderwidth 1m
+ frame .v.cframe -relief raised -borderwidth 1m
+
+ set z .v.correct
+
+ frame $z.rframe -relief raised -borderwidth 1m
+
+ label $z.rframe.lb \
+ -font $HelvBig \
+ -text "Options" \
+ -relief sunken -borderwidth 1m
+
+ checkbutton $z.rframe.fc -text "With Weak Fairness" \
+ -variable cyc_typ \
+ -relief flat
+ checkbutton $z.rframe.xu -text "Check xr/xs Assertions" \
+ -variable xu_typ \
+ -relief flat
+
+ pack append $z.rframe \
+ $z.rframe.lb {top pady 4 frame w fillx} \
+ $z.rframe.fc {top pady 4 frame w} \
+ $z.rframe.xu {top pady 4 frame w filly}
+
+ pack append $z $z.rframe {top frame nw filly}
+
+ label .v.cframe.lb \
+ -font $HelvBig \
+ -text "Verification" \
+ -relief sunken -borderwidth 1m
+
+ radiobutton .v.cframe.ea -text "Exhaustive" \
+ -variable ct_typ -value 0 \
+ -relief flat
+ radiobutton .v.cframe.sa -text "Supertrace/Bitstate" \
+ -variable ct_typ -value 1 \
+ -relief flat
+ radiobutton .v.cframe.hc -text "Hash-Compact" \
+ -variable ct_typ -value 2 \
+ -relief flat
+
+ pack append .v.cframe .v.cframe.lb {top pady 4 frame nw fillx} \
+ .v.cframe.ea {top pady 4 frame nw} \
+ .v.cframe.sa {top pady 4 frame nw} \
+ .v.cframe.hc {top pady 4 frame nw}
+
+ frame .v.pf -relief raised -borderwidth 1m
+ frame .v.pf.mesg -borderwidth 1m
+
+ label .v.pf.mesg.loss0 \
+ -font $HelvBig \
+ -text "A Full Queue" \
+ -relief sunken -borderwidth 1m
+ radiobutton .v.pf.mesg.loss1 -text "Blocks New Msgs" \
+ -variable l_typ -value 0 \
+ -relief flat
+ radiobutton .v.pf.mesg.loss2 -text "Loses New Msgs" \
+ -variable l_typ -value 1 \
+ -relief flat
+ pack append .v.pf.mesg \
+ .v.pf.mesg.loss0 {top pady 4 frame w expand fillx} \
+ .v.pf.mesg.loss1 {top pady 4 frame w} \
+ .v.pf.mesg.loss2 {top pady 4 frame w}
+ pack append .v.pf \
+ .v.pf.mesg {left frame w expand fillx}
+
+ pack append .v \
+ .v.cframe {top frame w fill}\
+ .v.correct {top frame w fill}\
+ .v.pf {top frame w expand fill}
+
+ pack append .v [button .v.adv -text "\[Set Advanced Options]" \
+ -command "advanced_val" ] {top fillx}
+ pack append .v [button .v.run -text "Run" \
+ -command {runval ".tl.results.t"} ] {right frame se}
+ pack append .v [button .v.exit -text "Cancel" \
+ -command "set PlaceSim [wm geometry .v]; \
+ set stop 1; destroy .v"] {right frame se}
+ pack append .v [button .v.help -text "Help" -fg red \
+ -command "roadmap2f" ] {right frame se}
+
+ tkwait visibility .v
+ raise .v
+}
+
+set PlaceBasic "+200+10"
+set PlaceAdv "+150+10"
+
+proc basicval {} {
+ global e ival expl HelvBig PlaceBasic
+ global an_typ nv_typ firstime as_typ ie_typ
+ global cyc_typ ct_typ lt_typ as_typ ie_typ
+ global et_typ st_typ se_typ bf_typ stop
+ global vb_typ pr_typ vr_typ ur_typ xu_typ
+
+ catch { .menu.run.m entryconfigure 8 -state normal }
+ catch { .tl.results.top.rv configure -state normal }
+ set stop 0
+ set firstime 0
+ set lt_typ 0
+
+ catch {destroy .v}
+ toplevel .v
+
+ wm title .v "Basic Verification Options"
+ wm iconname .v "VAL"
+ wm geometry .v $PlaceBasic
+
+ prescan
+
+ frame .v.correct -relief flat -borderwidth 1m
+ frame .v.cframe -relief raised -borderwidth 1m
+
+ set z .v.correct
+
+ frame $z.rframe -relief raised -borderwidth 1m
+
+ label $z.rframe.lb \
+ -font $HelvBig \
+ -text "Correctness Properties" \
+ -relief sunken -borderwidth 1m
+ radiobutton $z.rframe.sp -text "Safety (state properties)" \
+ -variable an_typ -value 0 \
+ -relief flat \
+ -command { set cyc_typ 0; set cp_typ 0
+ if {$an_typ == 0} {
+ set as_typ [hasWord "assert"]
+ set ie_typ 1
+ }
+ }
+ frame $z.rframe.sf
+ checkbutton $z.rframe.sf.as -text "Assertions" \
+ -variable as_typ \
+ -relief flat \
+ -command {
+ set an_typ 0
+ if {![hasWord "assert"] && $as_typ==1} then {
+ complain6
+ }
+ }
+ checkbutton $z.rframe.sf.ie -text "Invalid Endstates" \
+ -variable ie_typ \
+ -relief flat \
+ -command { set an_typ 0 }
+
+ frame $z.rframe.sf.fill -width 15
+ pack append $z.rframe.sf \
+ $z.rframe.sf.fill {left frame w} \
+ $z.rframe.sf.as {top pady 4 frame w} \
+ $z.rframe.sf.ie {top pady 4 frame w}
+
+ radiobutton $z.rframe.cp -text "Liveness (cycles/sequences)" \
+ -variable an_typ -value 1 \
+ -relief flat \
+ -command {
+ set as_typ 0; set ie_typ 0
+ if {[hasWord "accept"]} then { set cp_typ 2 }\
+ elseif {[hasWord "progress"]} then { set cp_typ 1 } \
+ else complain5
+ }
+
+ frame $z.rframe.sub
+ radiobutton $z.rframe.sub.np -text "Non-Progress Cycles" \
+ -variable cp_typ -value 1 \
+ -relief flat \
+ -command {
+ set an_typ 1
+ if {![hasWord "progress"] && $cp_typ==1} then {
+ complain4
+ }
+ }
+ radiobutton $z.rframe.sub.ac -text "Acceptance Cycles" \
+ -variable cp_typ -value 2 \
+ -relief flat \
+ -command {
+ set an_typ 1
+ if {![hasWord "accept"] && $cp_typ==2} then {
+ complain1
+ }
+ }
+ checkbutton $z.rframe.sub.fc -text "With Weak Fairness" \
+ -variable cyc_typ \
+ -relief flat \
+ -command { if {$an_typ==0} then "set cyc_typ 0; complain3" }
+
+ checkbutton $z.rframe.nv -text "Apply Never Claim (If Present)" \
+ -variable nv_typ \
+ -relief flat \
+ -command { if {![hasWord "never"] && $nv_typ==1} then "complain2" }
+ checkbutton $z.rframe.ur -text "Report Unreachable Code" \
+ -variable ur_typ \
+ -relief flat
+ checkbutton $z.rframe.xu -text "Check xr/xs Assertions" \
+ -variable xu_typ \
+ -relief flat
+
+ frame $z.rframe.sub.fill -width 15
+ pack append $z.rframe.sub \
+ $z.rframe.sub.fill {left frame w} \
+ $z.rframe.sub.np {top pady 4 frame w} \
+ $z.rframe.sub.ac {top pady 4 frame w} \
+ $z.rframe.sub.fc {top pady 4 frame w}
+
+ pack append $z.rframe \
+ $z.rframe.lb {top pady 4 frame w fillx} \
+ $z.rframe.sp {top pady 4 frame w} \
+ $z.rframe.sf {top pady 4 frame w} \
+ $z.rframe.cp {top pady 4 frame w} \
+ $z.rframe.sub {top pady 4 frame w} \
+ $z.rframe.nv {top pady 4 frame w} \
+ $z.rframe.ur {top pady 4 frame w} \
+ $z.rframe.xu {top pady 4 frame w filly}
+
+ pack append $z $z.rframe {top frame nw filly}
+
+ label .v.cframe.lb \
+ -font $HelvBig \
+ -text "Search Mode" \
+ -relief sunken -borderwidth 1m
+
+ radiobutton .v.cframe.ea -text "Exhaustive" \
+ -variable ct_typ -value 0 \
+ -relief flat
+ radiobutton .v.cframe.sa -text "Supertrace/Bitstate" \
+ -variable ct_typ -value 1 \
+ -relief flat
+ radiobutton .v.cframe.hc -text "Hash-Compact" \
+ -variable ct_typ -value 2 \
+ -relief flat
+
+ pack append .v.cframe .v.cframe.lb {top pady 4 frame nw fillx} \
+ .v.cframe.ea {top pady 4 frame nw} \
+ .v.cframe.sa {top pady 4 frame nw} \
+ .v.cframe.hc {top pady 4 frame nw}
+
+ frame .v.pf -relief raised -borderwidth 1m
+ frame .v.pf.mesg -borderwidth 1m
+
+ label .v.pf.mesg.loss0 \
+ -font $HelvBig \
+ -text "A Full Queue" \
+ -relief sunken -borderwidth 1m
+ radiobutton .v.pf.mesg.loss1 -text "Blocks New Msgs" \
+ -variable l_typ -value 0 \
+ -relief flat
+ radiobutton .v.pf.mesg.loss2 -text "Loses New Msgs" \
+ -variable l_typ -value 1 \
+ -relief flat
+ pack append .v.pf.mesg \
+ .v.pf.mesg.loss0 {top pady 4 frame w fillx} \
+ .v.pf.mesg.loss1 {top pady 4 frame w} \
+ .v.pf.mesg.loss2 {top pady 4 frame w}
+ pack append .v.pf \
+ .v.pf.mesg {left frame nw expand fill}
+
+ pack append .v \
+ .v.correct {left} \
+ .v.cframe {top frame n expand fill}\
+ .v.pf {top frame n expand fill}
+
+ pack append .v [button .v.nvr -text "\[Add Never Claim from File]" \
+ -command "call_nvr" ] {top fillx}
+ pack append .v [button .v.ltl -text "\[Verify an LTL Property]" \
+ -command "destroy .v; call_tl" ] {top fillx}
+ pack append .v [button .v.adv -text "\[Set Advanced Options]" \
+ -command "advanced_val" ] {top fillx}
+ pack append .v [button .v.run -text "Run" \
+ -command {set PlaceBasic [wm geometry .v]; runval "0"} ] {right frame se}
+ pack append .v [button .v.exit -text "Cancel" \
+ -command {set PlaceBasic [wm geometry .v]; \
+ set stop 1; destroy .v}] {right frame se}
+ pack append .v [button .v.help -text "Help" -fg red \
+ -command "roadmap2e" ] {right frame se}
+
+ tkwait visibility .v
+ raise .v
+}
+
+set HasNever ""
+
+proc call_nvr {} {
+ global HasNever
+ global xversion Fname nv_typ
+
+ switch [info commands tk_getOpenFile] "" {
+ set fileselect "FileSelect open"
+ } default {
+ set fileselect "tk_getOpenFile \
+ -filetypes { \
+ { { Aut files } .aut } \
+ { { Nvr files } .nvr } \
+ { { All Files } * } \
+ }"
+ }
+
+ set HasNever [eval $fileselect]
+
+ if {$HasNever == ""} {
+ wm title . "SPIN CONTROL $xversion -- File: $Fname"
+ set nv_typ 0
+ } else {
+ set nv_typ 1
+ set z [string last "\/" $HasNever]
+ if {$z > 0} {
+ incr z
+ set HsN [string range $HasNever $z end]
+ } else {
+ set HsN $HasNever
+ }
+ wm title . "SPIN CONTROL $xversion -- File: $Fname Claim: $HsN"
+ }
+}
+
+proc advanced_val {} {
+ global e ival expl HelvBig
+ global nv_typ firstime PlaceAdv
+ global cyc_typ ct_typ
+ global et_typ st_typ se_typ bf_typ stop po_typ cm_typ
+ global vb_typ pr_typ vr_typ ur_typ xu_typ
+
+ catch { .menu.run.m entryconfigure 8 -state normal }
+ catch { .tl.results.top.rv configure -state normal }
+ set stop 0
+ set firstime 0
+
+ catch {destroy .av}
+ toplevel .av
+
+ wm title .av "Advanced Verification Options"
+ wm iconname .av "VAL"
+ wm geometry .av $PlaceAdv
+
+ frame .av.pans
+ frame .av.pans.correct -relief flat
+ frame .av.memopts -relief flat; # memory options panel
+ frame .av.oframe -relief raised -borderwidth 1m ;# error trail options
+ frame .av.recomp -relief raised -borderwidth 1m ;# recompilation
+
+ prescan
+
+ for {set x 0} {$x<=2} {incr x} {
+ frame .av.memopts.choice$x -relief flat
+ entry .av.memopts.choice$x.e1 -relief sunken -width 20
+ label .av.memopts.choice$x.e2 -text $e($x) -relief flat
+ .av.memopts.choice$x.e1 insert end $ival($x)
+
+ pack append .av.memopts.choice$x \
+ .av.memopts.choice$x.e2 {left frame w fillx} \
+ [button .av.memopts.choice$x.e3 -text $expl($x) \
+ -command "d_list $x" ] {right frame e} \
+ .av.memopts.choice$x.e1 {right frame e fillx}
+
+ pack append .av.memopts \
+ .av.memopts.choice$x { top frame w pady 5 fillx}
+ }
+ for {set x 7} {$x<=7} {incr x} {
+ frame .av.memopts.choice$x -relief flat
+ entry .av.memopts.choice$x.e1 -relief sunken -width 20
+ label .av.memopts.choice$x.e2 -text $e($x) -relief flat
+ .av.memopts.choice$x.e1 insert end $ival($x)
+
+ pack append .av.memopts.choice$x \
+ .av.memopts.choice$x.e2 {left frame w fillx} \
+ [button .av.memopts.choice$x.e3 -text $expl($x) \
+ -command "d_list $x" ] {right frame e} \
+ .av.memopts.choice$x.e1 {right frame e fillx}
+
+ pack append .av.memopts \
+ .av.memopts.choice$x { top frame w pady 5 fillx}
+ }
+ for {set x 3} {$x<=5} {incr x} {
+ frame .av.memopts.choice$x -relief flat
+ entry .av.memopts.choice$x.e1 -relief sunken -width 20
+ label .av.memopts.choice$x.e2 -text $e($x) -relief flat
+ .av.memopts.choice$x.e1 insert end $ival($x)
+
+ pack append .av.memopts.choice$x \
+ .av.memopts.choice$x.e2 {left frame w fillx} \
+ [button .av.memopts.choice$x.e3 -text $expl($x) \
+ -command "d_list $x" ] {right frame e} \
+ .av.memopts.choice$x.e1 {right frame e fillx}
+
+ pack append .av.memopts \
+ .av.memopts.choice$x { top frame w pady 5 fillx}
+ }
+ set z .av.pans.correct
+ frame $z.rframe -relief raised -borderwidth 1m
+ label $z.rframe.lb3 \
+ -font $HelvBig \
+ -text " Error Trapping " \
+ -relief sunken -borderwidth 1m
+ radiobutton $z.rframe.c0 -text "Don't Stop at Errors" \
+ -variable et_typ -value 0 \
+ -relief flat
+ checkbutton $z.rframe.c0a -text "Save All Error-trails" \
+ -variable st_typ \
+ -relief flat
+ checkbutton $z.rframe.c0b -text "Find Shortest Trail (iterative)" \
+ -variable se_typ \
+ -relief flat
+ checkbutton $z.rframe.c0c -text "Use Breadth-First Search" \
+ -variable bf_typ \
+ -relief flat
+ frame $z.rframe.basic1
+
+ frame $z.rframe.cc
+ radiobutton $z.rframe.cc.c1 -text "Stop at Error Nr:" \
+ -variable et_typ -value 1 \
+ -relief flat
+ entry $z.rframe.cc.c2 -relief sunken -width 8
+ $z.rframe.cc.c2 insert end "$ival(6)"
+ pack append $z.rframe.cc \
+ $z.rframe.cc.c1 {left}\
+ $z.rframe.cc.c2 {right expand fillx}
+
+ pack append $z.rframe \
+ $z.rframe.lb3 { top expand fillx frame nw} \
+ $z.rframe.cc {top pady 4 frame w} \
+ $z.rframe.c0 {top pady 4 frame w} \
+ $z.rframe.c0a {top pady 4 frame w} \
+ $z.rframe.c0b {top pady 4 frame w} \
+ $z.rframe.c0c {top pady 4 frame w} \
+ $z.rframe.basic1 {top frame w}
+ pack append $z $z.rframe {top frame nw expand fill}
+
+ frame .av.pans.pf -relief flat
+ set z .av.pans.pf
+ frame $z.mesg -relief raised -borderwidth 1m; # queue loss options
+ frame $z.pframe -relief raised -borderwidth 1m
+ label $z.pframe.lb2 \
+ -font $HelvBig \
+ -text " Type of Run " \
+ -relief sunken -borderwidth 1m
+ checkbutton $z.pframe.po -text "Use Partial Order Reduction" \
+ -variable po_typ \
+ -relief flat
+ checkbutton $z.pframe.cm -text "Use Compression" \
+ -variable cm_typ \
+ -relief flat
+# checkbutton $z.pframe.vb -text "Verbose (For Debugging Only)" \
+# -variable vb_typ \
+# -relief flat
+ checkbutton $z.pframe.pr -text "Add Complexity Profiling" \
+ -variable pr_typ \
+ -relief flat
+ checkbutton $z.pframe.vr -text "Compute Variable Ranges" \
+ -variable vr_typ \
+ -relief flat
+
+ pack append $z.pframe \
+ $z.pframe.lb2 {top fillx pady 4 frame w} \
+ $z.pframe.po {top pady 4 frame w} \
+ $z.pframe.cm {top pady 4 frame w} \
+ $z.pframe.pr {top pady 4 frame w} \
+ $z.pframe.vr {top pady 4 frame w}
+
+ pack append .av.pans.pf \
+ .av.pans.pf.pframe {top frame nw expand fill}
+ pack append .av.pans \
+ .av.pans.correct {left frame nw expand fillx}\
+ .av.pans.pf {left frame nw expand fillx}
+
+ button .av.help -text "Help" -fg red -command "roadmap2d"
+ button .av.basic1 -text "Set" -fg red -command "stopval 1"
+ button .av.basic0 -text "Cancel" -command "stopval 0"
+
+ pack append .av \
+ .av.memopts {top frame w} \
+ .av.pans {top fillx} \
+ .av.help {left frame w} \
+ .av.basic1 {right frame e} \
+ .av.basic0 {right frame e}
+
+ raise .av
+}
+
+proc g_list {} {
+ global FG BG
+
+ catch {destroy .r}
+ toplevel .r
+
+ wm title .r "Options"
+ wm iconname .r "Options"
+
+ frame .r.top
+ listbox .r.top.list -width 6 -height 3 -relief raised
+ listbox .r.top.expl -width 40 -height 3 -relief flat
+ pack append .r.top \
+ .r.top.list {left}\
+ .r.top.expl {left}
+ frame .r.bot
+ text .r.bot.text -width 40 -height 1 -relief flat
+ .r.bot.text insert end "(Double-Click option or Cancel)"
+ button .r.bot.quit -text "Cancel" -command "destroy .r"
+ pack append .r.bot \
+ .r.bot.text {top}\
+ .r.bot.quit {frame s}
+
+ frame .r.caps
+ text .r.caps.cap1 -width 6 -height -1 -fg blue
+ text .r.caps.cap2 -width 30 -height -1 -fg blue
+ .r.caps.cap1 insert end "Option:"
+ .r.caps.cap2 insert end "Meaning:"
+ pack append .r.caps \
+ .r.caps.cap1 {left} \
+ .r.caps.cap2 {left}
+
+ pack append .r \
+ .r.caps {frame w}\
+ .r.top {top expand} \
+ .r.bot {bottom expand}
+
+ foreach i { "-o1" "-o2" "-o3" } {
+ .r.top.list insert end $i
+ }
+
+ foreach i { \
+ "disable dataflow-optimizations" \
+ "disable dead variables elimination" \
+ "disable statement merging" } {
+ .r.top.expl insert end $i
+ }
+ bind .r.top.list <Double-Button-1> {
+ set extra [selection get]
+ .av.memopts.choice5.e1 insert end " $extra"
+ destroy .r
+ }
+}
+
+proc r_list {} {
+ global FG BG
+
+ catch {destroy .r}
+ toplevel .r
+
+ wm title .r "Options"
+ wm iconname .r "Options"
+
+ frame .r.top
+ listbox .r.top.list -width 6 -height 8 -relief raised
+ listbox .r.top.expl -width 40 -height 8 -relief flat
+ pack append .r.top \
+ .r.top.list {left}\
+ .r.top.expl {left}
+ frame .r.bot
+ text .r.bot.text -width 40 -height 1 -relief flat
+ .r.bot.text insert end "(Double-Click option or Cancel)"
+ button .r.bot.quit -text "Cancel" -command "destroy .r"
+ pack append .r.bot \
+ .r.bot.text {top}\
+ .r.bot.quit {frame s}
+
+ frame .r.caps
+ text .r.caps.cap1 -width 6 -height -1 -fg blue
+ text .r.caps.cap2 -width 30 -height -1 -fg blue
+ .r.caps.cap1 insert end "Option:"
+ .r.caps.cap2 insert end "Meaning:"
+ pack append .r.caps \
+ .r.caps.cap1 {left} \
+ .r.caps.cap2 {left}
+
+ pack append .r \
+ .r.caps {frame w}\
+ .r.top {top expand} \
+ .r.bot {bottom expand}
+
+ foreach i { "-d" "-q" "-I" "-h?" "-s" "-A" "-E" "-w?" } {
+ .r.top.list insert end $i
+ }
+
+ foreach i { \
+ "print state tables and stop" \
+ "require all chans to be empty in valid endstates" \
+ "try to find shortest trail" \
+ "choose another seed for hash 1..32 (default 1)" \
+ "use 1-bit hashing (default is 2-bit)" \
+ "ignore assertion violation errors" \
+ "ignore invalid endstate errors" \
+ "set explicit -w parameter" \
+ "" } {
+ .r.top.expl insert end $i
+ }
+ bind .r.top.list <Double-Button-1> {
+ set extra [selection get]
+ .av.memopts.choice4.e1 insert end " $extra"
+ destroy .r
+ }
+}
+
+proc d_list {nr} {
+
+ if {$nr == 0} { roadmap2a; return }
+ if {$nr == 1} { roadmap2b; return }
+ if {$nr == 2} { roadmap2c; return }
+ if {$nr == 4} { r_list; return }
+ if {$nr == 5} { g_list; return }
+ if {$nr == 7} { roadmap2k; return }
+# if {$nr != 3} { roadmap2; return }
+
+ catch {destroy .b}
+ toplevel .b
+
+ wm title .b "Options"
+ wm iconname .b "Options"
+
+ frame .b.top
+ scrollbar .b.top.scroll -command ".b.top.list yview"
+ listbox .b.top.list -yscroll ".b.top.scroll set" -relief raised
+ pack append .b.top \
+ .b.top.scroll {right filly}\
+ .b.top.list {left expand}
+
+ frame .b.bot
+ text .b.bot.text -width 21 -height 1 -relief flat
+ .b.bot.text insert end "(Double-Click option)"
+ button .b.bot.quit -text "Cancel" -command "destroy .b"
+ button .b.bot.expl -text "Explanations" -command "roadmap6"
+ pack append .b.bot \
+ .b.bot.text {top frame nw}\
+ .b.bot.expl {left}\
+ .b.bot.quit {left}
+
+
+ pack append .b \
+ .b.top {top frame nw expand} \
+ .b.bot {bottom}
+
+ foreach i { \
+ "-DBCOMP" \
+ "-DCTL" \
+ "-DGLOB_ALPHA" \
+ "-DMA=?" \
+ "-DNFAIR=?" \
+ "-DNIBIS" \
+ "-DNOBOUNDCHECK" \
+ "-DNOREDUCE" \
+ "-DNOCOMP" \
+ "-DNOSTUTTER" \
+ "-DNOVSZ" \
+ "-DPRINTF" \
+ "-DRANDSTORE=?" \
+ "-DR_XPT" \
+ "-DSDUMP" \
+ "-DSVDUMP" \
+ "-DVECTORSZ=?" \
+ "-DW_XPT=?" \
+ "-DXUSAFE" \
+ } {
+ .b.top.list insert end $i
+ }
+ bind .b.top.list <Double-Button-1> {
+ set directive [selection get]
+ .av.memopts.choice3.e1 insert end " $directive"
+ destroy .b
+ }
+}
+
+proc complain1 {} {
+ set m "warning: there are no accept labels"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain2 {} {
+ set m "warning: there is no never claim"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain3 {} {
+ set m "weak fairness is irrelevant to state properties"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain4 {} {
+ set m "warning: there are no progress labels"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain5 {} {
+ global an_typ
+ set m "warning: there are neither accept nor progress labels"
+ add_log $m
+ set an_typ 0
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain6 {} {
+ set m "warning: there are no assert statements in the spec"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain7 {} {
+ set m "warning: Breadth-First Search implies a restriction to Safety properties"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc complain8 {} {
+ set m "error: cannot combine -DMA and -DBITSTATE"
+ add_log $m
+ catch { tk_messageBox -icon info -message $m }
+}
+
+proc stopval {how} {
+ global stop ival PlaceAdv
+
+ if {$how} {
+ set ival(0) "[.av.memopts.choice0.e1 get]"
+ set ival(1) "[.av.memopts.choice1.e1 get]"
+ set ival(2) "[.av.memopts.choice2.e1 get]"
+ set ival(3) "[.av.memopts.choice3.e1 get]"
+ set ival(4) "[.av.memopts.choice4.e1 get]"
+ set ival(5) "[.av.memopts.choice5.e1 get]"
+ set ival(7) "[.av.memopts.choice7.e1 get]"
+ set ival(6) "[.av.pans.correct.rframe.cc.c2 get]"
+ }
+ set stop 1
+ if {[winfo exists .av]} {
+ set PlaceAdv [wm geometry .av]
+ set k [string first "\+" $PlaceAdv]
+ if {$k > 0} {
+ set PlaceAdv [string range $PlaceAdv $k end]
+ }
+ destroy .av
+ }
+}
+
+proc log {n} {
+ set m 1
+ set cnt 0
+ while {$m<$n} {
+ set m [expr $m*2]
+ incr cnt
+ }
+ return $cnt
+}
+
+proc bld_v_options {} {
+ global an_typ cp_typ cyc_typ as_typ ie_typ
+ global et_typ st_typ se_typ l_typ bf_typ
+ global ct_typ ival v_options a_options
+ global c_options po_typ cm_typ vb_typ
+ global pr_typ vr_typ ur_typ xu_typ
+ global ol_typ oct_typ nv_typ lt_typ
+
+ set ol_typ $l_typ
+ set oct_typ $ct_typ
+
+ set a_options "-a -X"
+ if {$l_typ==1} { set a_options [format "%s -m" $a_options] }
+ if {$lt_typ==1} { set a_options [format "%s -N pan.ltl" $a_options] }
+
+ set Mbytes $ival(0)
+ catch { set Mbytes [.av.memopts.choice0.e1 get] }
+
+ # the Mstate scale resolution is in thousands: 2^10
+ set Mstate [expr 10+[log $ival(1)]]
+ catch { set Mstate [expr 10+[log [.av.memopts.choice1.e1 get]]] }
+ set Mdepth $ival(2)
+ catch { set Mdepth [.av.memopts.choice2.e1 get] }
+ catch { set ival(0) "[.av.memopts.choice0.e1 get]" }
+ catch { set ival(1) "[.av.memopts.choice1.e1 get]" }
+ catch { set ival(2) "[.av.memopts.choice2.e1 get]" }
+ catch { set ival(3) "[.av.memopts.choice3.e1 get]" }
+ catch { set ival(4) "[.av.memopts.choice4.e1 get]" }
+ catch { set ival(5) "[.av.memopts.choice5.e1 get]" }
+ catch { set ival(7) "[.av.memopts.choice7.e1 get]" }
+
+ if {$ct_typ == 2} { ;# hash-compact
+ set c_options [format "-DHC -DMEMLIM=%d" $Mbytes]
+
+ # in exhaustive mode: #hashtable ~~ #states
+
+ set v_options "-X -m$Mdepth -w$Mstate"
+
+ if {$Mstate >= $Mbytes} {
+ catch {
+ tk_messageBox -icon info \
+ -message "The Estimated Statespace size exceeds \
+the maximum that the Memory limit you set would allow."
+ }
+ return 0
+ }
+ } elseif {$ct_typ==1} {
+ set c_options [format "-DBITSTATE -DMEMLIM=%d" $Mbytes]
+
+ # in supertrace mode: #bits ~~ 128x #states
+ # (effectively the #bytes will be ~~ 16x #states)
+
+ set Mstate [expr 7+$Mstate]
+ set v_options "-X -m$Mdepth -w$Mstate"
+
+ if {$Mstate-3 >= $Mbytes} {
+ catch {
+ tk_messageBox -icon info \
+ -message "The Estimated Statespace size exceeds \
+maximum allowed by the Memory limit that you set would allow."
+ }
+ return 0
+ }
+ } else {
+ set c_options [format "-DMEMLIM=%d" $Mbytes]
+
+ # in exhaustive mode: #hashtable ~~ #states
+
+ set v_options "-X -m$Mdepth -w$Mstate"
+
+ if {$Mstate >= $Mbytes} {
+ catch {
+ tk_messageBox -icon info \
+ -message "The Estimated Statespace size exceeds \
+the maximum that the Physical Memory limit allows."
+ }
+ return 0
+ }
+ }
+ set c_options [format "-D_POSIX_SOURCE %s" $c_options]
+ if {$an_typ==0} { set c_options [format "%s -DSAFETY" $c_options] }
+ if {$an_typ==1 && $cp_typ==1} { set c_options [format "%s -DNP" $c_options] }
+ if {$po_typ==0} { set c_options [format "%s -DNOREDUCE" $c_options] }
+ if {$cm_typ==1 && $ct_typ!=1} { set c_options [format "%s -DCOLLAPSE" $c_options] }
+ if {$vb_typ==1} { set c_options [format "%s -DCHECK" $c_options] }
+ if {$nv_typ==0} { set c_options [format "%s -DNOCLAIM" $c_options] }
+ if {$se_typ!=0} { set c_options [format "%s -DREACH" $c_options] }
+ if {$bf_typ!=0} {
+ if {$an_typ != 0} {
+ complain7
+ set c_options [format "%s -DBFS -DSAFETY" $c_options]
+ } else {
+ set c_options [format "%s -DBFS" $c_options]
+ } }
+ if {$xu_typ==0 && $po_typ!=0} { set c_options [format "%s -DXUSAFE" $c_options] }
+ if {$pr_typ==1} { set c_options [format "%s -DPEG" $c_options] }
+ if {$vr_typ==1} { set c_options [format "%s -DVAR_RANGES" $c_options] }
+ if {$cyc_typ==1} {
+ set c_options [format "%s -DNFAIR=3" $c_options]
+ } else {
+ set c_options [format "%s -DNOFAIR" $c_options]
+ }
+ set foo $ival(3)
+ catch { set foo [.av.memopts.choice3.e1 get] }
+
+ if {[string first "-DBITSTATE" $c_options] > 0 && [string first "-DMA" $foo] > 0} {
+ complain8
+ }
+ set c_options [format "%s %s" $c_options $foo ]
+
+ set foo $ival(4)
+ catch { set foo [.av.memopts.choice4.e1 get] }
+ set v_options [format "%s %s" $v_options $foo ]
+
+ set foo $ival(5)
+ catch { set foo [.av.memopts.choice.5.e1 get] }
+ set a_options [format "%s %s" $a_options $foo ]
+
+ if {$an_typ==0 && $as_typ==0} { set v_options [format "%s -A" $v_options] }
+ if {$an_typ==0 && $ie_typ==0} { set v_options [format "%s -E" $v_options] }
+ if {$an_typ==1 && $cp_typ==1} { set v_options [format "%s -l" $v_options] }
+ if {$an_typ==1 && $cp_typ==2} { set v_options [format "%s -a" $v_options] }
+ if {$cyc_typ==1} { set v_options [format "%s -f" $v_options] }
+ if {$ur_typ==0} { set v_options [format "%s -n" $v_options] }
+ if {$st_typ==1} { set v_options [format "%s -e" $v_options] }
+ if {$se_typ!=0} { set v_options [format "%s -i" $v_options] }
+ if {$et_typ==0} { set v_options [format "%s -c0" $v_options] }
+ if {$et_typ==1} { set v_options [format "%s -c%s" $v_options $ival(6)] }
+ if {$ct_typ==1 && $ival(7) != 2} { set v_options [format "%s -k%s" $v_options $ival(7)] }
+ return 1
+}
+
+set mt 0
+set skipmax 10
+
+proc runval {havedest} {
+ global Unix CC SPIN Fname PlaceSim
+ global v_options a_options notignored
+ global c_options Job_Done mt skipmax
+ global stop s_typ vbox waitwhat not_warned_yet
+ global LastGenerate LastCompile NextCompile
+
+ set stop 0
+ if {[bld_v_options] == 0} {
+ advanced_val
+ return
+ }
+ if {[winfo exists .v]} {
+ set PlaceSim [wm geometry .v]
+ destroy .v
+ }
+ catch {destroy .av}
+ if {[string first "\?" $c_options] > 0} {
+ add_log "error: undefined '?' in optional compiler directives"
+ return
+ }
+ if {[string first "\?" $v_options] > 0} {
+ add_log "error: undefined '?' in extra runtime options"
+ return
+ }
+ add_log "<starting verification>"
+ if {$havedest != "0"} {
+ $havedest insert end "<starting verification>\n"
+ }
+
+ set nochange [no_change]
+ if {$nochange == 0} { set LastGenerate "" }
+
+ if {$LastGenerate == $a_options} {
+ add_log "<no code regeneration necessary>"
+ if {$havedest != "0"} {
+ $havedest insert end "<no code regeneration necessary>\n"
+ }
+ set errmsg 0
+ } else {
+ set LastCompile ""
+ add_log "$SPIN $a_options pan_in"; update
+ if {$havedest != "0"} {
+ $havedest insert end "$SPIN $a_options pan_in\n"
+ }
+ if {$Unix} {
+ catch {eval exec $SPIN $a_options pan_in} errmsg
+ } else {
+ catch {eval exec $SPIN $a_options pan_in >&pan.tmp}
+ set errmsg [msg_file pan.tmp 0]
+ }
+ if {[string length $errmsg]>0} {
+ set foo [string first "Exit-Status 0" $errmsg]
+ if {$foo<0} {
+ add_log "$errmsg"
+ if {$havedest != "0"} {
+ $havedest insert end "$errmsg\n"
+ }
+ update
+ return
+ }
+ incr foo -2
+ set errmsg [string range $errmsg 0 $foo]
+ add_log "$errmsg"
+ if {$havedest != "0"} {
+ $havedest insert end "$errmsg\n"
+ }
+ } }
+ set LastGenerate $a_options
+
+ set NextCompile "$CC -o pan $c_options pan.c"
+
+ if {$havedest != "0"} {
+ catch { $havedest yview -pickplace end }
+ }
+ if {$LastCompile == $NextCompile && [file exists pan]} {
+ add_log "<no recompilation necessary>"
+ if {$havedest != "0"} {
+ $havedest insert end "<no recompilation necessary>\n"
+ }
+ set errmsg 0
+ } else {
+ add_log $NextCompile
+ if {$havedest != "0"} {
+ $havedest insert end "$NextCompile\n"
+ }
+ catch {
+ warner "Compiling" "Please wait until compilation of the \
+executable produced by spin completes." 300
+ }
+ update
+ if {$Unix} {
+ rmfile "./pan"
+ catch {eval exec $NextCompile >pan.tmp 2>pan.err}
+ } else {
+ rmfile "pan.exe"
+ catch {eval exec $NextCompile >pan.tmp 2>pan.err}
+ }
+
+ set errmsg [msg_file pan.tmp 0]
+ if {[string length $errmsg] == 0} {
+ set errmsg [msg_file pan.err 0]
+ } else {
+ set errmsg "$errmsg\n[msg_file pan.err 0]"
+ }
+
+ catch {destroy .warn}
+
+ auto_reset
+ if {$Unix} {
+ if {[auto_execok "./pan"] == "" \
+ || [auto_execok "./pan"] == 0} {
+ add_log "$errmsg"
+ add_log "compilation failed"
+ if {$havedest != "0"} {
+ $havedest insert end "<compilation failed>\n"
+ }
+ update
+ return
+ }
+ } else {
+ if {[auto_execok "pan"] == "" \
+ || [auto_execok "pan"] == 0} {
+ add_log "$errmsg"
+ add_log "compilation failed"
+ if {$havedest != "0"} {
+ $havedest insert end "<compilation failed>\n"
+ }
+ update
+ return
+ } } }
+
+ set LastCompile $NextCompile
+ set NextCompile ""
+
+ if {$Unix} {
+ set PREFIX "time ./pan -v"
+ } else {
+ set PREFIX "pan -v"
+ }
+ add_log "$PREFIX $v_options"; update
+ if {$havedest != "0"} {
+ $havedest insert end "$PREFIX $v_options\n"
+ catch { $havedest yview -pickplace end }
+ }
+ set errmsg ""
+ update
+ rmfile pan.out
+ set errmsg [catch {eval exec $PREFIX $v_options >&pan.out &}]
+ if {$errmsg} {
+ add_log "$errmsg"
+ add_log "could not run verification"
+ if {$havedest != "0"} {
+ $havedest insert end "<could not run verification>\n"
+ }
+ update
+ return
+ }
+
+ set not_warned_yet 1
+ if {$havedest != "0"} {
+ set vbox $havedest
+ } else {
+ set vv [mkbox "Verification Output" "VerOut" "$Fname.out" 80 20]
+ set vbox .c$vv.z.t
+ }
+ update
+ set mt 0
+ after 5000 outcheck $vbox
+ update
+}
+
+proc outcheck {vbox} {
+ global stop where not_warned_yet runtime mt Unix Fname FG skipmax
+
+ set firstline 1
+ set have_trail 0
+ set po_red_viol 0
+
+ if {[winfo exists $vbox] == 0} {
+ add_log "<verification output panel $vbox was closed>"
+ return
+ }
+
+ set erline 0; set lnr 0
+ set nomem 0; set nerr 0
+
+ if {$stop == 0} {
+ catch { set nt [file mtime pan.out] }
+ if {$mt == $nt && $skipmax > 0} {
+ incr skipmax -1
+ } else {
+ set mt $nt
+ set skipmax 10
+ set fd [open pan.out r]
+ while {[gets $fd line] > -1} {
+ if {$firstline} {
+ set firstline 0
+ set nerr 0
+ set trunc 0
+ set nomem 0
+ .inp.t tag remove hilite 0.0 end
+ catch { $vbox delete 0.0 end }
+ set lnr 0
+ }
+ catch { $vbox insert end "$line\n" }
+ incr lnr
+ catch { $vbox yview -pickplace end }
+ update
+
+ if {[string first "line" $line]>=0} {
+ scan $line "\tline %d" where
+ catch { src_line $where }
+ }
+ if {[string first "State-vector" $line] == 0 \
+ || ([string first "error:" $line] == 0 \
+ && [string first "error: max search depth too small" $line] != 0)} {
+ set stop 1 ;# run must have completed
+ set nerr [string first "errors:" $line]
+ if {$nerr > 0} {
+ set part [string range $line $nerr end]
+ scan $part "errors: %d" nerr
+ if {$nerr == 0} {
+ catch { .tl.results.top.title configure\
+ -text "Verification Result: valid" -fg $FG
+ }
+ } else {
+ catch { .tl.results.top.title configure\
+ -text "Verification Result: not valid" -fg red
+ }
+ }
+ set erline $lnr
+ incr erline -1
+ }
+ }
+ if {[string first "search depth too small" $line]>0} {
+ set trunc 1
+ }
+ if {[string first "wrote pan_in.trail" $line]>0} {
+ set have_trail 1
+ }
+ if {[string first "violated: access to chan" $line]>0} {
+ set po_red_viol 1
+ }
+ if {[string first "out of memory" $line]>0} {
+ set nomem 1
+ }
+ if {[string first "A=atomic;" $line]>0} {
+ set stop 1
+ }
+ update
+ }
+ catch "close $fd"
+ } }
+ if {$nomem || $po_red_viol} { set erline 0 }
+
+ if {$stop || ($have_trail && $po_red_viol==0 && ($nerr>0 || $trunc>0))} {
+ catch { $vbox yview 0.0 }
+ add_log "<verification done>"
+ if {$nerr > 0 || $trunc == 1 || $nomem == 1} {
+ if {[file exists pan_in.trail]} {
+ cpfile pan_in.trail $Fname.trail
+ } elseif {[file exists pan_in.tra]} {
+ cpfile pan_in.tra $Fname.trail
+ }
+ catch { repeatbox $nerr $trunc $nomem }
+ }
+ } else {
+ if {$not_warned_yet} {
+ set runtime 0
+ set stop 0
+ set line "Running verification -- waiting for output"
+ catch { $vbox insert end "$line\n" }
+ set line "\t(kill background process 'pan' to abort run)"
+ catch { $vbox insert end "$line\n" }
+ if {$Unix == 0} {
+ set line "\t(use ctl-alt-del to kill pan)"
+ catch { $vbox insert end "$line\n" }
+ } else {
+ set line "\t(use /bin/ps to find pid; then: kill -2 pid)"
+ catch { $vbox insert end "$line\n" }
+ catch { $vbox yview -pickplace end }
+ }
+ set not_warned_yet 0
+ } else {
+ incr runtime 5
+ if {$stop} {
+ add_log "<done>"
+ return
+ } else {
+ if {$runtime%60 == 0} {
+ set rt [expr $runtime / 60]
+ add_log "verification now running for approx $rt min.."
+ }}
+ update
+ }
+ after 5000 outcheck $vbox
+ }
+}
+
+proc src_line {s} {
+ global FG BG
+ scan $s %d tgt_lnr
+
+ if {$tgt_lnr > 0} {
+ .inp.t tag add hilite $tgt_lnr.0 $tgt_lnr.end
+ .inp.t tag configure hilite -background $FG -foreground $BG
+ if {$tgt_lnr > 10} {
+ .inp.t yview -pickplace [expr $tgt_lnr-10]
+ } else {
+ .inp.t yview -pickplace [expr $tgt_lnr-1]
+ }
+ }
+}
+
+proc repeatbox { {nerr} {trunc} {nomem}} {
+ global s_typ PlaceWarn whichsim
+
+ if {$nerr <= 0 && $trunc <= 0 && $nomem <= 0} { return }
+
+ catch {destroy .rep}
+ toplevel .rep
+
+ wm title .rep "Suggested Action"
+ wm iconname .rep "Suggest"
+ wm geometry .rep $PlaceWarn
+ button .rep.b0 -text "Close" -command {destroy .rep}
+ button .rep.b1 -text "Run Guided Simulation.." \
+ -command {destroy .rep; Rewind }
+ button .rep.b2 -text "Setup Guided Simulation.." \
+ -command {destroy .rep; simulation }
+
+ if {$nerr>0} {
+ message .rep.t -width 500 -text "\
+Optionally, repeat the run with a different search\
+depth to find a shorter path to the error.
+
+Or, perform a GUIDED simulation to retrace\
+the error found in this run, and skip\
+the first series of steps if the error was\
+found at a depth greater than about 100 steps)."
+ set s_typ 1
+ set whichsim 0
+
+ pack append .rep .rep.t {top expand fill}
+ pack append .rep .rep.b0 {right}
+ pack append .rep .rep.b1 {right}
+ pack append .rep .rep.b2 {right}
+ } else {
+
+ if {$nomem>0} {
+ message .rep.t -width 500 -text "\
+Make sure the Physical Memory parameter (under Advanced\
+Options) is set to the maximum physical memory available.\
+If not, repeat the verification with the true maximum.\
+(Don't set it higher than the physical limit though.)\
+Otherwise, use compression, or switch to a\
+Supertrace Verification."
+ } else {
+
+ if {$trunc} {
+
+ message .rep.t -width 500 -text "\
+If the search was incomplete (truncated)\
+because the max search depth was too small,\
+try to increase the depth limit (it is set\
+in Advanced Options of the Verification Parameters\
+Panel), and repeat the verification."
+ }
+ }
+ pack append .rep .rep.t {top expand fill}
+ pack append .rep .rep.b0 {right}
+ }
+}
+
+# Main startup and arg processing
+# this is at the end - to make sure all
+# proc declarations were seen
+
+if {$argc == 1} {
+ set Fname "$argv"
+ wm title . "SPIN CONTROL $xversion -- File: $Fname"
+ cleanup 0
+ cpfile $argv.trail pan_in.trail
+ reopen
+
+ check_xsp $argv
+}
+
+focus .inp.t
+update
--- /dev/null
+
+// LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v1
+// Created for the Spin validator.
+// Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+// June 2008
+
+// TODO : create test cases that will generate an overflow on the offset and
+// counter type. Counter types smaller than a byte should be used.
+
+// Promela only has unsigned char, no signed char.
+// Because detection of difference < 0 depends on a signed type, but we want
+// compactness, check also for the values being higher than half of the unsigned
+// char range (and consider them negative). The model, by design, does not use
+// offsets or counts higher than 127 because we would then have to use a larger
+// type (short or int).
+#define HALF_UCHAR (255/2)
+
+// NUMPROCS 4 : causes event loss with some reader timings.
+// e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer
+#define NUMPROCS 4
+
+// NUMPROCS 3 : does not cause event loss because buffers are big enough.
+//#define NUMPROCS 3
+// e.g. 3 events, 1 switch, read 1 subbuffer
+
+#define NUMSWITCH 1
+#define BUFSIZE 4
+#define NR_SUBBUFS 2
+#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS)
+
+// Writer counters
+byte write_off = 0;
+byte commit_count[NR_SUBBUFS];
+
+// Reader counters
+byte read_off = 0;
+byte retrieve_count[NR_SUBBUFS];
+
+byte events_lost = 0;
+byte refcount = 0;
+
+bool deliver = 0;
+
+// buffer slot in-use bit. Detects racy use (more than a single process
+// accessing a slot at any given step).
+bool buffer_use[BUFSIZE];
+
+// Proceed to a sub-subber switch is needed.
+// Used in a periodical timer interrupt to fill and ship the current subbuffer
+// to the reader so we can guarantee a steady flow. If a subbuffer is
+// completely empty, don't switch.
+// Also used as "finalize" operation to complete the last subbuffer after
+// all writers have finished so the last subbuffer can be read by the reader.
+proctype switcher()
+{
+ byte prev_off, new_off, tmp_commit;
+ byte size;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE);
+ new_off = prev_off + size;
+ if
+ :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR)
+ || size == SUBBUF_SIZE ->
+ refcount = refcount - 1;
+ goto not_needed;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ }
+
+ atomic {
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver = 1
+ :: else -> skip
+ fi;
+ refcount = refcount - 1;
+ }
+not_needed:
+ skip;
+}
+
+// tracer
+// Writes 1 byte of information in the buffer at the current
+// "write_off" position and then increment the commit_count of the sub-buffer
+// the information has been written to.
+proctype tracer()
+{
+ byte size = 1;
+ byte prev_off, new_off, tmp_commit;
+ byte i, j;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ new_off = prev_off + size;
+ }
+ atomic {
+ if
+ :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR ->
+ goto lost
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ i = 0;
+ do
+ :: i < size ->
+ assert(buffer_use[(prev_off + i) % BUFSIZE] == 0);
+ buffer_use[(prev_off + i) % BUFSIZE] = 1;
+ i++
+ :: i >= size -> break
+ od;
+ }
+
+ // writing to buffer...
+
+ atomic {
+ i = 0;
+ do
+ :: i < size ->
+ buffer_use[(prev_off + i) % BUFSIZE] = 0;
+ i++
+ :: i >= size -> break
+ od;
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver = 1;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ goto end;
+lost:
+ events_lost++;
+end:
+ refcount = refcount - 1;
+ }
+}
+
+// reader
+// Read the information sub-buffer per sub-buffer when available.
+//
+// Reads the information as soon as it is ready, or may be delayed by
+// an asynchronous delivery. Being modeled as a process insures all cases
+// (scheduled very quickly or very late, causing event loss) are covered.
+// Only one reader per buffer (normally ensured by a mutex). This is modeled
+// by using a single reader process.
+proctype reader()
+{
+ byte i, j;
+ byte tmp_retrieve;
+ byte lwrite_off, lcommit_count;
+
+ do
+ :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0
+ && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR
+ && commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ - retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ == SUBBUF_SIZE ->
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ assert(buffer_use[(read_off + i) % BUFSIZE] == 0);
+ buffer_use[(read_off + i) % BUFSIZE] = 1;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ }
+ // reading from buffer...
+
+ // Since there is only one reader per buffer at any given time,
+ // we don't care about retrieve_count vs read_off ordering :
+ // combined use of retrieve_count and read_off are made atomic by a
+ // mutex.
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ buffer_use[(read_off + i) % BUFSIZE] = 0;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ tmp_retrieve = retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ + SUBBUF_SIZE;
+ retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE] = tmp_retrieve;
+ read_off = read_off + SUBBUF_SIZE;
+ }
+ :: read_off >= (NUMPROCS - events_lost) -> break;
+ od;
+}
+
+// Waits for all tracer and switcher processes to finish before finalizing
+// the buffer. Only after that will the reader be allowed to read the
+// last subbuffer.
+proctype cleaner()
+{
+ atomic {
+ do
+ :: refcount == 0 ->
+ refcount = refcount + 1;
+ run switcher(); // Finalize the last sub-buffer so it can be read.
+ break;
+ od;
+ }
+}
+
+init {
+ byte i = 0;
+ byte j = 0;
+ byte sum = 0;
+ byte commit_sum = 0;
+
+ atomic {
+ i = 0;
+ do
+ :: i < NR_SUBBUFS ->
+ commit_count[i] = 0;
+ retrieve_count[i] = 0;
+ i++
+ :: i >= NR_SUBBUFS -> break
+ od;
+ i = 0;
+ do
+ :: i < BUFSIZE ->
+ buffer_use[i] = 0;
+ i++
+ :: i >= BUFSIZE -> break
+ od;
+ run reader();
+ run cleaner();
+ i = 0;
+ do
+ :: i < NUMPROCS ->
+ refcount = refcount + 1;
+ run tracer();
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ i = 0;
+ do
+ :: i < NUMSWITCH ->
+ refcount = refcount + 1;
+ run switcher();
+ i++
+ :: i >= NUMSWITCH -> break
+ od;
+ }
+ // Assertions.
+ atomic {
+ // The writer head must always be superior or equal to the reader head.
+ assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR);
+ j = 0;
+ commit_sum = 0;
+ do
+ :: j < NR_SUBBUFS ->
+ commit_sum = commit_sum + commit_count[j];
+ // The commit count of a particular subbuffer must always be higher
+ // or equal to the retrieve_count of this subbuffer.
+ assert(commit_count[j] - retrieve_count[j] >= 0 &&
+ commit_count[j] - retrieve_count[j] < HALF_UCHAR);
+ j++
+ :: j >= NR_SUBBUFS -> break
+ od;
+ // The sum of all subbuffer commit counts must always be lower or equal
+ // to the writer head, because space must be reserved before it is
+ // written to and then committed.
+ assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR);
+
+ // If we have less writers than the buffer space available, we should
+ // not lose events
+ assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0);
+ }
+}
--- /dev/null
+//#define NUMPROCS 5
+#define NUMPROCS 4
+#define BUFSIZE 4
+#define NR_SUBBUFS 2
+#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS)
+
+byte write_off = 0;
+byte read_off = 0;
+byte events_lost = 0;
+byte deliver = 0; // Number of subbuffers delivered
+byte refcount = 0;
+
+byte commit_count[NR_SUBBUFS];
+
+proctype switcher()
+{
+ int prev_off, new_off, tmp_commit;
+ int size;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE);
+ new_off = prev_off + size;
+ if
+ :: new_off - read_off > BUFSIZE ->
+ goto not_needed;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ }
+
+ atomic {
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver++
+ :: else -> skip
+ fi;
+ }
+not_needed:
+ skip;
+}
+
+proctype tracer(byte size)
+{
+ int prev_off, new_off, tmp_commit;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ new_off = prev_off + size;
+ }
+ atomic {
+ if
+ :: new_off - read_off > BUFSIZE -> goto lost
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ }
+
+ atomic {
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver++
+ :: else -> skip
+ fi;
+ }
+ goto end;
+lost:
+ events_lost++;
+end:
+ refcount = refcount - 1;
+}
+
+proctype reader()
+{
+ //atomic {
+ // do
+ // :: (deliver == (NUMPROCS - events_lost) / SUBBUF_SIZE) -> break;
+ // od;
+ //}
+ // made atomic to use less memory. Not really true.
+ atomic {
+ do
+ :: write_off - read_off >= SUBBUF_SIZE && commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] % SUBBUF_SIZE == 0 ->
+ read_off = read_off + SUBBUF_SIZE;
+ :: read_off >= (NUMPROCS - events_lost) -> break;
+ od;
+ }
+}
+
+proctype cleaner()
+{
+ atomic {
+ do
+ :: refcount == 0 ->
+ run switcher();
+ break;
+ od;
+ }
+}
+
+init {
+ int i = 0;
+ int j = 0;
+ int sum = 0;
+ int commit_sum = 0;
+
+ atomic {
+ i = 0;
+ do
+ :: i < NR_SUBBUFS ->
+ commit_count[i] = 0;
+ i++
+ :: i >= NR_SUBBUFS -> break
+ od;
+ run reader();
+ run cleaner();
+ i = 0;
+ do
+ :: i < NUMPROCS ->
+ refcount = refcount + 1;
+ run tracer(1);
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ run switcher();
+ }
+ atomic {
+ assert(write_off - read_off >= 0);
+ j = 0;
+ commit_sum = 0;
+ do
+ :: j < NR_SUBBUFS ->
+ commit_sum = commit_sum + commit_count[j];
+ j++
+ :: j >= NR_SUBBUFS -> break
+ od;
+ assert(commit_sum <= write_off);
+ //assert(events_lost == 0);
+ }
+}
--- /dev/null
+
+// does not cause event loss
+//#define NUMPROCS 3
+// e.g. 3 events, 1 switch, read 1 subbuffer
+
+// causes event loss with some reader timings,
+// e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer
+#define NUMPROCS 4
+
+#define NUMSWITCH 1
+#define BUFSIZE 4
+#define NR_SUBBUFS 2
+#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS)
+
+// Writer counters
+byte write_off = 0;
+byte commit_count[NR_SUBBUFS];
+
+// Reader counters
+byte read_off = 0;
+byte retrieve_count[NR_SUBBUFS];
+
+byte events_lost = 0;
+byte refcount = 0;
+
+bool deliver = 0;
+
+// buffer slot in-use bit. Detects racy use (more than a single process
+// accessing a slot at any given step).
+bool buffer_use[BUFSIZE];
+
+// Proceed to a sub-subber switch is needed.
+// Used in a periodical timer interrupt to fill and ship the current subbuffer
+// to the reader so we can guarantee a steady flow.
+// Also used as "finalize" operation to complete the last subbuffer after
+// all writers have finished so the last subbuffer can be read by the reader.
+proctype switcher()
+{
+ byte prev_off, new_off, tmp_commit;
+ byte size;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE);
+ new_off = prev_off + size;
+ if
+ :: new_off - read_off > BUFSIZE ->
+ refcount = refcount - 1;
+ goto not_needed;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ }
+
+ atomic {
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver = 1
+ :: else -> skip
+ fi;
+ refcount = refcount - 1;
+ }
+not_needed:
+ skip;
+}
+
+// tracer
+// Writes "size" bytes of information in the buffer at the current
+// "write_off" position and then increment the commit_count of the sub-buffer
+// the information has been written to.
+proctype tracer(byte size)
+{
+ byte prev_off, new_off, tmp_commit;
+ byte i, j;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ new_off = prev_off + size;
+ }
+ atomic {
+ if
+ :: new_off - read_off > BUFSIZE -> goto lost
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ i = 0;
+ do
+ :: i < size ->
+ assert(buffer_use[(prev_off + i) % BUFSIZE] == 0);
+ buffer_use[(prev_off + i) % BUFSIZE] = 1;
+ i++
+ :: i >= size -> break
+ od;
+ }
+
+ // writing to buffer...
+
+ atomic {
+ i = 0;
+ do
+ :: i < size ->
+ buffer_use[(prev_off + i) % BUFSIZE] = 0;
+ i++
+ :: i >= size -> break
+ od;
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver = 1;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ goto end;
+lost:
+ events_lost++;
+end:
+ refcount = refcount - 1;
+ }
+}
+
+// reader
+// Read the information sub-buffer per sub-buffer when available.
+//
+// Reads the information as soon as it is ready, or may be delayed by
+// an asynchronous delivery. Being modeled as a process insures all cases
+// (scheduled very quickly or very late, causing event loss) are covered.
+// Only one reader per buffer (normally ensured by a mutex). This is modeled
+// by using a single reader process.
+proctype reader()
+{
+ byte i, j;
+ byte tmp_retrieve;
+ byte lwrite_off, lcommit_count;
+
+ do
+ :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0
+ && commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ - retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ == SUBBUF_SIZE ->
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ assert(buffer_use[(read_off + i) % BUFSIZE] == 0);
+ buffer_use[(read_off + i) % BUFSIZE] = 1;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ }
+ // reading from buffer...
+
+ // Since there is only one reader per buffer at any given time,
+ // we don't care about retrieve_count vs read_off ordering :
+ // combined use of retrieve_count and read_off are made atomic by a
+ // mutex.
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ buffer_use[(read_off + i) % BUFSIZE] = 0;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ tmp_retrieve = retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE]
+ + SUBBUF_SIZE;
+ retrieve_count[(read_off % BUFSIZE) / SUBBUF_SIZE] = tmp_retrieve;
+ read_off = read_off + SUBBUF_SIZE;
+ }
+ :: read_off >= (NUMPROCS - events_lost) -> break;
+ od;
+}
+
+// Waits for all tracer and switcher processes to finish before finalizing
+// the buffer. Only after that will the reader be allowed to read the
+// last subbuffer.
+proctype cleaner()
+{
+ atomic {
+ do
+ :: refcount == 0 ->
+ refcount = refcount + 1;
+ run switcher(); // Finalize the last sub-buffer so it can be read.
+ break;
+ od;
+ }
+}
+
+init {
+ byte i = 0;
+ byte j = 0;
+ byte sum = 0;
+ byte commit_sum = 0;
+
+ atomic {
+ i = 0;
+ do
+ :: i < NR_SUBBUFS ->
+ commit_count[i] = 0;
+ retrieve_count[i] = 0;
+ i++
+ :: i >= NR_SUBBUFS -> break
+ od;
+ i = 0;
+ do
+ :: i < BUFSIZE ->
+ buffer_use[i] = 0;
+ i++
+ :: i >= BUFSIZE -> break
+ od;
+ run reader();
+ run cleaner();
+ i = 0;
+ do
+ :: i < NUMPROCS ->
+ refcount = refcount + 1;
+ run tracer(1);
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ i = 0;
+ do
+ :: i < NUMSWITCH ->
+ refcount = refcount + 1;
+ run switcher();
+ i++
+ :: i >= NUMSWITCH -> break
+ od;
+ }
+ // Assertions.
+ atomic {
+ // The writer head must always be superior to the reader head.
+ assert(write_off - read_off >= 0);
+ j = 0;
+ commit_sum = 0;
+ do
+ :: j < NR_SUBBUFS ->
+ commit_sum = commit_sum + commit_count[j];
+ // The commit count of a particular subbuffer must always be higher
+ // or equal to the retrieve_count of this subbuffer.
+ assert(commit_count[j] >= retrieve_count[j]);
+ j++
+ :: j >= NR_SUBBUFS -> break
+ od;
+ // The sum of all subbuffer commit counts must always be lower or equal
+ // to the writer head, because space must be reserved before it is
+ // written to and then committed.
+ assert(commit_sum <= write_off);
+ j = 0;
+
+ // If we have less writers than the buffer space available, we should
+ // not lose events
+ assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0);
+ }
+}
--- /dev/null
+//#define NUMPROCS 5
+#define NUMPROCS 4
+#define BUFSIZE 4
+#define NR_SUBBUFS 2
+#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS)
+
+byte write_off = 0;
+byte read_off = 0;
+byte events_lost = 0;
+byte deliver = 0; // Number of subbuffers delivered
+byte refcount = 0;
+
+byte commit_count[NR_SUBBUFS];
+
+byte buffer_use_count[BUFSIZE];
+
+proctype switcher()
+{
+ int prev_off, new_off, tmp_commit;
+ int size;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE);
+ new_off = prev_off + size;
+ if
+ :: new_off - read_off > BUFSIZE ->
+ goto not_needed;
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ }
+
+ atomic {
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver++
+ :: else -> skip
+ fi;
+ }
+not_needed:
+ skip;
+}
+
+proctype tracer(byte size)
+{
+ int prev_off, new_off, tmp_commit;
+ int i;
+ int j;
+
+cmpxchg_loop:
+ atomic {
+ prev_off = write_off;
+ new_off = prev_off + size;
+ }
+ atomic {
+ if
+ :: new_off - read_off > BUFSIZE -> goto lost
+ :: else -> skip
+ fi;
+ }
+ atomic {
+ if
+ :: prev_off != write_off -> goto cmpxchg_loop
+ :: else -> write_off = new_off;
+ fi;
+ i = 0;
+ do
+ :: i < size ->
+ buffer_use_count[(prev_off + i) % BUFSIZE]
+ = buffer_use_count[(prev_off + i) % BUFSIZE] + 1;
+ i++
+ :: i >= size -> break
+ od;
+ }
+ do
+ :: j < BUFSIZE ->
+ assert(buffer_use_count[j] < 2);
+ j++
+ :: j >= BUFSIZE -> break
+ od;
+
+
+
+ // writing to buffer...
+
+ atomic {
+ i = 0;
+ do
+ :: i < size ->
+ buffer_use_count[(prev_off + i) % BUFSIZE]
+ = buffer_use_count[(prev_off + i) % BUFSIZE] - 1;
+ i++
+ :: i >= size -> break
+ od;
+ tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size;
+ commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit;
+ if
+ :: tmp_commit % SUBBUF_SIZE == 0 -> deliver++
+ :: else -> skip
+ fi;
+ }
+ goto end;
+lost:
+ events_lost++;
+end:
+ refcount = refcount - 1;
+}
+
+proctype reader()
+{
+ int i;
+ int j;
+ //atomic {
+ // do
+ // :: (deliver == (NUMPROCS - events_lost) / SUBBUF_SIZE) -> break;
+ // od;
+ //}
+ do
+ :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 && commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] % SUBBUF_SIZE == 0 ->
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ buffer_use_count[(read_off + i) % BUFSIZE]
+ = buffer_use_count[(read_off + i) % BUFSIZE] + 1;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ }
+ j = 0;
+ do
+ :: j < BUFSIZE ->
+ assert(buffer_use_count[j] < 2);
+ j++
+ :: j >= BUFSIZE -> break
+ od;
+
+ // reading from buffer...
+
+ atomic {
+ i = 0;
+ do
+ :: i < SUBBUF_SIZE ->
+ buffer_use_count[(read_off + i) % BUFSIZE]
+ = buffer_use_count[(read_off + i) % BUFSIZE] - 1;
+ i++
+ :: i >= SUBBUF_SIZE -> break
+ od;
+ read_off = read_off + SUBBUF_SIZE;
+ }
+ :: read_off >= (NUMPROCS - events_lost) -> break;
+ od;
+}
+
+proctype cleaner()
+{
+ atomic {
+ do
+ :: refcount == 0 ->
+ run switcher();
+ break;
+ od;
+ }
+}
+
+init {
+ int i = 0;
+ int j = 0;
+ int sum = 0;
+ int commit_sum = 0;
+
+ atomic {
+ i = 0;
+ do
+ :: i < NR_SUBBUFS ->
+ commit_count[i] = 0;
+ i++
+ :: i >= NR_SUBBUFS -> break
+ od;
+ i = 0;
+ do
+ :: i < BUFSIZE ->
+ buffer_use_count[i] = 0;
+ i++
+ :: i >= BUFSIZE -> break
+ od;
+ run reader();
+ run cleaner();
+ i = 0;
+ do
+ :: i < NUMPROCS ->
+ refcount = refcount + 1;
+ run tracer(1);
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ run switcher();
+ }
+ atomic {
+ assert(write_off - read_off >= 0);
+ j = 0;
+ commit_sum = 0;
+ do
+ :: j < NR_SUBBUFS ->
+ commit_sum = commit_sum + commit_count[j];
+ j++
+ :: j >= NR_SUBBUFS -> break
+ od;
+ assert(commit_sum <= write_off);
+ j = 0;
+ do
+ :: j < BUFSIZE ->
+ assert(buffer_use_count[j] < 2);
+ j++
+ :: j >= BUFSIZE -> break
+ od;
+
+ //assert(events_lost == 0);
+ }
+}
--- /dev/null
+-4:-4:-4
+1:0:120
+2:0:121
+3:0:121
+4:0:125
+5:0:131
+6:0:131
+7:0:131
+8:0:131
+9:0:134
+10:0:139
+11:0:140
+12:0:142
+13:0:144
+14:0:142
+15:0:144
+16:0:142
+17:0:144
+18:0:146
+19:0:152
+20:0:154
+21:0:156
+22:0:160
+23:6:0
+24:6:6
+25:6:7
+26:6:13
+27:6:14
+28:6:18
+29:6:20
+30:6:28
+31:6:29
+32:5:30
+33:5:35
+34:5:36
+35:5:42
+36:5:43
+37:5:47
+38:5:51
+39:5:55
+40:5:57
+41:5:58
+42:5:61
+43:5:70
+44:5:71
+45:5:75
+46:5:77
+47:5:79
+48:4:30
+49:4:35
+50:4:36
+51:4:42
+52:4:43
+53:4:47
+54:4:51
+55:4:55
+56:4:57
+57:4:58
+58:4:61
+59:4:68
+60:4:75
+61:4:77
+62:4:79
+63:3:30
+64:3:33
+65:3:76
+66:3:77
+67:3:79
+68:2:111
+69:2:113
+70:2:117
+71:3:0
+72:3:3
+73:3:28
+74:3:29
+75:2:119
+76:1:80
+77:1:81
+78:1:82
+79:1:82
+80:1:86
+81:1:90
+82:1:92
+83:1:93
+84:1:93
+85:1:96
+86:1:80
+87:1:81
+88:1:82
+89:1:82
+90:1:86
+91:1:90
+92:1:92
+93:1:93
+94:1:93
+95:1:96
+96:1:105
+97:1:110
+98:0:162
+99:0:165
+100:0:165
+101:0:169
--- /dev/null
+ switch (t->back) {
+ default: Uerror("bad return move");
+ case 0: goto R999; /* nothing to undo */
+
+ /* PROC :init: */
+
+ case 3: /* STATE 1 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 4: /* STATE 5 */
+ ;
+ ((P4 *)this)->i = trpt->bup.ovals[2];
+ now.retrieve_count[ Index(((P4 *)this)->i, 2) ] = trpt->bup.ovals[1];
+ now.commit_count[ Index(((P4 *)this)->i, 2) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+
+ case 5: /* STATE 11 */
+ ;
+ ((P4 *)this)->i = trpt->bup.ovals[1];
+ /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 6: /* STATE 11 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 7: /* STATE 14 */
+ ;
+ ((P4 *)this)->i = trpt->bup.ovals[1];
+ now.buffer_use[ Index(((P4 *)this)->i, 4) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 8: /* STATE 15 */
+ ;
+ /* 0 */ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+
+ case 9: /* STATE 20 */
+ ;
+ ;
+ delproc(0, now._nr_pr-1);
+ ;
+ goto R999;
+
+ case 10: /* STATE 22 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ delproc(0, now._nr_pr-1);
+ ;
+ goto R999;
+
+ case 11: /* STATE 24 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 12: /* STATE 26 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ delproc(0, now._nr_pr-1);
+ ;
+ goto R999;
+
+ case 13: /* STATE 32 */
+ ;
+ ((P4 *)this)->i = trpt->bup.ovals[1];
+ /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 14: /* STATE 32 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 15: /* STATE 34 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 16: /* STATE 36 */
+ ;
+ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ delproc(0, now._nr_pr-1);
+ ;
+ goto R999;
+
+ case 17: /* STATE 37 */
+ ;
+ /* 0 */ ((P4 *)this)->i = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+
+ case 18: /* STATE 45 */
+ ;
+ ((P4 *)this)->commit_sum = trpt->bup.ovals[1];
+ ((P4 *)this)->j = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 19: /* STATE 49 */
+ ;
+ ((P4 *)this)->j = trpt->bup.ovals[1];
+ ((P4 *)this)->commit_sum = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 20: /* STATE 50 */
+ ;
+ /* 0 */ ((P4 *)this)->j = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+;
+
+ case 21: /* STATE 55 */
+ goto R999;
+
+ case 22: /* STATE 58 */
+ ;
+ p_restor(II);
+ ;
+ ;
+ goto R999;
+
+ /* PROC cleaner */
+
+ case 23: /* STATE 2 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 24: /* STATE 3 */
+ ;
+ ;
+ delproc(0, now._nr_pr-1);
+ ;
+ goto R999;
+
+ case 25: /* STATE 9 */
+ ;
+ p_restor(II);
+ ;
+ ;
+ goto R999;
+
+ /* PROC reader */
+;
+ ;
+
+ case 27: /* STATE 2 */
+ ;
+ ((P2 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 28: /* STATE 6 */
+ ;
+ ((P2 *)this)->i = trpt->bup.ovals[1];
+ now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 29: /* STATE 7 */
+ ;
+ /* 0 */ ((P2 *)this)->i = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+
+ case 30: /* STATE 16 */
+ ;
+ ((P2 *)this)->i = trpt->bup.ovals[1];
+ now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 31: /* STATE 24 */
+ ;
+ now.read_off = trpt->bup.ovals[3];
+ now.retrieve_count[ Index(((now.read_off%4)/(4/2)), 2) ] = trpt->bup.ovals[2];
+ ((P2 *)this)->tmp_retrieve = trpt->bup.ovals[1];
+ /* 0 */ ((P2 *)this)->i = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 4);
+ goto R999;
+
+ case 32: /* STATE 24 */
+ ;
+ now.read_off = trpt->bup.ovals[2];
+ now.retrieve_count[ Index(((now.read_off%4)/(4/2)), 2) ] = trpt->bup.ovals[1];
+ ((P2 *)this)->tmp_retrieve = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+;
+ ;
+
+ case 34: /* STATE 31 */
+ ;
+ p_restor(II);
+ ;
+ ;
+ goto R999;
+
+ /* PROC tracer */
+
+ case 35: /* STATE 2 */
+ ;
+ ((P1 *)this)->new_off = trpt->bup.ovals[1];
+ ((P1 *)this)->prev_off = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 36: /* STATE 4 */
+ ;
+ /* 0 */ ((P1 *)this)->new_off = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+;
+
+ case 37: /* STATE 7 */
+ goto R999;
+
+ case 38: /* STATE 11 */
+ ;
+ /* 0 */ ((P1 *)this)->prev_off = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+
+ case 39: /* STATE 17 */
+ ;
+ ((P1 *)this)->i = trpt->bup.ovals[1];
+ now.write_off = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 40: /* STATE 17 */
+ ;
+ ((P1 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 41: /* STATE 21 */
+ ;
+ ((P1 *)this)->i = trpt->bup.ovals[1];
+ now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 42: /* STATE 22 */
+ ;
+ /* 0 */ ((P1 *)this)->i = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+
+ case 43: /* STATE 28 */
+ ;
+ ((P1 *)this)->i = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 44: /* STATE 31 */
+ ;
+ ((P1 *)this)->i = trpt->bup.ovals[1];
+ now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 45: /* STATE 38 */
+ ;
+ now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[2];
+ ((P1 *)this)->tmp_commit = trpt->bup.ovals[1];
+ /* 0 */ ((P1 *)this)->i = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+
+ case 46: /* STATE 38 */
+ ;
+ now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[1];
+ ((P1 *)this)->tmp_commit = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 47: /* STATE 40 */
+ ;
+ deliver = trpt->bup.ovals[1];
+ /* 0 */ ((P1 *)this)->tmp_commit = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+;
+
+ case 48: /* STATE 44 */
+ goto R999;
+;
+
+ case 49: /* STATE 42 */
+ goto R999;
+
+ case 50: /* STATE 47 */
+ ;
+ now.events_lost = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 51: /* STATE 48 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 52: /* STATE 50 */
+ ;
+ p_restor(II);
+ ;
+ ;
+ goto R999;
+
+ /* PROC switcher */
+
+ case 53: /* STATE 3 */
+ ;
+ ((P0 *)this)->new_off = trpt->bup.ovals[2];
+ ((P0 *)this)->size = trpt->bup.ovals[1];
+ ((P0 *)this)->prev_off = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+
+ case 54: /* STATE 5 */
+ ;
+ now.refcount = trpt->bup.ovals[2];
+ /* 1 */ ((P0 *)this)->size = trpt->bup.ovals[1];
+ /* 0 */ ((P0 *)this)->new_off = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+;
+
+ case 55: /* STATE 8 */
+ goto R999;
+
+ case 56: /* STATE 12 */
+ ;
+ /* 0 */ ((P0 *)this)->prev_off = trpt->bup.oval;
+ ;
+ ;
+ goto R999;
+;
+
+ case 57: /* STATE 17 */
+ goto R999;
+
+ case 58: /* STATE 15 */
+ ;
+ now.write_off = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 59: /* STATE 20 */
+ ;
+ now.commit_count[ Index(((((P0 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[1];
+ ((P0 *)this)->tmp_commit = trpt->bup.ovals[0];
+ ;
+ ungrab_ints(trpt->bup.ovals, 2);
+ goto R999;
+
+ case 60: /* STATE 27 */
+ ;
+ now.refcount = trpt->bup.ovals[2];
+ deliver = trpt->bup.ovals[1];
+ /* 0 */ ((P0 *)this)->tmp_commit = trpt->bup.ovals[0];
+ ;
+ ;
+ ungrab_ints(trpt->bup.ovals, 3);
+ goto R999;
+
+ case 61: /* STATE 27 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 62: /* STATE 27 */
+ ;
+ now.refcount = trpt->bup.oval;
+ ;
+ goto R999;
+
+ case 63: /* STATE 30 */
+ ;
+ p_restor(II);
+ ;
+ ;
+ goto R999;
+ }
+
--- /dev/null
+/*** Generated by Spin Version 5.1.6 -- 9 May 2008 ***/
+/*** From source: buffer.spin ***/
+
+#ifdef SC
+#define _FILE_OFFSET_BITS 64
+#endif
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#if defined(WIN32) || defined(WIN64)
+#include <time.h>
+#else
+#include <unistd.h>
+#include <sys/times.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#define Offsetof(X, Y) ((unsigned long)(&(((X *)0)->Y)))
+#ifndef max
+#define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif
+#ifndef PRINTF
+int Printf(const char *fmt, ...); /* prototype only */
+#endif
+#include "pan.h"
+#ifdef LOOPSTATE
+double cnt_loops;
+#endif
+State A_Root; /* seed-state for cycles */
+State now; /* the full state-vector */
+#undef C_States
+#if defined(C_States) && defined(HAS_TRACK)
+void
+c_update(uchar *p_t_r)
+{
+#ifdef VERBOSE
+ printf("c_update %u\n", p_t_r);
+#endif
+}
+void
+c_revert(uchar *p_t_r)
+{
+#ifdef VERBOSE
+ printf("c_revert %u\n", p_t_r);
+#endif
+}
+#endif
+void
+globinit(void)
+{
+}
+void
+locinit4(int h)
+{
+}
+void
+locinit3(int h)
+{
+}
+void
+locinit2(int h)
+{
+}
+void
+locinit1(int h)
+{
+}
+void
+locinit0(int h)
+{
+}
+#ifdef CNTRSTACK
+#define onstack_now() (LL[trpt->j6] && LL[trpt->j7])
+#define onstack_put() LL[trpt->j6]++; LL[trpt->j7]++
+#define onstack_zap() LL[trpt->j6]--; LL[trpt->j7]--
+#endif
+#if !defined(SAFETY) && !defined(NOCOMP)
+#define V_A (((now._a_t&1)?2:1) << (now._a_t&2))
+#define A_V (((now._a_t&1)?1:2) << (now._a_t&2))
+int S_A = 0;
+#else
+#define V_A 0
+#define A_V 0
+#define S_A 0
+#endif
+#ifdef MA
+#undef onstack_now
+#undef onstack_put
+#undef onstack_zap
+#define onstack_put() ;
+#define onstack_zap() gstore((char *) &now, vsize, 4)
+#else
+#if defined(FULLSTACK) && !defined(BITSTATE)
+#define onstack_put() trpt->ostate = Lstate
+#define onstack_zap() { \
+ if (trpt->ostate) \
+ trpt->ostate->tagged = \
+ (S_A)? (trpt->ostate->tagged&~V_A) : 0; \
+ }
+#endif
+#endif
+#ifndef NO_V_PROVISO
+#define V_PROVISO
+#endif
+#if !defined(NO_RESIZE) && !defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(SPACE) && NCORE==1
+ #define AUTO_RESIZE
+#endif
+
+struct H_el {
+ struct H_el *nxt;
+#ifdef FULLSTACK
+ unsigned int tagged;
+ #if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY)
+ unsigned int proviso;
+ #endif
+#endif
+#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK))
+ unsigned long st_id;
+#endif
+#if !defined(SAFETY) || defined(REACH)
+ unsigned int D;
+#endif
+#if NCORE>1
+ /* could cost 1 extra word: 4 bytes if 32-bit and 8 bytes if 64-bit */
+ #ifdef V_PROVISO
+ uchar cpu_id; /* id of cpu that created the state */
+ #endif
+#endif
+#ifdef COLLAPSE
+ #if VECTORSZ<65536
+ unsigned short ln;
+ #else
+ unsigned long ln;
+ #endif
+#endif
+#if defined(AUTO_RESIZE) && !defined(BITSTATE)
+ unsigned long m_K1;
+#endif
+ unsigned long state;
+} **H_tab, **S_Tab;
+
+typedef struct Trail {
+ int st; /* current state */
+ uchar pr; /* process id */
+ uchar tau; /* 8 bit-flags */
+ uchar o_pm; /* 8 more bit-flags */
+#if 0
+ Meaning of bit-flags:
+ tau&1 -> timeout enabled
+ tau&2 -> request to enable timeout 1 level up (in claim)
+ tau&4 -> current transition is a claim move
+ tau&8 -> current transition is an atomic move
+ tau&16 -> last move was truncated on stack
+ tau&32 -> current transition is a preselected move
+ tau&64 -> at least one next state is not on the stack
+ tau&128 -> current transition is a stutter move
+ o_pm&1 -> the current pid moved -- implements else
+ o_pm&2 -> this is an acceptance state
+ o_pm&4 -> this is a progress state
+ o_pm&8 -> fairness alg rule 1 undo mark
+ o_pm&16 -> fairness alg rule 3 undo mark
+ o_pm&32 -> fairness alg rule 2 undo mark
+ o_pm&64 -> the current proc applied rule2
+ o_pm&128 -> a fairness, dummy move - all procs blocked
+#endif
+#ifdef NSUCC
+ uchar n_succ; /* nr of successor states */
+#endif
+#if defined(FULLSTACK) && defined(MA) && !defined(BFS)
+ uchar proviso;
+#endif
+#ifndef BFS
+ uchar o_n, o_ot; /* to save locals */
+#endif
+ uchar o_m;
+#ifdef EVENT_TRACE
+#if nstates_event<256
+ uchar o_event;
+#else
+ unsigned short o_event;
+#endif
+#endif
+ int o_tt;
+#ifndef BFS
+ short o_To;
+#ifdef RANDOMIZE
+ short oo_i;
+#endif
+#endif
+#if defined(HAS_UNLESS) && !defined(BFS)
+ int e_state; /* if escape trans - state of origin */
+#endif
+#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS) || (NCORE>1)
+ struct H_el *ostate; /* pointer to stored state */
+#endif
+#if defined(CNTRSTACK) && !defined(BFS)
+ long j6, j7;
+#endif
+ Trans *o_t;
+#ifdef SCHED
+ /* based on Qadeer&Rehof, Tacas 2005, LNCS 3440, pp. 93-107 */
+ #if NCORE>1
+ #error "-DSCHED cannot be combined with -DNCORE (yet)"
+ #endif
+ int sched_limit;
+#endif
+#ifdef HAS_SORTED
+ short ipt;
+#endif
+ union {
+ int oval;
+ int *ovals;
+ } bup;
+} Trail;
+Trail *trail, *trpt;
+FILE *efd;
+uchar *this;
+long maxdepth=10000;
+long omaxdepth=10000;
+#ifdef SCHED
+int sched_max = 10;
+#endif
+#ifdef PERMUTED
+ uchar permuted = 1;
+#else
+ uchar permuted = 0;
+#endif
+double quota; /* time limit */
+#if NCORE>1
+long z_handoff = -1;
+#endif
+#ifdef SC
+char *stackfile;
+#endif
+uchar *SS, *LL;
+uchar HASH_NR = 0;
+
+double memcnt = (double) 0;
+double memlim = (double) (1<<30); /* 1 GB */
+#if NCORE>1
+double mem_reserved = (double) 0;
+#endif
+
+/* for emalloc: */
+static char *have;
+static long left = 0L;
+static double fragment = (double) 0;
+static unsigned long grow;
+
+unsigned int HASH_CONST[] = {
+ /* asuming 4 bytes per int */
+ 0x88888EEF, 0x00400007,
+ 0x04c11db7, 0x100d4e63,
+ 0x0fc22f87, 0x3ff0c3ff,
+ 0x38e84cd7, 0x02b148e9,
+ 0x98b2e49d, 0xb616d379,
+ 0xa5247fd9, 0xbae92a15,
+ 0xb91c8bc5, 0x8e5880f3,
+ 0xacd7c069, 0xb4c44bb3,
+ 0x2ead1fb7, 0x8e428171,
+ 0xdbebd459, 0x828ae611,
+ 0x6cb25933, 0x86cdd651,
+ 0x9e8f5f21, 0xd5f8d8e7,
+ 0x9c4e956f, 0xb5cf2c71,
+ 0x2e805a6d, 0x33fc3a55,
+ 0xaf203ed1, 0xe31f5909,
+ 0x5276db35, 0x0c565ef7,
+ 0x273d1aa5, 0x8923b1dd,
+ 0
+};
+#if NCORE>1
+extern int core_id;
+#endif
+long mreached=0;
+int done=0, errors=0, Nrun=1;
+int c_init_done=0;
+char *c_stack_start = (char *) 0;
+double nstates=0, nlinks=0, truncs=0, truncs2=0;
+double nlost=0, nShadow=0, hcmp=0, ngrabs=0;
+#if defined(ZAPH) && defined(BITSTATE)
+double zstates = 0;
+#endif
+int c_init_run;
+#ifdef BFS
+double midrv=0, failedrv=0, revrv=0;
+#endif
+unsigned long nr_states=0; /* nodes in DFA */
+long Fa=0, Fh=0, Zh=0, Zn=0;
+long PUT=0, PROBE=0, ZAPS=0;
+long Ccheck=0, Cholds=0;
+int a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0;
+#ifdef HAS_CODE
+int gui = 0, coltrace = 0, readtrail = 0;
+int whichtrail = 0, onlyproc = -1, silent = 0;
+#endif
+int state_tables=0, fairness=0, no_rck=0, Nr_Trails=0;
+char simvals[128];
+#ifndef INLINE
+int TstOnly=0;
+#endif
+unsigned long mask, nmask;
+#ifdef BITSTATE
+int ssize=23; /* 1 Mb */
+#else
+int ssize=19; /* 512K slots */
+#endif
+int hmax=0, svmax=0, smax=0;
+int Maxbody=0, XX;
+uchar *noptr; /* used by macro Pptr(x) */
+#ifdef VAR_RANGES
+void logval(char *, int);
+void dumpranges(void);
+#endif
+#ifdef MA
+#define INLINE_REV
+extern void dfa_init(unsigned short);
+extern int dfa_member(unsigned long);
+extern int dfa_store(uchar *);
+unsigned int maxgs = 0;
+#endif
+
+#ifdef ALIGNED
+ State comp_now __attribute__ ((aligned (8)));
+ /* gcc 64-bit aligned for Itanium2 systems */
+ /* MAJOR runtime penalty if not used on those systems */
+#else
+ State comp_now; /* compressed state vector */
+#endif
+
+State comp_msk;
+uchar *Mask = (uchar *) &comp_msk;
+#ifdef COLLAPSE
+State comp_tmp;
+static char *scratch = (char *) &comp_tmp;
+#endif
+Stack *stack; /* for queues, processes */
+Svtack *svtack; /* for old state vectors */
+#ifdef BITSTATE
+static unsigned int hfns = 3; /* new default */
+#endif
+static unsigned long j1;
+static unsigned long K1, K2;
+static unsigned long j2, j3, j4;
+#ifdef BITSTATE
+static long udmem;
+#endif
+static long A_depth = 0;
+long depth = 0;
+#if NCORE>1
+long nr_handoffs = 0;
+#endif
+static uchar warned = 0, iterative = 0, exclusive = 0, like_java = 0, every_error = 0;
+static uchar noasserts = 0, noends = 0, bounded = 0;
+#if SYNC>0 && ASYNC==0
+void set_recvs(void);
+int no_recvs(int);
+#endif
+#if SYNC
+#define IfNotBlocked if (boq != -1) continue;
+#define UnBlock boq = -1
+#else
+#define IfNotBlocked /* cannot block */
+#define UnBlock /* don't bother */
+#endif
+
+#ifdef BITSTATE
+int (*bstore)(char *, int);
+int bstore_reg(char *, int);
+int bstore_mod(char *, int);
+#endif
+void active_procs(void);
+void cleanup(void);
+void do_the_search(void);
+void find_shorter(int);
+void iniglobals(void);
+void stopped(int);
+void wrapup(void);
+int *grab_ints(int);
+void ungrab_ints(int *, int);
+#ifndef NOBOUNDCHECK
+#define Index(x, y) Boundcheck(x, y, II, tt, t)
+#else
+#define Index(x, y) x
+#endif
+short Air[] = { (short) Air0, (short) Air1, (short) Air2, (short) Air3, (short) Air4, (short) Air5 };
+int
+addproc(int n)
+{ int j, h = now._nr_pr;
+#ifndef NOCOMP
+ int k;
+#endif
+ uchar *o_this = this;
+
+#ifndef INLINE
+ if (TstOnly) return (h < MAXPROC);
+#endif
+#ifndef NOBOUNDCHECK
+/* redefine Index only within this procedure */
+#undef Index
+#define Index(x, y) Boundcheck(x, y, 0, 0, 0)
+#endif
+ if (h >= MAXPROC)
+ Uerror("too many processes");
+ switch (n) {
+ case 0: j = sizeof(P0); break;
+ case 1: j = sizeof(P1); break;
+ case 2: j = sizeof(P2); break;
+ case 3: j = sizeof(P3); break;
+ case 4: j = sizeof(P4); break;
+ case 5: j = sizeof(P5); break;
+ default: Uerror("bad proc - addproc");
+ }
+ if (vsize%WS)
+ proc_skip[h] = WS-(vsize%WS);
+ else
+ proc_skip[h] = 0;
+#ifndef NOCOMP
+ for (k = vsize + (int) proc_skip[h]; k > vsize; k--)
+ Mask[k-1] = 1; /* align */
+#endif
+ vsize += (int) proc_skip[h];
+ proc_offset[h] = vsize;
+#ifdef SVDUMP
+ if (vprefix > 0)
+ { int dummy = 0;
+ write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */
+ write(svfd, (uchar *) &h, sizeof(int));
+ write(svfd, (uchar *) &n, sizeof(int));
+#if VECTORSZ>32000
+ write(svfd, (uchar *) &proc_offset[h], sizeof(int));
+#else
+ write(svfd, (uchar *) &proc_offset[h], sizeof(short));
+#endif
+ write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */
+ }
+#endif
+ now._nr_pr += 1;
+ if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2))
+ { printf("pan: error: too many processes -- current");
+ printf(" max is %d procs (-DNFAIR=%d)\n",
+ (8*NFAIR)/2 - 2, NFAIR);
+ printf("\trecompile with -DNFAIR=%d\n",
+ NFAIR+1);
+ pan_exit(1);
+ }
+ vsize += j;
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+#ifndef NOCOMP
+ for (k = 1; k <= Air[n]; k++)
+ Mask[vsize - k] = 1; /* pad */
+ Mask[vsize-j] = 1; /* _pid */
+#endif
+ hmax = max(hmax, vsize);
+ if (vsize >= VECTORSZ)
+ { printf("pan: error, VECTORSZ too small, recompile pan.c");
+ printf(" with -DVECTORSZ=N with N>%d\n", (int) vsize);
+ Uerror("aborting");
+ }
+ memset((char *)pptr(h), 0, j);
+ this = pptr(h);
+ if (BASE > 0 && h > 0)
+ ((P0 *)this)->_pid = h-BASE;
+ else
+ ((P0 *)this)->_pid = h;
+ switch (n) {
+ case 5: /* np_ */
+ ((P5 *)pptr(h))->_t = 5;
+ ((P5 *)pptr(h))->_p = 0;
+ reached5[0] = 1;
+ accpstate[5][1] = 1;
+ break;
+ case 4: /* :init: */
+ ((P4 *)pptr(h))->_t = 4;
+ ((P4 *)pptr(h))->_p = 42; reached4[42]=1;
+ /* params: */
+ /* locals: */
+ ((P4 *)pptr(h))->i = 0;
+ ((P4 *)pptr(h))->j = 0;
+ ((P4 *)pptr(h))->sum = 0;
+ ((P4 *)pptr(h))->commit_sum = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((P4 *)pptr(h))->i);
+ logval(":init::j", ((P4 *)pptr(h))->j);
+ logval(":init::sum", ((P4 *)pptr(h))->sum);
+ logval(":init::commit_sum", ((P4 *)pptr(h))->commit_sum);
+#endif
+#ifdef HAS_CODE
+ locinit4(h);
+#endif
+ break;
+ case 3: /* cleaner */
+ ((P3 *)pptr(h))->_t = 3;
+ ((P3 *)pptr(h))->_p = 8; reached3[8]=1;
+ /* params: */
+ /* locals: */
+#ifdef VAR_RANGES
+#endif
+#ifdef HAS_CODE
+ locinit3(h);
+#endif
+ break;
+ case 2: /* reader */
+ ((P2 *)pptr(h))->_t = 2;
+ ((P2 *)pptr(h))->_p = 28; reached2[28]=1;
+ /* params: */
+ /* locals: */
+ ((P2 *)pptr(h))->i = 0;
+ ((P2 *)pptr(h))->j = 0;
+ ((P2 *)pptr(h))->tmp_retrieve = 0;
+ ((P2 *)pptr(h))->lwrite_off = 0;
+ ((P2 *)pptr(h))->lcommit_count = 0;
+#ifdef VAR_RANGES
+ logval("reader:i", ((P2 *)pptr(h))->i);
+ logval("reader:j", ((P2 *)pptr(h))->j);
+ logval("reader:tmp_retrieve", ((P2 *)pptr(h))->tmp_retrieve);
+ logval("reader:lwrite_off", ((P2 *)pptr(h))->lwrite_off);
+ logval("reader:lcommit_count", ((P2 *)pptr(h))->lcommit_count);
+#endif
+#ifdef HAS_CODE
+ locinit2(h);
+#endif
+ break;
+ case 1: /* tracer */
+ ((P1 *)pptr(h))->_t = 1;
+ ((P1 *)pptr(h))->_p = 3; reached1[3]=1;
+ /* params: */
+ /* locals: */
+ ((P1 *)pptr(h))->size = 1;
+ ((P1 *)pptr(h))->prev_off = 0;
+ ((P1 *)pptr(h))->new_off = 0;
+ ((P1 *)pptr(h))->tmp_commit = 0;
+ ((P1 *)pptr(h))->i = 0;
+ ((P1 *)pptr(h))->j = 0;
+#ifdef VAR_RANGES
+ logval("tracer:size", ((P1 *)pptr(h))->size);
+ logval("tracer:prev_off", ((P1 *)pptr(h))->prev_off);
+ logval("tracer:new_off", ((P1 *)pptr(h))->new_off);
+ logval("tracer:tmp_commit", ((P1 *)pptr(h))->tmp_commit);
+ logval("tracer:i", ((P1 *)pptr(h))->i);
+ logval("tracer:j", ((P1 *)pptr(h))->j);
+#endif
+#ifdef HAS_CODE
+ locinit1(h);
+#endif
+ break;
+ case 0: /* switcher */
+ ((P0 *)pptr(h))->_t = 0;
+ ((P0 *)pptr(h))->_p = 11; reached0[11]=1;
+ /* params: */
+ /* locals: */
+ ((P0 *)pptr(h))->prev_off = 0;
+ ((P0 *)pptr(h))->new_off = 0;
+ ((P0 *)pptr(h))->tmp_commit = 0;
+ ((P0 *)pptr(h))->size = 0;
+#ifdef VAR_RANGES
+ logval("switcher:prev_off", ((P0 *)pptr(h))->prev_off);
+ logval("switcher:new_off", ((P0 *)pptr(h))->new_off);
+ logval("switcher:tmp_commit", ((P0 *)pptr(h))->tmp_commit);
+ logval("switcher:size", ((P0 *)pptr(h))->size);
+#endif
+#ifdef HAS_CODE
+ locinit0(h);
+#endif
+ break;
+ }
+ this = o_this;
+ return h-BASE;
+#ifndef NOBOUNDCHECK
+#undef Index
+#define Index(x, y) Boundcheck(x, y, II, tt, t)
+#endif
+}
+
+#if defined(BITSTATE) && defined(COLLAPSE)
+/* just to allow compilation, to generate the error */
+long col_p(int i, char *z) { return 0; }
+long col_q(int i, char *z) { return 0; }
+#endif
+#ifndef BITSTATE
+#ifdef COLLAPSE
+long
+col_p(int i, char *z)
+{ int j, k; unsigned long ordinal(char *, long, short);
+ char *x, *y;
+ P0 *ptr = (P0 *) pptr(i);
+ switch (ptr->_t) {
+ case 0: j = sizeof(P0); break;
+ case 1: j = sizeof(P1); break;
+ case 2: j = sizeof(P2); break;
+ case 3: j = sizeof(P3); break;
+ case 4: j = sizeof(P4); break;
+ case 5: j = sizeof(P5); break;
+ default: Uerror("bad proctype - collapse");
+ }
+ if (z) x = z; else x = scratch;
+ y = (char *) ptr; k = proc_offset[i];
+ for ( ; j > 0; j--, y++)
+ if (!Mask[k++]) *x++ = *y;
+ for (j = 0; j < WS-1; j++)
+ *x++ = 0;
+ x -= j;
+ if (z) return (long) (x - z);
+ return ordinal(scratch, x-scratch, (short) (2+ptr->_t));
+}
+#endif
+#endif
+void
+run(void)
+{ /* int i; */
+ memset((char *)&now, 0, sizeof(State));
+ vsize = (unsigned long) (sizeof(State) - VECTORSZ);
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+/* optional provisioning statements, e.g. to */
+/* set hidden variables, used as constants */
+#ifdef PROV
+#include PROV
+#endif
+ settable();
+ Maxbody = max(Maxbody, ((int) sizeof(P0)));
+ Maxbody = max(Maxbody, ((int) sizeof(P1)));
+ Maxbody = max(Maxbody, ((int) sizeof(P2)));
+ Maxbody = max(Maxbody, ((int) sizeof(P3)));
+ Maxbody = max(Maxbody, ((int) sizeof(P4)));
+ Maxbody = max(Maxbody, ((int) sizeof(P5)));
+ reached[0] = reached0;
+ reached[1] = reached1;
+ reached[2] = reached2;
+ reached[3] = reached3;
+ reached[4] = reached4;
+ reached[5] = reached5;
+ accpstate[0] = (uchar *) emalloc(nstates0);
+ accpstate[1] = (uchar *) emalloc(nstates1);
+ accpstate[2] = (uchar *) emalloc(nstates2);
+ accpstate[3] = (uchar *) emalloc(nstates3);
+ accpstate[4] = (uchar *) emalloc(nstates4);
+ accpstate[5] = (uchar *) emalloc(nstates5);
+ progstate[0] = (uchar *) emalloc(nstates0);
+ progstate[1] = (uchar *) emalloc(nstates1);
+ progstate[2] = (uchar *) emalloc(nstates2);
+ progstate[3] = (uchar *) emalloc(nstates3);
+ progstate[4] = (uchar *) emalloc(nstates4);
+ progstate[5] = (uchar *) emalloc(nstates5);
+ loopstate0 = loopstate[0] = (uchar *) emalloc(nstates0);
+ loopstate1 = loopstate[1] = (uchar *) emalloc(nstates1);
+ loopstate2 = loopstate[2] = (uchar *) emalloc(nstates2);
+ loopstate3 = loopstate[3] = (uchar *) emalloc(nstates3);
+ loopstate4 = loopstate[4] = (uchar *) emalloc(nstates4);
+ loopstate5 = loopstate[5] = (uchar *) emalloc(nstates5);
+ stopstate[0] = (uchar *) emalloc(nstates0);
+ stopstate[1] = (uchar *) emalloc(nstates1);
+ stopstate[2] = (uchar *) emalloc(nstates2);
+ stopstate[3] = (uchar *) emalloc(nstates3);
+ stopstate[4] = (uchar *) emalloc(nstates4);
+ stopstate[5] = (uchar *) emalloc(nstates5);
+ visstate[0] = (uchar *) emalloc(nstates0);
+ visstate[1] = (uchar *) emalloc(nstates1);
+ visstate[2] = (uchar *) emalloc(nstates2);
+ visstate[3] = (uchar *) emalloc(nstates3);
+ visstate[4] = (uchar *) emalloc(nstates4);
+ visstate[5] = (uchar *) emalloc(nstates5);
+ mapstate[0] = (short *) emalloc(nstates0 * sizeof(short));
+ mapstate[1] = (short *) emalloc(nstates1 * sizeof(short));
+ mapstate[2] = (short *) emalloc(nstates2 * sizeof(short));
+ mapstate[3] = (short *) emalloc(nstates3 * sizeof(short));
+ mapstate[4] = (short *) emalloc(nstates4 * sizeof(short));
+ mapstate[5] = (short *) emalloc(nstates5 * sizeof(short));
+#ifdef HAS_CODE
+#ifdef HAS_CODE
+#ifdef HAS_CODE
+#ifdef HAS_CODE
+#ifdef HAS_CODE
+#ifdef HAS_CODE
+ NrStates[0] = nstates0;
+ NrStates[1] = nstates1;
+ NrStates[2] = nstates2;
+ NrStates[3] = nstates3;
+ NrStates[4] = nstates4;
+ NrStates[5] = nstates5;
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+ stopstate[0][endstate0] = 1;
+ stopstate[1][endstate1] = 1;
+ stopstate[2][endstate2] = 1;
+ stopstate[3][endstate3] = 1;
+ stopstate[4][endstate4] = 1;
+ stopstate[5][endstate5] = 1;
+ stopstate[1][48] = 1;
+ retrans(0, nstates0, start0, src_ln0, reached0, loopstate0);
+ retrans(1, nstates1, start1, src_ln1, reached1, loopstate1);
+ retrans(2, nstates2, start2, src_ln2, reached2, loopstate2);
+ retrans(3, nstates3, start3, src_ln3, reached3, loopstate3);
+ retrans(4, nstates4, start4, src_ln4, reached4, loopstate4);
+ if (state_tables)
+ { printf("\nTransition Type: ");
+ printf("A=atomic; D=d_step; L=local; G=global\n");
+ printf("Source-State Labels: ");
+ printf("p=progress; e=end; a=accept;\n");
+#ifdef MERGED
+ printf("Note: statement merging was used. Only the first\n");
+ printf(" stmnt executed in each merge sequence is shown\n");
+ printf(" (use spin -a -o3 to disable statement merging)\n");
+#endif
+ pan_exit(0);
+ }
+ iniglobals();
+#if defined(VERI) && !defined(NOREDUCE) && !defined(NP)
+ if (!state_tables
+#ifdef HAS_CODE
+ && !readtrail
+#endif
+#if NCORE>1
+ && core_id == 0
+#endif
+ )
+ { printf("warning: for p.o. reduction to be valid ");
+ printf("the never claim must be stutter-invariant\n");
+ printf("(never claims generated from LTL ");
+ printf("formulae are stutter-invariant)\n");
+ }
+#endif
+ UnBlock; /* disable rendez-vous */
+#ifdef BITSTATE
+ if (udmem)
+ { udmem *= 1024L*1024L;
+ #if NCORE>1
+ if (!readtrail)
+ { void init_SS(unsigned long);
+ init_SS((unsigned long) udmem);
+ } else
+ #endif
+ SS = (uchar *) emalloc(udmem);
+ bstore = bstore_mod;
+ } else
+ #if NCORE>1
+ { void init_SS(unsigned long);
+ init_SS(ONE_L<<(ssize-3));
+ }
+ #else
+ SS = (uchar *) emalloc(ONE_L<<(ssize-3));
+ #endif
+#else
+ hinit();
+#endif
+#if defined(FULLSTACK) && defined(BITSTATE)
+ onstack_init();
+#endif
+#if defined(CNTRSTACK) && !defined(BFS)
+ LL = (uchar *) emalloc(ONE_L<<(ssize-3));
+#endif
+ stack = ( Stack *) emalloc(sizeof(Stack));
+ svtack = (Svtack *) emalloc(sizeof(Svtack));
+ /* a place to point for Pptr of non-running procs: */
+ noptr = (uchar *) emalloc(Maxbody * sizeof(char));
+#ifdef SVDUMP
+ if (vprefix > 0)
+ write(svfd, (uchar *) &vprefix, sizeof(int));
+#endif
+#ifdef VERI
+ Addproc(VERI); /* never - pid = 0 */
+#endif
+ active_procs(); /* started after never */
+#ifdef EVENT_TRACE
+ now._event = start_event;
+ reached[EVENT_TRACE][start_event] = 1;
+#endif
+#ifdef HAS_CODE
+ globinit();
+#endif
+#ifdef BITSTATE
+go_again:
+#endif
+ do_the_search();
+#ifdef BITSTATE
+ if (--Nrun > 0 && HASH_CONST[++HASH_NR])
+ { printf("Run %d:\n", HASH_NR);
+ wrap_stats();
+ printf("\n");
+ memset(SS, 0, ONE_L<<(ssize-3));
+#ifdef CNTRSTACK
+ memset(LL, 0, ONE_L<<(ssize-3));
+#endif
+#ifdef FULLSTACK
+ memset((uchar *) S_Tab, 0,
+ maxdepth*sizeof(struct H_el *));
+#endif
+ nstates=nlinks=truncs=truncs2=ngrabs = 0;
+ nlost=nShadow=hcmp = 0;
+ Fa=Fh=Zh=Zn = 0;
+ PUT=PROBE=ZAPS=Ccheck=Cholds = 0;
+ goto go_again;
+ }
+#endif
+}
+#ifdef HAS_PROVIDED
+int provided(int, uchar, int, Trans *);
+#endif
+#if NCORE>1
+#define GLOBAL_LOCK (0)
+#ifndef CS_N
+#define CS_N (256*NCORE)
+#endif
+#ifdef NGQ
+#define NR_QS (NCORE)
+#define CS_NR (CS_N+1) /* 2^N + 1, nr critical sections */
+#define GQ_RD GLOBAL_LOCK
+#define GQ_WR GLOBAL_LOCK
+#define CS_ID (1 + (int) (j1 & (CS_N-1))) /* mask: 2^N - 1, zero reserved */
+#define QLOCK(n) (1+n)
+#else
+#define NR_QS (NCORE+1)
+#define CS_NR (CS_N+3)
+#define GQ_RD (1)
+#define GQ_WR (2)
+#define CS_ID (3 + (int) (j1 & (CS_N-1)))
+#define QLOCK(n) (3+n)
+#endif
+
+void e_critical(int);
+void x_critical(int);
+
+#ifndef SEP_STATE
+ #define enter_critical(w) e_critical(w)
+ #define leave_critical(w) x_critical(w)
+#else
+ #ifdef NGQ
+ #define enter_critical(w) { if (w < 1+NCORE) e_critical(w); }
+ #define leave_critical(w) { if (w < 1+NCORE) x_critical(w); }
+ #else
+ #define enter_critical(w) { if (w < 3+NCORE) e_critical(w); }
+ #define leave_critical(w) { if (w < 3+NCORE) x_critical(w); }
+ #endif
+#endif
+
+int
+cpu_printf(const char *fmt, ...)
+{ va_list args;
+ enter_critical(GLOBAL_LOCK); /* printing */
+ printf("cpu%d: ", core_id);
+ fflush(stdout);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ fflush(stdout);
+ leave_critical(GLOBAL_LOCK);
+ return 1;
+}
+#else
+int
+cpu_printf(const char *fmt, ...)
+{ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ return 1;
+}
+#endif
+int
+Printf(const char *fmt, ...)
+{ /* Make sure the args to Printf
+ * are always evaluated (e.g., they
+ * could contain a run stmnt)
+ * but do not generate the output
+ * during verification runs
+ * unless explicitly wanted
+ * If this fails on your system
+ * compile SPIN itself -DPRINTF
+ * and this code is not generated
+ */
+#ifdef HAS_CODE
+ if (readtrail)
+ { va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ return 1;
+ }
+#endif
+#ifdef PRINTF
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+#endif
+ return 1;
+}
+extern void printm(int);
+#ifndef SC
+#define getframe(i) &trail[i];
+#else
+static long HHH, DDD, hiwater;
+static long CNT1, CNT2;
+static int stackwrite;
+static int stackread;
+static Trail frameptr;
+Trail *
+getframe(int d)
+{
+ if (CNT1 == CNT2)
+ return &trail[d];
+
+ if (d >= (CNT1-CNT2)*DDD)
+ return &trail[d - (CNT1-CNT2)*DDD];
+
+ if (!stackread
+ && (stackread = open(stackfile, 0)) < 0)
+ { printf("getframe: cannot open %s\n", stackfile);
+ wrapup();
+ }
+ if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1
+ || read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail))
+ { printf("getframe: frame read error\n");
+ wrapup();
+ }
+ return &frameptr;
+}
+#endif
+#if !defined(SAFETY) && !defined(BITSTATE)
+#if !defined(FULLSTACK) || defined(MA)
+#define depth_of(x) A_depth /* an estimate */
+#else
+int
+depth_of(struct H_el *s)
+{ Trail *t; int d;
+ for (d = 0; d <= A_depth; d++)
+ { t = getframe(d);
+ if (s == t->ostate)
+ return d;
+ }
+ printf("pan: cannot happen, depth_of\n");
+ return depthfound;
+}
+#endif
+#endif
+#if NCORE>1
+extern void cleanup_shm(int);
+volatile unsigned int *search_terminated; /* to signal early termination */
+#endif
+void
+pan_exit(int val)
+{ void stop_timer(void);
+ if (signoff)
+ { printf("--end of output--\n");
+ }
+#if NCORE>1
+ if (search_terminated != NULL)
+ { *search_terminated |= 1; /* pan_exit */
+ }
+#ifdef USE_DISK
+ { void dsk_stats(void);
+ dsk_stats();
+ }
+#endif
+ if (!state_tables && !readtrail)
+ { cleanup_shm(1);
+ }
+#endif
+ if (val == 2)
+ { val = 0;
+ } else
+ { stop_timer();
+ }
+ exit(val);
+}
+#ifdef HAS_CODE
+char *
+transmognify(char *s)
+{ char *v, *w;
+ static char buf[2][2048];
+ int i, toggle = 0;
+ if (!s || strlen(s) > 2047) return s;
+ memset(buf[0], 0, 2048);
+ memset(buf[1], 0, 2048);
+ strcpy(buf[toggle], s);
+ while ((v = strstr(buf[toggle], "{c_code")))
+ { *v = '\0'; v++;
+ strcpy(buf[1-toggle], buf[toggle]);
+ for (w = v; *w != '}' && *w != '\0'; w++) /* skip */;
+ if (*w != '}') return s;
+ *w = '\0'; w++;
+ for (i = 0; code_lookup[i].c; i++)
+ if (strcmp(v, code_lookup[i].c) == 0
+ && strlen(v) == strlen(code_lookup[i].c))
+ { if (strlen(buf[1-toggle])
+ + strlen(code_lookup[i].t)
+ + strlen(w) > 2047)
+ return s;
+ strcat(buf[1-toggle], code_lookup[i].t);
+ break;
+ }
+ strcat(buf[1-toggle], w);
+ toggle = 1 - toggle;
+ }
+ buf[toggle][2047] = '\0';
+ return buf[toggle];
+}
+#else
+char * transmognify(char *s) { return s; }
+#endif
+#ifdef HAS_CODE
+void
+add_src_txt(int ot, int tt)
+{ Trans *t;
+ char *q;
+
+ for (t = trans[ot][tt]; t; t = t->nxt)
+ { printf("\t\t");
+ q = transmognify(t->tp);
+ for ( ; q && *q; q++)
+ if (*q == '\n')
+ printf("\\n");
+ else
+ putchar(*q);
+ printf("\n");
+ }
+}
+void
+wrap_trail(void)
+{ static int wrap_in_progress = 0;
+ int i; short II;
+ P0 *z;
+
+ if (wrap_in_progress++) return;
+
+ printf("spin: trail ends after %ld steps\n", depth);
+ if (onlyproc >= 0)
+ { if (onlyproc >= now._nr_pr) { pan_exit(0); }
+ II = onlyproc;
+ z = (P0 *)pptr(II);
+ printf("%3ld: proc %d (%s) ",
+ depth, II, procname[z->_t]);
+ for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf(" line %3d",
+ src_all[i].src[z->_p]);
+ break;
+ }
+ printf(" (state %2d)", z->_p);
+ if (!stopstate[z->_t][z->_p])
+ printf(" (invalid end state)");
+ printf("\n");
+ add_src_txt(z->_t, z->_p);
+ pan_exit(0);
+ }
+ printf("#processes %d:\n", now._nr_pr);
+ if (depth < 0) depth = 0;
+ for (II = 0; II < now._nr_pr; II++)
+ { z = (P0 *)pptr(II);
+ printf("%3ld: proc %d (%s) ",
+ depth, II, procname[z->_t]);
+ for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf(" line %3d",
+ src_all[i].src[z->_p]);
+ break;
+ }
+ printf(" (state %2d)", z->_p);
+ if (!stopstate[z->_t][z->_p])
+ printf(" (invalid end state)");
+ printf("\n");
+ add_src_txt(z->_t, z->_p);
+ }
+ c_globals();
+ for (II = 0; II < now._nr_pr; II++)
+ { z = (P0 *)pptr(II);
+ c_locals(II, z->_t);
+ }
+#ifdef ON_EXIT
+ ON_EXIT;
+#endif
+ pan_exit(0);
+}
+FILE *
+findtrail(void)
+{ FILE *fd;
+ char fnm[512], *q;
+ char MyFile[512];
+ char MySuffix[16];
+ int try_core;
+ int candidate_files;
+
+ if (trailfilename != NULL)
+ { fd = fopen(trailfilename, "r");
+ if (fd == NULL)
+ { printf("pan: cannot find %s\n", trailfilename);
+ pan_exit(1);
+ } /* else */
+ goto success;
+ }
+talk:
+ try_core = 1;
+ candidate_files = 0;
+ tprefix = "trail";
+ strcpy(MyFile, TrailFile);
+ do { /* see if there's more than one possible trailfile */
+ if (whichtrail)
+ { sprintf(fnm, "%s%d.%s",
+ MyFile, whichtrail, tprefix);
+ fd = fopen(fnm, "r");
+ if (fd != NULL)
+ { candidate_files++;
+ if (verbose==100)
+ printf("trail%d: %s\n",
+ candidate_files, fnm);
+ fclose(fd);
+ }
+ if ((q = strchr(MyFile, '.')) != NULL)
+ { *q = '\0';
+ sprintf(fnm, "%s%d.%s",
+ MyFile, whichtrail, tprefix);
+ *q = '.';
+ fd = fopen(fnm, "r");
+ if (fd != NULL)
+ { candidate_files++;
+ if (verbose==100)
+ printf("trail%d: %s\n",
+ candidate_files, fnm);
+ fclose(fd);
+ } }
+ } else
+ { sprintf(fnm, "%s.%s", MyFile, tprefix);
+ fd = fopen(fnm, "r");
+ if (fd != NULL)
+ { candidate_files++;
+ if (verbose==100)
+ printf("trail%d: %s\n",
+ candidate_files, fnm);
+ fclose(fd);
+ }
+ if ((q = strchr(MyFile, '.')) != NULL)
+ { *q = '\0';
+ sprintf(fnm, "%s.%s", MyFile, tprefix);
+ *q = '.';
+ fd = fopen(fnm, "r");
+ if (fd != NULL)
+ { candidate_files++;
+ if (verbose==100)
+ printf("trail%d: %s\n",
+ candidate_files, fnm);
+ fclose(fd);
+ } } }
+ tprefix = MySuffix;
+ sprintf(tprefix, "cpu%d_trail", try_core++);
+ } while (try_core <= NCORE);
+
+ if (candidate_files != 1)
+ { if (verbose != 100)
+ { printf("error: there are %d trail files:\n",
+ candidate_files);
+ verbose = 100;
+ goto talk;
+ } else
+ { printf("pan: rm or mv all except one\n");
+ exit(1);
+ } }
+ try_core = 1;
+ strcpy(MyFile, TrailFile); /* restore */
+ tprefix = "trail";
+try_again:
+ if (whichtrail)
+ { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, tprefix);
+ fd = fopen(fnm, "r");
+ if (fd == NULL && (q = strchr(MyFile, '.')))
+ { *q = '\0';
+ sprintf(fnm, "%s%d.%s",
+ MyFile, whichtrail, tprefix);
+ *q = '.';
+ fd = fopen(fnm, "r");
+ }
+ } else
+ { sprintf(fnm, "%s.%s", MyFile, tprefix);
+ fd = fopen(fnm, "r");
+ if (fd == NULL && (q = strchr(MyFile, '.')))
+ { *q = '\0';
+ sprintf(fnm, "%s.%s", MyFile, tprefix);
+ *q = '.';
+ fd = fopen(fnm, "r");
+ } }
+ if (fd == NULL)
+ { if (try_core < NCORE)
+ { tprefix = MySuffix;
+ sprintf(tprefix, "cpu%d_trail", try_core++);
+ goto try_again;
+ }
+ printf("pan: cannot find trailfile %s\n", fnm);
+ pan_exit(1);
+ }
+success:
+#if NCORE>1 && defined(SEP_STATE)
+ { void set_root(void); /* for partial traces from local root */
+ set_root();
+ }
+#endif
+ return fd;
+}
+
+uchar do_transit(Trans *, short);
+
+void
+getrail(void)
+{ FILE *fd;
+ char *q;
+ int i, t_id, lastnever=-1; short II;
+ Trans *t;
+ P0 *z;
+
+ fd = findtrail(); /* exits if unsuccessful */
+ while (fscanf(fd, "%ld:%d:%d\n", &depth, &i, &t_id) == 3)
+ { if (depth == -1)
+ printf("<<<<<START OF CYCLE>>>>>\n");
+ if (depth < 0)
+ continue;
+ if (i > now._nr_pr)
+ { printf("pan: Error, proc %d invalid pid ", i);
+ printf("transition %d\n", t_id);
+ break;
+ }
+ II = i;
+ z = (P0 *)pptr(II);
+ for (t = trans[z->_t][z->_p]; t; t = t->nxt)
+ if (t->t_id == (T_ID) t_id)
+ break;
+ if (!t)
+ { for (i = 0; i < NrStates[z->_t]; i++)
+ { t = trans[z->_t][i];
+ if (t && t->t_id == (T_ID) t_id)
+ { printf("\tRecovered at state %d\n", i);
+ z->_p = i;
+ goto recovered;
+ } }
+ printf("pan: Error, proc %d type %d state %d: ",
+ II, z->_t, z->_p);
+ printf("transition %d not found\n", t_id);
+ printf("pan: list of possible transitions in this process:\n");
+ if (z->_t >= 0 && z->_t <= _NP_)
+ for (t = trans[z->_t][z->_p]; t; t = t->nxt)
+ printf(" t_id %d -- case %d, [%s]\n",
+ t->t_id, t->forw, t->tp);
+ break; /* pan_exit(1); */
+ }
+recovered:
+ q = transmognify(t->tp);
+ if (gui) simvals[0] = '\0';
+ this = pptr(II);
+ trpt->tau |= 1;
+ if (!do_transit(t, II))
+ { if (onlyproc >= 0 && II != onlyproc)
+ goto moveon;
+ printf("pan: error, next transition UNEXECUTABLE on replay\n");
+ printf(" most likely causes: missing c_track statements\n");
+ printf(" or illegal side-effects in c_expr statements\n");
+ }
+ if (onlyproc >= 0 && II != onlyproc)
+ goto moveon;
+ if (verbose)
+ { printf("%3ld: proc %2d (%s) ", depth, II, procname[z->_t]);
+ for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf(" line %3d \"%s\" ",
+ src_all[i].src[z->_p], PanSource);
+ break;
+ }
+ printf("(state %d) trans {%d,%d} [%s]\n",
+ z->_p, t_id, t->forw, q?q:"");
+ c_globals();
+ for (i = 0; i < now._nr_pr; i++)
+ { c_locals(i, ((P0 *)pptr(i))->_t);
+ }
+ } else
+ if (strcmp(procname[z->_t], ":never:") == 0)
+ { if (lastnever != (int) z->_p)
+ { for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf("MSC: ~G %d\n",
+ src_all[i].src[z->_p]);
+ break;
+ }
+ if (!src_all[i].src)
+ printf("MSC: ~R %d\n", z->_p);
+ }
+ lastnever = z->_p;
+ goto sameas;
+ } else
+ if (strcmp(procname[z->_t], ":np_:") != 0)
+ {
+sameas: if (no_rck) goto moveon;
+ if (coltrace)
+ { printf("%ld: ", depth);
+ for (i = 0; i < II; i++)
+ printf("\t\t");
+ printf("%s(%d):", procname[z->_t], II);
+ printf("[%s]\n", q?q:"");
+ } else if (!silent)
+ { if (strlen(simvals) > 0) {
+ printf("%3ld: proc %2d (%s)",
+ depth, II, procname[z->_t]);
+ for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf(" line %3d \"%s\" ",
+ src_all[i].src[z->_p], PanSource);
+ break;
+ }
+ printf("(state %d) [values: %s]\n", z->_p, simvals);
+ }
+ printf("%3ld: proc %2d (%s)",
+ depth, II, procname[z->_t]);
+ for (i = 0; src_all[i].src; i++)
+ if (src_all[i].tp == (int) z->_t)
+ { printf(" line %3d \"%s\" ",
+ src_all[i].src[z->_p], PanSource);
+ break;
+ }
+ printf("(state %d) [%s]\n", z->_p, q?q:"");
+ /* printf("\n"); */
+ } }
+moveon: z->_p = t->st;
+ }
+ wrap_trail();
+}
+#endif
+int
+f_pid(int pt)
+{ int i;
+ P0 *z;
+ for (i = 0; i < now._nr_pr; i++)
+ { z = (P0 *)pptr(i);
+ if (z->_t == (unsigned) pt)
+ return BASE+z->_pid;
+ }
+ return -1;
+}
+#ifdef VERI
+void check_claim(int);
+#endif
+
+#if !defined(HASH64) && !defined(HASH32)
+ #define HASH32
+#endif
+#if defined(HASH32) && defined(SAFETY) && !defined(SFH) && !defined(SPACE)
+ #define SFH
+#endif
+#if defined(SFH) && (defined(BITSTATE) || defined(COLLAPSE) || defined(HC) || defined(HASH64))
+ #undef SFH
+#endif
+#if defined(SFH) && !defined(NOCOMP)
+ #define NOCOMP /* go for speed */
+#endif
+#if NCORE>1 && !defined(GLOB_HEAP)
+ #define SEP_HEAP /* version 5.1.2 */
+#endif
+
+#ifdef BITSTATE
+int
+bstore_mod(char *v, int n) /* hasharray size not a power of two */
+{ unsigned long x, y;
+ unsigned int i = 1;
+
+ d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */
+ x = K1; y = j3;
+ for (;;)
+ { if (!(SS[x%udmem]&(1<<y))) break;
+ if (i == hfns) {
+#ifdef DEBUG
+ printf("Old bitstate\n");
+#endif
+ return 1;
+ }
+ x = (x + K2 + i);
+ y = (y + j4) & 7;
+ i++;
+ }
+#ifdef RANDSTOR
+ if (rand()%100 > RANDSTOR) return 0;
+#endif
+ for (;;)
+ { SS[x%udmem] |= (1<<y);
+ if (i == hfns) break;
+ x = (x + K2 + i);
+ y = (y + j4) & 7;
+ i++;
+ }
+#ifdef DEBUG
+ printf("New bitstate\n");
+#endif
+ if (now._a_t&1)
+ { nShadow++;
+ }
+ return 0;
+}
+int
+bstore_reg(char *v, int n) /* extended hashing, Peter Dillinger, 2004 */
+{ unsigned long x, y;
+ unsigned int i = 1;
+
+ d_hash((uchar *) v, n); /* sets j1-j4 */
+ x = j2; y = j3;
+ for (;;)
+ { if (!(SS[x]&(1<<y))) break;
+ if (i == hfns) {
+#ifdef DEBUG
+ printf("Old bitstate\n");
+#endif
+ return 1;
+ }
+ x = (x + j1 + i) & nmask;
+ y = (y + j4) & 7;
+ i++;
+ }
+#ifdef RANDSTOR
+ if (rand()%100 > RANDSTOR) return 0;
+#endif
+ for (;;)
+ { SS[x] |= (1<<y);
+ if (i == hfns) break;
+ x = (x + j1 + i) & nmask;
+ y = (y + j4) & 7;
+ i++;
+ }
+#ifdef DEBUG
+ printf("New bitstate\n");
+#endif
+ if (now._a_t&1)
+ { nShadow++;
+ }
+ return 0;
+}
+#endif
+unsigned long TMODE = 0666; /* file permission bits for trail files */
+
+int trcnt=1;
+char snap[64], fnm[512];
+
+int
+make_trail(void)
+{ int fd;
+ char *q;
+ char MyFile[512];
+ int w_flags = O_CREAT|O_WRONLY|O_TRUNC;
+
+ if (exclusive == 1 && iterative == 0)
+ { w_flags |= O_EXCL;
+ }
+
+ q = strrchr(TrailFile, '/');
+ if (q == NULL) q = TrailFile; else q++;
+ strcpy(MyFile, q); /* TrailFile is not a writable string */
+
+ if (iterative == 0 && Nr_Trails++ > 0)
+ { sprintf(fnm, "%s%d.%s",
+ MyFile, Nr_Trails-1, tprefix);
+ } else
+ {
+#ifdef PUTPID
+ sprintf(fnm, "%s%d.%s", MyFile, getpid(), tprefix);
+#else
+ sprintf(fnm, "%s.%s", MyFile, tprefix);
+#endif
+ }
+ if ((fd = open(fnm, w_flags, TMODE)) < 0)
+ { if ((q = strchr(MyFile, '.')))
+ { *q = '\0';
+ if (iterative == 0 && Nr_Trails-1 > 0)
+ sprintf(fnm, "%s%d.%s",
+ MyFile, Nr_Trails-1, tprefix);
+ else
+ sprintf(fnm, "%s.%s", MyFile, tprefix);
+ *q = '.';
+ fd = open(fnm, w_flags, TMODE);
+ } }
+ if (fd < 0)
+ { printf("pan: cannot create %s\n", fnm);
+ perror("cause");
+ } else
+ {
+#if NCORE>1 && (defined(SEP_STATE) || !defined(FULL_TRAIL))
+ void write_root(void);
+ write_root();
+#else
+ printf("pan: wrote %s\n", fnm);
+#endif
+ }
+ return fd;
+}
+
+#ifndef FREQ
+#define FREQ (1000000)
+#endif
+#ifdef BFS
+#define Q_PROVISO
+#ifndef INLINE_REV
+#define INLINE_REV
+#endif
+
+typedef struct SV_Hold {
+ State *sv;
+ int sz;
+ struct SV_Hold *nxt;
+} SV_Hold;
+
+typedef struct EV_Hold {
+ char *sv;
+ int sz;
+ int nrpr;
+ int nrqs;
+ char *po;
+ char *qo;
+ char *ps, *qs;
+ struct EV_Hold *nxt;
+} EV_Hold;
+
+typedef struct BFS_Trail {
+ Trail *frame;
+ SV_Hold *onow;
+ EV_Hold *omask;
+#ifdef Q_PROVISO
+ struct H_el *lstate;
+#endif
+ short boq;
+ struct BFS_Trail *nxt;
+} BFS_Trail;
+
+BFS_Trail *bfs_trail, *bfs_bot, *bfs_free;
+
+SV_Hold *svhold, *svfree;
+
+#ifdef BFS_DISK
+#ifndef BFS_LIMIT
+ #define BFS_LIMIT 100000
+#endif
+#ifndef BFS_DSK_LIMIT
+ #define BFS_DSK_LIMIT 1000000
+#endif
+#if defined(WIN32) || defined(WIN64)
+ #define RFLAGS (O_RDONLY|O_BINARY)
+ #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)
+#else
+ #define RFLAGS (O_RDONLY)
+ #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC)
+#endif
+long bfs_size_limit;
+int bfs_dsk_write = -1;
+int bfs_dsk_read = -1;
+long bfs_dsk_writes, bfs_dsk_reads;
+int bfs_dsk_seqno_w, bfs_dsk_seqno_r;
+#endif
+
+uchar do_reverse(Trans *, short, uchar);
+void snapshot(void);
+
+SV_Hold *
+getsv(int n)
+{ SV_Hold *h = (SV_Hold *) 0, *oh;
+
+ oh = (SV_Hold *) 0;
+ for (h = svfree; h; oh = h, h = h->nxt)
+ { if (n == h->sz)
+ { if (!oh)
+ svfree = h->nxt;
+ else
+ oh->nxt = h->nxt;
+ h->nxt = (SV_Hold *) 0;
+ break;
+ }
+ if (n < h->sz)
+ { h = (SV_Hold *) 0;
+ break;
+ }
+ /* else continue */
+ }
+
+ if (!h)
+ { h = (SV_Hold *) emalloc(sizeof(SV_Hold));
+ h->sz = n;
+#ifdef BFS_DISK
+ if (bfs_size_limit >= BFS_LIMIT)
+ { h->sv = (State *) 0; /* means: read disk */
+ bfs_dsk_writes++; /* count */
+ if (bfs_dsk_write < 0 /* file descriptor */
+ || bfs_dsk_writes%BFS_DSK_LIMIT == 0)
+ { char dsk_nm[32];
+ if (bfs_dsk_write >= 0)
+ { (void) close(bfs_dsk_write);
+ }
+ sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_w++);
+ bfs_dsk_write = open(dsk_nm, WFLAGS, 0644);
+ if (bfs_dsk_write < 0)
+ { Uerror("could not create tmp disk file");
+ }
+ printf("pan: created disk file %s\n", dsk_nm);
+ }
+ if (write(bfs_dsk_write, (char *) &now, n) != n)
+ { Uerror("aborting -- disk write failed (disk full?)");
+ }
+ return h; /* no memcpy */
+ }
+ bfs_size_limit++;
+#endif
+ h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n);
+ }
+
+ memcpy((char *)h->sv, (char *)&now, n);
+ return h;
+}
+
+EV_Hold *
+getsv_mask(int n)
+{ EV_Hold *h;
+ static EV_Hold *kept = (EV_Hold *) 0;
+
+ for (h = kept; h; h = h->nxt)
+ if (n == h->sz
+ && (memcmp((char *) Mask, (char *) h->sv, n) == 0)
+ && (now._nr_pr == h->nrpr)
+ && (now._nr_qs == h->nrqs)
+#if VECTORSZ>32000
+ && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0)
+ && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0)
+#else
+ && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0)
+ && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0)
+#endif
+ && (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0)
+ && (memcmp((char *) q_skip, (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0))
+ break;
+ if (!h)
+ { h = (EV_Hold *) emalloc(sizeof(EV_Hold));
+ h->sz = n;
+ h->nrpr = now._nr_pr;
+ h->nrqs = now._nr_qs;
+
+ h->sv = (char *) emalloc(n * sizeof(char));
+ memcpy((char *) h->sv, (char *) Mask, n);
+
+ if (now._nr_pr > 0)
+ { h->ps = (char *) emalloc(now._nr_pr * sizeof(int));
+ memcpy((char *) h->ps, (char *) proc_skip, now._nr_pr * sizeof(uchar));
+#if VECTORSZ>32000
+ h->po = (char *) emalloc(now._nr_pr * sizeof(int));
+ memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int));
+#else
+ h->po = (char *) emalloc(now._nr_pr * sizeof(short));
+ memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short));
+#endif
+ }
+ if (now._nr_qs > 0)
+ { h->qs = (char *) emalloc(now._nr_qs * sizeof(int));
+ memcpy((char *) h->qs, (char *) q_skip, now._nr_qs * sizeof(uchar));
+#if VECTORSZ>32000
+ h->qo = (char *) emalloc(now._nr_qs * sizeof(int));
+ memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int));
+#else
+ h->qo = (char *) emalloc(now._nr_qs * sizeof(short));
+ memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short));
+#endif
+ }
+
+ h->nxt = kept;
+ kept = h;
+ }
+ return h;
+}
+
+void
+freesv(SV_Hold *p)
+{ SV_Hold *h, *oh;
+
+ oh = (SV_Hold *) 0;
+ for (h = svfree; h; oh = h, h = h->nxt)
+ if (h->sz >= p->sz)
+ break;
+
+ if (!oh)
+ { p->nxt = svfree;
+ svfree = p;
+ } else
+ { p->nxt = h;
+ oh->nxt = p;
+ }
+}
+
+BFS_Trail *
+get_bfs_frame(void)
+{ BFS_Trail *t;
+
+ if (bfs_free)
+ { t = bfs_free;
+ bfs_free = bfs_free->nxt;
+ t->nxt = (BFS_Trail *) 0;
+ } else
+ { t = (BFS_Trail *) emalloc(sizeof(BFS_Trail));
+ }
+ t->frame = (Trail *) emalloc(sizeof(Trail));
+ return t;
+}
+
+void
+push_bfs(Trail *f, int d)
+{ BFS_Trail *t;
+
+ t = get_bfs_frame();
+ memcpy((char *)t->frame, (char *)f, sizeof(Trail));
+ t->frame->o_tt = d; /* depth */
+
+ t->boq = boq;
+ t->onow = getsv(vsize);
+ t->omask = getsv_mask(vsize);
+#if defined(FULLSTACK) && defined(Q_PROVISO)
+ t->lstate = Lstate;
+#endif
+ if (!bfs_bot)
+ { bfs_bot = bfs_trail = t;
+ } else
+ { bfs_bot->nxt = t;
+ bfs_bot = t;
+ }
+#ifdef CHECK
+ printf("PUSH %u (%d)\n", t->frame, d);
+#endif
+}
+
+Trail *
+pop_bfs(void)
+{ BFS_Trail *t;
+
+ if (!bfs_trail)
+ return (Trail *) 0;
+
+ t = bfs_trail;
+ bfs_trail = t->nxt;
+ if (!bfs_trail)
+ bfs_bot = (BFS_Trail *) 0;
+#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE)
+ if (t->lstate) t->lstate->tagged = 0;
+#endif
+
+ t->nxt = bfs_free;
+ bfs_free = t;
+
+ vsize = t->onow->sz;
+ boq = t->boq;
+#ifdef BFS_DISK
+ if (t->onow->sv == (State *) 0)
+ { char dsk_nm[32];
+ bfs_dsk_reads++; /* count */
+ if (bfs_dsk_read >= 0 /* file descriptor */
+ && bfs_dsk_reads%BFS_DSK_LIMIT == 0)
+ { (void) close(bfs_dsk_read);
+ sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r-1);
+ (void) unlink(dsk_nm);
+ bfs_dsk_read = -1;
+ }
+ if (bfs_dsk_read < 0)
+ { sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r++);
+ bfs_dsk_read = open(dsk_nm, RFLAGS);
+ if (bfs_dsk_read < 0)
+ { Uerror("could not open temp disk file");
+ } }
+ if (read(bfs_dsk_read, (char *) &now, vsize) != vsize)
+ { Uerror("bad bfs disk file read");
+ }
+#ifndef NOVSZ
+ if (now._vsz != vsize)
+ { Uerror("disk read vsz mismatch");
+ }
+#endif
+ } else
+#endif
+ memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize);
+ memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize);
+ if (now._nr_pr > 0)
+#if VECTORSZ>32000
+ { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int));
+#else
+ { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short));
+#endif
+ memcpy((char *)proc_skip, (char *)t->omask->ps, now._nr_pr * sizeof(uchar));
+ }
+ if (now._nr_qs > 0)
+#if VECTORSZ>32000
+ { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int));
+#else
+ { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short));
+#endif
+ memcpy((uchar *)q_skip, (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar));
+ }
+#ifdef BFS_DISK
+ if (t->onow->sv != (State *) 0)
+#endif
+ freesv(t->onow); /* omask not freed */
+#ifdef CHECK
+ printf("POP %u (%d)\n", t->frame, t->frame->o_tt);
+#endif
+ return t->frame;
+}
+
+void
+store_state(Trail *ntrpt, int shortcut, short oboq)
+{
+#ifdef VERI
+ Trans *t2 = (Trans *) 0;
+ uchar ot; int tt, E_state;
+ uchar o_opm = trpt->o_pm, *othis = this;
+
+ if (shortcut)
+ {
+#ifdef VERBOSE
+ printf("claim: shortcut\n");
+#endif
+ goto store_it; /* no claim move */
+ }
+
+ this = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */
+ trpt->o_pm = 0;
+
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+
+#ifdef HAS_UNLESS
+ E_state = 0;
+#endif
+ for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0)
+ {
+#ifdef HAS_UNLESS
+ if (E_state > 0
+ && E_state != t2->e_trans)
+ break;
+#endif
+ if (do_transit(t2, 0))
+ {
+#ifdef VERBOSE
+ if (!reached[ot][t2->st])
+ printf("depth: %d -- claim move from %d -> %d\n",
+ trpt->o_tt, ((P0 *)this)->_p, t2->st);
+#endif
+#ifdef HAS_UNLESS
+ E_state = t2->e_trans;
+#endif
+ if (t2->st > 0)
+ { ((P0 *)this)->_p = t2->st;
+ reached[ot][t2->st] = 1;
+#ifndef NOCLAIM
+ check_claim(t2->st);
+#endif
+ }
+ if (now._nr_pr == 0) /* claim terminated */
+ uerror("end state in claim reached");
+
+#ifdef PEG
+ peg[t2->forw]++;
+#endif
+ trpt->o_pm |= 1;
+ if (t2->atom&2)
+ Uerror("atomic in claim not supported in BFS mode");
+store_it:
+
+#endif
+
+#ifdef BITSTATE
+ if (!bstore((char *)&now, vsize))
+#else
+#ifdef MA
+ if (!gstore((char *)&now, vsize, 0))
+#else
+ if (!hstore((char *)&now, vsize))
+#endif
+#endif
+ { static long sdone = (long) 0; long ndone;
+ nstates++;
+#ifndef NOREDUCE
+ trpt->tau |= 64;
+#endif
+ ndone = (unsigned long) (nstates/((double) FREQ));
+ if (ndone != sdone && mreached%10 != 0)
+ { snapshot();
+ sdone = ndone;
+#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
+ if (nstates > ((double)(1<<(ssize+1))))
+ { void resize_hashtable(void);
+ resize_hashtable();
+ }
+#endif
+ }
+#if SYNC
+ if (boq != -1)
+ midrv++;
+ else if (oboq != -1)
+ { Trail *x;
+ x = (Trail *) trpt->ostate; /* pre-rv state */
+ if (x) x->o_pm |= 4; /* mark success */
+ }
+#endif
+ push_bfs(ntrpt, trpt->o_tt+1);
+ } else
+ { truncs++;
+#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO)
+#if !defined(BITSTATE)
+ if (Lstate && Lstate->tagged) trpt->tau |= 64;
+#else
+ if (trpt->tau&32)
+ { BFS_Trail *tprov;
+ for (tprov = bfs_trail; tprov; tprov = tprov->nxt)
+ if (tprov->onow->sv != (State *) 0
+ && memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize) == 0)
+ { trpt->tau |= 64;
+ break; /* state is in queue */
+ } }
+#endif
+#endif
+ }
+#ifdef VERI
+ ((P0 *)this)->_p = tt; /* reset claim */
+ if (t2)
+ do_reverse(t2, 0, 0);
+ else
+ break;
+ } }
+ this = othis;
+ trpt->o_pm = o_opm;
+#endif
+}
+
+Trail *ntrpt;
+
+void
+bfs(void)
+{ Trans *t; Trail *otrpt, *x;
+ uchar _n, _m, ot, nps = 0;
+ int tt, E_state;
+ short II, From = (short) (now._nr_pr-1), To = BASE;
+ short oboq = boq;
+
+ ntrpt = (Trail *) emalloc(sizeof(Trail));
+ trpt->ostate = (struct H_el *) 0;
+ trpt->tau = 0;
+
+ trpt->o_tt = -1;
+ store_state(ntrpt, 0, oboq); /* initial state */
+
+ while ((otrpt = pop_bfs())) /* also restores now */
+ { memcpy((char *) trpt, (char *) otrpt, sizeof(Trail));
+#if defined(C_States) && (HAS_TRACK==1)
+ c_revert((uchar *) &(now.c_state[0]));
+#endif
+ if (trpt->o_pm & 4)
+ {
+#ifdef VERBOSE
+ printf("Revisit of atomic not needed (%d)\n",
+ trpt->o_pm);
+#endif
+ continue;
+ }
+#ifndef NOREDUCE
+ nps = 0;
+#endif
+ if (trpt->o_pm == 8)
+ { revrv++;
+ if (trpt->tau&8)
+ {
+#ifdef VERBOSE
+ printf("Break atomic (pm:%d,tau:%d)\n",
+ trpt->o_pm, trpt->tau);
+#endif
+ trpt->tau &= ~8;
+ }
+#ifndef NOREDUCE
+ else if (trpt->tau&32)
+ {
+#ifdef VERBOSE
+ printf("Void preselection (pm:%d,tau:%d)\n",
+ trpt->o_pm, trpt->tau);
+#endif
+ trpt->tau &= ~32;
+ nps = 1; /* no preselection in repeat */
+ }
+#endif
+ }
+ trpt->o_pm &= ~(4|8);
+ if (trpt->o_tt > mreached)
+ { mreached = trpt->o_tt;
+ if (mreached%10 == 0)
+ { snapshot();
+ } }
+ depth = trpt->o_tt;
+ if (depth >= maxdepth)
+ {
+#if SYNC
+ Trail *x;
+ if (boq != -1)
+ { x = (Trail *) trpt->ostate;
+ if (x) x->o_pm |= 4; /* not failing */
+ }
+#endif
+ truncs++;
+ if (!warned)
+ { warned = 1;
+ printf("error: max search depth too small\n");
+ }
+ if (bounded)
+ uerror("depth limit reached");
+ continue;
+ }
+#ifndef NOREDUCE
+ if (boq == -1 && !(trpt->tau&8) && nps == 0)
+ for (II = now._nr_pr-1; II >= BASE; II -= 1)
+ {
+Pickup: this = pptr(II);
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+ if (trans[ot][tt]->atom & 8)
+ { t = trans[ot][tt];
+ if (t->qu[0] != 0)
+ { Ccheck++;
+ if (!q_cond(II, t))
+ continue;
+ Cholds++;
+ }
+ From = To = II;
+ trpt->tau |= 32; /* preselect marker */
+#ifdef DEBUG
+ printf("%3d: proc %d PreSelected (tau=%d)\n",
+ depth, II, trpt->tau);
+#endif
+ goto MainLoop;
+ } }
+ trpt->tau &= ~32;
+#endif
+Repeat:
+ if (trpt->tau&8) /* atomic */
+ { From = To = (short ) trpt->pr;
+ nlinks++;
+ } else
+ { From = now._nr_pr-1;
+ To = BASE;
+ }
+MainLoop:
+ _n = _m = 0;
+ for (II = From; II >= To; II -= 1)
+ {
+ this = (((uchar *)&now)+proc_offset[II]);
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+#if SYNC
+ /* no rendezvous with same proc */
+ if (boq != -1 && trpt->pr == II) continue;
+#endif
+ ntrpt->pr = (uchar) II;
+ ntrpt->st = tt;
+ trpt->o_pm &= ~1; /* no move yet */
+#ifdef EVENT_TRACE
+ trpt->o_event = now._event;
+#endif
+#ifdef HAS_PROVIDED
+ if (!provided(II, ot, tt, t)) continue;
+#endif
+#ifdef HAS_UNLESS
+ E_state = 0;
+#endif
+ for (t = trans[ot][tt]; t; t = t->nxt)
+ {
+#ifdef HAS_UNLESS
+ if (E_state > 0
+ && E_state != t->e_trans)
+ break;
+#endif
+ ntrpt->o_t = t;
+
+ oboq = boq;
+
+ if (!(_m = do_transit(t, II)))
+ continue;
+
+ trpt->o_pm |= 1; /* we moved */
+ (trpt+1)->o_m = _m; /* for unsend */
+#ifdef PEG
+ peg[t->forw]++;
+#endif
+#ifdef CHECK
+ printf("%3d: proc %d exec %d, ",
+ depth, II, t->forw);
+ printf("%d to %d, %s %s %s",
+ tt, t->st, t->tp,
+ (t->atom&2)?"atomic":"",
+ (boq != -1)?"rendez-vous":"");
+#ifdef HAS_UNLESS
+ if (t->e_trans)
+ printf(" (escapes to state %d)", t->st);
+#endif
+ printf(" %saccepting [tau=%d]\n",
+ (trpt->o_pm&2)?"":"non-", trpt->tau);
+#endif
+#ifdef HAS_UNLESS
+ E_state = t->e_trans;
+#if SYNC>0
+ if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */))
+ { fprintf(efd, "error: the use of rendezvous stmnt in the escape clause\n");
+ fprintf(efd, " of an unless stmnt is not compatible with -DBFS\n");
+ pan_exit(1);
+ }
+#endif
+#endif
+ if (t->st > 0) ((P0 *)this)->_p = t->st;
+
+ /* ptr to pred: */ ntrpt->ostate = (struct H_el *) otrpt;
+ ntrpt->st = tt;
+ if (boq == -1 && (t->atom&2)) /* atomic */
+ ntrpt->tau = 8; /* record for next move */
+ else
+ ntrpt->tau = 0;
+
+ store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq);
+#ifdef EVENT_TRACE
+ now._event = trpt->o_event;
+#endif
+
+ /* undo move and continue */
+ trpt++; /* this is where ovals and ipt are set */
+ do_reverse(t, II, _m); /* restore now. */
+ trpt--;
+#ifdef CHECK
+ #if NCORE>1
+ enter_critical(GLOBAL_LOCK); /* in verbose mode only */
+ printf("cpu%d: ", core_id);
+ #endif
+ printf("%3d: proc %d ", depth, II);
+ printf("reverses %d, %d to %d,",
+ t->forw, tt, t->st);
+ printf(" %s [abit=%d,adepth=%d,",
+ t->tp, now._a_t, A_depth);
+ printf("tau=%d,%d]\n",
+ trpt->tau, (trpt-1)->tau);
+ #if NCORE>1
+ leave_critical(GLOBAL_LOCK);
+ #endif
+#endif
+ reached[ot][t->st] = 1;
+ reached[ot][tt] = 1;
+
+ ((P0 *)this)->_p = tt;
+ _n |= _m;
+ } }
+#ifndef NOREDUCE
+ /* preselected - no succ definitely outside stack */
+ if ((trpt->tau&32) && !(trpt->tau&64))
+ { From = now._nr_pr-1; To = BASE;
+#ifdef DEBUG
+ cpu_printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n",
+ depth, II+1, (int) _n, trpt->tau);
+#endif
+ _n = 0; trpt->tau &= ~32;
+ if (II >= BASE)
+ goto Pickup;
+ goto MainLoop;
+ }
+ trpt->tau &= ~(32|64);
+#endif
+ if (_n != 0)
+ continue;
+#ifdef DEBUG
+ printf("%3d: no move [II=%d, tau=%d, boq=%d, _nr_pr=%d]\n",
+ depth, II, trpt->tau, boq, now._nr_pr);
+#endif
+ if (boq != -1)
+ { failedrv++;
+ x = (Trail *) trpt->ostate; /* pre-rv state */
+ if (!x) continue; /* root state */
+ if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */
+ { x->o_pm |= 8; /* mark failure */
+ this = (((uchar *)&now)+proc_offset[otrpt->pr]);
+#ifdef VERBOSE
+ printf("\treset state of %d from %d to %d\n",
+ otrpt->pr, ((P0 *)this)->_p, otrpt->st);
+#endif
+ ((P0 *)this)->_p = otrpt->st;
+ unsend(boq); /* retract rv offer */
+ boq = -1;
+ push_bfs(x, x->o_tt);
+#ifdef VERBOSE
+ printf("failed rv, repush with %d\n", x->o_pm);
+#endif
+ }
+#ifdef VERBOSE
+ else printf("failed rv, tau at parent: %d\n", x->tau);
+#endif
+ } else if (now._nr_pr > 0)
+ {
+ if ((trpt->tau&8)) /* atomic */
+ { trpt->tau &= ~(1|8); /* 1=timeout, 8=atomic */
+#ifdef DEBUG
+ printf("%3d: atomic step proc %d blocks\n",
+ depth, II+1);
+#endif
+ goto Repeat;
+ }
+
+ if (!(trpt->tau&1)) /* didn't try timeout yet */
+ { trpt->tau |= 1;
+#ifdef DEBUG
+ printf("%d: timeout\n", depth);
+#endif
+ goto MainLoop;
+ }
+#ifndef VERI
+ if (!noends && !a_cycles && !endstate())
+ uerror("invalid end state");
+#endif
+ } }
+}
+
+void
+putter(Trail *trpt, int fd)
+{ long j;
+
+ if (!trpt) return;
+
+ if (trpt != (Trail *) trpt->ostate)
+ putter((Trail *) trpt->ostate, fd);
+
+ if (trpt->o_t)
+ { sprintf(snap, "%d:%d:%d\n",
+ trcnt++, trpt->pr, trpt->o_t->t_id);
+ j = strlen(snap);
+ if (write(fd, snap, j) != j)
+ { printf("pan: error writing %s\n", fnm);
+ pan_exit(1);
+ } }
+}
+
+void
+nuerror(char *str)
+{ int fd = make_trail();
+ int j;
+
+ if (fd < 0) return;
+#ifdef VERI
+ sprintf(snap, "-2:%d:-2\n", VERI);
+ write(fd, snap, strlen(snap));
+#endif
+#ifdef MERGED
+ sprintf(snap, "-4:-4:-4\n");
+ write(fd, snap, strlen(snap));
+#endif
+ trcnt = 1;
+ putter(trpt, fd);
+ if (ntrpt->o_t)
+ { sprintf(snap, "%d:%d:%d\n",
+ trcnt++, ntrpt->pr, ntrpt->o_t->t_id);
+ j = strlen(snap);
+ if (write(fd, snap, j) != j)
+ { printf("pan: error writing %s\n", fnm);
+ pan_exit(1);
+ } }
+ close(fd);
+ if (errors >= upto && upto != 0)
+ { wrapup();
+ }
+}
+#endif
+#if NCORE>1
+#if defined(WIN32) || defined(WIN64)
+#ifndef _CONSOLE
+ #define _CONSOLE
+#endif
+ #ifdef WIN64
+#undef long
+ #endif
+#include <windows.h>
+
+ #ifdef WIN64
+ #define long long long
+ #endif
+#else
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#endif
+
+/* code common to cygwin/linux and win32/win64: */
+
+#ifdef VERBOSE
+ #define VVERBOSE (1)
+#else
+ #define VVERBOSE (0)
+#endif
+
+/* the following values must be larger than 256 and must fit in an int */
+#define QUIT 1024 /* terminate now command */
+#define QUERY 512 /* termination status query message */
+#define QUERY_F 513 /* query failed, cannot quit */
+
+#define GN_FRAMES (int) (GWQ_SIZE / (double) sizeof(SM_frame))
+#define LN_FRAMES (int) (LWQ_SIZE / (double) sizeof(SM_frame))
+
+#ifndef VMAX
+ #define VMAX VECTORSZ
+#endif
+#ifndef PMAX
+ #define PMAX 64
+#endif
+#ifndef QMAX
+ #define QMAX 64
+#endif
+
+#if VECTORSZ>32000
+ #define OFFT int
+#else
+ #define OFFT short
+#endif
+
+#ifdef SET_SEG_SIZE
+ /* no longer usefule -- being recomputed for local heap size anyway */
+ double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.);
+#else
+ double SEG_SIZE = (1048576.*1024.); /* 1GB default shared memory pool segments */
+#endif
+
+double LWQ_SIZE = 0.; /* initialized in main */
+
+#ifdef SET_WQ_SIZE
+ #ifdef NGQ
+ #warning SET_WQ_SIZE applies to global queue -- ignored
+ double GWQ_SIZE = 0.;
+ #else
+ double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.);
+ /* must match the value in pan_proxy.c, if used */
+ #endif
+#else
+ #ifdef NGQ
+ double GWQ_SIZE = 0.;
+ #else
+ double GWQ_SIZE = (128.*1048576.); /* 128 MB default queue sizes */
+ #endif
+#endif
+
+/* Crash Detection Parameters */
+#ifndef ONESECOND
+ #define ONESECOND (1<<25)
+#endif
+#ifndef SHORT_T
+ #define SHORT_T (0.1)
+#endif
+#ifndef LONG_T
+ #define LONG_T (600)
+#endif
+
+double OneSecond = (double) (ONESECOND); /* waiting for a free slot -- checks crash */
+double TenSeconds = 10. * (ONESECOND); /* waiting for a lock -- check for a crash */
+
+/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */
+double Delay = ((double) SHORT_T) * (ONESECOND); /* termination detection trigger */
+double OneHour = ((double) LONG_T) * (ONESECOND); /* timeout termination detection */
+
+typedef struct SM_frame SM_frame;
+typedef struct SM_results SM_results;
+typedef struct sh_Allocater sh_Allocater;
+
+struct SM_frame { /* about 6K per slot */
+ volatile int m_vsize; /* 0 means free slot */
+ volatile int m_boq; /* >500 is a control message */
+#ifdef FULL_TRAIL
+ volatile struct Stack_Tree *m_stack; /* ptr to previous state */
+#endif
+ volatile uchar m_tau;
+ volatile uchar m_o_pm;
+ volatile int nr_handoffs; /* to compute real_depth */
+ volatile char m_now [VMAX];
+ volatile char m_Mask [(VMAX + 7)/8];
+ volatile OFFT m_p_offset[PMAX];
+ volatile OFFT m_q_offset[QMAX];
+ volatile uchar m_p_skip [PMAX];
+ volatile uchar m_q_skip [QMAX];
+#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
+ volatile uchar m_c_stack [StackSize];
+#endif
+};
+
+int proxy_pid; /* id of proxy if nonzero -- receive half */
+int store_proxy_pid;
+short remote_party;
+int proxy_pid_snd; /* id of proxy if nonzero -- send half */
+char o_cmdline[512]; /* to pass options to children */
+
+int iamin[CS_NR+NCORE]; /* non-shared */
+
+#if defined(WIN32) || defined(WIN64)
+int tas(volatile LONG *);
+
+HANDLE proxy_handle_snd; /* for Windows Create and Terminate */
+
+struct sh_Allocater { /* shared memory for states */
+ volatile char *dc_arena; /* to allocate states from */
+ volatile long pattern; /* to detect overruns */
+ volatile long dc_size; /* nr of bytes left */
+ volatile void *dc_start; /* where memory segment starts */
+ volatile void *dc_id; /* to attach, detach, remove shared memory segments */
+ volatile sh_Allocater *nxt; /* linked list of pools */
+};
+DWORD worker_pids[NCORE]; /* root mem of pids of all workers created */
+HANDLE worker_handles[NCORE]; /* for windows Create and Terminate */
+void * shmid [NR_QS]; /* return value from CreateFileMapping */
+void * shmid_M; /* shared mem for state allocation in hashtable */
+
+#ifdef SEP_STATE
+ void *shmid_X;
+#else
+ void *shmid_S; /* shared bitstate arena or hashtable */
+#endif
+#else
+int tas(volatile int *);
+
+struct sh_Allocater { /* shared memory for states */
+ volatile char *dc_arena; /* to allocate states from */
+ volatile long pattern; /* to detect overruns */
+ volatile long dc_size; /* nr of bytes left */
+ volatile char *dc_start; /* where memory segment starts */
+ volatile int dc_id; /* to attach, detach, remove shared memory segments */
+ volatile sh_Allocater *nxt; /* linked list of pools */
+};
+
+int worker_pids[NCORE]; /* root mem of pids of all workers created */
+int shmid [NR_QS]; /* return value from shmget */
+int nibis = 0; /* set after shared mem has been released */
+int shmid_M; /* shared mem for state allocation in hashtable */
+#ifdef SEP_STATE
+ long shmid_X;
+#else
+ int shmid_S; /* shared bitstate arena or hashtable */
+ volatile sh_Allocater *first_pool; /* of shared state memory */
+ volatile sh_Allocater *last_pool;
+#endif
+#endif
+
+struct SM_results { /* for shuttling back final stats */
+ volatile int m_vsize; /* avoid conflicts with frames */
+ volatile int m_boq; /* these 2 fields are not written in record_info */
+ /* probably not all fields really need to be volatile */
+ volatile double m_memcnt;
+ volatile double m_nstates;
+ volatile double m_truncs;
+ volatile double m_truncs2;
+ volatile double m_nShadow;
+ volatile double m_nlinks;
+ volatile double m_ngrabs;
+ volatile double m_nlost;
+ volatile double m_hcmp;
+ volatile double m_frame_wait;
+ volatile int m_hmax;
+ volatile int m_svmax;
+ volatile int m_smax;
+ volatile int m_mreached;
+ volatile int m_errors;
+ volatile int m_VMAX;
+ volatile short m_PMAX;
+ volatile short m_QMAX;
+ volatile uchar m_R; /* reached info for all proctypes */
+};
+
+int core_id = 0; /* internal process nr, to know which q to use */
+unsigned long nstates_put = 0; /* statistics */
+unsigned long nstates_get = 0;
+int query_in_progress = 0; /* termination detection */
+
+double free_wait = 0.; /* waiting for a free frame */
+double frame_wait = 0.; /* waiting for a full frame */
+double lock_wait = 0.; /* waiting for access to cs */
+double glock_wait[3]; /* waiting for access to global lock */
+
+char *sprefix = "rst";
+uchar was_interrupted, issued_kill, writing_trail;
+
+static SM_frame cur_Root; /* current root, to be safe with error trails */
+
+SM_frame *m_workq [NR_QS]; /* per cpu work queues + global q */
+char *shared_mem[NR_QS]; /* return value from shmat */
+#ifdef SEP_HEAP
+char *my_heap;
+long my_size;
+#endif
+volatile sh_Allocater *dc_shared; /* assigned at initialization */
+
+static int vmax_seen, pmax_seen, qmax_seen;
+static double gq_tries, gq_hasroom, gq_hasnoroom;
+
+volatile int *prfree;
+volatile int *prfull;
+volatile int *prcnt;
+volatile int *prmax;
+
+volatile int *sh_lock; /* mutual exclusion locks - in shared memory */
+volatile double *is_alive; /* to detect when processes crash */
+volatile int *grfree, *grfull, *grcnt, *grmax; /* access to shared global q */
+volatile double *gr_readmiss, *gr_writemiss;
+static int lrfree; /* used for temporary recording of slot */
+static int dfs_phase2;
+
+void mem_put(int); /* handoff state to other cpu */
+void mem_put_acc(void); /* liveness mode */
+void mem_get(void); /* get state from work queue */
+void sudden_stop(char *);
+#if 0
+void enter_critical(int);
+void leave_critical(int);
+#endif
+
+void
+record_info(SM_results *r)
+{ int i;
+ uchar *ptr;
+
+#ifdef SEP_STATE
+ if (0)
+ { cpu_printf("nstates %g nshadow %g -- memory %-6.3f Mb\n",
+ nstates, nShadow, memcnt/(1048576.));
+ }
+ r->m_memcnt = 0;
+#else
+ #ifdef BITSTATE
+ r->m_memcnt = 0; /* it's shared */
+ #endif
+ r->m_memcnt = memcnt;
+#endif
+ if (a_cycles && core_id == 1)
+ { r->m_nstates = nstates;
+ r->m_nShadow = nstates;
+ } else
+ { r->m_nstates = nstates;
+ r->m_nShadow = nShadow;
+ }
+ r->m_truncs = truncs;
+ r->m_truncs2 = truncs2;
+ r->m_nlinks = nlinks;
+ r->m_ngrabs = ngrabs;
+ r->m_nlost = nlost;
+ r->m_hcmp = hcmp;
+ r->m_frame_wait = frame_wait;
+ r->m_hmax = hmax;
+ r->m_svmax = svmax;
+ r->m_smax = smax;
+ r->m_mreached = mreached;
+ r->m_errors = errors;
+ r->m_VMAX = vmax_seen;
+ r->m_PMAX = (short) pmax_seen;
+ r->m_QMAX = (short) qmax_seen;
+ ptr = (uchar *) &(r->m_R);
+ for (i = 0; i <= _NP_; i++) /* all proctypes */
+ { memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar));
+ ptr += NrStates[i]*sizeof(uchar);
+ }
+ if (verbose>1)
+ { cpu_printf("Put Results nstates %g (sz %d)\n", nstates, ptr - &(r->m_R));
+ }
+}
+
+void snapshot(void);
+
+void
+retrieve_info(SM_results *r)
+{ int i, j;
+ volatile uchar *ptr;
+
+ snapshot(); /* for a final report */
+
+ enter_critical(GLOBAL_LOCK);
+#ifdef SEP_HEAP
+ if (verbose)
+ { printf("cpu%d: local heap-left %ld KB (%d MB)\n",
+ core_id, (int) (my_size/1024), (int) (my_size/1048576));
+ }
+#endif
+ if (verbose && core_id == 0)
+ { printf("qmax: ");
+ for (i = 0; i < NCORE; i++)
+ { printf("%d ", prmax[i]);
+ }
+#ifndef NGQ
+ printf("G: %d", *grmax);
+#endif
+ printf("\n");
+ }
+ leave_critical(GLOBAL_LOCK);
+
+ memcnt += r->m_memcnt;
+ nstates += r->m_nstates;
+ nShadow += r->m_nShadow;
+ truncs += r->m_truncs;
+ truncs2 += r->m_truncs2;
+ nlinks += r->m_nlinks;
+ ngrabs += r->m_ngrabs;
+ nlost += r->m_nlost;
+ hcmp += r->m_hcmp;
+ /* frame_wait += r->m_frame_wait; */
+ errors += r->m_errors;
+
+ if (hmax < r->m_hmax) hmax = r->m_hmax;
+ if (svmax < r->m_svmax) svmax = r->m_svmax;
+ if (smax < r->m_smax) smax = r->m_smax;
+ if (mreached < r->m_mreached) mreached = r->m_mreached;
+
+ if (vmax_seen < r->m_VMAX) vmax_seen = r->m_VMAX;
+ if (pmax_seen < (int) r->m_PMAX) pmax_seen = (int) r->m_PMAX;
+ if (qmax_seen < (int) r->m_QMAX) qmax_seen = (int) r->m_QMAX;
+
+ ptr = &(r->m_R);
+ for (i = 0; i <= _NP_; i++) /* all proctypes */
+ { for (j = 0; j < NrStates[i]; j++)
+ { if (*(ptr + j) != 0)
+ { reached[i][j] = 1;
+ } }
+ ptr += NrStates[i]*sizeof(uchar);
+ }
+ if (verbose>1)
+ { cpu_printf("Got Results (%d)\n", ptr - &(r->m_R));
+ snapshot();
+ }
+}
+
+#if !defined(WIN32) && !defined(WIN64)
+static void
+rm_shared_segments(void)
+{ int m;
+ volatile sh_Allocater *nxt_pool;
+ /*
+ * mark all shared memory segments for removal
+ * the actual removes wont happen intil last process dies or detaches
+ * the shmctl calls can return -1 if not all procs have detached yet
+ */
+ for (m = 0; m < NR_QS; m++) /* +1 for global q */
+ { if (shmid[m] != -1)
+ { (void) shmctl(shmid[m], IPC_RMID, NULL);
+ } }
+#ifdef SEP_STATE
+ if (shmid_M != -1)
+ { (void) shmctl(shmid_M, IPC_RMID, NULL);
+ }
+#else
+ if (shmid_S != -1)
+ { (void) shmctl(shmid_S, IPC_RMID, NULL);
+ }
+ for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)
+ { shmid_M = (int) (last_pool->dc_id);
+ nxt_pool = last_pool->nxt; /* as a pre-caution only */
+ if (shmid_M != -1)
+ { (void) shmctl(shmid_M, IPC_RMID, NULL);
+ } }
+#endif
+}
+#endif
+
+void
+sudden_stop(char *s)
+{ char b[64];
+ int i;
+
+ printf("cpu%d: stop - %s\n", core_id, s);
+#if !defined(WIN32) && !defined(WIN64)
+ if (proxy_pid != 0)
+ { rm_shared_segments();
+ }
+#endif
+ if (search_terminated != NULL)
+ { if (*search_terminated != 0)
+ { if (verbose)
+ { printf("cpu%d: termination initiated (%d)\n",
+ core_id, *search_terminated);
+ }
+ } else
+ { if (verbose)
+ { printf("cpu%d: initiated termination\n", core_id);
+ }
+ *search_terminated |= 8; /* sudden_stop */
+ }
+ if (core_id == 0)
+ { if (((*search_terminated) & 4) /* uerror in one of the cpus */
+ && !((*search_terminated) & (8|32|128|256))) /* abnormal stop */
+ { if (errors == 0) errors++; /* we know there is at least 1 */
+ }
+ wrapup(); /* incomplete stats, but at least something */
+ }
+ return;
+ } /* else: should rarely happen, take more drastic measures */
+
+ if (core_id == 0) /* local root process */
+ { for (i = 1; i < NCORE; i++) /* not for 0 of course */
+ {
+#if defined(WIN32) || defined(WIN64)
+ DWORD dwExitCode = 0;
+ GetExitCodeProcess(worker_handles[i], &dwExitCode);
+ if (dwExitCode == STILL_ACTIVE)
+ { TerminateProcess(worker_handles[i], 0);
+ }
+ printf("cpu0: terminate %d %d\n",
+ worker_pids[i], (dwExitCode == STILL_ACTIVE));
+#else
+ sprintf(b, "kill -%d %d", SIGKILL, worker_pids[i]);
+ system(b); /* if this is a proxy: receive half */
+ printf("cpu0: %s\n", b);
+#endif
+ }
+ issued_kill++;
+ } else
+ { /* on WIN32/WIN64 -- these merely kills the root process... */
+ if (was_interrupted == 0)
+ { sprintf(b, "kill -%d %d", SIGINT, worker_pids[0]);
+ system(b); /* warn the root process */
+ printf("cpu%d: %s\n", core_id, b);
+ issued_kill++;
+ } }
+}
+
+#define iam_alive() is_alive[core_id]++
+
+extern int crash_test(double);
+extern void crash_reset(void);
+
+int
+someone_crashed(int wait_type)
+{ static double last_value = 0.0;
+ static int count = 0;
+
+ if (search_terminated == NULL
+ || *search_terminated != 0)
+ {
+ if (!(*search_terminated & (8|32|128|256)))
+ { if (count++ < 100*NCORE)
+ { return 0;
+ } }
+ return 1;
+ }
+ /* check left neighbor only */
+ if (last_value == is_alive[(core_id + NCORE - 1) % NCORE])
+ { if (count++ >= 100) /* to avoid unnecessary checks */
+ { return 1;
+ }
+ return 0;
+ }
+ last_value = is_alive[(core_id + NCORE - 1) % NCORE];
+ count = 0;
+ crash_reset();
+ return 0;
+}
+
+void
+sleep_report(void)
+{
+ enter_critical(GLOBAL_LOCK);
+ if (verbose)
+ {
+#ifdef NGQ
+ printf("cpu%d: locks: global %g\tother %g\t",
+ core_id, glock_wait[0], lock_wait - glock_wait[0]);
+#else
+ printf("cpu%d: locks: GL %g, RQ %g, WQ %g, HT %g\t",
+ core_id, glock_wait[0], glock_wait[1], glock_wait[2],
+ lock_wait - glock_wait[0] - glock_wait[1] - glock_wait[2]);
+#endif
+ printf("waits: states %g slots %g\n", frame_wait, free_wait);
+#ifndef NGQ
+ printf("cpu%d: gq [tries %g, room %g, noroom %g]\n", core_id, gq_tries, gq_hasroom, gq_hasnoroom);
+ if (core_id == 0 && (*gr_readmiss >= 1.0 || *gr_readmiss >= 1.0 || *grcnt != 0))
+ printf("cpu0: gq [readmiss: %g, writemiss: %g cnt %d]\n", *gr_readmiss, *gr_writemiss, *grcnt);
+#endif
+ }
+ if (free_wait > 1000000.)
+ #ifndef NGQ
+ if (!a_cycles)
+ { printf("hint: this search may be faster with a larger work-queue\n");
+ printf(" (-DSET_WQ_SIZE=N with N>%g), and/or with -DUSE_DISK\n",
+ GWQ_SIZE/sizeof(SM_frame));
+ printf(" or with a larger value for -zN (N>%d)\n", z_handoff);
+ #else
+ { printf("hint: this search may be faster if compiled without -DNGQ, with -DUSE_DISK, ");
+ printf("or with a larger -zN (N>%d)\n", z_handoff);
+ #endif
+ }
+ leave_critical(GLOBAL_LOCK);
+}
+
+#ifndef MAX_DSK_FILE
+ #define MAX_DSK_FILE 1000000 /* default is max 1M states per file */
+#endif
+
+void
+multi_usage(FILE *fd)
+{ static int warned = 0;
+ if (warned > 0) { return; } else { warned++; }
+ fprintf(fd, "\n");
+ fprintf(fd, "Defining multi-core mode:\n\n");
+ fprintf(fd, " -DDUAL_CORE --> same as -DNCORE=2\n");
+ fprintf(fd, " -DQUAD_CORE --> same as -DNCORE=4\n");
+ fprintf(fd, " -DNCORE=N --> enables multi_core verification if N>1\n");
+ fprintf(fd, "\n");
+ fprintf(fd, "Additional directives supported in multi-core mode:\n\n");
+ fprintf(fd, " -DSEP_STATE --> forces separate statespaces instead of a single shared state space\n");
+ fprintf(fd, " -DNUSE_DISK --> use disk for storing states when a work queue overflows\n");
+ fprintf(fd, " -DMAX_DSK_FILE --> max nr of states per diskfile (%d)\n", MAX_DSK_FILE);
+ fprintf(fd, " -DFULL_TRAIL --> support full error trails (increases memory use)\n");
+ fprintf(fd, "\n");
+ fprintf(fd, "More advanced use (should rarely need changing):\n\n");
+ fprintf(fd, " To change the nr of states that can be stored in the global queue\n");
+ fprintf(fd, " (lower numbers allow for more states to be stored, prefer multiples of 8):\n");
+ fprintf(fd, " -DVMAX=N --> upperbound on statevector for handoffs (N=%d)\n", VMAX);
+ fprintf(fd, " -DPMAX=N --> upperbound on nr of procs (default: N=%d)\n", PMAX);
+ fprintf(fd, " -DQMAX=N --> upperbound on nr of channels (default: N=%d)\n", QMAX);
+ fprintf(fd, "\n");
+ fprintf(fd, " To set the total amount of memory reserved for the global workqueue:\n");
+ fprintf(fd, " -DSET_WQ_SIZE=N --> default: N=128 (defined in MBytes)\n\n");
+ fprintf(fd, " To force the use of a single global heap, instead of separate heaps:\n");
+ fprintf(fd, " -DGLOB_HEAP\n");
+ fprintf(fd, "\n");
+ fprintf(fd, " To define a fct to initialize data before spawning processes (use quotes):\n");
+ fprintf(fd, " \"-DC_INIT=fct()\"\n");
+ fprintf(fd, "\n");
+ fprintf(fd, " Timer settings for termination and crash detection:\n");
+ fprintf(fd, " -DSHORT_T=N --> timeout for termination detection trigger (N=%g)\n", (double) SHORT_T);
+ fprintf(fd, " -DLONG_T=N --> timeout for giving up on termination detection (N=%g)\n", (double) LONG_T);
+ fprintf(fd, " -DONESECOND --> (1<<29) --> timeout waiting for a free slot -- to check for crash\n");
+ fprintf(fd, " -DT_ALERT --> collect stats on crash alert timeouts\n\n");
+ fprintf(fd, "Help with Linux/Windows/Cygwin configuration for multi-core:\n");
+ fprintf(fd, " http://spinroot.com/spin/multicore/V5_Readme.html\n");
+ fprintf(fd, "\n");
+}
+#if NCORE>1 && defined(FULL_TRAIL)
+typedef struct Stack_Tree {
+ uchar pr; /* process that made transition */
+ T_ID t_id; /* id of transition */
+ volatile struct Stack_Tree *prv; /* backward link towards root */
+} Stack_Tree;
+
+struct H_el *grab_shared(int);
+volatile Stack_Tree **stack_last; /* in shared memory */
+char *stack_cache = NULL; /* local */
+int nr_cached = 0; /* local */
+
+#ifndef CACHE_NR
+ #define CACHE_NR 1024
+#endif
+
+volatile Stack_Tree *
+stack_prefetch(void)
+{ volatile Stack_Tree *st;
+
+ if (nr_cached == 0)
+ { stack_cache = (char *) grab_shared(CACHE_NR * sizeof(Stack_Tree));
+ nr_cached = CACHE_NR;
+ }
+ st = (volatile Stack_Tree *) stack_cache;
+ stack_cache += sizeof(Stack_Tree);
+ nr_cached--;
+ return st;
+}
+
+void
+Push_Stack_Tree(short II, T_ID t_id)
+{ volatile Stack_Tree *st;
+
+ st = (volatile Stack_Tree *) stack_prefetch();
+ st->pr = II;
+ st->t_id = t_id;
+ st->prv = (Stack_Tree *) stack_last[core_id];
+ stack_last[core_id] = st;
+}
+
+void
+Pop_Stack_Tree(void)
+{ volatile Stack_Tree *cf = stack_last[core_id];
+
+ if (cf)
+ { stack_last[core_id] = cf->prv;
+ } else if (nr_handoffs * z_handoff + depth > 0)
+ { printf("cpu%d: error pop_stack_tree (depth %d)\n",
+ core_id, depth);
+ }
+}
+#endif
+
+void
+e_critical(int which)
+{ double cnt_start;
+
+ if (readtrail || iamin[which] > 0)
+ { if (!readtrail && verbose)
+ { printf("cpu%d: Double Lock on %d (now %d)\n",
+ core_id, which, iamin[which]+1);
+ fflush(stdout);
+ }
+ iamin[which]++; /* local variable */
+ return;
+ }
+
+ cnt_start = lock_wait;
+
+ while (sh_lock != NULL) /* as long as we have shared memory */
+ { int r = tas(&sh_lock[which]);
+ if (r == 0)
+ { iamin[which] = 1;
+ return; /* locked */
+ }
+
+ lock_wait++;
+#ifndef NGQ
+ if (which < 3) { glock_wait[which]++; }
+#else
+ if (which == 0) { glock_wait[which]++; }
+#endif
+ iam_alive();
+
+ if (lock_wait - cnt_start > TenSeconds)
+ { printf("cpu%d: lock timeout on %d\n", core_id, which);
+ cnt_start = lock_wait;
+ if (someone_crashed(1))
+ { sudden_stop("lock timeout");
+ pan_exit(1);
+ } } }
+}
+
+void
+x_critical(int which)
+{
+ if (iamin[which] != 1)
+ { if (iamin[which] > 1)
+ { iamin[which]--; /* this is thread-local - no races on this one */
+ if (!readtrail && verbose)
+ { printf("cpu%d: Partial Unlock on %d (%d more needed)\n",
+ core_id, which, iamin[which]);
+ fflush(stdout);
+ }
+ return;
+ } else /* iamin[which] <= 0 */
+ { if (!readtrail)
+ { printf("cpu%d: Invalid Unlock iamin[%d] = %d\n",
+ core_id, which, iamin[which]);
+ fflush(stdout);
+ }
+ return;
+ } }
+
+ if (sh_lock != NULL)
+ { iamin[which] = 0;
+ sh_lock[which] = 0; /* unlock */
+ }
+}
+
+void
+#if defined(WIN32) || defined(WIN64)
+start_proxy(char *s, DWORD r_pid)
+#else
+start_proxy(char *s, int r_pid)
+#endif
+{ char Q_arg[16], Z_arg[16], Y_arg[16];
+ char *args[32], *ptr;
+ int argcnt = 0;
+
+ sprintf(Q_arg, "-Q%d", getpid());
+ sprintf(Y_arg, "-Y%d", r_pid);
+ sprintf(Z_arg, "-Z%d", proxy_pid /* core_id */);
+
+ args[argcnt++] = "proxy";
+ args[argcnt++] = s; /* -r or -s */
+ args[argcnt++] = Q_arg;
+ args[argcnt++] = Z_arg;
+ args[argcnt++] = Y_arg;
+
+ if (strlen(o_cmdline) > 0)
+ { ptr = o_cmdline; /* assume args separated by spaces */
+ do { args[argcnt++] = ptr++;
+ if ((ptr = strchr(ptr, ' ')) != NULL)
+ { while (*ptr == ' ')
+ { *ptr++ = '\0';
+ }
+ } else
+ { break;
+ }
+ } while (argcnt < 31);
+ }
+ args[argcnt] = NULL;
+#if defined(WIN32) || defined(WIN64)
+ execvp("pan_proxy", args); /* no return */
+#else
+ execvp("./pan_proxy", args); /* no return */
+#endif
+ Uerror("pan_proxy exec failed");
+}
+/*** end of common code fragment ***/
+
+#if !defined(WIN32) && !defined(WIN64)
+void
+init_shm(void) /* initialize shared work-queues - linux/cygwin */
+{ key_t key[NR_QS];
+ int n, m;
+ int must_exit = 0;
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 3: allocate shared workqueues %g MB\n",
+ ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.) );
+ }
+ for (m = 0; m < NR_QS; m++) /* last q is the global q */
+ { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;
+ key[m] = ftok(PanSource, m+1);
+ if (key[m] == -1)
+ { perror("ftok shared queues"); must_exit = 1; break;
+ }
+
+ if (core_id == 0) /* root creates */
+ { /* check for stale copy */
+ shmid[m] = shmget(key[m], (size_t) qsize, 0600);
+ if (shmid[m] != -1) /* yes there is one; remove it */
+ { printf("cpu0: removing stale q%d, status: %d\n",
+ m, shmctl(shmid[m], IPC_RMID, NULL));
+ }
+ shmid[m] = shmget(key[m], (size_t) qsize, 0600|IPC_CREAT|IPC_EXCL);
+ memcnt += qsize;
+ } else /* workers attach */
+ { shmid[m] = shmget(key[m], (size_t) qsize, 0600);
+ /* never called, since we create shm *before* we fork */
+ }
+ if (shmid[m] == -1)
+ { perror("shmget shared queues"); must_exit = 1; break;
+ }
+
+ shared_mem[m] = (char *) shmat(shmid[m], (void *) 0, 0); /* attach */
+ if (shared_mem[m] == (char *) -1)
+ { fprintf(stderr, "error: cannot attach shared wq %d (%d Mb)\n",
+ m+1, (int) (qsize/(1048576.)));
+ perror("shmat shared queues"); must_exit = 1; break;
+ }
+
+ m_workq[m] = (SM_frame *) shared_mem[m];
+ if (core_id == 0)
+ { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;
+ for (n = 0; n < nframes; n++)
+ { m_workq[m][n].m_vsize = 0;
+ m_workq[m][n].m_boq = 0;
+ } } }
+
+ if (must_exit)
+ { rm_shared_segments();
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1); /* calls cleanup_shm */
+ }
+}
+
+static uchar *
+prep_shmid_S(size_t n) /* either sets SS or H_tab, linux/cygwin */
+{ char *rval;
+#ifndef SEP_STATE
+ key_t key;
+
+ if (verbose && core_id == 0)
+ {
+ #ifdef BITSTATE
+ printf("cpu0: step 1: allocate shared bitstate %g Mb\n",
+ (double) n / (1048576.));
+ #else
+ printf("cpu0: step 1: allocate shared hastable %g Mb\n",
+ (double) n / (1048576.));
+ #endif
+ }
+ #ifdef MEMLIM
+ if (memcnt + (double) n > memlim)
+ { printf("cpu0: S %8g + %d Kb exceeds memory limit of %8g Mb\n",
+ memcnt/1024., n/1024, memlim/(1048576.));
+ printf("cpu0: insufficient memory -- aborting\n");
+ exit(1);
+ }
+ #endif
+
+ key = ftok(PanSource, NCORE+2); /* different from queues */
+ if (key == -1)
+ { perror("ftok shared bitstate or hashtable");
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+
+ if (core_id == 0) /* root */
+ { shmid_S = shmget(key, n, 0600);
+ if (shmid_S != -1)
+ { printf("cpu0: removing stale segment, status: %d\n",
+ shmctl(shmid_S, IPC_RMID, NULL));
+ }
+ shmid_S = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);
+ memcnt += (double) n;
+ } else /* worker */
+ { shmid_S = shmget(key, n, 0600);
+ }
+ if (shmid_S == -1)
+ { perror("shmget shared bitstate or hashtable too large?");
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+
+ rval = (char *) shmat(shmid_S, (void *) 0, 0); /* attach */
+ if ((char *) rval == (char *) -1)
+ { perror("shmat shared bitstate or hashtable");
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+#else
+ rval = (char *) emalloc(n);
+#endif
+ return (uchar *) rval;
+}
+
+#define TRY_AGAIN 1
+#define NOT_AGAIN 0
+
+static char shm_prep_result;
+
+static uchar *
+prep_state_mem(size_t n) /* sets memory arena for states linux/cygwin */
+{ char *rval;
+ key_t key;
+ static int cnt = 3; /* start larger than earlier ftok calls */
+
+ shm_prep_result = NOT_AGAIN; /* default */
+ if (verbose && core_id == 0)
+ { printf("cpu0: step 2+: pre-allocate memory arena %d of %6.2g Mb\n",
+ cnt-3, (double) n / (1048576.));
+ }
+ #ifdef MEMLIM
+ if (memcnt + (double) n > memlim)
+ { printf("cpu0: error: M %.0f + %.0f Kb exceeds memory limit of %.0f Mb\n",
+ memcnt/1024.0, (double) n/1024.0, memlim/(1048576.));
+ return NULL;
+ }
+ #endif
+
+ key = ftok(PanSource, NCORE+cnt); cnt++;
+ if (key == -1)
+ { perror("ftok T");
+ printf("pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+
+ if (core_id == 0)
+ { shmid_M = shmget(key, n, 0600);
+ if (shmid_M != -1)
+ { printf("cpu0: removing stale memory segment %d, status: %d\n",
+ cnt-3, shmctl(shmid_M, IPC_RMID, NULL));
+ }
+ shmid_M = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);
+ /* memcnt += (double) n; -- only amount actually used is counted */
+ } else
+ { shmid_M = shmget(key, n, 0600);
+
+ }
+ if (shmid_M == -1)
+ { if (verbose)
+ { printf("error: failed to get pool of shared memory %d of %.0f Mb\n",
+ cnt-3, ((double)n)/(1048576.));
+ perror("state mem");
+ printf("pan: check './pan --' for usage details\n");
+ }
+ shm_prep_result = TRY_AGAIN;
+ return NULL;
+ }
+ rval = (char *) shmat(shmid_M, (void *) 0, 0); /* attach */
+
+ if ((char *) rval == (char *) -1)
+ { printf("cpu%d error: failed to attach pool of shared memory %d of %.0f Mb\n",
+ core_id, cnt-3, ((double)n)/(1048576.));
+ perror("state mem");
+ return NULL;
+ }
+ return (uchar *) rval;
+}
+
+void
+init_HT(unsigned long n) /* cygwin/linux version */
+{ volatile char *x;
+ double get_mem;
+#ifndef SEP_STATE
+ volatile char *dc_mem_start;
+ double need_mem, got_mem = 0.;
+#endif
+
+#ifdef SEP_STATE
+ #ifndef MEMLIM
+ if (verbose)
+ { printf("cpu0: steps 0,1: no -DMEMLIM set\n");
+ }
+ #else
+ if (verbose)
+ { printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n",
+ MEMLIM, ((double)n/(1048576.)), (((double) NCORE * LWQ_SIZE) + GWQ_SIZE) /(1048576.) );
+ }
+ #endif
+ get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *) + 4*sizeof(void *) + 2*sizeof(double);
+ /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */
+ get_mem += 4 * NCORE * sizeof(void *); /* prfree, prfull, prcnt, prmax */
+ #ifdef FULL_TRAIL
+ get_mem += (NCORE) * sizeof(Stack_Tree *); /* NCORE * stack_last */
+ #endif
+ x = (volatile char *) prep_state_mem((size_t) get_mem); /* work queues and basic structs */
+ shmid_X = (long) x;
+ if (x == NULL)
+ { printf("cpu0: could not allocate shared memory, see ./pan --\n");
+ exit(1);
+ }
+ search_terminated = (volatile unsigned int *) x; /* comes first */
+ x += sizeof(void *); /* maintain alignment */
+
+ is_alive = (volatile double *) x;
+ x += NCORE * sizeof(double);
+
+ sh_lock = (volatile int *) x;
+ x += CS_NR * sizeof(void *);
+
+ grfree = (volatile int *) x;
+ x += sizeof(void *);
+ grfull = (volatile int *) x;
+ x += sizeof(void *);
+ grcnt = (volatile int *) x;
+ x += sizeof(void *);
+ grmax = (volatile int *) x;
+ x += sizeof(void *);
+ prfree = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prfull = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prcnt = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prmax = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ gr_readmiss = (volatile double *) x;
+ x += sizeof(double);
+ gr_writemiss = (volatile double *) x;
+ x += sizeof(double);
+
+ #ifdef FULL_TRAIL
+ stack_last = (volatile Stack_Tree **) x;
+ x += NCORE * sizeof(Stack_Tree *);
+ #endif
+
+ #ifndef BITSTATE
+ H_tab = (struct H_el **) emalloc(n);
+ #endif
+#else
+ #ifndef MEMLIM
+ #warning MEMLIM not set
+ #define MEMLIM (2048)
+ #endif
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 0: -DMEMLIM=%d Mb minus hashtable+workqs (%g + %g Mb) leaves %g Mb\n",
+ MEMLIM, ((double)n/(1048576.)), (NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),
+ (memlim - memcnt - (double) n - (NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));
+ }
+ #ifndef BITSTATE
+ H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */
+ #endif
+ need_mem = memlim - memcnt - ((double) NCORE * LWQ_SIZE) - GWQ_SIZE;
+ if (need_mem <= 0.)
+ { Uerror("internal error -- shared state memory");
+ }
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 2: pre-allocate shared state memory %g Mb\n",
+ need_mem/(1048576.));
+ }
+#ifdef SEP_HEAP
+ SEG_SIZE = need_mem / NCORE;
+ if (verbose && core_id == 0)
+ { printf("cpu0: setting segsize to %6g MB\n",
+ SEG_SIZE/(1048576.));
+ }
+ #if defined(CYGWIN) || defined(__CYGWIN__)
+ if (SEG_SIZE > 512.*1024.*1024.)
+ { printf("warning: reducing SEG_SIZE of %g MB to 512MB (exceeds max for Cygwin)\n",
+ SEG_SIZE/(1024.*1024.));
+ SEG_SIZE = 512.*1024.*1024.;
+ }
+ #endif
+#endif
+ mem_reserved = need_mem;
+ while (need_mem > 1024.)
+ { get_mem = need_mem;
+shm_more:
+ if (get_mem > (double) SEG_SIZE)
+ { get_mem = (double) SEG_SIZE;
+ }
+ if (get_mem <= 0.0) break;
+
+ /* for allocating states: */
+ x = dc_mem_start = (volatile char *) prep_state_mem((size_t) get_mem);
+ if (x == NULL)
+ { if (shm_prep_result == NOT_AGAIN
+ || first_pool != NULL
+ || SEG_SIZE < (16. * 1048576.))
+ { break;
+ }
+ SEG_SIZE /= 2.;
+ if (verbose)
+ { printf("pan: lowered segsize to 0.000000\n", SEG_SIZE);
+ }
+ if (SEG_SIZE >= 1024.)
+ { goto shm_more;
+ }
+ break;
+ }
+
+ need_mem -= get_mem;
+ got_mem += get_mem;
+ if (first_pool == NULL)
+ { search_terminated = (volatile unsigned int *) x; /* comes first */
+ x += sizeof(void *); /* maintain alignment */
+
+ is_alive = (volatile double *) x;
+ x += NCORE * sizeof(double);
+
+ sh_lock = (volatile int *) x;
+ x += CS_NR * sizeof(void *);
+
+ grfree = (volatile int *) x;
+ x += sizeof(void *);
+ grfull = (volatile int *) x;
+ x += sizeof(void *);
+ grcnt = (volatile int *) x;
+ x += sizeof(void *);
+ grmax = (volatile int *) x;
+ x += sizeof(void *);
+ prfree = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prfull = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prcnt = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prmax = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ gr_readmiss = (volatile double *) x;
+ x += sizeof(double);
+ gr_writemiss = (volatile double *) x;
+ x += sizeof(double);
+ #ifdef FULL_TRAIL
+ stack_last = (volatile Stack_Tree **) x;
+ x += NCORE * sizeof(Stack_Tree *);
+ #endif
+ if (((long)x)&(sizeof(void *)-1)) /* 64-bit word alignment */
+ { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1));
+ }
+
+ #ifdef COLLAPSE
+ ncomps = (unsigned long *) x;
+ x += (256+2) * sizeof(unsigned long);
+ #endif
+ }
+
+ dc_shared = (sh_Allocater *) x; /* must be in shared memory */
+ x += sizeof(sh_Allocater);
+
+ if (core_id == 0) /* root only */
+ { dc_shared->dc_id = shmid_M;
+ dc_shared->dc_start = dc_mem_start;
+ dc_shared->dc_arena = x;
+ dc_shared->pattern = 1234567; /* protection */
+ dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start);
+ dc_shared->nxt = (long) 0;
+
+ if (last_pool == NULL)
+ { first_pool = last_pool = dc_shared;
+ } else
+ { last_pool->nxt = dc_shared;
+ last_pool = dc_shared;
+ }
+ } else if (first_pool == NULL)
+ { first_pool = dc_shared;
+ } }
+
+ if (need_mem > 1024.)
+ { printf("cpu0: could allocate only %g Mb of shared memory (wanted %g more)\n",
+ got_mem/(1048576.), need_mem/(1048576.));
+ }
+
+ if (!first_pool)
+ { printf("cpu0: insufficient memory -- aborting.\n");
+ exit(1);
+ }
+ /* we are still single-threaded at this point, with core_id 0 */
+ dc_shared = first_pool;
+
+#endif
+}
+
+ /* Test and Set assembly code */
+
+ #if defined(i386) || defined(__i386__) || defined(__x86_64__)
+ int
+ tas(volatile int *s) /* tested */
+ { int r;
+ __asm__ __volatile__(
+ "xchgl %0, %1 \n\t"
+ : "=r"(r), "=m"(*s)
+ : "0"(1), "m"(*s)
+ : "memory");
+
+ return r;
+ }
+ #elif defined(__arm__)
+ int
+ tas(volatile int *s) /* not tested */
+ { int r = 1;
+ __asm__ __volatile__(
+ "swpb %0, %0, [%3] \n"
+ : "=r"(r), "=m"(*s)
+ : "0"(r), "r"(s));
+
+ return r;
+ }
+ #elif defined(sparc) || defined(__sparc__)
+ int
+ tas(volatile int *s) /* not tested */
+ { int r = 1;
+ __asm__ __volatile__(
+ " ldstub [%2], %0 \n"
+ : "=r"(r), "=m"(*s)
+ : "r"(s));
+
+ return r;
+ }
+ #elif defined(ia64) || defined(__ia64__)
+ /* Intel Itanium */
+ int
+ tas(volatile int *s) /* tested */
+ { long int r;
+ __asm__ __volatile__(
+ " xchg4 %0=%1,%2 \n"
+ : "=r"(r), "+m"(*s)
+ : "r"(1)
+ : "memory");
+ return (int) r;
+ }
+ #else
+ #error missing definition of test and set operation for this platform
+ #endif
+
+void
+cleanup_shm(int val)
+{ volatile sh_Allocater *nxt_pool;
+ unsigned long cnt = 0;
+ int m;
+
+ if (nibis != 0)
+ { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val);
+ return;
+ } else
+ { nibis = 1;
+ }
+ if (search_terminated != NULL)
+ { *search_terminated |= 16; /* cleanup_shm */
+ }
+
+ for (m = 0; m < NR_QS; m++)
+ { if (shmdt((void *) shared_mem[m]) > 0)
+ { perror("shmdt detaching from shared queues");
+ } }
+
+#ifdef SEP_STATE
+ if (shmdt((void *) shmid_X) != 0)
+ { perror("shmdt detaching from shared state memory");
+ }
+#else
+ #ifdef BITSTATE
+ if (SS > 0 && shmdt((void *) SS) != 0)
+ { if (verbose)
+ { perror("shmdt detaching from shared bitstate arena");
+ } }
+ #else
+ if (core_id == 0)
+ { /* before detaching: */
+ for (nxt_pool = dc_shared; nxt_pool != NULL; nxt_pool = nxt_pool->nxt)
+ { cnt += nxt_pool->dc_size;
+ }
+ if (verbose)
+ { printf("cpu0: done, %ld Mb of shared state memory left\n",
+ cnt / (long)(1048576));
+ } }
+
+ if (shmdt((void *) H_tab) != 0)
+ { perror("shmdt detaching from shared hashtable");
+ }
+
+ for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)
+ { nxt_pool = last_pool->nxt;
+ if (shmdt((void *) last_pool->dc_start) != 0)
+ { perror("shmdt detaching from shared state memory");
+ } }
+ first_pool = last_pool = NULL; /* precaution */
+ #endif
+#endif
+ /* detached from shared memory - so cannot use cpu_printf */
+ if (verbose)
+ { printf("cpu%d: done -- got %d states from queue\n",
+ core_id, nstates_get);
+ }
+}
+
+extern void give_up(int);
+extern void Read_Queue(int);
+
+void
+mem_get(void)
+{ SM_frame *f;
+ int is_parent;
+
+#if defined(MA) && !defined(SEP_STATE)
+ #error MA without SEP_STATE is not supported with multi-core
+#endif
+#ifdef BFS
+ #error BFS is not supported with multi-core
+#endif
+#ifdef SC
+ #error SC is not supported with multi-core
+#endif
+ init_shm(); /* we are single threaded when this starts */
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 4: calling fork()\n");
+ }
+ fflush(stdout);
+
+/* if NCORE > 1 the child or the parent should fork N-1 more times
+ * the parent is the only process with core_id == 0 and is_parent > 0
+ * the workers have is_parent = 0 and core_id = 1..NCORE-1
+ */
+ if (core_id == 0)
+ { worker_pids[0] = getpid(); /* for completeness */
+ while (++core_id < NCORE) /* first worker sees core_id = 1 */
+ { is_parent = fork();
+ if (is_parent == -1)
+ { Uerror("fork failed");
+ }
+ if (is_parent == 0) /* this is a worker process */
+ { if (proxy_pid == core_id) /* always non-zero */
+ { start_proxy("-r", 0); /* no return */
+ }
+ goto adapt; /* root process continues spawning */
+ }
+ worker_pids[core_id] = is_parent;
+ }
+ /* note that core_id is now NCORE */
+ if (proxy_pid > 0 && proxy_pid < NCORE)
+ { proxy_pid_snd = fork();
+ if (proxy_pid_snd == -1)
+ { Uerror("proxy fork failed");
+ }
+ if (proxy_pid_snd == 0)
+ { start_proxy("-s", worker_pids[proxy_pid]); /* no return */
+ } } /* else continue */
+ if (is_parent > 0)
+ { core_id = 0; /* reset core_id for root process */
+ }
+ } else /* worker */
+ { static char db0[16]; /* good for up to 10^6 cores */
+ static char db1[16];
+adapt: tprefix = db0; sprefix = db1;
+ sprintf(tprefix, "cpu%d_trail", core_id);
+ sprintf(sprefix, "cpu%d_rst", core_id);
+ memcnt = 0; /* count only additionally allocated memory */
+ }
+ signal(SIGINT, give_up);
+
+ if (proxy_pid == 0) /* not in a cluster setup, pan_proxy must attach */
+ { rm_shared_segments(); /* mark all shared segments for removal on exit */
+ }
+ if (verbose)
+ { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid());
+ }
+#if defined(SEP_HEAP) && !defined(SEP_STATE)
+ { int i;
+ volatile sh_Allocater *ptr;
+ ptr = first_pool;
+ for (i = 0; i < NCORE && ptr != NULL; i++)
+ { if (i == core_id)
+ { my_heap = (char *) ptr->dc_arena;
+ my_size = (long) ptr->dc_size;
+ if (verbose)
+ cpu_printf("local heap %ld MB\n", my_size/(1048576));
+ break;
+ }
+ ptr = ptr->nxt; /* local */
+ }
+ if (my_heap == NULL)
+ { printf("cpu%d: no local heap\n", core_id);
+ pan_exit(1);
+ } /* else */
+ #if defined(CYGWIN) || defined(__CYGWIN__)
+ ptr = first_pool;
+ for (i = 0; i < NCORE && ptr != NULL; i++)
+ { ptr = ptr->nxt; /* local */
+ }
+ dc_shared = ptr; /* any remainder */
+ #else
+ dc_shared = NULL; /* used all mem for local heaps */
+ #endif
+ }
+#endif
+ if (core_id == 0 && !remote_party)
+ { new_state(); /* cpu0 explores root */
+ if (verbose)
+ cpu_printf("done with 1st dfs, nstates %g (put %d states), read q\n",
+ nstates, nstates_put);
+ dfs_phase2 = 1;
+ }
+ Read_Queue(core_id); /* all cores */
+
+ if (verbose)
+ { cpu_printf("put %6d states into queue -- got %6d\n",
+ nstates_put, nstates_get);
+ }
+ if (proxy_pid != 0)
+ { rm_shared_segments();
+ }
+ done = 1;
+ wrapup();
+ exit(0);
+}
+
+#else
+int unpack_state(SM_frame *, int);
+#endif
+
+struct H_el *
+grab_shared(int n)
+{
+#ifndef SEP_STATE
+ char *rval = (char *) 0;
+
+ if (n == 0)
+ { printf("cpu%d: grab shared zero\n", core_id); fflush(stdout);
+ return (struct H_el *) rval;
+ } else if (n&(sizeof(void *)-1))
+ { n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */
+ }
+
+#ifdef SEP_HEAP
+ /* no locking */
+ if (my_heap != NULL && my_size > n)
+ { rval = my_heap;
+ my_heap += n;
+ my_size -= n;
+ goto done;
+ }
+#endif
+
+ if (!dc_shared)
+ { sudden_stop("pan: out of memory");
+ }
+
+ /* another lock is always already in effect when this is called */
+ /* but not always the same lock -- i.e., on different parts of the hashtable */
+ enter_critical(GLOBAL_LOCK); /* this must be independently mutex */
+#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64)
+ { static int noted = 0;
+ if (!noted)
+ { noted = 1;
+ printf("cpu%d: global heap has %ld bytes left, needed %d\n",
+ core_id, dc_shared?dc_shared->dc_size:0, n);
+ } }
+#endif
+#if 0
+ if (dc_shared->pattern != 1234567)
+ { leave_critical(GLOBAL_LOCK);
+ Uerror("overrun -- memory corruption");
+ }
+#endif
+ if (dc_shared->dc_size < n)
+ { if (verbose)
+ { printf("Next Pool %g Mb + %d\n", memcnt/(1048576.), n);
+ }
+ if (dc_shared->nxt == NULL
+ || dc_shared->nxt->dc_arena == NULL
+ || dc_shared->nxt->dc_size < n)
+ { printf("cpu%d: memcnt %g Mb + wanted %d bytes more\n",
+ core_id, memcnt / (1048576.), n);
+ leave_critical(GLOBAL_LOCK);
+ sudden_stop("out of memory -- aborting");
+ wrapup(); /* exits */
+ } else
+ { dc_shared = (sh_Allocater *) dc_shared->nxt;
+ } }
+
+ rval = (char *) dc_shared->dc_arena;
+ dc_shared->dc_arena += n;
+ dc_shared->dc_size -= (long) n;
+#if 0
+ if (VVERBOSE)
+ printf("cpu%d grab shared (%d bytes) -- %ld left\n",
+ core_id, n, dc_shared->dc_size);
+#endif
+ leave_critical(GLOBAL_LOCK);
+done:
+ memset(rval, 0, n);
+ memcnt += (double) n;
+
+ return (struct H_el *) rval;
+#else
+ return (struct H_el *) emalloc(n);
+#endif
+}
+
+SM_frame *
+Get_Full_Frame(int n)
+{ SM_frame *f;
+ double cnt_start = frame_wait;
+
+ f = &m_workq[n][prfull[n]];
+ while (f->m_vsize == 0) /* await full slot LOCK : full frame */
+ { iam_alive();
+#ifndef NGQ
+ #ifndef SAFETY
+ if (!a_cycles || core_id != 0)
+ #endif
+ if (*grcnt > 0) /* accessed outside lock, but safe even if wrong */
+ { enter_critical(GQ_RD); /* gq - read access */
+ if (*grcnt > 0) /* could have changed */
+ { f = &m_workq[NCORE][*grfull]; /* global q */
+ if (f->m_vsize == 0)
+ { /* writer is still filling the slot */
+ *gr_writemiss++;
+ f = &m_workq[n][prfull[n]]; /* reset */
+ } else
+ { *grfull = (*grfull+1) % (GN_FRAMES);
+ enter_critical(GQ_WR);
+ *grcnt = *grcnt - 1;
+ leave_critical(GQ_WR);
+ leave_critical(GQ_RD);
+ return f;
+ } }
+ leave_critical(GQ_RD);
+ }
+#endif
+ if (frame_wait++ - cnt_start > Delay)
+ { if (0)
+ { cpu_printf("timeout on q%d -- %u -- query %d\n",
+ n, f, query_in_progress);
+ }
+ return (SM_frame *) 0; /* timeout */
+ } }
+ iam_alive();
+ if (VVERBOSE) cpu_printf("got frame from q%d\n", n);
+ prfull[n] = (prfull[n] + 1) % (LN_FRAMES);
+ enter_critical(QLOCK(n));
+ prcnt[n]--; /* lock out increments */
+ leave_critical(QLOCK(n));
+ return f;
+}
+
+SM_frame *
+Get_Free_Frame(int n)
+{ SM_frame *f;
+ double cnt_start = free_wait;
+
+ if (VVERBOSE) { cpu_printf("get free frame from q%d\n", n); }
+
+ if (n == NCORE) /* global q */
+ { f = &(m_workq[n][lrfree]);
+ } else
+ { f = &(m_workq[n][prfree[n]]);
+ }
+ while (f->m_vsize != 0) /* await free slot LOCK : free slot */
+ { iam_alive();
+ if (free_wait++ - cnt_start > OneSecond)
+ { if (verbose)
+ { cpu_printf("timeout waiting for free slot q%d\n", n);
+ }
+ cnt_start = free_wait;
+ if (someone_crashed(1))
+ { printf("cpu%d: search terminated\n", core_id);
+ sudden_stop("get free frame");
+ pan_exit(1);
+ } } }
+ if (n != NCORE)
+ { prfree[n] = (prfree[n] + 1) % (LN_FRAMES);
+ enter_critical(QLOCK(n));
+ prcnt[n]++; /* lock out decrements */
+ if (prmax[n] < prcnt[n])
+ { prmax[n] = prcnt[n];
+ }
+ leave_critical(QLOCK(n));
+ }
+ return f;
+}
+#ifndef NGQ
+int
+GlobalQ_HasRoom(void)
+{ int rval = 0;
+
+ gq_tries++;
+ if (*grcnt < GN_FRAMES) /* there seems to be room */
+ { enter_critical(GQ_WR); /* gq write access */
+ if (*grcnt < GN_FRAMES)
+ { if (m_workq[NCORE][*grfree].m_vsize != 0)
+ { /* can happen if reader is slow emptying slot */
+ *gr_readmiss++;
+ goto out; /* dont wait: release lock and return */
+ }
+ lrfree = *grfree; /* Get_Free_Frame use lrfree in this mode */
+ *grfree = (*grfree + 1) % GN_FRAMES;
+ *grcnt = *grcnt + 1; /* count nr of slots filled -- no additional lock needed */
+ if (*grmax < *grcnt) *grmax = *grcnt;
+ leave_critical(GQ_WR); /* for short lock duration */
+ gq_hasroom++;
+ mem_put(NCORE); /* copy state into reserved slot */
+ rval = 1; /* successfull handoff */
+ } else
+ { gq_hasnoroom++;
+out: leave_critical(GQ_WR);
+ } }
+ return rval;
+}
+#endif
+
+int
+unpack_state(SM_frame *f, int from_q)
+{ int i, j;
+ static struct H_el D_State;
+
+ if (f->m_vsize > 0)
+ { boq = f->m_boq;
+ if (boq > 256)
+ { cpu_printf("saw control %d, expected state\n", boq);
+ return 0;
+ }
+ vsize = f->m_vsize;
+correct:
+ memcpy((uchar *) &now, (uchar *) f->m_now, vsize);
+ for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
+ { Mask[i] = (f->m_Mask[i/8] & (1<<j)) ? 1 : 0;
+ }
+ if (now._nr_pr > 0)
+ { memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT));
+ memcpy((uchar *) proc_skip, (uchar *) f->m_p_skip, now._nr_pr * sizeof(uchar));
+ }
+ if (now._nr_qs > 0)
+ { memcpy((uchar *) q_offset, (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT));
+ memcpy((uchar *) q_skip, (uchar *) f->m_q_skip, now._nr_qs * sizeof(uchar));
+ }
+#ifndef NOVSZ
+ if (vsize != now._vsz)
+ { cpu_printf("vsize %d != now._vsz %d (type %d) %d\n",
+ vsize, now._vsz, f->m_boq, f->m_vsize);
+ vsize = now._vsz;
+ goto correct; /* rare event: a race */
+ }
+#endif
+ hmax = max(hmax, vsize);
+
+ if (f != &cur_Root)
+ { memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame));
+ }
+
+ if (((now._a_t) & 1) == 1) /* i.e., when starting nested DFS */
+ { A_depth = depthfound = 0;
+ memcpy((uchar *)&A_Root, (uchar *)&now, vsize);
+ }
+ nr_handoffs = f->nr_handoffs;
+ } else
+ { cpu_printf("pan: state empty\n");
+ }
+
+ depth = 0;
+ trpt = &trail[1];
+ trpt->tau = f->m_tau;
+ trpt->o_pm = f->m_o_pm;
+
+ (trpt-1)->ostate = &D_State; /* stub */
+ trpt->ostate = &D_State;
+
+#ifdef FULL_TRAIL
+ if (upto > 0)
+ { stack_last[core_id] = (Stack_Tree *) f->m_stack;
+ }
+ #if defined(VERBOSE)
+ if (stack_last[core_id])
+ { cpu_printf("%d: UNPACK -- SET m_stack %u (%d,%d)\n",
+ depth, stack_last[core_id], stack_last[core_id]->pr,
+ stack_last[core_id]->t_id);
+ }
+ #endif
+#endif
+
+ if (!trpt->o_t)
+ { static Trans D_Trans;
+ trpt->o_t = &D_Trans;
+ }
+
+ #ifdef VERI
+ if ((trpt->tau & 4) != 4)
+ { trpt->tau |= 4; /* the claim moves first */
+ cpu_printf("warning: trpt was not up to date\n");
+ }
+ #endif
+
+ for (i = 0; i < (int) now._nr_pr; i++)
+ { P0 *ptr = (P0 *) pptr(i);
+ #ifndef NP
+ if (accpstate[ptr->_t][ptr->_p])
+ { trpt->o_pm |= 2;
+ }
+ #else
+ if (progstate[ptr->_t][ptr->_p])
+ { trpt->o_pm |= 4;
+ }
+ #endif
+ }
+
+ #ifdef EVENT_TRACE
+ #ifndef NP
+ if (accpstate[EVENT_TRACE][now._event])
+ { trpt->o_pm |= 2;
+ }
+ #else
+ if (progstate[EVENT_TRACE][now._event])
+ { trpt->o_pm |= 4;
+ }
+ #endif
+ #endif
+
+ #if defined(C_States) && (HAS_TRACK==1)
+ /* restore state of tracked C objects */
+ c_revert((uchar *) &(now.c_state[0]));
+ #if (HAS_STACK==1)
+ c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */
+ #endif
+ #endif
+ return 1;
+}
+
+void
+write_root(void) /* for trail file */
+{ int fd;
+
+ if (iterative == 0 && Nr_Trails > 1)
+ sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix);
+ else
+ sprintf(fnm, "%s.%s", TrailFile, sprefix);
+
+ if (cur_Root.m_vsize == 0)
+ { (void) unlink(fnm); /* remove possible old copy */
+ return; /* its the default initial state */
+ }
+
+ if ((fd = creat(fnm, TMODE)) < 0)
+ { char *q;
+ if ((q = strchr(TrailFile, '.')))
+ { *q = '\0'; /* strip .pml */
+ if (iterative == 0 && Nr_Trails-1 > 0)
+ sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix);
+ else
+ sprintf(fnm, "%s.%s", TrailFile, sprefix);
+ *q = '.';
+ fd = creat(fnm, TMODE);
+ }
+ if (fd < 0)
+ { cpu_printf("pan: cannot create %s\n", fnm);
+ perror("cause");
+ return;
+ } }
+
+ if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))
+ { cpu_printf("pan: error writing %s\n", fnm);
+ } else
+ { cpu_printf("pan: wrote %s\n", fnm);
+ }
+ close(fd);
+}
+
+void
+set_root(void)
+{ int fd;
+ char *q;
+ char MyFile[512];
+ char MySuffix[16];
+ char *ssuffix = "rst";
+ int try_core = 1;
+
+ strcpy(MyFile, TrailFile);
+try_again:
+ if (whichtrail > 0)
+ { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix);
+ fd = open(fnm, O_RDONLY, 0);
+ if (fd < 0 && (q = strchr(MyFile, '.')))
+ { *q = '\0'; /* strip .pml */
+ sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix);
+ *q = '.';
+ fd = open(fnm, O_RDONLY, 0);
+ }
+ } else
+ { sprintf(fnm, "%s.%s", MyFile, ssuffix);
+ fd = open(fnm, O_RDONLY, 0);
+ if (fd < 0 && (q = strchr(MyFile, '.')))
+ { *q = '\0'; /* strip .pml */
+ sprintf(fnm, "%s.%s", MyFile, ssuffix);
+ *q = '.';
+ fd = open(fnm, O_RDONLY, 0);
+ } }
+
+ if (fd < 0)
+ { if (try_core < NCORE)
+ { ssuffix = MySuffix;
+ sprintf(ssuffix, "cpu%d_rst", try_core++);
+ goto try_again;
+ }
+ cpu_printf("no file '%s.rst' or '%s' (not an error)\n", MyFile, fnm);
+ } else
+ { if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))
+ { cpu_printf("read error %s\n", fnm);
+ close(fd);
+ pan_exit(1);
+ }
+ close(fd);
+ (void) unpack_state(&cur_Root, -2);
+#ifdef SEP_STATE
+ cpu_printf("partial trail -- last few steps only\n");
+#endif
+ cpu_printf("restored root from '%s'\n", fnm);
+ printf("=====State:=====\n");
+ { int i, j; P0 *z;
+ for (i = 0; i < now._nr_pr; i++)
+ { z = (P0 *)pptr(i);
+ printf("proc %2d (%s) ", i, procname[z->_t]);
+ for (j = 0; src_all[j].src; j++)
+ if (src_all[j].tp == (int) z->_t)
+ { printf(" line %3d \"%s\" ",
+ src_all[j].src[z->_p], PanSource);
+ break;
+ }
+ printf("(state %d)\n", z->_p);
+ c_locals(i, z->_t);
+ }
+ c_globals();
+ }
+ printf("================\n");
+ }
+}
+
+#ifdef USE_DISK
+unsigned long dsk_written, dsk_drained;
+void mem_drain(void);
+#endif
+
+void
+m_clear_frame(SM_frame *f)
+{ int i, clr_sz = sizeof(SM_results);
+
+ for (i = 0; i <= _NP_; i++) /* all proctypes */
+ { clr_sz += NrStates[i]*sizeof(uchar);
+ }
+ memset(f, 0, clr_sz);
+ /* caution if sizeof(SM_results) > sizeof(SM_frame) */
+}
+
+#define TargetQ_Full(n) (m_workq[n][prfree[n]].m_vsize != 0)
+#define TargetQ_NotFull(n) (m_workq[n][prfree[n]].m_vsize == 0)
+
+int
+AllQueuesEmpty(void)
+{ int q;
+#ifndef NGQ
+ if (*grcnt != 0)
+ { return 0;
+ }
+#endif
+ for (q = 0; q < NCORE; q++)
+ { if (prcnt[q] != 0)
+ { return 0;
+ } }
+ return 1;
+}
+
+void
+Read_Queue(int q)
+{ SM_frame *f, *of;
+ int remember, target_q;
+ SM_results *r;
+ double patience = 0.0;
+
+ target_q = (q + 1) % NCORE;
+
+ for (;;)
+ { f = Get_Full_Frame(q);
+ if (!f) /* 1 second timeout -- and trigger for Query */
+ { if (someone_crashed(2))
+ { printf("cpu%d: search terminated [code %d]\n",
+ core_id, search_terminated?*search_terminated:-1);
+ sudden_stop("");
+ pan_exit(1);
+ }
+#ifdef TESTING
+ /* to profile with cc -pg and gprof pan.exe -- set handoff depth beyond maxdepth */
+ exit(0);
+#endif
+ remember = *grfree;
+ if (core_id == 0 /* root can initiate termination */
+ && remote_party == 0 /* and only the original root */
+ && query_in_progress == 0 /* unless its already in progress */
+ && AllQueuesEmpty())
+ { f = Get_Free_Frame(target_q);
+ query_in_progress = 1; /* only root process can do this */
+ if (!f) { Uerror("Fatal1: no free slot"); }
+ f->m_boq = QUERY; /* initiate Query */
+ if (verbose)
+ { cpu_printf("snd QUERY to q%d (%d) into slot %d\n",
+ target_q, nstates_get + 1, prfree[target_q]-1);
+ }
+ f->m_vsize = remember + 1;
+ /* number will not change unless we receive more states */
+ } else if (patience++ > OneHour) /* one hour watchdog timer */
+ { cpu_printf("timeout -- giving up\n");
+ sudden_stop("queue timeout");
+ pan_exit(1);
+ }
+ if (0) cpu_printf("timed out -- try again\n");
+ continue;
+ }
+ patience = 0.0; /* reset watchdog */
+
+ if (f->m_boq == QUERY)
+ { if (verbose)
+ { cpu_printf("got QUERY on q%d (%d <> %d) from slot %d\n",
+ q, f->m_vsize, nstates_put + 1, prfull[q]-1);
+ snapshot();
+ }
+ remember = f->m_vsize;
+ f->m_vsize = 0; /* release slot */
+
+ if (core_id == 0 && remote_party == 0) /* original root cpu0 */
+ { if (query_in_progress == 1 /* didn't send more states in the interim */
+ && *grfree + 1 == remember) /* no action on global queue meanwhile */
+ { if (verbose) cpu_printf("Termination detected\n");
+ if (TargetQ_Full(target_q))
+ { if (verbose)
+ cpu_printf("warning: target q is full\n");
+ }
+ f = Get_Free_Frame(target_q);
+ if (!f) { Uerror("Fatal2: no free slot"); }
+ m_clear_frame(f);
+ f->m_boq = QUIT; /* send final Quit, collect stats */
+ f->m_vsize = 111; /* anything non-zero will do */
+ if (verbose)
+ cpu_printf("put QUIT on q%d\n", target_q);
+ } else
+ { if (verbose) cpu_printf("Stale Query\n");
+#ifdef USE_DISK
+ mem_drain();
+#endif
+ }
+ query_in_progress = 0;
+ } else
+ { if (TargetQ_Full(target_q))
+ { if (verbose)
+ cpu_printf("warning: forward query - target q full\n");
+ }
+ f = Get_Free_Frame(target_q);
+ if (verbose)
+ cpu_printf("snd QUERY response to q%d (%d <> %d) in slot %d\n",
+ target_q, remember, *grfree + 1, prfree[target_q]-1);
+ if (!f) { Uerror("Fatal4: no free slot"); }
+
+ if (*grfree + 1 == remember) /* no action on global queue */
+ { f->m_boq = QUERY; /* forward query, to root */
+ f->m_vsize = remember;
+ } else
+ { f->m_boq = QUERY_F; /* no match -- busy */
+ f->m_vsize = 112; /* anything non-zero */
+#ifdef USE_DISK
+ if (dsk_written != dsk_drained)
+ { mem_drain();
+ }
+#endif
+ } }
+ continue;
+ }
+
+ if (f->m_boq == QUERY_F)
+ { if (verbose)
+ { cpu_printf("got QUERY_F on q%d from slot %d\n", q, prfull[q]-1);
+ }
+ f->m_vsize = 0; /* release slot */
+
+ if (core_id == 0 && remote_party == 0) /* original root cpu0 */
+ { if (verbose) cpu_printf("No Match on Query\n");
+ query_in_progress = 0;
+ } else
+ { if (TargetQ_Full(target_q))
+ { if (verbose) cpu_printf("warning: forwarding query_f, target queue full\n");
+ }
+ f = Get_Free_Frame(target_q);
+ if (verbose) cpu_printf("forward QUERY_F to q%d into slot %d\n",
+ target_q, prfree[target_q]-1);
+ if (!f) { Uerror("Fatal5: no free slot"); }
+ f->m_boq = QUERY_F; /* cannot terminate yet */
+ f->m_vsize = 113; /* anything non-zero */
+ }
+#ifdef USE_DISK
+ if (dsk_written != dsk_drained)
+ { mem_drain();
+ }
+#endif
+ continue;
+ }
+
+ if (f->m_boq == QUIT)
+ { if (0) cpu_printf("done -- local memcnt %g Mb\n", memcnt/(1048576.));
+ retrieve_info((SM_results *) f); /* collect and combine stats */
+ if (verbose)
+ { cpu_printf("received Quit\n");
+ snapshot();
+ }
+ f->m_vsize = 0; /* release incoming slot */
+ if (core_id != 0)
+ { f = Get_Free_Frame(target_q); /* new outgoing slot */
+ if (!f) { Uerror("Fatal6: no free slot"); }
+ m_clear_frame(f); /* start with zeroed stats */
+ record_info((SM_results *) f);
+ f->m_boq = QUIT; /* forward combined results */
+ f->m_vsize = 114; /* anything non-zero */
+ if (verbose>1)
+ cpu_printf("fwd Results to q%d\n", target_q);
+ }
+ break; /* successful termination */
+ }
+
+ /* else: 0<= boq <= 255, means STATE transfer */
+ if (unpack_state(f, q) != 0)
+ { nstates_get++;
+ f->m_vsize = 0; /* release slot */
+ if (VVERBOSE) cpu_printf("Got state\n");
+
+ if (search_terminated != NULL
+ && *search_terminated == 0)
+ { new_state(); /* explore successors */
+ memset((uchar *) &cur_Root, 0, sizeof(SM_frame)); /* avoid confusion */
+ } else
+ { pan_exit(0);
+ }
+ } else
+ { pan_exit(0);
+ } }
+ if (verbose) cpu_printf("done got %d put %d\n", nstates_get, nstates_put);
+ sleep_report();
+}
+
+void
+give_up(int unused_x)
+{
+ if (search_terminated != NULL)
+ { *search_terminated |= 32; /* give_up */
+ }
+ if (!writing_trail)
+ { was_interrupted = 1;
+ snapshot();
+ cpu_printf("Give Up\n");
+ sleep_report();
+ pan_exit(1);
+ } else /* we are already terminating */
+ { cpu_printf("SIGINT\n");
+ }
+}
+
+void
+check_overkill(void)
+{
+ vmax_seen = (vmax_seen + 7)/ 8;
+ vmax_seen *= 8; /* round up to a multiple of 8 */
+
+ if (core_id == 0
+ && !remote_party
+ && nstates_put > 0
+ && VMAX - vmax_seen > 8)
+ {
+#ifdef BITSTATE
+ printf("cpu0: max VMAX value seen in this run: ");
+#else
+ printf("cpu0: recommend recompiling with ");
+#endif
+ printf("-DVMAX=%d\n", vmax_seen);
+ }
+}
+
+void
+mem_put(int q) /* handoff state to other cpu, workq q */
+{ SM_frame *f;
+ int i, j;
+
+ if (vsize > VMAX)
+ { vsize = (vsize + 7)/8; vsize *= 8; /* round up */
+ printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize);
+ Uerror("aborting");
+ }
+ if (now._nr_pr > PMAX)
+ { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr);
+ Uerror("aborting");
+ }
+ if (now._nr_qs > QMAX)
+ { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs);
+ Uerror("aborting");
+ }
+ if (vsize > vmax_seen) vmax_seen = vsize;
+ if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr;
+ if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs;
+
+ f = Get_Free_Frame(q); /* not called in likely deadlock states */
+ if (!f) { Uerror("Fatal3: no free slot"); }
+
+ if (VVERBOSE) cpu_printf("putting state into q%d\n", q);
+
+ memcpy((uchar *) f->m_now, (uchar *) &now, vsize);
+ memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char));
+ for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
+ { if (Mask[i])
+ { f->m_Mask[i/8] |= (1<<j);
+ } }
+
+ if (now._nr_pr > 0)
+ { memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT));
+ memcpy((uchar *) f->m_p_skip, (uchar *) proc_skip, now._nr_pr * sizeof(uchar));
+ }
+ if (now._nr_qs > 0)
+ { memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT));
+ memcpy((uchar *) f->m_q_skip, (uchar *) q_skip, now._nr_qs * sizeof(uchar));
+ }
+#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
+ c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */
+#endif
+#ifdef FULL_TRAIL
+ f->m_stack = stack_last[core_id];
+#endif
+ f->nr_handoffs = nr_handoffs+1;
+ f->m_tau = trpt->tau;
+ f->m_o_pm = trpt->o_pm;
+ f->m_boq = boq;
+ f->m_vsize = vsize; /* must come last - now the other cpu can see it */
+
+ if (query_in_progress == 1)
+ query_in_progress = 2; /* make sure we know, if a query makes the rounds */
+ nstates_put++;
+}
+
+#ifdef USE_DISK
+int Dsk_W_Nr, Dsk_R_Nr;
+int dsk_file = -1, dsk_read = -1;
+unsigned long dsk_written, dsk_drained;
+char dsk_name[512];
+
+#ifndef BFS_DISK
+#if defined(WIN32) || defined(WIN64)
+ #define RFLAGS (O_RDONLY|O_BINARY)
+ #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)
+#else
+ #define RFLAGS (O_RDONLY)
+ #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC)
+#endif
+#endif
+
+void
+dsk_stats(void)
+{ int i;
+
+ if (dsk_written > 0)
+ { cpu_printf("dsk_written %d states in %d files\ncpu%d: dsk_drained %6d states\n",
+ dsk_written, Dsk_W_Nr, core_id, dsk_drained);
+ close(dsk_read);
+ close(dsk_file);
+ for (i = 0; i < Dsk_W_Nr; i++)
+ { sprintf(dsk_name, "Q%.3d_%.3d.tmp", i, core_id);
+ unlink(dsk_name);
+ } }
+}
+
+void
+mem_drain(void)
+{ SM_frame *f, g;
+ int q = (core_id + 1) % NCORE; /* target q */
+ int sz;
+
+ if (dsk_read < 0
+ || dsk_written <= dsk_drained)
+ { return;
+ }
+
+ while (dsk_written > dsk_drained
+ && TargetQ_NotFull(q))
+ { f = Get_Free_Frame(q);
+ if (!f) { Uerror("Fatal: unhandled condition"); }
+
+ if ((dsk_drained+1)%MAX_DSK_FILE == 0) /* 100K states max per file */
+ { (void) close(dsk_read); /* close current read handle */
+ sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr++, core_id);
+ (void) unlink(dsk_name); /* remove current file */
+ sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr, core_id);
+ cpu_printf("reading %s\n", dsk_name);
+ dsk_read = open(dsk_name, RFLAGS); /* open next file */
+ if (dsk_read < 0)
+ { Uerror("could not open dsk file");
+ } }
+ if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame))
+ { Uerror("bad dsk file read");
+ }
+ sz = g.m_vsize;
+ g.m_vsize = 0;
+ memcpy(f, &g, sizeof(SM_frame));
+ f->m_vsize = sz; /* last */
+
+ dsk_drained++;
+ }
+}
+
+void
+mem_file(void)
+{ SM_frame f;
+ int i, j, q = (core_id + 1) % NCORE; /* target q */
+
+ if (vsize > VMAX)
+ { printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize);
+ Uerror("aborting");
+ }
+ if (now._nr_pr > PMAX)
+ { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr);
+ Uerror("aborting");
+ }
+ if (now._nr_qs > QMAX)
+ { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs);
+ Uerror("aborting");
+ }
+
+ if (VVERBOSE) cpu_printf("filing state for q%d\n", q);
+
+ memcpy((uchar *) f.m_now, (uchar *) &now, vsize);
+ memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char));
+ for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
+ { if (Mask[i])
+ { f.m_Mask[i/8] |= (1<<j);
+ } }
+
+ if (now._nr_pr > 0)
+ { memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT));
+ memcpy((uchar *)f.m_p_skip, (uchar *)proc_skip, now._nr_pr*sizeof(uchar));
+ }
+ if (now._nr_qs > 0)
+ { memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT));
+ memcpy((uchar *) f.m_q_skip, (uchar *) q_skip, now._nr_qs*sizeof(uchar));
+ }
+#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
+ c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */
+#endif
+#ifdef FULL_TRAIL
+ f.m_stack = stack_last[core_id];
+#endif
+ f.nr_handoffs = nr_handoffs+1;
+ f.m_tau = trpt->tau;
+ f.m_o_pm = trpt->o_pm;
+ f.m_boq = boq;
+ f.m_vsize = vsize;
+
+ if (query_in_progress == 1)
+ { query_in_progress = 2;
+ }
+ if (dsk_file < 0)
+ { sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr, core_id);
+ dsk_file = open(dsk_name, WFLAGS, 0644);
+ dsk_read = open(dsk_name, RFLAGS);
+ if (dsk_file < 0 || dsk_read < 0)
+ { cpu_printf("File: <%s>\n", dsk_name);
+ Uerror("cannot open diskfile");
+ }
+ Dsk_W_Nr++; /* nr of next file to open */
+ cpu_printf("created temporary diskfile %s\n", dsk_name);
+ } else if ((dsk_written+1)%MAX_DSK_FILE == 0)
+ { close(dsk_file); /* close write handle */
+ sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr++, core_id);
+ dsk_file = open(dsk_name, WFLAGS, 0644);
+ if (dsk_file < 0)
+ { cpu_printf("File: <%s>\n", dsk_name);
+ Uerror("aborting: cannot open new diskfile");
+ }
+ cpu_printf("created temporary diskfile %s\n", dsk_name);
+ }
+ if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame))
+ { Uerror("aborting -- disk write failed (disk full?)");
+ }
+ nstates_put++;
+ dsk_written++;
+}
+#endif
+
+int
+mem_hand_off(void)
+{
+ if (search_terminated == NULL
+ || *search_terminated != 0) /* not a full crash check */
+ { pan_exit(0);
+ }
+ iam_alive(); /* on every transition of Down */
+#ifdef USE_DISK
+ mem_drain(); /* maybe call this also on every Up */
+#endif
+ if (depth > z_handoff /* above handoff limit */
+#ifndef SAFETY
+ && !a_cycles /* not in liveness mode */
+#endif
+#if SYNC
+ && boq == -1 /* not mid-rv */
+#endif
+#ifdef VERI
+ && (trpt->tau&4) /* claim moves first */
+ && !((trpt-1)->tau&128) /* not a stutter move */
+#endif
+ && !(trpt->tau&8)) /* not an atomic move */
+ { int q = (core_id + 1) % NCORE; /* circular handoff */
+ #ifdef GENEROUS
+ if (prcnt[q] < LN_FRAMES)
+ #else
+ if (TargetQ_NotFull(q)
+ && (dfs_phase2 == 0 || prcnt[core_id] > 0))
+ #endif
+ { mem_put(q);
+ return 1;
+ }
+ { int rval;
+ #ifndef NGQ
+ rval = GlobalQ_HasRoom();
+ #else
+ rval = 0;
+ #endif
+ #ifdef USE_DISK
+ if (rval == 0)
+ { void mem_file(void);
+ mem_file();
+ rval = 1;
+ }
+ #endif
+ return rval;
+ }
+ }
+ return 0; /* i.e., no handoff */
+}
+
+void
+mem_put_acc(void) /* liveness mode */
+{ int q = (core_id + 1) % NCORE;
+
+ if (search_terminated == NULL
+ || *search_terminated != 0)
+ { pan_exit(0);
+ }
+#ifdef USE_DISK
+ mem_drain();
+#endif
+ /* some tortured use of preprocessing: */
+#if !defined(NGQ) || defined(USE_DISK)
+ if (TargetQ_Full(q))
+ {
+#endif
+#ifndef NGQ
+ if (GlobalQ_HasRoom())
+ { return;
+ }
+#endif
+#ifdef USE_DISK
+ mem_file();
+ } else
+#else
+ #if !defined(NGQ) || defined(USE_DISK)
+ }
+ #endif
+#endif
+ { mem_put(q);
+ }
+}
+
+#if defined(WIN32) || defined(WIN64)
+void
+init_shm(void) /* initialize shared work-queues */
+{ char key[512];
+ int n, m;
+ int must_exit = 0;
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 3: allocate shared work-queues %g Mb\n",
+ ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.));
+ }
+ for (m = 0; m < NR_QS; m++) /* last q is global 1 */
+ { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;
+ sprintf(key, "Global\\pan_%s_%.3d", PanSource, m);
+ if (core_id == 0)
+ { shmid[m] = CreateFileMapping(
+ INVALID_HANDLE_VALUE, /* use paging file */
+ NULL, /* default security */
+ PAGE_READWRITE, /* access permissions */
+ 0, /* high-order 4 bytes */
+ qsize, /* low-order bytes, size in bytes */
+ key); /* name */
+ } else /* worker nodes just open these segments */
+ { shmid[m] = OpenFileMapping(
+ FILE_MAP_ALL_ACCESS, /* read/write access */
+ FALSE, /* children do not inherit handle */
+ key);
+ }
+ if (shmid[m] == NULL)
+ { fprintf(stderr, "cpu%d: could not create or open shared queues\n",
+ core_id);
+ must_exit = 1;
+ break;
+ }
+ /* attach: */
+ shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if (shared_mem[m] == NULL)
+ { fprintf(stderr, "cpu%d: cannot attach shared q%d (%d Mb)\n",
+ core_id, m+1, (int) (qsize/(1048576.)));
+ must_exit = 1;
+ break;
+ }
+
+ memcnt += qsize;
+
+ m_workq[m] = (SM_frame *) shared_mem[m];
+ if (core_id == 0)
+ { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;
+ for (n = 0; n < nframes; n++)
+ { m_workq[m][n].m_vsize = 0;
+ m_workq[m][n].m_boq = 0;
+ } } }
+
+ if (must_exit)
+ { fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1); /* calls cleanup_shm */
+ }
+}
+
+static uchar *
+prep_shmid_S(size_t n) /* either sets SS or H_tab, WIN32/WIN64 */
+{ char *rval;
+#ifndef SEP_STATE
+ char key[512];
+
+ if (verbose && core_id == 0)
+ {
+ #ifdef BITSTATE
+ printf("cpu0: step 1: allocate shared bitstate %g Mb\n",
+ (double) n / (1048576.));
+ #else
+ printf("cpu0: step 1: allocate shared hastable %g Mb\n",
+ (double) n / (1048576.));
+ #endif
+ }
+ #ifdef MEMLIM
+ if (memcnt + (double) n > memlim)
+ { printf("cpu%d: S %8g + %d Kb exceeds memory limit of %8g Mb\n",
+ core_id, memcnt/1024., n/1024, memlim/(1048576.));
+ printf("cpu%d: insufficient memory -- aborting\n", core_id);
+ exit(1);
+ }
+ #endif
+
+ /* make key different from queues: */
+ sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+2); /* different from qs */
+
+ if (core_id == 0) /* root */
+ { shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
+#ifdef WIN64
+ PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);
+#else
+ PAGE_READWRITE, 0, n, key);
+#endif
+ memcnt += (double) n;
+ } else /* worker */
+ { shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);
+ }
+ if (shmid_S == NULL)
+ {
+ #ifdef BITSTATE
+ fprintf(stderr, "cpu%d: cannot %s shared bitstate",
+ core_id, core_id?"open":"create");
+ #else
+ fprintf(stderr, "cpu%d: cannot %s shared hashtable",
+ core_id, core_id?"open":"create");
+ #endif
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+
+ rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */
+ if ((char *) rval == NULL)
+ { fprintf(stderr, "cpu%d: cannot attach shared bitstate or hashtable\n", core_id);
+ fprintf(stderr, "pan: check './pan --' for usage details\n");
+ pan_exit(1);
+ }
+#else
+ rval = (char *) emalloc(n);
+#endif
+ return (uchar *) rval;
+}
+
+static uchar *
+prep_state_mem(size_t n) /* WIN32/WIN64 sets memory arena for states */
+{ char *rval;
+ char key[512];
+ static int cnt = 3; /* start larger than earlier ftok calls */
+
+ if (verbose && core_id == 0)
+ { printf("cpu0: step 2+: pre-allocate memory arena %d of %g Mb\n",
+ cnt-3, (double) n / (1048576.));
+ }
+ #ifdef MEMLIM
+ if (memcnt + (double) n > memlim)
+ { printf("cpu%d: error: M %.0f + %.0f exceeds memory limit of %.0f Kb\n",
+ core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0);
+ return NULL;
+ }
+ #endif
+
+ sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+cnt); cnt++;
+
+ if (core_id == 0)
+ { shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
+#ifdef WIN64
+ PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);
+#else
+ PAGE_READWRITE, 0, n, key);
+#endif
+ } else
+ { shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);
+ }
+ if (shmid_M == NULL)
+ { printf("cpu%d: failed to get pool of shared memory nr %d of size %d\n",
+ core_id, cnt-3, n);
+ printf("pan: check './pan --' for usage details\n");
+ return NULL;
+ }
+ rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */
+
+ if (rval == NULL)
+ { printf("cpu%d: failed to attach pool of shared memory nr %d of size %d\n",
+ core_id, cnt-3, n);
+ return NULL;
+ }
+ return (uchar *) rval;
+}
+
+void
+init_HT(unsigned long n) /* WIN32/WIN64 version */
+{ volatile char *x;
+ double get_mem;
+#ifndef SEP_STATE
+ char *dc_mem_start;
+#endif
+ if (verbose) printf("cpu%d: initialization for Windows\n", core_id);
+
+#ifdef SEP_STATE
+ #ifndef MEMLIM
+ if (verbose)
+ { printf("cpu0: steps 0,1: no -DMEMLIM set\n");
+ }
+ #else
+ if (verbose)
+ printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n",
+ MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.));
+#endif
+ get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double);
+ /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */
+ get_mem += 4 * NCORE * sizeof(void *);
+ #ifdef FULL_TRAIL
+ get_mem += (NCORE) * sizeof(Stack_Tree *);
+ /* NCORE * stack_last */
+ #endif
+ x = (volatile char *) prep_state_mem((size_t) get_mem);
+ shmid_X = (void *) x;
+ if (x == NULL)
+ { printf("cpu0: could not allocate shared memory, see ./pan --\n");
+ exit(1);
+ }
+ search_terminated = (volatile unsigned int *) x; /* comes first */
+ x += sizeof(void *); /* maintain alignment */
+
+ is_alive = (volatile double *) x;
+ x += NCORE * sizeof(double);
+
+ sh_lock = (volatile int *) x;
+ x += CS_NR * sizeof(void *); /* allow 1 word per entry */
+
+ grfree = (volatile int *) x;
+ x += sizeof(void *);
+ grfull = (volatile int *) x;
+ x += sizeof(void *);
+ grcnt = (volatile int *) x;
+ x += sizeof(void *);
+ grmax = (volatile int *) x;
+ x += sizeof(void *);
+ prfree = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prfull = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prcnt = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prmax = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ gr_readmiss = (volatile double *) x;
+ x += sizeof(double);
+ gr_writemiss = (volatile double *) x;
+ x += sizeof(double);
+
+ #ifdef FULL_TRAIL
+ stack_last = (volatile Stack_Tree **) x;
+ x += NCORE * sizeof(Stack_Tree *);
+ #endif
+
+ #ifndef BITSTATE
+ H_tab = (struct H_el **) emalloc(n);
+ #endif
+#else
+ #ifndef MEMLIM
+ #warning MEMLIM not set
+ #define MEMLIM (2048)
+ #endif
+
+ if (core_id == 0 && verbose)
+ printf("cpu0: step 0: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb) = %g Mb for state storage\n",
+ MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),
+ (memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));
+ #ifndef BITSTATE
+ H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */
+ #endif
+ get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE;
+ if (get_mem <= 0)
+ { Uerror("internal error -- shared state memory");
+ }
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 2: shared state memory %g Mb\n",
+ get_mem/(1048576.));
+ }
+ x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem); /* for states */
+ if (x == NULL)
+ { printf("cpu%d: insufficient memory -- aborting\n", core_id);
+ exit(1);
+ }
+
+ search_terminated = (volatile unsigned int *) x; /* comes first */
+ x += sizeof(void *); /* maintain alignment */
+
+ is_alive = (volatile double *) x;
+ x += NCORE * sizeof(double);
+
+ sh_lock = (volatile int *) x;
+ x += CS_NR * sizeof(int);
+
+ grfree = (volatile int *) x;
+ x += sizeof(void *);
+ grfull = (volatile int *) x;
+ x += sizeof(void *);
+ grcnt = (volatile int *) x;
+ x += sizeof(void *);
+ grmax = (volatile int *) x;
+ x += sizeof(void *);
+ prfree = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prfull = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prcnt = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ prmax = (volatile int *) x;
+ x += NCORE * sizeof(void *);
+ gr_readmiss = (volatile double *) x;
+ x += sizeof(double);
+ gr_writemiss = (volatile double *) x;
+ x += sizeof(double);
+
+ #ifdef FULL_TRAIL
+ stack_last = (volatile Stack_Tree **) x;
+ x += NCORE * sizeof(Stack_Tree *);
+ #endif
+ if (((long)x)&(sizeof(void *)-1)) /* word alignment */
+ { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */
+ }
+
+ #ifdef COLLAPSE
+ ncomps = (unsigned long *) x;
+ x += (256+2) * sizeof(unsigned long);
+ #endif
+
+ dc_shared = (sh_Allocater *) x; /* in shared memory */
+ x += sizeof(sh_Allocater);
+
+ if (core_id == 0) /* root only */
+ { dc_shared->dc_id = shmid_M;
+ dc_shared->dc_start = (void *) dc_mem_start;
+ dc_shared->dc_arena = x;
+ dc_shared->pattern = 1234567;
+ dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start);
+ dc_shared->nxt = NULL;
+ }
+#endif
+}
+
+#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__)
+extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit);
+int
+tas(volatile LONG *s)
+{ return InterlockedBitTestAndSet(s, 1);
+}
+#else
+ #error missing definition of test and set operation for this platform
+#endif
+
+void
+cleanup_shm(int val)
+{ int m;
+ static int nibis = 0;
+
+ if (nibis != 0)
+ { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val);
+ return;
+ } else
+ { nibis = 1;
+ }
+ if (search_terminated != NULL)
+ { *search_terminated |= 16; /* cleanup_shm */
+ }
+
+ for (m = 0; m < NR_QS; m++)
+ { if (shmid[m] != NULL)
+ { UnmapViewOfFile((char *) shared_mem[m]);
+ CloseHandle(shmid[m]);
+ } }
+#ifdef SEP_STATE
+ UnmapViewOfFile((void *) shmid_X);
+ CloseHandle((void *) shmid_M);
+#else
+ #ifdef BITSTATE
+ if (shmid_S != NULL)
+ { UnmapViewOfFile(SS);
+ CloseHandle(shmid_S);
+ }
+ #else
+ if (core_id == 0 && verbose)
+ { printf("cpu0: done, %ld Mb of shared state memory left\n",
+ dc_shared->dc_size / (long)(1048576));
+ }
+ if (shmid_S != NULL)
+ { UnmapViewOfFile(H_tab);
+ CloseHandle(shmid_S);
+ }
+ shmid_M = (void *) (dc_shared->dc_id);
+ UnmapViewOfFile((char *) dc_shared->dc_start);
+ CloseHandle(shmid_M);
+ #endif
+#endif
+ /* detached from shared memory - so cannot use cpu_printf */
+ if (verbose)
+ { printf("cpu%d: done -- got %d states from queue\n",
+ core_id, nstates_get);
+ }
+}
+
+void
+mem_get(void)
+{ SM_frame *f;
+ int is_parent;
+
+#if defined(MA) && !defined(SEP_STATE)
+ #error MA requires SEP_STATE in multi-core mode
+#endif
+#ifdef BFS
+ #error BFS is not supported in multi-core mode
+#endif
+#ifdef SC
+ #error SC is not supported in multi-core mode
+#endif
+ init_shm(); /* we are single threaded when this starts */
+ signal(SIGINT, give_up); /* windows control-c interrupt */
+
+ if (core_id == 0 && verbose)
+ { printf("cpu0: step 4: creating additional workers (proxy %d)\n",
+ proxy_pid);
+ }
+#if 0
+ if NCORE > 1 the child or the parent should fork N-1 more times
+ the parent is the only process with core_id == 0 and is_parent > 0
+ the others (workers) have is_parent = 0 and core_id = 1..NCORE-1
+#endif
+ if (core_id == 0) /* root starts up the workers */
+ { worker_pids[0] = (DWORD) getpid(); /* for completeness */
+ while (++core_id < NCORE) /* first worker sees core_id = 1 */
+ { char cmdline[64];
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+
+ if (proxy_pid == core_id) /* always non-zero */
+ { sprintf(cmdline, "pan_proxy.exe -r %s-Q%d -Z%d",
+ o_cmdline, getpid(), core_id);
+ } else
+ { sprintf(cmdline, "pan.exe %s-Q%d -Z%d",
+ o_cmdline, getpid(), core_id);
+ }
+ if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline);
+
+ is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi);
+ if (is_parent == 0)
+ { Uerror("fork failed");
+ }
+ worker_pids[core_id] = pi.dwProcessId;
+ worker_handles[core_id] = pi.hProcess;
+ if (verbose)
+ { cpu_printf("created core %d, pid %d\n",
+ core_id, pi.dwProcessId);
+ }
+ if (proxy_pid == core_id) /* we just created the receive half */
+ { /* add proxy send, store pid in proxy_pid_snd */
+ sprintf(cmdline, "pan_proxy.exe -s %s-Q%d -Z%d -Y%d",
+ o_cmdline, getpid(), core_id, worker_pids[proxy_pid]);
+ if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline);
+ is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi);
+ if (is_parent == 0)
+ { Uerror("fork failed");
+ }
+ proxy_pid_snd = pi.dwProcessId;
+ proxy_handle_snd = pi.hProcess;
+ if (verbose)
+ { cpu_printf("created core %d, pid %d (send proxy)\n",
+ core_id, pi.dwProcessId);
+ } } }
+ core_id = 0; /* reset core_id for root process */
+ } else /* worker */
+ { static char db0[16]; /* good for up to 10^6 cores */
+ static char db1[16];
+ tprefix = db0; sprefix = db1;
+ sprintf(tprefix, "cpu%d_trail", core_id); /* avoid conflicts on file access */
+ sprintf(sprefix, "cpu%d_rst", core_id);
+ memcnt = 0; /* count only additionally allocated memory */
+ }
+ if (verbose)
+ { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid());
+ }
+ if (core_id == 0 && !remote_party)
+ { new_state(); /* root starts the search */
+ if (verbose)
+ cpu_printf("done with 1st dfs, nstates %g (put %d states), start reading q\n",
+ nstates, nstates_put);
+ dfs_phase2 = 1;
+ }
+ Read_Queue(core_id); /* all cores */
+
+ if (verbose)
+ { cpu_printf("put %6d states into queue -- got %6d\n",
+ nstates_put, nstates_get);
+ }
+ done = 1;
+ wrapup();
+ exit(0);
+}
+#endif
+
+#ifdef BITSTATE
+void
+init_SS(unsigned long n)
+{
+ SS = (uchar *) prep_shmid_S((size_t) n);
+ init_HT(0L);
+}
+#endif
+
+#endif
+clock_t start_time;
+#if NCORE>1
+clock_t crash_stamp;
+#endif
+#if !defined(WIN32) && !defined(WIN64)
+struct tms start_tm;
+#endif
+
+void
+start_timer(void)
+{
+#if defined(WIN32) || defined(WIN64)
+ start_time = clock();
+#else
+ start_time = times(&start_tm);
+#endif
+}
+
+void
+stop_timer(void)
+{ clock_t stop_time;
+ double delta_time;
+#if !defined(WIN32) && !defined(WIN64)
+ struct tms stop_tm;
+ stop_time = times(&stop_tm);
+ delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));
+#else
+ stop_time = clock();
+ delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);
+#endif
+ if (readtrail || delta_time < 0.00) return;
+#if NCORE>1
+ if (core_id == 0 && nstates > (double) 0)
+ { printf("\ncpu%d: elapsed time %.3g seconds (%g states visited)\n", core_id, delta_time, nstates);
+ if (delta_time > 0.01)
+ { printf("cpu%d: rate %g states/second\n", core_id, nstates/delta_time);
+ }
+ { void check_overkill(void);
+ check_overkill();
+ } }
+#else
+ printf("\npan: elapsed time %.3g seconds\n", delta_time);
+ if (delta_time > 0.01)
+ { printf("pan: rate %9.8g states/second\n", nstates/delta_time);
+ if (verbose)
+ { printf("pan: avg transition delay %.5g usec\n",
+ delta_time/(nstates+truncs));
+ } }
+#endif
+}
+
+#if NCORE>1
+#ifdef T_ALERT
+double t_alerts[17];
+
+void
+crash_report(void)
+{ int i;
+ printf("crash alert intervals:\n");
+ for (i = 0; i < 17; i++)
+ { printf("%d\t%g\n", i, t_alerts[i]);
+} }
+#endif
+
+void
+crash_reset(void)
+{ /* false alarm */
+ if (crash_stamp != (clock_t) 0)
+ {
+#ifdef T_ALERT
+ double delta_time;
+ int i;
+#if defined(WIN32) || defined(WIN64)
+ delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);
+#else
+ delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));
+#endif
+ for (i = 0; i < 16; i++)
+ { if (delta_time <= (i*30))
+ { t_alerts[i] = delta_time;
+ break;
+ } }
+ if (i == 16) t_alerts[i] = delta_time;
+#endif
+ if (verbose)
+ printf("cpu%d: crash alert off\n", core_id);
+ }
+ crash_stamp = (clock_t) 0;
+}
+
+int
+crash_test(double maxtime)
+{ double delta_time;
+ if (crash_stamp == (clock_t) 0)
+ { /* start timing */
+#if defined(WIN32) || defined(WIN64)
+ crash_stamp = clock();
+#else
+ crash_stamp = times(&start_tm);
+#endif
+ if (verbose)
+ { printf("cpu%d: crash detection\n", core_id);
+ }
+ return 0;
+ }
+#if defined(WIN32) || defined(WIN64)
+ delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);
+#else
+ delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));
+#endif
+ return (delta_time >= maxtime);
+}
+#endif
+
+void
+do_the_search(void)
+{ int i;
+ depth = mreached = 0;
+ trpt = &trail[0];
+#ifdef VERI
+ trpt->tau |= 4; /* the claim moves first */
+#endif
+ for (i = 0; i < (int) now._nr_pr; i++)
+ { P0 *ptr = (P0 *) pptr(i);
+#ifndef NP
+ if (!(trpt->o_pm&2)
+ && accpstate[ptr->_t][ptr->_p])
+ { trpt->o_pm |= 2;
+ }
+#else
+ if (!(trpt->o_pm&4)
+ && progstate[ptr->_t][ptr->_p])
+ { trpt->o_pm |= 4;
+ }
+#endif
+ }
+#ifdef EVENT_TRACE
+#ifndef NP
+ if (accpstate[EVENT_TRACE][now._event])
+ { trpt->o_pm |= 2;
+ }
+#else
+ if (progstate[EVENT_TRACE][now._event])
+ { trpt->o_pm |= 4;
+ }
+#endif
+#endif
+#ifndef NOCOMP
+ Mask[0] = Mask[1] = 1; /* _nr_pr, _nr_qs */
+ if (!a_cycles)
+ { i = &(now._a_t) - (uchar *) &now;
+ Mask[i] = 1; /* _a_t */
+ }
+#ifndef NOFAIR
+ if (!fairness)
+ { int j = 0;
+ i = &(now._cnt[0]) - (uchar *) &now;
+ while (j++ < NFAIR)
+ Mask[i++] = 1; /* _cnt[] */
+ }
+#endif
+#endif
+#ifndef NOFAIR
+ if (fairness
+ && (a_cycles && (trpt->o_pm&2)))
+ { now._a_t = 2; /* set the A-bit */
+ now._cnt[0] = now._nr_pr + 1;
+#ifdef VERBOSE
+ printf("%3d: fairness Rule 1, cnt=%d, _a_t=%d\n",
+ depth, now._cnt[now._a_t&1], now._a_t);
+#endif
+ }
+#endif
+ c_stack_start = (char *) &i; /* meant to be read-only */
+#if defined(HAS_CODE) && defined (C_INIT)
+ C_INIT; /* initialization of data that must precede fork() */
+ c_init_done++;
+#endif
+#if defined(C_States) && (HAS_TRACK==1)
+ /* capture initial state of tracked C objects */
+ c_update((uchar *) &(now.c_state[0]));
+#endif
+#ifdef HAS_CODE
+ if (readtrail) getrail(); /* no return */
+#endif
+ start_timer();
+#ifdef BFS
+ bfs();
+#else
+#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)
+ /* initial state of tracked & unmatched objects */
+ c_stack((uchar *) &(svtack->c_stack[0]));
+#endif
+#ifdef RANDOMIZE
+ #if RANDOMIZE>0
+ srand(RANDOMIZE);
+ #else
+ srand(123);
+ #endif
+#endif
+#if NCORE>1
+ mem_get();
+#else
+ new_state(); /* start 1st DFS */
+#endif
+#endif
+}
+#ifdef INLINE_REV
+uchar
+do_reverse(Trans *t, short II, uchar M)
+{ uchar _m = M;
+ int tt = (int) ((P0 *)this)->_p;
+#include REVERSE_MOVES
+R999: return _m;
+}
+#endif
+#ifndef INLINE
+#ifdef EVENT_TRACE
+static char _tp = 'n'; static int _qid = 0;
+#endif
+uchar
+do_transit(Trans *t, short II)
+{ uchar _m = 0;
+ int tt = (int) ((P0 *)this)->_p;
+#ifdef M_LOSS
+ uchar delta_m = 0;
+#endif
+#ifdef EVENT_TRACE
+ short oboq = boq;
+ uchar ot = (uchar) ((P0 *)this)->_t;
+ if (ot == EVENT_TRACE) boq = -1;
+#define continue { boq = oboq; return 0; }
+#else
+#define continue return 0
+#ifdef SEPARATE
+ uchar ot = (uchar) ((P0 *)this)->_t;
+#endif
+#endif
+#include FORWARD_MOVES
+P999:
+#ifdef EVENT_TRACE
+ if (ot == EVENT_TRACE) boq = oboq;
+#endif
+ return _m;
+#undef continue
+}
+#ifdef EVENT_TRACE
+void
+require(char tp, int qid)
+{ Trans *t;
+ _tp = tp; _qid = qid;
+
+ if (now._event != endevent)
+ for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt)
+ { if (do_transit(t, EVENT_TRACE))
+ { now._event = t->st;
+ reached[EVENT_TRACE][t->st] = 1;
+#ifdef VERBOSE
+ printf(" event_trace move to -> %d\n", t->st);
+#endif
+#ifndef BFS
+#ifndef NP
+ if (accpstate[EVENT_TRACE][now._event])
+ (trpt+1)->o_pm |= 2;
+#else
+ if (progstate[EVENT_TRACE][now._event])
+ (trpt+1)->o_pm |= 4;
+#endif
+#endif
+#ifdef NEGATED_TRACE
+ if (now._event == endevent)
+ {
+#ifndef BFS
+ depth++; trpt++;
+#endif
+ uerror("event_trace error (all events matched)");
+#ifndef BFS
+ trpt--; depth--;
+#endif
+ break;
+ }
+#endif
+ for (t = t->nxt; t; t = t->nxt)
+ { if (do_transit(t, EVENT_TRACE))
+ Uerror("non-determinism in event-trace");
+ }
+ return;
+ }
+#ifdef VERBOSE
+ else
+ printf(" event_trace miss '%c' -- %d, %d, %d\n",
+ tp, qid, now._event, t->forw);
+#endif
+ }
+#ifdef NEGATED_TRACE
+ now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */
+#else
+#ifndef BFS
+ depth++; trpt++;
+#endif
+ uerror("event_trace error (no matching event)");
+#ifndef BFS
+ trpt--; depth--;
+#endif
+#endif
+}
+#endif
+int
+enabled(int iam, int pid)
+{ Trans *t; uchar *othis = this;
+ int res = 0; int tt; uchar ot;
+#ifdef VERI
+ /* if (pid > 0) */ pid++;
+#endif
+ if (pid == iam)
+ Uerror("used: enabled(pid=thisproc)");
+ if (pid < 0 || pid >= (int) now._nr_pr)
+ return 0;
+ this = pptr(pid);
+ TstOnly = 1;
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+ for (t = trans[ot][tt]; t; t = t->nxt)
+ if (do_transit(t, (short) pid))
+ { res = 1;
+ break;
+ }
+ TstOnly = 0;
+ this = othis;
+ return res;
+}
+#endif
+void
+snap_time(void)
+{ clock_t stop_time;
+ double delta_time;
+#if !defined(WIN32) && !defined(WIN64)
+ struct tms stop_tm;
+ stop_time = times(&stop_tm);
+ delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));
+#else
+ stop_time = clock();
+ delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);
+#endif
+ if (delta_time > 0.01)
+ { printf("t= %6.3g ", delta_time);
+ printf("R= %7.0g", nstates/delta_time);
+ }
+ printf("\n");
+ if (quota > 0.1 && delta_time > quota)
+ { printf("Time limit of %6.3g minutes exceeded\n", quota/60.0);
+#if NCORE>1
+ fflush(stdout);
+ leave_critical(GLOBAL_LOCK);
+ sudden_stop("time-limit");
+ exit(1);
+#endif
+ wrapup();
+ }
+}
+void
+snapshot(void)
+{
+#if NCORE>1
+ enter_critical(GLOBAL_LOCK); /* snapshot */
+ printf("cpu%d: ", core_id);
+#endif
+ printf("Depth= %7ld States= %8.3g ",
+#if NCORE>1
+ (long) (nr_handoffs * z_handoff) +
+#endif
+ mreached, nstates);
+ printf("Transitions= %8.3g ", nstates+truncs);
+#ifdef MA
+ printf("Nodes= %7d ", nr_states);
+#endif
+ printf("Memory= %9.3f\t", memcnt/1048576.);
+ snap_time();
+ fflush(stdout);
+#if NCORE>1
+ leave_critical(GLOBAL_LOCK);
+#endif
+}
+#ifdef SC
+void
+stack2disk(void)
+{
+ if (!stackwrite
+ && (stackwrite = creat(stackfile, TMODE)) < 0)
+ Uerror("cannot create stackfile");
+
+ if (write(stackwrite, trail, DDD*sizeof(Trail))
+ != DDD*sizeof(Trail))
+ Uerror("stackfile write error -- disk is full?");
+
+ memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail));
+ memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail));
+ CNT1++;
+}
+void
+disk2stack(void)
+{ long have;
+
+ CNT2++;
+ memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail));
+
+ if (!stackwrite
+ || lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1)
+ Uerror("disk2stack lseek error");
+
+ if (!stackread
+ && (stackread = open(stackfile, 0)) < 0)
+ Uerror("cannot open stackfile");
+
+ if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1)
+ Uerror("disk2stack lseek error");
+
+ have = read(stackread, trail, DDD*sizeof(Trail));
+ if (have != DDD*sizeof(Trail))
+ Uerror("stackfile read error");
+}
+#endif
+uchar *
+Pptr(int x)
+{ if (x < 0 || x >= MAXPROC || !proc_offset[x])
+ return noptr;
+ else
+ return (uchar *) pptr(x);
+}
+int qs_empty(void);
+/*
+ * new_state() is the main DFS search routine in the verifier
+ * it has a lot of code ifdef-ed together to support
+ * different search modes, which makes it quite unreadable.
+ * if you are studying the code, first use the C preprocessor
+ * to generate a specific version from the pan.c source,
+ * e.g. by saying:
+ * gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c
+ * and then study the resulting file, rather than this one
+ */
+#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA))
+
+#ifdef NSUCC
+int N_succ[512];
+void
+tally_succ(int cnt)
+{ if (cnt < 512) N_succ[cnt]++;
+ else printf("tally_succ: cnt %d exceeds range\n", cnt);
+}
+
+void
+dump_succ(void)
+{ int i; double sum = 0.0;
+ double w_avg = 0.0;
+ printf("Successor counts:\n");
+ for (i = 0; i < 512; i++)
+ { sum += (double) N_succ[i];
+ }
+ for (i = 0; i < 512; i++)
+ { if (N_succ[i] > 0)
+ { printf("%3d %10d (%.4g %% of total)\n",
+ i, N_succ[i], (100.0 * (double) N_succ[i])/sum);
+ w_avg += (double) i * (double) N_succ[i];
+ } }
+ if (sum > N_succ[0])
+ printf("mean %.4g (without 0: %.4g)\n", w_avg / sum, w_avg / (sum - (double) N_succ[0]));
+}
+#endif
+
+void
+new_state(void)
+{ Trans *t;
+ uchar _n, _m, ot;
+#ifdef RANDOMIZE
+ short ooi, eoi;
+#endif
+#ifdef M_LOSS
+ uchar delta_m = 0;
+#endif
+ short II, JJ = 0, kk;
+ int tt;
+#ifdef REVERSE
+ short From = BASE, To = now._nr_pr-1;
+#else
+ short From = now._nr_pr-1, To = BASE;
+#endif
+Down:
+#ifdef CHECK
+ cpu_printf("%d: Down - %s %saccepting [pids %d-%d]\n",
+ depth, (trpt->tau&4)?"claim":"program",
+ (trpt->o_pm&2)?"":"non-", From, To);
+#endif
+#ifdef SCHED
+ if (depth > 0)
+ { trpt->sched_limit = (trpt-1)->sched_limit;
+ } else
+ { trpt->sched_limit = 0;
+ }
+#endif
+#ifdef SC
+ if (depth > hiwater)
+ { stack2disk();
+ maxdepth += DDD;
+ hiwater += DDD;
+ trpt -= DDD;
+ if(verbose)
+ printf("zap %d: %d (maxdepth now %d)\n",
+ CNT1, hiwater, maxdepth);
+ }
+#endif
+ trpt->tau &= ~(16|32|64); /* make sure these are off */
+#if defined(FULLSTACK) && defined(MA)
+ trpt->proviso = 0;
+#endif
+#ifdef NSUCC
+ trpt->n_succ = 0;
+#endif
+#if NCORE>1
+ if (mem_hand_off())
+ {
+#if SYNC
+ (trpt+1)->o_n = 1; /* not a deadlock: as below */
+#endif
+#ifndef LOOPSTATE
+ (trpt-1)->tau |= 16; /* worstcase guess: as below */
+#endif
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+#endif
+ if (depth >= maxdepth)
+ { if (!warned)
+ { warned = 1;
+ printf("error: max search depth too small\n");
+ }
+ if (bounded)
+ { uerror("depth limit reached");
+ }
+ truncs++;
+#if SYNC
+ (trpt+1)->o_n = 1; /* not a deadlock */
+#endif
+#ifndef LOOPSTATE
+ (trpt-1)->tau |= 16; /* worstcase guess */
+#endif
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+AllOver:
+#if (defined(FULLSTACK) && !defined(MA)) || NCORE>1
+ /* if atomic or rv move, carry forward previous state */
+ trpt->ostate = (trpt-1)->ostate;
+#endif
+#ifdef VERI
+ if ((trpt->tau&4) || ((trpt-1)->tau&128))
+#endif
+ if (boq == -1) { /* if not mid-rv */
+#ifndef SAFETY
+ /* this check should now be redundant
+ * because the seed state also appears
+ * on the 1st dfs stack and would be
+ * matched in hstore below
+ */
+ if ((now._a_t&1) && depth > A_depth)
+ { if (!memcmp((char *)&A_Root,
+ (char *)&now, vsize))
+ {
+ depthfound = A_depth;
+#ifdef CHECK
+ printf("matches seed\n");
+#endif
+#ifdef NP
+ uerror("non-progress cycle");
+#else
+ uerror("acceptance cycle");
+#endif
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+#ifdef CHECK
+ printf("not seed\n");
+#endif
+ }
+#endif
+ if (!(trpt->tau&8)) /* if no atomic move */
+ {
+#ifdef BITSTATE
+#ifdef CNTRSTACK
+ II = bstore((char *)&now, vsize);
+ trpt->j6 = j1; trpt->j7 = j2;
+ JJ = LL[j1] && LL[j2];
+#else
+#ifdef FULLSTACK
+ JJ = onstack_now();
+#else
+#ifndef NOREDUCE
+ JJ = II; /* worstcase guess for p.o. */
+#endif
+#endif
+ II = bstore((char *)&now, vsize);
+#endif
+#else
+#ifdef MA
+ II = gstore((char *)&now, vsize, 0);
+#ifndef FULLSTACK
+ JJ = II;
+#else
+ JJ = (II == 2)?1:0;
+#endif
+#else
+ II = hstore((char *)&now, vsize);
+#ifdef FULLSTACK
+ JJ = (II == 2)?1:0;
+#endif
+#endif
+#endif
+ kk = (II == 1 || II == 2);
+#ifndef SAFETY
+#if NCORE==1 || defined (SEP_STATE)
+ if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2)))
+ #ifndef NOFAIR
+#if 0
+ if (!fairness || ((now._a_t&1) && now._cnt[1] == 1)) /* 5.1.4 */
+#else
+ if (a_cycles && !fairness) /* 5.1.6 -- example by Hirofumi Watanabe */
+#endif
+ #endif
+ {
+ II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */
+#ifdef VERBOSE
+ printf("state match on dfs stack\n");
+#endif
+ goto same_case;
+ }
+#endif
+#if defined(FULLSTACK) && defined(BITSTATE)
+ if (!JJ && (now._a_t&1) && depth > A_depth)
+ { int oj1 = j1;
+ uchar o_a_t = now._a_t;
+ now._a_t &= ~(1|16|32);
+ if (onstack_now())
+ { II = 3;
+#ifdef VERBOSE
+ printf("state match on 1st dfs stack\n");
+#endif
+ }
+ now._a_t = o_a_t;
+ j1 = oj1;
+ }
+#endif
+ if (II == 3 && a_cycles && (now._a_t&1))
+ {
+#ifndef NOFAIR
+ if (fairness && now._cnt[1] > 1) /* was != 0 */
+ {
+#ifdef VERBOSE
+ printf(" fairness count non-zero\n");
+#endif
+ II = 0;
+ } else
+#endif
+ {
+#ifndef BITSTATE
+ nShadow--;
+#endif
+same_case: if (Lstate) depthfound = Lstate->D;
+#ifdef NP
+ uerror("non-progress cycle");
+#else
+ uerror("acceptance cycle");
+#endif
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+ }
+#endif
+#ifndef NOREDUCE
+#ifndef SAFETY
+#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)
+ if (II != 0 && (!Lstate || Lstate->cpu_id < core_id))
+ { (trpt-1)->tau |= 16;
+ }
+#endif
+ if ((II && JJ) || (II == 3))
+ { /* marker for liveness proviso */
+#ifndef LOOPSTATE
+ (trpt-1)->tau |= 16;
+#endif
+ truncs2++;
+ }
+#else
+#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)
+ if (!(II != 0 && (!Lstate || Lstate->cpu_id < core_id)))
+ { /* treat as stack state */
+ (trpt-1)->tau |= 16;
+ } else
+ { /* treat as non-stack state */
+ (trpt-1)->tau |= 64;
+ }
+#endif
+ if (!II || !JJ)
+ { /* successor outside stack */
+ (trpt-1)->tau |= 64;
+ }
+#endif
+#endif
+ if (II)
+ { truncs++;
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ if (depth == 0)
+ { return;
+ } }
+#endif
+ goto Up;
+ }
+ if (!kk)
+ { static long sdone = (long) 0; long ndone;
+ nstates++;
+#if defined(ZAPH) && defined(BITSTATE)
+ zstates += (double) hfns;
+#endif
+ ndone = (unsigned long) (nstates/((double) FREQ));
+ if (ndone != sdone)
+ { snapshot();
+ sdone = ndone;
+#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
+ if (nstates > ((double)(ONE_L<<(ssize+1))))
+ { void resize_hashtable(void);
+ resize_hashtable();
+ }
+#endif
+#if defined(ZAPH) && defined(BITSTATE)
+ if (zstates > ((double)(ONE_L<<(ssize-2))))
+ { /* more than half the bits set */
+ void zap_hashtable(void);
+ zap_hashtable();
+ zstates = 0;
+ }
+#endif
+ }
+#ifdef SVDUMP
+ if (vprefix > 0)
+ if (write(svfd, (uchar *) &now, vprefix) != vprefix)
+ { fprintf(efd, "writing %s.svd failed\n", PanSource);
+ wrapup();
+ }
+#endif
+#if defined(MA) && defined(W_XPT)
+ if ((unsigned long) nstates%W_XPT == 0)
+ { void w_xpoint(void);
+ w_xpoint();
+ }
+#endif
+ }
+#if defined(FULLSTACK) || defined(CNTRSTACK)
+ onstack_put();
+#ifdef DEBUG2
+#if defined(FULLSTACK) && !defined(MA)
+ printf("%d: putting %u (%d)\n", depth,
+ trpt->ostate,
+ (trpt->ostate)?trpt->ostate->tagged:0);
+#else
+ printf("%d: putting\n", depth);
+#endif
+#endif
+#else
+ #if NCORE>1
+ trpt->ostate = Lstate;
+ #endif
+#endif
+ } }
+ if (depth > mreached)
+ mreached = depth;
+#ifdef VERI
+ if (trpt->tau&4)
+#endif
+ trpt->tau &= ~(1|2); /* timeout and -request off */
+ _n = 0;
+#if SYNC
+ (trpt+1)->o_n = 0;
+#endif
+#ifdef VERI
+ if (now._nr_pr == 0) /* claim terminated */
+ uerror("end state in claim reached");
+ check_claim(((P0 *)pptr(0))->_p);
+Stutter:
+ if (trpt->tau&4) /* must make a claimmove */
+ {
+#ifndef NOFAIR
+ if ((now._a_t&2) /* A-bit set */
+ && now._cnt[now._a_t&1] == 1)
+ { now._a_t &= ~2;
+ now._cnt[now._a_t&1] = 0;
+ trpt->o_pm |= 16;
+#ifdef DEBUG
+ printf("%3d: fairness Rule 3.: _a_t = %d\n",
+ depth, now._a_t);
+#endif
+ }
+#endif
+ II = 0; /* never */
+ goto Veri0;
+ }
+#endif
+#ifndef NOREDUCE
+ /* Look for a process with only safe transitions */
+ /* (special rules apply in the 2nd dfs) */
+ if (boq == -1 && From != To
+
+#ifdef SAFETY
+ #if NCORE>1
+ && (depth < z_handoff)
+ #endif
+ )
+#else
+ #if NCORE>1
+ && ((a_cycles) || (!a_cycles && depth < z_handoff))
+ #endif
+ && (!(now._a_t&1)
+ || (a_cycles &&
+ #ifndef BITSTATE
+#ifdef MA
+#ifdef VERI
+ !((trpt-1)->proviso))
+#else
+ !(trpt->proviso))
+#endif
+#else
+#ifdef VERI
+ (trpt-1)->ostate &&
+ !(((char *)&((trpt-1)->ostate->state))[0] & 128))
+#else
+ !(((char *)&(trpt->ostate->state))[0] & 128))
+#endif
+#endif
+ #else
+#ifdef VERI
+ (trpt-1)->ostate &&
+ (trpt-1)->ostate->proviso == 0)
+#else
+ trpt->ostate->proviso == 0)
+#endif
+ #endif
+ ))
+#endif
+
+#ifdef REVERSE
+ for (II = From; II <= To; II++)
+#else
+ for (II = From; II >= To; II--)
+#endif
+ {
+Resume: /* pick up here if preselect fails */
+ this = pptr(II);
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+ if (trans[ot][tt]->atom & 8)
+ { t = trans[ot][tt];
+ if (t->qu[0] != 0)
+ { Ccheck++;
+ if (!q_cond(II, t))
+ continue;
+ Cholds++;
+ }
+ From = To = II; /* the process preselected */
+#ifdef NIBIS
+ t->om = 0;
+#endif
+ trpt->tau |= 32; /* preselect marker */
+#ifdef DEBUG
+#ifdef NIBIS
+ printf("%3d: proc %d Pre", depth, II);
+ printf("Selected (om=%d, tau=%d)\n",
+ t->om, trpt->tau);
+#else
+ printf("%3d: proc %d PreSelected (tau=%d)\n",
+ depth, II, trpt->tau);
+#endif
+#endif
+ goto Again;
+ }
+ }
+ trpt->tau &= ~32;
+#endif
+#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI))
+Again:
+#endif
+ /* The Main Expansion Loop over Processes */
+ trpt->o_pm &= ~(8|16|32|64); /* fairness-marks */
+#ifndef NOFAIR
+ if (fairness && boq == -1
+#ifdef VERI
+ && (!(trpt->tau&4) && !((trpt-1)->tau&128))
+#endif
+ && !(trpt->tau&8))
+ { /* A_bit = 1; Cnt = N in acc states with A_bit 0 */
+ if (!(now._a_t&2))
+ {
+ if (a_cycles && (trpt->o_pm&2))
+ { /* Accepting state */
+ now._a_t |= 2;
+ now._cnt[now._a_t&1] = now._nr_pr + 1;
+ trpt->o_pm |= 8;
+#ifdef DEBUG
+ printf("%3d: fairness Rule 1: cnt=%d, _a_t=%d\n",
+ depth, now._cnt[now._a_t&1], now._a_t);
+#endif
+ }
+ } else
+ { /* A_bit = 0 when Cnt 0 */
+ if (now._cnt[now._a_t&1] == 1)
+ { now._a_t &= ~2;
+ now._cnt[now._a_t&1] = 0;
+ trpt->o_pm |= 16;
+#ifdef DEBUG
+ printf("%3d: fairness Rule 3: _a_t = %d\n",
+ depth, now._a_t);
+#endif
+ } } }
+#endif
+
+#ifdef REVERSE
+ for (II = From; II <= To; II++)
+#else
+ for (II = From; II >= To; II--)
+#endif
+ {
+#if SYNC
+ /* no rendezvous with same proc */
+ if (boq != -1 && trpt->pr == II) continue;
+#endif
+#ifdef SCHED
+ /* limit max nr of interleavings */
+ if (From != To
+ && depth > 0
+ #ifdef VERI
+ && II != 0
+ #endif
+ && (trpt-1)->pr != II
+ && trpt->sched_limit >= sched_max)
+ { continue;
+ }
+#endif
+#ifdef VERI
+Veri0:
+#endif
+ this = pptr(II);
+ tt = (int) ((P0 *)this)->_p;
+ ot = (uchar) ((P0 *)this)->_t;
+#ifdef NIBIS
+ /* don't repeat a previous preselected expansion */
+ /* could hit this if reduction proviso was false */
+ t = trans[ot][tt];
+ if (!(trpt->tau&4)
+ && !(trpt->tau&1)
+ && !(trpt->tau&32)
+ && (t->atom & 8)
+ && boq == -1
+ && From != To)
+ { if (t->qu[0] == 0
+ || q_cond(II, t))
+ { _m = t->om;
+ if (_m>_n||(_n>3&&_m!=0)) _n=_m;
+ continue; /* did it before */
+ } }
+#endif
+ trpt->o_pm &= ~1; /* no move in this pid yet */
+#ifdef EVENT_TRACE
+ (trpt+1)->o_event = now._event;
+#endif
+ /* Fairness: Cnt++ when Cnt == II */
+#ifndef NOFAIR
+ trpt->o_pm &= ~64; /* didn't apply rule 2 */
+ if (fairness
+ && boq == -1
+ && !(trpt->o_pm&32)
+ && (now._a_t&2)
+ && now._cnt[now._a_t&1] == II+2)
+ { now._cnt[now._a_t&1] -= 1;
+#ifdef VERI
+ /* claim need not participate */
+ if (II == 1)
+ now._cnt[now._a_t&1] = 1;
+#endif
+#ifdef DEBUG
+ printf("%3d: proc %d fairness ", depth, II);
+ printf("Rule 2: --cnt to %d (%d)\n",
+ now._cnt[now._a_t&1], now._a_t);
+#endif
+ trpt->o_pm |= (32|64);
+ }
+#endif
+#ifdef HAS_PROVIDED
+ if (!provided(II, ot, tt, t)) continue;
+#endif
+ /* check all trans of proc II - escapes first */
+#ifdef HAS_UNLESS
+ trpt->e_state = 0;
+#endif
+ (trpt+1)->pr = (uchar) II;
+ (trpt+1)->st = tt;
+#ifdef RANDOMIZE
+ for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++)
+ { if (strcmp(t->tp, "else") == 0)
+ { eoi++;
+ break;
+ } }
+ if (eoi > 0)
+ { t = trans[ot][tt];
+ #ifdef VERBOSE
+ printf("randomizer: suppressed, saw else\n");
+ #endif
+ } else
+ { eoi = rand()%ooi;
+ #ifdef VERBOSE
+ printf("randomizer: skip %d in %d\n", eoi, ooi);
+ #endif
+ for (t = trans[ot][tt]; t; t = t->nxt)
+ if (eoi-- <= 0) break;
+ }
+domore:
+ for ( ; t && ooi > 0; t = t->nxt, ooi--)
+#else
+ for (t = trans[ot][tt]; t; t = t->nxt)
+#endif
+ {
+#ifdef HAS_UNLESS
+ /* exploring all transitions from
+ * a single escape state suffices
+ */
+ if (trpt->e_state > 0
+ && trpt->e_state != t->e_trans)
+ {
+#ifdef DEBUG
+ printf("skip 2nd escape %d (did %d before)\n",
+ t->e_trans, trpt->e_state);
+#endif
+ break;
+ }
+#endif
+ (trpt+1)->o_t = t;
+#ifdef INLINE
+#include FORWARD_MOVES
+P999: /* jumps here when move succeeds */
+#else
+ if (!(_m = do_transit(t, II))) continue;
+#endif
+#ifdef SCHED
+ if (depth > 0
+ #ifdef VERI
+ && II != 0
+ #endif
+ && (trpt-1)->pr != II)
+ { trpt->sched_limit = 1 + (trpt-1)->sched_limit;
+ }
+#endif
+ if (boq == -1)
+#ifdef CTL
+ /* for branching-time, can accept reduction only if */
+ /* the persistent set contains just 1 transition */
+ { if ((trpt->tau&32) && (trpt->o_pm&1))
+ trpt->tau |= 16;
+ trpt->o_pm |= 1; /* we moved */
+ }
+#else
+ trpt->o_pm |= 1; /* we moved */
+#endif
+#ifdef LOOPSTATE
+ if (loopstate[ot][tt])
+ {
+#ifdef VERBOSE
+ printf("exiting from loopstate:\n");
+#endif
+ trpt->tau |= 16;
+ cnt_loops++;
+ }
+#endif
+#ifdef PEG
+ peg[t->forw]++;
+#endif
+#if defined(VERBOSE) || defined(CHECK)
+#if defined(SVDUMP)
+ cpu_printf("%3d: proc %d exec %d \n", depth, II, t->t_id);
+#else
+ cpu_printf("%3d: proc %d exec %d, %d to %d, %s %s %s %saccepting [tau=%d]\n",
+ depth, II, t->forw, tt, t->st, t->tp,
+ (t->atom&2)?"atomic":"",
+ (boq != -1)?"rendez-vous":"",
+ (trpt->o_pm&2)?"":"non-", trpt->tau);
+#ifdef HAS_UNLESS
+ if (t->e_trans)
+ cpu_printf("\t(escape to state %d)\n", t->st);
+#endif
+#endif
+#ifdef RANDOMIZE
+ cpu_printf("\t(randomizer %d)\n", ooi);
+#endif
+#endif
+#ifdef HAS_LAST
+#ifdef VERI
+ if (II != 0)
+#endif
+ now._last = II - BASE;
+#endif
+#ifdef HAS_UNLESS
+ trpt->e_state = t->e_trans;
+#endif
+ depth++; trpt++;
+ trpt->pr = (uchar) II;
+ trpt->st = tt;
+ trpt->o_pm &= ~(2|4);
+ if (t->st > 0)
+ { ((P0 *)this)->_p = t->st;
+/* moved down reached[ot][t->st] = 1; */
+ }
+#ifndef SAFETY
+ if (a_cycles)
+ {
+#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP))
+ int ii;
+#endif
+#define P__Q ((P0 *)pptr(ii))
+#if ACCEPT_LAB>0
+#ifdef NP
+ /* state 1 of np_ claim is accepting */
+ if (((P0 *)pptr(0))->_p == 1)
+ trpt->o_pm |= 2;
+#else
+ for (ii = 0; ii < (int) now._nr_pr; ii++)
+ { if (accpstate[P__Q->_t][P__Q->_p])
+ { trpt->o_pm |= 2;
+ break;
+ } }
+#endif
+#endif
+#if defined(HAS_NP) && PROG_LAB>0
+ for (ii = 0; ii < (int) now._nr_pr; ii++)
+ { if (progstate[P__Q->_t][P__Q->_p])
+ { trpt->o_pm |= 4;
+ break;
+ } }
+#endif
+#undef P__Q
+ }
+#endif
+ trpt->o_t = t; trpt->o_n = _n;
+ trpt->o_ot = ot; trpt->o_tt = tt;
+ trpt->o_To = To; trpt->o_m = _m;
+ trpt->tau = 0;
+#ifdef RANDOMIZE
+ trpt->oo_i = ooi;
+#endif
+ if (boq != -1 || (t->atom&2))
+ { trpt->tau |= 8;
+#ifdef VERI
+ /* atomic sequence in claim */
+ if((trpt-1)->tau&4)
+ trpt->tau |= 4;
+ else
+ trpt->tau &= ~4;
+ } else
+ { if ((trpt-1)->tau&4)
+ trpt->tau &= ~4;
+ else
+ trpt->tau |= 4;
+ }
+ /* if claim allowed timeout, so */
+ /* does the next program-step: */
+ if (((trpt-1)->tau&1) && !(trpt->tau&4))
+ trpt->tau |= 1;
+#else
+ } else
+ trpt->tau &= ~8;
+#endif
+ if (boq == -1 && (t->atom&2))
+ { From = To = II; nlinks++;
+ } else
+#ifdef REVERSE
+ { From = BASE; To = now._nr_pr-1;
+#else
+ { From = now._nr_pr-1; To = BASE;
+#endif
+ }
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Push_Stack_Tree(II, t->t_id);
+ }
+#endif
+ goto Down; /* pseudo-recursion */
+Up:
+#ifdef CHECK
+ cpu_printf("%d: Up - %s\n", depth,
+ (trpt->tau&4)?"claim":"program");
+#endif
+#if NCORE>1
+ iam_alive();
+ #ifdef USE_DISK
+ mem_drain();
+ #endif
+#endif
+#if defined(MA) || NCORE>1
+ if (depth <= 0) return;
+ /* e.g., if first state is old, after a restart */
+#endif
+#ifdef SC
+ if (CNT1 > CNT2
+ && depth < hiwater - (HHH-DDD) + 2)
+ {
+ trpt += DDD;
+ disk2stack();
+ maxdepth -= DDD;
+ hiwater -= DDD;
+ if(verbose)
+ printf("unzap %d: %d\n", CNT2, hiwater);
+ }
+#endif
+#ifndef NOFAIR
+ if (trpt->o_pm&128) /* fairness alg */
+ { now._cnt[now._a_t&1] = trpt->bup.oval;
+ _n = 1; trpt->o_pm &= ~128;
+ depth--; trpt--;
+#if defined(VERBOSE) || defined(CHECK)
+ printf("%3d: reversed fairness default move\n", depth);
+#endif
+ goto Q999;
+ }
+#endif
+#ifdef HAS_LAST
+#ifdef VERI
+ { int d; Trail *trl;
+ now._last = 0;
+ for (d = 1; d < depth; d++)
+ { trl = getframe(depth-d); /* was (trpt-d) */
+ if (trl->pr != 0)
+ { now._last = trl->pr - BASE;
+ break;
+ } } }
+#else
+ now._last = (depth<1)?0:(trpt-1)->pr;
+#endif
+#endif
+#ifdef EVENT_TRACE
+ now._event = trpt->o_event;
+#endif
+#ifndef SAFETY
+ if ((now._a_t&1) && depth <= A_depth)
+ return; /* to checkcycles() */
+#endif
+ t = trpt->o_t; _n = trpt->o_n;
+ ot = trpt->o_ot; II = trpt->pr;
+ tt = trpt->o_tt; this = pptr(II);
+ To = trpt->o_To; _m = trpt->o_m;
+#ifdef RANDOMIZE
+ ooi = trpt->oo_i;
+#endif
+#ifdef INLINE_REV
+ _m = do_reverse(t, II, _m);
+#else
+#include REVERSE_MOVES
+R999: /* jumps here when done */
+#endif
+#ifdef VERBOSE
+ cpu_printf("%3d: proc %d reverses %d, %d to %d\n",
+ depth, II, t->forw, tt, t->st);
+ cpu_printf("\t%s [abit=%d,adepth=%d,tau=%d,%d]\n",
+ t->tp, now._a_t, A_depth, trpt->tau, (trpt-1)->tau);
+#endif
+#ifndef NOREDUCE
+ /* pass the proviso tags */
+ if ((trpt->tau&8) /* rv or atomic */
+ && (trpt->tau&16))
+ (trpt-1)->tau |= 16;
+#ifdef SAFETY
+ if ((trpt->tau&8) /* rv or atomic */
+ && (trpt->tau&64))
+ (trpt-1)->tau |= 64;
+#endif
+#endif
+ depth--; trpt--;
+
+#ifdef NSUCC
+ trpt->n_succ++;
+#endif
+#ifdef NIBIS
+ (trans[ot][tt])->om = _m; /* head of list */
+#endif
+ /* i.e., not set if rv fails */
+ if (_m)
+ {
+#if defined(VERI) && !defined(NP)
+ if (II == 0 && verbose && !reached[ot][t->st])
+ {
+ printf("depth %d: Claim reached state %d (line %d)\n",
+ depth, t->st, src_claim [t->st]);
+ fflush(stdout);
+ }
+#endif
+ reached[ot][t->st] = 1;
+ reached[ot][tt] = 1;
+ }
+#ifdef HAS_UNLESS
+ else trpt->e_state = 0; /* undo */
+#endif
+ if (_m>_n||(_n>3&&_m!=0)) _n=_m;
+ ((P0 *)this)->_p = tt;
+ } /* all options */
+#ifdef RANDOMIZE
+ if (!t && ooi > 0)
+ { t = trans[ot][tt];
+ #ifdef VERBOSE
+ printf("randomizer: continue for %d more\n", ooi);
+ #endif
+ goto domore;
+ }
+ #ifdef VERBOSE
+ else
+ printf("randomizer: done\n");
+ #endif
+#endif
+#ifndef NOFAIR
+ /* Fairness: undo Rule 2 */
+ if ((trpt->o_pm&32)
+ && (trpt->o_pm&64))
+ { if (trpt->o_pm&1)
+ {
+#ifdef VERI
+ if (now._cnt[now._a_t&1] == 1)
+ now._cnt[now._a_t&1] = 2;
+#endif
+ now._cnt[now._a_t&1] += 1;
+#ifdef VERBOSE
+ printf("%3d: proc %d fairness ", depth, II);
+ printf("undo Rule 2, cnt=%d, _a_t=%d\n",
+ now._cnt[now._a_t&1], now._a_t);
+#endif
+ trpt->o_pm &= ~(32|64);
+ } else
+ { if (_n > 0)
+ {
+ trpt->o_pm &= ~64;
+#ifdef REVERSE
+ II = From-1;
+#else
+ II = From+1;
+#endif
+ } } }
+#endif
+#ifdef VERI
+ if (II == 0) break; /* never claim */
+#endif
+ } /* all processes */
+#ifdef NSUCC
+ tally_succ(trpt->n_succ);
+#endif
+#ifdef SCHED
+ if (_n == 0 /* no process could move */
+ #ifdef VERI
+ && II != 0
+ #endif
+ && depth > 0
+ && trpt->sched_limit >= sched_max)
+ { _n = 1; /* not a deadlock */
+ }
+#endif
+#ifndef NOFAIR
+ /* Fairness: undo Rule 2 */
+ if (trpt->o_pm&32) /* remains if proc blocked */
+ {
+#ifdef VERI
+ if (now._cnt[now._a_t&1] == 1)
+ now._cnt[now._a_t&1] = 2;
+#endif
+ now._cnt[now._a_t&1] += 1;
+#ifdef VERBOSE
+ printf("%3d: proc -- fairness ", depth);
+ printf("undo Rule 2, cnt=%d, _a_t=%d\n",
+ now._cnt[now._a_t&1], now._a_t);
+#endif
+ trpt->o_pm &= ~32;
+ }
+#ifndef NP
+ if (fairness
+ && _n == 0 /* nobody moved */
+#ifdef VERI
+ && !(trpt->tau&4) /* in program move */
+#endif
+ && !(trpt->tau&8) /* not an atomic one */
+#ifdef OTIM
+ && ((trpt->tau&1) || endstate())
+#else
+#ifdef ETIM
+ && (trpt->tau&1) /* already tried timeout */
+#endif
+#endif
+#ifndef NOREDUCE
+ /* see below */
+ && !((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
+#endif
+ && now._cnt[now._a_t&1] > 0) /* needed more procs */
+ { depth++; trpt++;
+ trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4));
+ trpt->bup.oval = now._cnt[now._a_t&1];
+ now._cnt[now._a_t&1] = 1;
+#ifdef VERI
+ trpt->tau = 4;
+#else
+ trpt->tau = 0;
+#endif
+#ifdef REVERSE
+ From = BASE; To = now._nr_pr-1;
+#else
+ From = now._nr_pr-1; To = BASE;
+#endif
+#if defined(VERBOSE) || defined(CHECK)
+ printf("%3d: fairness default move ", depth);
+ printf("(all procs block)\n");
+#endif
+ goto Down;
+ }
+#endif
+Q999: /* returns here with _n>0 when done */;
+ if (trpt->o_pm&8)
+ { now._a_t &= ~2;
+ now._cnt[now._a_t&1] = 0;
+ trpt->o_pm &= ~8;
+#ifdef VERBOSE
+ printf("%3d: fairness undo Rule 1, _a_t=%d\n",
+ depth, now._a_t);
+#endif
+ }
+ if (trpt->o_pm&16)
+ { now._a_t |= 2;
+ now._cnt[now._a_t&1] = 1;
+ trpt->o_pm &= ~16;
+#ifdef VERBOSE
+ printf("%3d: fairness undo Rule 3, _a_t=%d\n",
+ depth, now._a_t);
+#endif
+ }
+#endif
+#ifndef NOREDUCE
+#ifdef SAFETY
+#ifdef LOOPSTATE
+ /* at least one move that was preselected at this */
+ /* level, blocked or was a loop control flow point */
+ if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
+#else
+ /* preselected move - no successors outside stack */
+ if ((trpt->tau&32) && !(trpt->tau&64))
+#endif
+#ifdef REVERSE
+ { From = BASE; To = now._nr_pr-1;
+#else
+ { From = now._nr_pr-1; To = BASE;
+#endif
+#ifdef DEBUG
+ printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n",
+ depth, II+1, _n, trpt->tau);
+#endif
+ _n = 0; trpt->tau &= ~(16|32|64);
+#ifdef REVERSE
+ if (II <= To) /* II already decremented */
+#else
+ if (II >= BASE) /* II already decremented */
+#endif
+ goto Resume;
+ else
+ goto Again;
+ }
+#else
+ /* at least one move that was preselected at this */
+ /* level, blocked or truncated at the next level */
+/* implied: #ifdef FULLSTACK */
+ if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
+ {
+#ifdef DEBUG
+ printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n",
+ depth, II+1, (int) _n, trpt->tau);
+#endif
+ if (a_cycles && (trpt->tau&16))
+ { if (!(now._a_t&1))
+ {
+#ifdef DEBUG
+ printf("%3d: setting proviso bit\n", depth);
+#endif
+#ifndef BITSTATE
+#ifdef MA
+#ifdef VERI
+ (trpt-1)->proviso = 1;
+#else
+ trpt->proviso = 1;
+#endif
+#else
+#ifdef VERI
+ if ((trpt-1)->ostate)
+ ((char *)&((trpt-1)->ostate->state))[0] |= 128;
+#else
+ ((char *)&(trpt->ostate->state))[0] |= 128;
+#endif
+#endif
+#else
+#ifdef VERI
+ if ((trpt-1)->ostate)
+ (trpt-1)->ostate->proviso = 1;
+#else
+ trpt->ostate->proviso = 1;
+#endif
+#endif
+#ifdef REVERSE
+ From = BASE; To = now._nr_pr-1;
+#else
+ From = now._nr_pr-1; To = BASE;
+#endif
+ _n = 0; trpt->tau &= ~(16|32|64);
+ goto Again; /* do full search */
+ } /* else accept reduction */
+ } else
+#ifdef REVERSE
+ { From = BASE; To = now._nr_pr-1;
+#else
+ { From = now._nr_pr-1; To = BASE;
+#endif
+ _n = 0; trpt->tau &= ~(16|32|64);
+#ifdef REVERSE
+ if (II <= To) /* already decremented */
+#else
+ if (II >= BASE) /* already decremented */
+#endif
+ goto Resume;
+ else
+ goto Again;
+ } }
+/* #endif */
+#endif
+#endif
+ if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2)))
+ {
+#ifdef DEBUG
+ cpu_printf("%3d: no move [II=%d, tau=%d, boq=%d]\n",
+ depth, II, trpt->tau, boq);
+#endif
+#if SYNC
+ /* ok if a rendez-vous fails: */
+ if (boq != -1) goto Done;
+#endif
+ /* ok if no procs or we're at maxdepth */
+ if ((now._nr_pr == 0 && (!strict || qs_empty()))
+#ifdef OTIM
+ || endstate()
+#endif
+ || depth >= maxdepth-1) goto Done;
+ if ((trpt->tau&8) && !(trpt->tau&4))
+ { trpt->tau &= ~(1|8);
+ /* 1=timeout, 8=atomic */
+#ifdef REVERSE
+ From = BASE; To = now._nr_pr-1;
+#else
+ From = now._nr_pr-1; To = BASE;
+#endif
+#ifdef DEBUG
+ cpu_printf("%3d: atomic step proc %d unexecutable\n", depth, II+1);
+#endif
+#ifdef VERI
+ trpt->tau |= 4; /* switch to claim */
+#endif
+ goto AllOver;
+ }
+#ifdef ETIM
+ if (!(trpt->tau&1)) /* didn't try timeout yet */
+ {
+#ifdef VERI
+ if (trpt->tau&4)
+ {
+#ifndef NTIM
+ if (trpt->tau&2) /* requested */
+#endif
+ { trpt->tau |= 1;
+ trpt->tau &= ~2;
+#ifdef DEBUG
+ cpu_printf("%d: timeout\n", depth);
+#endif
+ goto Stutter;
+ } }
+ else
+ { /* only claim can enable timeout */
+ if ((trpt->tau&8)
+ && !((trpt-1)->tau&4))
+/* blocks inside an atomic */ goto BreakOut;
+#ifdef DEBUG
+ cpu_printf("%d: req timeout\n",
+ depth);
+#endif
+ (trpt-1)->tau |= 2; /* request */
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+#else
+#ifdef DEBUG
+ cpu_printf("%d: timeout\n", depth);
+#endif
+ trpt->tau |= 1;
+ goto Again;
+#endif
+ }
+#endif
+#ifdef VERI
+BreakOut:
+#ifndef NOSTUTTER
+ if (!(trpt->tau&4))
+ { trpt->tau |= 4; /* claim stuttering */
+ trpt->tau |= 128; /* stutter mark */
+#ifdef DEBUG
+ cpu_printf("%d: claim stutter\n", depth);
+#endif
+ goto Stutter;
+ }
+#else
+ ;
+#endif
+#else
+ if (!noends && !a_cycles && !endstate())
+ { depth--; trpt--; /* new 4.2.3 */
+ uerror("invalid end state");
+ depth++; trpt++;
+ }
+#ifndef NOSTUTTER
+ else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */
+ { depth--; trpt--;
+ uerror("accept stutter");
+ depth++; trpt++;
+ }
+#endif
+#endif
+ }
+Done:
+ if (!(trpt->tau&8)) /* not in atomic seqs */
+ {
+#ifndef SAFETY
+ if (_n != 0
+#ifdef VERI
+ /* --after-- a program-step, i.e., */
+ /* after backtracking a claim-step */
+ && (trpt->tau&4)
+ /* with at least one running process */
+ /* unless in a stuttered accept state */
+ && ((now._nr_pr > 1) || (trpt->o_pm&2))
+#endif
+ && !(now._a_t&1))
+ {
+#ifndef NOFAIR
+ if (fairness)
+ {
+#ifdef VERBOSE
+ cpu_printf("Consider check %d %d...\n",
+ now._a_t, now._cnt[0]);
+#endif
+ if ((now._a_t&2) /* A-bit */
+ && (now._cnt[0] == 1))
+ checkcycles();
+ } else
+#endif
+ if (a_cycles && (trpt->o_pm&2))
+ checkcycles();
+ }
+#endif
+#ifndef MA
+#if defined(FULLSTACK) || defined(CNTRSTACK)
+#ifdef VERI
+ if (boq == -1
+ && (((trpt->tau&4) && !(trpt->tau&128))
+ || ( (trpt-1)->tau&128)))
+#else
+ if (boq == -1)
+#endif
+ {
+#ifdef DEBUG2
+#if defined(FULLSTACK)
+ printf("%d: zapping %u (%d)\n",
+ depth, trpt->ostate,
+ (trpt->ostate)?trpt->ostate->tagged:0);
+#endif
+#endif
+ onstack_zap();
+ }
+#endif
+#else
+#ifdef VERI
+ if (boq == -1
+ && (((trpt->tau&4) && !(trpt->tau&128))
+ || ( (trpt-1)->tau&128)))
+#else
+ if (boq == -1)
+#endif
+ {
+#ifdef DEBUG
+ printf("%d: zapping\n", depth);
+#endif
+ onstack_zap();
+#ifndef NOREDUCE
+ if (trpt->proviso)
+ gstore((char *) &now, vsize, 1);
+#endif
+ }
+#endif
+ }
+ if (depth > 0)
+ {
+#if NCORE>1 && defined(FULL_TRAIL)
+ if (upto > 0)
+ { Pop_Stack_Tree();
+ }
+#endif
+ goto Up;
+ }
+}
+
+#else
+void new_state(void) { /* place holder */ }
+#endif
+
+void
+assert(int a, char *s, int ii, int tt, Trans *t)
+{
+ if (!a && !noasserts)
+ { char bad[1024];
+ strcpy(bad, "assertion violated ");
+ if (strlen(s) > 1000)
+ { strncpy(&bad[19], (const char *) s, 1000);
+ bad[1019] = '\0';
+ } else
+ strcpy(&bad[19], s);
+ uerror(bad);
+ }
+}
+#ifndef NOBOUNDCHECK
+int
+Boundcheck(int x, int y, int a1, int a2, Trans *a3)
+{
+ assert((x >= 0 && x < y), "- invalid array index",
+ a1, a2, a3);
+ return x;
+}
+#endif
+void
+wrap_stats(void)
+{
+ if (nShadow>0)
+ printf("%9.8g states, stored (%g visited)\n",
+ nstates - nShadow, nstates);
+ else
+ printf("%9.8g states, stored\n", nstates);
+#ifdef BFS
+#if SYNC
+ printf(" %8g nominal states (- rv and atomic)\n", nstates-midrv-nlinks+revrv);
+ printf(" %8g rvs succeeded\n", midrv-failedrv);
+#else
+ printf(" %8g nominal states (stored-atomic)\n", nstates-nlinks);
+#endif
+#ifdef DEBUG
+ printf(" %8g midrv\n", midrv);
+ printf(" %8g failedrv\n", failedrv);
+ printf(" %8g revrv\n", revrv);
+#endif
+#endif
+ printf("%9.8g states, matched\n", truncs);
+#ifdef CHECK
+ printf("%9.8g matches within stack\n",truncs2);
+#endif
+ if (nShadow>0)
+ printf("%9.8g transitions (= visited+matched)\n",
+ nstates+truncs);
+ else
+ printf("%9.8g transitions (= stored+matched)\n",
+ nstates+truncs);
+ printf("%9.8g atomic steps\n", nlinks);
+ if (nlost) printf("%g lost messages\n", (double) nlost);
+
+#ifndef BITSTATE
+ printf("hash conflicts: %9.8g (resolved)\n", hcmp);
+ #ifndef AUTO_RESIZE
+ if (hcmp > (double) (1<<ssize))
+ { printf("hint: increase hashtable-size (-w) to reduce runtime\n");
+ } /* in multi-core: also reduces lock delays on access to hashtable */
+ #endif
+#else
+#ifdef CHECK
+ printf("%8g states allocated for dfs stack\n", ngrabs);
+#endif
+ if (udmem)
+ printf("\nhash factor: %4g (best if > 100.)\n\n",
+ (double)(((double) udmem) * 8.0) / (double) nstates);
+ else
+ printf("\nhash factor: %4g (best if > 100.)\n\n",
+ (double)(1<<(ssize-8)) / (double) nstates * 256.0);
+ printf("bits set per state: %u (-k%u)\n", hfns, hfns);
+ #if 0
+ if (udmem)
+ { printf("total bits available: %8g (-M%ld)\n",
+ ((double) udmem) * 8.0, udmem/(1024L*1024L));
+ } else
+ printf("total bits available: %8g (-w%d)\n",
+ ((double) (ONE_L << (ssize-4)) * 16.0), ssize);
+ #endif
+#endif
+#ifdef BFS_DISK
+ printf("bfs disk reads: %ld writes %ld -- diff %ld\n",
+ bfs_dsk_reads, bfs_dsk_writes, bfs_dsk_writes-bfs_dsk_reads);
+ if (bfs_dsk_read >= 0) (void) close(bfs_dsk_read);
+ if (bfs_dsk_write >= 0) (void) close(bfs_dsk_write);
+ (void) unlink("pan_bfs_dsk.tmp");
+#endif
+}
+
+void
+wrapup(void)
+{
+#if defined(BITSTATE) || !defined(NOCOMP)
+ double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0;
+ #if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM))
+ int mverbose = 1;
+ #else
+ int mverbose = verbose;
+ #endif
+#endif
+#if NCORE>1
+ if (verbose) cpu_printf("wrapup -- %d error(s)\n", errors);
+ if (core_id != 0)
+ {
+#ifdef USE_DISK
+ void dsk_stats(void);
+ dsk_stats();
+#endif
+ if (search_terminated != NULL)
+ { *search_terminated |= 2; /* wrapup */
+ }
+ exit(0); /* normal termination, not an error */
+ }
+#endif
+#if !defined(WIN32) && !defined(WIN64)
+ signal(SIGINT, SIG_DFL);
+#endif
+ printf("\n(%s)\n", SpinVersion);
+ if (!done) printf("Warning: Search not completed\n");
+#ifdef SC
+ (void) unlink((const char *)stackfile);
+#endif
+#if NCORE>1
+ if (a_cycles)
+ { printf(" + Multi-Core (NCORE=%d)\n", NCORE);
+ } else
+ { printf(" + Multi-Core (NCORE=%d -z%d)\n", NCORE, z_handoff);
+ }
+#endif
+#ifdef BFS
+ printf(" + Using Breadth-First Search\n");
+#endif
+#ifndef NOREDUCE
+ printf(" + Partial Order Reduction\n");
+#endif
+#ifdef REVERSE
+ printf(" + Reverse Depth-First Search Order\n");
+#endif
+#ifdef T_REVERSE
+ printf(" + Reverse Transition Ordering\n");
+#endif
+#ifdef RANDOMIZE
+ printf(" + Randomized Transition Ordering\n");
+#endif
+#ifdef SCHED
+ printf(" + Scheduling Restriction (-DSCHED=%d)\n", sched_max);
+#endif
+#ifdef COLLAPSE
+ printf(" + Compression\n");
+#endif
+#ifdef MA
+ printf(" + Graph Encoding (-DMA=%d)\n", MA);
+ #ifdef R_XPT
+ printf(" Restarted from checkpoint %s.xpt\n", PanSource);
+ #endif
+#endif
+#ifdef CHECK
+ #ifdef FULLSTACK
+ printf(" + FullStack Matching\n");
+ #endif
+ #ifdef CNTRSTACK
+ printf(" + CntrStack Matching\n");
+ #endif
+#endif
+#ifdef BITSTATE
+ printf("\nBit statespace search for:\n");
+#else
+#ifdef HC
+ printf("\nHash-Compact %d search for:\n", HC);
+#else
+ printf("\nFull statespace search for:\n");
+#endif
+#endif
+#ifdef EVENT_TRACE
+#ifdef NEGATED_TRACE
+ printf(" notrace assertion +\n");
+#else
+ printf(" trace assertion +\n");
+#endif
+#endif
+#ifdef VERI
+ printf(" never claim +\n");
+ printf(" assertion violations ");
+ if (noasserts)
+ printf("- (disabled by -A flag)\n");
+ else
+ printf("+ (if within scope of claim)\n");
+#else
+#ifdef NOCLAIM
+ printf(" never claim - (not selected)\n");
+#else
+ printf(" never claim - (none specified)\n");
+#endif
+ printf(" assertion violations ");
+ if (noasserts)
+ printf("- (disabled by -A flag)\n");
+ else
+ printf("+\n");
+#endif
+#ifndef SAFETY
+#ifdef NP
+ printf(" non-progress cycles ");
+#else
+ printf(" acceptance cycles ");
+#endif
+ if (a_cycles)
+ printf("+ (fairness %sabled)\n",
+ fairness?"en":"dis");
+ else printf("- (not selected)\n");
+#else
+ printf(" cycle checks - (disabled by -DSAFETY)\n");
+#endif
+#ifdef VERI
+ printf(" invalid end states - ");
+ printf("(disabled by ");
+ if (noends)
+ printf("-E flag)\n\n");
+ else
+ printf("never claim)\n\n");
+#else
+ printf(" invalid end states ");
+ if (noends)
+ printf("- (disabled by -E flag)\n\n");
+ else
+ printf("+\n\n");
+#endif
+ printf("State-vector %d byte, depth reached %ld", hmax,
+#if NCORE>1
+ (nr_handoffs * z_handoff) +
+#endif
+ mreached);
+ printf(", errors: %d\n", errors);
+ fflush(stdout);
+#ifdef MA
+ if (done)
+ { extern void dfa_stats(void);
+ if (maxgs+a_cycles+2 < MA)
+ printf("MA stats: -DMA=%d is sufficient\n",
+ maxgs+a_cycles+2);
+ dfa_stats();
+ }
+#endif
+ wrap_stats();
+#ifdef CHECK
+ printf("stackframes: %d/%d\n\n", smax, svmax);
+ printf("stats: fa %d, fh %d, zh %d, zn %d - ",
+ Fa, Fh, Zh, Zn);
+ printf("check %d holds %d\n", Ccheck, Cholds);
+ printf("stack stats: puts %d, probes %d, zaps %d\n",
+ PUT, PROBE, ZAPS);
+#else
+ printf("\n");
+#endif
+
+#if defined(BITSTATE) || !defined(NOCOMP)
+ nr1 = (nstates-nShadow)*
+ (double)(hmax+sizeof(struct H_el)-sizeof(unsigned));
+#ifdef BFS
+ nr2 = 0.0;
+#else
+ nr2 = (double) ((maxdepth+3)*sizeof(Trail));
+#endif
+#ifndef BITSTATE
+#if !defined(MA) || defined(COLLAPSE)
+ nr3 = (double) (ONE_L<<ssize)*sizeof(struct H_el *);
+#endif
+#else
+ if (udmem)
+ nr3 = (double) (udmem);
+ else
+ nr3 = (double) (ONE_L<<(ssize-3));
+#ifdef CNTRSTACK
+ nr5 = (double) (ONE_L<<(ssize-3));
+#endif
+#ifdef FULLSTACK
+ nr5 = (double) (maxdepth*sizeof(struct H_el *));
+#endif
+#endif
+ nr4 = (double) (svmax * (sizeof(Svtack) + hmax))
+ + (double) (smax * (sizeof(Stack) + Maxbody));
+#ifndef MA
+ if (mverbose || memcnt < nr1+nr2+nr3+nr4+nr5)
+#endif
+ { double remainder = memcnt;
+ double tmp_nr = memcnt-nr3-nr4-(nr2-fragment)-nr5;
+#if NCORE>1 && !defined(SEP_STATE)
+ tmp_nr -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;
+#endif
+ if (tmp_nr < 0.0) tmp_nr = 0.;
+ printf("Stats on memory usage (in Megabytes):\n");
+ printf("%9.3f equivalent memory usage for states",
+ nr1/1048576.); /* 1024*1024=1048576 */
+ printf(" (stored*(State-vector + overhead))\n");
+ #if NCORE>1 && !defined(WIN32) && !defined(WIN64)
+ printf("%9.3f shared memory reserved for state storage\n",
+ mem_reserved/1048576.);
+ #ifdef SEP_HEAP
+ printf(" in %d local heaps of %7.3f MB each\n",
+ NCORE, mem_reserved/(NCORE*1048576.));
+ #endif
+ printf("\n");
+ #endif
+#ifdef BITSTATE
+ if (udmem)
+ printf("%9.3f memory used for hash array (-M%ld)\n",
+ nr3/1048576., udmem/(1024L*1024L));
+ else
+ printf("%9.3f memory used for hash array (-w%d)\n",
+ nr3/1048576., ssize);
+ if (nr5 > 0.0)
+ printf("%9.3f memory used for bit stack\n",
+ nr5/1048576.);
+ remainder = remainder - nr3 - nr5;
+#else
+ printf("%9.3f actual memory usage for states",
+ tmp_nr/1048576.);
+ remainder -= tmp_nr;
+ printf(" (");
+ if (tmp_nr > 0.)
+ { if (tmp_nr > nr1) printf("unsuccessful ");
+ printf("compression: %.2f%%)\n",
+ (100.0*tmp_nr)/nr1);
+ } else
+ printf("less than 1k)\n");
+#ifndef MA
+ if (tmp_nr > 0.)
+ { printf(" state-vector as stored = %.0f byte",
+ (tmp_nr)/(nstates-nShadow) -
+ (double) (sizeof(struct H_el) - sizeof(unsigned)));
+ printf(" + %ld byte overhead\n",
+ (long int) sizeof(struct H_el)-sizeof(unsigned));
+ }
+#endif
+#if !defined(MA) || defined(COLLAPSE)
+ printf("%9.3f memory used for hash table (-w%d)\n",
+ nr3/1048576., ssize);
+ remainder -= nr3;
+#endif
+#endif
+#ifndef BFS
+ printf("%9.3f memory used for DFS stack (-m%ld)\n",
+ nr2/1048576., maxdepth);
+ remainder -= nr2;
+#endif
+#if NCORE>1
+ remainder -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;
+ printf("%9.3f shared memory used for work-queues\n",
+ (GWQ_SIZE + (double) NCORE * LWQ_SIZE) /1048576.);
+ printf(" in %d queues of %7.3f MB each",
+ NCORE, (double) LWQ_SIZE /1048576.);
+ #ifndef NGQ
+ printf(" + a global q of %7.3f MB\n",
+ (double) GWQ_SIZE / 1048576.);
+ #else
+ printf("\n");
+ #endif
+ #endif
+ if (remainder - fragment > 1048576.)
+ printf("%9.3f other (proc and chan stacks)\n",
+ (remainder-fragment)/1048576.);
+ if (fragment > 1048576.)
+ printf("%9.3f memory lost to fragmentation\n",
+ fragment/1048576.);
+ printf("%9.3f total actual memory usage\n\n",
+ memcnt/1048576.);
+ }
+#ifndef MA
+ else
+#endif
+#endif
+#ifndef MA
+ printf("%9.3f memory usage (Mbyte)\n\n",
+ memcnt/1048576.);
+#endif
+#ifdef COLLAPSE
+ printf("nr of templates: [ globals chans procs ]\n");
+ printf("collapse counts: [ ");
+ { int i; for (i = 0; i < 256+2; i++)
+ if (ncomps[i] != 0)
+ printf("%d ", ncomps[i]);
+ printf("]\n");
+ }
+#endif
+ if ((done || verbose) && !no_rck) do_reach();
+#ifdef PEG
+ { int i;
+ printf("\nPeg Counts (transitions executed):\n");
+ for (i = 1; i < NTRANS; i++)
+ { if (peg[i]) putpeg(i, peg[i]);
+ } }
+#endif
+#ifdef VAR_RANGES
+ dumpranges();
+#endif
+#ifdef SVDUMP
+ if (vprefix > 0) close(svfd);
+#endif
+#ifdef LOOPSTATE
+ printf("%g loopstates hit\n", cnt_loops);
+#endif
+#ifdef NSUCC
+ dump_succ();
+#endif
+#if NCORE>1 && defined(T_ALERT)
+ crash_report();
+#endif
+ pan_exit(0);
+}
+
+void
+stopped(int arg)
+{ printf("Interrupted\n");
+#if NCORE>1
+ was_interrupted = 1;
+#endif
+ wrapup();
+ pan_exit(0);
+}
+
+#ifdef SFH
+/*
+ * super fast hash, based on Paul Hsieh's function
+ * http://www.azillionmonkeys.com/qed/hash.html
+ */
+#include <stdint.h>
+ #undef get16bits
+ #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+ #define get16bits(d) (*((const uint16_t *) (d)))
+ #endif
+
+ #ifndef get16bits
+ #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+ #endif
+
+void
+d_sfh(const char *s, int len)
+{ uint32_t h = len, tmp;
+ int rem;
+
+ rem = len & 3;
+ len >>= 2;
+
+ for ( ; len > 0; len--)
+ { h += get16bits(s);
+ tmp = (get16bits(s+2) << 11) ^ h;
+ h = (h << 16) ^ tmp;
+ s += 2*sizeof(uint16_t);
+ h += h >> 11;
+ }
+ switch (rem) {
+ case 3: h += get16bits(s);
+ h ^= h << 16;
+ h ^= s[sizeof(uint16_t)] << 18;
+ h += h >> 11;
+ break;
+ case 2: h += get16bits(s);
+ h ^= h << 11;
+ h += h >> 17;
+ break;
+ case 1: h += *s;
+ h ^= h << 10;
+ h += h >> 1;
+ break;
+ }
+ h ^= h << 3;
+ h += h >> 5;
+ h ^= h << 4;
+ h += h >> 17;
+ h ^= h << 25;
+ h += h >> 6;
+
+ K1 = h;
+}
+#endif
+
+#include <stdint.h>
+#if defined(HASH64) || defined(WIN64)
+/* 64-bit Jenkins hash, 1997
+ * http://burtleburtle.net/bob/c/lookup8.c
+ */
+#define mix(a,b,c) \
+{ a -= b; a -= c; a ^= (c>>43); \
+ b -= c; b -= a; b ^= (a<<9); \
+ c -= a; c -= b; c ^= (b>>8); \
+ a -= b; a -= c; a ^= (c>>38); \
+ b -= c; b -= a; b ^= (a<<23); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>35); \
+ b -= c; b -= a; b ^= (a<<49); \
+ c -= a; c -= b; c ^= (b>>11); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<18); \
+ c -= a; c -= b; c ^= (b>>22); \
+}
+#else
+/* 32-bit Jenkins hash, 2006
+ * http://burtleburtle.net/bob/c/lookup3.c
+ */
+#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
+
+#define mix(a,b,c) \
+{ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+#define final(a,b,c) \
+{ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+#endif
+
+void
+d_hash(uchar *kb, int nbytes)
+{ uint8_t *bp;
+#if defined(HASH64) || defined(WIN64)
+ uint64_t a = 0, b, c, n;
+ uint64_t *k = (uint64_t *) kb;
+#else
+ uint32_t a, b, c, n;
+ uint32_t *k = (uint32_t *) kb;
+#endif
+ /* extend to multiple of words, if needed */
+ n = nbytes/WS; /* nr of words */
+ a = nbytes - (n*WS);
+ if (a > 0)
+ { n++;
+ bp = kb + nbytes;
+ switch (a) {
+ case 3: *bp++ = 0; /* fall thru */
+ case 2: *bp++ = 0; /* fall thru */
+ case 1: *bp = 0;
+ case 0: break;
+ } }
+#if defined(HASH64) || defined(WIN64)
+ b = HASH_CONST[HASH_NR];
+ c = 0x9e3779b97f4a7c13LL; /* arbitrary value */
+ while (n >= 3)
+ { a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ n -= 3;
+ k += 3;
+ }
+ c += (((uint64_t) nbytes)<<3);
+ switch (n) {
+ case 2: b += k[1];
+ case 1: a += k[0];
+ case 0: break;
+ }
+ mix(a,b,c);
+#else
+ a = c = 0xdeadbeef + (n<<2);
+ b = HASH_CONST[HASH_NR];
+ while (n > 3)
+ { a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ n -= 3;
+ k += 3;
+ }
+ switch (n) {
+ case 3: c += k[2];
+ case 2: b += k[1];
+ case 1: a += k[0];
+ case 0: break;
+ }
+ final(a,b,c);
+#endif
+ j1 = c&nmask; j3 = a&7; /* 1st bit */
+ j2 = b&nmask; j4 = (a>>3)&7; /* 2nd bit */
+ K1 = c; K2 = b;
+}
+
+void
+s_hash(uchar *cp, int om)
+{
+#if defined(SFH)
+ d_sfh((const char *) cp, om); /* sets K1 */
+#else
+ d_hash(cp, om); /* sets K1 etc */
+#endif
+#ifdef BITSTATE
+ if (S_Tab == H_tab)
+ j1 = K1 % omaxdepth;
+ else
+#endif
+ if (ssize < 8*WS)
+ j1 = K1&mask;
+ else
+ j1 = K1;
+}
+#ifndef RANDSTOR
+int *prerand;
+void
+inirand(void)
+{ int i;
+ srand(123); /* fixed startpoint */
+ prerand = (int *) emalloc((omaxdepth+3)*sizeof(int));
+ for (i = 0; i < omaxdepth+3; i++)
+ prerand[i] = rand();
+}
+int
+pan_rand(void)
+{ if (!prerand) inirand();
+ return prerand[depth];
+}
+#endif
+
+void
+set_masks(void) /* 4.2.5 */
+{
+ if (WS == 4 && ssize >= 32)
+ { mask = 0xffffffff;
+#ifdef BITSTATE
+ switch (ssize) {
+ case 34: nmask = (mask>>1); break;
+ case 33: nmask = (mask>>2); break;
+ default: nmask = (mask>>3); break;
+ }
+#else
+ nmask = mask;
+#endif
+ } else if (WS == 8)
+ { mask = ((ONE_L<<ssize)-1); /* hash init */
+#ifdef BITSTATE
+ nmask = mask>>3;
+#else
+ nmask = mask;
+#endif
+ } else if (WS != 4)
+ { fprintf(stderr, "pan: wordsize %ld not supported\n", (long int) WS);
+ exit(1);
+ } else /* WS == 4 and ssize < 32 */
+ { mask = ((ONE_L<<ssize)-1); /* hash init */
+ nmask = (mask>>3);
+ }
+}
+
+static long reclaim_size;
+static char *reclaim_mem;
+#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
+#if NCORE>1
+ #error cannot combine AUTO_RESIZE with NCORE>1 yet
+#endif
+static struct H_el **N_tab;
+void
+reverse_capture(struct H_el *p)
+{ if (!p) return;
+ reverse_capture(p->nxt);
+ /* last element of list moves first */
+ /* to preserve list-order */
+ j2 = p->m_K1;
+ if (ssize < 8*WS) /* probably always true */
+ { j2 &= mask;
+ }
+ p->nxt = N_tab[j2];
+ N_tab[j2] = p;
+}
+void
+resize_hashtable(void)
+{
+ if (WS == 4 && ssize >= 27 - 1)
+ { return; /* canot increase further */
+ }
+
+ ssize += 2; /* 4x size */
+
+ printf("pan: resizing hashtable to -w%d.. ", ssize);
+
+ N_tab = (struct H_el **)
+ emalloc((ONE_L<<ssize)*sizeof(struct H_el *));
+
+ set_masks(); /* they changed */
+
+ for (j1 = 0; j1 < (ONE_L << (ssize - 2)); j1++)
+ { reverse_capture(H_tab[j1]);
+ }
+ reclaim_mem = (char *) H_tab;
+ reclaim_size = (ONE_L << (ssize - 2));
+ H_tab = N_tab;
+
+ printf(" done\n");
+}
+#endif
+#if defined(ZAPH) && defined(BITSTATE)
+void
+zap_hashtable(void)
+{ cpu_printf("pan: resetting hashtable\n");
+ if (udmem)
+ { memset(SS, 0, udmem);
+ } else
+ { memset(SS, 0, ONE_L<<(ssize-3));
+ }
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{ void to_compile(void);
+
+ efd = stderr; /* default */
+#ifdef BITSTATE
+ bstore = bstore_reg; /* default */
+#endif
+#if NCORE>1
+ { int i, j;
+ strcpy(o_cmdline, "");
+ for (j = 1; j < argc; j++)
+ { strcat(o_cmdline, argv[j]);
+ strcat(o_cmdline, " ");
+ }
+ /* printf("Command Line: %s\n", o_cmdline); */
+ if (strlen(o_cmdline) >= sizeof(o_cmdline))
+ { Uerror("option list too long");
+ } }
+#endif
+ while (argc > 1 && argv[1][0] == '-')
+ { switch (argv[1][1]) {
+#ifndef SAFETY
+#ifdef NP
+ case 'a': fprintf(efd, "error: -a disabled");
+ usage(efd); break;
+#else
+ case 'a': a_cycles = 1; break;
+#endif
+#endif
+ case 'A': noasserts = 1; break;
+ case 'b': bounded = 1; break;
+#ifdef HAS_CODE
+ case 'C': coltrace = 1; goto samething;
+#endif
+ case 'c': upto = atoi(&argv[1][2]); break;
+ case 'd': state_tables++; break;
+ case 'e': every_error = 1; Nr_Trails = 1; break;
+ case 'E': noends = 1; break;
+#ifdef SC
+ case 'F': if (strlen(argv[1]) > 2)
+ stackfile = &argv[1][2];
+ break;
+#endif
+#if !defined(SAFETY) && !defined(NOFAIR)
+ case 'f': fairness = 1; break;
+#endif
+#ifdef HAS_CODE
+ case 'g': gui = 1; goto samething;
+#endif
+ case 'h': if (!argv[1][2]) usage(efd); else
+ HASH_NR = atoi(&argv[1][2])%33; break;
+ case 'I': iterative = 2; every_error = 1; break;
+ case 'i': iterative = 1; every_error = 1; break;
+ case 'J': like_java = 1; break; /* Klaus Havelund */
+#ifdef BITSTATE
+ case 'k': hfns = atoi(&argv[1][2]); break;
+#endif
+#ifdef SCHED
+ case 'L': sched_max = atoi(&argv[1][2]); break;
+#endif
+#ifndef SAFETY
+#ifdef NP
+ case 'l': a_cycles = 1; break;
+#else
+ case 'l': fprintf(efd, "error: -l disabled");
+ usage(efd); break;
+#endif
+#endif
+#ifdef BITSTATE
+ case 'M': udmem = atoi(&argv[1][2]); break;
+ case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break;
+#else
+ case 'M': case 'G':
+ fprintf(stderr, "-M and -G affect only -DBITSTATE\n");
+ break;
+#endif
+ case 'm': maxdepth = atoi(&argv[1][2]); break;
+ case 'n': no_rck = 1; break;
+ case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]);
+ if (argv[2][0] != '-') /* check next arg */
+ { trailfilename = argv[2];
+ argc--; argv++; /* skip next arg */
+ }
+ break;
+#ifdef SVDUMP
+ case 'p': vprefix = atoi(&argv[1][2]); break;
+#endif
+#if NCORE==1
+ case 'Q': quota = (double) 60.0 * (double) atoi(&argv[1][2]); break;
+#endif
+ case 'q': strict = 1; break;
+ case 'R': Nrun = atoi(&argv[1][2]); break;
+#ifdef HAS_CODE
+ case 'r':
+samething: readtrail = 1;
+ if (isdigit(argv[1][2]))
+ whichtrail = atoi(&argv[1][2]);
+ else if (argc > 2 && argv[2][0] != '-') /* check next arg */
+ { trailfilename = argv[2];
+ argc--; argv++; /* skip next arg */
+ }
+ break;
+ case 'S': silent = 1; goto samething;
+#endif
+#ifdef BITSTATE
+ case 's': hfns = 1; break;
+#endif
+ case 'T': TMODE = 0444; break;
+ case 't': if (argv[1][2]) tprefix = &argv[1][2]; break;
+ case 'V': start_timer(); printf("Generated by %s\n", SpinVersion);
+ to_compile(); pan_exit(2); break;
+ case 'v': verbose++; break;
+ case 'w': ssize = atoi(&argv[1][2]); break;
+ case 'Y': signoff = 1; break;
+ case 'X': efd = stdout; break;
+ case 'x': exclusive = 1; break;
+#if NCORE>1
+ /* -B ip is passthru to proxy of remote ip address: */
+ case 'B': argc--; argv++; break;
+ case 'Q': worker_pids[0] = atoi(&argv[1][2]); break;
+ /* -Un means that the nth worker should be instantiated as a proxy */
+ case 'U': proxy_pid = atoi(&argv[1][2]); break;
+ /* -W means that this copy is started by a cluster-server as a remote */
+ /* this flag is passed to ./pan_proxy, which interprets it */
+ case 'W': remote_party++; break;
+ case 'Z': core_id = atoi(&argv[1][2]);
+ if (verbose)
+ { printf("cpu%d: pid %d parent %d\n",
+ core_id, getpid(), worker_pids[0]);
+ }
+ break;
+ case 'z': z_handoff = atoi(&argv[1][2]); break;
+#else
+ case 'z': break; /* ignored for single-core */
+#endif
+ default : fprintf(efd, "saw option -%c\n", argv[1][1]); usage(efd); break;
+ }
+ argc--; argv++;
+ }
+ if (iterative && TMODE != 0666)
+ { TMODE = 0666;
+ fprintf(efd, "warning: -T ignored when -i or -I is used\n");
+ }
+#if defined(HASH32) && !defined(SFH)
+ if (WS > 4)
+ { fprintf(efd, "strong warning: compiling -DHASH32 on a 64-bit machine\n");
+ fprintf(efd, " without -DSFH can slow down performance a lot\n");
+ }
+#endif
+#if defined(WIN32) || defined(WIN64)
+ if (TMODE == 0666)
+ TMODE = _S_IWRITE | _S_IREAD;
+ else
+ TMODE = _S_IREAD;
+#endif
+#if NCORE>1
+ store_proxy_pid = proxy_pid; /* for checks in mem_file() and someone_crashed() */
+ if (core_id != 0) { proxy_pid = 0; }
+ #ifndef SEP_STATE
+ if (core_id == 0 && a_cycles)
+ { fprintf(efd, "hint: this search may be more efficient ");
+ fprintf(efd, "if pan.c is compiled -DSEP_STATE\n");
+ }
+ #endif
+ if (z_handoff < 0)
+ { z_handoff = 20; /* conservative default - for non-liveness checks */
+ }
+#if defined(NGQ) || defined(LWQ_FIXED)
+ LWQ_SIZE = (double) (128.*1048576.);
+#else
+ LWQ_SIZE = (double) ( z_handoff + 2.) * (double) sizeof(SM_frame);
+#endif
+ #if NCORE>2
+ if (a_cycles)
+ { fprintf(efd, "warning: the intended nr of cores to be used in liveness mode is 2\n");
+ #ifndef SEP_STATE
+ fprintf(efd, "warning: without -DSEP_STATE there is no guarantee that all liveness violations are found\n");
+ #endif
+ }
+ #endif
+ #ifdef HAS_HIDDEN
+ #error cannot use hidden variables when compiling multi-core
+ #endif
+#endif
+#ifdef BITSTATE
+ if (hfns <= 0)
+ { hfns = 1;
+ fprintf(efd, "warning: using -k%d as minimal usable value\n", hfns);
+ }
+#endif
+ omaxdepth = maxdepth;
+#ifdef BITSTATE
+ if (WS == 4 && ssize > 34)
+ { ssize = 34;
+ fprintf(efd, "warning: using -w%d as max usable value\n", ssize);
+/*
+ * -w35 would not work: 35-3 = 32 but 1^31 is the largest
+ * power of 2 that can be represented in an unsigned long
+ */
+ }
+#else
+ if (WS == 4 && ssize > 27)
+ { ssize = 27;
+ fprintf(efd, "warning: using -w%d as max usable value\n", ssize);
+/*
+ * for emalloc, the lookup table size multiplies by 4 for the pointers
+ * the largest power of 2 that can be represented in a ulong is 1^31
+ * hence the largest number of lookup table slots is 31-4 = 27
+ */
+ }
+#endif
+#ifdef SC
+ hiwater = HHH = maxdepth-10;
+ DDD = HHH/2;
+ if (!stackfile)
+ { stackfile = (char *) emalloc(strlen(PanSource)+4+1);
+ sprintf(stackfile, "%s._s_", PanSource);
+ }
+ if (iterative)
+ { fprintf(efd, "error: cannot use -i or -I with -DSC\n");
+ pan_exit(1);
+ }
+#endif
+#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA)
+ #warning -DR_XPT and -DW_XPT assume -DMA (ignored)
+#endif
+ if (iterative && a_cycles)
+ fprintf(efd, "warning: -i or -I work for safety properties only\n");
+#ifdef BFS
+ #ifdef SC
+ #error -DBFS not compatible with -DSC
+ #endif
+ #ifdef HAS_LAST
+ #error -DBFS not compatible with _last
+ #endif
+ #ifdef HAS_STACK
+ #error cannot use c_track UnMatched with BFS
+ #endif
+ #ifdef REACH
+ #warning -DREACH is redundant when -DBFS is used
+ #endif
+#endif
+#if defined(MERGED) && defined(PEG)
+ #error to use -DPEG use: spin -o3 -a
+#endif
+#ifdef HC
+ #ifdef SFH
+ #error cannot combine -DHC and -DSFH
+ /* use of NOCOMP is the real reason */
+ #else
+ #ifdef NOCOMP
+ #error cannot combine -DHC and -DNOCOMP
+ #endif
+ #endif
+ #ifdef BITSTATE
+ #error cannot combine -DHC and -DBITSTATE
+ #endif
+#endif
+#if defined(SAFETY) && defined(NP)
+ #error cannot combine -DNP and -DBFS or -DSAFETY
+#endif
+#ifdef MA
+ #ifdef BITSTATE
+ #error cannot combine -DMA and -DBITSTATE
+ #endif
+ #if MA <= 0
+ #error usage: -DMA=N with N > 0 and N < VECTORSZ
+ #endif
+#endif
+#ifdef COLLAPSE
+ #ifdef BITSTATE
+ #error cannot combine -DBITSTATE and -DCOLLAPSE
+ #endif
+ #ifdef SFH
+ #error cannot combine -DCOLLAPSE and -DSFH
+ /* use of NOCOMP is the real reason */
+ #else
+ #ifdef NOCOMP
+ #error cannot combine -DCOLLAPSE and -DNOCOMP
+ #endif
+ #endif
+#endif
+ if (maxdepth <= 0 || ssize <= 1) usage(efd);
+#if SYNC>0 && !defined(NOREDUCE)
+ if (a_cycles && fairness)
+ { fprintf(efd, "error: p.o. reduction not compatible with ");
+ fprintf(efd, "fairness (-f) in models\n");
+ fprintf(efd, " with rendezvous operations: ");
+ fprintf(efd, "recompile with -DNOREDUCE\n");
+ pan_exit(1);
+ }
+#endif
+#if defined(REM_VARS) && !defined(NOREDUCE)
+ #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE)
+#endif
+#if defined(NOCOMP) && !defined(BITSTATE)
+ if (a_cycles)
+ { fprintf(efd, "error: use of -DNOCOMP voids -l and -a\n");
+ pan_exit(1);
+ }
+#endif
+#ifdef MEMLIM
+ memlim = ((double) MEMLIM) * (double) (1<<20); /* size in Mbyte */
+#endif
+#ifndef BITSTATE
+ if (Nrun > 1) HASH_NR = Nrun - 1;
+#endif
+ if (Nrun < 1 || Nrun > 32)
+ { fprintf(efd, "error: invalid arg for -R\n");
+ usage(efd);
+ }
+#ifndef SAFETY
+ if (fairness && !a_cycles)
+ { fprintf(efd, "error: -f requires -a or -l\n");
+ usage(efd);
+ }
+ #if ACCEPT_LAB==0
+ if (a_cycles)
+ { fprintf(efd, "error: no accept labels defined ");
+ fprintf(efd, "in model (for option -a)\n");
+ usage(efd);
+ }
+ #endif
+#endif
+#ifndef NOREDUCE
+ #ifdef HAS_ENABLED
+ #error use of enabled() requires -DNOREDUCE
+ #endif
+ #ifdef HAS_PCVALUE
+ #error use of pcvalue() requires -DNOREDUCE
+ #endif
+ #ifdef HAS_BADELSE
+ #error use of 'else' combined with i/o stmnts requires -DNOREDUCE
+ #endif
+ #ifdef HAS_LAST
+ #error use of _last requires -DNOREDUCE
+ #endif
+#endif
+#if SYNC>0 && !defined(NOREDUCE)
+ #ifdef HAS_UNLESS
+ fprintf(efd, "warning: use of a rendezvous stmnts in the escape\n");
+ fprintf(efd, " of an unless clause, if present, could make p.o. reduction\n");
+ fprintf(efd, " invalid (use -DNOREDUCE to avoid this)\n");
+ #ifdef BFS
+ fprintf(efd, " (this type of rv is also not compatible with -DBFS)\n");
+ #endif
+ #endif
+#endif
+#if SYNC>0 && defined(BFS)
+ #warning use of rendezvous with BFS does not preserve all invalid endstates
+#endif
+#if !defined(REACH) && !defined(BITSTATE)
+ if (iterative != 0 && a_cycles == 0)
+ { fprintf(efd, "warning: -i and -I need -DREACH to work accurately\n");
+ }
+#endif
+#if defined(BITSTATE) && defined(REACH)
+ #warning -DREACH is voided by -DBITSTATE
+#endif
+#if defined(MA) && defined(REACH)
+ #warning -DREACH is voided by -DMA
+#endif
+#if defined(FULLSTACK) && defined(CNTRSTACK)
+ #error cannot combine -DFULLSTACK and -DCNTRSTACK
+#endif
+#if defined(VERI)
+ #if ACCEPT_LAB>0
+ #ifndef BFS
+ if (!a_cycles
+ #ifdef HAS_CODE
+ && !readtrail
+ #endif
+ #if NCORE>1
+ && core_id == 0
+ #endif
+ && !state_tables)
+ { fprintf(efd, "warning: never claim + accept labels ");
+ fprintf(efd, "requires -a flag to fully verify\n");
+ }
+ #else
+ if (!state_tables
+ #ifdef HAS_CODE
+ && !readtrail
+ #endif
+ )
+ { fprintf(efd, "warning: verification in BFS mode ");
+ fprintf(efd, "is restricted to safety properties\n");
+ }
+ #endif
+ #endif
+#endif
+#ifndef SAFETY
+ if (!a_cycles
+ #ifdef HAS_CODE
+ && !readtrail
+ #endif
+ #if NCORE>1
+ && core_id == 0
+ #endif
+ && !state_tables)
+ { fprintf(efd, "hint: this search is more efficient ");
+ fprintf(efd, "if pan.c is compiled -DSAFETY\n");
+ }
+ #ifndef NOCOMP
+ if (!a_cycles)
+ { S_A = 0;
+ } else
+ { if (!fairness)
+ { S_A = 1; /* _a_t */
+ #ifndef NOFAIR
+ } else /* _a_t and _cnt[NFAIR] */
+ { S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2;
+ /* -2 because first two uchars in now are masked */
+ #endif
+ } }
+ #endif
+#endif
+ signal(SIGINT, stopped);
+ set_masks();
+#ifdef BFS
+ trail = (Trail *) emalloc(6*sizeof(Trail));
+ trail += 3;
+#else
+ trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail));
+ trail++; /* protect trpt-1 refs at depth 0 */
+#endif
+#ifdef SVDUMP
+ if (vprefix > 0)
+ { char nm[64];
+ sprintf(nm, "%s.svd", PanSource);
+ if ((svfd = creat(nm, TMODE)) < 0)
+ { fprintf(efd, "couldn't create %s\n", nm);
+ vprefix = 0;
+ } }
+#endif
+#ifdef RANDSTOR
+ srand(123);
+#endif
+#if SYNC>0 && ASYNC==0
+ set_recvs();
+#endif
+ run();
+ done = 1;
+ wrapup();
+ return 0;
+}
+
+void
+usage(FILE *fd)
+{
+ fprintf(fd, "%s\n", SpinVersion);
+ fprintf(fd, "Valid Options are:\n");
+#ifndef SAFETY
+#ifdef NP
+ fprintf(fd, " -a -> is disabled by -DNP ");
+ fprintf(fd, "(-DNP compiles for -l only)\n");
+#else
+ fprintf(fd, " -a find acceptance cycles\n");
+#endif
+#else
+ fprintf(fd, " -a,-l,-f -> are disabled by -DSAFETY\n");
+#endif
+ fprintf(fd, " -A ignore assert() violations\n");
+ fprintf(fd, " -b consider it an error to exceed the depth-limit\n");
+ fprintf(fd, " -cN stop at Nth error ");
+ fprintf(fd, "(defaults to -c1)\n");
+ fprintf(fd, " -d print state tables and stop\n");
+ fprintf(fd, " -e create trails for all errors\n");
+ fprintf(fd, " -E ignore invalid end states\n");
+#ifdef SC
+ fprintf(fd, " -Ffile use 'file' to store disk-stack\n");
+#endif
+#ifndef NOFAIR
+ fprintf(fd, " -f add weak fairness (to -a or -l)\n");
+#endif
+ fprintf(fd, " -hN use different hash-seed N:1..32\n");
+ fprintf(fd, " -i search for shortest path to error\n");
+ fprintf(fd, " -I like -i, but approximate and faster\n");
+ fprintf(fd, " -J reverse eval order of nested unlesses\n");
+#ifdef BITSTATE
+ fprintf(fd, " -kN set N bits per state (defaults to 3)\n");
+#endif
+#ifdef SCHED
+ fprintf(fd, " -LN set scheduling restriction to N (default 10)\n");
+#endif
+#ifndef SAFETY
+#ifdef NP
+ fprintf(fd, " -l find non-progress cycles\n");
+#else
+ fprintf(fd, " -l find non-progress cycles -> ");
+ fprintf(fd, "disabled, requires ");
+ fprintf(fd, "compilation with -DNP\n");
+#endif
+#endif
+#ifdef BITSTATE
+ fprintf(fd, " -MN use N Megabytes for bitstate hash array\n");
+ fprintf(fd, " -GN use N Gigabytes for bitstate hash array\n");
+#endif
+ fprintf(fd, " -mN max depth N steps (default=10k)\n");
+ fprintf(fd, " -n no listing of unreached states\n");
+#ifdef SVDUMP
+ fprintf(fd, " -pN create svfile (save N bytes per state)\n");
+#endif
+ fprintf(fd, " -QN set time-limit on execution of N minutes\n");
+ fprintf(fd, " -q require empty chans in valid end states\n");
+#ifdef HAS_CODE
+ fprintf(fd, " -r read and execute trail - can add -v,-n,-PN,-g,-C\n");
+ fprintf(fd, " -rN read and execute N-th error trail\n");
+ fprintf(fd, " -C read and execute trail - columnated output (can add -v,-n)\n");
+ fprintf(fd, " -PN read and execute trail - restrict trail output to proc N\n");
+ fprintf(fd, " -g read and execute trail + msc gui support\n");
+ fprintf(fd, " -S silent replay: only user defined printfs show\n");
+#endif
+#ifdef BITSTATE
+ fprintf(fd, " -RN repeat run Nx with N ");
+ fprintf(fd, "[1..32] independent hash functions\n");
+ fprintf(fd, " -s same as -k1 (single bit per state)\n");
+#endif
+ fprintf(fd, " -T create trail files in read-only mode\n");
+ fprintf(fd, " -tsuf replace .trail with .suf on trailfiles\n");
+ fprintf(fd, " -V print SPIN version number\n");
+ fprintf(fd, " -v verbose -- filenames in unreached state listing\n");
+ fprintf(fd, " -wN hashtable of 2^N entries ");
+ fprintf(fd, "(defaults to -w%d)\n", ssize);
+ fprintf(fd, " -x do not overwrite an existing trail file\n");
+#if NCORE>1
+ fprintf(fd, " -zN handoff states below depth N to 2nd cpu (multi_core)\n");
+#endif
+#ifdef HAS_CODE
+ fprintf(fd, "\n options -r, -C, -PN, -g, and -S can optionally be followed by\n");
+ fprintf(fd, " a filename argument, as in '-r filename', naming the trailfile\n");
+#endif
+#if NCORE>1
+ multi_usage(fd);
+#endif
+ exit(1);
+}
+
+char *
+Malloc(unsigned long n)
+{ char *tmp;
+#ifdef MEMLIM
+ if (memcnt+ (double) n > memlim) goto err;
+#endif
+#if 1
+ tmp = (char *) malloc(n);
+ if (!tmp)
+#else
+ tmp = (char *) sbrk(n);
+ if (tmp == (char *) -ONE_L)
+#endif
+ {
+#ifdef MEMLIM
+err:
+#endif
+ printf("pan: out of memory\n");
+#ifdef MEMLIM
+ printf(" %g bytes used\n", memcnt);
+ printf(" %g bytes more needed\n", (double) n);
+ printf(" %g bytes limit\n",
+ memlim);
+#endif
+#ifdef COLLAPSE
+ printf("hint: to reduce memory, recompile with\n");
+#ifndef MA
+ printf(" -DMA=%d # better/slower compression, or\n", hmax);
+#endif
+ printf(" -DBITSTATE # supertrace, approximation\n");
+#else
+#ifndef BITSTATE
+ printf("hint: to reduce memory, recompile with\n");
+#ifndef HC
+ printf(" -DCOLLAPSE # good, fast compression, or\n");
+#ifndef MA
+ printf(" -DMA=%d # better/slower compression, or\n", hmax);
+#endif
+ printf(" -DHC # hash-compaction, approximation\n");
+#endif
+ printf(" -DBITSTATE # supertrace, approximation\n");
+#endif
+#endif
+#if NCORE>1
+ #ifdef FULL_TRAIL
+ printf(" omit -DFULL_TRAIL or use pan -c0 to reduce memory\n");
+ #endif
+ #ifdef SEP_STATE
+ printf("hint: to reduce memory, recompile without\n");
+ printf(" -DSEP_STATE # may be faster, but uses more memory\n");
+ #endif
+#endif
+ wrapup();
+ }
+ memcnt += (double) n;
+ return tmp;
+}
+
+#define CHUNK (100*VECTORSZ)
+
+char *
+emalloc(unsigned long n) /* never released or reallocated */
+{ char *tmp;
+ if (n == 0)
+ return (char *) NULL;
+ if (n&(sizeof(void *)-1)) /* for proper alignment */
+ n += sizeof(void *)-(n&(sizeof(void *)-1));
+ if ((unsigned long) left < n)
+ { grow = (n < CHUNK) ? CHUNK : n;
+ have = Malloc(grow);
+ fragment += (double) left;
+ left = grow;
+ }
+ tmp = have;
+ have += (long) n;
+ left -= (long) n;
+ memset(tmp, 0, n);
+ return tmp;
+}
+void
+Uerror(char *str)
+{ /* always fatal */
+ uerror(str);
+#if NCORE>1
+ sudden_stop("Uerror");
+#endif
+ wrapup();
+}
+
+#if defined(MA) && !defined(SAFETY)
+int
+Unwind(void)
+{ Trans *t; uchar ot, _m; int tt; short II;
+#ifdef VERBOSE
+ int i;
+#endif
+ uchar oat = now._a_t;
+ now._a_t &= ~(1|16|32);
+ memcpy((char *) &comp_now, (char *) &now, vsize);
+ now._a_t = oat;
+Up:
+#ifdef SC
+ trpt = getframe(depth);
+#endif
+#ifdef VERBOSE
+ printf("%d State: ", depth);
+ for (i = 0; i < vsize; i++) printf("%d%s,",
+ ((char *)&now)[i], Mask[i]?"*":"");
+ printf("\n");
+#endif
+#ifndef NOFAIR
+ if (trpt->o_pm&128) /* fairness alg */
+ { now._cnt[now._a_t&1] = trpt->bup.oval;
+ depth--;
+#ifdef SC
+ trpt = getframe(depth);
+#else
+ trpt--;
+#endif
+ goto Q999;
+ }
+#endif
+#ifdef HAS_LAST
+#ifdef VERI
+ { int d; Trail *trl;
+ now._last = 0;
+ for (d = 1; d < depth; d++)
+ { trl = getframe(depth-d); /* was trl = (trpt-d); */
+ if (trl->pr != 0)
+ { now._last = trl->pr - BASE;
+ break;
+ } } }
+#else
+ now._last = (depth<1)?0:(trpt-1)->pr;
+#endif
+#endif
+#ifdef EVENT_TRACE
+ now._event = trpt->o_event;
+#endif
+ if ((now._a_t&1) && depth <= A_depth)
+ { now._a_t &= ~(1|16|32);
+ if (fairness) now._a_t |= 2; /* ? */
+ A_depth = 0;
+ goto CameFromHere; /* checkcycles() */
+ }
+ t = trpt->o_t;
+ ot = trpt->o_ot; II = trpt->pr;
+ tt = trpt->o_tt; this = pptr(II);
+ _m = do_reverse(t, II, trpt->o_m);
+#ifdef VERBOSE
+ printf("%3d: proc %d ", depth, II);
+ printf("reverses %d, %d to %d,",
+ t->forw, tt, t->st);
+ printf(" %s [abit=%d,adepth=%d,",
+ t->tp, now._a_t, A_depth);
+ printf("tau=%d,%d] <unwind>\n",
+ trpt->tau, (trpt-1)->tau);
+#endif
+ depth--;
+#ifdef SC
+ trpt = getframe(depth);
+#else
+ trpt--;
+#endif
+ /* reached[ot][t->st] = 1; 3.4.13 */
+ ((P0 *)this)->_p = tt;
+#ifndef NOFAIR
+ if ((trpt->o_pm&32))
+ {
+#ifdef VERI
+ if (now._cnt[now._a_t&1] == 0)
+ now._cnt[now._a_t&1] = 1;
+#endif
+ now._cnt[now._a_t&1] += 1;
+ }
+Q999:
+ if (trpt->o_pm&8)
+ { now._a_t &= ~2;
+ now._cnt[now._a_t&1] = 0;
+ }
+ if (trpt->o_pm&16)
+ now._a_t |= 2;
+#endif
+CameFromHere:
+ if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0)
+ return depth;
+ if (depth > 0) goto Up;
+ return 0;
+}
+#endif
+static char unwinding;
+void
+uerror(char *str)
+{ static char laststr[256];
+ int is_cycle;
+
+ if (unwinding) return; /* 1.4.2 */
+ if (strncmp(str, laststr, 254))
+#if NCORE>1
+ cpu_printf("pan: %s (at depth %ld)\n", str,
+#else
+ printf("pan: %s (at depth %ld)\n", str,
+#endif
+#if NCORE>1
+ (nr_handoffs * z_handoff) +
+#endif
+ ((depthfound==-1)?depth:depthfound));
+ strncpy(laststr, str, 254);
+ errors++;
+#ifdef HAS_CODE
+ if (readtrail) { wrap_trail(); return; }
+#endif
+ is_cycle = (strstr(str, " cycle") != (char *) 0);
+ if (!is_cycle)
+ { depth++; trpt++;
+ }
+ if ((every_error != 0)
+ || errors == upto)
+ {
+#if defined(MA) && !defined(SAFETY)
+ if (is_cycle)
+ { int od = depth;
+ unwinding = 1;
+ depthfound = Unwind();
+ unwinding = 0;
+ depth = od;
+ }
+#endif
+#if NCORE>1
+ writing_trail = 1;
+#endif
+#ifdef BFS
+ if (depth > 1) trpt--;
+ nuerror(str);
+ if (depth > 1) trpt++;
+#else
+ putrail();
+#endif
+#if defined(MA) && !defined(SAFETY)
+ if (strstr(str, " cycle"))
+ { if (every_error)
+ printf("sorry: MA writes 1 trail max\n");
+ wrapup(); /* no recovery from unwind */
+ }
+#endif
+#if NCORE>1
+ if (search_terminated != NULL)
+ { *search_terminated |= 4; /* uerror */
+ }
+ writing_trail = 0;
+#endif
+ }
+ if (!is_cycle)
+ { depth--; trpt--; /* undo */
+ }
+#ifndef BFS
+ if (iterative != 0 && maxdepth > 0)
+ { maxdepth = (iterative == 1)?(depth-1):(depth/2);
+ warned = 1;
+ printf("pan: reducing search depth to %ld\n",
+ maxdepth);
+ } else
+#endif
+ if (errors >= upto && upto != 0)
+ {
+#if NCORE>1
+ sudden_stop("uerror");
+#endif
+ wrapup();
+ }
+ depthfound = -1;
+}
+
+int
+xrefsrc(int lno, S_F_MAP *mp, int M, int i)
+{ Trans *T; int j, retval=1;
+ for (T = trans[M][i]; T; T = T->nxt)
+ if (T && T->tp)
+ { if (strcmp(T->tp, ".(goto)") == 0
+ || strncmp(T->tp, "goto :", 6) == 0)
+ return 1; /* not reported */
+
+ printf("\tline %d", lno);
+ if (verbose)
+ for (j = 0; j < sizeof(mp); j++)
+ if (i >= mp[j].from && i <= mp[j].upto)
+ { printf(", \"%s\"", mp[j].fnm);
+ break;
+ }
+ printf(", state %d", i);
+ if (strcmp(T->tp, "") != 0)
+ { char *q;
+ q = transmognify(T->tp);
+ printf(", \"%s\"", q?q:"");
+ } else if (stopstate[M][i])
+ printf(", -end state-");
+ printf("\n");
+ retval = 0; /* reported */
+ }
+ return retval;
+}
+
+void
+r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp)
+{ int i, m=0;
+
+#ifdef VERI
+ if (M == VERI && !verbose) return;
+#endif
+ printf("unreached in proctype %s\n", procname[M]);
+ for (i = 1; i < N; i++)
+ if (which[i] == 0
+ && (mapstate[M][i] == 0
+ || which[mapstate[M][i]] == 0))
+ m += xrefsrc((int) src[i], mp, M, i);
+ else
+ m++;
+ printf(" (%d of %d states)\n", N-1-m, N-1);
+}
+#if NCORE>1 && !defined(SEP_STATE)
+static long rev_trail_cnt;
+
+#ifdef FULL_TRAIL
+void
+rev_trail(int fd, volatile Stack_Tree *st_tr)
+{ long j; char snap[64];
+
+ if (!st_tr)
+ { return;
+ }
+ rev_trail(fd, st_tr->prv);
+#ifdef VERBOSE
+ printf("%d (%d) LRT [%d,%d] -- %9u (root %9u)\n",
+ depth, rev_trail_cnt, st_tr->pr, st_tr->t_id, st_tr, stack_last[core_id]);
+#endif
+ if (st_tr->pr != 255)
+ { sprintf(snap, "%ld:%d:%d\n",
+ rev_trail_cnt++, st_tr->pr, st_tr->t_id);
+ j = strlen(snap);
+ if (write(fd, snap, j) != j)
+ { printf("pan: error writing trailfile\n");
+ close(fd);
+ wrapup();
+ return;
+ }
+ } else /* handoff point */
+ { if (a_cycles)
+ { write(fd, "-1:-1:-1\n", 9);
+ } }
+}
+#endif
+#endif
+
+void
+putrail(void)
+{ int fd;
+#if defined VERI || defined(MERGED)
+ char snap[64];
+#endif
+#if NCORE==1 || defined(SEP_STATE) || !defined(FULL_TRAIL)
+ long i, j;
+ Trail *trl;
+#endif
+ fd = make_trail();
+ if (fd < 0) return;
+#ifdef VERI
+ sprintf(snap, "-2:%d:-2\n", VERI);
+ write(fd, snap, strlen(snap));
+#endif
+#ifdef MERGED
+ sprintf(snap, "-4:-4:-4\n");
+ write(fd, snap, strlen(snap));
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && defined(FULL_TRAIL)
+ rev_trail_cnt = 1;
+ enter_critical(GLOBAL_LOCK);
+ rev_trail(fd, stack_last[core_id]);
+ leave_critical(GLOBAL_LOCK);
+#else
+ i = 1; /* trail starts at position 1 */
+ #if NCORE>1 && defined(SEP_STATE)
+ if (cur_Root.m_vsize > 0) { i++; depth++; }
+ #endif
+ for ( ; i <= depth; i++)
+ { if (i == depthfound+1)
+ write(fd, "-1:-1:-1\n", 9);
+ trl = getframe(i);
+ if (!trl->o_t) continue;
+ if (trl->o_pm&128) continue;
+ sprintf(snap, "%ld:%d:%d\n",
+ i, trl->pr, trl->o_t->t_id);
+ j = strlen(snap);
+ if (write(fd, snap, j) != j)
+ { printf("pan: error writing trailfile\n");
+ close(fd);
+ wrapup();
+ } }
+#endif
+ close(fd);
+#if NCORE>1
+ cpu_printf("pan: wrote trailfile\n");
+#endif
+}
+
+void
+sv_save(void) /* push state vector onto save stack */
+{ if (!svtack->nxt)
+ { svtack->nxt = (Svtack *) emalloc(sizeof(Svtack));
+ svtack->nxt->body = emalloc(vsize*sizeof(char));
+ svtack->nxt->lst = svtack;
+ svtack->nxt->m_delta = vsize;
+ svmax++;
+ } else if (vsize > svtack->nxt->m_delta)
+ { svtack->nxt->body = emalloc(vsize*sizeof(char));
+ svtack->nxt->lst = svtack;
+ svtack->nxt->m_delta = vsize;
+ svmax++;
+ }
+ svtack = svtack->nxt;
+#if SYNC
+ svtack->o_boq = boq;
+#endif
+ svtack->o_delta = vsize; /* don't compress */
+ memcpy((char *)(svtack->body), (char *) &now, vsize);
+#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)
+ c_stack((uchar *) &(svtack->c_stack[0]));
+#endif
+#ifdef DEBUG
+ cpu_printf("%d: sv_save\n", depth);
+#endif
+}
+
+void
+sv_restor(void) /* pop state vector from save stack */
+{
+ memcpy((char *)&now, svtack->body, svtack->o_delta);
+#if SYNC
+ boq = svtack->o_boq;
+#endif
+#if defined(C_States) && (HAS_TRACK==1)
+#ifdef HAS_STACK
+ c_unstack((uchar *) &(svtack->c_stack[0]));
+#endif
+ c_revert((uchar *) &(now.c_state[0]));
+#endif
+ if (vsize != svtack->o_delta)
+ Uerror("sv_restor");
+ if (!svtack->lst)
+ Uerror("error: v_restor");
+ svtack = svtack->lst;
+#ifdef DEBUG
+ cpu_printf(" sv_restor\n");
+#endif
+}
+
+void
+p_restor(int h)
+{ int i; char *z = (char *) &now;
+
+ proc_offset[h] = stack->o_offset;
+ proc_skip[h] = (uchar) stack->o_skip;
+#ifndef XUSAFE
+ p_name[h] = stack->o_name;
+#endif
+#ifndef NOCOMP
+ for (i = vsize + stack->o_skip; i > vsize; i--)
+ Mask[i-1] = 1; /* align */
+#endif
+ vsize += stack->o_skip;
+ memcpy(z+vsize, stack->body, stack->o_delta);
+ vsize += stack->o_delta;
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+#ifndef NOCOMP
+ for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++)
+ Mask[vsize - i] = 1; /* pad */
+ Mask[proc_offset[h]] = 1; /* _pid */
+#endif
+ if (BASE > 0 && h > 0)
+ ((P0 *)pptr(h))->_pid = h-BASE;
+ else
+ ((P0 *)pptr(h))->_pid = h;
+ i = stack->o_delqs;
+ now._nr_pr += 1;
+ if (!stack->lst) /* debugging */
+ Uerror("error: p_restor");
+ stack = stack->lst;
+ this = pptr(h);
+ while (i-- > 0)
+ q_restor();
+}
+
+void
+q_restor(void)
+{ char *z = (char *) &now;
+#ifndef NOCOMP
+ int k, k_end;
+#endif
+ q_offset[now._nr_qs] = stack->o_offset;
+ q_skip[now._nr_qs] = (uchar) stack->o_skip;
+#ifndef XUSAFE
+ q_name[now._nr_qs] = stack->o_name;
+#endif
+ vsize += stack->o_skip;
+ memcpy(z+vsize, stack->body, stack->o_delta);
+ vsize += stack->o_delta;
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+ now._nr_qs += 1;
+#ifndef NOCOMP
+ k_end = stack->o_offset;
+ k = k_end - stack->o_skip;
+#if SYNC
+#ifndef BFS
+ if (q_zero(now._nr_qs)) k_end += stack->o_delta;
+#endif
+#endif
+ for ( ; k < k_end; k++)
+ Mask[k] = 1;
+#endif
+ if (!stack->lst) /* debugging */
+ Uerror("error: q_restor");
+ stack = stack->lst;
+}
+typedef struct IntChunks {
+ int *ptr;
+ struct IntChunks *nxt;
+} IntChunks;
+IntChunks *filled_chunks[512];
+IntChunks *empty_chunks[512];
+int *
+grab_ints(int nr)
+{ IntChunks *z;
+ if (nr >= 512) Uerror("cannot happen grab_int");
+ if (filled_chunks[nr])
+ { z = filled_chunks[nr];
+ filled_chunks[nr] = filled_chunks[nr]->nxt;
+ } else
+ { z = (IntChunks *) emalloc(sizeof(IntChunks));
+ z->ptr = (int *) emalloc(nr * sizeof(int));
+ }
+ z->nxt = empty_chunks[nr];
+ empty_chunks[nr] = z;
+ return z->ptr;
+}
+void
+ungrab_ints(int *p, int nr)
+{ IntChunks *z;
+ if (!empty_chunks[nr]) Uerror("cannot happen ungrab_int");
+ z = empty_chunks[nr];
+ empty_chunks[nr] = empty_chunks[nr]->nxt;
+ z->ptr = p;
+ z->nxt = filled_chunks[nr];
+ filled_chunks[nr] = z;
+}
+int
+delproc(int sav, int h)
+{ int d, i=0;
+#ifndef NOCOMP
+ int o_vsize = vsize;
+#endif
+ if (h+1 != (int) now._nr_pr) return 0;
+
+ while (now._nr_qs
+ && q_offset[now._nr_qs-1] > proc_offset[h])
+ { delq(sav);
+ i++;
+ }
+ d = vsize - proc_offset[h];
+ if (sav)
+ { if (!stack->nxt)
+ { stack->nxt = (Stack *)
+ emalloc(sizeof(Stack));
+ stack->nxt->body =
+ emalloc(Maxbody*sizeof(char));
+ stack->nxt->lst = stack;
+ smax++;
+ }
+ stack = stack->nxt;
+ stack->o_offset = proc_offset[h];
+#if VECTORSZ>32000
+ stack->o_skip = (int) proc_skip[h];
+#else
+ stack->o_skip = (short) proc_skip[h];
+#endif
+#ifndef XUSAFE
+ stack->o_name = p_name[h];
+#endif
+ stack->o_delta = d;
+ stack->o_delqs = i;
+ memcpy(stack->body, (char *)pptr(h), d);
+ }
+ vsize = proc_offset[h];
+ now._nr_pr = now._nr_pr - 1;
+ memset((char *)pptr(h), 0, d);
+ vsize -= (int) proc_skip[h];
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+#ifndef NOCOMP
+ for (i = vsize; i < o_vsize; i++)
+ Mask[i] = 0; /* reset */
+#endif
+ return 1;
+}
+
+void
+delq(int sav)
+{ int h = now._nr_qs - 1;
+ int d = vsize - q_offset[now._nr_qs - 1];
+#ifndef NOCOMP
+ int k, o_vsize = vsize;
+#endif
+ if (sav)
+ { if (!stack->nxt)
+ { stack->nxt = (Stack *)
+ emalloc(sizeof(Stack));
+ stack->nxt->body =
+ emalloc(Maxbody*sizeof(char));
+ stack->nxt->lst = stack;
+ smax++;
+ }
+ stack = stack->nxt;
+ stack->o_offset = q_offset[h];
+#if VECTORSZ>32000
+ stack->o_skip = (int) q_skip[h];
+#else
+ stack->o_skip = (short) q_skip[h];
+#endif
+#ifndef XUSAFE
+ stack->o_name = q_name[h];
+#endif
+ stack->o_delta = d;
+ memcpy(stack->body, (char *)qptr(h), d);
+ }
+ vsize = q_offset[h];
+ now._nr_qs = now._nr_qs - 1;
+ memset((char *)qptr(h), 0, d);
+ vsize -= (int) q_skip[h];
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+#ifndef NOCOMP
+ for (k = vsize; k < o_vsize; k++)
+ Mask[k] = 0; /* reset */
+#endif
+}
+
+int
+qs_empty(void)
+{ int i;
+ for (i = 0; i < (int) now._nr_qs; i++)
+ { if (q_sz(i) > 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+endstate(void)
+{ int i; P0 *ptr;
+ for (i = BASE; i < (int) now._nr_pr; i++)
+ { ptr = (P0 *) pptr(i);
+ if (!stopstate[ptr->_t][ptr->_p])
+ return 0;
+ }
+ if (strict) return qs_empty();
+#if defined(EVENT_TRACE) && !defined(OTIM)
+ if (!stopstate[EVENT_TRACE][now._event] && !a_cycles)
+ { printf("pan: event_trace not completed\n");
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+#ifndef SAFETY
+void
+checkcycles(void)
+{ uchar o_a_t = now._a_t;
+#ifdef SCHED
+ int o_limit;
+#endif
+#ifndef NOFAIR
+ uchar o_cnt = now._cnt[1];
+#endif
+#ifdef FULLSTACK
+#ifndef MA
+ struct H_el *sv = trpt->ostate; /* save */
+#else
+ uchar prov = trpt->proviso; /* save */
+#endif
+#endif
+#ifdef DEBUG
+ { int i; uchar *v = (uchar *) &now;
+ printf(" set Seed state ");
+#ifndef NOFAIR
+ if (fairness) printf("(cnt = %d:%d, nrpr=%d) ",
+ now._cnt[0], now._cnt[1], now._nr_pr);
+#endif
+ /* for (i = 0; i < n; i++) printf("%d,", v[i]); */
+ printf("\n");
+ }
+ printf("%d: cycle check starts\n", depth);
+#endif
+ now._a_t |= (1|16|32);
+ /* 1 = 2nd DFS; (16|32) to help hasher */
+#ifndef NOFAIR
+ now._cnt[1] = now._cnt[0];
+#endif
+ memcpy((char *)&A_Root, (char *)&now, vsize);
+ A_depth = depthfound = depth;
+#if NCORE>1
+ mem_put_acc();
+#else
+ #ifdef SCHED
+ o_limit = trpt->sched_limit;
+ trpt->sched_limit = 0;
+ #endif
+ new_state(); /* start 2nd DFS */
+ #ifdef SCHED
+ trpt->sched_limit = o_limit;
+ #endif
+#endif
+ now._a_t = o_a_t;
+#ifndef NOFAIR
+ now._cnt[1] = o_cnt;
+#endif
+ A_depth = 0; depthfound = -1;
+#ifdef DEBUG
+ printf("%d: cycle check returns\n", depth);
+#endif
+#ifdef FULLSTACK
+#ifndef MA
+ trpt->ostate = sv; /* restore */
+#else
+ trpt->proviso = prov;
+#endif
+#endif
+}
+#endif
+
+#if defined(FULLSTACK) && defined(BITSTATE)
+struct H_el *Free_list = (struct H_el *) 0;
+void
+onstack_init(void) /* to store stack states in a bitstate search */
+{ S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *));
+}
+struct H_el *
+grab_state(int n)
+{ struct H_el *v, *last = 0;
+ if (H_tab == S_Tab)
+ { for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt)
+ { if ((int) v->tagged == n)
+ { if (last)
+ last->nxt = v->nxt;
+ else
+gotcha: Free_list = v->nxt;
+ v->tagged = 0;
+ v->nxt = 0;
+#ifdef COLLAPSE
+ v->ln = 0;
+#endif
+ return v;
+ }
+ Fh++; last=v;
+ }
+ /* new: second try */
+ v = Free_list;
+ if (v && ((int) v->tagged >= n))
+ goto gotcha;
+ ngrabs++;
+ }
+ return (struct H_el *)
+ emalloc(sizeof(struct H_el)+n-sizeof(unsigned));
+}
+
+#else
+#if NCORE>1
+struct H_el *
+grab_state(int n)
+{ struct H_el *grab_shared(int);
+ return grab_shared(sizeof(struct H_el)+n-sizeof(unsigned));
+}
+#else
+ #ifndef AUTO_RESIZE
+ #define grab_state(n) (struct H_el *) \
+ emalloc(sizeof(struct H_el)+n-sizeof(unsigned long));
+ #else
+ struct H_el *
+ grab_state(int n)
+ { struct H_el *p;
+ int cnt = sizeof(struct H_el)+n-sizeof(unsigned long);
+
+ if (reclaim_size >= cnt+WS)
+ { if ((cnt & (WS-1)) != 0) /* alignment */
+ { cnt += WS - (cnt & (WS-1));
+ }
+ p = (struct H_el *) reclaim_mem;
+ reclaim_mem += cnt;
+ reclaim_size -= cnt;
+ memset(p, 0, cnt);
+ } else
+ { p = (struct H_el *) emalloc(cnt);
+ }
+ return p;
+ }
+ #endif
+#endif
+#endif
+#ifdef COLLAPSE
+unsigned long
+ordinal(char *v, long n, short tp)
+{ struct H_el *tmp, *ntmp; long m;
+ struct H_el *olst = (struct H_el *) 0;
+ s_hash((uchar *)v, n);
+#if NCORE>1 && !defined(SEP_STATE)
+ enter_critical(CS_ID); /* uses spinlock - 1..128 */
+#endif
+ tmp = H_tab[j1];
+ if (!tmp)
+ { tmp = grab_state(n);
+ H_tab[j1] = tmp;
+ } else
+ for ( ;; olst = tmp, tmp = tmp->nxt)
+ { m = memcmp(((char *)&(tmp->state)), v, n);
+ if (n == tmp->ln)
+ {
+ if (m == 0)
+ goto done;
+ if (m < 0)
+ {
+Insert: ntmp = grab_state(n);
+ ntmp->nxt = tmp;
+ if (!olst)
+ H_tab[j1] = ntmp;
+ else
+ olst->nxt = ntmp;
+ tmp = ntmp;
+ break;
+ } else if (!tmp->nxt)
+ {
+Append: tmp->nxt = grab_state(n);
+ tmp = tmp->nxt;
+ break;
+ }
+ continue;
+ }
+ if (n < tmp->ln)
+ goto Insert;
+ else if (!tmp->nxt)
+ goto Append;
+ }
+ m = ++ncomps[tp];
+#ifdef FULLSTACK
+ tmp->tagged = m;
+#else
+ tmp->st_id = m;
+#endif
+#if defined(AUTO_RESIZE) && !defined(BITSTATE)
+ tmp->m_K1 = K1;
+#endif
+ memcpy(((char *)&(tmp->state)), v, n);
+ tmp->ln = n;
+done:
+#if NCORE>1 && !defined(SEP_STATE)
+ leave_critical(CS_ID); /* uses spinlock */
+#endif
+#ifdef FULLSTACK
+ return tmp->tagged;
+#else
+ return tmp->st_id;
+#endif
+}
+
+int
+compress(char *vin, int nin) /* collapse compression */
+{ char *w, *v = (char *) &comp_now;
+ int i, j;
+ unsigned long n;
+ static char *x;
+ static uchar nbytes[513]; /* 1 + 256 + 256 */
+ static unsigned short nbytelen;
+ long col_q(int, char *);
+ long col_p(int, char *);
+#ifndef SAFETY
+ if (a_cycles)
+ *v++ = now._a_t;
+#ifndef NOFAIR
+ if (fairness)
+ for (i = 0; i < NFAIR; i++)
+ *v++ = now._cnt[i];
+#endif
+#endif
+ nbytelen = 0;
+#ifndef JOINPROCS
+ for (i = 0; i < (int) now._nr_pr; i++)
+ { n = col_p(i, (char *) 0);
+#ifdef NOFIX
+ nbytes[nbytelen] = 0;
+#else
+ nbytes[nbytelen] = 1;
+ *v++ = ((P0 *) pptr(i))->_t;
+#endif
+ *v++ = n&255;
+ if (n >= (1<<8))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>8)&255;
+ }
+ if (n >= (1<<16))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>16)&255;
+ }
+ if (n >= (1<<24))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>24)&255;
+ }
+ nbytelen++;
+ }
+#else
+ x = scratch;
+ for (i = 0; i < (int) now._nr_pr; i++)
+ x += col_p(i, x);
+ n = ordinal(scratch, x-scratch, 2); /* procs */
+ *v++ = n&255;
+ nbytes[nbytelen] = 0;
+ if (n >= (1<<8))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>8)&255;
+ }
+ if (n >= (1<<16))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>16)&255;
+ }
+ if (n >= (1<<24))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>24)&255;
+ }
+ nbytelen++;
+#endif
+#ifdef SEPQS
+ for (i = 0; i < (int) now._nr_qs; i++)
+ { n = col_q(i, (char *) 0);
+ nbytes[nbytelen] = 0;
+ *v++ = n&255;
+ if (n >= (1<<8))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>8)&255;
+ }
+ if (n >= (1<<16))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>16)&255;
+ }
+ if (n >= (1<<24))
+ { nbytes[nbytelen]++;
+ *v++ = (n>>24)&255;
+ }
+ nbytelen++;
+ }
+#endif
+#ifdef NOVSZ
+ /* 3 = _a_t, _nr_pr, _nr_qs */
+ w = (char *) &now + 3 * sizeof(uchar);
+#ifndef NOFAIR
+ w += NFAIR;
+#endif
+#else
+#if VECTORSZ<65536
+ w = (char *) &(now._vsz) + sizeof(unsigned short);
+#else
+ w = (char *) &(now._vsz) + sizeof(unsigned long);
+#endif
+#endif
+ x = scratch;
+ *x++ = now._nr_pr;
+ *x++ = now._nr_qs;
+ if (now._nr_qs > 0 && qptr(0) < pptr(0))
+ n = qptr(0) - (uchar *) w;
+ else
+ n = pptr(0) - (uchar *) w;
+ j = w - (char *) &now;
+ for (i = 0; i < (int) n; i++, w++)
+ if (!Mask[j++]) *x++ = *w;
+#ifndef SEPQS
+ for (i = 0; i < (int) now._nr_qs; i++)
+ x += col_q(i, x);
+#endif
+ x--;
+ for (i = 0, j = 6; i < nbytelen; i++)
+ { if (j == 6)
+ { j = 0;
+ *(++x) = 0;
+ } else
+ j += 2;
+ *x |= (nbytes[i] << j);
+ }
+ x++;
+ for (j = 0; j < WS-1; j++)
+ *x++ = 0;
+ x -= j; j = 0;
+ n = ordinal(scratch, x-scratch, 0); /* globals */
+ *v++ = n&255;
+ if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; }
+ if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; }
+ if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; }
+ *v++ = j; /* add last count as a byte */
+ for (i = 0; i < WS-1; i++)
+ *v++ = 0;
+ v -= i;
+#if 0
+ printf("collapse %d -> %d\n",
+ vsize, v - (char *)&comp_now);
+#endif
+ return v - (char *)&comp_now;
+}
+#else
+#if !defined(NOCOMP)
+int
+compress(char *vin, int n) /* default compression */
+{
+#ifdef HC
+ int delta = 0;
+ s_hash((uchar *)vin, n); /* sets K1 and K2 */
+#ifndef SAFETY
+ if (S_A)
+ { delta++; /* _a_t */
+#ifndef NOFAIR
+ if (S_A > NFAIR)
+ delta += NFAIR; /* _cnt[] */
+#endif
+ }
+#endif
+ memcpy((char *) &comp_now + delta, (char *) &K1, WS);
+ delta += WS;
+#if HC>0
+ memcpy((char *) &comp_now + delta, (char *) &K2, HC);
+ delta += HC;
+#endif
+ return delta;
+#else
+ char *vv = vin;
+ char *v = (char *) &comp_now;
+ int i;
+ #ifndef NO_FAST_C
+ int r = 0, unroll = n/8;
+ if (unroll > 0)
+ { i = 0;
+ while (r++ < unroll)
+ { /* unroll 8 times, avoid ifs */
+ /* 1 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 2 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 3 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 4 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 5 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 6 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 7 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ /* 8 */ *v = *vv++;
+ v += 1 - Mask[i++];
+ }
+ r = n - i; /* the rest, at most 7 */
+ switch (r) {
+ case 7: *v = *vv++; v += 1 - Mask[i++];
+ case 6: *v = *vv++; v += 1 - Mask[i++];
+ case 5: *v = *vv++; v += 1 - Mask[i++];
+ case 4: *v = *vv++; v += 1 - Mask[i++];
+ case 3: *v = *vv++; v += 1 - Mask[i++];
+ case 2: *v = *vv++; v += 1 - Mask[i++];
+ case 1: *v = *vv++; v += 1 - Mask[i++];
+ case 0: break;
+ }
+ r = (n+WS-1)/WS; /* words rounded up */
+ r *= WS; /* bytes */
+ i = r - i; /* remainder */
+ switch (i) {
+ case 7: *v++ = 0; /* fall thru */
+ case 6: *v++ = 0;
+ case 5: *v++ = 0;
+ case 4: *v++ = 0;
+ case 3: *v++ = 0;
+ case 2: *v++ = 0;
+ case 1: *v++ = 0;
+ case 0: break;
+ default: Uerror("unexpected wordsize");
+ }
+ v -= i;
+ } else
+ #endif
+ { for (i = 0; i < n; i++, vv++)
+ if (!Mask[i]) *v++ = *vv;
+ for (i = 0; i < WS-1; i++)
+ *v++ = 0;
+ v -= i;
+ }
+#if 0
+ printf("compress %d -> %d\n",
+ n, v - (char *)&comp_now);
+#endif
+ return v - (char *)&comp_now;
+#endif
+}
+#endif
+#endif
+#if defined(FULLSTACK) && defined(BITSTATE)
+#if defined(MA)
+#if !defined(onstack_now)
+int onstack_now(void) {}
+#endif
+#if !defined(onstack_put)
+void onstack_put(void) {}
+#endif
+#if !defined(onstack_zap)
+void onstack_zap(void) {}
+#endif
+#else
+void
+onstack_zap(void)
+{ struct H_el *v, *w, *last = 0;
+ struct H_el **tmp = H_tab;
+ char *nv; int n, m;
+
+ static char warned = 0;
+
+ H_tab = S_Tab;
+#ifndef NOCOMP
+ nv = (char *) &comp_now;
+ n = compress((char *)&now, vsize);
+#else
+#if defined(BITSTATE) && defined(LC)
+ nv = (char *) &comp_now;
+ n = compact_stack((char *)&now, vsize);
+#else
+ nv = (char *) &now;
+ n = vsize;
+#endif
+#endif
+#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
+ s_hash((uchar *)nv, n);
+#endif
+ H_tab = tmp;
+ for (v = S_Tab[j1]; v; Zh++, last=v, v=v->nxt)
+ { m = memcmp(&(v->state), nv, n);
+ if (m == 0)
+ goto Found;
+ if (m < 0)
+ break;
+ }
+/* NotFound: */
+#ifndef ZAPH
+ #if defined(BITSTATE) && NCORE>1
+ /* seen this happen, likely harmless, but not yet understood */
+ if (warned == 0)
+ #endif
+ { /* Uerror("stack out of wack - zap"); */
+ cpu_printf("pan: warning, stack incomplete\n");
+ warned = 1;
+ }
+#endif
+ return;
+Found:
+ ZAPS++;
+ if (last)
+ last->nxt = v->nxt;
+ else
+ S_Tab[j1] = v->nxt;
+ v->tagged = (unsigned) n;
+#if !defined(NOREDUCE) && !defined(SAFETY)
+ v->proviso = 0;
+#endif
+ v->nxt = last = (struct H_el *) 0;
+ for (w = Free_list; w; Fa++, last=w, w = w->nxt)
+ { if ((int) w->tagged <= n)
+ { if (last)
+ { v->nxt = w;
+ last->nxt = v;
+ } else
+ { v->nxt = Free_list;
+ Free_list = v;
+ }
+ return;
+ }
+ if (!w->nxt)
+ { w->nxt = v;
+ return;
+ } }
+ Free_list = v;
+}
+void
+onstack_put(void)
+{ struct H_el **tmp = H_tab;
+ H_tab = S_Tab;
+ if (hstore((char *)&now, vsize) != 0)
+#if defined(BITSTATE) && defined(LC)
+ printf("pan: warning, double stack entry\n");
+#else
+ #ifndef ZAPH
+ Uerror("cannot happen - unstack_put");
+ #endif
+#endif
+ H_tab = tmp;
+ trpt->ostate = Lstate;
+ PUT++;
+}
+int
+onstack_now(void)
+{ struct H_el *tmp;
+ struct H_el **tmp2 = H_tab;
+ char *v; int n, m = 1;
+
+ H_tab = S_Tab;
+#ifdef NOCOMP
+#if defined(BITSTATE) && defined(LC)
+ v = (char *) &comp_now;
+ n = compact_stack((char *)&now, vsize);
+#else
+ v = (char *) &now;
+ n = vsize;
+#endif
+#else
+ v = (char *) &comp_now;
+ n = compress((char *)&now, vsize);
+#endif
+#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
+ s_hash((uchar *)v, n);
+#endif
+ H_tab = tmp2;
+ for (tmp = S_Tab[j1]; tmp; Zn++, tmp = tmp->nxt)
+ { m = memcmp(((char *)&(tmp->state)),v,n);
+ if (m <= 0)
+ { Lstate = (struct H_el *) tmp;
+ break;
+ } }
+ PROBE++;
+ return (m == 0);
+}
+#endif
+#endif
+#ifndef BITSTATE
+void
+hinit(void)
+{
+ #ifdef MA
+#ifdef R_XPT
+ { void r_xpoint(void);
+ r_xpoint();
+ }
+#else
+ dfa_init((unsigned short) (MA+a_cycles));
+#if NCORE>1 && !defined(COLLAPSE)
+ if (!readtrail)
+ { void init_HT(unsigned long);
+ init_HT(0L);
+ }
+#endif
+#endif
+ #endif
+ #if !defined(MA) || defined(COLLAPSE)
+#if NCORE>1
+ if (!readtrail)
+ { void init_HT(unsigned long);
+ init_HT((unsigned long) (ONE_L<<ssize)*sizeof(struct H_el *));
+ } else
+#endif
+ H_tab = (struct H_el **)
+ emalloc((ONE_L<<ssize)*sizeof(struct H_el *));
+ #endif
+}
+#endif
+
+#if !defined(BITSTATE) || defined(FULLSTACK)
+#ifdef DEBUG
+void
+dumpstate(int wasnew, char *v, int n, int tag)
+{ int i;
+#ifndef SAFETY
+ if (S_A)
+ { printf(" state tags %d (%d::%d): ",
+ V_A, wasnew, v[0]);
+#ifdef FULLSTACK
+ printf(" %d ", tag);
+#endif
+ printf("\n");
+ }
+#endif
+#ifdef SDUMP
+#ifndef NOCOMP
+ printf(" State: ");
+ for (i = 0; i < vsize; i++) printf("%d%s,",
+ ((char *)&now)[i], Mask[i]?"*":"");
+#endif
+ printf("\n Vector: ");
+ for (i = 0; i < n; i++) printf("%d,", v[i]);
+ printf("\n");
+#endif
+}
+#endif
+#ifdef MA
+int
+gstore(char *vin, int nin, uchar pbit)
+{ int n, i;
+ int ret_val = 1;
+ uchar *v;
+ static uchar Info[MA+1];
+#ifndef NOCOMP
+ n = compress(vin, nin);
+ v = (uchar *) &comp_now;
+#else
+ n = nin;
+ v = vin;
+#endif
+ if (n >= MA)
+ { printf("pan: error, MA too small, recompile pan.c");
+ printf(" with -DMA=N with N>%d\n", n);
+ Uerror("aborting");
+ }
+ if (n > (int) maxgs)
+ { maxgs = (unsigned int) n;
+ }
+ for (i = 0; i < n; i++)
+ { Info[i] = v[i];
+ }
+ for ( ; i < MA-1; i++)
+ { Info[i] = 0;
+ }
+ Info[MA-1] = pbit;
+ if (a_cycles) /* place _a_t at the end */
+ { Info[MA] = Info[0];
+ Info[0] = 0;
+ }
+
+#if NCORE>1 && !defined(SEP_STATE)
+ enter_critical(GLOBAL_LOCK); /* crude, but necessary */
+ /* to make this mode work, also replace emalloc with grab_shared inside store MA routines */
+#endif
+
+ if (!dfa_store(Info))
+ { if (pbit == 0
+ && (now._a_t&1)
+ && depth > A_depth)
+ { Info[MA] &= ~(1|16|32); /* _a_t */
+ if (dfa_member(MA))
+ { Info[MA-1] = 4; /* off-stack bit */
+ nShadow++;
+ if (!dfa_member(MA-1))
+ { ret_val = 3;
+ #ifdef VERBOSE
+ printf("intersected 1st dfs stack\n");
+ #endif
+ goto done;
+ } } }
+ ret_val = 0;
+ #ifdef VERBOSE
+ printf("new state\n");
+ #endif
+ goto done;
+ }
+#ifdef FULLSTACK
+ if (pbit == 0)
+ { Info[MA-1] = 1; /* proviso bit */
+#ifndef BFS
+ trpt->proviso = dfa_member(MA-1);
+#endif
+ Info[MA-1] = 4; /* off-stack bit */
+ if (dfa_member(MA-1))
+ { ret_val = 1; /* off-stack */
+ #ifdef VERBOSE
+ printf("old state\n");
+ #endif
+ } else
+ { ret_val = 2; /* on-stack */
+ #ifdef VERBOSE
+ printf("on-stack\n");
+ #endif
+ }
+ goto done;
+ }
+#endif
+ ret_val = 1;
+#ifdef VERBOSE
+ printf("old state\n");
+#endif
+done:
+#if NCORE>1 && !defined(SEP_STATE)
+ leave_critical(GLOBAL_LOCK);
+#endif
+ return ret_val; /* old state */
+}
+#endif
+#if defined(BITSTATE) && defined(LC)
+int
+compact_stack(char *vin, int n)
+{ int delta = 0;
+ s_hash((uchar *)vin, n); /* sets K1 and K2 */
+#ifndef SAFETY
+ delta++; /* room for state[0] |= 128 */
+#endif
+ memcpy((char *) &comp_now + delta, (char *) &K1, WS);
+ delta += WS;
+ memcpy((char *) &comp_now + delta, (char *) &K2, WS);
+ delta += WS; /* use all available bits */
+ return delta;
+}
+#endif
+int
+hstore(char *vin, int nin) /* hash table storage */
+{ struct H_el *ntmp;
+ struct H_el *tmp, *olst = (struct H_el *) 0;
+ char *v; int n, m=0;
+#ifdef HC
+ uchar rem_a;
+#endif
+#ifdef NOCOMP
+#if defined(BITSTATE) && defined(LC)
+ if (S_Tab == H_tab)
+ { v = (char *) &comp_now;
+ n = compact_stack(vin, nin);
+ } else
+ { v = vin; n = nin;
+ }
+#else
+ v = vin; n = nin;
+#endif
+#else
+ v = (char *) &comp_now;
+ #ifdef HC
+ rem_a = now._a_t;
+ now._a_t = 0;
+ #endif
+ n = compress(vin, nin);
+ #ifdef HC
+ now._a_t = rem_a;
+ #endif
+#ifndef SAFETY
+ if (S_A)
+ { v[0] = 0; /* _a_t */
+#ifndef NOFAIR
+ if (S_A > NFAIR)
+ for (m = 0; m < NFAIR; m++)
+ v[m+1] = 0; /* _cnt[] */
+#endif
+ m = 0;
+ }
+ #endif
+#endif
+#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
+ s_hash((uchar *)v, n);
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ enter_critical(CS_ID); /* uses spinlock */
+#endif
+ tmp = H_tab[j1];
+ if (!tmp)
+ { tmp = grab_state(n);
+#if NCORE>1
+ if (!tmp)
+ { /* if we get here -- we've already issued a warning */
+ /* but we want to allow the normal distributed termination */
+ /* to collect the stats on all cpus in the wrapup */
+ #if !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+ #endif
+ return 1; /* allow normal termination */
+ }
+#endif
+ H_tab[j1] = tmp;
+ } else
+ { for (;; hcmp++, olst = tmp, tmp = tmp->nxt)
+ { /* skip the _a_t and the _cnt bytes */
+#ifdef COLLAPSE
+ if (tmp->ln != 0)
+ { if (!tmp->nxt) goto Append;
+ continue;
+ }
+#endif
+ m = memcmp(((char *)&(tmp->state)) + S_A,
+ v + S_A, n - S_A);
+ if (m == 0) {
+#ifdef SAFETY
+#define wasnew 0
+#else
+ int wasnew = 0;
+#endif
+#ifndef SAFETY
+#ifndef NOCOMP
+ if (S_A)
+ { if ((((char *)&(tmp->state))[0] & V_A) != V_A)
+ { wasnew = 1; nShadow++;
+ ((char *)&(tmp->state))[0] |= V_A;
+ }
+#ifndef NOFAIR
+ if (S_A > NFAIR)
+ { /* 0 <= now._cnt[now._a_t&1] < MAXPROC */
+ unsigned ci, bp; /* index, bit pos */
+ ci = (now._cnt[now._a_t&1] / 8);
+ bp = (now._cnt[now._a_t&1] - 8*ci);
+ if (now._a_t&1) /* use tail-bits in _cnt */
+ { ci = (NFAIR - 1) - ci;
+ bp = 7 - bp; /* bp = 0..7 */
+ }
+ ci++; /* skip over _a_t */
+ bp = 1 << bp; /* the bit mask */
+ if ((((char *)&(tmp->state))[ci] & bp)==0)
+ { if (!wasnew)
+ { wasnew = 1;
+ nShadow++;
+ }
+ ((char *)&(tmp->state))[ci] |= bp;
+ }
+ }
+ /* else: wasnew == 0, i.e., old state */
+#endif
+ }
+#endif
+#endif
+#if NCORE>1
+ Lstate = (struct H_el *) tmp;
+#endif
+#ifdef FULLSTACK
+#ifndef SAFETY
+ if (wasnew)
+ { Lstate = (struct H_el *) tmp;
+ tmp->tagged |= V_A;
+ if ((now._a_t&1)
+ && (tmp->tagged&A_V)
+ && depth > A_depth)
+ {
+intersect:
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf("1st dfs-stack intersected on state %d+\n",
+ (int) tmp->st_id);
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 3;
+ }
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf(" New state %d+\n", (int) tmp->st_id);
+#endif
+#ifdef DEBUG
+ dumpstate(1, (char *)&(tmp->state),n,tmp->tagged);
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 0;
+ } else
+#endif
+ if ((S_A)?(tmp->tagged&V_A):tmp->tagged)
+ { Lstate = (struct H_el *) tmp;
+#ifndef SAFETY
+ /* already on current dfs stack */
+ /* but may also be on 1st dfs stack */
+ if ((now._a_t&1)
+ && (tmp->tagged&A_V)
+ && depth > A_depth
+#ifndef NOFAIR
+ && (!fairness || now._cnt[1] <= 1)
+#endif
+ )
+ goto intersect;
+#endif
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf(" Stack state %d\n", (int) tmp->st_id);
+#endif
+#ifdef DEBUG
+ dumpstate(0, (char *)&(tmp->state),n,tmp->tagged);
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 2; /* match on stack */
+ }
+#else
+ if (wasnew)
+ {
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf(" New state %d+\n", (int) tmp->st_id);
+#endif
+#ifdef DEBUG
+ dumpstate(1, (char *)&(tmp->state), n, 0);
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 0;
+ }
+#endif
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf(" Old state %d\n", (int) tmp->st_id);
+#endif
+#ifdef DEBUG
+ dumpstate(0, (char *)&(tmp->state), n, 0);
+#endif
+#ifdef REACH
+ if (tmp->D > depth)
+ { tmp->D = depth;
+#ifdef CHECK
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+ printf(" ReVisiting (from smaller depth)\n");
+#endif
+ nstates--;
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 0;
+ }
+#endif
+#if (defined(BFS) && defined(Q_PROVISO)) || NCORE>1
+ Lstate = (struct H_el *) tmp;
+#endif
+#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+#endif
+ return 1; /* match outside stack */
+ } else if (m < 0)
+ { /* insert state before tmp */
+ ntmp = grab_state(n);
+#if NCORE>1
+ if (!ntmp)
+ {
+ #if !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+ #endif
+ return 1; /* allow normal termination */
+ }
+#endif
+ ntmp->nxt = tmp;
+ if (!olst)
+ H_tab[j1] = ntmp;
+ else
+ olst->nxt = ntmp;
+ tmp = ntmp;
+ break;
+ } else if (!tmp->nxt)
+ { /* append after tmp */
+#ifdef COLLAPSE
+Append:
+#endif
+ tmp->nxt = grab_state(n);
+#if NCORE>1
+ if (!tmp->nxt)
+ {
+ #if !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+ #endif
+ return 1; /* allow normal termination */
+ }
+#endif
+ tmp = tmp->nxt;
+ break;
+ } }
+ }
+#ifdef CHECK
+ tmp->st_id = (unsigned) nstates;
+#if NCORE>1
+ printf("cpu%d: ", core_id);
+#endif
+#ifdef BITSTATE
+ printf(" Push state %d\n", ((int) nstates) - 1);
+#else
+ printf(" New state %d\n", (int) nstates);
+#endif
+#endif
+#if !defined(SAFETY) || defined(REACH)
+ tmp->D = depth;
+#endif
+#ifndef SAFETY
+#ifndef NOCOMP
+ if (S_A)
+ { v[0] = V_A;
+#ifndef NOFAIR
+ if (S_A > NFAIR)
+ { unsigned ci, bp; /* as above */
+ ci = (now._cnt[now._a_t&1] / 8);
+ bp = (now._cnt[now._a_t&1] - 8*ci);
+ if (now._a_t&1)
+ { ci = (NFAIR - 1) - ci;
+ bp = 7 - bp; /* bp = 0..7 */
+ }
+ v[1+ci] = 1 << bp;
+ }
+#endif
+ }
+#endif
+#endif
+#if defined(AUTO_RESIZE) && !defined(BITSTATE)
+ tmp->m_K1 = K1;
+#endif
+ memcpy(((char *)&(tmp->state)), v, n);
+#ifdef FULLSTACK
+ tmp->tagged = (S_A)?V_A:(depth+1);
+#ifdef DEBUG
+ dumpstate(-1, v, n, tmp->tagged);
+#endif
+ Lstate = (struct H_el *) tmp;
+#else
+ #ifdef DEBUG
+ dumpstate(-1, v, n, 0);
+ #endif
+ #if NCORE>1
+ Lstate = (struct H_el *) tmp;
+ #endif
+#endif
+/* #if NCORE>1 && !defined(SEP_STATE) */
+#if NCORE>1
+ #ifdef V_PROVISO
+ tmp->cpu_id = core_id;
+ #endif
+ #if !defined(SEP_STATE) && !defined(BITSTATE)
+ leave_critical(CS_ID);
+ #endif
+#endif
+ return 0;
+}
+#endif
+#include TRANSITIONS
+void
+do_reach(void)
+{
+ r_ck(reached0, nstates0, 0, src_ln0, src_file0);
+ r_ck(reached1, nstates1, 1, src_ln1, src_file1);
+ r_ck(reached2, nstates2, 2, src_ln2, src_file2);
+ r_ck(reached3, nstates3, 3, src_ln3, src_file3);
+ r_ck(reached4, nstates4, 4, src_ln4, src_file4);
+}
+
+void
+iniglobals(void)
+{
+ deliver = 0;
+ { int l_in;
+ for (l_in = 0; l_in < 4; l_in++)
+ {
+ now.buffer_use[l_in] = 0;
+ }
+ }
+ now.write_off = 0;
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ now.commit_count[l_in] = 0;
+ }
+ }
+ now.read_off = 0;
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ now.retrieve_count[l_in] = 0;
+ }
+ }
+ now.events_lost = 0;
+ now.refcount = 0;
+#ifdef VAR_RANGES
+ { int l_in;
+ for (l_in = 0; l_in < 4; l_in++)
+ {
+ logval("buffer_use[l_in]", now.buffer_use[l_in]);
+ }
+ }
+ logval("write_off", now.write_off);
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ logval("commit_count[l_in]", now.commit_count[l_in]);
+ }
+ }
+ logval("read_off", now.read_off);
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ logval("retrieve_count[l_in]", now.retrieve_count[l_in]);
+ }
+ }
+ logval("events_lost", now.events_lost);
+ logval("refcount", now.refcount);
+#endif
+ Maxbody = max(Maxbody, sizeof(State)-VECTORSZ);
+}
+
+int
+addqueue(int n, int is_rv)
+{ int j=0, i = now._nr_qs;
+#ifndef NOCOMP
+ int k;
+#endif
+ if (i >= MAXQ)
+ Uerror("too many queues");
+ switch (n) {
+ default: Uerror("bad queue - addqueue");
+ }
+ if (vsize%WS)
+ q_skip[i] = WS-(vsize%WS);
+ else
+ q_skip[i] = 0;
+#ifndef NOCOMP
+ k = vsize;
+#ifndef BFS
+ if (is_rv) k += j;
+#endif
+ for (k += (int) q_skip[i]; k > vsize; k--)
+ Mask[k-1] = 1;
+#endif
+ vsize += (int) q_skip[i];
+ q_offset[i] = vsize;
+ now._nr_qs += 1;
+ vsize += j;
+#ifndef NOVSZ
+ now._vsz = vsize;
+#endif
+ hmax = max(hmax, vsize);
+ if (vsize >= VECTORSZ)
+ Uerror("VECTORSZ is too small, edit pan.h");
+ memset((char *)qptr(i), 0, j);
+ ((Q0 *)qptr(i))->_t = n;
+ return i+1;
+}
+
+#if NQS>0
+void
+qsend(int into, int sorted, int args_given)
+{ int j; uchar *z;
+
+#ifdef HAS_SORTED
+ int k;
+#endif
+ if (!into--)
+ uerror("ref to uninitialized chan name (sending)");
+ if (into >= (int) now._nr_qs || into < 0)
+ Uerror("qsend bad queue#");
+ z = qptr(into);
+ j = ((Q0 *)qptr(into))->Qlen;
+ switch (((Q0 *)qptr(into))->_t) {
+ case 0: printf("queue %d was deleted\n", into+1);
+ default: Uerror("bad queue - qsend");
+ }
+#ifdef EVENT_TRACE
+ if (in_s_scope(into+1))
+ require('s', into);
+#endif
+}
+#endif
+
+#if SYNC
+int
+q_zero(int from)
+{ if (!from--)
+ { uerror("ref to uninitialized chan name (q_zero)");
+ return 0;
+ }
+ switch(((Q0 *)qptr(from))->_t) {
+ case 0: printf("queue %d was deleted\n", from+1);
+ }
+ Uerror("bad queue q-zero");
+ return -1;
+}
+int
+not_RV(int from)
+{ if (q_zero(from))
+ { printf("==>> a test of the contents of a rv ");
+ printf("channel always returns FALSE\n");
+ uerror("error to poll rendezvous channel");
+ }
+ return 1;
+}
+#endif
+#ifndef XUSAFE
+void
+setq_claim(int x, int m, char *s, int y, char *p)
+{ if (x == 0)
+ uerror("x[rs] claim on uninitialized channel");
+ if (x < 0 || x > MAXQ)
+ Uerror("cannot happen setq_claim");
+ q_claim[x] |= m;
+ p_name[y] = p;
+ q_name[x] = s;
+ if (m&2) q_S_check(x, y);
+ if (m&1) q_R_check(x, y);
+}
+short q_sender[MAXQ+1];
+int
+q_S_check(int x, int who)
+{ if (!q_sender[x])
+ { q_sender[x] = who+1;
+#if SYNC
+ if (q_zero(x))
+ { printf("chan %s (%d), ",
+ q_name[x], x-1);
+ printf("sndr proc %s (%d)\n",
+ p_name[who], who);
+ uerror("xs chans cannot be used for rv");
+ }
+#endif
+ } else
+ if (q_sender[x] != who+1)
+ { printf("pan: xs assertion violated: ");
+ printf("access to chan <%s> (%d)\npan: by ",
+ q_name[x], x-1);
+ if (q_sender[x] > 0 && p_name[q_sender[x]-1])
+ printf("%s (proc %d) and by ",
+ p_name[q_sender[x]-1], q_sender[x]-1);
+ printf("%s (proc %d)\n",
+ p_name[who], who);
+ uerror("error, partial order reduction invalid");
+ }
+ return 1;
+}
+short q_recver[MAXQ+1];
+int
+q_R_check(int x, int who)
+{ if (!q_recver[x])
+ { q_recver[x] = who+1;
+#if SYNC
+ if (q_zero(x))
+ { printf("chan %s (%d), ",
+ q_name[x], x-1);
+ printf("recv proc %s (%d)\n",
+ p_name[who], who);
+ uerror("xr chans cannot be used for rv");
+ }
+#endif
+ } else
+ if (q_recver[x] != who+1)
+ { printf("pan: xr assertion violated: ");
+ printf("access to chan %s (%d)\npan: ",
+ q_name[x], x-1);
+ if (q_recver[x] > 0 && p_name[q_recver[x]-1])
+ printf("by %s (proc %d) and ",
+ p_name[q_recver[x]-1], q_recver[x]-1);
+ printf("by %s (proc %d)\n",
+ p_name[who], who);
+ uerror("error, partial order reduction invalid");
+ }
+ return 1;
+}
+#endif
+int
+q_len(int x)
+{ if (!x--)
+ uerror("ref to uninitialized chan name (len)");
+ return ((Q0 *)qptr(x))->Qlen;
+}
+
+int
+q_full(int from)
+{ if (!from--)
+ uerror("ref to uninitialized chan name (qfull)");
+ switch(((Q0 *)qptr(from))->_t) {
+ case 0: printf("queue %d was deleted\n", from+1);
+ }
+ Uerror("bad queue - q_full");
+ return 0;
+}
+
+#ifdef HAS_UNLESS
+int
+q_e_f(int from)
+{ /* empty or full */
+ return !q_len(from) || q_full(from);
+}
+#endif
+#if NQS>0
+int
+qrecv(int from, int slot, int fld, int done)
+{ uchar *z;
+ int j, k, r=0;
+
+ if (!from--)
+ uerror("ref to uninitialized chan name (receiving)");
+ if (from >= (int) now._nr_qs || from < 0)
+ Uerror("qrecv bad queue#");
+ z = qptr(from);
+#ifdef EVENT_TRACE
+ if (done && (in_r_scope(from+1)))
+ require('r', from);
+#endif
+ switch (((Q0 *)qptr(from))->_t) {
+ case 0: printf("queue %d was deleted\n", from+1);
+ default: Uerror("bad queue - qrecv");
+ }
+ return r;
+}
+#endif
+
+#ifndef BITSTATE
+#ifdef COLLAPSE
+long
+col_q(int i, char *z)
+{ int j=0, k;
+ char *x, *y;
+ Q0 *ptr = (Q0 *) qptr(i);
+ switch (ptr->_t) {
+ default: Uerror("bad qtype - collapse");
+ }
+ if (z) x = z; else x = scratch;
+ y = (char *) ptr; k = q_offset[i];
+ /* no need to store the empty slots at the end */
+ j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]);
+ for ( ; j > 0; j--, y++)
+ if (!Mask[k++]) *x++ = *y;
+ for (j = 0; j < WS-1; j++)
+ *x++ = 0;
+ x -= j;
+ if (z) return (long) (x - z);
+ return ordinal(scratch, x-scratch, 1); /* chan */
+}
+#endif
+#endif
+int
+unsend(int into)
+{ int _m=0, j; uchar *z;
+
+#ifdef HAS_SORTED
+ int k;
+#endif
+ if (!into--)
+ uerror("ref to uninitialized chan (unsend)");
+ z = qptr(into);
+ j = ((Q0 *)z)->Qlen;
+ ((Q0 *)z)->Qlen = --j;
+ switch (((Q0 *)qptr(into))->_t) {
+ default: Uerror("bad queue - unsend");
+ }
+ return _m;
+}
+
+void
+unrecv(int from, int slot, int fld, int fldvar, int strt)
+{ int j; uchar *z;
+
+ if (!from--)
+ uerror("ref to uninitialized chan (unrecv)");
+ z = qptr(from);
+ j = ((Q0 *)z)->Qlen;
+ if (strt) ((Q0 *)z)->Qlen = j+1;
+ switch (((Q0 *)qptr(from))->_t) {
+ default: Uerror("bad queue - qrecv");
+ }
+}
+int
+q_cond(short II, Trans *t)
+{ int i = 0;
+ for (i = 0; i < 6; i++)
+ { if (t->ty[i] == TIMEOUT_F) return 1;
+ if (t->ty[i] == ALPHA_F)
+#ifdef GLOB_ALPHA
+ return 0;
+#else
+ return (II+1 == (short) now._nr_pr && II+1 < MAXPROC);
+#endif
+ switch (t->qu[i]) {
+ case 0: break;
+ default: Uerror("unknown qid - q_cond");
+ return 0;
+ }
+ }
+ return 1;
+}
+void
+to_compile(void)
+{ char ctd[1024], carg[64];
+#ifdef BITSTATE
+ strcpy(ctd, "-DBITSTATE ");
+#else
+ strcpy(ctd, "");
+#endif
+#ifdef NOVSZ
+ strcat(ctd, "-DNOVSZ ");
+#endif
+#ifdef REVERSE
+ strcat(ctd, "-DREVERSE ");
+#endif
+#ifdef T_REVERSE
+ strcat(ctd, "-DT_REVERSE ");
+#endif
+#ifdef RANDOMIZE
+ #if RANDOMIZE>0
+ sprintf(carg, "-DRANDOMIZE=%d ", RANDOMIZE);
+ strcat(ctd, carg);
+ #else
+ strcat(ctd, "-DRANDOMIZE ");
+ #endif
+#endif
+#ifdef SCHED
+ sprintf(carg, "-DSCHED=%d ", SCHED);
+ strcat(ctd, carg);
+#endif
+#ifdef BFS
+ strcat(ctd, "-DBFS ");
+#endif
+#ifdef MEMLIM
+ sprintf(carg, "-DMEMLIM=%d ", MEMLIM);
+ strcat(ctd, carg);
+#else
+#ifdef MEMCNT
+ sprintf(carg, "-DMEMCNT=%d ", MEMCNT);
+ strcat(ctd, carg);
+#endif
+#endif
+#ifdef NOCLAIM
+ strcat(ctd, "-DNOCLAIM ");
+#endif
+#ifdef SAFETY
+ strcat(ctd, "-DSAFETY ");
+#else
+#ifdef NOFAIR
+ strcat(ctd, "-DNOFAIR ");
+#else
+#ifdef NFAIR
+ if (NFAIR != 2)
+ { sprintf(carg, "-DNFAIR=%d ", NFAIR);
+ strcat(ctd, carg);
+ }
+#endif
+#endif
+#endif
+#ifdef NOREDUCE
+ strcat(ctd, "-DNOREDUCE ");
+#else
+#ifdef XUSAFE
+ strcat(ctd, "-DXUSAFE ");
+#endif
+#endif
+#ifdef NP
+ strcat(ctd, "-DNP ");
+#endif
+#ifdef PEG
+ strcat(ctd, "-DPEG ");
+#endif
+#ifdef VAR_RANGES
+ strcat(ctd, "-DVAR_RANGES ");
+#endif
+#ifdef HC0
+ strcat(ctd, "-DHC0 ");
+#endif
+#ifdef HC1
+ strcat(ctd, "-DHC1 ");
+#endif
+#ifdef HC2
+ strcat(ctd, "-DHC2 ");
+#endif
+#ifdef HC3
+ strcat(ctd, "-DHC3 ");
+#endif
+#ifdef HC4
+ strcat(ctd, "-DHC4 ");
+#endif
+#ifdef CHECK
+ strcat(ctd, "-DCHECK ");
+#endif
+#ifdef CTL
+ strcat(ctd, "-DCTL ");
+#endif
+#ifdef NIBIS
+ strcat(ctd, "-DNIBIS ");
+#endif
+#ifdef NOBOUNDCHECK
+ strcat(ctd, "-DNOBOUNDCHECK ");
+#endif
+#ifdef NOSTUTTER
+ strcat(ctd, "-DNOSTUTTER ");
+#endif
+#ifdef REACH
+ strcat(ctd, "-DREACH ");
+#endif
+#ifdef PRINTF
+ strcat(ctd, "-DPRINTF ");
+#endif
+#ifdef OTIM
+ strcat(ctd, "-DOTIM ");
+#endif
+#ifdef COLLAPSE
+ strcat(ctd, "-DCOLLAPSE ");
+#endif
+#ifdef MA
+ sprintf(carg, "-DMA=%d ", MA);
+ strcat(ctd, carg);
+#endif
+#ifdef SVDUMP
+ strcat(ctd, "-DSVDUMP ");
+#endif
+#ifdef VECTORSZ
+ if (VECTORSZ != 1024)
+ { sprintf(carg, "-DVECTORSZ=%d ", VECTORSZ);
+ strcat(ctd, carg);
+ }
+#endif
+#ifdef VERBOSE
+ strcat(ctd, "-DVERBOSE ");
+#endif
+#ifdef CHECK
+ strcat(ctd, "-DCHECK ");
+#endif
+#ifdef SDUMP
+ strcat(ctd, "-DSDUMP ");
+#endif
+#if NCORE>1
+ sprintf(carg, "-DNCORE=%d ", NCORE);
+ strcat(ctd, carg);
+#endif
+#ifdef SFH
+ sprintf(carg, "-DSFH ");
+ strcat(ctd, carg);
+#endif
+#ifdef VMAX
+ if (VMAX != 256)
+ { sprintf(carg, "-DVMAX=%d ", VMAX);
+ strcat(ctd, carg);
+ }
+#endif
+#ifdef PMAX
+ if (PMAX != 16)
+ { sprintf(carg, "-DPMAX=%d ", PMAX);
+ strcat(ctd, carg);
+ }
+#endif
+#ifdef QMAX
+ if (QMAX != 16)
+ { sprintf(carg, "-DQMAX=%d ", QMAX);
+ strcat(ctd, carg);
+ }
+#endif
+#ifdef SET_WQ_SIZE
+ sprintf(carg, "-DSET_WQ_SIZE=%d ", SET_WQ_SIZE);
+ strcat(ctd, carg);
+#endif
+ printf("Compiled as: cc -o pan %span.c\n", ctd);
+}
+void
+active_procs(void)
+{
+ if (!permuted) {
+ Addproc(4);
+ } else {
+ Addproc(4);
+ }
+}
+#ifdef MA
+/*
+#include <stdio.h>
+#define uchar unsigned char
+*/
+#define ulong unsigned long
+#define ushort unsigned short
+
+#define TWIDTH 256
+#define HASH(y,n) (n)*(((long)y))
+#define INRANGE(e,h) ((h>=e->From && h<=e->To)||(e->s==1 && e->S==h))
+
+extern char *emalloc(unsigned long); /* imported routine */
+extern void dfa_init(ushort); /* 4 exported routines */
+extern int dfa_member(ulong);
+extern int dfa_store(uchar *);
+extern void dfa_stats(void);
+
+typedef struct Edge {
+ uchar From, To; /* max range 0..255 */
+ uchar s, S; /* if s=1, S is singleton */
+ struct Vertex *Dst;
+ struct Edge *Nxt;
+} Edge;
+
+typedef struct Vertex {
+ ulong key, num; /* key for splay tree, nr incoming edges */
+ uchar from[2], to[2]; /* in-node predefined edge info */
+ struct Vertex *dst[2];/* most nodes have 2 or more edges */
+ struct Edge *Succ; /* in case there are more edges */
+ struct Vertex *lnk, *left, *right; /* splay tree plumbing */
+} Vertex;
+
+static Edge *free_edges;
+static Vertex *free_vertices;
+static Vertex **layers; /* one splay tree of nodes per layer */
+static Vertex **path; /* run of word in the DFA */
+static Vertex *R, *F, *NF; /* Root, Final, Not-Final */
+static uchar *word, *lastword;/* string, and last string inserted */
+static int dfa_depth, iv=0, nv=0, pfrst=0, Tally;
+
+static void insert_it(Vertex *, int); /* splay-tree code */
+static void delete_it(Vertex *, int);
+static Vertex *find_it(Vertex *, Vertex *, uchar, int);
+
+static void
+recyc_edges(Edge *e)
+{
+ if (!e) return;
+ recyc_edges(e->Nxt);
+ e->Nxt = free_edges;
+ free_edges = e;
+}
+
+static Edge *
+new_edge(Vertex *dst)
+{ Edge *e;
+
+ if (free_edges)
+ { e = free_edges;
+ free_edges = e->Nxt;
+ e->From = e->To = e->s = e->S = 0;
+ e->Nxt = (Edge *) 0;
+ } else
+ e = (Edge *) emalloc(sizeof(Edge));
+ e->Dst = dst;
+
+ return e;
+}
+
+static void
+recyc_vertex(Vertex *v)
+{
+ recyc_edges(v->Succ);
+ v->Succ = (Edge *) free_vertices;
+ free_vertices = v;
+ nr_states--;
+}
+
+static Vertex *
+new_vertex(void)
+{ Vertex *v;
+
+ if (free_vertices)
+ { v = free_vertices;
+ free_vertices = (Vertex *) v->Succ;
+ v->Succ = (Edge *) 0;
+ v->num = 0;
+ } else
+ v = (Vertex *) emalloc(sizeof(Vertex));
+
+ nr_states++;
+ return v;
+}
+
+static Vertex *
+allDelta(Vertex *v, int n)
+{ Vertex *dst = new_vertex();
+
+ v->from[0] = 0;
+ v->to[0] = 255;
+ v->dst[0] = dst;
+ dst->num = 256;
+ insert_it(v, n);
+ return dst;
+}
+
+static void
+insert_edge(Vertex *v, Edge *e)
+{ /* put new edge first */
+ if (!v->dst[0])
+ { v->dst[0] = e->Dst;
+ v->from[0] = e->From;
+ v->to[0] = e->To;
+ recyc_edges(e);
+ return;
+ }
+ if (!v->dst[1])
+ { v->from[1] = v->from[0]; v->from[0] = e->From;
+ v->to[1] = v->to[0]; v->to[0] = e->To;
+ v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;
+ recyc_edges(e);
+ return;
+ } /* shift */
+ { int f = v->from[1];
+ int t = v->to[1];
+ Vertex *d = v->dst[1];
+ v->from[1] = v->from[0]; v->from[0] = e->From;
+ v->to[1] = v->to[0]; v->to[0] = e->To;
+ v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;
+ e->From = f;
+ e->To = t;
+ e->Dst = d;
+ }
+ e->Nxt = v->Succ;
+ v->Succ = e;
+}
+
+static void
+copyRecursive(Vertex *v, Edge *e)
+{ Edge *f;
+ if (e->Nxt) copyRecursive(v, e->Nxt);
+ f = new_edge(e->Dst);
+ f->From = e->From;
+ f->To = e->To;
+ f->s = e->s;
+ f->S = e->S;
+ f->Nxt = v->Succ;
+ v->Succ = f;
+}
+
+static void
+copyEdges(Vertex *to, Vertex *from)
+{ int i;
+ for (i = 0; i < 2; i++)
+ { to->from[i] = from->from[i];
+ to->to[i] = from->to[i];
+ to->dst[i] = from->dst[i];
+ }
+ if (from->Succ) copyRecursive(to, from->Succ);
+}
+
+static Edge *
+cacheDelta(Vertex *v, int h, int first)
+{ static Edge *ov, tmp; int i;
+
+ if (!first && INRANGE(ov,h))
+ return ov; /* intercepts about 10% */
+ for (i = 0; i < 2; i++)
+ if (v->dst[i] && h >= v->from[i] && h <= v->to[i])
+ { tmp.From = v->from[i];
+ tmp.To = v->to[i];
+ tmp.Dst = v->dst[i];
+ tmp.s = tmp.S = 0;
+ ov = &tmp;
+ return ov;
+ }
+ for (ov = v->Succ; ov; ov = ov->Nxt)
+ if (INRANGE(ov,h)) return ov;
+
+ Uerror("cannot get here, cacheDelta");
+ return (Edge *) 0;
+}
+
+static Vertex *
+Delta(Vertex *v, int h) /* v->delta[h] */
+{ Edge *e;
+
+ if (v->dst[0] && h >= v->from[0] && h <= v->to[0])
+ return v->dst[0]; /* oldest edge */
+ if (v->dst[1] && h >= v->from[1] && h <= v->to[1])
+ return v->dst[1];
+ for (e = v->Succ; e; e = e->Nxt)
+ if (INRANGE(e,h))
+ return e->Dst;
+ Uerror("cannot happen Delta");
+ return (Vertex *) 0;
+}
+
+static void
+numDelta(Vertex *v, int d)
+{ Edge *e;
+ ulong cnt;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (v->dst[i])
+ { cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]);
+ if (d == 1 && cnt < v->dst[i]->num) goto bad;
+ v->dst[i]->num = cnt;
+ }
+ for (e = v->Succ; e; e = e->Nxt)
+ { cnt = e->Dst->num + d*(1 + e->To - e->From + e->s);
+ if (d == 1 && cnt < e->Dst->num)
+bad: Uerror("too many incoming edges");
+ e->Dst->num = cnt;
+ }
+}
+
+static void
+setDelta(Vertex *v, int h, Vertex *newdst) /* v->delta[h] = newdst; */
+{ Edge *e, *f = (Edge *) 0, *g;
+ int i;
+
+ /* remove the old entry, if there */
+ for (i = 0; i < 2; i++)
+ if (v->dst[i] && h >= v->from[i] && h <= v->to[i])
+ { if (h == v->from[i])
+ { if (h == v->to[i])
+ { v->dst[i] = (Vertex *) 0;
+ v->from[i] = v->to[i] = 0;
+ } else
+ v->from[i]++;
+ } else if (h == v->to[i])
+ { v->to[i]--;
+ } else
+ { g = new_edge(v->dst[i]);/* same dst */
+ g->From = v->from[i];
+ g->To = h-1; /* left half */
+ v->from[i] = h+1; /* right half */
+ insert_edge(v, g);
+ }
+ goto part2;
+ }
+ for (e = v->Succ; e; f = e, e = e->Nxt)
+ { if (e->s == 1 && e->S == h)
+ { e->s = e->S = 0;
+ goto rem_tst;
+ }
+ if (h >= e->From && h <= e->To)
+ { if (h == e->From)
+ { if (h == e->To)
+ { if (e->s)
+ { e->From = e->To = e->S;
+ e->s = 0;
+ break;
+ } else
+ goto rem_do;
+ } else
+ e->From++;
+ } else if (h == e->To)
+ { e->To--;
+ } else /* split */
+ { g = new_edge(e->Dst); /* same dst */
+ g->From = e->From;
+ g->To = h-1; /* g=left half */
+ e->From = h+1; /* e=right half */
+ g->Nxt = e->Nxt; /* insert g */
+ e->Nxt = g; /* behind e */
+ break; /* done */
+ }
+
+rem_tst: if (e->From > e->To)
+ { if (e->s == 0) {
+rem_do: if (f)
+ f->Nxt = e->Nxt;
+ else
+ v->Succ = e->Nxt;
+ e->Nxt = (Edge *) 0;
+ recyc_edges(e);
+ } else
+ { e->From = e->To = e->S;
+ e->s = 0;
+ } }
+ break;
+ } }
+part2:
+ /* check if newdst is already there */
+ for (i = 0; i < 2; i++)
+ if (v->dst[i] == newdst)
+ { if (h+1 == (int) v->from[i])
+ { v->from[i] = h;
+ return;
+ }
+ if (h == (int) v->to[i]+1)
+ { v->to[i] = h;
+ return;
+ } }
+ for (e = v->Succ; e; e = e->Nxt)
+ { if (e->Dst == newdst)
+ { if (h+1 == (int) e->From)
+ { e->From = h;
+ if (e->s == 1 && e->S+1 == e->From)
+ { e->From = e->S;
+ e->s = e->S = 0;
+ }
+ return;
+ }
+ if (h == (int) e->To+1)
+ { e->To = h;
+ if (e->s == 1 && e->S == e->To+1)
+ { e->To = e->S;
+ e->s = e->S = 0;
+ }
+ return;
+ }
+ if (e->s == 0)
+ { e->s = 1;
+ e->S = h;
+ return;
+ } } }
+ /* add as a new edge */
+ e = new_edge(newdst);
+ e->From = e->To = h;
+ insert_edge(v, e);
+}
+
+static ulong
+cheap_key(Vertex *v)
+{ ulong vk2 = 0;
+
+ if (v->dst[0])
+ { vk2 = (ulong) v->dst[0];
+ if ((ulong) v->dst[1] > vk2)
+ vk2 = (ulong) v->dst[1];
+ } else if (v->dst[1])
+ vk2 = (ulong) v->dst[1];
+ if (v->Succ)
+ { Edge *e;
+ for (e = v->Succ; e; e = e->Nxt)
+ if ((ulong) e->Dst > vk2)
+ vk2 = (ulong) e->Dst;
+ }
+ Tally = (vk2>>2)&(TWIDTH-1);
+ return v->key;
+}
+
+static ulong
+mk_key(Vertex *v) /* not sensitive to order */
+{ ulong m = 0, vk2 = 0;
+ Edge *e;
+
+ if (v->dst[0])
+ { m += HASH(v->dst[0], v->to[0] - v->from[0] + 1);
+ vk2 = (ulong) v->dst[0];
+ }
+ if (v->dst[1])
+ { m += HASH(v->dst[1], v->to[1] - v->from[1] + 1);
+ if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1];
+ }
+ for (e = v->Succ; e; e = e->Nxt)
+ { m += HASH(e->Dst, e->To - e->From + 1 + e->s);
+ if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst;
+ }
+ Tally = (vk2>>2)&(TWIDTH-1);
+ return m;
+}
+
+static ulong
+mk_special(int sigma, Vertex *n, Vertex *v)
+{ ulong m = 0, vk2 = 0;
+ Edge *f;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (v->dst[i])
+ { if (sigma >= v->from[i] && sigma <= v->to[i])
+ { m += HASH(v->dst[i], v->to[i]-v->from[i]);
+ if ((ulong) v->dst[i] > vk2
+ && v->to[i] > v->from[i])
+ vk2 = (ulong) v->dst[i];
+ } else
+ { m += HASH(v->dst[i], v->to[i]-v->from[i]+1);
+ if ((ulong) v->dst[i] > vk2)
+ vk2 = (ulong) v->dst[i];
+ } }
+ for (f = v->Succ; f; f = f->Nxt)
+ { if (sigma >= f->From && sigma <= f->To)
+ { m += HASH(f->Dst, f->To - f->From + f->s);
+ if ((ulong) f->Dst > vk2
+ && f->To - f->From + f->s > 0)
+ vk2 = (ulong) f->Dst;
+ } else if (f->s == 1 && sigma == f->S)
+ { m += HASH(f->Dst, f->To - f->From + 1);
+ if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst;
+ } else
+ { m += HASH(f->Dst, f->To - f->From + 1 + f->s);
+ if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst;
+ } }
+
+ if ((ulong) n > vk2) vk2 = (ulong) n;
+ Tally = (vk2>>2)&(TWIDTH-1);
+ m += HASH(n, 1);
+ return m;
+}
+
+void
+dfa_init(ushort nr_layers)
+{ int i; Vertex *r, *t;
+
+ dfa_depth = nr_layers; /* one byte per layer */
+ path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));
+ layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));
+ lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));
+ lastword[dfa_depth] = lastword[0] = 255;
+ path[0] = R = new_vertex(); F = new_vertex();
+
+ for (i = 1, r = R; i < dfa_depth; i++, r = t)
+ t = allDelta(r, i-1);
+ NF = allDelta(r, i-1);
+}
+
+#if 0
+static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; }
+#endif
+
+double
+tree_stats(Vertex *t)
+{ Edge *e; double cnt=0.0;
+ if (!t) return 0;
+ if (!t->key) return 0;
+ t->key = 0; /* precaution */
+ if (t->dst[0]) cnt++;
+ if (t->dst[1]) cnt++;
+ for (e = t->Succ; e; e = e->Nxt)
+ cnt++;
+ cnt += tree_stats(t->lnk);
+ cnt += tree_stats(t->left);
+ cnt += tree_stats(t->right);
+ return cnt;
+}
+
+void
+dfa_stats(void)
+{ int i, j; double cnt = 0.0;
+ for (j = 0; j < TWIDTH; j++)
+ for (i = 0; i < dfa_depth+1; i++)
+ cnt += tree_stats(layers[i*TWIDTH+j]);
+ printf("Minimized Automaton: %6d nodes and %6g edges\n",
+ nr_states, cnt);
+}
+
+int
+dfa_member(ulong n)
+{ Vertex **p, **q;
+ uchar *w = &word[n];
+ int i;
+
+ p = &path[n]; q = (p+1);
+ for (i = n; i < dfa_depth; i++)
+ *q++ = Delta(*p++, *w++);
+ return (*p == F);
+}
+
+int
+dfa_store(uchar *sv)
+{ Vertex **p, **q, *s, *y, *old, *new = F;
+ uchar *w, *u = lastword;
+ int i, j, k;
+
+ w = word = sv;
+ while (*w++ == *u++) /* find first byte that differs */
+ ;
+ pfrst = (int) (u - lastword) - 1;
+ memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst);
+ if (pfrst > iv) pfrst = iv;
+ if (pfrst > nv) pfrst = nv;
+/* phase1: */
+ p = &path[pfrst]; q = (p+1); w = &word[pfrst];
+ for (i = pfrst; i < dfa_depth; i++)
+ *q++ = Delta(*p++, *w++); /* (*p)->delta[*w++]; */
+
+ if (*p == F) return 1; /* it's already there */
+/* phase2: */
+ iv = dfa_depth;
+ do { iv--;
+ old = new;
+ new = find_it(path[iv], old, word[iv], iv);
+ } while (new && iv > 0);
+
+/* phase3: */
+ nv = k = 0; s = path[0];
+ for (j = 1; j <= iv; ++j)
+ if (path[j]->num > 1)
+ { y = new_vertex();
+ copyEdges(y, path[j]);
+ insert_it(y, j);
+ numDelta(y, 1);
+ delete_it(s, j-1);
+ setDelta(s, word[j-1], y);
+ insert_it(s, j-1);
+ y->num = 1; /* initial value 1 */
+ s = y;
+ path[j]->num--; /* only 1 moved from j to y */
+ k = 1;
+ } else
+ { s = path[j];
+ if (!k) nv = j;
+ }
+ y = Delta(s, word[iv]);
+ y->num--;
+ delete_it(s, iv);
+ setDelta(s, word[iv], old);
+ insert_it(s, iv);
+ old->num++;
+
+ for (j = iv+1; j < dfa_depth; j++)
+ if (path[j]->num == 0)
+ { numDelta(path[j], -1);
+ delete_it(path[j], j);
+ recyc_vertex(path[j]);
+ } else
+ break;
+ return 0;
+}
+
+static Vertex *
+splay(ulong i, Vertex *t)
+{ Vertex N, *l, *r, *y;
+
+ if (!t) return t;
+ N.left = N.right = (Vertex *) 0;
+ l = r = &N;
+ for (;;)
+ { if (i < t->key)
+ { if (!t->left) break;
+ if (i < t->left->key)
+ { y = t->left;
+ t->left = y->right;
+ y->right = t;
+ t = y;
+ if (!t->left) break;
+ }
+ r->left = t;
+ r = t;
+ t = t->left;
+ } else if (i > t->key)
+ { if (!t->right) break;
+ if (i > t->right->key)
+ { y = t->right;
+ t->right = y->left;
+ y->left = t;
+ t = y;
+ if (!t->right) break;
+ }
+ l->right = t;
+ l = t;
+ t = t->right;
+ } else
+ break;
+ }
+ l->right = t->left;
+ r->left = t->right;
+ t->left = N.right;
+ t->right = N.left;
+ return t;
+}
+
+static void
+insert_it(Vertex *v, int L)
+{ Vertex *new, *t;
+ ulong i; int nr;
+
+ i = mk_key(v);
+ nr = ((L*TWIDTH)+Tally);
+ t = layers[nr];
+
+ v->key = i;
+ if (!t)
+ { layers[nr] = v;
+ return;
+ }
+ t = splay(i, t);
+ if (i < t->key)
+ { new = v;
+ new->left = t->left;
+ new->right = t;
+ t->left = (Vertex *) 0;
+ } else if (i > t->key)
+ { new = v;
+ new->right = t->right;
+ new->left = t;
+ t->right = (Vertex *) 0;
+ } else /* it's already there */
+ { v->lnk = t->lnk; /* put in linked list off v */
+ t->lnk = v;
+ new = t;
+ }
+ layers[nr] = new;
+}
+
+static int
+checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma)
+{ Edge *g, *f;
+ int i, k, j = 1;
+
+ for (k = 0; k < 2; k++)
+ if (h->dst[k])
+ { if (sigma >= h->from[k] && sigma <= h->to[k])
+ { if (h->dst[k] != n) goto no_match;
+ }
+ for (i = h->from[k]; i <= h->to[k]; i++)
+ { if (i == sigma) continue;
+ g = cacheDelta(v, i, j); j = 0;
+ if (h->dst[k] != g->Dst)
+ goto no_match;
+ if (g->s == 0 || g->S != i)
+ i = g->To;
+ } }
+ for (f = h->Succ; f; f = f->Nxt)
+ { if (INRANGE(f,sigma))
+ { if (f->Dst != n) goto no_match;
+ }
+ for (i = f->From; i <= f->To; i++)
+ { if (i == sigma) continue;
+ g = cacheDelta(v, i, j); j = 0;
+ if (f->Dst != g->Dst)
+ goto no_match;
+ if (g->s == 1 && i == g->S)
+ continue;
+ i = g->To;
+ }
+ if (f->s && f->S != sigma)
+ { g = cacheDelta(v, f->S, 1);
+ if (f->Dst != g->Dst)
+ goto no_match;
+ }
+ }
+ if (h->Succ || h->dst[0] || h->dst[1]) return 1;
+no_match:
+ return 0;
+}
+
+static Vertex *
+find_it(Vertex *v, Vertex *n, uchar sigma, int L)
+{ Vertex *z, *t;
+ ulong i; int nr;
+
+ i = mk_special(sigma,n,v);
+ nr = ((L*TWIDTH)+Tally);
+ t = layers[nr];
+
+ if (!t) return (Vertex *) 0;
+ layers[nr] = t = splay(i, t);
+ if (i == t->key)
+ for (z = t; z; z = z->lnk)
+ if (checkit(z, v, n, sigma))
+ return z;
+
+ return (Vertex *) 0;
+}
+
+static void
+delete_it(Vertex *v, int L)
+{ Vertex *x, *t;
+ ulong i; int nr;
+
+ i = cheap_key(v);
+ nr = ((L*TWIDTH)+Tally);
+ t = layers[nr];
+ if (!t) return;
+
+ t = splay(i, t);
+ if (i == t->key)
+ { Vertex *z, *y = (Vertex *) 0;
+ for (z = t; z && z != v; y = z, z = z->lnk)
+ ;
+ if (z != v) goto bad;
+ if (y)
+ { y->lnk = z->lnk;
+ z->lnk = (Vertex *) 0;
+ layers[nr] = t;
+ return;
+ } else if (z->lnk) /* z == t == v */
+ { y = z->lnk;
+ y->left = t->left;
+ y->right = t->right;
+ t->left = t->right = t->lnk = (Vertex *) 0;
+ layers[nr] = y;
+ return;
+ }
+ /* delete the node itself */
+ if (!t->left)
+ { x = t->right;
+ } else
+ { x = splay(i, t->left);
+ x->right = t->right;
+ }
+ t->left = t->right = t->lnk = (Vertex *) 0;
+ layers[nr] = x;
+ return;
+ }
+bad: Uerror("cannot happen delete");
+}
+#endif
+#if defined(MA) && (defined(W_XPT) || defined(R_XPT))
+static Vertex **temptree;
+static char wbuf[4096];
+static int WCNT = 4096, wcnt=0;
+static uchar stacker[MA+1];
+static ulong stackcnt = 0;
+extern double nstates, nlinks, truncs, truncs2;
+
+static void
+xwrite(int fd, char *b, int n)
+{
+ if (wcnt+n >= 4096)
+ { write(fd, wbuf, wcnt);
+ wcnt = 0;
+ }
+ memcpy(&wbuf[wcnt], b, n);
+ wcnt += n;
+}
+
+static void
+wclose(fd)
+{
+ if (wcnt > 0)
+ write(fd, wbuf, wcnt);
+ wcnt = 0;
+ close(fd);
+}
+
+static void
+w_vertex(int fd, Vertex *v)
+{ char t[3]; int i; Edge *e;
+
+ xwrite(fd, (char *) &v, sizeof(Vertex *));
+ t[0] = 0;
+ for (i = 0; i < 2; i++)
+ if (v->dst[i])
+ { t[1] = v->from[i], t[2] = v->to[i];
+ xwrite(fd, t, 3);
+ xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *));
+ }
+ for (e = v->Succ; e; e = e->Nxt)
+ { t[1] = e->From, t[2] = e->To;
+ xwrite(fd, t, 3);
+ xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));
+
+ if (e->s)
+ { t[1] = t[2] = e->S;
+ xwrite(fd, t, 3);
+ xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));
+ } }
+}
+
+static void
+w_layer(int fd, Vertex *v)
+{ uchar c=1;
+
+ if (!v) return;
+ xwrite(fd, (char *) &c, 1);
+ w_vertex(fd, v);
+ w_layer(fd, v->lnk);
+ w_layer(fd, v->left);
+ w_layer(fd, v->right);
+}
+
+void
+w_xpoint(void)
+{ int fd; char nm[64];
+ int i, j; uchar c;
+ static uchar xwarned = 0;
+
+ sprintf(nm, "%s.xpt", PanSource);
+ if ((fd = creat(nm, 0666)) <= 0)
+ if (!xwarned)
+ { xwarned = 1;
+ printf("cannot creat checkpoint file\n");
+ return;
+ }
+ xwrite(fd, (char *) &nstates, sizeof(double));
+ xwrite(fd, (char *) &truncs, sizeof(double));
+ xwrite(fd, (char *) &truncs2, sizeof(double));
+ xwrite(fd, (char *) &nlinks, sizeof(double));
+ xwrite(fd, (char *) &dfa_depth, sizeof(int));
+ xwrite(fd, (char *) &R, sizeof(Vertex *));
+ xwrite(fd, (char *) &F, sizeof(Vertex *));
+ xwrite(fd, (char *) &NF, sizeof(Vertex *));
+
+ for (j = 0; j < TWIDTH; j++)
+ for (i = 0; i < dfa_depth+1; i++)
+ { w_layer(fd, layers[i*TWIDTH+j]);
+ c = 2; xwrite(fd, (char *) &c, 1);
+ }
+ wclose(fd);
+}
+
+static void
+xread(int fd, char *b, int n)
+{ int m = wcnt; int delta = 0;
+ if (m < n)
+ { if (m > 0) memcpy(b, &wbuf[WCNT-m], m);
+ delta = m;
+ WCNT = wcnt = read(fd, wbuf, 4096);
+ if (wcnt < n-m)
+ Uerror("xread failed -- insufficient data");
+ n -= m;
+ }
+ memcpy(&b[delta], &wbuf[WCNT-wcnt], n);
+ wcnt -= n;
+}
+
+static void
+x_cleanup(Vertex *c)
+{ Edge *e; /* remove the tree and edges from c */
+ if (!c) return;
+ for (e = c->Succ; e; e = e->Nxt)
+ x_cleanup(e->Dst);
+ recyc_vertex(c);
+}
+
+static void
+x_remove(void)
+{ Vertex *tmp; int i, s;
+ int r, j;
+ /* double-check: */
+ stacker[dfa_depth-1] = 0; r = dfa_store(stacker);
+ stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1);
+ if (r != 1 || j != 0)
+ { printf("%d: ", stackcnt);
+ for (i = 0; i < dfa_depth; i++)
+ printf("%d,", stacker[i]);
+ printf(" -- not a stackstate <o:%d,4:%d>\n", r, j);
+ return;
+ }
+ stacker[dfa_depth-1] = 1;
+ s = dfa_member(dfa_depth-1);
+
+ { tmp = F; F = NF; NF = tmp; } /* complement */
+ if (s) dfa_store(stacker);
+ stacker[dfa_depth-1] = 0;
+ dfa_store(stacker);
+ stackcnt++;
+ { tmp = F; F = NF; NF = tmp; }
+}
+
+static void
+x_rm_stack(Vertex *t, int k)
+{ int j; Edge *e;
+
+ if (k == 0)
+ { x_remove();
+ return;
+ }
+ if (t)
+ for (e = t->Succ; e; e = e->Nxt)
+ { for (j = e->From; j <= (int) e->To; j++)
+ { stacker[k] = (uchar) j;
+ x_rm_stack(e->Dst, k-1);
+ }
+ if (e->s)
+ { stacker[k] = e->S;
+ x_rm_stack(e->Dst, k-1);
+ } }
+}
+
+static Vertex *
+insert_withkey(Vertex *v, int L)
+{ Vertex *new, *t = temptree[L];
+
+ if (!t) { temptree[L] = v; return v; }
+ t = splay(v->key, t);
+ if (v->key < t->key)
+ { new = v;
+ new->left = t->left;
+ new->right = t;
+ t->left = (Vertex *) 0;
+ } else if (v->key > t->key)
+ { new = v;
+ new->right = t->right;
+ new->left = t;
+ t->right = (Vertex *) 0;
+ } else
+ { if (t != R && t != F && t != NF)
+ Uerror("double insert, bad checkpoint data");
+ else
+ { recyc_vertex(v);
+ new = t;
+ } }
+ temptree[L] = new;
+
+ return new;
+}
+
+static Vertex *
+find_withkey(Vertex *v, int L)
+{ Vertex *t = temptree[L];
+ if (t)
+ { temptree[L] = t = splay((ulong) v, t);
+ if (t->key == (ulong) v)
+ return t;
+ }
+ Uerror("not found error, bad checkpoint data");
+ return (Vertex *) 0;
+}
+
+void
+r_layer(int fd, int n)
+{ Vertex *v;
+ Edge *e;
+ char c, t[2];
+
+ for (;;)
+ { xread(fd, &c, 1);
+ if (c == 2) break;
+ if (c == 1)
+ { v = new_vertex();
+ xread(fd, (char *) &(v->key), sizeof(Vertex *));
+ v = insert_withkey(v, n);
+ } else /* c == 0 */
+ { e = new_edge((Vertex *) 0);
+ xread(fd, t, 2);
+ e->From = t[0];
+ e->To = t[1];
+ xread(fd, (char *) &(e->Dst), sizeof(Vertex *));
+ insert_edge(v, e);
+ } }
+}
+
+static void
+v_fix(Vertex *t, int nr)
+{ int i; Edge *e;
+
+ if (!t) return;
+
+ for (i = 0; i < 2; i++)
+ if (t->dst[i])
+ t->dst[i] = find_withkey(t->dst[i], nr);
+
+ for (e = t->Succ; e; e = e->Nxt)
+ e->Dst = find_withkey(e->Dst, nr);
+
+ v_fix(t->left, nr);
+ v_fix(t->right, nr);
+}
+
+static void
+v_insert(Vertex *t, int nr)
+{ Edge *e; int i;
+
+ if (!t) return;
+ v_insert(t->left, nr);
+ v_insert(t->right, nr);
+
+ /* remove only leafs from temptree */
+ t->left = t->right = t->lnk = (Vertex *) 0;
+ insert_it(t, nr); /* into layers */
+ for (i = 0; i < 2; i++)
+ if (t->dst[i])
+ t->dst[i]->num += (t->to[i] - t->from[i] + 1);
+ for (e = t->Succ; e; e = e->Nxt)
+ e->Dst->num += (e->To - e->From + 1 + e->s);
+}
+
+static void
+x_fixup(void)
+{ int i;
+
+ for (i = 0; i < dfa_depth; i++)
+ v_fix(temptree[i], (i+1));
+
+ for (i = dfa_depth; i >= 0; i--)
+ v_insert(temptree[i], i);
+}
+
+static Vertex *
+x_tail(Vertex *t, ulong want)
+{ int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0;
+
+ if (!t) return v;
+
+ yes = no = 0;
+ for (i = 0; i < 2; i++)
+ if ((ulong) t->dst[i] == want)
+ { /* was t->from[i] <= 0 && t->to[i] >= 0 */
+ /* but from and to are uchar */
+ if (t->from[i] == 0)
+ yes = 1;
+ else
+ if (t->from[i] <= 4 && t->to[i] >= 4)
+ no = 1;
+ }
+
+ for (e = t->Succ; e; e = e->Nxt)
+ if ((ulong) e->Dst == want)
+ { /* was INRANGE(e,0) but From and To are uchar */
+ if ((e->From == 0) || (e->s==1 && e->S==0))
+ yes = 1;
+ else if (INRANGE(e, 4))
+ no = 1;
+ }
+ if (yes && !no) return t;
+ v = x_tail(t->left, want); if (v) return v;
+ v = x_tail(t->right, want); if (v) return v;
+ return (Vertex *) 0;
+}
+
+static void
+x_anytail(Vertex *t, Vertex *c, int nr)
+{ int i; Edge *e, *f; Vertex *v;
+
+ if (!t) return;
+
+ for (i = 0; i < 2; i++)
+ if ((ulong) t->dst[i] == c->key)
+ { v = new_vertex(); v->key = t->key;
+ f = new_edge(v);
+ f->From = t->from[i];
+ f->To = t->to[i];
+ f->Nxt = c->Succ;
+ c->Succ = f;
+ if (nr > 0)
+ x_anytail(temptree[nr-1], v, nr-1);
+ }
+
+ for (e = t->Succ; e; e = e->Nxt)
+ if ((ulong) e->Dst == c->key)
+ { v = new_vertex(); v->key = t->key;
+ f = new_edge(v);
+ f->From = e->From;
+ f->To = e->To;
+ f->s = e->s;
+ f->S = e->S;
+ f->Nxt = c->Succ;
+ c->Succ = f;
+ x_anytail(temptree[nr-1], v, nr-1);
+ }
+
+ x_anytail(t->left, c, nr);
+ x_anytail(t->right, c, nr);
+}
+
+static Vertex *
+x_cpy_rev(void)
+{ Vertex *c, *v; /* find 0 and !4 predecessor of F */
+
+ v = x_tail(temptree[dfa_depth-1], F->key);
+ if (!v) return (Vertex *) 0;
+
+ c = new_vertex(); c->key = v->key;
+
+ /* every node on dfa_depth-2 that has v->key as succ */
+ /* make copy and let c point to these (reversing ptrs) */
+
+ x_anytail(temptree[dfa_depth-2], c, dfa_depth-2);
+
+ return c;
+}
+
+void
+r_xpoint(void)
+{ int fd; char nm[64]; Vertex *d;
+ int i, j;
+
+ wcnt = 0;
+ sprintf(nm, "%s.xpt", PanSource);
+ if ((fd = open(nm, 0)) < 0) /* O_RDONLY */
+ Uerror("cannot open checkpoint file");
+
+ xread(fd, (char *) &nstates, sizeof(double));
+ xread(fd, (char *) &truncs, sizeof(double));
+ xread(fd, (char *) &truncs2, sizeof(double));
+ xread(fd, (char *) &nlinks, sizeof(double));
+ xread(fd, (char *) &dfa_depth, sizeof(int));
+
+ if (dfa_depth != MA+a_cycles)
+ Uerror("bad dfa_depth in checkpoint file");
+
+ path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));
+ layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));
+ temptree = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *));
+ lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));
+ lastword[dfa_depth] = lastword[0] = 255;
+
+ path[0] = R = new_vertex();
+ xread(fd, (char *) &R->key, sizeof(Vertex *));
+ R = insert_withkey(R, 0);
+
+ F = new_vertex();
+ xread(fd, (char *) &F->key, sizeof(Vertex *));
+ F = insert_withkey(F, dfa_depth);
+
+ NF = new_vertex();
+ xread(fd, (char *) &NF->key, sizeof(Vertex *));
+ NF = insert_withkey(NF, dfa_depth);
+
+ for (j = 0; j < TWIDTH; j++)
+ for (i = 0; i < dfa_depth+1; i++)
+ r_layer(fd, i);
+
+ if (wcnt != 0) Uerror("bad count in checkpoint file");
+
+ d = x_cpy_rev();
+ x_fixup();
+ stacker[dfa_depth-1] = 0;
+ x_rm_stack(d, dfa_depth-2);
+ x_cleanup(d);
+ close(fd);
+
+ printf("pan: removed %d stackstates\n", stackcnt);
+ nstates -= (double) stackcnt;
+}
+#endif
+#ifdef VERI
+void
+check_claim(int st)
+{
+ if (st == endclaim)
+ uerror("claim violated!");
+ if (stopstate[VERI][st])
+ uerror("end state in claim reached");
+}
+#endif
+void
+c_globals(void)
+{ /* int i; */
+ printf("global vars:\n");
+ printf(" byte write_off: %d\n", now.write_off);
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ printf(" byte commit_count[%d]: %d\n", l_in, now.commit_count[l_in]);
+ }
+ }
+ printf(" byte read_off: %d\n", now.read_off);
+ { int l_in;
+ for (l_in = 0; l_in < 2; l_in++)
+ {
+ printf(" byte retrieve_count[%d]: %d\n", l_in, now.retrieve_count[l_in]);
+ }
+ }
+ printf(" byte events_lost: %d\n", now.events_lost);
+ printf(" byte refcount: %d\n", now.refcount);
+ { int l_in;
+ for (l_in = 0; l_in < 4; l_in++)
+ {
+ printf(" bit buffer_use[%d]: %d\n", l_in, now.buffer_use[l_in]);
+ }
+ }
+}
+void
+c_locals(int pid, int tp)
+{ /* int i; */
+ switch(tp) {
+ case 4:
+ printf("local vars proc %d (:init:):\n", pid);
+ printf(" byte i: %d\n", ((P4 *)pptr(pid))->i);
+ printf(" byte j: %d\n", ((P4 *)pptr(pid))->j);
+ printf(" byte sum: %d\n", ((P4 *)pptr(pid))->sum);
+ printf(" byte commit_sum: %d\n", ((P4 *)pptr(pid))->commit_sum);
+ break;
+ case 3:
+ /* none */
+ break;
+ case 2:
+ printf("local vars proc %d (reader):\n", pid);
+ printf(" byte i: %d\n", ((P2 *)pptr(pid))->i);
+ printf(" byte j: %d\n", ((P2 *)pptr(pid))->j);
+ printf(" byte tmp_retrieve: %d\n", ((P2 *)pptr(pid))->tmp_retrieve);
+ printf(" byte lwrite_off: %d\n", ((P2 *)pptr(pid))->lwrite_off);
+ printf(" byte lcommit_count: %d\n", ((P2 *)pptr(pid))->lcommit_count);
+ break;
+ case 1:
+ printf("local vars proc %d (tracer):\n", pid);
+ printf(" byte size: %d\n", ((P1 *)pptr(pid))->size);
+ printf(" byte prev_off: %d\n", ((P1 *)pptr(pid))->prev_off);
+ printf(" byte new_off: %d\n", ((P1 *)pptr(pid))->new_off);
+ printf(" byte tmp_commit: %d\n", ((P1 *)pptr(pid))->tmp_commit);
+ printf(" byte i: %d\n", ((P1 *)pptr(pid))->i);
+ printf(" byte j: %d\n", ((P1 *)pptr(pid))->j);
+ break;
+ case 0:
+ printf("local vars proc %d (switcher):\n", pid);
+ printf(" byte prev_off: %d\n", ((P0 *)pptr(pid))->prev_off);
+ printf(" byte new_off: %d\n", ((P0 *)pptr(pid))->new_off);
+ printf(" byte tmp_commit: %d\n", ((P0 *)pptr(pid))->tmp_commit);
+ printf(" byte size: %d\n", ((P0 *)pptr(pid))->size);
+ break;
+ }
+}
+void
+printm(int x)
+{
+ switch (x) {
+ default: Printf("%d", x);
+ }
+}
+void
+c_chandump(int unused) { unused++; /* avoid complaints */ }
--- /dev/null
+#define SpinVersion "Spin Version 5.1.6 -- 9 May 2008"
+#define PanSource "buffer.spin"
+
+#ifdef WIN64
+#define ONE_L ((unsigned long) 1)
+#define long long long
+#else
+#define ONE_L (1L)
+#endif
+char *TrailFile = PanSource; /* default */
+char *trailfilename;
+#if defined(BFS)
+#ifndef SAFETY
+#define SAFETY
+#endif
+#ifndef XUSAFE
+#define XUSAFE
+#endif
+#endif
+#ifndef uchar
+#define uchar unsigned char
+#endif
+#ifndef uint
+#define uint unsigned int
+#endif
+#define DELTA 500
+#ifdef MA
+ #if NCORE>1 && !defined(SEP_STATE)
+ #define SEP_STATE
+ #endif
+#if MA==1
+#undef MA
+#define MA 100
+#endif
+#endif
+#ifdef W_XPT
+#if W_XPT==1
+#undef W_XPT
+#define W_XPT 1000000
+#endif
+#endif
+#ifndef NFAIR
+#define NFAIR 2 /* must be >= 2 */
+#endif
+#define HAS_CODE
+#define MERGED 1
+#ifdef NP /* includes np_ demon */
+#define HAS_NP 2
+#define VERI 5
+#define endclaim 3 /* none */
+#endif
+typedef struct S_F_MAP {
+ char *fnm; int from; int upto;
+} S_F_MAP;
+
+#define nstates4 59 /* :init: */
+#define endstate4 58
+short src_ln4 [] = {
+ 0, 225, 227, 228, 229, 230, 231, 231,
+ 226, 233, 226, 233, 235, 236, 237, 238,
+ 238, 234, 240, 234, 240, 241, 242, 244,
+ 245, 246, 247, 248, 248, 243, 250, 243,
+ 250, 252, 253, 254, 255, 256, 256, 251,
+ 258, 251, 224, 262, 263, 264, 266, 267,
+ 271, 272, 273, 273, 265, 278, 265, 278,
+ 282, 260, 284, 0, };
+S_F_MAP src_file4 [] = {
+ { "-", 0, 0 },
+ { "buffer.spin", 1, 58 },
+ { "-", 59, 60 }
+};
+uchar reached4 [] = {
+ 0, 1, 1, 0, 0, 0, 1, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 1, 1, 0, 0, 0, 1,
+ 0, 0, 0, 1, 1, 0, 1, 1,
+ 0, 1, 0, 0, 0, 1, 1, 0,
+ 1, 1, 0, 1, 0, 0, 1, 0,
+ 0, 0, 1, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, };
+uchar *loopstate4;
+
+#define nstates3 10 /* cleaner */
+#define endstate3 9
+short src_ln3 [] = {
+ 0, 210, 211, 212, 213, 209, 215, 209,
+ 208, 216, 0, };
+S_F_MAP src_file3 [] = {
+ { "-", 0, 0 },
+ { "buffer.spin", 1, 9 },
+ { "-", 10, 11 }
+};
+uchar reached3 [] = {
+ 0, 1, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, };
+uchar *loopstate3;
+
+#define nstates2 32 /* reader */
+#define endstate2 31
+short src_ln2 [] = {
+ 0, 169, 171, 173, 174, 175, 176, 177,
+ 177, 172, 179, 172, 170, 187, 189, 190,
+ 191, 192, 192, 188, 194, 188, 194, 196,
+ 197, 186, 199, 199, 164, 201, 164, 201,
+ 0, };
+S_F_MAP src_file2 [] = {
+ { "-", 0, 0 },
+ { "buffer.spin", 1, 31 },
+ { "-", 32, 33 }
+};
+uchar reached2 [] = {
+ 0, 1, 1, 1, 0, 0, 0, 1,
+ 1, 0, 1, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 1, 1, 0, 1, 1, 0,
+ 0, };
+uchar *loopstate2;
+
+#define nstates1 51 /* tracer */
+#define endstate1 50
+short src_ln1 [] = {
+ 0, 99, 100, 98, 104, 105, 106, 106,
+ 103, 108, 102, 111, 111, 112, 112, 110,
+ 114, 114, 116, 117, 118, 119, 120, 120,
+ 115, 122, 115, 109, 127, 129, 130, 131,
+ 132, 132, 128, 134, 128, 134, 135, 137,
+ 137, 138, 138, 136, 140, 126, 142, 144,
+ 146, 141, 148, 0, };
+S_F_MAP src_file1 [] = {
+ { "-", 0, 0 },
+ { "buffer.spin", 1, 50 },
+ { "-", 51, 52 }
+};
+uchar reached1 [] = {
+ 0, 1, 0, 0, 1, 1, 1, 0,
+ 1, 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 1, 0, 0, 0, 1, 1,
+ 0, 1, 1, 0, 1, 1, 0, 0,
+ 1, 1, 0, 1, 1, 0, 0, 1,
+ 0, 1, 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, };
+uchar *loopstate1;
+
+#define nstates0 31 /* switcher */
+#define endstate0 30
+short src_ln0 [] = {
+ 0, 56, 57, 58, 61, 62, 63, 64,
+ 64, 59, 66, 55, 69, 69, 70, 70,
+ 68, 72, 67, 75, 76, 78, 78, 79,
+ 79, 77, 81, 81, 74, 84, 85, 0, };
+S_F_MAP src_file0 [] = {
+ { "-", 0, 0 },
+ { "buffer.spin", 1, 30 },
+ { "-", 31, 32 }
+};
+uchar reached0 [] = {
+ 0, 1, 0, 0, 1, 0, 1, 1,
+ 0, 0, 1, 0, 1, 1, 1, 0,
+ 1, 1, 0, 1, 0, 1, 0, 1,
+ 0, 0, 1, 0, 0, 1, 0, 0, };
+uchar *loopstate0;
+struct {
+ int tp; short *src;
+} src_all[] = {
+ { 4, &src_ln4[0] },
+ { 3, &src_ln3[0] },
+ { 2, &src_ln2[0] },
+ { 1, &src_ln1[0] },
+ { 0, &src_ln0[0] },
+ { 0, (short *) 0 }
+};
+short *frm_st0;
+struct {
+ char *c; char *t;
+} code_lookup[] = {
+ { (char *) 0, "" }
+};
+#define _T5 64
+#define _T2 65
+#define T_ID unsigned char
+#define SYNC 0
+#define ASYNC 0
+
+#ifndef NCORE
+ #ifdef DUAL_CORE
+ #define NCORE 2
+ #elif QUAD_CORE
+ #define NCORE 4
+ #else
+ #define NCORE 1
+ #endif
+#endif
+char *procname[] = {
+ "switcher",
+ "tracer",
+ "reader",
+ "cleaner",
+ ":init:",
+ ":np_:",
+};
+
+#define Pinit ((P4 *)this)
+typedef struct P4 { /* :init: */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+ uchar i;
+ uchar j;
+ uchar sum;
+ uchar commit_sum;
+} P4;
+#define Air4 (sizeof(P4) - Offsetof(P4, commit_sum) - 1*sizeof(uchar))
+#define Pcleaner ((P3 *)this)
+typedef struct P3 { /* cleaner */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+} P3;
+#define Air3 (sizeof(P3) - 3)
+#define Preader ((P2 *)this)
+typedef struct P2 { /* reader */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+ uchar i;
+ uchar j;
+ uchar tmp_retrieve;
+ uchar lwrite_off;
+ uchar lcommit_count;
+} P2;
+#define Air2 (sizeof(P2) - Offsetof(P2, lcommit_count) - 1*sizeof(uchar))
+#define Ptracer ((P1 *)this)
+typedef struct P1 { /* tracer */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+ uchar size;
+ uchar prev_off;
+ uchar new_off;
+ uchar tmp_commit;
+ uchar i;
+ uchar j;
+} P1;
+#define Air1 (sizeof(P1) - Offsetof(P1, j) - 1*sizeof(uchar))
+#define Pswitcher ((P0 *)this)
+typedef struct P0 { /* switcher */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+ uchar prev_off;
+ uchar new_off;
+ uchar tmp_commit;
+ uchar size;
+} P0;
+#define Air0 (sizeof(P0) - Offsetof(P0, size) - 1*sizeof(uchar))
+typedef struct P5 { /* np_ */
+ unsigned _pid : 8; /* 0..255 */
+ unsigned _t : 4; /* proctype */
+ unsigned _p : 7; /* state */
+} P5;
+#define Air5 (sizeof(P5) - 3)
+#if defined(BFS) && defined(REACH)
+#undef REACH
+#endif
+#ifdef VERI
+#define BASE 1
+#else
+#define BASE 0
+#endif
+typedef struct Trans {
+ short atom; /* if &2 = atomic trans; if &8 local */
+#ifdef HAS_UNLESS
+ short escp[HAS_UNLESS]; /* lists the escape states */
+ short e_trans; /* if set, this is an escp-trans */
+#endif
+ short tpe[2]; /* class of operation (for reduction) */
+ short qu[6]; /* for conditional selections: qid's */
+ uchar ty[6]; /* ditto: type's */
+#ifdef NIBIS
+ short om; /* completion status of preselects */
+#endif
+ char *tp; /* src txt of statement */
+ int st; /* the nextstate */
+ int t_id; /* transition id, unique within proc */
+ int forw; /* index forward transition */
+ int back; /* index return transition */
+ struct Trans *nxt;
+} Trans;
+
+#define qptr(x) (((uchar *)&now)+(int)q_offset[x])
+#define pptr(x) (((uchar *)&now)+(int)proc_offset[x])
+extern uchar *Pptr(int);
+#define q_sz(x) (((Q0 *)qptr(x))->Qlen)
+
+#ifndef VECTORSZ
+#define VECTORSZ 1024 /* sv size in bytes */
+#endif
+
+#ifdef VERBOSE
+#ifndef CHECK
+#define CHECK
+#endif
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+#ifdef SAFETY
+#ifndef NOFAIR
+#define NOFAIR
+#endif
+#endif
+#ifdef NOREDUCE
+#ifndef XUSAFE
+#define XUSAFE
+#endif
+#if !defined(SAFETY) && !defined(MA)
+#define FULLSTACK
+#endif
+#else
+#ifdef BITSTATE
+#if defined(SAFETY) && !defined(HASH64)
+#define CNTRSTACK
+#else
+#define FULLSTACK
+#endif
+#else
+#define FULLSTACK
+#endif
+#endif
+#ifdef BITSTATE
+#ifndef NOCOMP
+#define NOCOMP
+#endif
+#if !defined(LC) && defined(SC)
+#define LC
+#endif
+#endif
+#if defined(COLLAPSE2) || defined(COLLAPSE3) || defined(COLLAPSE4)
+/* accept the above for backward compatibility */
+#define COLLAPSE
+#endif
+#ifdef HC
+#undef HC
+#define HC4
+#endif
+#ifdef HC0
+#define HC 0
+#endif
+#ifdef HC1
+#define HC 1
+#endif
+#ifdef HC2
+#define HC 2
+#endif
+#ifdef HC3
+#define HC 3
+#endif
+#ifdef HC4
+#define HC 4
+#endif
+#ifdef COLLAPSE
+#if NCORE>1 && !defined(SEP_STATE)
+unsigned long *ncomps; /* in shared memory */
+#else
+unsigned long ncomps[256+2];
+#endif
+#endif
+#define MAXQ 255
+#define MAXPROC 255
+#define WS sizeof(void *) /* word size in bytes */
+typedef struct Stack { /* for queues and processes */
+#if VECTORSZ>32000
+ int o_delta;
+ int o_offset;
+ int o_skip;
+ int o_delqs;
+#else
+ short o_delta;
+ short o_offset;
+ short o_skip;
+ short o_delqs;
+#endif
+ short o_boq;
+#ifndef XUSAFE
+ char *o_name;
+#endif
+ char *body;
+ struct Stack *nxt;
+ struct Stack *lst;
+} Stack;
+
+typedef struct Svtack { /* for complete state vector */
+#if VECTORSZ>32000
+ int o_delta;
+ int m_delta;
+#else
+ short o_delta; /* current size of frame */
+ short m_delta; /* maximum size of frame */
+#endif
+#if SYNC
+ short o_boq;
+#endif
+#define StackSize (WS)
+ char *body;
+ struct Svtack *nxt;
+ struct Svtack *lst;
+} Svtack;
+
+Trans ***trans; /* 1 ptr per state per proctype */
+
+struct H_el *Lstate;
+int depthfound = -1; /* loop detection */
+#if VECTORSZ>32000
+int proc_offset[MAXPROC];
+int q_offset[MAXQ];
+#else
+short proc_offset[MAXPROC];
+short q_offset[MAXQ];
+#endif
+uchar proc_skip[MAXPROC];
+uchar q_skip[MAXQ];
+unsigned long vsize; /* vector size in bytes */
+#ifdef SVDUMP
+int vprefix=0, svfd; /* runtime option -pN */
+#endif
+char *tprefix = "trail"; /* runtime option -tsuffix */
+short boq = -1; /* blocked_on_queue status */
+typedef struct State {
+ uchar _nr_pr;
+ uchar _nr_qs;
+ uchar _a_t; /* cycle detection */
+#ifndef NOFAIR
+ uchar _cnt[NFAIR]; /* counters, weak fairness */
+#endif
+#ifndef NOVSZ
+#if VECTORSZ<65536
+ unsigned short _vsz;
+#else
+ unsigned long _vsz;
+#endif
+#endif
+#ifdef HAS_LAST
+ uchar _last; /* pid executed in last step */
+#endif
+#ifdef EVENT_TRACE
+#if nstates_event<256
+ uchar _event;
+#else
+ unsigned short _event;
+#endif
+#endif
+ uchar buffer_use[4];
+ uchar write_off;
+ uchar commit_count[2];
+ uchar read_off;
+ uchar retrieve_count[2];
+ uchar events_lost;
+ uchar refcount;
+ uchar sv[VECTORSZ];
+} State;
+
+#define HAS_TRACK 0
+/* hidden variable: */ uchar deliver;
+int _; /* a predefined write-only variable */
+
+#define FORWARD_MOVES "pan.m"
+#define REVERSE_MOVES "pan.b"
+#define TRANSITIONS "pan.t"
+#define _NP_ 5
+uchar reached5[3]; /* np_ */
+uchar *loopstate5; /* np_ */
+#define nstates5 3 /* np_ */
+#define endstate5 2 /* np_ */
+
+#define start5 0 /* np_ */
+#define start4 42
+#define start3 8
+#define start2 28
+#define start1 3
+#define start0 11
+#ifdef NP
+ #define ACCEPT_LAB 1 /* at least 1 in np_ */
+#else
+ #define ACCEPT_LAB 0 /* user-defined accept labels */
+#endif
+#ifdef MEMCNT
+ #ifdef MEMLIM
+ #warning -DMEMLIM takes precedence over -DMEMCNT
+ #undef MEMCNT
+ #else
+ #if MEMCNT<20
+ #warning using minimal value -DMEMCNT=20 (=1MB)
+ #define MEMLIM (1)
+ #undef MEMCNT
+ #else
+ #if MEMCNT==20
+ #define MEMLIM (1)
+ #undef MEMCNT
+ #else
+ #if MEMCNT>=50
+ #error excessive value for MEMCNT
+ #else
+ #define MEMLIM (1<<(MEMCNT-20))
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+#if NCORE>1 && !defined(MEMLIM)
+ #define MEMLIM (2048) /* need a default, using 2 GB */
+#endif
+#define PROG_LAB 0 /* progress labels */
+uchar *accpstate[6];
+uchar *progstate[6];
+uchar *loopstate[6];
+uchar *reached[6];
+uchar *stopstate[6];
+uchar *visstate[6];
+short *mapstate[6];
+#ifdef HAS_CODE
+int NrStates[6];
+#endif
+#define NQS 0
+short q_flds[1];
+short q_max[1];
+typedef struct Q0 { /* generic q */
+ uchar Qlen; /* q_size */
+ uchar _t;
+} Q0;
+
+/** function prototypes **/
+char *emalloc(unsigned long);
+char *Malloc(unsigned long);
+int Boundcheck(int, int, int, int, Trans *);
+int addqueue(int, int);
+/* int atoi(char *); */
+/* int abort(void); */
+int close(int);
+int delproc(int, int);
+int endstate(void);
+int hstore(char *, int);
+#ifdef MA
+int gstore(char *, int, uchar);
+#endif
+int q_cond(short, Trans *);
+int q_full(int);
+int q_len(int);
+int q_zero(int);
+int qrecv(int, int, int, int);
+int unsend(int);
+/* void *sbrk(int); */
+void Uerror(char *);
+void assert(int, char *, int, int, Trans *);
+void c_chandump(int);
+void c_globals(void);
+void c_locals(int, int);
+void checkcycles(void);
+void crack(int, int, Trans *, short *);
+void d_sfh(const char *, int);
+void sfh(const char *, int);
+void d_hash(uchar *, int);
+void s_hash(uchar *, int);
+void r_hash(uchar *, int);
+void delq(int);
+void do_reach(void);
+void pan_exit(int);
+void exit(int);
+void hinit(void);
+void imed(Trans *, int, int, int);
+void new_state(void);
+void p_restor(int);
+void putpeg(int, int);
+void putrail(void);
+void q_restor(void);
+void retrans(int, int, int, short *, uchar *, uchar *);
+void settable(void);
+void setq_claim(int, int, char *, int, char *);
+void sv_restor(void);
+void sv_save(void);
+void tagtable(int, int, int, short *, uchar *);
+void do_dfs(int, int, int, short *, uchar *, uchar *);
+void uerror(char *);
+void unrecv(int, int, int, int, int);
+void usage(FILE *);
+void wrap_stats(void);
+#if defined(FULLSTACK) && defined(BITSTATE)
+int onstack_now(void);
+void onstack_init(void);
+void onstack_put(void);
+void onstack_zap(void);
+#endif
+#ifndef XUSAFE
+int q_S_check(int, int);
+int q_R_check(int, int);
+uchar q_claim[MAXQ+1];
+char *q_name[MAXQ+1];
+char *p_name[MAXPROC+1];
+#endif
+void qsend(int, int, int);
+#define Addproc(x) addproc(x)
+#define LOCAL 1
+#define Q_FULL_F 2
+#define Q_EMPT_F 3
+#define Q_EMPT_T 4
+#define Q_FULL_T 5
+#define TIMEOUT_F 6
+#define GLOBAL 7
+#define BAD 8
+#define ALPHA_F 9
+#define NTRANS 66
+#ifdef PEG
+long peg[NTRANS];
+#endif
--- /dev/null
+#define rand pan_rand
+#if defined(HAS_CODE) && defined(VERBOSE)
+ cpu_printf("Pr: %d Tr: %d\n", II, t->forw);
+#endif
+ switch (t->forw) {
+ default: Uerror("bad forward move");
+ case 0: /* if without executable clauses */
+ continue;
+ case 1: /* generic 'goto' or 'skip' */
+ IfNotBlocked
+ _m = 3; goto P999;
+ case 2: /* generic 'else' */
+ IfNotBlocked
+ if (trpt->o_pm&1) continue;
+ _m = 3; goto P999;
+
+ /* PROC :init: */
+ case 3: /* STATE 1 - line 225 "buffer.spin" - [i = 0] (0:0:1 - 1) */
+ IfNotBlocked
+ reached[4][1] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 4: /* STATE 2 - line 227 "buffer.spin" - [((i<2))] (8:0:3 - 1) */
+ IfNotBlocked
+ reached[4][2] = 1;
+ if (!((((int)((P4 *)this)->i)<2)))
+ continue;
+ /* merge: commit_count[i] = 0(8, 3, 8) */
+ reached[4][3] = 1;
+ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ]);
+ now.commit_count[ Index(((P4 *)this)->i, 2) ] = 0;
+#ifdef VAR_RANGES
+ logval("commit_count[:init::i]", ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ]));
+#endif
+ ;
+ /* merge: retrieve_count[i] = 0(8, 4, 8) */
+ reached[4][4] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)now.retrieve_count[ Index(((int)((P4 *)this)->i), 2) ]);
+ now.retrieve_count[ Index(((P4 *)this)->i, 2) ] = 0;
+#ifdef VAR_RANGES
+ logval("retrieve_count[:init::i]", ((int)now.retrieve_count[ Index(((int)((P4 *)this)->i), 2) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(8, 5, 8) */
+ reached[4][5] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = (((int)((P4 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 9, 8) */
+ reached[4][9] = 1;
+ ;
+ _m = 3; goto P999; /* 4 */
+ case 5: /* STATE 6 - line 231 "buffer.spin" - [((i>=2))] (17:0:2 - 1) */
+ IfNotBlocked
+ reached[4][6] = 1;
+ if (!((((int)((P4 *)this)->i)>=2)))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((P4 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P4 *)this)->i = 0;
+ /* merge: goto :b6(17, 7, 17) */
+ reached[4][7] = 1;
+ ;
+ /* merge: i = 0(17, 11, 17) */
+ reached[4][11] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 18, 17) */
+ reached[4][18] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 6: /* STATE 11 - line 233 "buffer.spin" - [i = 0] (0:17:1 - 3) */
+ IfNotBlocked
+ reached[4][11] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 18, 17) */
+ reached[4][18] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 7: /* STATE 12 - line 235 "buffer.spin" - [((i<4))] (17:0:2 - 1) */
+ IfNotBlocked
+ reached[4][12] = 1;
+ if (!((((int)((P4 *)this)->i)<4)))
+ continue;
+ /* merge: buffer_use[i] = 0(17, 13, 17) */
+ reached[4][13] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 4) ]);
+ now.buffer_use[ Index(((P4 *)this)->i, 4) ] = 0;
+#ifdef VAR_RANGES
+ logval("buffer_use[:init::i]", ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 4) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(17, 14, 17) */
+ reached[4][14] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = (((int)((P4 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 18, 17) */
+ reached[4][18] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 8: /* STATE 15 - line 238 "buffer.spin" - [((i>=4))] (0:0:1 - 1) */
+ IfNotBlocked
+ reached[4][15] = 1;
+ if (!((((int)((P4 *)this)->i)>=4)))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P4 *)this)->i = 0;
+ _m = 3; goto P999; /* 0 */
+ case 9: /* STATE 20 - line 240 "buffer.spin" - [(run reader())] (0:0:0 - 3) */
+ IfNotBlocked
+ reached[4][20] = 1;
+ if (!(addproc(2)))
+ continue;
+ _m = 3; goto P999; /* 0 */
+ case 10: /* STATE 21 - line 241 "buffer.spin" - [(run cleaner())] (29:0:1 - 1) */
+ IfNotBlocked
+ reached[4][21] = 1;
+ if (!(addproc(3)))
+ continue;
+ /* merge: i = 0(0, 22, 29) */
+ reached[4][22] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 30, 29) */
+ reached[4][30] = 1;
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 11: /* STATE 23 - line 244 "buffer.spin" - [((i<4))] (25:0:1 - 1) */
+ IfNotBlocked
+ reached[4][23] = 1;
+ if (!((((int)((P4 *)this)->i)<4)))
+ continue;
+ /* merge: refcount = (refcount+1)(0, 24, 25) */
+ reached[4][24] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)+1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 12: /* STATE 25 - line 246 "buffer.spin" - [(run tracer())] (29:0:1 - 1) */
+ IfNotBlocked
+ reached[4][25] = 1;
+ if (!(addproc(1)))
+ continue;
+ /* merge: i = (i+1)(0, 26, 29) */
+ reached[4][26] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = (((int)((P4 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 30, 29) */
+ reached[4][30] = 1;
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 13: /* STATE 27 - line 248 "buffer.spin" - [((i>=4))] (39:0:2 - 1) */
+ IfNotBlocked
+ reached[4][27] = 1;
+ if (!((((int)((P4 *)this)->i)>=4)))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((P4 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P4 *)this)->i = 0;
+ /* merge: goto :b8(39, 28, 39) */
+ reached[4][28] = 1;
+ ;
+ /* merge: i = 0(39, 32, 39) */
+ reached[4][32] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 40, 39) */
+ reached[4][40] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 14: /* STATE 32 - line 250 "buffer.spin" - [i = 0] (0:39:1 - 3) */
+ IfNotBlocked
+ reached[4][32] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 40, 39) */
+ reached[4][40] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 15: /* STATE 33 - line 252 "buffer.spin" - [((i<1))] (35:0:1 - 1) */
+ IfNotBlocked
+ reached[4][33] = 1;
+ if (!((((int)((P4 *)this)->i)<1)))
+ continue;
+ /* merge: refcount = (refcount+1)(0, 34, 35) */
+ reached[4][34] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)+1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 16: /* STATE 35 - line 254 "buffer.spin" - [(run switcher())] (39:0:1 - 1) */
+ IfNotBlocked
+ reached[4][35] = 1;
+ if (!(addproc(0)))
+ continue;
+ /* merge: i = (i+1)(0, 36, 39) */
+ reached[4][36] = 1;
+ (trpt+1)->bup.oval = ((int)((P4 *)this)->i);
+ ((P4 *)this)->i = (((int)((P4 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval(":init::i", ((int)((P4 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 40, 39) */
+ reached[4][40] = 1;
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 17: /* STATE 37 - line 256 "buffer.spin" - [((i>=1))] (41:0:1 - 1) */
+ IfNotBlocked
+ reached[4][37] = 1;
+ if (!((((int)((P4 *)this)->i)>=1)))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P4 *)this)->i = 0;
+ /* merge: goto :b9(0, 38, 41) */
+ reached[4][38] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 18: /* STATE 43 - line 262 "buffer.spin" - [assert((((write_off-read_off)>=0)&&((write_off-read_off)<(255/2))))] (0:52:2 - 1) */
+ IfNotBlocked
+ reached[4][43] = 1;
+ assert((((((int)now.write_off)-((int)now.read_off))>=0)&&((((int)now.write_off)-((int)now.read_off))<(255/2))), "(((write_off-read_off)>=0)&&((write_off-read_off)<(255/2)))", II, tt, t);
+ /* merge: j = 0(52, 44, 52) */
+ reached[4][44] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)((P4 *)this)->j);
+ ((P4 *)this)->j = 0;
+#ifdef VAR_RANGES
+ logval(":init::j", ((int)((P4 *)this)->j));
+#endif
+ ;
+ /* merge: commit_sum = 0(52, 45, 52) */
+ reached[4][45] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->commit_sum);
+ ((P4 *)this)->commit_sum = 0;
+#ifdef VAR_RANGES
+ logval(":init::commit_sum", ((int)((P4 *)this)->commit_sum));
+#endif
+ ;
+ /* merge: .(goto)(0, 53, 52) */
+ reached[4][53] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 19: /* STATE 46 - line 266 "buffer.spin" - [((j<2))] (52:0:2 - 1) */
+ IfNotBlocked
+ reached[4][46] = 1;
+ if (!((((int)((P4 *)this)->j)<2)))
+ continue;
+ /* merge: commit_sum = (commit_sum+commit_count[j])(52, 47, 52) */
+ reached[4][47] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)((P4 *)this)->commit_sum);
+ ((P4 *)this)->commit_sum = (((int)((P4 *)this)->commit_sum)+((int)now.commit_count[ Index(((int)((P4 *)this)->j), 2) ]));
+#ifdef VAR_RANGES
+ logval(":init::commit_sum", ((int)((P4 *)this)->commit_sum));
+#endif
+ ;
+ /* merge: assert((((commit_count[j]-retrieve_count[j])>=0)&&((commit_count[j]-retrieve_count[j])<(255/2))))(52, 48, 52) */
+ reached[4][48] = 1;
+ assert((((((int)now.commit_count[ Index(((int)((P4 *)this)->j), 2) ])-((int)now.retrieve_count[ Index(((int)((P4 *)this)->j), 2) ]))>=0)&&((((int)now.commit_count[ Index(((int)((P4 *)this)->j), 2) ])-((int)now.retrieve_count[ Index(((int)((P4 *)this)->j), 2) ]))<(255/2))), "(((commit_count[j]-retrieve_count[j])>=0)&&((commit_count[j]-retrieve_count[j])<(255/2)))", II, tt, t);
+ /* merge: j = (j+1)(52, 49, 52) */
+ reached[4][49] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->j);
+ ((P4 *)this)->j = (((int)((P4 *)this)->j)+1);
+#ifdef VAR_RANGES
+ logval(":init::j", ((int)((P4 *)this)->j));
+#endif
+ ;
+ /* merge: .(goto)(0, 53, 52) */
+ reached[4][53] = 1;
+ ;
+ _m = 3; goto P999; /* 4 */
+ case 20: /* STATE 50 - line 273 "buffer.spin" - [((j>=2))] (58:0:1 - 1) */
+ IfNotBlocked
+ reached[4][50] = 1;
+ if (!((((int)((P4 *)this)->j)>=2)))
+ continue;
+ /* dead 1: j */ (trpt+1)->bup.oval = ((P4 *)this)->j;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P4 *)this)->j = 0;
+ /* merge: goto :b10(58, 51, 58) */
+ reached[4][51] = 1;
+ ;
+ /* merge: assert((((write_off-commit_sum)>=0)&&((write_off-commit_sum)<(255/2))))(58, 55, 58) */
+ reached[4][55] = 1;
+ assert((((((int)now.write_off)-((int)((P4 *)this)->commit_sum))>=0)&&((((int)now.write_off)-((int)((P4 *)this)->commit_sum))<(255/2))), "(((write_off-commit_sum)>=0)&&((write_off-commit_sum)<(255/2)))", II, tt, t);
+ /* merge: assert((((4+1)>4)||(events_lost==0)))(58, 56, 58) */
+ reached[4][56] = 1;
+ assert((((4+1)>4)||(((int)now.events_lost)==0)), "(((4+1)>4)||(events_lost==0))", II, tt, t);
+ _m = 3; goto P999; /* 3 */
+ case 21: /* STATE 55 - line 278 "buffer.spin" - [assert((((write_off-commit_sum)>=0)&&((write_off-commit_sum)<(255/2))))] (0:58:0 - 3) */
+ IfNotBlocked
+ reached[4][55] = 1;
+ assert((((((int)now.write_off)-((int)((P4 *)this)->commit_sum))>=0)&&((((int)now.write_off)-((int)((P4 *)this)->commit_sum))<(255/2))), "(((write_off-commit_sum)>=0)&&((write_off-commit_sum)<(255/2)))", II, tt, t);
+ /* merge: assert((((4+1)>4)||(events_lost==0)))(58, 56, 58) */
+ reached[4][56] = 1;
+ assert((((4+1)>4)||(((int)now.events_lost)==0)), "(((4+1)>4)||(events_lost==0))", II, tt, t);
+ _m = 3; goto P999; /* 1 */
+ case 22: /* STATE 58 - line 284 "buffer.spin" - [-end-] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[4][58] = 1;
+ if (!delproc(1, II)) continue;
+ _m = 3; goto P999; /* 0 */
+
+ /* PROC cleaner */
+ case 23: /* STATE 1 - line 210 "buffer.spin" - [((refcount==0))] (3:0:1 - 1) */
+ IfNotBlocked
+ reached[3][1] = 1;
+ if (!((((int)now.refcount)==0)))
+ continue;
+ /* merge: refcount = (refcount+1)(0, 2, 3) */
+ reached[3][2] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)+1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 24: /* STATE 3 - line 212 "buffer.spin" - [(run switcher())] (7:0:0 - 1) */
+ IfNotBlocked
+ reached[3][3] = 1;
+ if (!(addproc(0)))
+ continue;
+ /* merge: goto :b5(0, 4, 7) */
+ reached[3][4] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 25: /* STATE 9 - line 216 "buffer.spin" - [-end-] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[3][9] = 1;
+ if (!delproc(1, II)) continue;
+ _m = 3; goto P999; /* 0 */
+
+ /* PROC reader */
+ case 26: /* STATE 1 - line 169 "buffer.spin" - [((((((write_off/(4/2))-(read_off/(4/2)))>0)&&(((write_off/(4/2))-(read_off/(4/2)))<(255/2)))&&((commit_count[((read_off%4)/(4/2))]-retrieve_count[((read_off%4)/(4/2))])==(4/2))))] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[2][1] = 1;
+ if (!((((((((int)now.write_off)/(4/2))-(((int)now.read_off)/(4/2)))>0)&&(((((int)now.write_off)/(4/2))-(((int)now.read_off)/(4/2)))<(255/2)))&&((((int)now.commit_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ])-((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ]))==(4/2)))))
+ continue;
+ _m = 3; goto P999; /* 0 */
+ case 27: /* STATE 2 - line 171 "buffer.spin" - [i = 0] (0:0:1 - 1) */
+ IfNotBlocked
+ reached[2][2] = 1;
+ (trpt+1)->bup.oval = ((int)((P2 *)this)->i);
+ ((P2 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval("reader:i", ((int)((P2 *)this)->i));
+#endif
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 28: /* STATE 3 - line 173 "buffer.spin" - [((i<(4/2)))] (9:0:2 - 1) */
+ IfNotBlocked
+ reached[2][3] = 1;
+ if (!((((int)((P2 *)this)->i)<(4/2))))
+ continue;
+ /* merge: assert((buffer_use[((read_off+i)%4)]==0))(9, 4, 9) */
+ reached[2][4] = 1;
+ assert((((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ])==0), "(buffer_use[((read_off+i)%4)]==0)", II, tt, t);
+ /* merge: buffer_use[((read_off+i)%4)] = 1(9, 5, 9) */
+ reached[2][5] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]);
+ now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = 1;
+#ifdef VAR_RANGES
+ logval("buffer_use[((read_off+reader:i)%4)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(9, 6, 9) */
+ reached[2][6] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i);
+ ((P2 *)this)->i = (((int)((P2 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval("reader:i", ((int)((P2 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 10, 9) */
+ reached[2][10] = 1;
+ ;
+ _m = 3; goto P999; /* 4 */
+ case 29: /* STATE 7 - line 177 "buffer.spin" - [((i>=(4/2)))] (11:0:1 - 1) */
+ IfNotBlocked
+ reached[2][7] = 1;
+ if (!((((int)((P2 *)this)->i)>=(4/2))))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.oval = ((P2 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P2 *)this)->i = 0;
+ /* merge: goto :b3(0, 8, 11) */
+ reached[2][8] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+/* STATE 13 - line 187 "buffer.spin" - [i = 0] (0:0 - 1) same as 27 (0:0 - 1) */
+ case 30: /* STATE 14 - line 189 "buffer.spin" - [((i<(4/2)))] (19:0:2 - 1) */
+ IfNotBlocked
+ reached[2][14] = 1;
+ if (!((((int)((P2 *)this)->i)<(4/2))))
+ continue;
+ /* merge: buffer_use[((read_off+i)%4)] = 0(19, 15, 19) */
+ reached[2][15] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]);
+ now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = 0;
+#ifdef VAR_RANGES
+ logval("buffer_use[((read_off+reader:i)%4)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(19, 16, 19) */
+ reached[2][16] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i);
+ ((P2 *)this)->i = (((int)((P2 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval("reader:i", ((int)((P2 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 20, 19) */
+ reached[2][20] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 31: /* STATE 17 - line 192 "buffer.spin" - [((i>=(4/2)))] (28:0:4 - 1) */
+ IfNotBlocked
+ reached[2][17] = 1;
+ if (!((((int)((P2 *)this)->i)>=(4/2))))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(4);
+ (trpt+1)->bup.ovals[0] = ((P2 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P2 *)this)->i = 0;
+ /* merge: goto :b4(28, 18, 28) */
+ reached[2][18] = 1;
+ ;
+ /* merge: tmp_retrieve = (retrieve_count[((read_off%4)/(4/2))]+(4/2))(28, 22, 28) */
+ reached[2][22] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->tmp_retrieve);
+ ((P2 *)this)->tmp_retrieve = (((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ])+(4/2));
+#ifdef VAR_RANGES
+ logval("reader:tmp_retrieve", ((int)((P2 *)this)->tmp_retrieve));
+#endif
+ ;
+ /* merge: retrieve_count[((read_off%4)/(4/2))] = tmp_retrieve(28, 23, 28) */
+ reached[2][23] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ]);
+ now.retrieve_count[ Index(((now.read_off%4)/(4/2)), 2) ] = ((int)((P2 *)this)->tmp_retrieve);
+#ifdef VAR_RANGES
+ logval("retrieve_count[((read_off%4)/(4/2))]", ((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ]));
+#endif
+ ;
+ /* merge: read_off = (read_off+(4/2))(28, 24, 28) */
+ reached[2][24] = 1;
+ (trpt+1)->bup.ovals[3] = ((int)now.read_off);
+ now.read_off = (((int)now.read_off)+(4/2));
+#ifdef VAR_RANGES
+ logval("read_off", ((int)now.read_off));
+#endif
+ ;
+ /* merge: .(goto)(0, 29, 28) */
+ reached[2][29] = 1;
+ ;
+ _m = 3; goto P999; /* 5 */
+ case 32: /* STATE 22 - line 194 "buffer.spin" - [tmp_retrieve = (retrieve_count[((read_off%4)/(4/2))]+(4/2))] (0:28:3 - 3) */
+ IfNotBlocked
+ reached[2][22] = 1;
+ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((int)((P2 *)this)->tmp_retrieve);
+ ((P2 *)this)->tmp_retrieve = (((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ])+(4/2));
+#ifdef VAR_RANGES
+ logval("reader:tmp_retrieve", ((int)((P2 *)this)->tmp_retrieve));
+#endif
+ ;
+ /* merge: retrieve_count[((read_off%4)/(4/2))] = tmp_retrieve(28, 23, 28) */
+ reached[2][23] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ]);
+ now.retrieve_count[ Index(((now.read_off%4)/(4/2)), 2) ] = ((int)((P2 *)this)->tmp_retrieve);
+#ifdef VAR_RANGES
+ logval("retrieve_count[((read_off%4)/(4/2))]", ((int)now.retrieve_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ]));
+#endif
+ ;
+ /* merge: read_off = (read_off+(4/2))(28, 24, 28) */
+ reached[2][24] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)now.read_off);
+ now.read_off = (((int)now.read_off)+(4/2));
+#ifdef VAR_RANGES
+ logval("read_off", ((int)now.read_off));
+#endif
+ ;
+ /* merge: .(goto)(0, 29, 28) */
+ reached[2][29] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 33: /* STATE 26 - line 199 "buffer.spin" - [((read_off>=(4-events_lost)))] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[2][26] = 1;
+ if (!((((int)now.read_off)>=(4-((int)now.events_lost)))))
+ continue;
+ _m = 3; goto P999; /* 0 */
+ case 34: /* STATE 31 - line 201 "buffer.spin" - [-end-] (0:0:0 - 3) */
+ IfNotBlocked
+ reached[2][31] = 1;
+ if (!delproc(1, II)) continue;
+ _m = 3; goto P999; /* 0 */
+
+ /* PROC tracer */
+ case 35: /* STATE 1 - line 99 "buffer.spin" - [prev_off = write_off] (0:10:2 - 1) */
+ IfNotBlocked
+ reached[1][1] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->prev_off);
+ ((P1 *)this)->prev_off = ((int)now.write_off);
+#ifdef VAR_RANGES
+ logval("tracer:prev_off", ((int)((P1 *)this)->prev_off));
+#endif
+ ;
+ /* merge: new_off = (prev_off+size)(10, 2, 10) */
+ reached[1][2] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->new_off);
+ ((P1 *)this)->new_off = (((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->size));
+#ifdef VAR_RANGES
+ logval("tracer:new_off", ((int)((P1 *)this)->new_off));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 36: /* STATE 4 - line 104 "buffer.spin" - [((((new_off-read_off)>4)&&((new_off-read_off)<(255/2))))] (0:0:1 - 1) */
+ IfNotBlocked
+ reached[1][4] = 1;
+ if (!((((((int)((P1 *)this)->new_off)-((int)now.read_off))>4)&&((((int)((P1 *)this)->new_off)-((int)now.read_off))<(255/2)))))
+ continue;
+ /* dead 1: new_off */ (trpt+1)->bup.oval = ((P1 *)this)->new_off;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P1 *)this)->new_off = 0;
+ _m = 3; goto P999; /* 0 */
+ case 37: /* STATE 7 - line 106 "buffer.spin" - [(1)] (27:0:0 - 1) */
+ IfNotBlocked
+ reached[1][7] = 1;
+ if (!(1))
+ continue;
+ /* merge: .(goto)(0, 9, 27) */
+ reached[1][9] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 38: /* STATE 11 - line 111 "buffer.spin" - [((prev_off!=write_off))] (3:0:1 - 1) */
+ IfNotBlocked
+ reached[1][11] = 1;
+ if (!((((int)((P1 *)this)->prev_off)!=((int)now.write_off))))
+ continue;
+ /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P1 *)this)->prev_off;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P1 *)this)->prev_off = 0;
+ /* merge: goto cmpxchg_loop(0, 12, 3) */
+ reached[1][12] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 39: /* STATE 14 - line 112 "buffer.spin" - [write_off = new_off] (0:24:2 - 1) */
+ IfNotBlocked
+ reached[1][14] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.write_off);
+ now.write_off = ((int)((P1 *)this)->new_off);
+#ifdef VAR_RANGES
+ logval("write_off", ((int)now.write_off));
+#endif
+ ;
+ /* merge: .(goto)(24, 16, 24) */
+ reached[1][16] = 1;
+ ;
+ /* merge: i = 0(24, 17, 24) */
+ reached[1][17] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i);
+ ((P1 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval("tracer:i", ((int)((P1 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 25, 24) */
+ reached[1][25] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 40: /* STATE 17 - line 114 "buffer.spin" - [i = 0] (0:24:1 - 2) */
+ IfNotBlocked
+ reached[1][17] = 1;
+ (trpt+1)->bup.oval = ((int)((P1 *)this)->i);
+ ((P1 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval("tracer:i", ((int)((P1 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 25, 24) */
+ reached[1][25] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 41: /* STATE 18 - line 116 "buffer.spin" - [((i<size))] (24:0:2 - 1) */
+ IfNotBlocked
+ reached[1][18] = 1;
+ if (!((((int)((P1 *)this)->i)<((int)((P1 *)this)->size))))
+ continue;
+ /* merge: assert((buffer_use[((prev_off+i)%4)]==0))(24, 19, 24) */
+ reached[1][19] = 1;
+ assert((((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ])==0), "(buffer_use[((prev_off+i)%4)]==0)", II, tt, t);
+ /* merge: buffer_use[((prev_off+i)%4)] = 1(24, 20, 24) */
+ reached[1][20] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]);
+ now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = 1;
+#ifdef VAR_RANGES
+ logval("buffer_use[((tracer:prev_off+tracer:i)%4)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(24, 21, 24) */
+ reached[1][21] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i);
+ ((P1 *)this)->i = (((int)((P1 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval("tracer:i", ((int)((P1 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 25, 24) */
+ reached[1][25] = 1;
+ ;
+ _m = 3; goto P999; /* 4 */
+ case 42: /* STATE 22 - line 120 "buffer.spin" - [((i>=size))] (26:0:1 - 1) */
+ IfNotBlocked
+ reached[1][22] = 1;
+ if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size))))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.oval = ((P1 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P1 *)this)->i = 0;
+ /* merge: goto :b0(0, 23, 26) */
+ reached[1][23] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 43: /* STATE 28 - line 127 "buffer.spin" - [i = 0] (0:0:1 - 1) */
+ IfNotBlocked
+ reached[1][28] = 1;
+ (trpt+1)->bup.oval = ((int)((P1 *)this)->i);
+ ((P1 *)this)->i = 0;
+#ifdef VAR_RANGES
+ logval("tracer:i", ((int)((P1 *)this)->i));
+#endif
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 44: /* STATE 29 - line 129 "buffer.spin" - [((i<size))] (34:0:2 - 1) */
+ IfNotBlocked
+ reached[1][29] = 1;
+ if (!((((int)((P1 *)this)->i)<((int)((P1 *)this)->size))))
+ continue;
+ /* merge: buffer_use[((prev_off+i)%4)] = 0(34, 30, 34) */
+ reached[1][30] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]);
+ now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = 0;
+#ifdef VAR_RANGES
+ logval("buffer_use[((tracer:prev_off+tracer:i)%4)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]));
+#endif
+ ;
+ /* merge: i = (i+1)(34, 31, 34) */
+ reached[1][31] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i);
+ ((P1 *)this)->i = (((int)((P1 *)this)->i)+1);
+#ifdef VAR_RANGES
+ logval("tracer:i", ((int)((P1 *)this)->i));
+#endif
+ ;
+ /* merge: .(goto)(0, 35, 34) */
+ reached[1][35] = 1;
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 45: /* STATE 32 - line 132 "buffer.spin" - [((i>=size))] (43:0:3 - 1) */
+ IfNotBlocked
+ reached[1][32] = 1;
+ if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size))))
+ continue;
+ /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((P1 *)this)->i;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P1 *)this)->i = 0;
+ /* merge: goto :b1(43, 33, 43) */
+ reached[1][33] = 1;
+ ;
+ /* merge: tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)(43, 37, 43) */
+ reached[1][37] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->tmp_commit);
+ ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P1 *)this)->size));
+#ifdef VAR_RANGES
+ logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit));
+#endif
+ ;
+ /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(43, 38, 43) */
+ reached[1][38] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]);
+ now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P1 *)this)->tmp_commit);
+#ifdef VAR_RANGES
+ logval("commit_count[((tracer:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]));
+#endif
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 46: /* STATE 37 - line 134 "buffer.spin" - [tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)] (0:43:2 - 3) */
+ IfNotBlocked
+ reached[1][37] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->tmp_commit);
+ ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P1 *)this)->size));
+#ifdef VAR_RANGES
+ logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit));
+#endif
+ ;
+ /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(43, 38, 43) */
+ reached[1][38] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]);
+ now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P1 *)this)->tmp_commit);
+#ifdef VAR_RANGES
+ logval("commit_count[((tracer:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 47: /* STATE 39 - line 137 "buffer.spin" - [(((tmp_commit%(4/2))==0))] (49:0:2 - 1) */
+ IfNotBlocked
+ reached[1][39] = 1;
+ if (!(((((int)((P1 *)this)->tmp_commit)%(4/2))==0)))
+ continue;
+ /* dead 1: tmp_commit */ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((P1 *)this)->tmp_commit;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P1 *)this)->tmp_commit = 0;
+ /* merge: deliver = 1(49, 40, 49) */
+ reached[1][40] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)deliver);
+ deliver = 1;
+#ifdef VAR_RANGES
+ logval("deliver", ((int)deliver));
+#endif
+ ;
+ /* merge: .(goto)(49, 44, 49) */
+ reached[1][44] = 1;
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 48: /* STATE 44 - line 140 "buffer.spin" - [.(goto)] (0:49:0 - 2) */
+ IfNotBlocked
+ reached[1][44] = 1;
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 49: /* STATE 42 - line 138 "buffer.spin" - [(1)] (49:0:0 - 1) */
+ IfNotBlocked
+ reached[1][42] = 1;
+ if (!(1))
+ continue;
+ /* merge: .(goto)(49, 44, 49) */
+ reached[1][44] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 50: /* STATE 47 - line 144 "buffer.spin" - [events_lost = (events_lost+1)] (0:0:1 - 2) */
+ IfNotBlocked
+ reached[1][47] = 1;
+ (trpt+1)->bup.oval = ((int)now.events_lost);
+ now.events_lost = (((int)now.events_lost)+1);
+#ifdef VAR_RANGES
+ logval("events_lost", ((int)now.events_lost));
+#endif
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 51: /* STATE 48 - line 146 "buffer.spin" - [refcount = (refcount-1)] (0:0:1 - 2) */
+ IfNotBlocked
+ reached[1][48] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)-1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 52: /* STATE 50 - line 148 "buffer.spin" - [-end-] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[1][50] = 1;
+ if (!delproc(1, II)) continue;
+ _m = 3; goto P999; /* 0 */
+
+ /* PROC switcher */
+ case 53: /* STATE 1 - line 56 "buffer.spin" - [prev_off = write_off] (0:9:3 - 1) */
+ IfNotBlocked
+ reached[0][1] = 1;
+ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->prev_off);
+ ((P0 *)this)->prev_off = ((int)now.write_off);
+#ifdef VAR_RANGES
+ logval("switcher:prev_off", ((int)((P0 *)this)->prev_off));
+#endif
+ ;
+ /* merge: size = ((4/2)-(prev_off%(4/2)))(9, 2, 9) */
+ reached[0][2] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)((P0 *)this)->size);
+ ((P0 *)this)->size = ((4/2)-(((int)((P0 *)this)->prev_off)%(4/2)));
+#ifdef VAR_RANGES
+ logval("switcher:size", ((int)((P0 *)this)->size));
+#endif
+ ;
+ /* merge: new_off = (prev_off+size)(9, 3, 9) */
+ reached[0][3] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)((P0 *)this)->new_off);
+ ((P0 *)this)->new_off = (((int)((P0 *)this)->prev_off)+((int)((P0 *)this)->size));
+#ifdef VAR_RANGES
+ logval("switcher:new_off", ((int)((P0 *)this)->new_off));
+#endif
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 54: /* STATE 4 - line 61 "buffer.spin" - [(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))] (29:0:3 - 1) */
+ IfNotBlocked
+ reached[0][4] = 1;
+ if (!(((((((int)((P0 *)this)->new_off)-((int)now.read_off))>4)&&((((int)((P0 *)this)->new_off)-((int)now.read_off))<(255/2)))||(((int)((P0 *)this)->size)==(4/2)))))
+ continue;
+ /* dead 1: new_off */ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((P0 *)this)->new_off;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P0 *)this)->new_off = 0;
+ /* dead 1: size */ (trpt+1)->bup.ovals[1] = ((P0 *)this)->size;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P0 *)this)->size = 0;
+ /* merge: refcount = (refcount-1)(29, 5, 29) */
+ reached[0][5] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)-1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ /* merge: goto not_needed(29, 6, 29) */
+ reached[0][6] = 1;
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 55: /* STATE 8 - line 64 "buffer.spin" - [(1)] (18:0:0 - 1) */
+ IfNotBlocked
+ reached[0][8] = 1;
+ if (!(1))
+ continue;
+ /* merge: .(goto)(0, 10, 18) */
+ reached[0][10] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 56: /* STATE 12 - line 69 "buffer.spin" - [((prev_off!=write_off))] (11:0:1 - 1) */
+ IfNotBlocked
+ reached[0][12] = 1;
+ if (!((((int)((P0 *)this)->prev_off)!=((int)now.write_off))))
+ continue;
+ /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P0 *)this)->prev_off;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P0 *)this)->prev_off = 0;
+ /* merge: goto cmpxchg_loop(0, 13, 11) */
+ reached[0][13] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 57: /* STATE 17 - line 72 "buffer.spin" - [.(goto)] (0:28:0 - 1) */
+ IfNotBlocked
+ reached[0][17] = 1;
+ ;
+ _m = 3; goto P999; /* 0 */
+ case 58: /* STATE 15 - line 70 "buffer.spin" - [write_off = new_off] (0:28:1 - 1) */
+ IfNotBlocked
+ reached[0][15] = 1;
+ (trpt+1)->bup.oval = ((int)now.write_off);
+ now.write_off = ((int)((P0 *)this)->new_off);
+#ifdef VAR_RANGES
+ logval("write_off", ((int)now.write_off));
+#endif
+ ;
+ /* merge: .(goto)(28, 17, 28) */
+ reached[0][17] = 1;
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 59: /* STATE 19 - line 75 "buffer.spin" - [tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)] (0:25:2 - 1) */
+ IfNotBlocked
+ reached[0][19] = 1;
+ (trpt+1)->bup.ovals = grab_ints(2);
+ (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->tmp_commit);
+ ((P0 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P0 *)this)->size));
+#ifdef VAR_RANGES
+ logval("switcher:tmp_commit", ((int)((P0 *)this)->tmp_commit));
+#endif
+ ;
+ /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(25, 20, 25) */
+ reached[0][20] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ]);
+ now.commit_count[ Index(((((P0 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P0 *)this)->tmp_commit);
+#ifdef VAR_RANGES
+ logval("commit_count[((switcher:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ]));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 60: /* STATE 21 - line 78 "buffer.spin" - [(((tmp_commit%(4/2))==0))] (29:0:3 - 1) */
+ IfNotBlocked
+ reached[0][21] = 1;
+ if (!(((((int)((P0 *)this)->tmp_commit)%(4/2))==0)))
+ continue;
+ /* dead 1: tmp_commit */ (trpt+1)->bup.ovals = grab_ints(3);
+ (trpt+1)->bup.ovals[0] = ((P0 *)this)->tmp_commit;
+#ifdef HAS_CODE
+ if (!readtrail)
+#endif
+ ((P0 *)this)->tmp_commit = 0;
+ /* merge: deliver = 1(29, 22, 29) */
+ reached[0][22] = 1;
+ (trpt+1)->bup.ovals[1] = ((int)deliver);
+ deliver = 1;
+#ifdef VAR_RANGES
+ logval("deliver", ((int)deliver));
+#endif
+ ;
+ /* merge: .(goto)(29, 26, 29) */
+ reached[0][26] = 1;
+ ;
+ /* merge: refcount = (refcount-1)(29, 27, 29) */
+ reached[0][27] = 1;
+ (trpt+1)->bup.ovals[2] = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)-1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 3 */
+ case 61: /* STATE 26 - line 81 "buffer.spin" - [.(goto)] (0:29:1 - 2) */
+ IfNotBlocked
+ reached[0][26] = 1;
+ ;
+ /* merge: refcount = (refcount-1)(29, 27, 29) */
+ reached[0][27] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)-1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 1 */
+ case 62: /* STATE 24 - line 79 "buffer.spin" - [(1)] (29:0:1 - 1) */
+ IfNotBlocked
+ reached[0][24] = 1;
+ if (!(1))
+ continue;
+ /* merge: .(goto)(29, 26, 29) */
+ reached[0][26] = 1;
+ ;
+ /* merge: refcount = (refcount-1)(29, 27, 29) */
+ reached[0][27] = 1;
+ (trpt+1)->bup.oval = ((int)now.refcount);
+ now.refcount = (((int)now.refcount)-1);
+#ifdef VAR_RANGES
+ logval("refcount", ((int)now.refcount));
+#endif
+ ;
+ _m = 3; goto P999; /* 2 */
+ case 63: /* STATE 30 - line 85 "buffer.spin" - [-end-] (0:0:0 - 1) */
+ IfNotBlocked
+ reached[0][30] = 1;
+ if (!delproc(1, II)) continue;
+ _m = 3; goto P999; /* 0 */
+ case _T5: /* np_ */
+ if (!((!(trpt->o_pm&4) && !(trpt->tau&128))))
+ continue;
+ /* else fall through */
+ case _T2: /* true */
+ _m = 3; goto P999;
+#undef rand
+ }
+
--- /dev/null
+#ifdef PEG
+struct T_SRC {
+ char *fl; int ln;
+} T_SRC[NTRANS];
+
+void
+tr_2_src(int m, char *file, int ln)
+{ T_SRC[m].fl = file;
+ T_SRC[m].ln = ln;
+}
+
+void
+putpeg(int n, int m)
+{ printf("%5d trans %4d ", m, n);
+ printf("file %s line %3d\n",
+ T_SRC[n].fl, T_SRC[n].ln);
+}
+#endif
+
+void
+settable(void)
+{ Trans *T;
+ Trans *settr(int, int, int, int, int, char *, int, int, int);
+
+ trans = (Trans ***) emalloc(6*sizeof(Trans **));
+
+ /* proctype 4: :init: */
+
+ trans[4] = (Trans **) emalloc(59*sizeof(Trans *));
+
+ T = trans[ 4][42] = settr(161,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(161,2,1,0,0,"ATOMIC", 1, 2, 0);
+ trans[4][1] = settr(120,2,8,3,3,"i = 0", 1, 2, 0);
+ trans[4][9] = settr(128,2,8,1,0,".(goto)", 1, 2, 0);
+ T = trans[4][8] = settr(127,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(127,2,2,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(127,2,6,0,0,"DO", 1, 2, 0);
+ trans[4][2] = settr(121,2,8,4,4,"((i<2))", 1, 2, 0); /* m: 3 -> 8,0 */
+ reached4[3] = 1;
+ trans[4][3] = settr(0,0,0,0,0,"commit_count[i] = 0",0,0,0);
+ trans[4][4] = settr(0,0,0,0,0,"retrieve_count[i] = 0",0,0,0);
+ trans[4][5] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[4][6] = settr(125,2,17,5,5,"((i>=2))", 1, 2, 0); /* m: 11 -> 17,0 */
+ reached4[11] = 1;
+ trans[4][7] = settr(126,2,11,1,0,"goto :b6", 1, 2, 0); /* m: 11 -> 0,17 */
+ reached4[11] = 1;
+ trans[4][10] = settr(129,2,11,1,0,"break", 1, 2, 0);
+ trans[4][11] = settr(130,2,17,6,6,"i = 0", 1, 2, 0);
+ trans[4][18] = settr(137,2,17,1,0,".(goto)", 1, 2, 0);
+ T = trans[4][17] = settr(136,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(136,2,12,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(136,2,15,0,0,"DO", 1, 2, 0);
+ trans[4][12] = settr(131,2,17,7,7,"((i<4))", 1, 2, 0); /* m: 13 -> 17,0 */
+ reached4[13] = 1;
+ trans[4][13] = settr(0,0,0,0,0,"buffer_use[i] = 0",0,0,0);
+ trans[4][14] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[4][15] = settr(134,2,20,8,8,"((i>=4))", 1, 2, 0);
+ trans[4][16] = settr(135,2,20,1,0,"goto :b7", 1, 2, 0);
+ trans[4][19] = settr(138,2,20,1,0,"break", 1, 2, 0);
+ trans[4][20] = settr(139,2,21,9,9,"(run reader())", 1, 2, 0);
+ trans[4][21] = settr(140,2,29,10,10,"(run cleaner())", 1, 2, 0); /* m: 22 -> 29,0 */
+ reached4[22] = 1;
+ trans[4][22] = settr(0,0,0,0,0,"i = 0",0,0,0);
+ trans[4][30] = settr(149,2,29,1,0,".(goto)", 1, 2, 0);
+ T = trans[4][29] = settr(148,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(148,2,23,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(148,2,27,0,0,"DO", 1, 2, 0);
+ trans[4][23] = settr(142,2,25,11,11,"((i<4))", 1, 2, 0); /* m: 24 -> 25,0 */
+ reached4[24] = 1;
+ trans[4][24] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0);
+ trans[4][25] = settr(144,2,29,12,12,"(run tracer())", 1, 2, 0); /* m: 26 -> 29,0 */
+ reached4[26] = 1;
+ trans[4][26] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[4][27] = settr(146,2,39,13,13,"((i>=4))", 1, 2, 0); /* m: 32 -> 39,0 */
+ reached4[32] = 1;
+ trans[4][28] = settr(147,2,32,1,0,"goto :b8", 1, 2, 0); /* m: 32 -> 0,39 */
+ reached4[32] = 1;
+ trans[4][31] = settr(150,2,32,1,0,"break", 1, 2, 0);
+ trans[4][32] = settr(151,2,39,14,14,"i = 0", 1, 2, 0);
+ trans[4][40] = settr(159,2,39,1,0,".(goto)", 1, 2, 0);
+ T = trans[4][39] = settr(158,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(158,2,33,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(158,2,37,0,0,"DO", 1, 2, 0);
+ trans[4][33] = settr(152,2,35,15,15,"((i<1))", 1, 2, 0); /* m: 34 -> 35,0 */
+ reached4[34] = 1;
+ trans[4][34] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0);
+ trans[4][35] = settr(154,2,39,16,16,"(run switcher())", 1, 2, 0); /* m: 36 -> 39,0 */
+ reached4[36] = 1;
+ trans[4][36] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[4][37] = settr(156,2,41,17,17,"((i>=1))", 1, 2, 0); /* m: 38 -> 41,0 */
+ reached4[38] = 1;
+ trans[4][38] = settr(157,2,41,1,0,"goto :b9", 1, 2, 0);
+ trans[4][41] = settr(160,0,57,1,0,"break", 1, 2, 0);
+ T = trans[ 4][57] = settr(176,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(176,2,43,0,0,"ATOMIC", 1, 2, 0);
+ trans[4][43] = settr(162,2,52,18,18,"assert((((write_off-read_off)>=0)&&((write_off-read_off)<(255/2))))", 1, 2, 0); /* m: 44 -> 0,52 */
+ reached4[44] = 1;
+ trans[4][44] = settr(0,0,0,0,0,"j = 0",0,0,0);
+ trans[4][45] = settr(0,0,0,0,0,"commit_sum = 0",0,0,0);
+ trans[4][53] = settr(172,2,52,1,0,".(goto)", 1, 2, 0);
+ T = trans[4][52] = settr(171,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(171,2,46,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(171,2,50,0,0,"DO", 1, 2, 0);
+ trans[4][46] = settr(165,2,52,19,19,"((j<2))", 1, 2, 0); /* m: 47 -> 52,0 */
+ reached4[47] = 1;
+ trans[4][47] = settr(0,0,0,0,0,"commit_sum = (commit_sum+commit_count[j])",0,0,0);
+ trans[4][48] = settr(0,0,0,0,0,"assert((((commit_count[j]-retrieve_count[j])>=0)&&((commit_count[j]-retrieve_count[j])<(255/2))))",0,0,0);
+ trans[4][49] = settr(0,0,0,0,0,"j = (j+1)",0,0,0);
+ trans[4][50] = settr(169,4,58,20,20,"((j>=2))", 1, 2, 0); /* m: 55 -> 58,0 */
+ reached4[55] = 1;
+ trans[4][51] = settr(170,2,55,1,0,"goto :b10", 1, 2, 0); /* m: 55 -> 0,58 */
+ reached4[55] = 1;
+ trans[4][54] = settr(173,2,55,1,0,"break", 1, 2, 0);
+ trans[4][55] = settr(174,4,58,21,21,"assert((((write_off-commit_sum)>=0)&&((write_off-commit_sum)<(255/2))))", 1, 2, 0); /* m: 56 -> 0,58 */
+ reached4[56] = 1;
+ trans[4][56] = settr(0,0,0,0,0,"assert((((4+1)>4)||(events_lost==0)))",0,0,0);
+ trans[4][58] = settr(177,0,0,22,22,"-end-", 0, 3500, 0);
+
+ /* proctype 3: cleaner */
+
+ trans[3] = (Trans **) emalloc(10*sizeof(Trans *));
+
+ T = trans[ 3][8] = settr(118,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(118,2,5,0,0,"ATOMIC", 1, 2, 0);
+ trans[3][6] = settr(116,2,5,1,0,".(goto)", 1, 2, 0);
+ T = trans[3][5] = settr(115,2,0,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(115,2,1,0,0,"DO", 1, 2, 0);
+ trans[3][1] = settr(111,2,3,23,23,"((refcount==0))", 1, 2, 0); /* m: 2 -> 3,0 */
+ reached3[2] = 1;
+ trans[3][2] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0);
+ trans[3][3] = settr(113,2,7,24,24,"(run switcher())", 1, 2, 0); /* m: 4 -> 7,0 */
+ reached3[4] = 1;
+ trans[3][4] = settr(114,2,7,1,0,"goto :b5", 1, 2, 0);
+ trans[3][7] = settr(117,0,9,1,0,"break", 1, 2, 0);
+ trans[3][9] = settr(119,0,0,25,25,"-end-", 0, 3500, 0);
+
+ /* proctype 2: reader */
+
+ trans[2] = (Trans **) emalloc(32*sizeof(Trans *));
+
+ trans[2][29] = settr(108,0,28,1,0,".(goto)", 0, 2, 0);
+ T = trans[2][28] = settr(107,0,0,0,0,"DO", 0, 2, 0);
+ T = T->nxt = settr(107,0,1,0,0,"DO", 0, 2, 0);
+ T->nxt = settr(107,0,26,0,0,"DO", 0, 2, 0);
+ trans[2][1] = settr(80,0,12,26,0,"((((((write_off/(4/2))-(read_off/(4/2)))>0)&&(((write_off/(4/2))-(read_off/(4/2)))<(255/2)))&&((commit_count[((read_off%4)/(4/2))]-retrieve_count[((read_off%4)/(4/2))])==(4/2))))", 1, 2, 0);
+ T = trans[ 2][12] = settr(91,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(91,2,2,0,0,"ATOMIC", 1, 2, 0);
+ trans[2][2] = settr(81,2,9,27,27,"i = 0", 1, 2, 0);
+ trans[2][10] = settr(89,2,9,1,0,".(goto)", 1, 2, 0);
+ T = trans[2][9] = settr(88,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(88,2,3,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(88,2,7,0,0,"DO", 1, 2, 0);
+ trans[2][3] = settr(82,2,9,28,28,"((i<(4/2)))", 1, 2, 0); /* m: 4 -> 9,0 */
+ reached2[4] = 1;
+ trans[2][4] = settr(0,0,0,0,0,"assert((buffer_use[((read_off+i)%4)]==0))",0,0,0);
+ trans[2][5] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%4)] = 1",0,0,0);
+ trans[2][6] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[2][7] = settr(86,2,11,29,29,"((i>=(4/2)))", 1, 2, 0); /* m: 8 -> 11,0 */
+ reached2[8] = 1;
+ trans[2][8] = settr(87,2,11,1,0,"goto :b3", 1, 2, 0);
+ trans[2][11] = settr(90,0,25,1,0,"break", 1, 2, 0);
+ T = trans[ 2][25] = settr(104,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(104,2,13,0,0,"ATOMIC", 1, 2, 0);
+ trans[2][13] = /* c */ settr(92,2,19,27,27,"i = 0", 1, 2, 0);
+ trans[2][20] = settr(99,2,19,1,0,".(goto)", 1, 2, 0);
+ T = trans[2][19] = settr(98,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(98,2,14,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(98,2,17,0,0,"DO", 1, 2, 0);
+ trans[2][14] = settr(93,2,19,30,30,"((i<(4/2)))", 1, 2, 0); /* m: 15 -> 19,0 */
+ reached2[15] = 1;
+ trans[2][15] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%4)] = 0",0,0,0);
+ trans[2][16] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[2][17] = settr(96,0,28,31,31,"((i>=(4/2)))", 1, 2, 0); /* m: 22 -> 28,0 */
+ reached2[22] = 1;
+ trans[2][18] = settr(97,2,22,1,0,"goto :b4", 1, 2, 0); /* m: 22 -> 0,28 */
+ reached2[22] = 1;
+ trans[2][21] = settr(100,2,22,1,0,"break", 1, 2, 0);
+ trans[2][22] = settr(101,0,28,32,32,"tmp_retrieve = (retrieve_count[((read_off%4)/(4/2))]+(4/2))", 1, 2, 0); /* m: 23 -> 0,28 */
+ reached2[23] = 1;
+ trans[2][23] = settr(0,0,0,0,0,"retrieve_count[((read_off%4)/(4/2))] = tmp_retrieve",0,0,0);
+ trans[2][24] = settr(0,0,0,0,0,"read_off = (read_off+(4/2))",0,0,0);
+ trans[2][26] = settr(105,0,31,33,0,"((read_off>=(4-events_lost)))", 1, 2, 0);
+ trans[2][27] = settr(106,0,31,1,0,"goto :b2", 0, 2, 0);
+ trans[2][30] = settr(109,0,31,1,0,"break", 0, 2, 0);
+ trans[2][31] = settr(110,0,0,34,34,"-end-", 0, 3500, 0);
+
+ /* proctype 1: tracer */
+
+ trans[1] = (Trans **) emalloc(51*sizeof(Trans *));
+
+ T = trans[ 1][3] = settr(32,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(32,2,1,0,0,"ATOMIC", 1, 2, 0);
+ trans[1][1] = settr(30,4,10,35,35,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,10 */
+ reached1[2] = 1;
+ trans[1][2] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0);
+ T = trans[ 1][10] = settr(39,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(39,2,8,0,0,"ATOMIC", 1, 2, 0);
+ T = trans[1][8] = settr(37,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(37,2,4,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(37,2,6,0,0,"IF", 1, 2, 0);
+ trans[1][4] = settr(33,2,47,36,36,"((((new_off-read_off)>4)&&((new_off-read_off)<(255/2))))", 1, 2, 0);
+ trans[1][5] = settr(34,2,47,1,0,"goto lost", 1, 2, 0);
+ trans[1][9] = settr(38,0,27,1,0,".(goto)", 1, 2, 0);
+ trans[1][6] = settr(35,2,7,2,0,"else", 1, 2, 0);
+ trans[1][7] = settr(36,4,27,37,37,"(1)", 1, 2, 0); /* m: 9 -> 27,0 */
+ reached1[9] = 1;
+ T = trans[ 1][27] = settr(56,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(56,2,15,0,0,"ATOMIC", 1, 2, 0);
+ T = trans[1][15] = settr(44,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(44,2,11,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(44,2,13,0,0,"IF", 1, 2, 0);
+ trans[1][11] = settr(40,4,3,38,38,"((prev_off!=write_off))", 1, 2, 0); /* m: 12 -> 3,0 */
+ reached1[12] = 1;
+ trans[1][12] = settr(41,0,3,1,0,"goto cmpxchg_loop", 1, 2, 0);
+ trans[1][16] = settr(45,2,17,1,0,".(goto)", 1, 2, 0); /* m: 17 -> 0,24 */
+ reached1[17] = 1;
+ trans[1][13] = settr(42,2,14,2,0,"else", 1, 2, 0);
+ trans[1][14] = settr(43,2,24,39,39,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,24 */
+ reached1[17] = 1;
+ trans[1][17] = settr(46,2,24,40,40,"i = 0", 1, 2, 0);
+ trans[1][25] = settr(54,2,24,1,0,".(goto)", 1, 2, 0);
+ T = trans[1][24] = settr(53,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(53,2,18,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(53,2,22,0,0,"DO", 1, 2, 0);
+ trans[1][18] = settr(47,2,24,41,41,"((i<size))", 1, 2, 0); /* m: 19 -> 24,0 */
+ reached1[19] = 1;
+ trans[1][19] = settr(0,0,0,0,0,"assert((buffer_use[((prev_off+i)%4)]==0))",0,0,0);
+ trans[1][20] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%4)] = 1",0,0,0);
+ trans[1][21] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[1][22] = settr(51,2,26,42,42,"((i>=size))", 1, 2, 0); /* m: 23 -> 26,0 */
+ reached1[23] = 1;
+ trans[1][23] = settr(52,2,26,1,0,"goto :b0", 1, 2, 0);
+ trans[1][26] = settr(55,0,45,1,0,"break", 1, 2, 0);
+ T = trans[ 1][45] = settr(74,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(74,2,28,0,0,"ATOMIC", 1, 2, 0);
+ trans[1][28] = settr(57,2,34,43,43,"i = 0", 1, 2, 0);
+ trans[1][35] = settr(64,2,34,1,0,".(goto)", 1, 2, 0);
+ T = trans[1][34] = settr(63,2,0,0,0,"DO", 1, 2, 0);
+ T = T->nxt = settr(63,2,29,0,0,"DO", 1, 2, 0);
+ T->nxt = settr(63,2,32,0,0,"DO", 1, 2, 0);
+ trans[1][29] = settr(58,2,34,44,44,"((i<size))", 1, 2, 0); /* m: 30 -> 34,0 */
+ reached1[30] = 1;
+ trans[1][30] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%4)] = 0",0,0,0);
+ trans[1][31] = settr(0,0,0,0,0,"i = (i+1)",0,0,0);
+ trans[1][32] = settr(61,2,43,45,45,"((i>=size))", 1, 2, 0); /* m: 37 -> 43,0 */
+ reached1[37] = 1;
+ trans[1][33] = settr(62,2,37,1,0,"goto :b1", 1, 2, 0); /* m: 37 -> 0,43 */
+ reached1[37] = 1;
+ trans[1][36] = settr(65,2,37,1,0,"break", 1, 2, 0);
+ trans[1][37] = settr(66,2,43,46,46,"tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)", 1, 2, 0); /* m: 38 -> 0,43 */
+ reached1[38] = 1;
+ trans[1][38] = settr(0,0,0,0,0,"commit_count[((prev_off%4)/(4/2))] = tmp_commit",0,0,0);
+ T = trans[1][43] = settr(72,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(72,2,39,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(72,2,41,0,0,"IF", 1, 2, 0);
+ trans[1][39] = settr(68,4,49,47,47,"(((tmp_commit%(4/2))==0))", 1, 2, 0); /* m: 40 -> 49,0 */
+ reached1[40] = 1;
+ trans[1][40] = settr(0,0,0,0,0,"deliver = 1",0,0,0);
+ trans[1][44] = settr(73,0,49,48,48,".(goto)", 1, 2, 0);
+ trans[1][41] = settr(70,2,42,2,0,"else", 1, 2, 0);
+ trans[1][42] = settr(71,4,49,49,49,"(1)", 1, 2, 0); /* m: 44 -> 49,0 */
+ reached1[44] = 1;
+ T = trans[ 1][49] = settr(78,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(78,2,46,0,0,"ATOMIC", 1, 2, 0);
+ trans[1][46] = settr(75,2,48,1,0,"goto end", 1, 2, 0);
+ trans[1][47] = settr(76,2,48,50,50,"events_lost = (events_lost+1)", 1, 2, 0);
+ trans[1][48] = settr(77,0,50,51,51,"refcount = (refcount-1)", 1, 2, 0);
+ trans[1][50] = settr(79,0,0,52,52,"-end-", 0, 3500, 0);
+
+ /* proctype 0: switcher */
+
+ trans[0] = (Trans **) emalloc(31*sizeof(Trans *));
+
+ T = trans[ 0][11] = settr(10,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(10,2,1,0,0,"ATOMIC", 1, 2, 0);
+ trans[0][1] = settr(0,2,9,53,53,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,9 */
+ reached0[2] = 1;
+ trans[0][2] = settr(0,0,0,0,0,"size = ((4/2)-(prev_off%(4/2)))",0,0,0);
+ trans[0][3] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0);
+ T = trans[0][9] = settr(8,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(8,2,4,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(8,2,7,0,0,"IF", 1, 2, 0);
+ trans[0][4] = settr(3,4,29,54,54,"(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))", 1, 2, 0); /* m: 5 -> 29,0 */
+ reached0[5] = 1;
+ trans[0][5] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0);
+ trans[0][6] = settr(5,0,29,1,0,"goto not_needed", 1, 2, 0);
+ trans[0][10] = settr(9,0,18,1,0,".(goto)", 1, 2, 0);
+ trans[0][7] = settr(6,2,8,2,0,"else", 1, 2, 0);
+ trans[0][8] = settr(7,4,18,55,55,"(1)", 1, 2, 0); /* m: 10 -> 18,0 */
+ reached0[10] = 1;
+ T = trans[ 0][18] = settr(17,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(17,2,16,0,0,"ATOMIC", 1, 2, 0);
+ T = trans[0][16] = settr(15,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(15,2,12,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(15,2,14,0,0,"IF", 1, 2, 0);
+ trans[0][12] = settr(11,4,11,56,56,"((prev_off!=write_off))", 1, 2, 0); /* m: 13 -> 11,0 */
+ reached0[13] = 1;
+ trans[0][13] = settr(12,0,11,1,0,"goto cmpxchg_loop", 1, 2, 0);
+ trans[0][17] = settr(16,0,28,57,57,".(goto)", 1, 2, 0);
+ trans[0][14] = settr(13,2,15,2,0,"else", 1, 2, 0);
+ trans[0][15] = settr(14,4,28,58,58,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,28 */
+ reached0[17] = 1;
+ T = trans[ 0][28] = settr(27,2,0,0,0,"ATOMIC", 1, 2, 0);
+ T->nxt = settr(27,2,19,0,0,"ATOMIC", 1, 2, 0);
+ trans[0][19] = settr(18,2,25,59,59,"tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)", 1, 2, 0); /* m: 20 -> 0,25 */
+ reached0[20] = 1;
+ trans[0][20] = settr(0,0,0,0,0,"commit_count[((prev_off%4)/(4/2))] = tmp_commit",0,0,0);
+ T = trans[0][25] = settr(24,2,0,0,0,"IF", 1, 2, 0);
+ T = T->nxt = settr(24,2,21,0,0,"IF", 1, 2, 0);
+ T->nxt = settr(24,2,23,0,0,"IF", 1, 2, 0);
+ trans[0][21] = settr(20,4,29,60,60,"(((tmp_commit%(4/2))==0))", 1, 2, 0); /* m: 22 -> 29,0 */
+ reached0[22] = 1;
+ trans[0][22] = settr(0,0,0,0,0,"deliver = 1",0,0,0);
+ trans[0][26] = settr(25,4,29,61,61,".(goto)", 1, 2, 0); /* m: 27 -> 0,29 */
+ reached0[27] = 1;
+ trans[0][23] = settr(22,2,24,2,0,"else", 1, 2, 0);
+ trans[0][24] = settr(23,4,29,62,62,"(1)", 1, 2, 0); /* m: 26 -> 29,0 */
+ reached0[26] = 1;
+ trans[0][27] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0);
+ trans[0][29] = settr(28,0,30,1,0,"(1)", 0, 2, 0);
+ trans[0][30] = settr(29,0,0,63,63,"-end-", 0, 3500, 0);
+ /* np_ demon: */
+ trans[_NP_] = (Trans **) emalloc(2*sizeof(Trans *));
+ T = trans[_NP_][0] = settr(9997,0,1,_T5,0,"(np_)", 1,2,0);
+ T->nxt = settr(9998,0,0,_T2,0,"(1)", 0,2,0);
+ T = trans[_NP_][1] = settr(9999,0,1,_T5,0,"(np_)", 1,2,0);
+}
+
+Trans *
+settr( int t_id, int a, int b, int c, int d,
+ char *t, int g, int tpe0, int tpe1)
+{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));
+
+ tmp->atom = a&(6|32); /* only (2|8|32) have meaning */
+ if (!g) tmp->atom |= 8; /* no global references */
+ tmp->st = b;
+ tmp->tpe[0] = tpe0;
+ tmp->tpe[1] = tpe1;
+ tmp->tp = t;
+ tmp->t_id = t_id;
+ tmp->forw = c;
+ tmp->back = d;
+ return tmp;
+}
+
+Trans *
+cpytr(Trans *a)
+{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));
+
+ int i;
+ tmp->atom = a->atom;
+ tmp->st = a->st;
+#ifdef HAS_UNLESS
+ tmp->e_trans = a->e_trans;
+ for (i = 0; i < HAS_UNLESS; i++)
+ tmp->escp[i] = a->escp[i];
+#endif
+ tmp->tpe[0] = a->tpe[0];
+ tmp->tpe[1] = a->tpe[1];
+ for (i = 0; i < 6; i++)
+ { tmp->qu[i] = a->qu[i];
+ tmp->ty[i] = a->ty[i];
+ }
+ tmp->tp = (char *) emalloc(strlen(a->tp)+1);
+ strcpy(tmp->tp, a->tp);
+ tmp->t_id = a->t_id;
+ tmp->forw = a->forw;
+ tmp->back = a->back;
+ return tmp;
+}
+
+#ifndef NOREDUCE
+int
+srinc_set(int n)
+{ if (n <= 2) return LOCAL;
+ if (n <= 2+ DELTA) return Q_FULL_F; /* 's' or nfull */
+ if (n <= 2+2*DELTA) return Q_EMPT_F; /* 'r' or nempty */
+ if (n <= 2+3*DELTA) return Q_EMPT_T; /* empty */
+ if (n <= 2+4*DELTA) return Q_FULL_T; /* full */
+ if (n == 5*DELTA) return GLOBAL;
+ if (n == 6*DELTA) return TIMEOUT_F;
+ if (n == 7*DELTA) return ALPHA_F;
+ Uerror("cannot happen srinc_class");
+ return BAD;
+}
+int
+srunc(int n, int m)
+{ switch(m) {
+ case Q_FULL_F: return n-2;
+ case Q_EMPT_F: return n-2-DELTA;
+ case Q_EMPT_T: return n-2-2*DELTA;
+ case Q_FULL_T: return n-2-3*DELTA;
+ case ALPHA_F:
+ case TIMEOUT_F: return 257; /* non-zero, and > MAXQ */
+ }
+ Uerror("cannot happen srunc");
+ return 0;
+}
+#endif
+int cnt;
+#ifdef HAS_UNLESS
+int
+isthere(Trans *a, int b)
+{ Trans *t;
+ for (t = a; t; t = t->nxt)
+ if (t->t_id == b)
+ return 1;
+ return 0;
+}
+#endif
+#ifndef NOREDUCE
+int
+mark_safety(Trans *t) /* for conditional safety */
+{ int g = 0, i, j, k;
+
+ if (!t) return 0;
+ if (t->qu[0])
+ return (t->qu[1])?2:1; /* marked */
+
+ for (i = 0; i < 2; i++)
+ { j = srinc_set(t->tpe[i]);
+ if (j >= GLOBAL && j != ALPHA_F)
+ return -1;
+ if (j != LOCAL)
+ { k = srunc(t->tpe[i], j);
+ if (g == 0
+ || t->qu[0] != k
+ || t->ty[0] != j)
+ { t->qu[g] = k;
+ t->ty[g] = j;
+ g++;
+ } } }
+ return g;
+}
+#endif
+void
+retrans(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])
+ /* process n, with m states, is=initial state */
+{ Trans *T0, *T1, *T2, *T3;
+ int i, k;
+#ifndef NOREDUCE
+ int g, h, j, aa;
+#endif
+#ifdef HAS_UNLESS
+ int p;
+#endif
+ if (state_tables >= 4)
+ { printf("STEP 1 proctype %s\n",
+ procname[n]);
+ for (i = 1; i < m; i++)
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ crack(n, i, T0, srcln);
+ return;
+ }
+ do {
+ for (i = 1, cnt = 0; i < m; i++)
+ { T2 = trans[n][i];
+ T1 = T2?T2->nxt:(Trans *)0;
+/* prescan: */ for (T0 = T1; T0; T0 = T0->nxt)
+/* choice in choice */ { if (T0->st && trans[n][T0->st]
+ && trans[n][T0->st]->nxt)
+ break;
+ }
+#if 0
+ if (T0)
+ printf("\tstate %d / %d: choice in choice\n",
+ i, T0->st);
+#endif
+ if (T0)
+ for (T0 = T1; T0; T0 = T0->nxt)
+ { T3 = trans[n][T0->st];
+ if (!T3->nxt)
+ { T2->nxt = cpytr(T0);
+ T2 = T2->nxt;
+ imed(T2, T0->st, n, i);
+ continue;
+ }
+ do { T3 = T3->nxt;
+ T2->nxt = cpytr(T3);
+ T2 = T2->nxt;
+ imed(T2, T0->st, n, i);
+ } while (T3->nxt);
+ cnt++;
+ }
+ }
+ } while (cnt);
+ if (state_tables >= 3)
+ { printf("STEP 2 proctype %s\n",
+ procname[n]);
+ for (i = 1; i < m; i++)
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ crack(n, i, T0, srcln);
+ return;
+ }
+ for (i = 1; i < m; i++)
+ { if (trans[n][i] && trans[n][i]->nxt) /* optimize */
+ { T1 = trans[n][i]->nxt;
+#if 0
+ printf("\t\tpull %d (%d) to %d\n",
+ T1->st, T1->forw, i);
+#endif
+ if (!trans[n][T1->st]) continue;
+ T0 = cpytr(trans[n][T1->st]);
+ trans[n][i] = T0;
+ reach[T1->st] = 1;
+ imed(T0, T1->st, n, i);
+ for (T1 = T1->nxt; T1; T1 = T1->nxt)
+ {
+#if 0
+ printf("\t\tpull %d (%d) to %d\n",
+ T1->st, T1->forw, i);
+#endif
+ if (!trans[n][T1->st]) continue;
+ T0->nxt = cpytr(trans[n][T1->st]);
+ T0 = T0->nxt;
+ reach[T1->st] = 1;
+ imed(T0, T1->st, n, i);
+ } } }
+ if (state_tables >= 2)
+ { printf("STEP 3 proctype %s\n",
+ procname[n]);
+ for (i = 1; i < m; i++)
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ crack(n, i, T0, srcln);
+ return;
+ }
+#ifdef HAS_UNLESS
+ for (i = 1; i < m; i++)
+ { if (!trans[n][i]) continue;
+ /* check for each state i if an
+ * escape to some state p is defined
+ * if so, copy and mark p's transitions
+ * and prepend them to the transition-
+ * list of state i
+ */
+ if (!like_java) /* the default */
+ { for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ for (k = HAS_UNLESS-1; k >= 0; k--)
+ { if (p = T0->escp[k])
+ for (T1 = trans[n][p]; T1; T1 = T1->nxt)
+ { if (isthere(trans[n][i], T1->t_id))
+ continue;
+ T2 = cpytr(T1);
+ T2->e_trans = p;
+ T2->nxt = trans[n][i];
+ trans[n][i] = T2;
+ } }
+ } else /* outermost unless checked first */
+ { Trans *T4;
+ T4 = T3 = (Trans *) 0;
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ for (k = HAS_UNLESS-1; k >= 0; k--)
+ { if (p = T0->escp[k])
+ for (T1 = trans[n][p]; T1; T1 = T1->nxt)
+ { if (isthere(trans[n][i], T1->t_id))
+ continue;
+ T2 = cpytr(T1);
+ T2->nxt = (Trans *) 0;
+ T2->e_trans = p;
+ if (T3) T3->nxt = T2;
+ else T4 = T2;
+ T3 = T2;
+ } }
+ if (T4)
+ { T3->nxt = trans[n][i];
+ trans[n][i] = T4;
+ }
+ }
+ }
+#endif
+#ifndef NOREDUCE
+ for (i = 1; i < m; i++)
+ { if (a_cycles)
+ { /* moves through these states are visible */
+ #if PROG_LAB>0 && defined(HAS_NP)
+ if (progstate[n][i])
+ goto degrade;
+ for (T1 = trans[n][i]; T1; T1 = T1->nxt)
+ if (progstate[n][T1->st])
+ goto degrade;
+ #endif
+ if (accpstate[n][i] || visstate[n][i])
+ goto degrade;
+ for (T1 = trans[n][i]; T1; T1 = T1->nxt)
+ if (accpstate[n][T1->st])
+ goto degrade;
+ }
+ T1 = trans[n][i];
+ if (!T1) continue;
+ g = mark_safety(T1); /* V3.3.1 */
+ if (g < 0) goto degrade; /* global */
+ /* check if mixing of guards preserves reduction */
+ if (T1->nxt)
+ { k = 0;
+ for (T0 = T1; T0; T0 = T0->nxt)
+ { if (!(T0->atom&8))
+ goto degrade;
+ for (aa = 0; aa < 2; aa++)
+ { j = srinc_set(T0->tpe[aa]);
+ if (j >= GLOBAL && j != ALPHA_F)
+ goto degrade;
+ if (T0->tpe[aa]
+ && T0->tpe[aa]
+ != T1->tpe[0])
+ k = 1;
+ } }
+ /* g = 0; V3.3.1 */
+ if (k) /* non-uniform selection */
+ for (T0 = T1; T0; T0 = T0->nxt)
+ for (aa = 0; aa < 2; aa++)
+ { j = srinc_set(T0->tpe[aa]);
+ if (j != LOCAL)
+ { k = srunc(T0->tpe[aa], j);
+ for (h = 0; h < 6; h++)
+ if (T1->qu[h] == k
+ && T1->ty[h] == j)
+ break;
+ if (h >= 6)
+ { T1->qu[g%6] = k;
+ T1->ty[g%6] = j;
+ g++;
+ } } }
+ if (g > 6)
+ { T1->qu[0] = 0; /* turn it off */
+ printf("pan: warning, line %d, ",
+ srcln[i]);
+ printf("too many stmnt types (%d)",
+ g);
+ printf(" in selection\n");
+ goto degrade;
+ }
+ }
+ /* mark all options global if >=1 is global */
+ for (T1 = trans[n][i]; T1; T1 = T1->nxt)
+ if (!(T1->atom&8)) break;
+ if (T1)
+degrade: for (T1 = trans[n][i]; T1; T1 = T1->nxt)
+ T1->atom &= ~8; /* mark as unsafe */
+ /* can only mix 'r's or 's's if on same chan */
+ /* and not mixed with other local operations */
+ T1 = trans[n][i];
+ if (!T1 || T1->qu[0]) continue;
+ j = T1->tpe[0];
+ if (T1->nxt && T1->atom&8)
+ { if (j == 5*DELTA)
+ { printf("warning: line %d ", srcln[i]);
+ printf("mixed condition ");
+ printf("(defeats reduction)\n");
+ goto degrade;
+ }
+ for (T0 = T1; T0; T0 = T0->nxt)
+ for (aa = 0; aa < 2; aa++)
+ if (T0->tpe[aa] && T0->tpe[aa] != j)
+ { printf("warning: line %d ", srcln[i]);
+ printf("[%d-%d] mixed %stion ",
+ T0->tpe[aa], j,
+ (j==5*DELTA)?"condi":"selec");
+ printf("(defeats reduction)\n");
+ printf(" '%s' <-> '%s'\n",
+ T1->tp, T0->tp);
+ goto degrade;
+ } }
+ }
+#endif
+ for (i = 1; i < m; i++)
+ { T2 = trans[n][i];
+ if (!T2
+ || T2->nxt
+ || strncmp(T2->tp, ".(goto)", 7)
+ || !stopstate[n][i])
+ continue;
+ stopstate[n][T2->st] = 1;
+ }
+ if (state_tables)
+ { printf("proctype ");
+ if (!strcmp(procname[n], ":init:"))
+ printf("init\n");
+ else
+ printf("%s\n", procname[n]);
+ for (i = 1; i < m; i++)
+ reach[i] = 1;
+ tagtable(n, m, is, srcln, reach);
+ } else
+ for (i = 1; i < m; i++)
+ { int nrelse;
+ if (strcmp(procname[n], ":never:") != 0)
+ { for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ { if (T0->st == i
+ && strcmp(T0->tp, "(1)") == 0)
+ { printf("error: proctype '%s' ",
+ procname[n]);
+ printf("line %d, state %d: has un",
+ srcln[i], i);
+ printf("conditional self-loop\n");
+ pan_exit(1);
+ } } }
+ nrelse = 0;
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ { if (strcmp(T0->tp, "else") == 0)
+ nrelse++;
+ }
+ if (nrelse > 1)
+ { printf("error: proctype '%s' state",
+ procname[n]);
+ printf(" %d, inherits %d", i, nrelse);
+ printf(" 'else' stmnts\n");
+ pan_exit(1);
+ } }
+ if (!state_tables && strcmp(procname[n], ":never:") == 0)
+ { int h = 0;
+ for (i = 1; i < m; i++)
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ if (T0->forw > h) h = T0->forw;
+ h++;
+ frm_st0 = (short *) emalloc(h * sizeof(short));
+ for (i = 1; i < m; i++)
+ for (T0 = trans[n][i]; T0; T0 = T0->nxt)
+ frm_st0[T0->forw] = i;
+ }
+#ifndef LOOPSTATE
+ if (state_tables)
+#endif
+ do_dfs(n, m, is, srcln, reach, lstate);
+#ifdef T_REVERSE
+ /* process n, with m states, is=initial state -- reverse list */
+ if (!state_tables && strcmp(procname[n], ":never:") != 0)
+ { for (i = 1; i < m; i++)
+ { Trans *T4 = (Trans *) 0;
+ T1 = (Trans *) 0;
+ T2 = (Trans *) 0;
+ T3 = (Trans *) 0;
+ for (T0 = trans[n][i]; T0; T0 = T4)
+ { T4 = T0->nxt;
+ if (strcmp(T0->tp, "else") == 0)
+ { T3 = T0;
+ T0->nxt = (Trans *) 0;
+ } else
+ { T0->nxt = T1;
+ if (!T1) { T2 = T0; }
+ T1 = T0;
+ } }
+ if (T2 && T3) { T2->nxt = T3; }
+ trans[n][i] = T1; /* reversed -- else at end */
+ } }
+#endif
+}
+void
+imed(Trans *T, int v, int n, int j) /* set intermediate state */
+{ progstate[n][T->st] |= progstate[n][v];
+ accpstate[n][T->st] |= accpstate[n][v];
+ stopstate[n][T->st] |= stopstate[n][v];
+ mapstate[n][j] = T->st;
+}
+void
+tagtable(int n, int m, int is, short srcln[], uchar reach[])
+{ Trans *z;
+
+ if (is >= m || !trans[n][is]
+ || is <= 0 || reach[is] == 0)
+ return;
+ reach[is] = 0;
+ if (state_tables)
+ for (z = trans[n][is]; z; z = z->nxt)
+ crack(n, is, z, srcln);
+ for (z = trans[n][is]; z; z = z->nxt)
+ {
+#ifdef HAS_UNLESS
+ int i, j;
+#endif
+ tagtable(n, m, z->st, srcln, reach);
+#ifdef HAS_UNLESS
+ for (i = 0; i < HAS_UNLESS; i++)
+ { j = trans[n][is]->escp[i];
+ if (!j) break;
+ tagtable(n, m, j, srcln, reach);
+ }
+#endif
+ }
+}
+void
+dfs_table(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])
+{ Trans *z;
+
+ if (is >= m || is <= 0 || !trans[n][is])
+ return;
+ if ((reach[is] & (4|8|16)) != 0)
+ { if ((reach[is] & (8|16)) == 16) /* on stack, not yet recorded */
+ { lstate[is] = 1;
+ reach[is] |= 8; /* recorded */
+ if (state_tables)
+ { printf("state %d line %d is a loopstate\n", is, srcln[is]);
+ } }
+ return;
+ }
+ reach[is] |= (4|16); /* visited | onstack */
+ for (z = trans[n][is]; z; z = z->nxt)
+ {
+#ifdef HAS_UNLESS
+ int i, j;
+#endif
+ dfs_table(n, m, z->st, srcln, reach, lstate);
+#ifdef HAS_UNLESS
+ for (i = 0; i < HAS_UNLESS; i++)
+ { j = trans[n][is]->escp[i];
+ if (!j) break;
+ dfs_table(n, m, j, srcln, reach, lstate);
+ }
+#endif
+ }
+ reach[is] &= ~16; /* no longer on stack */
+}
+void
+do_dfs(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[])
+{ int i;
+ dfs_table(n, m, is, srcln, reach, lstate);
+ for (i = 0; i < m; i++)
+ reach[i] &= ~(4|8|16);
+}
+void
+crack(int n, int j, Trans *z, short srcln[])
+{ int i;
+
+ if (!z) return;
+ printf(" state %3d -(tr %3d)-> state %3d ",
+ j, z->forw, z->st);
+ printf("[id %3d tp %3d", z->t_id, z->tpe[0]);
+ if (z->tpe[1]) printf(",%d", z->tpe[1]);
+#ifdef HAS_UNLESS
+ if (z->e_trans)
+ printf(" org %3d", z->e_trans);
+ else if (state_tables >= 2)
+ for (i = 0; i < HAS_UNLESS; i++)
+ { if (!z->escp[i]) break;
+ printf(" esc %d", z->escp[i]);
+ }
+#endif
+ printf("]");
+ printf(" [%s%s%s%s%s] line %d => ",
+ z->atom&6?"A":z->atom&32?"D":"-",
+ accpstate[n][j]?"a" :"-",
+ stopstate[n][j]?"e" : "-",
+ progstate[n][j]?"p" : "-",
+ z->atom & 8 ?"L":"G",
+ srcln[j]);
+ for (i = 0; z->tp[i]; i++)
+ if (z->tp[i] == '\n')
+ printf("\\n");
+ else
+ putchar(z->tp[i]);
+ if (z->qu[0])
+ { printf("\t[");
+ for (i = 0; i < 6; i++)
+ if (z->qu[i])
+ printf("(%d,%d)",
+ z->qu[i], z->ty[i]);
+ printf("]");
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#ifdef VAR_RANGES
+#define BYTESIZE 32 /* 2^8 : 2^3 = 256:8 = 32 */
+
+typedef struct Vr_Ptr {
+ char *nm;
+ uchar vals[BYTESIZE];
+ struct Vr_Ptr *nxt;
+} Vr_Ptr;
+Vr_Ptr *ranges = (Vr_Ptr *) 0;
+
+void
+logval(char *s, int v)
+{ Vr_Ptr *tmp;
+
+ if (v<0 || v > 255) return;
+ for (tmp = ranges; tmp; tmp = tmp->nxt)
+ if (!strcmp(tmp->nm, s))
+ goto found;
+ tmp = (Vr_Ptr *) emalloc(sizeof(Vr_Ptr));
+ tmp->nxt = ranges;
+ ranges = tmp;
+ tmp->nm = s;
+found:
+ tmp->vals[(v)/8] |= 1<<((v)%8);
+}
+
+void
+dumpval(uchar X[], int range)
+{ int w, x, i, j = -1;
+
+ for (w = i = 0; w < range; w++)
+ for (x = 0; x < 8; x++, i++)
+ {
+from: if ((X[w] & (1<<x)))
+ { printf("%d", i);
+ j = i;
+ goto upto;
+ } }
+ return;
+ for (w = 0; w < range; w++)
+ for (x = 0; x < 8; x++, i++)
+ {
+upto: if (!(X[w] & (1<<x)))
+ { if (i-1 == j)
+ printf(", ");
+ else
+ printf("-%d, ", i-1);
+ goto from;
+ } }
+ if (j >= 0 && j != 255)
+ printf("-255");
+}
+
+void
+dumpranges(void)
+{ Vr_Ptr *tmp;
+ printf("\nValues assigned within ");
+ printf("interval [0..255]:\n");
+ for (tmp = ranges; tmp; tmp = tmp->nxt)
+ { printf("\t%s\t: ", tmp->nm);
+ dumpval(tmp->vals, BYTESIZE);
+ printf("\n");
+ }
+}
+#endif
--- /dev/null
+#!/bin/bash
+
+../Spin/Src5.1.6/spin -a buffer.spin
+cc -DSAFETY -o pan pan.c
+./pan
--- /dev/null
+#!/bin/bash
+../Spin/Src5.1.6/spin -t -p buffer.spin |less
--- /dev/null
+#!/bin/bash
+
+../Spin/Src5.1.6/spin -a buffer.spin
+cc -o pan pan.c
+./pan
--- /dev/null
+#define NUMPROCS 2
+
+byte counter = 0;
+byte progress[NUMPROCS];
+
+proctype incrementer(byte me)
+{
+ int temp;
+
+ temp = counter;
+ counter = temp + 1;
+ progress[me] = 1;
+}
+
+init {
+ int i = 0;
+ int sum = 0;
+
+ atomic {
+ i = 0;
+ do
+ :: i < NUMPROCS ->
+ progress[i] = 0;
+ run incrementer(i);
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ }
+ atomic {
+ i = 0;
+ sum = 0;
+ do
+ :: i < NUMPROCS ->
+ sum = sum + progress[i];
+ i++
+ :: i >= NUMPROCS -> break
+ od;
+ assert(sum < NUMPROCS || counter == NUMPROCS)
+ }
+}
--- /dev/null
+-4:-4:-4
+1:0:4
+2:0:5
+3:0:7
+4:0:5
+5:0:7
+6:0:9
+7:0:13
+8:2:0
+9:1:0
+10:2:1
+11:2:2
+12:2:3
+13:1:1
+14:1:2
+15:1:3
+16:0:15
+17:0:17
+18:0:17
+19:0:20
+20:0:24
+21:0:25