From 0b55f123deb998c049b24b23c1651519b639a95b Mon Sep 17 00:00:00 2001 From: compudj Date: Wed, 15 Oct 2008 14:14:27 +0000 Subject: [PATCH] add formal verif git-svn-id: http://ltt.polymtl.ca/svn@3108 04897980-b3bd-0310-b5e0-8ef037075253 --- trunk/verif/Spin/Doc/Book.Ch6.add | 183 + trunk/verif/Spin/Doc/Book.Errata | 444 + trunk/verif/Spin/Doc/Book.answers | 612 + trunk/verif/Spin/Doc/Book.samples | 1769 +++ trunk/verif/Spin/Doc/Book2003Errata.html | 383 + trunk/verif/Spin/Doc/Book91_Ch6_add.txt | 183 + trunk/verif/Spin/Doc/Book91_Errata.txt | 452 + trunk/verif/Spin/Doc/Book91_answers.txt | 612 + trunk/verif/Spin/Doc/Book91_samples_bundle | 1769 +++ trunk/verif/Spin/Doc/V1.Updates | 358 + trunk/verif/Spin/Doc/V2.Updates | 1163 ++ trunk/verif/Spin/Doc/V3.Updates | 925 ++ trunk/verif/Spin/Doc/V4.Updates | 654 + trunk/verif/Spin/Doc/V5.Updates | 174 + trunk/verif/Spin/Man/spin.1 | 274 + trunk/verif/Spin/README.html | 374 + trunk/verif/Spin/Src5.1.6/dstep.c | 411 + trunk/verif/Spin/Src5.1.6/flow.c | 794 ++ trunk/verif/Spin/Src5.1.6/guided.c | 333 + trunk/verif/Spin/Src5.1.6/main.c | 788 ++ trunk/verif/Spin/Src5.1.6/make_pc | 31 + trunk/verif/Spin/Src5.1.6/makefile | 74 + trunk/verif/Spin/Src5.1.6/mesg.c | 650 + trunk/verif/Spin/Src5.1.6/pangen1.c | 1298 ++ trunk/verif/Spin/Src5.1.6/pangen1.h | 6529 +++++++++ trunk/verif/Spin/Src5.1.6/pangen2.c | 3095 +++++ trunk/verif/Spin/Src5.1.6/pangen2.h | 987 ++ trunk/verif/Spin/Src5.1.6/pangen3.c | 394 + trunk/verif/Spin/Src5.1.6/pangen3.h | 1023 ++ trunk/verif/Spin/Src5.1.6/pangen4.c | 351 + trunk/verif/Spin/Src5.1.6/pangen4.h | 727 + trunk/verif/Spin/Src5.1.6/pangen5.c | 862 ++ trunk/verif/Spin/Src5.1.6/pangen5.h | 424 + trunk/verif/Spin/Src5.1.6/pangen6.c | 2354 ++++ trunk/verif/Spin/Src5.1.6/pangen6.h | 2841 ++++ trunk/verif/Spin/Src5.1.6/pc_zpp.c | 408 + trunk/verif/Spin/Src5.1.6/ps_msc.c | 445 + trunk/verif/Spin/Src5.1.6/reprosrc.c | 136 + trunk/verif/Spin/Src5.1.6/run.c | 602 + trunk/verif/Spin/Src5.1.6/sched.c | 1036 ++ trunk/verif/Spin/Src5.1.6/spin | Bin 0 -> 623534 bytes trunk/verif/Spin/Src5.1.6/spin.h | 404 + trunk/verif/Spin/Src5.1.6/spin.y | 731 + trunk/verif/Spin/Src5.1.6/spinlex.c | 1405 ++ trunk/verif/Spin/Src5.1.6/structs.c | 664 + trunk/verif/Spin/Src5.1.6/sym.c | 533 + trunk/verif/Spin/Src5.1.6/tl.h | 128 + trunk/verif/Spin/Src5.1.6/tl_buchi.c | 666 + trunk/verif/Spin/Src5.1.6/tl_cache.c | 328 + trunk/verif/Spin/Src5.1.6/tl_lex.c | 148 + trunk/verif/Spin/Src5.1.6/tl_main.c | 234 + trunk/verif/Spin/Src5.1.6/tl_mem.c | 120 + trunk/verif/Spin/Src5.1.6/tl_parse.c | 400 + trunk/verif/Spin/Src5.1.6/tl_rewrt.c | 304 + trunk/verif/Spin/Src5.1.6/tl_trans.c | 878 ++ trunk/verif/Spin/Src5.1.6/vars.c | 357 + trunk/verif/Spin/Src5.1.6/version.h | 1 + trunk/verif/Spin/Src5.1.6/y.output | 6714 +++++++++ trunk/verif/Spin/Src5.1.6/y.tab.h | 209 + trunk/verif/Spin/Test/README.tests | 308 + trunk/verif/Spin/Test/abp | 47 + trunk/verif/Spin/Test/erathostenes | 40 + trunk/verif/Spin/Test/eratosthenes | 49 + trunk/verif/Spin/Test/examples | 1171 ++ trunk/verif/Spin/Test/hello | 3 + trunk/verif/Spin/Test/leader | 85 + trunk/verif/Spin/Test/leader.ltl | 70 + trunk/verif/Spin/Test/leader2 | 130 + trunk/verif/Spin/Test/leader_trace | 96 + trunk/verif/Spin/Test/loops | 12 + trunk/verif/Spin/Test/mobile1 | 151 + trunk/verif/Spin/Test/mobile1.ltl | 112 + trunk/verif/Spin/Test/mobile2 | 132 + trunk/verif/Spin/Test/mobile2.ltl | 107 + trunk/verif/Spin/Test/pathfinder | 50 + trunk/verif/Spin/Test/peterson | 20 + trunk/verif/Spin/Test/petersonN | 45 + trunk/verif/Spin/Test/pftp | 204 + trunk/verif/Spin/Test/priorities | 22 + trunk/verif/Spin/Test/snoopy | 257 + trunk/verif/Spin/Test/sort | 75 + trunk/verif/Spin/Test/wordcount | 33 + trunk/verif/Spin/Xspin5.1/xspin510.tcl | 7095 ++++++++++ trunk/verif/examples/buffer.spin | 289 + trunk/verif/examples/buffer.spin.bkp1 | 154 + trunk/verif/examples/buffer.spin.bkp2 | 268 + .../buffer.spin.missing_retrieve_count | 228 + trunk/verif/examples/buffer.spin.trail | 102 + trunk/verif/examples/pan | Bin 0 -> 102109 bytes trunk/verif/examples/pan.b | 439 + trunk/verif/examples/pan.c | 11530 ++++++++++++++++ trunk/verif/examples/pan.h | 613 + trunk/verif/examples/pan.m | 1039 ++ trunk/verif/examples/pan.t | 926 ++ trunk/verif/examples/run | 5 + trunk/verif/examples/run2 | 2 + trunk/verif/examples/run3 | 5 + trunk/verif/examples/spin-increment.spin | 40 + .../verif/examples/spin-increment.spin.trail | 22 + trunk/verif/spin516.tar.gz | Bin 0 -> 401339 bytes 100 files changed, 77122 insertions(+) create mode 100755 trunk/verif/Spin/Doc/Book.Ch6.add create mode 100755 trunk/verif/Spin/Doc/Book.Errata create mode 100755 trunk/verif/Spin/Doc/Book.answers create mode 100755 trunk/verif/Spin/Doc/Book.samples create mode 100755 trunk/verif/Spin/Doc/Book2003Errata.html create mode 100755 trunk/verif/Spin/Doc/Book91_Ch6_add.txt create mode 100755 trunk/verif/Spin/Doc/Book91_Errata.txt create mode 100755 trunk/verif/Spin/Doc/Book91_answers.txt create mode 100755 trunk/verif/Spin/Doc/Book91_samples_bundle create mode 100755 trunk/verif/Spin/Doc/V1.Updates create mode 100755 trunk/verif/Spin/Doc/V2.Updates create mode 100755 trunk/verif/Spin/Doc/V3.Updates create mode 100755 trunk/verif/Spin/Doc/V4.Updates create mode 100755 trunk/verif/Spin/Doc/V5.Updates create mode 100755 trunk/verif/Spin/Man/spin.1 create mode 100755 trunk/verif/Spin/README.html create mode 100755 trunk/verif/Spin/Src5.1.6/dstep.c create mode 100755 trunk/verif/Spin/Src5.1.6/flow.c create mode 100755 trunk/verif/Spin/Src5.1.6/guided.c create mode 100755 trunk/verif/Spin/Src5.1.6/main.c create mode 100755 trunk/verif/Spin/Src5.1.6/make_pc create mode 100755 trunk/verif/Spin/Src5.1.6/makefile create mode 100755 trunk/verif/Spin/Src5.1.6/mesg.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen1.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen1.h create mode 100755 trunk/verif/Spin/Src5.1.6/pangen2.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen2.h create mode 100755 trunk/verif/Spin/Src5.1.6/pangen3.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen3.h create mode 100755 trunk/verif/Spin/Src5.1.6/pangen4.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen4.h create mode 100755 trunk/verif/Spin/Src5.1.6/pangen5.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen5.h create mode 100755 trunk/verif/Spin/Src5.1.6/pangen6.c create mode 100755 trunk/verif/Spin/Src5.1.6/pangen6.h create mode 100755 trunk/verif/Spin/Src5.1.6/pc_zpp.c create mode 100755 trunk/verif/Spin/Src5.1.6/ps_msc.c create mode 100755 trunk/verif/Spin/Src5.1.6/reprosrc.c create mode 100755 trunk/verif/Spin/Src5.1.6/run.c create mode 100755 trunk/verif/Spin/Src5.1.6/sched.c create mode 100755 trunk/verif/Spin/Src5.1.6/spin create mode 100755 trunk/verif/Spin/Src5.1.6/spin.h create mode 100755 trunk/verif/Spin/Src5.1.6/spin.y create mode 100755 trunk/verif/Spin/Src5.1.6/spinlex.c create mode 100755 trunk/verif/Spin/Src5.1.6/structs.c create mode 100755 trunk/verif/Spin/Src5.1.6/sym.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl.h create mode 100755 trunk/verif/Spin/Src5.1.6/tl_buchi.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_cache.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_lex.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_main.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_mem.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_parse.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_rewrt.c create mode 100755 trunk/verif/Spin/Src5.1.6/tl_trans.c create mode 100755 trunk/verif/Spin/Src5.1.6/vars.c create mode 100755 trunk/verif/Spin/Src5.1.6/version.h create mode 100644 trunk/verif/Spin/Src5.1.6/y.output create mode 100644 trunk/verif/Spin/Src5.1.6/y.tab.h create mode 100755 trunk/verif/Spin/Test/README.tests create mode 100755 trunk/verif/Spin/Test/abp create mode 100755 trunk/verif/Spin/Test/erathostenes create mode 100755 trunk/verif/Spin/Test/eratosthenes create mode 100755 trunk/verif/Spin/Test/examples create mode 100755 trunk/verif/Spin/Test/hello create mode 100755 trunk/verif/Spin/Test/leader create mode 100755 trunk/verif/Spin/Test/leader.ltl create mode 100755 trunk/verif/Spin/Test/leader2 create mode 100755 trunk/verif/Spin/Test/leader_trace create mode 100755 trunk/verif/Spin/Test/loops create mode 100755 trunk/verif/Spin/Test/mobile1 create mode 100755 trunk/verif/Spin/Test/mobile1.ltl create mode 100755 trunk/verif/Spin/Test/mobile2 create mode 100755 trunk/verif/Spin/Test/mobile2.ltl create mode 100755 trunk/verif/Spin/Test/pathfinder create mode 100755 trunk/verif/Spin/Test/peterson create mode 100755 trunk/verif/Spin/Test/petersonN create mode 100755 trunk/verif/Spin/Test/pftp create mode 100755 trunk/verif/Spin/Test/priorities create mode 100755 trunk/verif/Spin/Test/snoopy create mode 100755 trunk/verif/Spin/Test/sort create mode 100755 trunk/verif/Spin/Test/wordcount create mode 100755 trunk/verif/Spin/Xspin5.1/xspin510.tcl create mode 100644 trunk/verif/examples/buffer.spin create mode 100644 trunk/verif/examples/buffer.spin.bkp1 create mode 100644 trunk/verif/examples/buffer.spin.bkp2 create mode 100644 trunk/verif/examples/buffer.spin.missing_retrieve_count create mode 100644 trunk/verif/examples/buffer.spin.trail create mode 100755 trunk/verif/examples/pan create mode 100644 trunk/verif/examples/pan.b create mode 100644 trunk/verif/examples/pan.c create mode 100644 trunk/verif/examples/pan.h create mode 100644 trunk/verif/examples/pan.m create mode 100644 trunk/verif/examples/pan.t create mode 100755 trunk/verif/examples/run create mode 100755 trunk/verif/examples/run2 create mode 100755 trunk/verif/examples/run3 create mode 100644 trunk/verif/examples/spin-increment.spin create mode 100644 trunk/verif/examples/spin-increment.spin.trail create mode 100644 trunk/verif/spin516.tar.gz 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 0000000000000000000000000000000000000000..6b70f42b62c872c428aea7019d40084149c6f1f5 GIT binary patch literal 623534 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 z zRutS)#o0Q)8QXAIg<7#-n~Ck%wouPCY;A*Wu#r1wG{P%2xs7E$$q1TWV?4Q=t-v$I zlLj;2{HU?o-Kn+_#>=G%n^n79@I~!)BqhmOCLAB>dqSI~!ndiCXLt2+FVC*jg2nAI z)*oa-rZmjQ+KZOzVu*b&ZFuy;zEj~qEn;BBpgy3(dUooyQ(4X99OzwUl%CFjGV zcYB!v88jTB5~pgwDJaoK3}C1cz+iD0Mpw#riDVIlP<0lJ{}nP6+*qJ)m1c)jl zLzgXkYmeiSnm4Mf!TLTP z%ZC^W!8oU%SmW8~u&qx10g*Ls9p+d-YMS27eX5WRN)&CRqJ@jAC=jE&IOr2rI42ATjOQF{|7{`|axPwaDiidm%#!UNgOc7%5UzRXLHx0S{mD)+7O z$OeW%>{>!V;$j)8ui(@$fqPAdwIpsZI`ha8 z%80ae6i+eUd+vu`2-%86y}Z8ieS4&#LM900BHF4PpU6T>02~NuHYCM)%pT@~5C{m? z3+x2nn!cbA3l%)CKt==^etLX9bt6s{@`HPLTELZ-k^Sf`z{U=hN?H#o7hpSaOWN$> zu_g~qfcO+8)477!mIa3^2u(_2@T8`N%+Afna|p3~331^%5l4X}%z}qoq)}`J?40wd~_+-J+RXacL2UtdJnvxfcd_e z83)KyJJ2aMSV7(}BXkv7&AK5Wi*zXTPT_86vixqApU#(zZ6X2XcSV`_94Qs=-jplZ zJ5Z1YNOcrS%sRyO5I)pX8pYkY8uE+87wKS^E+Q0#vA~_9pBD|3JP#4p0XxI=L@!SU zKWBBuJkpuDt^;n9LScijTD<9i#$DG$ZIkab9TkAbk z3j+3Yn~C}uERgMsMi{WwlD1&Tz9|a}^oa<6h5F6g2?Vpi1S;W3r(;ENU!I%KyKrO0 z*%`@64~%$>QiYLcRS^!z^&}b*db@irQiC!j7$pOhau?Z}}amRU*4f5e1LZQPFRX#cBIKZg&-8Yw@WbLt;lWC7;S-q*E86=zREE4LnD@up|G8 z&;fiToOQ1wmK2^B7AOJ+TLW{*N@dJm;W%|fAa6+6D;-nVd@-K=HfpX9q(b~;{AU%Hb33}OF#S3+EAS@ zr{7~-|1otRe~9{j;O}`Ko4-fc#d-X@&yT)l|2D$xZ~fd$Q$Gg&{2JFM_^vTN3!nP_ zHurCVXMdaPH@JS^b8t<2^AVTo>3jK8-^-VtFVNP1_?-4)`TO*{$Mt!xzrvN5aQM^< z>K`x7CARk)?1gjCfN$aRZ*slLwZ&CuZ~jTHKf|*wS1hm38}zB{jnifv-E2vkz&viJ zTEyqEh9SC76Ogu-O+WEt^;TeG7Xj1Ql80u&S-CIkX)tqT*&qH#`2D#)$MqGz@Z#&o z4=MNarTf3lcZp}5r+0y>ipH~`17zZ1Gx^==!9DTZaxN9plq_2wm199KuCCm=y>$J~ z;?mgNRam3tkuY((Zc?*SAXu`Kw@ALScJ@A5=~%6qf{eT5#&d z!+NYt-{ty?E9B=0Z`_y&vI#?!Yn-Uq3J}Iw%{LM+B@l;vG7HZ&)^TM7JLTjsu@~J4^vp;xMFEW|Bs(gG zbhGi~ez*BFCpC_zd_wo^tG7TH*50{(=jN(o^kZ}}tXUlYJ16|KlQ-4D<4sOt77d>W zrBa3=2yUr8k*ln7t=SYss~YjLo#opX$C;=Kfd(3OR`H-^iyj`Z;F>?+^<%_#pv0>c zTX5-}rImF{BgHOM3Yxl|g`r8S=f8aK1ReHnsmi^(rBX^#HV3lmU}wact!}-sMN$Lw zk0yC_wp-Xl8~u%KA*05teP64suml2<5D~)q7L;{mZT;@))AnQK_B|0ywzu<^>haqY zpGGUObW))V4GO}Io+Y=?Go@#GnUP*VfJ`OWFi{49l3Xn^r=d8#i81C+(=B zPO;47>T2A#1~iun1}-xeoC;VJ)bzLNm^4or@>VJe4kzi?fwopm`U06PUte1I+_lxU zg+;6r8xI#2w+VYoZCa9|xPq6n40l;OU&KK9}E2)rvkcjR4CoY{bif_eKYURyOr9vyW z>z`VqR4Bo`Vjrr0ny=7rnB_;GrrGkNkv^BHSDm99!C@w^KA6i-=Qq;DwUpRE^siR_G z7c?+Scb2#65u`Tr%YOIC_}#eFYfGzjXZ*s&I5)h(t$tBuf2LAucHd>DSQb~#Jf>K6ly?sXUu~<@qX&@nF9b>W>U%wEn?47WFUu9{W%A5A+W< z;y;<3<8O19U5YzzCPRE`)Me5$ao_3l*mz5r@65#rte7XLo2V^4i8|*b8whf*i1Fs; z&fZ`fLtGQHk8aSLagVB>nP(t&H_&Vwm!5>82xC@IKgU*KWK>zDPhc4*WMM_Cc1&a> zrD1uxY)3O$g%eT|Z8_S8oZ$6@H?VohyTOO4V>WxTC*{@F?AGiVd7^kGqQoXdalFU;cRI-Rxbe_Xjo_;MaUE?SaQaTb2RuCCI8b5rgI(!ZxEgjoz};F zP5=ly#?M~!e3^(YWzLXg3*&2tV8^|FaPc!0Yr_v_VaQ)yUeWn3(pzx0ef9S8%K96z zjQkeXkcAHiS9%;V!3wt2;@f4}dmMKvhpcV|VFzs7td1#-lai3X_10Tw^6S^HlO?0Q z&qDVvk32>wfM)aW`E;MjNK)E(Xu{pW+X7O$3o~r*pkXlz#LLT>|2~x!L!&iJ)X!Wn zQg_lA)uH6afY@R1O$KlBQ!~GcpsK<(D|w0u2;^2okjFUr}D8>Q}l-)R>3erXo-llt_d6nkiVbFFo zAzP>77C)UnXm<}=!G0wbt1wsDD9X^DQzIHU@X%_D?ufSY!}N@2XzK|$Dr*1~@GfVP z`twuSP!j)ccdNr$mynU#P|+VhK9ir?r=G?H)wP@V_crgIzjycixr?tDMPfPH@Ye3u z7OU<3YDmWh+9%*Rk)(KnP#CH4vV5z??+3IRG&MDN?w%=IYE@tUBH$ zk4@;<-xju2tB)B2h4_E8(D7$}?DsgLy`XeT1y8zmIWQR$J89UWg9KAq+ic+u zRz)A+SwhV@c&h*4c4<+C%P-3wV(5@W(o%`IT|5eZe7g`ln6MiCgQF|;L7(&EPn=kF z5!pqwhCsw9)n&E~H+dEEjSwE2)qY;{^J#Tkp*s^dnLtrSCNX2EP6e3AA8Jx4qDweSKeE!0- z@p{@o4p}b~q|xKSIc)M7^qf=|W-XhZCX=Lva4>#^5;0%_K~2l8E}NXBuOJJJ-!^-i zWs*|{>PXQsdPpg(RF;>SMr=k8IMq?~JVNlz=->Nt^u zfYZiy1|TSVQ+GoV2Y27AU9AuAX~0fj!oC+OVZuiIo#kuX^|WC-vv@efhQxpX@=R_` z*_syU5~9@z%3ussXVVmw`D*?5y#F|d6p2CX#ta};yTU8YLEXcwx;y;~JUJ8_PGA zV84oMD{rJ{6@RdajOO%Vv$K2m6LaTbbD|V}@8;7tV0L!9xV5&oiS_#K+@;8sAqh`O zDBN#RMq5vDM9nsN5n6R=2I9b4L+f5QEZ98>Jc8A;%BCdGO;agG%JIsIzOSL0P^l;= z8EzC<$E1^ZX+eDK0^6M<%^A`c05Z7-j||LH1V};^r{R=@eXwWQohT{LsICqOIl2X)(`Z+lsJJ{>u z^HDaHG!q}E0D>^Gv2FLAk3t;-{<56F4j^E2ndKwUgGwAoQ4^WU$L+u=t%SD`;BRZl z!?bYhyl3NpkU*gwud!jTP-3cug}HO)=P2z|8?hOK!YWxMjxmlR&Y7GM_^2wIb|MN4 z@Zaj7Dt09JZub*>PtKl14ILfhDV1}UD|!MwdI5}H45W&;IeXHldnn8%0qt<8O#oW} zOe9K&0q%K_8wcG7a>vr{1YB`M$s>*wcyS2a4lR$cr2pup*b_IOI^__^;tGVPV5(aO zl?$i60|SWr4Jt|b5Aq;&$J^3R-7L#f;ldc^2m`QfGshAp+D*sBf^JxV-Mnu-GwOks zzSD>WhIpAb$yQ;k3&+EN|VRbBKqiMmS0*Y9P2okD6v&>~5nA6P7xWg|ym5ol%N#7;>v0J#&IAvG33gDG32h zsMv?k--J9Z_LuXAh%kw)9g2E}+X=y5Tt05m#8?8-6sQ(3&E_x(orN87d>;~_1D8q5 zCR~Yo`~WAjJt1U75OJOs$ztO4!kNIb$kHa;lkJYHc$$hg110}TzzH*u;`XfTErr3R zA-t00La?shYp9(-^tOk8SeP)U&IwdDMwzN=D+I+v@iSVt@jhaDP&q4}d1YI7q(d}zg9!Ge5c5j011A_aWesu;PG%{M zHmd{*vPocj2SYO*!5tBt#fG>{RyYyF)_Tx3+6cre{WaO320^9|DhI8)t2-601}$y%W_xE%K~y&wT3fD9!|8V6(?NT?ep;~Yrzigq)?)i&t(Qg}cxYm(ZR zr_0((QhT9w7(b2ejjj_&z%dlXX}kC;ER#k{MMWSFkg*SK8}uv zhlMt@BDSigp9+rt3w~N(S*>f)J{l4TR&gQ~Ljxfu84fI?Cg0BxZb!VGCgXTOg|K$) z_}W#FV&A?5Lqfnz5u!4A(1092+*)DA2sWXe z0dsSO>(7UoDiU5k&sf2n4J}Ich6%X=^-s?a};k*MUb31LH6eNi8@? znVKd80(`AnG9|TVc#$E_G@~zCYiU!McE?Obu*z@%?zk~Tvug#Gn7FleMqw1{1^$R; zFTc|BR?XMr!z`SExhgfo2gt(^#)_jEgP4O=L0 zv6%mYTdfQu+=hU$ts$dxz{DK!j4`=-SaK$#W zXC$S8u57sDtpJY6HWCU<>IkzhuZ6HhZ?`k!zCkY1oM`Vg)Q&gV7#J3L1p`eUKo2)b zj+9SCKQH622fs={!=xuukO>F$K4Sk56kS&QbC^g++8woaM*6{^oc6xJ;_0|n2E*vO zN2_PR$pV~{iA3!45S$w&6y9cpXNfSB5=aC_>6j!hwXrtep9P;CsT-MVtWyT}*;5IH zjVn}4ZkKN9NHm|s9;nSgu7Z-qonANf))RAt_ROc?hV@|EED3T4sm=kzrVCnHco#Sj zqEoE(Os?j^f}ljaer;{(wC%CB%#-0R+JJdz@eUG{7~WtaE40||s1IGipO-F<`rCEy zf*hC8i|#jK-RNi!#YXg|YX`meYj39B)q zh2BtD2l;~wXYvOZ&*X0tv-7qMvhsw=Z=-8I<@3P>nl=J)`Lgjar%cC)4Ziz?ch%}N z!o3sKxhq$mDt0q@2Zh<}h=qK6vxEEPnSjVr=hv5Bzw@SWJ8t}2dJ7p^s^o$sss$96 zY1u|oeS*J}(VY%DokmuD%FhSF>ilJugK#G=!0l@**Voo>-Ddt82y$k#Ix{_uO$r(s zA4VTnaM_n9B$3$XWG#=*CC)L3{vMg$u5;R!dqg`i`z2#4umAdC3WZ*5J& z2ZehO_S-bXAm|MW+9F6JzmDX}&lburiSsWyYJG^co~u2u!Uog_=Zhtd8ojUUqy-gH z*fJ2Vt7QP}9fj!vpl6)5xclT@8E9F;Mnlo{n;Zh7fpeEGT{6@&^G#3DC>ZmR_XR&Q zw7|O+{3TPPW+hxkPUs95zy%9~7<3xFyO%EFm%-{2TrtYv?xxb<7X?YFXwDpc*{^#( z{)PNjeew3vwe|OJ5!`!g<;G|EvVQx{%A(Os!xhYsL}2QjUm&m9i+B-;-h}6OB#}I; z#0Ei=Tlkw6Zvcg0eK+&_x@PePli-zGXHOcx5=u}cxNGnWFDUBUOI>z%3oc?G$C#4@ z*RQr~DqdvnQ8=BQ9HK^uFV&v6%{MY?(eIUh-I^e+xk(b5NVL->yC5=6bdPA4M$>ny zTZ2plqaM?$qedx03J; za+Kol1d->MW7YSIphkvCJE3A_Ukn-S2C~SR3vbfk0GC1? zxv|Uq5;LFy}2gEtZaZoOIijWRY zYQ%bYHAmZ9D%$gStkhhm-yMYQHv27XxM`4s$2u&-GY)jwlVbHfN-r+B8o#l8Q?C0c zlBHV}$DM>6dT@R1?NENSLb@L+og0+}KfSzy)LRxOxr1!X^on+Fzj5Q*n`@QoEzYYgGRZMQ;m zq!|eJyk7DZ5xYZEaNxW1SP*lc=_0vUn3lGf^fKJ1;!1| zMbAosBJzF=9-CUFqcVzVO6arAbgJpsj83DS>TZLv-2+d!X>zI_1Z2y%FXvW}w>j$e zb`i`a+RI5VyEouXCQ;=+*4@?*SdrmtszmGjb-jhryXoG@KvlszxuSk!ACrbH$=~8F zq7Oupk5xX#V8F{~mGNrvbZv;z4iK6#B5l1f#9)8bAhdV|*2sRF_Lgu^9WX>iW>y0M z1=JQ`i`Atwn8zMVj627aId`g=gP|_{{ za%wn9Al@{VK3H8{ZAvg2?)G59v4^lV%q4GizYfq%F!6 z$JR8!$6>|Pvhr9MRdKj&!Lx~*|Lt_bT!VyaJ!gRoE1AXC}I$l#6_JN)x}FANqScBSW42q zSSVrHsm?U+mqm?6s8FB}kq7}_r1ZS1(PMOuqv!P1;9)U(BPOQ9 zr|n?13Y$})Y7$knW+P7@+35rTBI(6Mu>`%V7f>bPmf8ms$00K(r!4e7h0)>kx*$mk z=Hrl6pi1x&Wbn9YC${-$VuO!L4Km`14Sos7>0bCa(nII}(Z)K~=<{HzK=m<*!jvaL z)FKI93{!6qKUcybR{T+^%`#9&+BE(P@iBf=Lg%rq7oH@3BU}^5Nk|~>7qF~!U!t(B zo9#&Lm`G`2`#iACIP}>?t)9N`w@q6*f`iIqvMGCbJ94%l+@No8bd(kIQ|KXAqw!} z$C26_T`Z8&73?67h*lM?CJYH{RYu|Gu9fgUjqOo=e4)zilorZi^ys6k_Og~;ua zx^X#e`1d4t_JSxZ;lpKZ3?C+z(?`@1OVW3bEm$fc(Z3{x^HdU|X~?;q5H(rNROMs} zER~Q;C(6SbKK#*RYb1cUe(JKVzB8bB+Xkqv@e5%)5MGvAb#4!x^0r1FMZ66>F;#QY zMkF;MA3Ex)Hp4uMG%BHz-8W(z1QzDej;SOSHS(F1Z(?7F~B z8X50W_!J9rsIflO?3*UKGapl1@U;zO6TQVGlf zxMJ@DT4N!-i&ByXy7~TDuVq8-`k1IgApvz~Q@w$K9b>m^rv(m=&JIWz|hn zZm3Inelj3gnik($T6|luh*rAQJ8N&@0N>O3lBNOoC(RLe3oRyyHfYAEeVPSp&gDp{M8wNUH(5RFpROcwd*>SUi#N}xmH z5yKnhGa#)^wXC<52M1VX)|oI%H+}P@F!D!L^o2TYKk2k%eT`_eZq4%1Mxq*l7A)^t z9|jP0Mo<1ARaOmg+<07*Re3cf04A+eKME2Rlu{=cQcV4u@V?xYGLtg+k_xP5q%E_d z&BDBL>z%dFh5k%hdJKm;v^~>j)dc;$r)V?FV``>%+K4RQ1c>}p)SL^aYk~WyZjsm> ztQJ(g)QqZ$SZYU<>Ip<80WY{E#DC&If?PdOWh)weuHpt(eW}_@@`ToC(h#Fi7G6v zr6HC;QI;63q)7*XW+xg7HjmIy+an^Yuz8%gb7YIO<*7$KFz3YUZmk~5>S)K9 z9?c;eh#Ev0B|K1G59M_<_8ggAN71J)u^2>1w;rP5^DYbla*xbo=XK9R;#5mNoJBfh zx*=P`kcvy*xR_rhJgs+oyW%+(gn!}J&ho_kE2O$|f57z#!$D@=eY&f;JPwLUe6A_PMQsF~BHy-BRDyLiu0 zu#|DqjAO+VcxVj1f(*tVZA$Bqn(r};>Pi8ONW~b)V;M(}@iK*ELxfNL&dQCYwKaT6 z4#?1v+pmUN0feTJ!_kBe`2F**4bPicGM^U7Otb=SY>h1myu?@I9S~F1%xO<}2v1eM z9D01-5~%RNo+uXsVNOjyo=wfckZgB6j2LR7DCxbYJ=R)F=Br?dI_);Ei3)*{)T>0O z3Vp`Gha_iRIBSbz9vQM&u8qt~I@RG80>Ri?2zb_-Rbg`+mw6m za%3mY@tDU;C1a@DZk>UqJ$v(7QGUxBnN)rhp|%e#heO=^M{sgGuQ}d$!8|XnV9)87 z2$APP>gcvFZAe5YKoyJWIdg3_9pys6{iTbcQfzi4V1}%Cp|!JBw#6RDU1xH+(M@aA zdTPH|-x67TY;3UIu$BsS(gXLFfR&1xf!0}iQ&q3T>4foX#7V=tc4r;W=hE8p=Lo!` z)fZ?DHnu=4iXciZJ9X?W%#-6$i zm#=+xWzo7-uhJ%%ug)RaNOs%JzNHt@FY~Rblvqo-SjznK#PlYsX>m^Q8={clH-Svp zoXl~-(coJDaX8f?)mMLN`^ilHL_E+p#ziZ<+vq=xN2en{pm1yTB%ol3RTIPex>G*Eba4lMG`nRa=MNo{PuV$X``h>-kGQnrL`43~>PTmU5A`rXMl*2ee(7Xt)VTJ;-pk@Akx_}gRV%5q|;s`D zDvX(yOyn{tk9%xLZrMOpJ?*bChSK^ptDBWhODs9x2SpjDrQw;- z;lx7zwXcA0C6!&@2ZD!PKrFjI;*H z;G7$-dXw`@bFfCL7vHy>TqB?ag|8+q?*NTFAAF3>fP*_5UXr+sS2ZsKR z>16;d8M}p3{sRCYj~d+byg+9Yd^sSZ+V%~mWib0!~x7kB$Xeh#%yM? z)%M22y&lJ&BH1scJks2aq`4Y6&Sq?F*tpQhLsdXqD@|Zn*6(UpweJb=4m;9}(jz6T zZAVNT%X>O&T9!6FPj=84974w)krIa}d@&260yK91V!)HPSMkK!v0mUQ#yga?%IXvw z4fTV4rhSz*6^q<|NO*i;!I6e=MyfdHIgz)PAGeMazzAWTY+H_qT0FONHUFgSLl_gdz~Qim?fQMWV43%8+`cXT;FBm)P*=o}%rZ}rxEw!XxtPk|;AuY`*EG&!k~Z5A1Vdbj zL=u7&)-p24!bSjywG=MnYFy=wbs%K`B@G<0zAPn<=MrJ#T>u3Jm5e26>X_Wrhi@O# zZh}aqF<8sTsMe<~(GYYO5~Y=QqBmp6rZE(4ke;UN)d+Iqi(Rn=TP>N3 zSTr_aFElLcr)V73ir2!Mh>vrq*mJ}&!GfK=cD(db7L^HE0;E6#-UeosJ$rahG9jNDPxS8BTQYs zYPl$#Dy6Z@c4Y!(nzU#t!I*`%%z8>|*HE72n*sDK%NgR_74AgFSu%zpGnNQIdS#;? zJ4mGMP%v0gy-ifYkv1!mJoPqt4gn1i!1ao|kYQV?xrx0~`Y)p{wd)v=5(C$Zcw+WC zY0?4T!dgKmErvF;|t$kCwzDwiId0#0>pm zPO;3GY{8aHMC$;p3fjB~6hw}HBIQcd%86WHGQ!CNZ5V;O+u*#M#FjG+>&?E3dO6(W zl!w&0e8~yKGck8{AQExxfzT-X!dO0%NL8?n8(2l_DIMxR|HT3zMG%nPT5raBkjHJs zqtAx%Z8%gKmtTc%fwLjOAnn0sG(T<7sFT5yYU1olZsg`m&MT1>B)&2e+N)P3S$w&6 zVM#LADG-=!Sy<;7NRay{%UC03420L>RT0O>(BMv|kTB~VTOQuRIWB1?OgYV=Iv3() zeDPhDz-{6i=H_%esh9vwlR{Ytii7iX6H;AqFrxGVp|yJ9{QJyYMb#SJxhOAKM@;eQ zMG@-m^76=(2#N1L@;jc-Q{MwdF~&5_($uU)ii{TfzVi~3FJutawz?8;Oq(pne##8M)4M(X8Q9(Hmf9SeO-OB>@=z^xd-#`$#vmV1wZNyNHv zmY`4Q19lRMJI`lgno%^jgE>z3%*;QbF6&vWkNF5=^|;4MX*dd{%7g{2(HP-)1!JFx zP_T`;s9Uxb%gR)plkrBy<`%+Rsn6uEER57i^Yq;no+*!Vp{x2!u5AGsNog-dPl3Ck zUFi(#VfbF6bog8((UF2y@82cIULHnN(uK<7ht#?uMIAw(+i*si zO{iB0@Gzbl4>PZwx2%D+^M)Ay@;#;>jt^UIK`&XJqN9$y%{P~Bzqxe%V5=ABYx7-WZ&4VJ_r*JxC~Gf_*w)WaX6= z#^20BU2?A&`RxzwZ5X5}2=_MnI5cH@-^Elk;&=;(P$hoEsMc@_HFz_IyV85eIawbf z^n`D0zVNCIc`xiCw*ZNC7~c~!@$(qBcSa*XGE%8 z8*kUwc@L+Mp{QJ*{qL=>MZHUoSN1L4=Dj(K8okjFWLeJ??q`N0JQUzK%W3wsDKuA6 ziSj#ag*m~G4>w7Ue7y3aA93Tp+lh)sYX>hqGOYl~=AFH;rFZXPHXX?C!Vh!*@V<83 zGuWtYyM_Lwcf_%Q=3Y{uvOyKk#MNfcVOiFAOM{3*Ak&4qj(wf={zrR?p$SE+VXea( zS3*W2Oh)~OBq~Yn{2cw3V- z)=1+q?`oDo&+GpaS zcH5Bm4Yc~h!QHu+Ine6l1Hp$_nc%c0J zNX6nSF?Y3n>H;qV(>#HG3Chg1C~0sIAXzq)&<{m4YJ5Uu7cEPEywNd)#TAVjeK5XC zVkG6g7-^NyxJgDNdq3&;j4?IXE05d`RzUhofntaAwei9Ko1mEH8wQU9HWCuaBXMCa zLo>=R3J!-%Oekl5(d)}sW{)If=aa)L@ypWl8>p)>zrIw z%?=>F+barnDQX^g=SvR-bLCIju*oid7E=#6V+&< z>J&r2g707Ti*7$ceL9aimFJ|(DmoV6`F!o$oLkMb$K2jRzz*+GQA{`iY3le`)VZ9` zLzP$!j|QnD&x!7(NGLl{Aplz2ZEsr&qe`m9Vl;1DTfQASKsUz$KTzzhD-lv*glpxl z$lIl;lZm~B{NoQuMn&dpwH#!UFpkrwOCwQ3_tNZQ-=Ir)4`ZCJ1g@a@29z?o`^?|j zt@-e>Hp6wPDUaomgfs3}<>H<<-wzKkT-74|rc{PrI12uyfNQwCFIHRws- zfkz%v1lE13a6eTtoFhaAhT>=E6dkmvBv=ATxXFtx>&q+4>$cDQt2G%yo!al=Ss@o2MkHQV(*QhJwRZO) z0ob`Vi2jlH;bC>g)?ve5cwblas@&4{4*88n4}#l594UuR(VWad50PsZ@hLrkSR10+ zT=#lOh^H?PVtrYef*urZG#r72ZCcu_qGZIHt1{ewJQ7{aN zMx(2IeuvHr{g7OEyr_#gmSz~&a%6gg$FfP+u7-6EQCSR60-;h9-nvQLExCx8bowdB z-2lO}SxvVzIlnb`{(^pRuBHxTq8`|3?R(r8Hwtb%$7)_D>Fu;h2a z^kVUX@oghqrG;&<L;rm_hP}&i3Aim}phF{V9$$g+1lyi{H~KRcAyER> zJtvP=^x%v5G?yS{BLTTGR>dw@YQRj3f#!SXg@+dzJbI@}HF)P=*-aJce8p<3mkker z5Z$e%gCy=YX75F8#TU)+;DI*z4<$WN))-a@rzpuY*2agDASSlqVuW`1%F>%4$aTlW z()db?1GW~6Lvtulyu%N|Zq`bVJ1U(ad5ohYK*bG?Rl?|U0~T-VazYr>{+RGttMS@g zHOUu)PqF%brkNO)k=HE>hA&=ahjk^t>m)vqfQ!&{#sG6-{2;J}<1cFv9 z>6Y4iVa{a~4T~W^ofbiQmp%5iFqyf$5WIPgKnhQ2 z-EI&$t36F(PVLXq3o`@3A3HuLxT!quC!@Mn8Ha5s&G9^w2}A|-Iu?t(ICWOd8#=}N zU>;pdsE>zoAiVaI(r-`;q4uyzQ-mG#WzKOaY;k*yQ?P zcuNwKJEWh>taOHsW{vV62piG-Y>Sq-4P+=rz^gaSN&(LTki^HLYYEDMlJgj=EJlA5 zT9TosWa6ZDQYxcmb@Xx4q*;@~t~gu(YcI)&Xi*7KjRXjSVM6-bD)#d1xh+afw1cIR zpuJVLX+PTQA|uM07+$Ir+vlD=WA?>Z)@9N3=vnho+OXKAdsrQEz4=SQ<)*+}-Lsc4 zQfjp-o10|g-Nsss9l}nRR$dT&FxYE0TOCB7Zm)V=aI0CjzWMDv%!&*W!0kMgE6%{< zMFAtRhW4Z27iq?Iku?J$2}sw>K&xM`INX^*I51WPC%2K{l>3Ea%MTI>r zfdHNQFe%1tj)PL$akZjd(bqMOYlWy5EB&&)3yIz(D{*)LsKzokTf5!@+$~SYm1V|HXfeWx%hyzh8B8__Qpf=TLs;$xB((t zK3i6kGRjLgo~c)O8R@`i2HaG|QP9N;caIev$hn4t{Y-xS`t?(zhbgiO0?4HMwijZb zsTXsh5wj+TS;}xn+!C+P+Ih`z32|Fi5}3F37;Gzaj7C>|>#etHd_8@BW^5R~M%0x_ zHPlIksdjUuwCStt%I&vQ?~HQl0WA&V$wX6S0)NjQm$Ao;O zUl+FtJf+j69hN-=28O$Z#mD&J6Lzx+@q;>fIEGIhaV}?PPic)HQ6&vh;g_=OEJ z`eQgr(T6DW_67-BdW^%Tz1eQ{y*_poj&$Y?4$9hLBE6@aSDhe|)(~!ZA$W^A7HJty z1c!c`Z3T@emL~L~06lUa*b5mM!b4aFZKIFis@m%al9FR)HD&f#QX-N*;_ z(xWr#xHY>qo8RZX4Gr}<&#lT9F=@3Wwnsdw$=BDH*D*4zFNFrKVjERU25-ed(ssv3 zZbL*k*xNBzzEexaWgvr*@SH`#{6bf7B2 z(GX&#u5h}LTAw#}?mV>{hL;$xENTF}-lWULu^m11$;z1G@q-nBP_8c_$aY2 zu0%D6Kdect$72xkvxNo42>uACYsHCDh82!~b^$=E z+fFxRSo-R();gZaA3oIZK^4V+27x2ki(>t7WJaf@!s*gN?za=3p`TE${BYzn>hmP| z7tpT(YlN(L61Cn^bc~kMr2<1d(9Sy=$m;0Xz=59ku5jJbeV{qHw6;;X-R<^1gpar& z${VY7@s6<|p+=zu0GBcIhnhCRLG`b2-*!{_VQnOyxpbuLmaDI7h)3NEnEP4DPJ|;kWYf_E?Vh!wPtnl ziFpWP?161uchp$7oczB>*B+nLMv-wu)@i9xt19bOMWWNQ_Kp8yocAXevr* zK8HcxZ|yK|jg5hs)$_)!TdT$sES<@GmY0uY zVrhZZvY|Jt$4#RCFsCuqSLraO=>~{=$q=_&Y+x{rEx{ ztN68@omghS64t_6M4Lzm_JlhTBr81v4#<92t? z!oZM4w4@RHigPH2jT*JzGXAGAjnE+V9`1w*o|rpN5G(CDjKk4YUeY84<(^!Wx>}Grx6$+2+qQ-5p~1-}=Ei4pvIQp? ziaOlzTHWwR^9QE{f@5soa>nfw*Xp-t0bH0yef+KXkJ5j8qkq*DpTqIs0J@*#VfhKlW6 z*nqU+#Q?pvVe!!u4C#Mfy z)=zWdS4;-DapUd-oZNUW^(|!gwiAAr03akhg#;Y3~Lpf-eX(7aD`XoR`d{zTXMNucOmDyZ-wO4HmYHA zQ?Z5YCo=q9MIgY@94qv8yNd#F7+2XDe=N3V_@k1SWKL;4At)B7ISSKT?S6N2Z?~O) z8lN);X~QYTi9e#fX*lDHcEQ$nZDV`C3z;~PFAX?w@m@pkKl{X){7qhx-tL^Z+3k0W zH=;3~WayT#CL+LFOU4fBv8DWY@KZyl;@+l?k^WaVp03#uFn8mgx?nsIkj zB;GOC5n^o^S0uNGkYwv8mN|qZAEe2L5#^GI<&!JX0hiyBJW-GV!8%H@@5J|K1vxv? z2ZOnA_gKj5#IBBWmBNE#<_Z8OJ7aKHFhVCKqq30UnxH4dpuMmDYCj(p(8N3)RAr?@ z1_t@}c6eo;LWFdh#YAW)5tBO*D!cgRhRU#7$JF6Rn}y+}jfJC8M6iH*E=V{D@Ij0| z*CHP*b8w)SE*km?5XTr7z&r*dgbUW5%qC__I(YstOo!|ShB>xAPC%feAxj&L6uwC- zaqQtkEvR@xTfwT9obNe)Jg7l2)5D=G9+n$! z^id$3l}a3w>udq`kFoaQS50;spK@>^Jk}YT!ay2iJ+EPBZbIz@FHR-~mB!D(s~s5| zF&E4Z9&-fG&MiW{&K!I@dU1SYbZyZ(%bWVs_0Oz&tX7yodO@(I9-N8(pXq5UDUKX4 zDdg4LuRrN^QM;mLR}kgZmEoq#WM$k+@{Voph;b<4w>>aQ3m7>PM{p#7QJr%BR@d{Q zorkms%}69n%C(w`Dxnzybfm}OSI($zTf}oVxTxrgPL#~c_hw!z|B|w-Eamg=MV#1| zEspizh*Pc)|8_db#u1iBW*_}@+Q#uaW$ke&>dm zj)~~Y5E^NAf~xr+$U)a9z!XPHuSeWMaJ8HVZ4hET7*n9N2(p-9!pxqq&Gl~Fp+S4k zwLgDx>9lC1YnWiO_1LLK8`4FSX`H6y=2?Ff*fY>JzyeLdNQp z>A=MDzz44*T@_<324M2z_yZl@4rataq(?g$dkVC~F+Jwq@JtJ837R-h;;EWQ(Jy*C zsSIvHFH@^oncH}gc3bn>p*`}6ahNf;^c$c89L}@b8N-TkxzA54#>6ofJTEZDU?8M4 zy!JE@!+WD?#jr@z#z+BnrZZ&%sK%0tJ*$l76jS4qQi>HB#B2;BskMTMK*9#_mZVlp zAG;L07mO4)TUma6nLtnkQY>pJZ&rztb7JB-smD}vl)|VxGj*@_TL=WU(}bB+&W;7oWY#XmQz7FzE1^VX`O*`-p!Obca+ps_eHYtxHwUh-ywjH} z8F{`%qJYL@q-95QYqiAjU5s+Rj$_A;5{Rr>h6aBalxju(%bJs(1Osi! zm#7#N|H+U9e9n)jbbTiFDCHpV;A9BdCJ3-F9bA;w{TYelKyq7Y-}@d{J%2~cHe1R( z3_y%DT)eznCsUwoW=N1Z$q%8L$Tg9X_#P|vET_S2PUSss*fX}bJ%_bzzfp$8t%879 zQw7W&5b83qE@@-*Xm7PWnEUcs#STpvgGn|g)vxbft#{skpENvIFPu10-#xZ~@XhhA z1eo7X1g@K)NdOc=D%LXMCU5o_!wVW`3hmt!T<=I5PwY2uN(P`k7u zX1XqxB$1KQS;50d!}2oWceECW!x$B^{HogXhODj%DaA2Uwp2bGHffV-E?A>&i%AfR z6z4GFA`>cY9>S3096cZx**ORSBXx|ro}b4|&X9HB012R!cG=2K?0$r>GOrR6z!FlL zCymcGhwY~4r?ymC?Mc0df&F0Y9UnUDcQu;qtThv~Fv?78I;r=XFM+ycKQGt<)yZA9i9X@}Rq+G4XmgX*bS+k8yp^mXGF9u)Kdl22GdDxC!;j<@A; zEDp0=5Mdt)c1-2~d}`7RK6IK=$2kxIg1ytllfP9UgNG$s$|;HoMmTZ_oud#NJS2T| zL1nPFNibb&;DwUE$ni8{i#V3I!|((DiO=9oEMh4UYTRc7flexM=+Cx{D5JhOX$;X< z+83#bDEfx>9moYLa=u8omaJTUR4&WxOcf0QkOu^;4P2WtLTCmLvjevX5;E@Oo9)mv ztT4P%wp$@!iE|q*ESR+ktp+Zc=OeRrSyagGkSud+=oRt9HYE!oDLqTtR^n zhOGe;)DFB4mfC5On&RmdsbKOgLTHIMn=ymoYtGa&EvwZZw8>`$8Iy@e6Ghq|05mrF zoNr=7ALAN^JHBCkyq1U!+CMG}<8~AVMKXh!9-?9b-i#4G=Y8cIwqaivQ8u(C!0(`Z z7^X(bVtb_6^g2Bt+}~7swNyCf+?X^VjS?zex57?hD!l_qaT!mt?+0QtYJt_eCv9<3 zv9))u-?_P}@r}l8`33c^1f0{c;v3i2)-_&@BJ6EPXp>}i@MJ@E zjI}Df(PflT8d)ZTwvCNsfL}&01D<)63A{FviO6UfikCGTsVUdz-}i)&Hb$FoaGPk{ zlizVApfJ|6rz)a>7$*szEY4bX$+%$cghTxWWsDjxtDt>Qy|%cxw7L$EUcYK-iIV|g zWfehKQ3=Gqzgaww76(CAMGO-ZJlclS#^bZA_4=n`f>E<*x7(Cdf938X{eXP=lFv!f z>GbNsRF248V7t%208~9_DlbWjW#GDM4h)@3w@suW{8%}9{K&N7fGOs>?M6P8e`bDm zx(^geGxEe3;hSg1ko!3DfoSq}8#&5c^H-)f0c@hN1zf{^@;;IDLhg)@&?k=4 zkNXK?Rr2;Cl5s{HokTKQFgSE{qmH#{w450k^_ss}&JOFjXsA_=x7QV%DT8Ocv1{#d ziaZq(j|gIDtk$gy_)#7c<`s&$uncT%c!qF8sR-1-ZFq}_(;RR`#W*7{kPk;;S%`10 zPb3s6nW3qVv&4`zj3l%O#I(+(o7#zfx6Bn|44*eb_YB>;Lq6~Bv-8BH@hl;XcAc@( zD&?A8Y*Av%mOomnN<1=d?^aJqt^@1^hq2MlxgD>>la(fSbg1C94mdhZOY(#>?h+gw zW+}O@Eu5^f#p?~!MtW}KkCkPeifs|!-%~0#^3cl%${A#eRizqTE5cd>H%dG(*gO^S zZN52@ubH zZ3COQLuQ2Q%WH3|8O0ViIb_xe{?;S|$XyJkCU1^I-=#-a4`Mih!yO#ZDHV?SZibUE zPUUorWCz`)v592~(H&hz`S>*Hu%e2?h5_hIRlpO-Md&DoX9I$Ge2gQFgvDh!wGEw1 z0phsQij!&%fFiQI2plhB%ggu5;pA#NO8I>>_JcDi3=Blw74~zZ4qF`UZD&COeBsj_9E%7mAx>!c!4*YwnNk1NA;ds@mx;eo(Zm?a<73SFgOLxx=iGVJDq6S_@P5ooZBge@>)YE(@PsD zCtF2{+>^e~SmfAkOE6FA0|Y^nFv8*|OC-Vj+REA_zXJP!2%K2Vn*)SX1>kj{K*50)%jw9LojTVCtU<&g6WfUz@@Fjl8VJB4>f5=qS^DR$9d?HL=S!*{S+N55DnSsA?}9HiaCprP0%#M?2*nYeH^OlGM8LbUp1A=V%u|HiG8R_bFm;RUFLrk3VkX56h{{_^vwAs zn+)4B^}q|02M14|Y}N&bpVK?wF?Y?h_k5pNPEpZ2LkZs&#GF-3l~SC=yvt3k z>*nf@Rl~2%NL5#YE8jm+K5^<0?0G}E5Ov~0n`cFRqYLE=anzk;V)Tj2YA$0VFyRg) zrFl`UNx9=_#?A5`koAT(Mfy&3(c*7uQ{hc2zfwsa4VmrWuzM}*s0)KxfjNVl^cFaO z!@eSIGYv*?eE_DliVbK4ZTTWZ59Ec4u=SMQn1g`q$XVXIhaX>F3 zHIlFNY+ofQa~|LeBr~=2=3-=Tf!_`T@jYLVH7O7Q-M+A7RB-r)?sjZR+iA=+#K_No zbE_(o9C1V%i67i*GynY%O zErw5zaJiQPpXZqUHp<(W>W<-Nv9(Gws2DmimirG&`Y|46~&ED)V{F)mhUp&J=S- z<~@a^LijwG>>e29uyV$6S{N4BSipQGBPt_HwDtMT{Ds5PxMGvko9dz+RhKO{)CHv} z+}=(N2rK{rw^grcX33KjH0?&IMi!SMuO#s{b3_}-PU^xuXJ3%pOeb_ln;z4DfFr4@ zj01aS9mwHMt_It_2Dz+xAhz-NY)t5pksMHq`V2{A67tRAG5ijG34)R0Vbl#d16_NC z*k1n%`k$EZ60E;VObsX<~Xs z%Zf#P0p^0lShgl<%UsM<(Z=Vt75$WAB|qzfwQgqFI!7gftp7}5q-t^|i52#6BI3bJ z>Og^dc(nW}cD-ylmaEkE*!o3`2$nElRf6VA(s2pC`3#f32&UfMw#C&m9$SIB1V$1- zG`xuX>{6i;{){^)S~(l73il_Cvkff{oX{lIAaP*C9t>X~gO3^UnhA&lLna~~!ZS7E zNIC~~tZQ9W+qT!1jge`24m4!k#<$4!BuU<4AU{iX{QMkw%mwoRFliW?i}mO{rM9U( zI=DkTs73bF;>j^9?x-z@ zN8G!6WcGEwBBr19HDil3X@b`^?}En7gdjL0HK7ATPA-AFj3+WGYwQUr#Cn=PK}cd% z*u>f4tLJBl{P>Goegd_ZY%z^MR&YxfSPt11X<5U|jT;Do+03j|oIn3xUS7_7)2184$V=AF?r9Q`qSNl~Ww$JX)Yla4D(?*TwpXsL-msiF1U;MVo_^@^5<6ipEA>B~ z3&(a#bD@e_q@-nQTttve@IF&^ieB@cbZ#HIm#pER6bzOdJ~D%p#)H9HdBZ&KedQ-v zMSb`CweRd@45DtqV)iC-MHRr7P;}$@&(B|NJXn*3;KI*@58pHNHu#WNRbctxe+FEb z^*BH;9?%(F2Rs2#!i>W5nSMtBM?WkAfF2c4aLOKtgv><5Q814b_e!Ihm#Vej~%3?fDd$0BMFd25B&fr@`hk zZ5p!H#cSO=!5T>iH^O{pgqN+iS9N>Rn8_{M3A|hJMfDzWu_kYOVX%VgbMiLmQks>w zIeAMk1BHXgajm_gIDe%xwn}+t%^!yYq&IIA-oF}YwPC&3eC^lVv86FsvGg#fEt{Pc znm13yjLy0A6r0b|pIGt8bLaFr!6nM4a9BFFIEPFMrAiU|qY0O&V>-~VGwZC&+GpVUft_Ho6meDbUb+JI|edK!VcM@+-2m3>> zl*46u>2lo&k$WH^;Ok@VXj}@TU-I2d$Hy2_I{F>pzyNG=_@?`>wx0K2u5T(;jE_Sl zphdZ-?#i7zQ_%`Y0&>2sppha2Y~@8hR8^OJsDFE zT>6g~8&>W_foXe+9Yf^7V@%Dw!j#z~S0sJP;Df@`8h0TD`?&7N_?a3nG8RdXI^H!x zD+`q}Mibi|?rBH6y}8pCW{H1erR531k&@c5AhI4OW$+d{qT8xk0e;=yvv0pWc3hzjVz4xma*Cwx z(S)xOZFcp>(?+LABp;T=p`+G}kc>4t!fH6tMtK5Nq> z@MW-P_)h3Ri^3waa(=@tD+07V*wQf{D)slKM}4b1N2K1K ztv|z3&RF~6)l+=8jYEZ9I7!wXA+8k*QM`+<0poaDtjg=9PgTn*PTxa#P95Xz!LVH` zlrrb){-o{&oQ_sq`z06(pOY=9i#+?>fOULJhEBzeS5Kdc^`!d8&eCiVyV#3@#i-(L zWO*S76=QeD@#F+Z(4C9wRK35NHLnywMAS%z1ueXi zLs%s#$R1gM9$FxFpQ=x;Ec*iM(?gcv`9y60%OtrDC^XxBiEeGeDfU1r<3_t)-7B#P zz;E*HrZCctv86VQEp~?_>zWbI?=w07?KvksuSz+eO?dLXb7;XnlYzVEpx3RU+k3DH zx6ONa$9*FX&T1L0E|VEOqGT=+q?J|gJ7`lO#{ z_v=oe0k=7x0wgaLWU8@Nl?{?5U`M@{r<2_%Tr&wqFTX!^mCuX8Xw%}~dvnN2(KJ5USsoXbP4KUbr@l^Boy%$2 z>Tw{tYn7_zmn)5ng3Blh*8SG~`SlfKnRe^~E9y{Og`t%Rw-ng@aB5kh6q~bmAGP$r z!oiCw)a7s~q>;qK?~Y_7u{B$ge=doy<=~a&f%s){N zZG2>E$Tf?h`grmbS&w#*-DzcTDb~V8VishVl;t0G6@Dqo)8J z8Bz)908ZsdVi9N%u*_eIwWR-q)cG?({9S%fj3L1x7i!Pv!p_(p_!+aoHc^*`Hocr}X7jq%M$V!}$!GXZLOC=rGZ6WnytZ>O;&F zfQM-_+HEmzjAnAbdMrdUnpN%sin)7a7XCi1tH9CkOx08%tEO-HV}K~@=&Jg9AfS_0 z<-q^`mHwLlIu0Q=xQEE;9un!`-}=AvKMAqyWU+;lh|iKLXhxOCdg8CWp6Q$WrRY2( zAZno1x*}mW7rz(>xxMr&p!4Dt92(ATKd&g;~qZp7#Gn^wv;RgAP+(#RN&ZV7cYzZV)LnjaeRK6}?=Ub<4$mA|A+ zn(q7|`I>THX|!vo#_h2i0N=D+xpF7cGMFF(`C%oG_H@7P3Q5Zg#(4YxXeCSg|V1-Q+T zc{_jY(GLH=(SyLO_SjVWvL#yN)%U8XH4bkr6Gx}@tz-J?jEy#Ne5!BRR*P9a{q2{& zWImeUqxh}W)dZs+$)Y68$RoS9>|@TGS47h!Gt7wWZ`HZPRPsKpo(=a0qM*~o;Fxs& z=6VuLU4PwC9$1LFocbFLNMfU1Ih?ZVRZn}Y$Rnq#l_U-(cDNh)<&thH5d_KAB?VsU zYLuC@U$&57a(7saW6tv<&jaRfPd36lHQ#!9h5g!`xlBopEzqTD`=fefy zoF&R#YGY(%5MF}=zOl+4%kJ2CD#%g1(%3Z4Y##tYJ6;4U+i6qrh0Ztc>DUFer z@opMHYF6TbxCKfKxV-vW>83IZ+VP}POFo{(t7=%I%JpGt<@khLV80IWsa^ zHIf~*5R7u_r_uM99u?F}=o8~>(m*BOHIyfj#!(N4cES>;jn+*kPI>;$o=kZTwi)aG zG9C0*XGvf>H-i3UR7r( zs`f-(3m78{@#EYS3sJJ{H{dzOm2g7NV|lnpiHP1e)LbqMw6S*TmTNYlF+J;kewW2u zn>c2ACloEI+CXBaw0h}f*-Ha%(Uxl&=1{_{NYOC}`_ycxUgzeRg1dO84eM7#sAxh# zk7#&C`bnwR;peww;mCdi*er7ZHjgs5G&+##?T(L+IYpJ$5%rYSGYadD(1*7TgS#oX z27DfRnaerNpMe(P7?GQ{$>W#A&3RP1NOB3uC5&pOuJc%g9-t_)2N=pM_(r(?)o9$G zhzB*7TaI>f>`y~8T&?xhzmHxF_mSsm8nU2Z(xK>di$pFK1uqt!+nN~uBMKUVuwhqAFUJYR+$&+9fhSEei)F- zkoib=q$QaWDZjB6-+n8FX9P~dr6df(K@g96B2PZaotAGFL`pYfmu{%K| z2lI0}#?*r<95-Z&@$-XjBruBHDaF`P^A0;Vy2j7Z52S9pju=4Bgxf!)2R_)>6>^`0z4iCOl>fe#cZLQA1+1cr>t5@H>d$&CJ zi`4CGw3G4ZX!v2e3}Rh?D7(1(cbBlTH=CAoCbM#rC}~T) zg*T@PQ<$CC1?`}TRSVZwiKwoi-MP<x2^=rXi+^cJar>-xo4J()@Ax^Q~Y#RxK{< z^{1~78d{UE9O5=3oJxbK4nP{-;azfa3i>ieT?P5Yzy68X;X3RoRNcdFMhCJO#V&F8 z?4A6K`Jpg-FjNOzSuK)+FLl?|*m*wUDQUJ_LwQQ*O3iu&Uhsda12#>Xvf5Hv!mjiA zras#s^LONg^tGC<-D!!m4PzW6K|?n#pVly8)?q~UH};+E0j}R-{xic><^Q?M7wgs= z<9WKBIJQ8A#Xqcsd3sqhb#I*%(g)nbTQOgU|J_ zwP5u(t^mb1`S#V{R@75nan;KL6Pt`O2>esxBdv?JY}#wYQhLlqv)E7FxO#H;y?LXj ztIK~4N7#2lCymmq{PfcgB|X_WpPU^J{om!$@a*c&c<1WwRp{%{`RR}32~HdG&h9OkYO@D>%PBow20Y9_x&PPDo2Zp z|5rcn-6QI2Pmd-$3p@b_96?lX_ew>mHsc{a0jmd^O9{dah#QF+iz(qBaxZR{dpdWCtY_AdAp=yv0iVIu-!?4@m){Po zxLJ&8^j<=-rbbJ@nIvB<(?IegJ84HA{BTF-=@GaP6_`gp>;^jOcut2MFd~dKfHCV2 z!ir=2aWFaeVWo1MR-Y&rjd3hdh?7{2rJC1n<=93$e9#%uUwgi>xwUOSH(tu}nDOkY z*887^*MIoor=6dEczt8=!|uxUr%!EEg9&HHWPr35(d)SfrG9sSu?KLFO_B_GKXz=7 z9APT2&TS(c!9UqZv@4FbJ(FgAcIXp1uBoqIM=HcGjfG#qoe6PBHRZT<=MMC{<{L&e z*8qUZ9@8;h4$0mX;Ao6Pi|ZgzZseUItT9sl(VaV%4zZrAO*Qc?>c6Vl78`cVvfT9o zaK~~S?p_+545ml7t3Rdm7+m@R3Z(xen}+?@0WIsEKc%|a$z@GRF}Qc{`l2dOR*{BN zS>ptt82_|7+*KNTbC-bA+M8nn?v$*;=EKJm2VtBS|L)UiL>~9UgHIS{3(29P1^0cFD^hq(JC0A(^#&VVXQ5%YPsqiI( zRPmy=I_J{(U~}vF%LPqs2_dAy+1%#oP(Q--8<BxiSNx)Ix(=tdb<0m7xNk4W8m|~;I6lWP7FjKy zgz!8J+vD4|sqffNz`QTugiV^Jc4K*U`TM*8zikToH>Bio=xU2982v|r-C_Euo&Gxh z(=sG{w!%i9cHHouvCEb-PCB5UBrx=?IMAZdPeQ0qE4dQSrvBLo(IN+o8zFdPW}mQuoY`(`{9I_p8%IZl)Aa#JP5r)84(&`!nG&K(HWy z!($6>pi|glhABVKsN6}RrP6M}mc;L0oIMO`l-@J!)VI)Alo&7QQomRHoXRANJ9av~8 z;Zp!dGh=1UZ!gT=Uvt%xq*gO?tl-RZr_USjO?jYCwXD(*(Z(a2I$^jg1OS++1eTX2 zH1vG{$ZaCpw9VBGolholU!c7gwPPGL(TV(l2g460CEbUnYNV7g5riFdFf;tcDBC*F zl|1NtkI0;C_&--ttVhA9iE4IgC;_*6=BsZ3@LbbLZcJ9F{H}#kf%zRSbgTje0>85W z4>4&9SJuqt>9*0NmmyDWlJ=!Vh$s|!6iiP~FYVD`Iq$k%M5<{x-*kLCStR zgpe`9Ap2DL{dW5gZ8$ULIUDySIfnJ}WA66u|!^hLckn=d!sWTeOQgHtFhkUo= z!7(HTDQ`(j$|Ee7-I=a1cdKF!+5oId8lo}H*^nbF9UI0o8HGOB(%o5-Q z9QP@+e;%5YS#alh9>hGpD!|;pnH1kgqmCQhyrm)f1R3n}v~v4T)IQ1zc#gLM1t-wV zQlwgGy$)M-AP1kV^Iz0CK0F%s|HOBwTrwTm|2!U?PpiiR!c>8!e(uFyI-;IjlIG26 z-h7@?KAW+Brc#>EF?Bw-&s1jqo<45vX~%(QlsEGKg=2X1>GIyx63B!JQ=WI4fI9PT zX-0bwTu6h>oNVuz=lXp_pzYU~tfF9oN$NfL`N_gFlf*1(OWRP^H@louCU^9x_;53 zrr~aC?po8Bdz*Boa#P9$-6$}zUdt`MZ#{edV4WdfyK$r5Qw5DaqfACUpKx`??L=AJ zn}LWNXN3r5&ou>Pi z?!pM5JYnncywiAsN4^*->hk}s@>mv+@q{dqEOq!VzGuFZn11zcuiJR9GVf76PyK8u z7sBF>^nR|$o25*EIE$Fj)2@Hx_aAgcL@PMP%8C+$y*(M4)p;U(KmAPm?S0&)Ioflw zB^plpq#K+XANPq}A-F>ddarOW9?Am5A}m_g3ZhhcL)G+Pe2$~&j#6!~#yBY3YFbQ1 zh#8}g`%uu*SB$~lYY20qXJdonvBMN%zJ~&7UnO23qZ*ZhQ74b`Du1UR-S~PlOgG&uE=}<{V6V1)ZTb-CGK{sUnst%@fu|6IO?7qukuM&dD!DIG`a0 za5A_$30nQl{*&KcKG|9)j<0^dT)+Q7cvF6lcV=$tdnLAT-pv{_!gcJP6hg9k&lPUiYRX$zm3nrp6;L(%ez985I*20{~(X4fOfsr z@B^@W_s35C?rv4Bx|&P2HW<76tt0IqB4Ptb`Nt_A%}D0K0ad4`r2+%9%Z8zYj5HrU z`Pcdb%|Bag7JOEhph(NL0lV#!x#bFSK^mE5MH9%(S%?-kpY<_kJTX`2%T#sH1oIE;Ux|4ybJ6mCU%!;$og3OoeC(;VTG~Gp+E!Ka|@% z;uLw#n3|Fy*YPZ}eull3E;d0TX2rPGbrb?@qr@>?$3ExtW07UHjRlXOnQ>^qcAg|%H__+ONL(k z5G;ltxncWPB~87C{=nv1On?txH1_J;H7ZksLX3yR?9lhJwbA*>j)S5(G)@30@wB?W z(v$9V^#`n}4&T9~$yMs{lW zE;3~wYTIW>*vXj)l_k9VvbufgsT!4`oNNCq`YjeMa=#yA`e@^E0CN;t$^Aj_sKgfUx-CAWfh6;i8Q#&D!30!*SuhzO1u{9uZa zvRyRdSnyJfb-K8ZD6Df$E0yF>v#_2szFe5f86bstHo=IDoWg5InVF$%yol)vmBa}@ z@FqS9BhX*^iCS=G(8YJ!a$+~;ZnFLGo!$ujD-82BYhVKn%CGBdm@#br`%`mHQ;lAe zGwvtHmg>k>ob=e=G6z<@&Qe zJ`kHtQk?2*Zzq!R1yG1eRn??|RPp_T3@cfm@Flxx`!phZDiO@V*wvPJj^l zOfrZ;xGpDpD;9ozR&^0}l?Yp7`SN;EesrmPHT zLBKCBox)l~$s||P0;kG`LSclU^8kaD)(Mway@)C#XI>9a9;7$hZ8R~aCPM>@Rir(r z#3&d{6CwX~y2?P9AZO}asHr>V*)YbWm!BDzHFby$4eW?G8gZ>AC32QVEws54yJNVn z5X53o)Amg($b*e19cm5w?kgbpm%|l;%s_Bf&Pe|k$QWlvdV!Gf{=@aH|E^C>bM8z0 z>Br7y|2{WzUE#wZF&)xyPrUNa=$&9Gl+(+F_R5;6b;xhE$NPAUZf`ui|K#N#ex>+u`k-DYkjYLPjSc1ev~7T&z+EVz3h? znwu{RD_hC4u4A(RYNcR3+K!y6M3X$<$rkWTgLtiw?WxbEHn&;kS3A_6N4i$z ztJ%T2S*`=iSoQAd98N}Kell=tjXO-EZ^A6{B`zaa)f&5La+ElzTVu8Mkt86pGzw?h z6Bph1cAG7KrFVP5Zjn@Xpy&o#TyG@N%q9#u9%ZC%LiyhA zN`l;Mn|t9yFMTY?=EiMy9IC6JSB~)+_XLEYa~v`MwFm1+t+%AUBME0$GK8_ z&JWGB&16E~>F0Mw{1x0t%XUOw4-a884_>S=8h7$M4UoBJPpXVoy~7GO63ZVZq(!@qVLje32=y$zkwNDoyn0fZ zSsB7m+{2KXlb!+%JgNW|MPyxW^~1^h_4qH@x^SpMqKB}WT+n1{4wsJVNm6uAS7HH3 z1g06hIs&hT;FQP2mJ`1tgJiE6i>_acLr=ZFnX7qtR&I(pJS*_tK=b5HVfS@$JI9=* z+Q)MuOD3?*jQtaJw7qS2G;u2J_AB*GYVIC#3gk>m$YVENTsV$rjx!r|@GqlWcsDsL z&6UH#M$KQtN2&g!!w4Ss_v)W)NE;MKm*@m>_A}H86P6+qHDS@|jVizm>P;;jAvkkB zVr!$WOu;T=#_f8i8;J%G$%1`}T|8!w!XX7dlP24dD6Mw8{x6O*GAeSKCoQ_i>JV2b zNv(8Y=QMU9%-;y_O)i0c=M=d+M>VD{aFqGN)u1lzF-+V>%=TA=34s-mO<&Ix^CBL- zw^w?jE4>#RS9<$bdPy@eyN4zljDbn zLL=Sm4Lu*Z4Q|=VE_zF%0#^ty44KelB6Ki*ml|rr?4{;+3WKD~3f0E@B$d`yZkF9P ziv0&@!3is^brAF%h$JIevtFl12nbmeN)_5WS^>k&YQ{VkKa1a6%`+0hhO|VeDJye9e*YGlbt=8lmY zcz|E_R_H88BxC{?L89+^1loy@-{FEjfK=`q7n#u_n)Ipo);*BhFELk^-SnJXP zzH_T>m74BMj=H&weJnK8J@YbJT%a4X3y&+9do{{B1Po(5gyBpJRL9XGDbRkLN>CZI zzf)Nz#E7_1rM8N4cGY*UdfLM;G8r{n*xmVc665Z1gdH}G_Cu(U61>EC#f6lLm)Y@i zO^LYksCK>qO_i+hs*J`UnW_po*T%-%S^S|#Xx7wP1^9@ zzlqpGitt{;M{Q*N+Bak}9V=upZ0VdqSof{E=w!cfPFjN|Fq(~dPWMS{Ch1&%>*f6? zPg`>#&y?)v>hDrXNI-#xs_N}t)8>^%8Y_zuDN?)H-iv07F@D+lfNW`rin@YLZkB0= zS(-EL&d@WykRdh308+mvAx>6STZ4( z|4r~#^*~$`9X{HOTq;~tBN+we^+|plxZs?_dC^9c7|Mn+Hp&%C{ z#3}}*#p)maqr#H|aHatqh)2CkOk*QSs%A7GeNM6c8q1*$izcjpf>2SHw@$^>EDzQ? zGc-49Vm1q-i6?_h{>hygm>!i-L-{xImZfs8qA`X|Hy zU>VPNYxh}F`O>pq9r8B{!h}UuZbXy3PH872Y5f1SwDB6Tke!@<+3@I+C_$g%%I+sI z8xlZCA|tb*s^qrrR^x#hm#mKNFqt;fMK}izY)T4W5-G=ity67EpndWn1l4sY8RkuS1;dSa<5 zwSHOVFgP}s9)yWD9Z6`@ye_RMrh-@AIY(Q@)rFCIR$Ux>_<7NcBmc0KG53*Qe){Pr zDtxph+q|pX6DCzTM!9v$~|_ELGyOC=f1G+eIysBCb)ZY z@??U92xRE`?nx{4T6h`&IaU_<92fFqV*pgMuf580Q&T59DlGmtcgZ$xEg9ZqbcW$KYp7B0vz-C4fh>HD=jIkPo zb!NNDzG&-!#v$<79-sZOZR6FJT)Ex3zIV`OSQlXkNSc7HuR5IF>DxSR@`;U5K8ZoMs{wv@_AHsvxgM z=@#RpSIlEO;0+ux3;QjjuHfij!FlLrL(P9-f z$xe%@TT?pTYV5ha9CkLnE?m$*?D3-O}6Hd^arQX^8QKEb@>~_*_(mT53_pIyh{)bU+?(+lRV{nu>`xH7I!X$~g`V zrdbwRnaWaZOaP<~Syl+&JKC3FYPi_)Qe(CzK~rp}@)qulnvBuBf(GLb@-sDP$}lT6 z7kZkpt6>WOxlTJ_&~!YRAiKg?(nJr~cQYIf6(MYKKd@{u7B1KLaa~JxC9nA48NnQ4 zIsvT5`FZwk*iq{<^kb$C933_c*g#X{W*2G0DtGMd3wX7CbFv}HR*@F?O}M3)M4n)U z#VBfk4WPKgA7sDfzph$XF=7RyTyZu+Z8SHUUB!eJp&aogv=igl-QigN)TyCIgR}LS zi_>c2Q_q6uyqD5;vfC9t9*@SP5bQtfKfC{;S`vS)x6k~~p_u9|=W(~3tNl}#?=u*# zyVg|jhg-~>wH!^ipZx)&2OGuzc(>(YfPCw0$#{h~XNWQcv!GTYR`wPh>T*}7|00{w z%r^`!81rfW8&ON;j7PY|;YSc!zli~kAkePymqihxK#SAl7?9BL_cH}H(a}qNrVo&; zA7ktOWYCIV16vn&F6F+>aZYT^hYB=ua=QKJAIp}bj#&{7JH7Jl&w4V$)a8}xcT*34 zO^;DAG}Cnnlc>e04sAu}z{jZ+hD&PQ+3Yt)3ErdyI2rGcusLyvS~GJ;@#Y_IoD_Yq3@-wNC;i={WIACUr;cIB`vTwm zsxG21Ojy9mmmQFZHLW)Mq9d+fecHe+q8oG8OO{?%(x(O$2U=h*ABDK?UAx^ZV&P=2 z!SrY@Rne!k!N4J_#DGye>%vC8_I` zYNNN(!+?T6G!X1Y1Q&bj=Y+T|lR_descPxa0_>_6Fe)JfhquZ@kNi}Z#N8tTwBSCr zG{wi=uHM$_pG57|m2^59oVx9YE!9_13Y>McU@~?4YTF9vW@5?7^Sm@2O>MDy`f{t< z=GPVe|A*&u{Rt`aWXv`v=)Y$%HkdfU6O}4vSoDy8o|zl5^{jWAllYD8Fd#F0@J+!K z)2(dvdrQ*P$<95@)NDVF_iiyX{Xg;4^%Mo;%a?Pc{VU2;6(OMSd!dNY1bL^zl zF<=K}A(_X;*+SyGcv@%w;M7CRa}zZCCLNwhT3<&zbgG+dz3hDcXpFZ zdD?#o>Mx)4?ULeG5M&q-24^ny0~cbWXT*iK^T~LKM9>@&7+i3p#**W?X5(;P@*&xsewqEI%HIWlnBN+R{NMb+Xsw7!~JPPpJ{m>06t;uQnV-yJphiz1XbNl-V zqMBn1Ipb2hC;{JSCrBa{TAhSP#e5WP(dr8+T66ufF?YPRJVt{Ydna`=?(dAnx$v(h zSiEnJTe}B4JlXe!?#B7^mTVNuL6LDE+v0`t(dHdkRGW?sD~#&dHZg$nR%-w8q`($T z$r{LF9AFb!W@NT5o-GH*0T4qS2Z(`+!=nCRP=O>s2F&L=yb*!ZZXeZ!rb-eE=K>MY z<=O8lWIKyryfWkK_>^%%YZRHQ0SAl1}G`E z@s*1^zP3MvrwKq>d2d+KyVM1hn13mm(26+hSrZoHG_8cV#RCwM9VxCb7>=dCXf0pf z1#fYR@DbzPKCwU09c&*+KxKfD2+v!TemW`F#zS5gO1 zuWx)~vst}jCYg>X?^=WuF zy~-jV`(#eB@`*FfX4+hs5FN6~Ey4x`4HG5g*2$)`oa7hbuaU%x#TY!Y&Ot9p#b-wu zC77v>?KmK$)>*MfdW`*j@nafe8_5%nI+Ckq-Irr{ zDQcGfMbWcl1_l!N{5qXC3DNnSe>?M)ZhW)2&S=>TM^~z&K6f+?e}RSq@ojCH+?(nA z9bjc`1C%8{u@ls`;eNY(hHGqt`g?%2W8HN4eQGOd!`9>Y^yxi||3Icb|;T)(*}` zM^i=n1m2w`GzP4pf9}+umxz3Pd*MHI1uaPN88-fSxYysc#?pg`)UISByLJ`z{_=GftcWmzh=P_Et3-&y?e_rN0b+BcrVLe8qdBJ0grKur zNQ7xKUzl=d=H9mgv33%o$c8fvJ@JabwI=ec5arZh?@CKW9!C5g&cNQfTGGI{! z6$bhgZa|Uw@U8udQot;(q?LDrpnjBq<8WRIk(l7EePwi^-PA>Jt{XE*3!d$&tlGSh zwTmTSW@vCE)w7*h4?fPk^ZBwvM12jI@GUNX;Y9TVmAm*MViqSTtM|acc^O8PwfWI3zgvign89GI%SOb1$c^g%pd8+3& zw@#yVf4zHncTUz8)H_=DF&&;-&1GW>@f#`C54ego*0?cd=RNI2p9i@1`sNldSl`Cj zZEeL_I8$4R8^~)r!w$z~m3BTgo77 zO^J&>WtJkb9>$2t1O0f2{hi+hOH+`<8HZJsS(r5JsuAn=WRBzEv5UoSkpnBlGJixIiOjOVhyWDYX(KI21pA9if z!)Bwqk`3Q9cE+PzhcYTBb@lD4>sE2HoH>AvG0RvCdNqn-V&%#zA6335r!(TxeUqNn zM^V#$ZlP1#B$+VCWTUj|0#s|Rk6gTu@1$wP*(16qY_2^foJJ?Gjq;8>9+5m@;LJP9 zK@ph1@SWr~5QT_>B2cBN+Z5+jtx?+mAOkDR0kjRk_9jfoi6B*v;9Ad*Rc=ht6vP*= zpQ~@03rKtAE}{SFTQk{m`Hk-uFA*DrBKie!fe;?PHl1E$>peub#GW;#Nmd%Ve)*{KL1DsmiQD>JEzgMN+RP z-mW zb*Bjh!(BUMJ^>D!m)QsSG}ND6>D*)}hS9<*a99@;G_1{=K(T&{mq)$0A0)eRT@iGz zDDcznj-J-u*1-2$bXtClri#iJbWlNEOz>Ewa{>^!HTbdYcX`Jv*Vwn5{`m4pQ9w+-Ab;X*NB|_z)vgl z)KJtxJSC2Fk6B(O=B^)G=Gv4KJJCGp8SGtwP1d~UX;Stl$cBb4_=P0zb`|5bnR$Kw z4Z3G=meFpq?#xHMdK(m}W8W~Xl2raKwIf3iv zqvUytWFQ(TAk*{AWc*8_trW6Gtd*}cmmt6yFtlOorLH<1l}L5#*WOir4#}E^{gutb z4uOUHlJTL{LQxwKnm;~oEtS#m)RG)R>|FrGfZ)?r5i>4m^+-=v$~&f%mg4f|k+gNw z?Xy!|YOTpQw^*n2215wKJW-rC)XG)}%7RQC(@_lDHuo_g=bS^<#Qe z!{0>fi|sHDKW`PNJGA{5;+U_{Lv5NS((uWZurqF+*(=MeggYeZ~PTOBP zOC((p=))s`!a}EfvxzM)MNQm6EWl2>>^7=HK4oqQt1B0J#$=U*NOolY*YhVEFJ3-h zBPJ`p3VhR08BopnvzZO=#J`ix>B+yq@BmC|(#1aW-Bksc`aHUX z`H-G{n{l`)?BS1Q{m%F-3cz;mRw{ZnR%&{rMCfxHAept%lAC2yeJ9nb@9k8IsfIq+#CWi6cYLqN7?O=g zgXu#VXiKEdKALcsTJ4Xr+I?qCo(#{p!kGoYEFtl0k&86Q8{!HAf6Uy0Kl*tCLgnPwU;bs7_(n!A) zA%{PbVcEL7<^s;Lz`uM(vqr*=U=8LL@m-v46+OJ&;zD8vX849MM6e)3ZKlC4Uap%l zoKoL2yu3INNex(7t}B+}TLD7OfCJd?8b?>9Jy(qqC)YfgW!z11t#<2CJ;Jjlmu zJjJ5eOCFv4y_LiPCe7C^-tnrYjy3zm6+HNijpzM`_cyotYsQjQ(o}H;#PS^Gj^CrJ zKhl998?Rg=04Y#GJ|$i0gsplf>Peo?8UXsJ^kb#KKU1O)NaTe*x}O^qko~AInAVow zDx;l<$y)(#1*CM-B1jvrDXe8~73M7`&78M4=Dt;~@!1x{GS0sJVeVU944Y*cvFZ01 z@1dclBI-(j$aK#LBZ(+bO;IeRU!6c0gbSYQ=?C(I)$?aF&Q3m;z4z#qS z_0GWBdS~Eky)!VSci<2#o#&@d<}&Bxf;=V6C6F+n|K#3ze3)Mt=KEioUD;tYZ_y7=&cKxOUSGl;sL^HGz;=x}F? z3%ZjMB8eL!f_2`wT)q1EpZRrAe=IH-9ZGe0^O-uN*lyL~OygdA-+p(_qtElT&9`YBDjm&?OxDedqDK2Q8GwZZbJ(jtILsR zdRBZ6J!Qy6qX1RgJi;i6$r4b2toR{ATEWHC6%y2;e7Ifx8)?4#8sDX2p^d;VlENXA zyVON#An*^7!{r|ASS)02CP#WGBE;#|N|KImtSYDG00dAfr z_mqrP#~%?X6|SAYs%{F$xXDaRa4BvMCkoE)k!PfK{(u@*#1LO$;`SJY(vD7ko1l%( zXgi`4*|6;4`En2wdCwI^s@;wJw+{_CmUD*QExNa(lt&z0hH{n|8o! zB-#ifH2Rxlg-M6gas5^ZQ}f$WZv!`?u){-C#m?_E@F&IlZ%W@%D3lHj)!IDcn$rkk zd(M+98~*5`o7GCUt9Q4nhcKCeKkOiVe`k#`w~+X=gBSHO?U`>*p0v05SyYhn5C_f1bDy6|y z{~ig?{2TuUj@ceA=v2KNP3iI5E3TvvTA&8-?a%sr>^P{HOB?6aD*$}?{O5Jsv~lG~ zI)>W|2ek?t%};>AqxB0QmWWH6z)spp|Ghc#`H?AX&TrzLx%YbDO7Xn4L&k%lOz=4{^?(?Jyjnzqnh7<0V%!z zik@=4R+ClNV%Xie;|cUGRK&aDXl4Ph?zDMkj;B5X3$I1_0h()#oyU9Us4TLAlVCmQ zrITEn!!1||{ec!a{ZAXzcngJt-nSqx=DB)H9-)hhs3aQmYyUpai_D%d008u^on@F z&qotYC5Ng|D9YH~+)$HvibN%|+;wgHvB_kNk0602IJhfpZI6%3kbw}=rd(b3LdfHq&rC%JJA@R#1H|fUhbXM0%N4N~*`L~(Wxs@u>4X*DcyO}|mPVW0 z0A)@yvaVvjR@S{YnT%w5F03kx>gWv!yZ4aRN_0?T%R642kB3DyISyB9%UFZhN7uDz z@7aB+I;=67O?AB8Yj{N&hfJF-{cpn~R@b@R$@w1P5fzJjG~8v02X4p_ycZTamfyzM zFH2sbOe);)kEe?A;R6iKxUWcIF#!k_4L)pqSX*wYECc}pH3g$pB7cs`nuJ8k&L9XR z?v`)4L|nd&9R3pSuJ*=lpjgNp+lu!ER4(1E{-4@3O~5oTs$=I-)iZ1^2`LpkPK1OH zxdC4ok4b{zuE$@ukhtDLXS8`RAenJUZtBV8CS~ZLCTX+Ybl0^;DL^BU5Pp7679|zR zbOEeWgmb&bt{_6$Q-EM`48dKhY(vLgH!i%&?LY%nFLR}hN&go>vL!4Cw~eQIe8>VW@rAE>W}Z4#Y`vP(8&n}(nhY!cWBk#J&G zoi;Ph9H|n*HbZOhw`Hjq0}CarJTv$)BF#@o2^!(bs0_=F`(^jXUQ%(=zS8Dv)d0T>2*C0gx88)uLD%a{Fn z70PqVE9+FMW)G_3Rq%F%hh+!TFFnG)T1Y&GkbXL)Egy?PYVEYdnf^Eq+9@Ia6>Yi9 zQ$kx#kPssoZ?{i4dh3d{RNgMuG(G1RWRs}}kf$L8hUl_n_&1H}`>LP0wLKD1kAY=24Jt)K7hJW_ z3Za6s$cAjqcpyr#-rA8MTnU*GN`Pbt>5O$pS--@c`eW=UJ5x=sWqbOgpon6ug7KjND==t=zh;={^7_m#ZOnc%-{7^ygLnfJhCm=sZO8&ZR9wGRd`Ix5;`Re9fJGNrqf@e~5i)oOXTtWBDHKa2_J zd``k(OHz#3_GHM)YV@;G<;rO)GAl2N;~Md+P1+>YiuhVE#hIN{EX~B!1Ya3kQ7!^i zg;*NTp?mFR)0Yzfk^)2_beqhG+`=d>B!rn1!tB<4b~X}2n%j;$ShMB&GLKF<9qFG#q50_Tre1|o1v1($UhoD6N$K0wxhDb#&l2t3S#zHUZl)}`{wj=I;<4`+-^cU zB;6iW4Sqc^d3I6K5`LA;=OkgtTUbN`X3w$M$Z%dCz*)d^)5M5*wX|6C0IE7QOhMN# zhG`sV&O?)+Y4;$|1L>vXc-`)WYb7O)1?Eh+*Lj{dO1iDjL9j0xb80*IG|R0S3vLVh zfQ{ghVR&l>%)NULpsC>#RKT+Ha`@|^B0CW#qU+&dB!Pf%dHsC%lKhoaJ#IiGd0efk zA%SYCEBd7m+5h#)=5wrJSl^`4$eY9YgaGULS=^H)85RkK*yz8)SU9Yh(--Hp%%C}T z?9LFr0g{eF&55}9Z)V%(+k8iSrLW@K_^;+%bR2z>b%BZ+Xx(dspLPlf4uySm$=3KK zK)uYhj`kr&A)j+O>6#+}jWbki?iLgQKJqo(Rho1dQEO7gC@%m&Dq%a#$B|w!DFeO^ zfAwqc-Rijz5Fs3LY-sMw7j$3g%uL zBwGA6?NeGc;3=dLwmPz$oecRU*pGIPk&;!o(Qa%$*?RJPV-pi}qP~zo%q9DFA4~Fh zjd}6#4bG;;Wjb0$W z#Q}FT<>t)WA8{ff2A-2@?%+z(r-aw1+jaKrKdcRT?GfM}F3j^`J3(ljxvsMS02Y2zpnRBUx_pHu{Q$k7G_9^d&U0eu;a_HG`7obbK9U+}J9H2dQB3SIRJ?gERZd$g^T?5J7aR5!iq ztXx~Y5eJI?fK51u;~gjeFMyrRq96m-7LLGc|IE$AQ+#u(le%a=;emi%xAwm_chbP; zh9dNs(T5rO4R4s=A%o79vUaz%=zWq_ z26__l=Qx!`T~b7w{HSYven6&d@z|Vp*EQ3#M?Ilu zIdh}^(i#g4jO_m`G{o`FcWHVHTI_aY0Ug7~Vno$L3tLV zJ5a_n$ehdq%yO9qt7Z)fJ;}buom-P}=H7~GFD2q`FS>|#KP+~B zmWiYVRk0W5a}MrFh6yMbfEh;^rgM5S&TPRTn~0Oa=tE7b&6dGzudm%Q=b$(kUdaDv zhntB2!1$Xc(mTZT)UdgM;&!87*(Sm)QosFHZu?Rq*HOj*9EQ#wY}mfjaJ#@YqaSYk zkYhKY1@L=s+1hOaI^{sBM@1mjBUctD^$A9f0&Bz=!!uw`Q@TLcyybzereRmht=EXz z`N@fbG&8&u9$On78JO=Fd7W`JJt4wbfR9>4QP9b7GMI#z>*Bfxt$Ma*mMDe5zO8<^ z@izMSRN3aVW-4Admka9I&QWlPPxT3t&&`}W5R|KTBbja0@E{y)jt47j&B9AbqkVBq zU|^1gEd7eGx%=ai`x}uz1-zPP^TRc52Dzm*jDxjh^TY9}D<^^#!(*}_^&VIc7CtEi z)}5kTFdbhaAB3IZSqT37nUd6;oGO}Y%6l$WUM;1Dte(p-4722-g6Fp2C?={aTnA%` zQRcCBM;KRIj52V>e0aQ8uZrNMLacUNT`SW2(*!Gqc{!l&fo6*>_TYV2KWjsEBNyH@ zKu%X4ry28Ld?lCpjCGZ0P7>56prqz!t*m_2Z5)PNvP)1jjU-_%dVX{8^}Ml9{8V`Fe1CJUP-jpbJno=gwdrnMGKI7AVL zm$5dCrOHv&{*u@sZd&&@`LGDs{^F(u zzd@pJ#l7rK6_*M>XB#o~4AjaBATlH}R^W!NvFd>n-xP=m9B zfCHkW8a>piV@UV{emNXe)!~rVE3Z`brl&~Lnmt}1{g=NQrLew)c1$N`>D`Nd*L3pk z-Fx@2M?tM)!ad0$1k=a*@t@NPuF!N^?eSU#;D-haBeo^(YCWS$fT zWIL5-Df+~{;a_jZh#aF4M_BgPo4y4yvY|ulwIJjHGM0J?)?CjpD*lY>#^=QW0e{2V zrtaQvPY4=rElFKByI&Z0#*+8i0oo1kVal9J8jp|w-1I-F$7l`2c(>o1wp`I+BJk`7 zon@yyN6=9U5SJpz-^nG!NN@U^M9UtM48b_#{0TdQH~p8*PtNy4)&#kUu7baqU2@%q zAQJ$4cR(ILhs;;GilmOLck#@4C@G@k+OWV5WQ=yU7s#PSg57}8U~5A1+$g-BnPO+S zjw2(-bhKv~qLo}U>Lq?6L5R+Y<*30rJ+4>J(SmuAmP_mMTT^C4!P zmorV;aK(35tPV>Z(INsDijl(Liw0#l8Fa+5ik(|DQB>|~ZtW`|NA4$}o+jLEEWtaP zX@Z+Eq|IV`^9n~DFhak8Bn@R^R!XqtFqJ>t6W9|L!ZTveQet63*9);Qs$?w8&ovf8 zk7~H+yWi59_@gpu;O|k4fSI4ile)7{;K}5FfhWb-3`=ZdI4;zEws9eUY2yM5-&{dM z2j6TN7s}egh2lB(QNo6q<_sa;^idx;dCf$ESRlUV6J$TfXGjAfgb?lrcT=8kSxupX z_4WDjad8sm)%#o@Z&cBH$vkU)*7caW3TPB>`oq3O($8l%Hw+7b3^G?LWm2emtWZS( z!OlSu{urb^yOw$5mrWuzyb*Bo*!}dG9xGS6@ci|$g3j|pfw%YOYUCn*&c3zzIZfh1elF$=Y=pL&i4-EtxjzZcNhQQN ziEL%oX#(q%tIp^3?6XCZa$fJ#e47Sols(XCa(h9_g#|@!vKU=Tr0B=~Uxv2=ur53j z@%VdAza@#r#b-(Q3wvrTS1_(1O6EyDjI=SK$Sp&A{ZW!nEgXRykh%!BTX#7Rw@arX z#6Y&L=zP8p{3=SPSy*S@WiaKgt?(Qr6Ss}fg1H@tGpppsBREftrjz- z*pXq-K~wb1NjS!ji@8QypDQmxyp`vcaM#50f1@`2;b`->#>a~MoeoHD+iczsO{8oy z)31YMW#Lq3W3nk&OamAhRK7-yv2Vi*1|VE+VzgKqkS?*VlT=whDK$B>ACukw@kqb& ziYX{(2B=n6bikVJ8+zav2Hyy3f(C@q{(WT17FWJ+p-c%@O2$H|%>BD=h;h4maCZ;K zrXeQ2Pw$!`#_j6SU60D-Hw4urwDC%eO*0ib$1*ul&+`~vGTr+0-f^1{1a4etVh z+tuK%D_C!*Cd)Q)y=RCvAG1|B8BQz@lJ1w{ygd!C&Dyjj4ikUy# zXKSPrih|Ur`5*YhomB!Q!QA0*UR=+J-Qsdf#-*>-5z0{QJ0S!AhbcSdhN!Ki#pauipoEbF@tm= zIxGa$)#}mDw#L5t6$is3LnKFxw9^V~2o|S7El{K)Sb)Gfa) z>2JD%ER{so^{pl06tVoW^X!*6&nRz+o(r;=N$-`aNjkBC>XV~r80VMCZpg*hP(bNh z)<8*=%TeVjI6FJf&N2Q;BHu66lRbHYwV5o=JQ_}H*lu=eDh*kZ=?Ju&#sBjhY52R?2|Bkm*guxJ*TKl9-Kz_MOqd|Sb`#{; zrE9$Jli6KSjFTOog!#32;^GUgt_ZYems<;?8JA}bM%I1cLhB7!*6W$l?Sl1_B@^KC zhK15fAO9tnS7ys4BJQ?lY%AgU7hF)8gF5gzIL}Dsq!CCnVcdU8!gzHFi>vdI+s<%K zoQ;KWTv52A_})(3aby285C8g1v}?C@wX%Jy^(kx$c(l2c|AcuEjj3>{N3&AA2T|

    N}_*M?5#(J6n6mD-f-I*NOozNg; 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 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 0000000000000000000000000000000000000000..2843959e0fba2b9c4585171883875f1a9318edcd GIT binary patch literal 401339 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 n!L#3s! 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

    1sW7H|m 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

    GfbKYl4e3I%(?nn2*D)98U!DyNqy(yqd6zoW| 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%0A#pBdJuONWEzb>ytTew0o|i~JVFla2 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 HMUMOr&+<