From: compudj Date: Wed, 15 Oct 2008 14:14:27 +0000 (+0000) Subject: add formal verif X-Git-Tag: v0.12.20~381 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=0b55f123deb998c049b24b23c1651519b639a95b;p=lttv.git add formal verif git-svn-id: http://ltt.polymtl.ca/svn@3108 04897980-b3bd-0310-b5e0-8ef037075253 --- diff --git a/trunk/verif/Spin/Doc/Book.Ch6.add b/trunk/verif/Spin/Doc/Book.Ch6.add new file mode 100755 index 00000000..4dbf1644 --- /dev/null +++ b/trunk/verif/Spin/Doc/Book.Ch6.add @@ -0,0 +1,183 @@ +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). diff --git a/trunk/verif/Spin/Doc/Book.Errata b/trunk/verif/Spin/Doc/Book.Errata new file mode 100755 index 00000000..e3df18dc --- /dev/null +++ b/trunk/verif/Spin/Doc/Book.Errata @@ -0,0 +1,444 @@ +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 || (0 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= diff --git a/trunk/verif/Spin/Doc/Book.answers b/trunk/verif/Spin/Doc/Book.answers new file mode 100755 index 00000000..b70deb3a --- /dev/null +++ b/trunk/verif/Spin/Doc/Book.answers @@ -0,0 +1,612 @@ + + +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. diff --git a/trunk/verif/Spin/Doc/Book.samples b/trunk/verif/Spin/Doc/Book.samples new file mode 100755 index 00000000..43b4f6d4 --- /dev/null +++ b/trunk/verif/Spin/Doc/Book.samples @@ -0,0 +1,1769 @@ +# 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 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]) +- 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 diff --git a/trunk/verif/Spin/Doc/Book2003Errata.html b/trunk/verif/Spin/Doc/Book2003Errata.html new file mode 100755 index 00000000..36b9fc4a --- /dev/null +++ b/trunk/verif/Spin/Doc/Book2003Errata.html @@ -0,0 +1,383 @@ + + +Book Errata - The Spin Model Checker + +

Typos found in the first printing (August 2003)

+ +
    +
  • p. vi chapter 8 topic listings, Breath-First -> Breadth-First
  • +
  • p. 2 line 16 "always explicitly" -> "usually"
  • +
  • p. 3 figure 1.1 is mirror reversed
  • +
  • p. 4 the website crashdatabas.com no longer seems to exist
  • +
  • p. 20 See note (*) below, provided by Heikki Tauriainen (Feb 1, 2006). +
  • p. 22 6th line from the bottom: "if" -> "of"
  • +
  • p. 25 4th line from the bottom: "variable in" -> "variable cnt"
  • +
  • p. 26 10th line from bottom: "set to false" -> "set to true"
  • +
  • p. 27 an error slipped into Figure 2.6. The fragment +
    +	M?data ->       /* receive data */
    +	do
    +	:: W!data       /* send data */
    +	:: W!shutup;    /* or shutdown */
    +		break
    +	od
    +
    +is an unfortunate last-minute rewrite of the originally intended version: +
    +	do
    +	:: M?data -> W!data
    +	:: M?data -> W!shutup;
    +		break
    +	od
    +
    +The behavior is of course not equivalent. +In particular, the version in the book cannot create the error scenario +given on page 29, but the intended version can.
  • +
  • p. 33 8th line from bottom: "the specification" -> "the specification of"
  • +
  • p. 33 3rd line from bottom: "functions pointers" -> "function pointers
  • +
  • p. 41 "1 <= n <= 32" -> "1 <= n < 32".
  • +
  • p. 43 appel -> apple
  • +
  • p. 52 11th line from the top: "p. 39." -> "p. 38."
  • +
  • p. 69 bottom line: "exclusive read and exclusive write" -> "exclusive receive and exclusive send"
  • +
  • p. 75 and 548 Goldstein -> Goldstine
  • +
  • p. 81 and 271 pan.trail -> fair.pml.trail
  • +
  • p. 81 3rd line from the bottom: "trace fpr" -> "trace for"
  • +
  • p. 82 18th line from the bottom: "process than" -> "process that"
  • +
  • p. 92 4th line from bottom: "Even traces" -> "Event traces"
  • +
  • p. 96 middle of page identify -> identity
  • +
  • p. 96 l. -6, for -> by
  • +
  • p. 111 below figure 5.4: "f==free" -> "f=free"
  • +
  • p. 119 12th line from the bottom; "xDm" -> "Dm"
  • +
  • p. 121 figure 5.8, in captions on bottom two figures: "p" -> "q"
  • +
  • p. 137 14th line from bottom (first rule in list): first 3 chars in wrong font
  • +
  • p. 139 last line; italic P -> roman P
  • +
  • p. 142 3rd line from bottom: "reach" -> "reached"
  • +
  • p. 142 5th line from bottom: omit comma
  • +
  • p. 148 middle of the page: "can be express" -> "can be expressed"
  • +
  • p. 149 5th line from bottom: "eventually always" -> "always eventually"
  • +
  • p. 150 replace "it is impossible for p to hold only in even steps in a run, but never at odd steps" +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"
  • +
  • p. 150 Omega-Regular Properties, line 1: "that" -> "than"
  • +
  • p. 158 middle of page: redundant space after "("
  • +
  • p. 168 the list of properties given for < and > is not exhaustive
  • +
  • p. 174 11th line from bottom: "but" -> "by"
  • +
  • p. 177 Procedure Search() in Figure 8.6 is incorrect. A corrected version is: +
    +Search()
    +{
    +	while (Empty_Queue(D) == false)
    +	{	s = Del_Queue(D)
    +		for each (s,1,s') member A.T
    +		if In_Statespace(V, s') == false
    +		{	Add_Statespace(V, s')
    +			Add_Queue(D, s')
    +		}
    +	}
    +}
    +
    +
  • +
  • p. 178 2nd line from top: "at state" -> "at each state", "of each state" -> "of that state"
  • +
  • p. 179 lead -> led
  • +
  • p. 180 before first 'if' stmnt inside for loop add: if (toggle == true)
  • +
  • p. 185 Fig. 8.10, circle at s^{1}_{2} should be dotted
  • +
  • p. 187 line -11: "interative" -> "iterative"
  • +
  • p. 188 replace "(RxB)+(k+2)" with "Rx(B+k+2)"
  • +
  • p. 193 Fig. 9.2, the two circles labeled 0,1,0 should be dashed
  • +
  • p. 193 7th line from bottom: "g=g*2," -> "g=g*2."
  • +
  • p. 196 line -9: "control control" -> "control"
  • +
  • p. 204 last line, "to 133 seconds" -> "to 53 seconds"
  • +
  • p. 208 a goof: m changes from bits to bytes between 2nd and 3rd paragraph
  • +
  • p. 209 in first two formulas: (1-1/m) sup {kr}.
  • +
  • p. 211 3rd line from below: probabilitie -> probabilities
  • +
  • p. 212 line -2: "ration" -> "ratio"
  • +
  • p. 214 A formal -> Formal
  • +
  • p. 216 1st-2nd line: 'collissions' -> 'collisions'
  • +
  • p. 219 last paragraph: missing right parenthesis
  • +
  • p. 228 Celcius -> Celsius
  • +
  • p. 228 in the list at the bottom: there are just 6 entries with 'keep' as a target
  • +
  • p. 237 12th line from below: "postive" -> "zero".
  • +
  • p. 237 4th line from below: "unsound" -> "incomplete".
  • +
  • p. 238 knifes -> knives
  • +
  • p. 241 7th line from bottom: "world0" -> "world\n"
  • +
  • p. 243 Pressburger -> Presburger
  • +
  • p. 251 Selet -> Select
  • +
  • p. 262 10th line from bottom: "do to" -> "due to"
  • +
  • p. 272 an basic -> a basic
  • +
  • p. 272 "As a special feature [...], if the statement" omit "if"
  • +
  • p. 279 "#define q" -> "#define r" + +
  • p. 281 Automata View -> Automaton View
  • +
  • p. 283 The correct wording of the quote from Willem L. van der Poel, as corrected by its author: +
    "There are no wrong programs, it simply is another program."
    + (email from the author, Feb 1, 2006). +
  • p. 284 8th line from bottom: omit "blue"
  • +
  • p. 287 6th line from top: omit "blue"
  • +
  • p. 307 top of page: "ringtone" -> "ring tone"
  • +
  • p. 307 top of page: "dialtone" -> "dial tone"
  • +
  • p. 307 top of page: "notone" -> "no tone"
  • + +
  • p. 333 11th line from top: "and early version" -> "an early version"
  • +
  • p. 338 the line numbered [19] is actually from the FIX
  • +
  • p. 339 6th line from below: pid 1 -> pid 0
  • +
  • p. 393 2nd line from top: "innermostn" -> "innermost"
  • +
  • p. 341 10th line from top: "body ," -> "body,"
  • +
  • p. 346 identificatio -> identification
  • +
  • p. 346 middle of the page: "tranaction" -> "transaction"
  • +
  • p. 349 55 is not the integer square root of either 1024 or 3601.
  • +
  • p. 356 n=1<<30 does not fail on all systems
  • +
  • p. 359 Fig. 15.8: what looks like commas are really single quotes
  • +
  • p. 359 Fig. 15.8: the automaton fails to detect strings that start inside a comment;
  • +unfortunate given the example that also appears on this page... +
  • p. 365 the grammar listing misses productions for inlines
  • +
  • p. 365 [active] PROCTYPE -> [active ['[' const ']']] PROCTYPE
  • +
  • p. 367 "PRINT" -> "PRINTF"
  • +
  • p. 369 in PREDEFINED: "373last" -> "374"
  • +
  • p. 369 in PREDEFINED: "373nr_pr" -> "376"
  • +
  • p. 369 5th line from bottom: "special case" -> "special cases"
  • +
  • p. 370 8th line from bottom: "p.272" -> "p. 272"
  • +
  • p. 370 2nd line from bottom: "(434)" -> "(p. 434)"
  • +
  • p. 370 2nd line from bottom: "(p, 483)" -> "(p. 483)"
  • +
  • p. 371 2nd line from top: "Two" -> "Three"
  • +
  • p. 371 9th line from top: "or both of the above two" -> "of the above"
  • +
  • p. 374 11th line from bottom: "from into" -> "to"
  • +
  • p. 376 5th line from bottom: "at 256" -> "at 255"
  • +
  • p. 377 5th line in DESCRIPTION: "process" -> "processes"
  • +
  • p. 381 4th line from bottom: "four process" -> "four processes"
  • +
  • p. 381 3rd line from bottom: "to three" -> "to four"
  • +
  • p. 390 9th line from bottom: "recepient" -> "recipient"
  • +
  • p. 393 10th line from bottom: "label L1" -> "label L2" +
  • p. 395 6th line from top: "multiple field" -> "multiple fields"
  • +
  • p. 397 4th line from top: "the the" -> "the"
  • +
  • p. 398 11th line from bottom: redundant space after "("
  • +
  • p. 402 7th line from top, "accidentily" -> "accidentally"
  • +
  • p. 404 mixed fonts in Table
  • +
  • p. 404 5th line from bottom: "the fact the" -> "the fact that the"
  • +
  • p. 407 in Notes, 2nd line: "tha" -> "that"
  • +
  • p. 408 "(x < 0)" -> "(x <= 0)"
  • +
  • p. 411 last line: "ltl len" -> "ltl, len"
  • +
  • p. 425 11th line from top: "followin" -> "following"
  • +
  • p. 440 11th line from top: "equivalents" -> "equivalent"
  • +
  • p. 441 middle of page: "LTL formula" -> "LTL formulae"
  • +
  • p. 446 10th line from top: "equivalents" -> "equivalent"
  • +
  • p. 450 last example in notes should be: atomic { P && qname?[ack,var] -> qname?ack,var }
  • +
  • p. 452 15th line from bottom: "will included" -> "will be included"
  • +
  • p. 455 5th line from top: "restrction" -> "restriction"
  • +
  • p. 456 middle of page: "type main" -> "type fact"
  • +
  • p. 456 12th line from bottom: "2,147,483,648" ->"2,147,483,647"
  • +
  • p. 456 10th line from bottom: 13! = 6,227,020,800 (and so even 13! > 2^31-1)
  • +
  • p. 464 9th line from bottom: "just and safe" -> "justified and safe" (2x)
  • +
  • p. 466 1st line in EFFECT: "to the" -> "of the"
  • +
  • p. 476 in EXAMPLES (2x): "b = a" -> "b = tmp"
  • +
  • p. 479 7th line from top: "can are" -> "are"
  • +
  • p. 496 6th line: "in in" -> "in"
  • +
  • p. 498 2nd line from bottom: "coord.trail" -> "example.trail"
  • +
  • p. 509 13th line from bottom: "known the" -> "known. The"
  • +
  • p. 512 middle of page: "an pointer" -> "a pointer"
  • +
  • p. 518 l -8, most -> must
  • +
  • p. 519 l -10, -rthis -> -r, this
  • +
  • p. 521 5th line from bottom: "substitions" -> "substitutions"
  • +
  • p. 528 under basic options -DBFS, "reducting" -> "reducing"
  • +
  • p. 532 under -DSDUMP, replace "-DCHECK" with: "-DVERBOSE or -DDEBUG"
  • +
  • p. 532 under -DSVDUMP, replace "a file named svdump" with "a file with extension .svd"
  • +
  • p. 541 11th line from bottom: "-a" in wrong font
  • +
  • p. 543 middle of page: "two for processes" -> "three for processes"
  • +
  • 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...
  • +
  • p. 547 Entry for Emerson: "model logic" -> "modal logic"
  • +
  • p. 553 13th line from bottom: "to represents" -> "to represent"
  • +
  • p. 554 10th line from top: "product" -> "products"
  • +
  • p. 561 DEADLOCK DETECTION, 1st line: "is system" -> "is a system"
  • +
  • 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."
  • +
  • p. 565 4th line from top: "andq, r" -> "q and r"
  • +
  • p. 566 define BDD (Binary Decision Diagram) and NP (Non-deterministic Polynomial)
  • +
  • p. 572 l 8, wil -> will
  • +
  • p. 575 10th line from bottom should be: spin -a -m ex2
  • +
  • p. 575 9th line from bottom should be: cc -DPC -DBITSTATE -DSAFETY -o pan pan.c
  • +
  • p. 577 C.9., 1st line: "an little" -> "a little"
  • +
  • p. 579 5th line from top: "these tool" -> "these tools"
  • +
+
+
+Statistics: +The list above contains +roughly 128 reported typos and goofs in the first printing of the book. +There are approximately 340K words in the book, giving 1 reported defect +per 2,650 words written. At and average of 10 words per sentence, this is +about 4 reported defects per 1,000 sentences in the book, which is roughly +on par with a reasonably good software development process of 1-10 residual +defects (after testing) per 1,000 lines of non-comment source code written. +As in software, the number of reported defects depends both on the number of +latent defects and on the number of users/readers +(i.e., unread books will have no reported typos...). +
+Note (*) on the example used on p. 20, provided by Heikki Tauriainen. +
+Date: Wed, 01 Feb 2006 21:10:54 +0200 (EET) 
+From: heikki.tauriainen [atsign] tkk [dot] fi 
+Subject: Spin book: Doran & Thomas's mutual exclusion algorithm 
+
+Dear Dr. Holzmann,
+
+Keijo Heljanko and I are giving at Helsinki University of Technology
+a basic course on parallel and distributed systems, using Spin for
+examples on model checking.  To demonstrate using the tool, we
+considered Dekker's mutual exclusion algorithm found in your Spin
+book (p. 20) and the variant of the algorithm by Doran and Thomas
+mentioned on p. 22.
+
+According to the Spin book, Doran and Thomas's algorithm can be
+obtained from Dekker's algorithm by simply changing the outer do-loop
+of the algorithm into an if-selection, and this change is claimed to
+preserve the correctness of the algorithm.  This doesn't, however,
+seem to be the case, as the verification results using the Promela
+models distributed in the package
+ were
+somewhat unexpected (unless, of course, the models in the package are
+deliberately faulty).  I'm referring to the file CH2/mutex.pml in the
+package.
+
+The Promela model uses a preprocessor directive (DORAN) to choose
+between the algorithm with the do-loop and the algorithm with the
+if-selection. Verifying the model with the do-loop indeed gives the
+expected result (no assertion violations).  Firstly, however, Spin
+doesn't directly accept the model of the variant of the algorithm:
+
+$ spin -DDORAN -a mutex.pml
+spin: line  30 "mutex.pml", Error: misplaced break statement    saw '-2'' near 'break'
+$
+
+After the obvious change of making the 'break' keyword at line 30
+apply only to the variant with the do-loop, that is, changing lines
+29--35 to read
+
+        :: else ->
+#ifdef DORAN
+        fi;
+#else
+                break
+        od;
+#endif
+
+and then verifying the mutual exclusion algorithm gives, however,
+the following (unexpected) result:
+
+$ spin -DDORAN -a mutex.pml
+$ gcc -o -DBFS -o pan pan.c
+$ ./pan
+pan: assertion violated (cnt==1) (at depth 9)
+pan: wrote mutex.pml.trail
+(Spin Version 4.2.6 -- 27 October 2005)
+Warning: Search not completed
+        + Using Breadth-First Search
+        + Partial Order Reduction
+
+Full statespace search for:
+        never claim             - (none specified)
+        assertion violations    +
+        cycle checks            - (disabled by -DSAFETY)
+        invalid end states      +
+
+State-vector 20 byte, depth reached 9, errors: 1
+      56 states, stored
+              56 nominal states (stored-atomic)
+      32 states, matched
+      88 transitions (= stored+matched)
+       0 atomic steps
+hash conflicts: 0 (resolved)
+
+2.302   memory usage (Mbyte)
+
+$ spin -DDORAN -p -t mutex.pml
+Starting mutex with pid 0
+Starting mutex with pid 1
+  1:    proc  1 (mutex) line  11 "mutex.pml" (state 1)  [i = _pid]
+  1:    proc  1 (mutex) line  12 "mutex.pml" (state 2)  [j = (1-_pid)]
+  2:    proc  0 (mutex) line  11 "mutex.pml" (state 1)  [i = _pid]
+  2:    proc  0 (mutex) line  12 "mutex.pml" (state 2)  [j = (1-_pid)]
+  3:    proc  1 (mutex) line  14 "mutex.pml" (state 3)  [flag[i] = 1]
+  4:    proc  1 (mutex) line  29 "mutex.pml" (state 12) [else]
+  5:    proc  1 (mutex) line  37 "mutex.pml" (state 15) [cnt = (cnt+1)]
+  6:    proc  0 (mutex) line  14 "mutex.pml" (state 3)  [flag[i] = 1]
+  7:    proc  0 (mutex) line  21 "mutex.pml" (state 4)  [(flag[j])]
+  8:    proc  0 (mutex) line  27 "mutex.pml" (state 9)  [else]
+  9:    proc  0 (mutex) line  37 "mutex.pml" (state 15) [cnt = (cnt+1)]
+spin: trail ends after 9 steps
+#processes: 2
+                turn = 0
+                flag[0] = 1
+                flag[1] = 1
+                cnt = 2
+  9:    proc  1 (mutex) line  38 "mutex.pml" (state 16)
+  9:    proc  0 (mutex) line  38 "mutex.pml" (state 16)
+2 processes created
+$
+
+Trying to find a reason for this unexpected result, I compared the
+model with the algorithm in Doran and Thomas's original article [1].
+It appears that the model in fact differs from that algorithm
+(repeated below from [1], Fig. 1)
+
+Process A                           Process B
+  1. A_needs := true;                    B_needs :=  true;
+  2. if B_needs then begin               if A_needs then begin
+  3.   if turn = 'B' then begin            if turn = 'A' then begin
+  4.     A_needs := false;                   B_needs := false;
+  5.     wait until turn = 'A';              wait until turn = 'B';
+  6.     A_needs := true;                    B_needs := true;
+  7.     end;                                end;
+  8.   wait until !B_needs;                wait until !A_needs;
+  9.   end;                                end;
+ 10. CRITICAL SECTION                    CRITICAL SECTION
+ 11. turn := 'B';                        turn := 'A';
+ 12. A_needs := false;                   B_needs := false;
+ 13. NON-CRITICAL SECTION                NON-CRITICAL SECTION
+
+In particular, the Promela model has no corresponding construct for
+line 8 of this algorithm, which appears to be critical to its
+correctness: changing the outer if-selection to read
+
+        if
+        :: flag[j] ->
+                if
+                :: turn == j ->
+                        flag[i] = false;
+                        !(turn == j);
+                        flag[i] = true
+                :: else
+                fi;
+                (!flag[j]);    /* needed for correctness */
+        :: else ->
+        fi;
+
+fixes the error.  However, it is not sufficient to simply
+replace the do-loop with an if-selection, although the wording
+on page 22 of the Spin book can be interpreteted to suggest
+otherwise (at least both I and Keijo were surprised, that's why
+we decided to write this report).
+
+(The example file suggests that the model is taken from the book
+[2] instead of directly from Doran and Thomas's original article [1].
+As a matter of fact, this book---at least its English translation---contains the same error.  This is probably also the
+reason why the model is faulty.)
+
+Best regards,
+Heikki Tauriainen
+
+
+References:
+
+[1] R. W. Doran and L. K. Thomas.  Variants of the software solution to
+    mutual exclusion.  Information Processing Letters 10(4--5):206--208,
+    1980.
+
+[2] M. Raynal.  Algorithms for mutual exclusion.  North Oxford Academic
+    Publishers Ltd., 1986.
+
+
+book home page +
+Spin home page +
+Last updated: 1 February 2006 + diff --git a/trunk/verif/Spin/Doc/Book91_Ch6_add.txt b/trunk/verif/Spin/Doc/Book91_Ch6_add.txt new file mode 100755 index 00000000..9fcd25e9 --- /dev/null +++ b/trunk/verif/Spin/Doc/Book91_Ch6_add.txt @@ -0,0 +1,183 @@ +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). diff --git a/trunk/verif/Spin/Doc/Book91_Errata.txt b/trunk/verif/Spin/Doc/Book91_Errata.txt new file mode 100755 index 00000000..0e9703bb --- /dev/null +++ b/trunk/verif/Spin/Doc/Book91_Errata.txt @@ -0,0 +1,452 @@ +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 || (0 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= diff --git a/trunk/verif/Spin/Doc/Book91_answers.txt b/trunk/verif/Spin/Doc/Book91_answers.txt new file mode 100755 index 00000000..b70deb3a --- /dev/null +++ b/trunk/verif/Spin/Doc/Book91_answers.txt @@ -0,0 +1,612 @@ + + +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. diff --git a/trunk/verif/Spin/Doc/Book91_samples_bundle b/trunk/verif/Spin/Doc/Book91_samples_bundle new file mode 100755 index 00000000..b039b209 --- /dev/null +++ b/trunk/verif/Spin/Doc/Book91_samples_bundle @@ -0,0 +1,1769 @@ +# 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 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]) +- 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 diff --git a/trunk/verif/Spin/Doc/V1.Updates b/trunk/verif/Spin/Doc/V1.Updates new file mode 100755 index 00000000..fdd97154 --- /dev/null +++ b/trunk/verif/Spin/Doc/V1.Updates @@ -0,0 +1,358 @@ +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. diff --git a/trunk/verif/Spin/Doc/V2.Updates b/trunk/verif/Spin/Doc/V2.Updates new file mode 100755 index 00000000..bfca2620 --- /dev/null +++ b/trunk/verif/Spin/Doc/V2.Updates @@ -0,0 +1,1163 @@ +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? - fifo poll, exactly as A, but the message is + not removed from the channel + F. q?? - 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 typedef 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 button to most text windows (to clear the contents + of the display) + +- added and 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 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 + (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 diff --git a/trunk/verif/Spin/Doc/V3.Updates b/trunk/verif/Spin/Doc/V3.Updates new file mode 100755 index 00000000..6bf770b3 --- /dev/null +++ b/trunk/verif/Spin/Doc/V3.Updates @@ -0,0 +1,925 @@ +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 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 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 diff --git a/trunk/verif/Spin/Doc/V4.Updates b/trunk/verif/Spin/Doc/V4.Updates new file mode 100755 index 00000000..183ffa78 --- /dev/null +++ b/trunk/verif/Spin/Doc/V4.Updates @@ -0,0 +1,654 @@ +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 diff --git a/trunk/verif/Spin/Doc/V5.Updates b/trunk/verif/Spin/Doc/V5.Updates new file mode 100755 index 00000000..bb90d82e --- /dev/null +++ b/trunk/verif/Spin/Doc/V5.Updates @@ -0,0 +1,174 @@ +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. diff --git a/trunk/verif/Spin/Man/spin.1 b/trunk/verif/Spin/Man/spin.1 new file mode 100755 index 00000000..3dbc4931 --- /dev/null +++ b/trunk/verif/Spin/Man/spin.1 @@ -0,0 +1,274 @@ +.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 (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 diff --git a/trunk/verif/Spin/README.html b/trunk/verif/Spin/README.html new file mode 100755 index 00000000..4df3ed9d --- /dev/null +++ b/trunk/verif/Spin/README.html @@ -0,0 +1,374 @@ + + +Spin - Version 5.1 - December 2007 + + + +

+

SPIN README

+
    +

    Overview of this File

    +
      +
    1. Downloading Spin +
    2. Installing Spin +
    3. Related software + (gcc, cpp, tcl/tk wish, yacc, dot, jspin, ltl2ba)
    +
    + +

    0. Overview

    This readme file contains the guidelines for +downloading and installing Spin and related software on Unix/Linux and Windows +platforms. Refer to Spin's +homepage for a general description of Spin, with pointers to manual pages, newsletters. +

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

    Commercial application of the Spin software is also allowed, but requires the +acceptance of a basic license. Refer to the Spin Public license for details. +

    +


    + +

    1. Downloading Spin

    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 +Spin Binaries. +

    +All binaries have an extension that matches the Spin version number, +such as spin427.exe. To install the binary, rename it to +spin.exe and copy it into your bin directory. +

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

      +
    • Unix systems:
      download the most recent .tar-file with sources, + the graphical interface Xspin, documentation and examples from the Spin Distribution, and + continue at Step 2a.
    • +

      +
    • PCs (Windows95/98/2000/NT/XP):
      download the most recent + pc_spin*.zip file, with a precompiled Spin executable, the graphical interface + Xspin, and some examples from the Spin Distribution, and + continue at Step 2b.
    • +

      +
    • Macs (Mac OS X):
      download the most recent .tar-file with sources, + from the Spin Distribution, + and continue at Step 2c. +
    +

    +


    + +

    2. Installing Spin

    +

    +


    + +

    2a. Installing Spin on a Unix/Linux +System

    +
      Place the *.tar.gz file from the Spin Source Distribution 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:
      	gunzip *.tar.gz
      +	tar -xf *.tar
      +	cd Src*
      +	make 	# or, on older distributions: make -f make_unix
      +
      +

      If you are on a SOLARIS system, edit the makefile and add + -DSOLARIS 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. +

      If all else fails, you can also compile everything with the following line: +

      	yacc -v -d spin.y; cc -o spin *.c
      +
      +

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

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

      Testing

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

      Adding Xspin (Unix/Linux)

      Xspin is an optional, but highly + recommended, graphical user interface to Spin, written in Tcl/Tk. To obtain + Tcl/Tk, see Related + software. The Xspin source can be found the Xspin4.? directory that will + also have been created when you unpacked the source tarfile. +

      The current version of Xspin is compatible with +

      	Tk version 4.2 - Tcl version 7.6
      +	Tk version 8.4 - Tcl version 8.4
      +
      +

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

      	info tclversion
      +	puts $tk_version
      +
      You can find out which version of Spin you have by typing, at the + command prompt:
      	$ spin -V
      +
      +

      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 Related software. +

      To install Xspin on Unix/Linux: +

        +
      • cd to directory Xspin... from the distribution, +
      • 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. +
      • copy the file into a directory within your search path, renamed to plain xspin +and make it + executable, for instance: +
        	cp xspin510.tcl /usr/local/bin/xspin
        +	chmod +x /usr/local/bin/xspin
        +
        +
      • On Unix/Linux, invoke the program by typing +
        	xspin	# or xspin.tcl if you keep the extension...
        +or
        +	xspin promela_spec
        +
        +For example:
        +	cd Test
        +	xspin leader
        +
      Check the online Help menus in xspin for more details on + routine use.
    +

    +


    + +

    2b. Installing Spin on a Windows +PC

    +
      If you just need to update the Spin executable itself, download a new + version from http://spinroot.com/spin/Bin/index.html + If you need more files, e.g. a new copy of Xspin, download the latest + pc_spin*.zip file from http://spinroot.com/spin/Src/index.html + 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) +

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

      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.: +

      +	#include <stdio.h>
      +	int main(void) { printf("hello\n"); }
      +
      +

      To find a public version of a C compiler and some instructions on how to + install it see Related + software. +

      Adding Xspin (PC)

      To run Xspin on a PC, you need the PC + version of Tcl/Tk, which you can find under Related software. +

      The xspin*.tcl source is contained in the .zip file of the distribution. 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. +

      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:

      	xspin leader
      +
      +

      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:

      	source xspin.tcl  # or whatever else you've named this
      +
      and you should be up and running. +

      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.

    +

    +

    2c. Installing Spin on a Mac

    +
      + 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: domi@saargate.de. +

      +The C preprocessor on Mac OS X cannot be found in /lib. +Change the path in the makefile for the +proper location (/usr/bin/cpp), 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 +

        +
        +-DCPP="\"/usr/bin/cpp -xassembler-with-cpp\""
        +
        +
      +to the definition of CFLAGS. + +

      +

      Adding Xspin (Unix)

      +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 + +

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

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

        +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
        +
        +
      +
    + +
    + +

    3. Related Software

    +
      Pointers to public domain versions of some related software packages are + discussed below: +
        +
      • Gcc,
      • +
      • Cpp,
      • +
      • Yacc,
      • +
      • Tcl/Tk wish,
      • +
      • Dot,
      • +
      • JSpin, and
      • +
      • Ltl2Ba.
      +

      GCC

      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 cygwin toolkit, which is mostly self-installing and + available from: http://www.cygwin.com/ +
      See also what's available in http://spinroot.com/spin/Bin/index.html. + +

      Tcl/Tk Wish

      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: http://www.tcl.tk/ or also (a more recent extension): + + http://www.activestate.com/Products/ActiveTcl/
      More details can be found + in netnews-group: comp.lang.tcl + +

      Yacc (optional)

      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 cygwin, or also from: +
      	ftp://ftp.cs.berkeley.edu/ucb/4bsd/byacc.tar.Z
      +
      A copy of this file is also available in: http://spinroot.com/spin/Bin/index.html + (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. +

      Dot (optional)

      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 +
      +	http://www.graphviz.org/
      +
      +The are both PC and Unix versions of dot available. For documentation of +dot see, for instance: +
      	A technique for drawing directed graphs,
      +	by Gansner, Koutsofios, North and Vo,
      +	IEEE-TSE, March, 1993.
      +
      +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: +
      +	set DOT "C:/Program\\ Files\ATT\Graphviz/bin/dot.exe"
      +
      +(the line that sets the location of DOT appears near the top of the xspin.tcl file). +

      JSpin (optional)

      +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 +
      +	http://stwww.weizmann.ac.il/g-cs/benari/jspin/
      +
      +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. +

      Ltl2Ba (optional)

      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: +
      +	http://spinroot.com/spin/Src/ltl2ba.tar.gz
      +
      +The latest version can be obtained from the authors website via: +
      +	http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/download.php
      +See also
      +	http://www.lsv.ens-cachan.fr/~gastin/ltl2ba/index.php
      +
      +The C source code can be linked with Spin, or run as a standalone tool. +

      +

    +
    + + + + + + + +
    Spin HomePage +(Page Last Updated: 26 April 2008) +
    +
    + + diff --git a/trunk/verif/Spin/Src5.1.6/dstep.c b/trunk/verif/Spin/Src5.1.6/dstep.c new file mode 100755 index 00000000..5f6f3b39 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/dstep.c @@ -0,0 +1,411 @@ +/***** 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; + } } +} diff --git a/trunk/verif/Spin/Src5.1.6/flow.c b/trunk/verif/Spin/Src5.1.6/flow.c new file mode 100755 index 00000000..ac3e4e4b --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/flow.c @@ -0,0 +1,794 @@ +/***** 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); +} diff --git a/trunk/verif/Spin/Src5.1.6/guided.c b/trunk/verif/Spin/Src5.1.6/guided.c new file mode 100755 index 00000000..5f72f696 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/guided.c @@ -0,0 +1,333 @@ +/***** 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 +#include +#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, "<<<<>>>>\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, "\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", + 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; +} diff --git a/trunk/verif/Spin/Src5.1.6/main.c b/trunk/verif/Spin/Src5.1.6/main.c new file mode 100755 index 00000000..528b729c --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/main.c @@ -0,0 +1,788 @@ +/***** 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 +#include "spin.h" +#include "version.h" +#include +/* #include */ +#include +#ifdef PC +#include +extern int unlink(const char *); +#else +#include +#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(""); + 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; + } +} diff --git a/trunk/verif/Spin/Src5.1.6/make_pc b/trunk/verif/Spin/Src5.1.6/make_pc new file mode 100755 index 00000000..d6641e83 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/make_pc @@ -0,0 +1,31 @@ +#!/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 diff --git a/trunk/verif/Spin/Src5.1.6/makefile b/trunk/verif/Spin/Src5.1.6/makefile new file mode 100755 index 00000000..c8c93c8f --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/makefile @@ -0,0 +1,74 @@ +# 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 diff --git a/trunk/verif/Spin/Src5.1.6/mesg.c b/trunk/verif/Spin/Src5.1.6/mesg.c new file mode 100755 index 00000000..f89338f5 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/mesg.c @@ -0,0 +1,650 @@ +/***** 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); + } +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen1.c b/trunk/verif/Spin/Src5.1.6/pangen1.c new file mode 100755 index 00000000..36694d8b --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen1.c @@ -0,0 +1,1298 @@ +/***** 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; kcontents", 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"); +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen1.h b/trunk/verif/Spin/Src5.1.6/pangen1.h new file mode 100755 index 00000000..2a0ec12d --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen1.h @@ -0,0 +1,6529 @@ +/***** 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(\"<<<<>>>>\\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< RANDSTOR) return 0;", + "#endif", + " for (;;)", + " { SS[x%%udmem] |= (1< RANDSTOR) return 0;", + "#endif", + " for (;;)", + " { SS[x] |= (1< 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< 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<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 ", /* 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 ", /* 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<>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<>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<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] \\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<= 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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pangen2.c b/trunk/verif/Spin/Src5.1.6/pangen2.c new file mode 100755 index 00000000..bc614133 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen2.c @@ -0,0 +1,3095 @@ +/***** 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", ""); +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen2.h b/trunk/verif/Spin/Src5.1.6/pangen2.h new file mode 100755 index 00000000..58c1b782 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen2.h @@ -0,0 +1,987 @@ +/***** 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 ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#if defined(WIN32) || defined(WIN64)", + "#include ", + "#else", + "#include ", + "#include ", + "#endif", + "#include ", /* defines off_t */ + "#include ", + "#include ", + + "#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<= 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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pangen3.c b/trunk/verif/Spin/Src5.1.6/pangen3.c new file mode 100755 index 00000000..53b76049 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen3.c @@ -0,0 +1,394 @@ +/***** 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; +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen3.h b/trunk/verif/Spin/Src5.1.6/pangen3.h new file mode 100755 index 00000000..c4c17fa8 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen3.h @@ -0,0 +1,1023 @@ +/***** 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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pangen4.c b/trunk/verif/Spin/Src5.1.6/pangen4.c new file mode 100755 index 00000000..12477c8b --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen4.c @@ -0,0 +1,351 @@ +/***** 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; +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen4.h b/trunk/verif/Spin/Src5.1.6/pangen4.h new file mode 100755 index 00000000..d80bdeaa --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen4.h @@ -0,0 +1,727 @@ +/***** 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 in 1992. */ + +static char *Dfa[] = { + "#ifdef MA", + "/*", + "#include ", + "#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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pangen5.c b/trunk/verif/Spin/Src5.1.6/pangen5.c new file mode 100755 index 00000000..68f62a39 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen5.c @@ -0,0 +1,862 @@ +/***** 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] \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\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\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; + } +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen5.h b/trunk/verif/Spin/Src5.1.6/pangen5.h new file mode 100755 index 00000000..8a876efd --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen5.h @@ -0,0 +1,424 @@ +/***** 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 \\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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pangen6.c b/trunk/verif/Spin/Src5.1.6/pangen6.c new file mode 100755 index 00000000..ec2658ed --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen6.c @@ -0,0 +1,2354 @@ +/***** 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("<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<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<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<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<nstates; cnt++) + if (!fsm_tbl[cnt]->seen) + { i = cnt / BPW; + j = cnt % BPW; + f->dom[i] &= ~(1<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<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 */ + } } +} diff --git a/trunk/verif/Spin/Src5.1.6/pangen6.h b/trunk/verif/Spin/Src5.1.6/pangen6.h new file mode 100755 index 00000000..431349b5 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pangen6.h @@ -0,0 +1,2841 @@ +/***** 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 ", + "", + " #ifdef WIN64", + " #define long long long", + " #endif", + "#else", + "#include ", + "#include ", + "#include ", + "#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< 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< 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< 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, +}; diff --git a/trunk/verif/Spin/Src5.1.6/pc_zpp.c b/trunk/verif/Spin/Src5.1.6/pc_zpp.c new file mode 100755 index 00000000..5cfc61ac --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/pc_zpp.c @@ -0,0 +1,408 @@ +/***** 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 +#include +#include +#include + +#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 diff --git a/trunk/verif/Spin/Src5.1.6/ps_msc.c b/trunk/verif/Spin/Src5.1.6/ps_msc.c new file mode 100755 index 00000000..31165578 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/ps_msc.c @@ -0,0 +1,445 @@ +/***** 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); + } +} diff --git a/trunk/verif/Spin/Src5.1.6/reprosrc.c b/trunk/verif/Spin/Src5.1.6/reprosrc.c new file mode 100755 index 00000000..0d4ba6be --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/reprosrc.c @@ -0,0 +1,136 @@ +/***** 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 +#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); +} diff --git a/trunk/verif/Spin/Src5.1.6/run.c b/trunk/verif/Spin/Src5.1.6/run.c new file mode 100755 index 00000000..4c57d0b3 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/run.c @@ -0,0 +1,602 @@ +/***** 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 +#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; +} diff --git a/trunk/verif/Spin/Src5.1.6/sched.c b/trunk/verif/Spin/Src5.1.6/sched.c new file mode 100755 index 00000000..ed4b0851 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/sched.c @@ -0,0 +1,1036 @@ +/***** 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 +#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<>\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<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(" "); + } + } +} + +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; +} diff --git a/trunk/verif/Spin/Src5.1.6/spin b/trunk/verif/Spin/Src5.1.6/spin new file mode 100755 index 00000000..6b70f42b Binary files /dev/null and b/trunk/verif/Spin/Src5.1.6/spin differ diff --git a/trunk/verif/Spin/Src5.1.6/spin.h b/trunk/verif/Spin/Src5.1.6/spin.h new file mode 100755 index 00000000..aa55c6f7 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/spin.h @@ -0,0 +1,404 @@ +/***** 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 +#include +#include +#ifndef PC +#include +#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 diff --git a/trunk/verif/Spin/Src5.1.6/spin.y b/trunk/verif/Spin/Src5.1.6/spin.y new file mode 100755 index 00000000..3d6cb77a --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/spin.y @@ -0,0 +1,731 @@ +/***** 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 + +#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); +} diff --git a/trunk/verif/Spin/Src5.1.6/spinlex.c b/trunk/verif/Spin/Src5.1.6/spinlex.c new file mode 100755 index 00000000..9265c871 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/spinlex.c @@ -0,0 +1,1405 @@ +/***** 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 +#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(""); +#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; +} diff --git a/trunk/verif/Spin/Src5.1.6/structs.c b/trunk/verif/Spin/Src5.1.6/structs.c new file mode 100755 index 00000000..fe90e162 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/structs.c @@ -0,0 +1,664 @@ +/***** 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<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; +} diff --git a/trunk/verif/Spin/Src5.1.6/sym.c b/trunk/verif/Spin/Src5.1.6/sym.c new file mode 100755 index 00000000..823dbf06 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/sym.c @@ -0,0 +1,533 @@ +/***** 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", -(sp->Nid)); + else + printf("\t"); + 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); + } } +} diff --git a/trunk/verif/Spin/Src5.1.6/tl.h b/trunk/verif/Spin/Src5.1.6/tl.h new file mode 100755 index 00000000..1e6c0949 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl.h @@ -0,0 +1,128 @@ +/***** 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 +#include + +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); } } diff --git a/trunk/verif/Spin/Src5.1.6/tl_buchi.c b/trunk/verif/Spin/Src5.1.6/tl_buchi.c new file mode 100755 index 00000000..7dd9d30d --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_buchi.c @@ -0,0 +1,666 @@ +/***** 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"); +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_cache.c b/trunk/verif/Spin/Src5.1.6/tl_cache.c new file mode 100755 index 00000000..fc902dcb --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_cache.c @@ -0,0 +1,328 @@ +/***** 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; +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_lex.c b/trunk/verif/Spin/Src5.1.6/tl_lex.c new file mode 100755 index 00000000..110e06ea --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_lex.c @@ -0,0 +1,148 @@ +/***** 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 +#include +#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; +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_main.c b/trunk/verif/Spin/Src5.1.6/tl_main.c new file mode 100755 index 00000000..10ab0e9b --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_main.c @@ -0,0 +1,234 @@ +/***** 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); +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_mem.c b/trunk/verif/Spin/Src5.1.6/tl_mem.c new file mode 100755 index 00000000..52021e46 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_mem.c @@ -0,0 +1,120 @@ +/***** 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); + } +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_parse.c b/trunk/verif/Spin/Src5.1.6/tl_parse.c new file mode 100755 index 00000000..6206a0d9 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_parse.c @@ -0,0 +1,400 @@ +/***** 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); +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_rewrt.c b/trunk/verif/Spin/Src5.1.6/tl_rewrt.c new file mode 100755 index 00000000..8a70b48d --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_rewrt.c @@ -0,0 +1,304 @@ +/***** 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; +} diff --git a/trunk/verif/Spin/Src5.1.6/tl_trans.c b/trunk/verif/Spin/Src5.1.6/tl_trans.c new file mode 100755 index 00000000..c3992a2f --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/tl_trans.c @@ -0,0 +1,878 @@ +/***** 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"); + } +} diff --git a/trunk/verif/Spin/Src5.1.6/vars.c b/trunk/verif/Spin/Src5.1.6/vars.c new file mode 100755 index 00000000..291983dd --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/vars.c @@ -0,0 +1,357 @@ +/***** 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<>(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); + } } } +} diff --git a/trunk/verif/Spin/Src5.1.6/version.h b/trunk/verif/Spin/Src5.1.6/version.h new file mode 100755 index 00000000..23eb6a51 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/version.h @@ -0,0 +1 @@ +#define SpinVersion "Spin Version 5.1.6 -- 9 May 2008" diff --git a/trunk/verif/Spin/Src5.1.6/y.output b/trunk/verif/Spin/Src5.1.6/y.output new file mode 100644 index 00000000..9c7ce68b --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/y.output @@ -0,0 +1,6714 @@ +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) diff --git a/trunk/verif/Spin/Src5.1.6/y.tab.h b/trunk/verif/Spin/Src5.1.6/y.tab.h new file mode 100644 index 00000000..2a963670 --- /dev/null +++ b/trunk/verif/Spin/Src5.1.6/y.tab.h @@ -0,0 +1,209 @@ +/* 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; + diff --git a/trunk/verif/Spin/Test/README.tests b/trunk/verif/Spin/Test/README.tests new file mode 100755 index 00000000..f072ccd3 --- /dev/null +++ b/trunk/verif/Spin/Test/README.tests @@ -0,0 +1,308 @@ +-- 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 + <<<<>>>> + 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) + 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 diff --git a/trunk/verif/Spin/Test/abp b/trunk/verif/Spin/Test/abp new file mode 100755 index 00000000..0b618902 --- /dev/null +++ b/trunk/verif/Spin/Test/abp @@ -0,0 +1,47 @@ +/* + * 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 +} diff --git a/trunk/verif/Spin/Test/erathostenes b/trunk/verif/Spin/Test/erathostenes new file mode 100755 index 00000000..b14446cd --- /dev/null +++ b/trunk/verif/Spin/Test/erathostenes @@ -0,0 +1,40 @@ +/* + 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 +} diff --git a/trunk/verif/Spin/Test/eratosthenes b/trunk/verif/Spin/Test/eratosthenes new file mode 100755 index 00000000..36c45074 --- /dev/null +++ b/trunk/verif/Spin/Test/eratosthenes @@ -0,0 +1,49 @@ +/* + 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 +} diff --git a/trunk/verif/Spin/Test/examples b/trunk/verif/Spin/Test/examples new file mode 100755 index 00000000..0edd8879 --- /dev/null +++ b/trunk/verif/Spin/Test/examples @@ -0,0 +1,1171 @@ +# 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<&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 diff --git a/trunk/verif/Spin/Test/hello b/trunk/verif/Spin/Test/hello new file mode 100755 index 00000000..cf80a124 --- /dev/null +++ b/trunk/verif/Spin/Test/hello @@ -0,0 +1,3 @@ +init { + printf("passed first test!\n") +} diff --git a/trunk/verif/Spin/Test/leader b/trunk/verif/Spin/Test/leader new file mode 100755 index 00000000..fa7ffc39 --- /dev/null +++ b/trunk/verif/Spin/Test/leader @@ -0,0 +1,85 @@ +/* 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 + } +} + diff --git a/trunk/verif/Spin/Test/leader.ltl b/trunk/verif/Spin/Test/leader.ltl new file mode 100755 index 00000000..776c3677 --- /dev/null +++ b/trunk/verif/Spin/Test/leader.ltl @@ -0,0 +1,70 @@ +#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 diff --git a/trunk/verif/Spin/Test/leader2 b/trunk/verif/Spin/Test/leader2 new file mode 100755 index 00000000..40f2ec3b --- /dev/null +++ b/trunk/verif/Spin/Test/leader2 @@ -0,0 +1,130 @@ +/* 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 diff --git a/trunk/verif/Spin/Test/leader_trace b/trunk/verif/Spin/Test/leader_trace new file mode 100755 index 00000000..a81f7e97 --- /dev/null +++ b/trunk/verif/Spin/Test/leader_trace @@ -0,0 +1,96 @@ +/* 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 + } +} + diff --git a/trunk/verif/Spin/Test/loops b/trunk/verif/Spin/Test/loops new file mode 100755 index 00000000..1ed711dd --- /dev/null +++ b/trunk/verif/Spin/Test/loops @@ -0,0 +1,12 @@ +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 +} diff --git a/trunk/verif/Spin/Test/mobile1 b/trunk/verif/Spin/Test/mobile1 new file mode 100755 index 00000000..b179f0bb --- /dev/null +++ b/trunk/verif/Spin/Test/mobile1 @@ -0,0 +1,151 @@ +/* + * 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 +} diff --git a/trunk/verif/Spin/Test/mobile1.ltl b/trunk/verif/Spin/Test/mobile1.ltl new file mode 100755 index 00000000..ea51a483 --- /dev/null +++ b/trunk/verif/Spin/Test/mobile1.ltl @@ -0,0 +1,112 @@ +#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 diff --git a/trunk/verif/Spin/Test/mobile2 b/trunk/verif/Spin/Test/mobile2 new file mode 100755 index 00000000..4526b10a --- /dev/null +++ b/trunk/verif/Spin/Test/mobile2 @@ -0,0 +1,132 @@ +/* + * 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 +} diff --git a/trunk/verif/Spin/Test/mobile2.ltl b/trunk/verif/Spin/Test/mobile2.ltl new file mode 100755 index 00000000..93d7def7 --- /dev/null +++ b/trunk/verif/Spin/Test/mobile2.ltl @@ -0,0 +1,107 @@ +#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 diff --git a/trunk/verif/Spin/Test/pathfinder b/trunk/verif/Spin/Test/pathfinder new file mode 100755 index 00000000..a264eed4 --- /dev/null +++ b/trunk/verif/Spin/Test/pathfinder @@ -0,0 +1,50 @@ +/* + * 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 + +} + diff --git a/trunk/verif/Spin/Test/peterson b/trunk/verif/Spin/Test/peterson new file mode 100755 index 00000000..156da7ed --- /dev/null +++ b/trunk/verif/Spin/Test/peterson @@ -0,0 +1,20 @@ +/* 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 +} diff --git a/trunk/verif/Spin/Test/petersonN b/trunk/verif/Spin/Test/petersonN new file mode 100755 index 00000000..1cccdb52 --- /dev/null +++ b/trunk/verif/Spin/Test/petersonN @@ -0,0 +1,45 @@ +/* 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 +} diff --git a/trunk/verif/Spin/Test/pftp b/trunk/verif/Spin/Test/pftp new file mode 100755 index 00000000..cd05dce7 --- /dev/null +++ b/trunk/verif/Spin/Test/pftp @@ -0,0 +1,204 @@ +/* + * 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 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]) + } +} diff --git a/trunk/verif/Spin/Test/priorities b/trunk/verif/Spin/Test/priorities new file mode 100755 index 00000000..bf7a8c41 --- /dev/null +++ b/trunk/verif/Spin/Test/priorities @@ -0,0 +1,22 @@ +/* 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; +} } diff --git a/trunk/verif/Spin/Test/snoopy b/trunk/verif/Spin/Test/snoopy new file mode 100755 index 00000000..20c0294e --- /dev/null +++ b/trunk/verif/Spin/Test/snoopy @@ -0,0 +1,257 @@ +/* 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() + } +} diff --git a/trunk/verif/Spin/Test/sort b/trunk/verif/Spin/Test/sort new file mode 100755 index 00000000..e1f5068b --- /dev/null +++ b/trunk/verif/Spin/Test/sort @@ -0,0 +1,75 @@ +/* + * 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] ) + } +} diff --git a/trunk/verif/Spin/Test/wordcount b/trunk/verif/Spin/Test/wordcount new file mode 100755 index 00000000..5c3efc9b --- /dev/null +++ b/trunk/verif/Spin/Test/wordcount @@ -0,0 +1,33 @@ +/* + 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) +} diff --git a/trunk/verif/Spin/Xspin5.1/xspin510.tcl b/trunk/verif/Spin/Xspin5.1/xspin510.tcl new file mode 100755 index 00000000..3597ebe2 --- /dev/null +++ b/trunk/verif/Spin/Xspin5.1/xspin510.tcl @@ -0,0 +1,7095 @@ +#!/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 +#-- 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 { + .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 { + .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 { + tk_popup .inp.t.edit %X %Y + } + bind .inp.t { 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 "" + return 1 + } + + add_log "" + 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 "" + 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 "" + 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 "" + 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 +# 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 "FileSelect.Selected $w %x %y" + bind $fileList "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 { + set nd [.b.bot.entry get] + if {[file isdirectory $nd]} { + cd $nd + fillerup $suffix + add_log "cd $nd" + } + } + + fillerup $suffix + bind .b.top.list { + 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 "" + 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 "" + + 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 { + .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 "" + 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 "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 "+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 "" + } + + 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 "" + + 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 { + 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 "" + 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 " + .f$cnr.c itemconfigure {$Lab} -fill black -text {$t} + if {[string first \"end\" $n] < 0 } { set_src {$t} } + " + .f$cnr.c bind $Lab " + .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 " + .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 " + .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 "$cv scan dragto %x %y" + + .f$cnr.c bind node " + moveNode $cnr \[.f$cnr.c find withtag current] %x %y 1 + " + +# .f$cnr.c bind node " +# 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 "" + 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 "" + catch { + tk_messageBox -icon info -message "" + } + 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 {} ;# no moving + .f$cnr.c bind node {} + 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 { 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 "" + 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 "" + 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 { 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 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 or hit the 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 "" + 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 "" + 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 " 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 "" + } +##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 ""] + } + } elseif { [string first "" $stmnt] == 0} { + set HAS 3 + set stmnt [format ""] + } + 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 "" + } + + 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) " + .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) " + .f$dbox2.c itemconfigure $T($pstp,$pno) \ + -font $SmallFont -text {$pstp} + " + } else { + .f$dbox2.c bind $T($pstp,$pno) " + .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 "<>" + } 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 "" + } else { + add_log "" + } + 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 "" + + 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 "report $k" + bind .prompt.b$i "report 0" + bind .prompt.b$i "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 { + 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 { + 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 { + 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 "" + if {$havedest != "0"} { + $havedest insert end "\n" + } + + set nochange [no_change] + if {$nochange == 0} { set LastGenerate "" } + + if {$LastGenerate == $a_options} { + add_log "" + if {$havedest != "0"} { + $havedest insert end "\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 "" + if {$havedest != "0"} { + $havedest insert end "\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 "\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 "\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 "\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 "" + 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 "" + 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 "" + 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 diff --git a/trunk/verif/examples/buffer.spin b/trunk/verif/examples/buffer.spin new file mode 100644 index 00000000..41965dc3 --- /dev/null +++ b/trunk/verif/examples/buffer.spin @@ -0,0 +1,289 @@ + +// LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v1 +// Created for the Spin validator. +// Mathieu Desnoyers +// 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); + } +} diff --git a/trunk/verif/examples/buffer.spin.bkp1 b/trunk/verif/examples/buffer.spin.bkp1 new file mode 100644 index 00000000..8a0ad29c --- /dev/null +++ b/trunk/verif/examples/buffer.spin.bkp1 @@ -0,0 +1,154 @@ +//#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); + } +} diff --git a/trunk/verif/examples/buffer.spin.bkp2 b/trunk/verif/examples/buffer.spin.bkp2 new file mode 100644 index 00000000..99779ff0 --- /dev/null +++ b/trunk/verif/examples/buffer.spin.bkp2 @@ -0,0 +1,268 @@ + +// 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); + } +} diff --git a/trunk/verif/examples/buffer.spin.missing_retrieve_count b/trunk/verif/examples/buffer.spin.missing_retrieve_count new file mode 100644 index 00000000..8bec913a --- /dev/null +++ b/trunk/verif/examples/buffer.spin.missing_retrieve_count @@ -0,0 +1,228 @@ +//#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); + } +} diff --git a/trunk/verif/examples/buffer.spin.trail b/trunk/verif/examples/buffer.spin.trail new file mode 100644 index 00000000..93bb3331 --- /dev/null +++ b/trunk/verif/examples/buffer.spin.trail @@ -0,0 +1,102 @@ +-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 diff --git a/trunk/verif/examples/pan b/trunk/verif/examples/pan new file mode 100755 index 00000000..a0356c18 Binary files /dev/null and b/trunk/verif/examples/pan differ diff --git a/trunk/verif/examples/pan.b b/trunk/verif/examples/pan.b new file mode 100644 index 00000000..8816afb3 --- /dev/null +++ b/trunk/verif/examples/pan.b @@ -0,0 +1,439 @@ + 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; + } + diff --git a/trunk/verif/examples/pan.c b/trunk/verif/examples/pan.c new file mode 100644 index 00000000..928afc4e --- /dev/null +++ b/trunk/verif/examples/pan.c @@ -0,0 +1,11530 @@ +/*** Generated by Spin Version 5.1.6 -- 9 May 2008 ***/ +/*** From source: buffer.spin ***/ + +#ifdef SC +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined(WIN32) || defined(WIN64) +#include +#else +#include +#include +#endif +#include +#include +#include +#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("<<<<>>>>\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< RANDSTOR) return 0; +#endif + for (;;) + { SS[x%udmem] |= (1< RANDSTOR) return 0; +#endif + for (;;) + { SS[x] |= (1< 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 + + #ifdef WIN64 + #define long long long + #endif +#else +#include +#include +#include +#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< 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< 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< 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< 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<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 + #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 +#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<>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<>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<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] \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<= 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 +#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 \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 */ } diff --git a/trunk/verif/examples/pan.h b/trunk/verif/examples/pan.h new file mode 100644 index 00000000..0dcc8a8b --- /dev/null +++ b/trunk/verif/examples/pan.h @@ -0,0 +1,613 @@ +#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 diff --git a/trunk/verif/examples/pan.m b/trunk/verif/examples/pan.m new file mode 100644 index 00000000..5d40303e --- /dev/null +++ b/trunk/verif/examples/pan.m @@ -0,0 +1,1039 @@ +#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" - [((ii)<((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" - [((ii)<((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 + } + diff --git a/trunk/verif/examples/pan.t b/trunk/verif/examples/pan.t new file mode 100644 index 00000000..4dc0dfa0 --- /dev/null +++ b/trunk/verif/examples/pan.t @@ -0,0 +1,926 @@ +#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 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 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<= 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 diff --git a/trunk/verif/examples/run b/trunk/verif/examples/run new file mode 100755 index 00000000..5a47e644 --- /dev/null +++ b/trunk/verif/examples/run @@ -0,0 +1,5 @@ +#!/bin/bash + +../Spin/Src5.1.6/spin -a buffer.spin +cc -DSAFETY -o pan pan.c +./pan diff --git a/trunk/verif/examples/run2 b/trunk/verif/examples/run2 new file mode 100755 index 00000000..f69a545b --- /dev/null +++ b/trunk/verif/examples/run2 @@ -0,0 +1,2 @@ +#!/bin/bash +../Spin/Src5.1.6/spin -t -p buffer.spin |less diff --git a/trunk/verif/examples/run3 b/trunk/verif/examples/run3 new file mode 100755 index 00000000..69ac0915 --- /dev/null +++ b/trunk/verif/examples/run3 @@ -0,0 +1,5 @@ +#!/bin/bash + +../Spin/Src5.1.6/spin -a buffer.spin +cc -o pan pan.c +./pan diff --git a/trunk/verif/examples/spin-increment.spin b/trunk/verif/examples/spin-increment.spin new file mode 100644 index 00000000..b308fb56 --- /dev/null +++ b/trunk/verif/examples/spin-increment.spin @@ -0,0 +1,40 @@ +#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) + } +} diff --git a/trunk/verif/examples/spin-increment.spin.trail b/trunk/verif/examples/spin-increment.spin.trail new file mode 100644 index 00000000..aff3eba3 --- /dev/null +++ b/trunk/verif/examples/spin-increment.spin.trail @@ -0,0 +1,22 @@ +-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 diff --git a/trunk/verif/spin516.tar.gz b/trunk/verif/spin516.tar.gz new file mode 100644 index 00000000..2843959e Binary files /dev/null and b/trunk/verif/spin516.tar.gz differ