Browse Source

Plan 9 from Bell Labs 2011-12-12

David du Colombier 12 years ago
parent
commit
04a15a99de

+ 201 - 152
sys/man/1/spin

@@ -3,103 +3,23 @@
 spin - verification tool for models of concurrent systems
 .SH SYNOPSIS
 .B spin
-.B -a
-[
-.B -m
-]
-[
-.BI -P cpp
-]
-.I file
-.PP
-.B spin
-[
-.B -bglmprsv
-]
-[
-.BI -n N
-]
-[
-.BI -P cpp
-]
-.I file
-.PP
-.B spin
-.B -c
-[
-.B -t
-]
 [
-.BI -P cpp
+.I options
 ]
 .I file
-.PP
-.B spin
-.B -d
-[
-.BI -P cpp
-]
-.I file
-.PP
-.B spin
-.B -f
-.I ltl
-.PP
-.B spin
-.B -F
-.I file
-.PP
-.B spin
-.B -i
-[
-.B -bglmprsv
-]
-[
-.BI -n N
-]
-[
-.BI -P cpp
-]
-.I file
-.PP
-.B spin
-.B -M
-[
-.B -t
-]
-[
-.BI -P cpp
-]
-.I file
-.PP
-.B spin
-.BR -t [ \fIN ]
-[
-.B -bglmprsv
-]
-[
-.BI -j N
-]
-[
-.BI -P cpp
-]
-.I file
-.PP
+.br
 .B spin
 .B -V
 .SH DESCRIPTION
 .I Spin
 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
-.IR 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.
+asynchronous systems, specifically distributed software,
+multi-threaded systems, and communication protocols.
+A model of the system is specified
+in a guarded command language called Promela (process meta language).
+This modeling language supports dynamic creation of
+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.
@@ -110,23 +30,32 @@ Given a Promela model stored in
 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.
+verification of the correctness requirements for the system.
+The main options supported are as follows. (You can always get
+a full list of current options with the command "spin --").
 .
+.TF -Exxx
+.PD
 .TP
 .B -a
-Generate a verifier (model checker) for the specification.
-The output is written into a set of C files, named
+Generate a verifier (a model checker) for the specification.
+The output is written into a set of C files named
 .BR pan.[cbhmt] ,
 that can be compiled
 .RB ( "pcc pan.c" )
 to produce an executable verifier.
 The online
 .I spin
-manuals (see below) contain
+manuals contain
 the details on compilation and use of the verifiers.
 .
 .TP
+.B -b
+Do not execute
+.I printf
+statements in a simulation run.
+.
+.TP
 .B -c
 Produce an ASCII approximation of a message sequence
 chart for a random or guided (when combined with
@@ -135,6 +64,15 @@ simulation run. See also option
 .BR -M .
 .
 .TP
+.B -Dxxx
+Pass
+.I -Dxxx
+to the preprocessor (see also
+.I -E
+and
+.IR -I ).
+.
+.TP
 .B -d
 Produce symbol table information for the model specified in
 .IR file .
@@ -148,6 +86,21 @@ For structure variables, the third field defines the
 name of the structure declaration that contains the variable.
 .
 .TP
+.B -Exxx
+Pass
+.I xxx
+to the preprocessor (see also
+.I -D
+and
+.IR -I ).
+.
+.TP
+.B -e
+If the specification contains multiple never claims, or ltl properties,
+compute the synchronous product of all claims and write the result
+to the standard output.
+.
+.TP
 .BI -f " ltl"
 Translate the LTL formula
 .I ltl
@@ -176,6 +129,9 @@ If the formula contains spaces, it should be quoted to form a
 single argument to the
 .I spin
 command.
+.br
+This option has largely been replaced with the support
+for inline specification of ltl formula, in Spin version 6.0.
 .
 .TP
 .BI -F " file"
@@ -201,6 +157,25 @@ Option
 is meant to solve those problems.)
 .
 .TP
+.B -g
+In combination with option
+.BR -p ,
+print all global variable updates in a simulation run.
+.
+.TP
+.B -h
+At the end of a simulation run, print the value of the seed
+that was used for the random number generator.
+By specifying the same seed with the
+.B -n
+option, the exact
+run can be repeated later.
+.
+.TP
+.B -I
+Show the result of inlining and preprocessing.
+.
+.TP
 .B -i
 Perform an interactive simulation, prompting the user at
 every execution step that requires a nondeterministic choice
@@ -208,6 +183,30 @@ to be made.  The simulation proceeds without user intervention
 when execution is deterministic.
 .
 .TP
+.BI -j N
+Skip printing for the first
+.I N
+steps in a simulation run.
+.
+.TP
+.B -J
+Reverse the evaluation order for nested unless statements,
+e.g., to match the way in which Java handles exceptions.
+.
+.TP
+.BI -k " file"
+Use the file name
+.I file
+as the trail-file, see also
+.BR -t .
+.
+.TP
+.B -l
+In combination with option
+.BR -p ,
+include all local variable updates in the output of a simulation run.
+.
+.TP
 .B -M
 Produce a message sequence chart in Postscript form for a
 random simulation or a guided simulation
@@ -237,60 +236,56 @@ and the integer
 .IR N .
 .
 .TP
-.B -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.
+.BI -N " file"
+Use the never claim stored in
+.I file
+to generate the verified (see
+.BR -a ).
 .
 .TP
-.B -V
-Prints the
-.I spin
-version number and exits.
-.
-.PP
-With only a filename as an argument and no options,
-.I spin
-performs a random simulation of the model specified in
-the file (standard input is the default if the filename is omitted).
-If option
-.B -i
-is added, the simulation is
-.IR interactive ,
-or if option
-.B -t
-is added, the simulation is
-.IR guided .
-.PP
-The simulation normally does not generate output, except what is generated
-explicitly by the user within the model with
-.I printf
-statements, and some details about the final state that is
-reached after the simulation completes.
-The group of options
-.B -bglmprsv
-sets 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.
+.BI -O
+Use the original scope rules from pre-Spin version 6.
 .
 .TP
-.B -b
-Suppress the execution of
-.I printf
-statements within the model.
+.BI -o [123]
+Turn off data-flow optimization (
+.IR -o1 ).
+Do not hide write-only variables (
+.I -o2
+) during verification.
+Turn off statement merging (
+.I -o3
+) during verification.
+Turn on rendezvous optimization (
+.I -o4
+) during verification.
+Turn on case caching (
+.I -o5
+) to reduce the size of pan.m,
+but losing accuracy in reachability reports.
+.
 .TP
-.B -g
-Show at each time step the current value of global variables.
+.BI -O
+Use the scope rules pre-version 6.0. In this case there are only two
+possible levels of scope for all data declarations: global, or proctype local.
+In version 6.0 and later there is a third level of scope: inlines or blocks.
+.
 .TP
-.B -l
-In combination with option
-.BR -p ,
-show the current value of local variables of the process.
+.BI -P xxx
+Use the command
+.I xxx
+for preprocessing instead of the standard C preprocessor.
+.
 .TP
 .B -p
-Show at each simulation step which process changed state,
-and what source statement was executed.
+Include all statement executions in the output of simulation runs.
+.
+.TP
+.BI -q N
+Suppress the output generated for channel
+.B N
+during simulation runs.
+.
 .TP
 .B -r
 Show all message-receive events, giving
@@ -298,34 +293,88 @@ 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
 .B -s
-Show all message-send events.
+Include all send operations in the output of simulation runs.
+.
+.TP
+.B -T
+Do not automatically indent the
+.I printf
+output of process
+.I i
+with
+.I i
+tabs.
+.
+.TP
+.B -t[\f2N\f1]
+Perform a guided simulation, following the [\f2N\f1th] error trail that
+was produces by an earlier verification run, see the online manuals
+for the details on verification. By default the error trail is looked for
+in a file with the same basename as the model, and with extension .trail.
+See also
+.BR -k .
+.
 .TP
 .B -v
 Verbose mode, add some more detail, and generate more
 hints and warnings about the model.
+.
+.TP
+.B -V
+Prints the
+.I spin
+version number and exit.
+.
+.PP
+With only a filename as an argument and no option flags,
+.I spin
+performs a random simulation of the model specified in
+the file.
+This normally does not generate output, except what is generated
+explicitly by the user within the model with
+.I printf
+statements, and some details about the final state that is
+reached after the simulation completes.
+The group of options
+.B -bgilmprstv
+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 included, the simulation i
+.IR interactive ,
+or if option
+.B -t
+or
+.BI -k file
+is added, the simulation is
+.IR guided .
+.
 .SH SOURCE
 .B /sys/src/cmd/spin
 .SH SEE ALSO
 .in +4
 .ti -4
-.BR http://spinroot.com :
-.BR GettingStarted.pdf ,
-.BR Roadmap.pdf ,
-.BR Manual.pdf ,
-.BR WhatsNew.pdf ,
-.B Exercises.pdf
+.B http://spinroot.com/spin/Man/
 .br
 .in -4
 G.J. Holzmann,
-.IR "Design and Validation of Computer Protocols" ,
-Prentice Hall, 1991.
+.IR "The Spin Model Checker (Primer and Reference Manual)" ,
+Addison-Wesley, Reading, Mass., 2004.
+.br
+—, `The model checker Spin,'
+.IR "IEEE Trans. on SE" ,
+Vol, 23, No. 5, May 1997.
 .br
 —, `Design and validation of protocols: a tutorial,'
 .IR "Computer Networks and ISDN Systems" ,
 Vol. 25, No. 9, 1993, pp. 981-1017.
 .br
-—, `The model checker Spin,'
-.IR "IEEE Trans. on SE" ,
-Vol, 23, No. 5, May 1997.
+—,
+.IR "Design and Validation of Computer Protocols" ,
+Prentice Hall, Englewood Cliffs, NJ, 1991.

+ 2 - 1
sys/src/9/ip/netlog.c

@@ -202,10 +202,11 @@ netlogctl(Fs *f, char* s, int n)
 		else
 			f->alog->iponlyset = 1;
 		free(cb);
+		poperror();
 		return;
 
 	default:
-		cmderror(cb, "unknown ip control message");
+		cmderror(cb, "unknown netlog control message");
 	}
 
 	for(i = 1; i < cb->nf; i++){

+ 1 - 1
sys/src/cmd/rc/pfnc.c

@@ -48,7 +48,7 @@ struct{
 	Xglob, "Xglob",
 	Xrdfn, "Xrdfn",
 	Xsimple, "Xsimple",
-	Xrdfn, "Xrdfn",
+	Xsub, "Xsub",
 	Xqdol, "Xqdol",
 0};
 

+ 9 - 9
sys/src/cmd/spin/dstep.c

@@ -12,7 +12,7 @@
 #include "spin.h"
 #include "y.tab.h"
 
-#define MAXDSTEP	1024	/* was 512 */
+#define MAXDSTEP	2048	/* was 512 */
 
 char	*NextLab[64];
 int	Level=0, GenCode=0, IsGuard=0, TestOnly=0;
@@ -21,7 +21,7 @@ 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;
+extern int	Pid, separate, OkBreak;
 
 static void
 Sourced(int n, int special)
@@ -59,7 +59,7 @@ Mopup(FILE *fd)
 			if (Tojump[j] == Jumpto[i])
 				break;
 		if (j == Tj)
-		{	char buf[12];
+		{	char buf[16];
 			if (Jumpto[i] == OkBreak)
 			{	if (!LastGoto)
 				fprintf(fd, "S_%.3d_0:	/* break-dest */\n",
@@ -164,7 +164,7 @@ CollectGuards(FILE *fd, Element *e, int inh)
 			break;
 		case ELSE:
 			if (inh++ > 0) fprintf(fd, " || ");
-/* 4.2.5 */		if (Pid != claimnr)
+/* 4.2.5 */		if (!pid_is_claim(Pid))
 				fprintf(fd, "(boq == -1 /* else */)");
 			else
 				fprintf(fd, "(1 /* else */)");
@@ -184,17 +184,17 @@ CollectGuards(FILE *fd, Element *e, int inh)
 		case 's':
 			if (inh++ > 0) fprintf(fd, " || ");
 			fprintf(fd, "("); TestOnly=1;
-/* 4.2.1 */		if (Pid != claimnr) fprintf(fd, "(boq == -1) && ");
+/* 4.2.1 */		if (!pid_is_claim(Pid)) 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)
+			if (!pid_is_claim(Pid))
 				fprintf(fd, "(boq == -1 && ");
 			putstmnt(fd, ee->n->lft, e->seqno);
-			if (Pid != claimnr)
+			if (!pid_is_claim(Pid))
 				fprintf(fd, ")");
 			fprintf(fd, ")"); TestOnly=0;
 			break;
@@ -245,7 +245,7 @@ putcode(FILE *fd, Sequence *s, Element *nxt, int justguards, int ln, int seqno)
 	case 's':
 		fprintf(fd, "if (");
 #if 1
-/* 4.2.1 */	if (Pid != claimnr) fprintf(fd, "(boq != -1) || ");
+/* 4.2.1 */	if (!pid_is_claim(Pid)) fprintf(fd, "(boq != -1) || ");
 #endif
 		fprintf(fd, "!("); TestOnly=1;
 		putstmnt(fd, s->frst->n, s->frst->seqno);
@@ -253,7 +253,7 @@ putcode(FILE *fd, Sequence *s, Element *nxt, int justguards, int ln, int seqno)
 		break;
 	case 'c':
 		fprintf(fd, "if (!(");
-		if (Pid != claimnr) fprintf(fd, "boq == -1 && ");
+		if (!pid_is_claim(Pid)) fprintf(fd, "boq == -1 && ");
 		TestOnly=1;
 		putstmnt(fd, s->frst->n->lft, s->frst->seqno);
 		fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0;

+ 301 - 26
sys/src/cmd/spin/flow.c

@@ -13,12 +13,14 @@
 #include "y.tab.h"
 
 extern Symbol	*Fname;
-extern int	nr_errs, lineno, verbose;
-extern short	has_unless, has_badelse;
+extern int	nr_errs, lineno, verbose, in_for;
+extern short	has_unless, has_badelse, has_xu;
+extern char CurScope[MAXSCOPESZ];
 
 Element *Al_El = ZE;
 Label	*labtab = (Label *) 0;
-int	Unique=0, Elcnt=0, DstepStart = -1;
+int	Unique = 0, Elcnt = 0, DstepStart = -1;
+int	initialization_ok = 1;
 
 static Lbreak	*breakstack = (Lbreak *) 0;
 static Lextok	*innermost;
@@ -40,7 +42,10 @@ open_seq(int top)
 
 	t = seqlist(s, cur_s);
 	cur_s = t;
-	if (top) Elcnt = 1;
+	if (top)
+	{	Elcnt = 1;
+		initialization_ok = 1;
+	}
 }
 
 void
@@ -113,8 +118,8 @@ check_sequence(Sequence *s)
 			&&  n->ntyp != PRINT
 			&&  n->ntyp != PRINTM)
 			{	if (verbose&32)
-					printf("spin: line %d %s, redundant skip\n",
-						n->ln, n->fn->name);
+					printf("spin: %s:%d, redundant skip\n",
+						n->fn->name, n->ln);
 				if (e != s->frst
 				&&  e != s->last
 				&&  e != s->extent)
@@ -147,6 +152,10 @@ close_seq(int nottop)
 {	Sequence *s = cur_s->this;
 	Symbol *z;
 
+	if (nottop == 0)	/* end of proctype body */
+	{	initialization_ok = 1;
+	}
+
 	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:"-",
@@ -388,8 +397,13 @@ if_seq(Lextok *n)
 	&&  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);
+		if (has_xu)
+		{	fatal("invalid use of 'else' combined with i/o and xr/xs assertions,",
+				(char *)0);
+		} else
+		{	non_fatal("dubious use of 'else' combined with i/o,",
+				(char *)0);
+		}
 		nr_errs--;
 	}
 
@@ -560,36 +574,72 @@ add_seq(Lextok *n)
 		add_el(e, cur_s->this);
 }
 
+void
+show_lab(void)
+{	Label *l;
+	for (l = labtab; l; l = l->nxt)
+		printf("label %s\n", l->s->name);
+}
+
 void
 set_lab(Symbol *s, Element *e)
 {	Label *l; extern Symbol *context;
+	int cur_uiid = is_inline();
 
 	if (!s) return;
+
 	for (l = labtab; l; l = l->nxt)
-		if (l->s == s && l->c == context)
+	{	if (strcmp(l->s->name, s->name) == 0
+		&&  l->c == context
+		&&  l->uiid == cur_uiid)
 		{	non_fatal("label %s redeclared", s->name);
 			break;
-		}
+	}	}
+
 	l = (Label *) emalloc(sizeof(Label));
 	l->s = s;
 	l->c = context;
 	l->e = e;
+	l->uiid = cur_uiid;
 	l->nxt = labtab;
 	labtab = l;
 }
 
+static Label *
+get_labspec(Lextok *n)
+{	Symbol *s = n->sym;
+	Label *l, *anymatch = (Label *) 0;
+	int cur_uiid = n->uiid;
+	/*
+	 * try to find a label with the same uiid
+	 * but if it doesn't exist, return any other
+	 * that is defined within the same scope
+	 */
+	for (l = labtab; l; l = l->nxt)
+	{	if (strcmp(s->name, l->s->name) == 0
+		&&  s->context == l->s->context)
+		{	anymatch = l;
+			if (cur_uiid == l->uiid) /* best match */
+			{	return l;
+	}	}	}
+
+	return anymatch; /* likely to be 0 */
+}
+
 Element *
 get_lab(Lextok *n, int md)
-{	Label *l;
-	Symbol *s = n->sym;
+{	Label *l = get_labspec(n);
 
-	for (l = labtab; l; l = l->nxt)
-		if (s == l->s)
-			return (l->e);
+	if (l != (Label *) 0)
+	{	return (l->e);
+	}
+
+	if (md)
+	{	lineno = n->ln;
+		Fname  = n->fn;
+		fatal("undefined label %s", n->sym->name);
+	}
 
-	lineno = n->ln;
-	Fname = n->fn;
-	if (md) fatal("undefined label %s", s->name);
 	return ZE;
 }
 
@@ -735,6 +785,226 @@ make_atomic(Sequence *s, int added)
 	}
 }
 
+#if 0
+static int depth = 0;
+void dump_sym(Symbol *, char *);
+
+void
+dump_lex(Lextok *t, char *s)
+{	int i;
+
+	depth++;
+	printf(s);
+	for (i = 0; i < depth; i++)
+		printf("\t");
+	explain(t->ntyp);
+	if (t->ntyp == NAME) printf(" %s ", t->sym->name);
+	if (t->ntyp == CONST) printf(" %d ", t->val);
+	if (t->ntyp == STRUCT)
+	{	dump_sym(t->sym, "\n:Z:");
+	}
+	if (t->lft)
+	{	dump_lex(t->lft, "\nL");
+	}
+	if (t->rgt)
+	{	dump_lex(t->rgt, "\nR");
+	}	
+	depth--;
+}
+void
+dump_sym(Symbol *z, char *s)
+{	int i;
+	char txt[64];
+	depth++;
+	printf(s);
+	for (i = 0; i < depth; i++)
+		printf("\t");
+
+	if (z->type == CHAN)
+	{	if (z->ini && z->ini->rgt && z->ini->rgt->sym)
+		{	// dump_sym(z->ini->rgt->sym, "\n:I:"); /* could also be longer list */
+			if (z->ini->rgt->rgt
+			|| !z->ini->rgt->sym)
+			fatal("chan %s in for should have only one field (a typedef)", z->name);
+			printf(" -- %s %p -- ", z->ini->rgt->sym->name, z->ini->rgt->sym);
+		}
+	} else if (z->type == STRUCT)
+	{	if (z->Snm)
+			printf(" == %s %p == ", z->Snm->name, z->Snm);
+		else
+		{	if (z->Slst)
+				dump_lex(z->Slst, "\n:X:");
+			if (z->ini)
+				dump_lex(z->ini, "\n:I:");
+		}
+	}
+	depth--;
+
+}
+#endif
+
+int
+match_struct(Symbol *s, Symbol *t)
+{
+	if (!t
+	||  !t->ini
+	||  !t->ini->rgt
+	||  !t->ini->rgt->sym
+	||   t->ini->rgt->rgt)
+	{	fatal("chan %s in for should have only one field (a typedef)", t->name);
+	}
+	/* we already know that s is a STRUCT */
+	if (0)
+	{	printf("index type %s %p ==\n", s->Snm->name, s->Snm);
+		printf("chan type  %s %p --\n\n", t->ini->rgt->sym->name, t->ini->rgt->sym);
+	}
+
+	return (s->Snm == t->ini->rgt->sym);
+}
+
+void
+valid_name(Lextok *a3, Lextok *a5, Lextok *a8, char *tp)
+{
+	if (a3->ntyp != NAME)
+	{	fatal("%s ( .name : from .. to ) { ... }", tp);
+	}
+	if (a3->sym->type == CHAN
+	||  a3->sym->type == STRUCT
+	||  a3->sym->isarray != 0)
+	{	fatal("bad index in for-construct %s", a3->sym->name);
+	}
+	if (a5->ntyp == CONST && a8->ntyp == CONST && a5->val > a8->val)
+	{	non_fatal("start value for %s exceeds end-value", a3->sym->name);
+	}
+}
+
+void
+for_setup(Lextok *a3, Lextok *a5, Lextok *a8)
+{	/* for ( a3 : a5 .. a8 ) */
+
+	valid_name(a3, a5, a8, "for");
+	/* a5->ntyp = a8->ntyp = CONST; */
+	add_seq(nn(a3, ASGN, a3, a5));	/* start value */
+	open_seq(0);
+	add_seq(nn(ZN, 'c', nn(a3, LE, a3, a8), ZN));	/* condition */
+}
+
+Lextok *
+for_index(Lextok *a3, Lextok *a5)
+{	Lextok *z0, *z1, *z2, *z3;
+	Symbol *tmp_cnt;
+	char tmp_nm[MAXSCOPESZ];
+	/* for ( a3 in a5 ) { ... } */
+
+	if (a3->ntyp != NAME)
+	{	fatal("for ( .name in name ) { ... }", (char *) 0);
+	}
+
+	if (a5->ntyp != NAME)
+	{	fatal("for ( %s in .name ) { ... }", a3->sym->name);
+	}
+
+	if (a3->sym->type == STRUCT)
+	{	if (a5->sym->type != CHAN)
+		{	fatal("for ( %s in .channel_name ) { ... }",
+				a3->sym->name);
+		}
+		z0 = a5->sym->ini;
+		if (!z0
+		|| z0->val <= 0
+		|| z0->rgt->ntyp != STRUCT
+		|| z0->rgt->rgt != NULL)
+		{	fatal("bad channel type %s in for", a5->sym->name);
+		}
+
+		if (!match_struct(a3->sym, a5->sym))
+		{	fatal("type of %s does not match chan", a3->sym->name);
+		}
+
+		z1 = nn(ZN, CONST, ZN, ZN); z1->val = 0;
+		z2 = nn(a5, LEN, a5, ZN);
+
+		sprintf(tmp_nm, "_f0r_t3mp%s", CurScope); /* make sure it's unique */
+		tmp_cnt = lookup(tmp_nm);
+		if (z0->val > 255)			/* check nr of slots, i.e. max length */
+		{	tmp_cnt->type = SHORT;	/* should be rare */
+		} else
+		{	tmp_cnt->type = BYTE;
+		}
+		z3 = nn(ZN, NAME, ZN, ZN);
+		z3->sym = tmp_cnt;
+
+		add_seq(nn(z3, ASGN, z3, z1));	/* start value 0 */
+
+		open_seq(0);
+
+		add_seq(nn(ZN, 'c', nn(z3, LT, z3, z2), ZN));	/* condition */
+
+		/* retrieve  message from the right slot -- for now: rotate contents */
+		in_for = 0;
+		add_seq(nn(a5, 'r', a5, expand(a3, 1)));	/* receive */
+		add_seq(nn(a5, 's', a5, expand(a3, 1)));	/* put back in to rotate */
+		in_for = 1;
+		return z3;
+	} else
+	{	if (a5->sym->isarray == 0
+		||  a5->sym->nel <= 0)
+		{	fatal("bad arrayname %s", a5->sym->name);
+		}
+		z1 = nn(ZN, CONST, ZN, ZN); z1->val = 0;
+		z2 = nn(ZN, CONST, ZN, ZN); z2->val = a5->sym->nel - 1;
+		for_setup(a3, z1, z2);
+		return a3;
+	}
+}
+
+Lextok *
+for_body(Lextok *a3, int with_else)
+{	Lextok *t1, *t2, *t0, *rv;
+
+	rv = nn(ZN, CONST, ZN, ZN); rv->val = 1;
+	rv = nn(ZN,  '+', a3, rv);
+	rv = nn(a3, ASGN, a3, rv);
+	add_seq(rv);	/* initial increment */
+
+	pushbreak();
+
+	/* completed loop body, main sequence */
+	t1 = nn(ZN, 0, ZN, ZN);
+	t1->sq = close_seq(8);
+
+	open_seq(0);		/* add else -> break sequence */
+	if (with_else)
+	{	add_seq(nn(ZN, ELSE, ZN, ZN));
+	}
+	t2 = nn(ZN, GOTO, ZN, ZN);
+	t2->sym = break_dest();
+	add_seq(t2);
+	t2 = nn(ZN, 0, ZN, ZN);
+	t2->sq = close_seq(9);
+
+	t0 = nn(ZN, 0, ZN, ZN);
+	t0->sl = seqlist(t2->sq, seqlist(t1->sq, 0));
+
+	rv = nn(ZN, DO, ZN, ZN);
+	rv->sl = t0->sl;
+	return rv;
+}
+
+Lextok *
+sel_index(Lextok *a3, Lextok *a5, Lextok *a7)
+{	/* select ( a3 : a5 .. a7 ) */
+
+	valid_name(a3, a5, a7, "select");
+	/* a5->ntyp = a7->ntyp = CONST; */
+
+	add_seq(nn(a3, ASGN, a3, a5));	/* start value */
+	open_seq(0);
+	add_seq(nn(ZN, 'c', nn(a3, LT, a3, a7), ZN));	/* condition */
+
+	return for_body(a3, 0);	/* no else, just a non-deterministic break */
+}
+
 static void
 walk_atomic(Element *a, Element *b, int added)
 {	Element *f; Symbol *ofn; int oln;
@@ -747,16 +1017,16 @@ walk_atomic(Element *a, Element *b, int 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");
+			  printf("spin: warning, %s:%d, atomic inside %s (ignored)\n",
+			  f->n->fn->name, f->n->ln, (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);
+			printf("spin: warning, %s:%d, d_step inside ",
+			 f->n->fn->name, f->n->ln);
 			if (added)
 			{	printf("d_step (ignored)\n");
 				goto mknonat;
@@ -770,8 +1040,8 @@ mknonat:		f->n->ntyp = NON_ATOMIC; /* can jump here */
 			break;
 		case UNLESS:
 			if (added)
-			{ printf("spin: error, line %3d %s, unless in d_step (ignored)\n",
-			 	 f->n->ln, f->n->fn->name);
+			{ printf("spin: error, %s:%d, unless in d_step (ignored)\n",
+			 	 f->n->fn->name, f->n->ln);
 			}
 		}
 		for (h = f->sub; h; h = h->nxt)
@@ -789,6 +1059,11 @@ dumplabels(void)
 
 	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);
+		{	printf("label	%s	%d	",
+				l->s->name, l->e->seqno);
+			if (l->uiid == 0)
+				printf("<%s>\n", l->c->name);
+			else
+				printf("<%s i%d>\n", l->c->name, l->uiid);
+		}
 }

+ 38 - 9
sys/src/cmd/spin/guided.c

@@ -21,6 +21,7 @@ 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);
+extern char	**trailfilename;
 
 int	TstOnly = 0, pno;
 
@@ -88,10 +89,18 @@ match_trail(void)
 	 *	leader.tra
 	 */
 
-	if (ntrail)
-		sprintf(snap, "%s%d.trail", oFname->name, ntrail);
-	else
-		sprintf(snap, "%s.trail", oFname->name);
+	if (trailfilename)
+	{	if (strlen(*trailfilename) < sizeof(snap))
+		{	strcpy(snap, (const char *) *trailfilename);
+		} else
+		{	fatal("filename %s too long", *trailfilename);
+		}
+	} else
+	{	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 */
@@ -188,16 +197,36 @@ okay:
 				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)
-		{	printf("%3d: no process %d ", depth, pno - Have_claim);
-			printf("(state %d)\n", nst);
-			lost_trail();
+		{	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;
 		}
-		X->pc  = dothis;
+
 		lineno = dothis->n->ln;
 		Fname  = dothis->n->fn;
 
@@ -271,7 +300,7 @@ keepgoing:		if (dothis->merge_start)
 		}	}
 
 		if (Have_claim && X && X->pid == 0
-		&&  dothis && dothis->n
+		&&  dothis->n
 		&&  lastclaim != dothis->n->ln)
 		{	lastclaim = dothis->n->ln;
 			if (columns == 2)

+ 193 - 111
sys/src/cmd/spin/main.c

@@ -12,6 +12,8 @@
 #include <stdlib.h>
 #include "spin.h"
 #include "version.h"
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <signal.h>
 /* #include <malloc.h> */
 #include <time.h>
@@ -29,6 +31,7 @@ extern Symbol	*context;
 extern char	*claimproc;
 extern void	repro_src(void);
 extern void	qhide(int);
+extern char	CurScope[MAXSCOPESZ];
 
 Symbol	*Fname, *oFname;
 
@@ -40,12 +43,16 @@ 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	inlineonly;	/* show inlined code */
-int	seedy;		/* be verbose about chosen seed */
+int	old_scope_rules;	/* use pre 5.3.0 rules */
+int	split_decl = 1, product, Strict;
 
-int	dataflow = 1, merger = 1, deadvar = 1, ccache = 1;
+int	merger = 1, deadvar = 1;
+int	ccache = 0; /* oyvind teig: 5.2.0 case caching off by default */
 
 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:
@@ -61,33 +68,44 @@ meaning of flags on verbose:
 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	**add_ltl  = (char **) 0;
+static char	**ltl_file = (char **) 0;
+static char	**nvr_file = (char **) 0;
+static char	*ltl_claims = (char *) 0;
+static FILE	*fd_ltl = (FILE *) 0;
 static char	*PreArg[64];
 static int	PreCnt = 0;
 static char	out1[64];
-static void	explain(int);
 
+char	**trailfilename;	/* new option 'k' */
+
+void	explain(int);
+
+	/* to use visual C++:
+		#define CPP	"CL -E/E"
+	   or call spin as:	"spin -PCL  -E/E"
+
+	   on OS2:
+		#define CPP	"icc -E/Pd+ -E/Q+"
+	   or call spin as:	"spin -Picc -E/Pd+ -E/Q+"
+	*/
 #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	"/bin/cpp"	/* classic Unix systems */
-#endif
-#endif
+	#if defined(PC) || defined(MAC)
+		#define CPP	"gcc -E -x c"	/* most systems have gcc or cpp */
+		/* if gcc-4 is available, this setting is modified below */
+	#else
+		#ifdef SOLARIS
+			#define CPP	"/usr/ccs/lib/cpp"
+		#else
+			#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+				#define CPP	"cpp"
+			#else
+				#define CPP	"/bin/cpp"	/* classic Unix systems */
+			#endif
+		#endif
+	#endif
 #endif
 
-#endif
 static char	*PreProc = CPP;
 extern int	depth; /* at least some steps were made */
 
@@ -114,16 +132,36 @@ alldone(int estatus)
 
 void
 preprocess(char *a, char *b, int a_tmp)
-{	char precmd[512], cmd[1024]; int i;
+{	char precmd[1024], cmd[2048]; int i;
+#if defined(WIN32) || defined(WIN64)
+	struct _stat x;
+/*	struct stat x;	*/
+#endif
 #ifdef PC
 	extern int try_zpp(char *, char *);
-	if (PreCnt == 0 && try_zpp(a, b)) goto out;
+	if (PreCnt == 0 && try_zpp(a, b))
+	{	goto out;
+	}
+#endif
+#if defined(WIN32) || defined(WIN64)
+	if (strncmp(PreProc, "gcc -E -x c", strlen("gcc -E -x c")) == 0)
+	{	if (stat("/bin/gcc-4.exe", (struct stat *)&x) == 0	/* for PCs with cygwin */
+		||  stat("c:/cygwin/bin/gcc-4.exe", (struct stat *)&x) == 0)
+		{	PreProc = "gcc-4 -E -x c";
+		} else if (stat("/bin/gcc-3.exe", (struct stat *)&x) == 0
+		       ||  stat("c:/cygwin/bin/gcc-3.exe", (struct stat *)&x) == 0)
+		{	PreProc = "gcc-3 -E -x c";
+	}	}
 #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);
@@ -137,23 +175,6 @@ out:
 	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)
 {
@@ -168,25 +189,30 @@ usage(void)
 	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-e compute synchronous product of multiple never claims (modified by -L)\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-F file  like -f, but with the LTL 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-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-k fname use the trailfile stored in file fname, see also -t\n");
+	printf("\t-L when using -e, use strict language intersection\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-N fname use never claim stored in file fname\n");
 	printf("\t-nN seed for random nr generator\n");
+	printf("\t-O use old scope rules (pre 5.3.0)\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-o4 turn on rendezvous optiomizations in verifier\n");
+	printf("\t-o5 turn on case caching (reduces size of pan.m, but affects reachability reports)\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");
@@ -194,7 +220,7 @@ usage(void)
 	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-t[N] follow [Nth] simulation trail, see also -k\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");
@@ -205,7 +231,7 @@ usage(void)
 }
 
 void
-optimizations(char nr)
+optimizations(int nr)
 {
 	switch (nr) {
 	case '1':
@@ -250,31 +276,6 @@ optimizations(char nr)
 	}
 }
 
-#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;
@@ -285,16 +286,15 @@ main(int argc, char *argv[])
 	yyin  = stdin;
 	yyout = stdout;
 	tl_out = stdout;
+	strcpy(CurScope, "_");
 
-	/* unused flags: e, w, x, y, z, A, G, I, L, O, Q, R, S, T, W */
+	/* unused flags: y, z, G, L, Q, R, 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;
@@ -305,6 +305,7 @@ main(int argc, char *argv[])
 		case 'd': dumptab =  1; break;
 		case 'E': PreArg[++PreCnt] = (char *) &argv[1][2];
 			  break;
+		case 'e': product++; break; /* see also 'L' */
 		case 'F': ltl_file = (char **) (argv+2);
 			  argc--; argv++; break;
 		case 'f': add_ltl = (char **) argv;
@@ -315,34 +316,42 @@ main(int argc, char *argv[])
 		case 'I': inlineonly = 1; break;
 		case 'J': like_java = 1; break;
 		case 'j': jumpsteps = atoi(&argv[1][2]); break;
+		case 'k': s_trail = 1;
+			  trailfilename = (char **) (argv+2);
+			  argc--; argv++; break;
+		case 'L': Strict++; break; /* modified -e */
 		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': old_scope_rules = 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]))
+		case 'q': if (isdigit((int) 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]))
+			  if (isdigit((int)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", Version);
+		case 'V': printf("%s\n", SpinVersion);
 			  alldone(0);
 			  break;
 		case 'w': verbose += 64; break;
+#if 0
+		case 'x': split_decl = 0; break;	/* experimental */
+#endif
 		case 'X': xspin = notabs = 1;
 #ifndef PC
 			  signal(SIGPIPE, alldone); /* not posix... */
@@ -355,9 +364,10 @@ main(int argc, char *argv[])
 		}
 		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';
@@ -365,41 +375,42 @@ main(int argc, char *argv[])
 		{	printf("spin: cannot open %s\n", *ltl_file);
 			alldone(1);
 		}
-		fgets(formula, 4096, tl_out);
+		if (!fgets(formula, 4096, tl_out))
+		{	printf("spin: cannot read %s\n", *ltl_file);
+		}
 		fclose(tl_out);
 		tl_out = stdout;
 		*ltl_file = (char *) formula;
 	}
 	if (argc > 1)
-	{	char cmd[128], out2[64];
+	{	FILE *fd = stdout;
+		char cmd[512], out2[512];
 
 		/* must remain in current dir */
 		strcpy(out1, "pan.pre");
 
 		if (add_ltl || nvr_file)
-			strcpy(out2, "pan.___");
+		{	sprintf(out2, "%s.nvr", argv[1]);
+			if ((fd = fopen(out2, MFLAGS)) == NULL)
+			{	printf("spin: cannot create tmp file %s\n",
+					out2);
+				alldone(1);
+			}
+			fprintf(fd, "#include \"%s\"\n", argv[1]);
+		}
 
 		if (add_ltl)
-		{	tl_out = cpyfile(argv[1], out2);
-			nr_errs = tl_main(2, add_ltl);	/* in tl_main.c */
-			fclose(tl_out);
+		{	tl_out = fd;
+			nr_errs = tl_main(2, add_ltl);
+			fclose(fd);
 			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);
+		{	fprintf(fd, "#include \"%s\"\n", *nvr_file);
 			fclose(fd);
 			preprocess(out2, out1, 1);
 		} else
-			preprocess(argv[1], out1, 0);
+		{	preprocess(argv[1], out1, 0);
+		}
 
 		if (preprocessonly)
 			alldone(0);
@@ -409,8 +420,8 @@ main(int argc, char *argv[])
 			alldone(1);
 		}
 
-		if (strncmp(argv[1], "progress", 8) == 0
-		||  strncmp(argv[1], "accept", 6) == 0)
+		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]);
@@ -428,9 +439,10 @@ main(int argc, char *argv[])
 			printf("spin: missing argument to -f\n");
 			alldone(1);
 		}
-		printf("%s\n", Version);
-		printf("reading input from stdin:\n");
+		printf("%s\n", SpinVersion);
+		fprintf(stderr, "spin: error, no filename specified");
 		fflush(stdout);
+		alldone(1);
 	}
 	if (columns == 2)
 	{	extern void putprelude(void);
@@ -444,7 +456,7 @@ main(int argc, char *argv[])
 		verbose += (8+16);
 	if (columns == 2 && limited_vis)
 		verbose += (1+4);
-	Srand(T);	/* defined in run.c */
+	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;
@@ -454,6 +466,23 @@ main(int argc, char *argv[])
 
 	yyparse();
 	fclose(yyin);
+
+	if (ltl_claims)
+	{	Symbol *r;
+		fclose(fd_ltl);
+		if (!(yyin = fopen(ltl_claims, "r")))
+		{	fatal("cannot open %s", ltl_claims);
+		}
+		r = oFname;
+		oFname = Fname = lookup(ltl_claims);
+		lineno = 0;
+		yyparse();
+		fclose(yyin);
+		oFname = Fname = r;
+		if (0)
+		{	(void) unlink(ltl_claims);
+	}	}
+
 	loose_ends();
 
 	if (inlineonly)
@@ -471,6 +500,35 @@ main(int argc, char *argv[])
 	return 0;
 }
 
+void
+ltl_list(char *nm, char *fm)
+{
+	if (analyze || dumptab)	/* when generating pan.c only */
+	{	if (!ltl_claims)
+		{	ltl_claims = "_spin_nvr.tmp";
+			if ((fd_ltl = fopen(ltl_claims, MFLAGS)) == NULL)
+			{	fatal("cannot open tmp file %s", ltl_claims);
+			}
+			tl_out = fd_ltl;
+		}
+
+		add_ltl = (char **) emalloc(5 * sizeof(char *));
+		add_ltl[1] = "-c";
+		add_ltl[2] = nm;
+		add_ltl[3] = "-f";
+		add_ltl[4] = (char *) emalloc(strlen(fm)+4);
+		strcpy(add_ltl[4], "!(");
+		strcat(add_ltl[4], fm);
+		strcat(add_ltl[4], ")");
+		/* add_ltl[4] = fm; */
+
+		nr_errs += tl_main(4, add_ltl);
+
+		fflush(tl_out);
+		/* should read this file after the main file is read */
+	}
+}
+
 int
 yywrap(void)	/* dummy routine */
 {
@@ -481,13 +539,13 @@ void
 non_fatal(char *s1, char *s2)
 {	extern char yytext[];
 
-	printf("spin: line %3d %s, Error: ",
-		lineno, Fname?Fname->name:"nofilename");
+	printf("spin: %s:%d, Error: ",
+		oFname?oFname->name:"nofilename", lineno);
 	if (s2)
 		printf(s1, s2);
 	else
 		printf(s1);
-	if (yytext && strlen(yytext)>1)
+	if (strlen(yytext)>1)
 		printf(" near '%s'", yytext);
 	printf("\n");
 	nr_errs++;
@@ -497,24 +555,35 @@ void
 fatal(char *s1, char *s2)
 {
 	non_fatal(s1, s2);
+	(void) unlink("pan.b");
+	(void) unlink("pan.c");
+	(void) unlink("pan.h");
+	(void) unlink("pan.m");
+	(void) unlink("pan.t");
+	(void) unlink("pan.pre");
 	alldone(1);
 }
 
 char *
-emalloc(int n)
+emalloc(size_t n)
 {	char *tmp;
+	static unsigned long cnt = 0;
 
 	if (n == 0)
 		return NULL;	/* robert shelton 10/20/06 */
 
 	if (!(tmp = (char *) malloc(n)))
+	{	printf("spin: allocated %ld Gb, wanted %d bytes more\n",
+			cnt/(1024*1024*1024), (int) n);
 		fatal("not enough memory", (char *)0);
+	}
+	cnt += (unsigned long) n;
 	memset(tmp, 0, n);
 	return tmp;
 }
 
 void
-trapwonly(Lextok *n, char *unused)
+trapwonly(Lextok *n /* , char *unused */)
 {	extern int realread;
 	short i = (n->sym)?n->sym->type:0;
 
@@ -555,6 +624,7 @@ nn(Lextok *s, int t, Lextok *ll, Lextok *rl)
 {	Lextok *n = (Lextok *) emalloc(sizeof(Lextok));
 	static int warn_nn = 0;
 
+	n->uiid = is_inline();	/* record origin of the statement */
 	n->ntyp = (short) t;
 	if (s && s->fn)
 	{	n->ln = s->ln;
@@ -679,13 +749,13 @@ rem_var(Symbol *a, Lextok *b, Symbol *c, Lextok *ndx)
 #endif
 }
 
-static void
+void
 explain(int n)
 {	FILE *fd = stdout;
 	switch (n) {
 	default:	if (n > 0 && n < 256)
-				fprintf(fd, "'%c' = '", n);
-			fprintf(fd, "%d'", n);
+				fprintf(fd, "'%c' = ", n);
+			fprintf(fd, "%d", n);
 			break;
 	case '\b':	fprintf(fd, "\\b"); break;
 	case '\t':	fprintf(fd, "\\t"); break;
@@ -698,6 +768,16 @@ explain(int n)
 	case 'R':	fprintf(fd, "recv poll %s", Operator); break;
 	case '@':	fprintf(fd, "@"); break;
 	case '?':	fprintf(fd, "(x->y:z)"); break;
+#if 1
+	case NEXT:	fprintf(fd, "X"); break;
+	case ALWAYS:	fprintf(fd, "[]"); break;
+	case EVENTUALLY: fprintf(fd, "<>"); break;
+	case IMPLIES:	fprintf(fd, "->"); break;
+	case EQUIV:	fprintf(fd, "<->"); break;
+	case UNTIL:	fprintf(fd, "U"); break;
+	case WEAK_UNTIL: fprintf(fd, "W"); break;
+	case IN: fprintf(fd, "%sin", Keyword); break;
+#endif
 	case ACTIVE:	fprintf(fd, "%sactive",	Keyword); break;
 	case AND:	fprintf(fd, "%s&&",	Operator); break;
 	case ASGN:	fprintf(fd, "%s=",	Operator); break;
@@ -776,3 +856,5 @@ explain(int n)
 	case UNLESS:	fprintf(fd, "%sunless",	Keyword); break;
 	}
 }
+
+

+ 153 - 12
sys/src/cmd/spin/mesg.c

@@ -185,9 +185,11 @@ sa_snd(Queue *q, Lextok *n)	/* sorted asynchronous */
 	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 */
-		if (New <  Old) goto found;
+		if (New == Old)
+			continue;
+		if (New >  Old)
+			break;	/* inner loop */
+		goto found;	/* New < Old */
 	}
 found:
 	for (j = q->qlen-1; j >= i; j--)
@@ -383,7 +385,7 @@ s_snd(Queue *q, Lextok *n)
 	return 1;
 }
 
-void
+static void
 channm(Lextok *n)
 {	char lbuf[512];
 
@@ -394,7 +396,11 @@ channm(Lextok *n)
 	else if (n->sym->type == STRUCT)
 	{	Symbol *r = n->sym;
 		if (r->context)
-			r = findloc(r);
+		{	r = findloc(r);
+			if (!r)
+			{	strcat(Buf, "*?*");
+				return;
+		}	}
 		ini_struct(r);
 		printf("%s", r->name);
 		strcpy(lbuf, "");
@@ -462,7 +468,7 @@ typedef struct QH {
 	int	n;
 	struct	QH *nxt;
 } QH;
-QH *qh;
+static QH *qh;
 
 void
 qhide(int q)
@@ -483,7 +489,7 @@ qishidden(int q)
 
 static void
 sr_talk(Lextok *n, int v, char *tr, char *a, int j, Queue *q)
-{	char s[64];
+{	char s[128];
 
 	if (qishidden(eval(n->lft)))
 		return;
@@ -510,9 +516,20 @@ sr_talk(Lextok *n, int v, char *tr, char *a, int j, Queue *q)
 	}
 
 	if (j == 0)
-	{	whoruns(1);
-		printf("line %3d %s %s",
-			n->ln, n->fn->name, s);
+	{	char snm[128];
+		whoruns(1);
+		{	char *ptr = n->fn->name;
+			char *qtr = snm;
+			while (*ptr != '\0')
+			{	if (*ptr != '\"')
+				{	*qtr++ = *ptr;
+				}
+				ptr++;
+			}
+			*qtr = '\0';
+			printf("%s:%d %s",
+				snm, n->ln, s);
+		}
 	} else
 		printf(",");
 	sr_mesg(stdout, v, q->fld_width[j] == MTYPE);
@@ -572,10 +589,15 @@ doq(Symbol *s, int n, RunList *r)
 			continue;
 		if (q->nslots == 0)
 			continue; /* rv q always empty */
+#if 0
+		if (q->qlen == 0)	/* new 7/10 -- dont show if queue is empty */
+		{	continue;
+		}
+#endif
 		printf("\t\tqueue %d (", q->qid);
 		if (r)
 		printf("%s(%d):", r->n->name, r->pid - Have_claim);
-		if (s->nel != 1)
+		if (s->nel > 1 || s->isarray)
 		  printf("%s[%d]): ", s->name, n);
 		else
 		  printf("%s): ", s->name);
@@ -609,7 +631,10 @@ nochan_manip(Lextok *p, Lextok *n, int d)
 		}	
 	}
 
-	if (!n || n->ntyp == LEN || n->ntyp == RUN)
+	/* ok on the rhs of an assignment: */
+	if (!n || n->ntyp == LEN || n->ntyp == RUN
+	||  n->ntyp == FULL  || n->ntyp == NFULL
+	||  n->ntyp == EMPTY || n->ntyp == NEMPTY)
 		return;
 
 	if (n->sym && n->sym->type == CHAN)
@@ -627,6 +652,120 @@ nochan_manip(Lextok *p, Lextok *n, int d)
 	nochan_manip(p, n->rgt, 1);
 }
 
+typedef struct BaseName {
+	char *str;
+	int cnt;
+	struct BaseName *nxt;
+} BaseName;
+BaseName *bsn;
+
+void
+newbasename(char *s)
+{	BaseName *b;
+
+/*	printf("+++++++++%s\n", s);	*/
+	for (b = bsn; b; b = b->nxt)
+		if (strcmp(b->str, s) == 0)
+		{	b->cnt++;
+			return;
+		}
+	b = (BaseName *) emalloc(sizeof(BaseName));
+	b->str = emalloc(strlen(s)+1);
+	b->cnt = 1;
+	strcpy(b->str, s);
+	b->nxt = bsn;
+	bsn = b;
+}
+
+void
+delbasename(char *s)
+{	BaseName *b, *prv = (BaseName *) 0;
+
+	for (b = bsn; b; prv = b, b = b->nxt)
+	{	if (strcmp(b->str, s) == 0)
+		{	b->cnt--;
+			if (b->cnt == 0)
+			{	if (prv)
+				{	prv->nxt = b->nxt;
+				} else
+				{	bsn = b->nxt;
+			}	}
+/*	printf("---------%s\n", s);	*/
+			break;
+	}	}
+}
+
+void
+checkindex(char *s, char *t)
+{	BaseName *b;
+
+/*	printf("xxx Check %s (%s)\n", s, t);	*/
+	for (b = bsn; b; b = b->nxt)
+	{
+/*		printf("	%s\n", b->str);	*/
+		if (strcmp(b->str, s) == 0)
+		{	non_fatal("do not index an array with itself (%s)", t);
+			break;
+	}	}
+}
+
+void
+scan_tree(Lextok *t, char *mn, char *mx)
+{	char sv[512];
+	char tmp[32];
+	int oln = lineno;
+
+	if (!t) return;
+
+	lineno = t->ln;
+
+	if (t->ntyp == NAME)
+	{	strcat(mn, t->sym->name);
+		strcat(mx, t->sym->name);
+		if (t->lft)		/* array index */
+		{	strcat(mn, "[]");
+			newbasename(mn);
+				strcpy(sv, mn);		/* save */
+				strcpy(mn, "");		/* clear */
+				strcat(mx, "[");
+				scan_tree(t->lft, mn, mx);	/* index */
+				strcat(mx, "]");
+				checkindex(mn, mx);	/* match against basenames */
+				strcpy(mn, sv);		/* restore */
+			delbasename(mn);
+		}
+		if (t->rgt)	/* structure element */
+		{	scan_tree(t->rgt, mn, mx);
+		}
+	} else if (t->ntyp == CONST)
+	{	strcat(mn, "1"); /* really: t->val */
+		sprintf(tmp, "%d", t->val);
+		strcat(mx, tmp);
+	} else if (t->ntyp == '.')
+	{	strcat(mn, ".");
+		strcat(mx, ".");
+		scan_tree(t->lft, mn, mx);
+	} else
+	{	strcat(mn, "??");
+		strcat(mx, "??");
+	}
+	lineno = oln;
+}
+
+void
+no_nested_array_refs(Lextok *n)	/* a [ a[1] ] with a[1] = 1, causes trouble in pan.b */
+{	char mn[512];
+	char mx[512];
+
+/*	printf("==================================ZAP\n");	*/
+	bsn = (BaseName *) 0;	/* start new list */
+	strcpy(mn, "");
+	strcpy(mx, "");
+
+	scan_tree(n, mn, mx);
+/*	printf("==> %s\n", mn);	*/
+}
+
 void
 no_internals(Lextok *n)
 {	char *sp;
@@ -641,4 +780,6 @@ no_internals(Lextok *n)
 	||  (strlen(sp) == strlen("_p") && strcmp(sp, "_p") == 0))
 	{	fatal("attempt to assign value to system variable %s", sp);
 	}
+
+	no_nested_array_refs(n);
 }

+ 2 - 1
sys/src/cmd/spin/mkfile

@@ -14,6 +14,7 @@ SPIN_OS=\
 	pangen4.$O\
 	pangen5.$O\
 	pangen6.$O\
+	pangen7.$O\
 	pc_zpp.$O\
 	ps_msc.$O\
 	reprosrc.$O\
@@ -53,4 +54,4 @@ $TL_OS:		tl.h
 
 main.$O pangen2.$O ps_msc.$O:	version.h
 pangen1.$O:			pangen1.h pangen3.h
-pangen2.$O:			pangen2.h pangen4.h pangen5.h
+pangen2.$O:			pangen2.h pangen4.h pangen5.h pangen6.h

+ 363 - 111
sys/src/cmd/spin/pangen1.c

@@ -8,11 +8,14 @@
 /* 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"
+#include <assert.h>
 
 extern FILE	*tc, *th, *tt;
 extern Label	*labtab;
@@ -20,9 +23,10 @@ extern Ordered	*all_names;
 extern ProcList	*rdy;
 extern Queue	*qtab;
 extern Symbol	*Fname;
-extern int	lineno, verbose, Pid, separate;
+extern int	lineno, verbose, Pid, separate, old_scope_rules, nclaims;
 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;
@@ -36,9 +40,10 @@ 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	put_ptype(char *, int, int, int, enum btypes);
 static void	tc_predef_np(void);
 static void	put_pinit(ProcList *);
+static void	multi_init(void);
        void	walk_struct(FILE *, int, char *, Symbol *, char *, char *, char *);
 
 static void
@@ -48,6 +53,21 @@ reverse_names(ProcList *p)
 	reverse_names(p->nxt);
 	fprintf(th, "   \"%s\",\n", p->n->name);
 }
+static void
+reverse_types(ProcList *p)
+{
+	if (!p) return;
+	reverse_types(p->nxt);
+	fprintf(th, "   %d,	/* %s */\n", p->b, p->n->name);
+}
+
+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;
+}
 
 void
 genheader(void)
@@ -57,9 +77,19 @@ genheader(void)
 	{	putunames(th);
 		goto here;
 	}
-
+	/* 5.2.3: gcc 3 no longer seems to compute sizeof at compile time */
+	fprintf(th, "#define WS		%d /* word size in bytes */\n", (int) sizeof(void *));
 	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);
 
@@ -67,6 +97,11 @@ genheader(void)
 	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_ */
+	if (nclaims > 1)
+	{	fprintf(tc, "\n#ifndef NOCLAIM\n");
+		fprintf(tc, "	, (short) Air%d", i+1);	/* Multi */
+		fprintf(tc, "\n#endif\n\t");
+	}
 	fprintf(tc, " };\n");
 
 	fprintf(th, "char *procname[] = {\n");
@@ -74,18 +109,86 @@ genheader(void)
 	fprintf(th, "   \":np_:\",\n");
 	fprintf(th, "};\n\n");
 
+	fprintf(th, "enum btypes { NONE=%d, N_CLAIM=%d,", NONE, N_CLAIM);
+	fprintf(th, " I_PROC=%d, A_PROC=%d,", I_PROC, A_PROC);
+	fprintf(th, " P_PROC=%d, E_TRACE=%d, N_TRACE=%d };\n",
+		P_PROC, E_TRACE, N_TRACE);
+	fprintf(th, "int Btypes[] = {\n");
+		reverse_types(rdy);
+	fprintf(th, "   0	/* :np_: */\n");
+	fprintf(th, "};\n\n");
+
 here:
 	for (p = rdy; p; p = p->nxt)
-		put_ptype(p->n->name, p->tn, mst, nrRdy+1);
+		put_ptype(p->n->name, p->tn, mst, nrRdy+1, p->b);
 		/* +1 for np_ */
-	put_ptype("np_", nrRdy, mst, nrRdy+1);
+	put_ptype("np_", nrRdy, mst, nrRdy+1, 0);
+
+	if (nclaims > 1)
+	{	/* this is the structure that goes into the state-vector
+		 * instead of the actual never claims
+		 * this assumes that the claims do not have any local variables
+		 * this claim records the types and states of all subclaims in an array
+		 * NB: not sure if we need the first 3 fields in this structure
+		 *     it's here for now to avoid breaking some possible dependence
+		 * in the calculations above, we were already taking into account
+		 * that there is one never-claim, which will now be this one
+		 */
+
+		i = blog(mst);
+		fprintf(th, "\n");
+		fprintf(th, "#ifndef NOCLAIM\n");
+		fprintf(th, "	#undef VERI\n");
+		fprintf(th, "	#define VERI	%d\n", nrRdy+1);
+		fprintf(th, "	#define Pclaim	P%d\n\n", nrRdy+1);
+		fprintf(th, "typedef struct P%d {\n", nrRdy+1);
+		fprintf(th, "	unsigned _pid : 8; /* always zero */\n");
+		fprintf(th, "	unsigned _t   : %d; /* active-claim type  */\n",
+			blog(nrRdy+1));
+		fprintf(th, "	unsigned _p   : %d; /* active-claim state */\n",
+			i);
+		fprintf(th, "	unsigned _n   : %d; /* active-claim index */\n",
+			blog(nclaims));
+		if (i <= 255)	/* in stdint.h = UCHAR_MAX from limits.h */
+		{	fprintf(th, "	uchar c_cur[NCLAIMS]; /* claim-states */\n");
+		} else if (i <= 65535)	/* really USHRT_MAX from limits.h */
+		{	fprintf(th, "	ushort c_cur[NCLAIMS]; /* claim-states */\n");
+		} else	/* the most unlikely case */
+		{	fprintf(th, "	uint c_cur[NCLAIMS]; /* claim-states */\n");
+		}
+		fprintf(th, "} P%d;\n", nrRdy+1);
+		fprintf(th, "uchar spin_c_typ[NCLAIMS]; /* claim-types */\n");
+		fprintf(th, "	#define Air%d	(0)\n\n", nrRdy+1);
+		fprintf(th, "#endif\n");
+		/*
+		 * find special states as:
+		 *	stopstate [ claimnr ][ curstate ] == 1
+		 *	accpstate [ claimnr ][ curstate ]
+		 *	progstate [ claimnr ][ curstate ]
+		 *	reached   [ claimnr ][ curstate ]
+		 *	visstate  [ claimnr ][ curstate ]
+		 *	loopstate [ claimnr ][ curstate ]
+		 *	mapstate  [ claimnr ][ curstate ]
+		 */
+	} else
+	{	fprintf(th, "\n#define Pclaim	P0\n");
+		fprintf(th, "#ifndef NCLAIMS\n");
+		fprintf(th, "	#define NCLAIMS 1\n");
+		fprintf(th, "#endif\n");
+		fprintf(th, "uchar spin_c_typ[NCLAIMS]; /* claim-types */\n");
+	}
 
 	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);
 	}
@@ -96,7 +199,13 @@ here:
 
 	hastrack = c_add_sv(th);
 
+	fprintf(th, "#ifdef TRIX\n");
+	fprintf(th, "	/* room for 512 proc+chan ptrs, + safety margin */\n");
+	fprintf(th, "	char *_ids_[MAXPROC+MAXQ+4];\n");
+	fprintf(th, "#else\n");
 	fprintf(th, "	uchar sv[VECTORSZ];\n");
+	fprintf(th, "#endif\n");
+
 	fprintf(th, "} State");
 #ifdef SOLARIS
 	fprintf(th,"\n#ifdef GCC\n");
@@ -105,6 +214,23 @@ here:
 #endif
 	fprintf(th, ";\n\n");
 
+	fprintf(th, "#ifdef TRIX\n");
+	fprintf(th, "typedef struct TRIX_v6 {\n");
+	fprintf(th, "	uchar *body; /* aligned */\n");
+	fprintf(th, "#ifndef BFS\n");
+	fprintf(th, "	short modified;\n");
+	fprintf(th, "#endif\n");
+	fprintf(th, "	short psize;\n");
+	fprintf(th, "	short parent_pid;\n");
+	fprintf(th, "	struct TRIX_v6 *nxt;\n");
+	fprintf(th, "} TRIX_v6;\n");
+	fprintf(th, "TRIX_v6 *freebodies;\n");
+	fprintf(th, "TRIX_v6 *processes[MAXPROC+1];\n");
+	fprintf(th, "TRIX_v6 *channels[MAXQ+1]; \n");
+	fprintf(th, "long _p_count[MAXPROC];\n");
+	fprintf(th, "long _c_count[MAXPROC];\n");
+	fprintf(th, "#endif\n\n");
+
 	fprintf(th, "#define HAS_TRACK	%d\n", hastrack);
 
 	if (separate != 2)
@@ -114,16 +240,52 @@ here:
 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++)
+	int i = 0, j;
+
+	if (separate == 2) goto shortcut;
+
+	fprintf(tc, "\n#ifdef TRIX\n");
+	fprintf(tc, "int what_p_size(int);\n");
+	fprintf(tc, "int what_q_size(int);\n\n");
+	/* the number of processes just changed by 1 (up or down) */
+	/* this means that the channel indices move up or down by one slot */
+	/* not all new channels may have a valid index yet, but we move */
+	/* all of them anyway, as if they existed */
+	fprintf(tc, "void\nre_mark_all(int whichway)\n");
+	fprintf(tc, "{	int j;\n");
+	fprintf(tc, "	#ifdef V_TRIX\n");
+	fprintf(tc, "		printf(\"%%d: re_mark_all channels %%d\\n\", depth, whichway);\n");
+	fprintf(tc, "	#endif\n");
+	fprintf(tc, "	#ifndef BFS\n");
+	fprintf(tc, "	for (j = 0; j < now._nr_qs; j++)\n");
+	fprintf(tc, "		channels[j]->modified = 1; /* channel index moved */\n");
+	fprintf(tc, "	#endif\n");
+	fprintf(tc, "	#ifndef TRIX_ORIG\n");
+	fprintf(tc, "	if (whichway > 0)\n");
+	fprintf(tc, "	{	for (j = now._nr_pr + now._nr_qs - 1; j >= now._nr_pr; j--)\n");
+	fprintf(tc, "			now._ids_[j] = now._ids_[j-1];\n");
+	fprintf(tc, "	} else\n");
+	fprintf(tc, "	{	for (j = now._nr_pr; j < now._nr_pr + now._nr_qs; j++)\n");
+	fprintf(tc, "			now._ids_[j] = now._ids_[j+1];\n");
+	fprintf(tc, "	}\n");
+	fprintf(tc, "	#endif\n");
+	fprintf(tc, "}\n");
+
+	fprintf(tc, "#endif\n\n");
+
+	fprintf(tc, "int\naddproc(int calling_pid, 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_ */
+
+	if (nclaims > 1)
+	{	fprintf(tc, "#ifndef NOCLAIM\n");
+		ntimes(tc, nrRdy+1, nrRdy+2, R5);
+		fprintf(tc, "#endif\n");
+	}
+
 	ntimes(tc, 0, 1, Addp1);
 
 	if (has_provided)
@@ -132,6 +294,9 @@ genaddproc(void)
 		fprintf(tt, "{\n\tswitch(ot) {\n");
 	}
 shortcut:
+	if (nclaims > 1)
+	{	multi_init();
+	}
 	tc_predef_np();
 	for (p = rdy; p; p = p->nxt)
 	{	Pid = p->tn;
@@ -167,13 +332,18 @@ genother(void)
 
 	switch (separate) {
 	case 2:
-		if (claimnr >= 0)
-		ntimes(tc, claimnr, claimnr+1, R0); /* claim only */
+		if (nclaims > 0)
+		{	for (p = rdy; p; p = p->nxt)
+			{	if (p->b == N_CLAIM)
+				{	ntimes(tc, p->tn, p->tn+1, R0); /* claims only */
+		}	}	}
 		break;
 	case 1:
 		ntimes(tc,     0,    1, Code0);
-		ntimes(tc, 0, claimnr, R0);	/* all except claim */
-		ntimes(tc, claimnr+1, nrRdy, R0);
+		for (p = rdy; p; p = p->nxt)
+		{	if (p->b != N_CLAIM)
+			{	ntimes(tc, p->tn, p->tn+1, R0); /* all except claims */
+		}	}
 		break;
 	case 0:
 		ntimes(tc,     0,    1, Code0);
@@ -186,44 +356,53 @@ genother(void)
 
 	switch (separate) {
 	case 2:
-		if (claimnr >= 0)
-		ntimes(tc, claimnr, claimnr+1, R0a); /* claim only */
+		if (nclaims > 0)
+		{	for (p = rdy; p; p = p->nxt)
+			{	if (p->b == N_CLAIM)
+				{	ntimes(tc, p->tn, p->tn+1, R0a); /* claims only */
+		}	}	}
 		return;
 	case 1:
-		ntimes(tc, 0, claimnr, R0a);	/* all except claim */
-		ntimes(tc, claimnr+1, nrRdy, R0a);
+		for (p = rdy; p; p = p->nxt)
+		{	if (p->b != N_CLAIM)
+			{	ntimes(tc, p->tn, p->tn+1, R0a); /* all except claims */
+		}	}
 		fprintf(tc, "	if (state_tables)\n");
-		fprintf(tc, "		ini_claim(%d, 0);\n", claimnr);
+		fprintf(tc, "		ini_claim(%d, 0);\n", claimnr);	/* the default claim */
+		if (acceptors == 0)
+		{	acceptors = 1;	/* assume at least 1 acceptstate */
+		}
 		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, Code2a);	/* dfs, bfs */
+	ntimes(tc, 0,     1, Code2c);	/* multicore */
+	ntimes(tc, 0,     1, Code2d);
+
+	fprintf(tc, "void\ndo_reach(void)\n{\n");
 	ntimes(tc, 0,     nrRdy, R4);
 	fprintf(tc, "}\n\n");
 
-	fprintf(tc, "void\n");
-	fprintf(tc, "iniglobals(void)\n{\n");
+	fprintf(tc, "void\niniglobals(int calling_pid)\n{\n");
+	ntimes(tc, 1, u_sync+u_async+1, R3); /* because nqs is still 0 */
+	fprintf(tc, "\tMaxbody = max(Maxbody, sizeof(State)-VECTORSZ);\n");
+	fprintf(tc, "\tif ((Maxbody %% WS) != 0)\n");
+	fprintf(tc, "\t	Maxbody += WS - (Maxbody %% WS);\n\n");
+
+	/* after the value of Maxbody has settled */
 	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");
+	fprintf(tc, "}\n\n");
 }
 
 void
@@ -248,13 +427,13 @@ end_labs(Symbol *s, int i)
 	Label *l;
 	int j; char foo[128];
 
-	if ((i == claimnr && separate == 1)
-	||  (i != claimnr && separate == 2))
+	if ((pid_is_claim(i) && separate == 1)
+	|| (!pid_is_claim(i) && 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
+	{	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);
@@ -272,8 +451,7 @@ end_labs(Symbol *s, int i)
 				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
@@ -305,7 +483,7 @@ prehint(Symbol *s)
 
 	n = (s->context != ZS)?s->context->ini:s->ini;
 	if (n)
-	printf("line %3d %s, ", n->ln, n->fn->name);
+	printf("line %s:%d, ", n->fn->name, n->ln);
 }
 
 void
@@ -338,7 +516,7 @@ checktype(Symbol *sp, char *s)
 	{	if (!(verbose&32)) return;
 		sputtype(buf, sp->type);
 		i = (int) strlen(buf);
-		while (buf[--i] == ' ') buf[i] = '\0';
+		while (i > 0 && buf[--i] == ' ') buf[i] = '\0';
 		prehint(sp);
 		if (sp->context)
 			printf("proctype %s:", s);
@@ -361,12 +539,12 @@ checktype(Symbol *sp, char *s)
 	}
 }
 
-int
-dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s)
+static int
+dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s, enum btypes b)
 {	int h, j, k=0; extern int nr_errs;
 	Ordered *walk;
 	Symbol *sp;
-	char buf[64], buf2[128], buf3[128];
+	char buf[128], buf2[128], buf3[128];
 
 	if (dowhat == INIV)
 	{	/* initialize in order of declaration */
@@ -390,7 +568,8 @@ dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s)
 			if (sp->context
 			&& !sp->owner
 			&&  sp->type == Types[j]
-			&&  ((h == 0 && sp->nel == 1) || (h == 1 && sp->nel > 1))
+			&&  ((h == 0 && (sp->nel == 1 && sp->isarray == 0))
+			||   (h == 1 && (sp->nel  > 1 || sp->isarray == 1)))
 			&&  strcmp(s, sp->context->name) == 0)
 			{	switch (dowhat) {
 				case LOGV:
@@ -409,7 +588,7 @@ dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s)
 					k++;
 					break;
 				}
-				if (strcmp(s, ":never:") == 0)
+				if (b == N_CLAIM)
 				{	printf("error: %s defines local %s\n",
 						s, sp->name);
 					nr_errs++;
@@ -426,7 +605,7 @@ c_chandump(FILE *fd)
 
 	if (!qtab)
 	{	fprintf(fd, "void\nc_chandump(int unused) ");
-		fprintf(fd, "{ unused = unused++; /* avoid complaints */ }\n");
+		fprintf(fd, "{ unused++; /* avoid complaints */ }\n");
 		return;
 	}
 
@@ -465,9 +644,19 @@ c_chandump(FILE *fd)
 
 void
 c_var(FILE *fd, char *pref, Symbol *sp)
-{	char buf[256];
+{	char *ptr, buf[256];
 	int i;
 
+	if (!sp)
+	{	fatal("cannot happen - c_var", 0);
+	}
+
+	ptr = sp?sp->name:"";
+	if (!old_scope_rules)
+	{	while (*ptr == '_' || isdigit((int)*ptr))
+		{	ptr++;
+	}	}
+
 	switch (sp->type) {
 	case STRUCT:
 		/* c_struct(fd, pref, sp); */
@@ -480,30 +669,29 @@ c_var(FILE *fd, char *pref, Symbol *sp)
 	case SHORT: case INT:
 	case UNSIGNED:
 		sputtype(buf, sp->type);
-		if (sp->nel == 1)
+		if (sp->nel == 1 && sp->isarray == 0)
 		{	fprintf(fd, "\tprintf(\"\t%s %s:\t%%d\\n\", %s%s);\n",
-				buf, sp->name, pref, sp->name);
+				buf, ptr, 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);
+						buf, ptr, 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);
+		if (sp->nel == 1 && sp->isarray == 0)
+		{  fprintf(fd, "\tprintf(\"\tchan %s (=%%d):\tlen %%d:\\t\", ", ptr);
 		   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);
+			ptr, 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",
@@ -518,9 +706,7 @@ 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)
+	if (p->b != N_CLAIM && p->b != E_TRACE && p->b != N_TRACE)
 	for (walk = all_names; walk; walk = walk->next)
 	{	sp = walk->entry;
 		if (!sp->context
@@ -541,9 +727,7 @@ c_splurge(FILE *fd, ProcList *p)
 	Symbol *sp;
 	char pref[64];
 
-	if (strcmp(p->n->name, ":never:") != 0
-	&&  strcmp(p->n->name, ":trace:") != 0
-	&&  strcmp(p->n->name, ":notrace:") != 0)
+	if (p->b != N_CLAIM && p->b != E_TRACE && p->b != N_TRACE)
 	for (walk = all_names; walk; walk = walk->next)
 	{	sp = walk->entry;
 		if (!sp->context
@@ -583,8 +767,6 @@ c_wrapper(FILE *fd)	/* allow pan.c to print out global sv entries */
 	fprintf(fd, "	switch(tp) {\n");
 	for (p = rdy; p; p = p->nxt)
 	{	fprintf(fd, "	case %d:\n", p->tn);
-		fprintf(fd, "	\tprintf(\"local vars proc %%d (%s):\\n\", pid);\n",
-			p->n->name);
 		if (c_splurge_any(p))
 		{	fprintf(fd, "	\tprintf(\"local vars proc %%d (%s):\\n\", pid);\n",
 				p->n->name);
@@ -633,8 +815,9 @@ doglobal(char *pre, int dowhat)
 				checktype(sp, (char *) 0);
 				cnt++; /* fall through */
 			case PUTV:
-				do_var(tc, dowhat, (sp->hidden&1)?"":"now.", sp,
-				"", " = ", ";\n");
+				do_var(tc, dowhat,
+					(sp->hidden&1)?"":"now.", sp,
+					"", " = ", ";\n");
 				break;
 	}	}	}
 	return cnt;
@@ -665,15 +848,25 @@ void
 do_var(FILE *ofd, int dowhat, char *s, Symbol *sp,
 	char *pre, char *sep, char *ter)
 {	int i;
+	char *ptr = sp?sp->name:"";
+
+	if (!sp)
+	{	fatal("cannot happen - do_var", 0);
+	}
 
 	switch(dowhat) {
 	case PUTV:
-
 		if (sp->hidden&1) break;
 
 		typ2c(sp);
 		break;
+
 	case LOGV:
+		if (!old_scope_rules)
+		{	while (*ptr == '_' || isdigit((int)*ptr))
+			{	ptr++;
+		}	}
+		/* fall thru */
 	case INIV:
 		if (sp->type == STRUCT)
 		{	/* struct may contain a chan */
@@ -682,13 +875,16 @@ do_var(FILE *ofd, int dowhat, char *s, Symbol *sp,
 		}
 		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)
+		if (sp->nel == 1 && sp->isarray == 0)
+		{	if (dowhat == LOGV)
+			{	fprintf(ofd, "\t\t%s%s%s%s",
+					pre, s, ptr, sep);
 				fprintf(ofd, "%s%s", s, sp->name);
-			else
+			} else
+			{	fprintf(ofd, "\t\t%s%s%s%s",
+					pre, s, sp->name, sep);
 				do_init(ofd, sp);
+			}
 			fprintf(ofd, "%s", ter);
 		} else
 		{	if (sp->ini && sp->ini->ntyp == CHAN)
@@ -722,42 +918,31 @@ do_var(FILE *ofd, int dowhat, char *s, Symbol *sp,
 
 static void
 do_init(FILE *ofd, Symbol *sp)
-{	int i; extern Queue *ltab[];
+{	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);
+		{	fprintf(ofd, "addqueue(calling_pid, %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;
+	{	putstmnt(ofd, sp->ini, 0);
+	}
 }
 
 static void
-put_ptype(char *s, int i, int m0, int m1)
+put_ptype(char *s, int i, int m0, int m1, enum btypes b)
 {	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);
+	if (b == I_PROC)
+	{	fprintf(th, "#define Pinit	((P%d *)this)\n", i);
+	} else if (b == P_PROC || b == A_PROC)
+	{	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");
@@ -765,14 +950,14 @@ put_ptype(char *s, int i, int m0, int 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 */
+	k = dolocal(tc, "", PUTV, i, s, b);	/* 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
+	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);
@@ -813,6 +998,7 @@ tc_predef_np(void)
 	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);
@@ -830,6 +1016,48 @@ tc_predef_np(void)
 	fprintf(tc, "\t\tbreak;\n");
 }
 
+static void
+multi_init(void)
+{	ProcList *p;
+	Element	*e;
+	int i = nrRdy+1;
+	int ini, j;
+	int nrc = nclaims;
+
+	fprintf(tc, "#ifndef NOCLAIM\n");
+	fprintf(tc, "\tcase %d:	/* claim select */\n", i);
+	for (p = rdy, j = 0; p; p = p->nxt, j++)
+	{	if (p->b == N_CLAIM)
+		{	e = p->s->frst;
+			ini = huntele(e, e->status, -1)->seqno;
+
+			fprintf(tc, "\t\tspin_c_typ[%d] = %d; /* %s */\n",
+				j, p->tn, p->n->name);
+			fprintf(tc, "\t\t((P%d *)pptr(h))->c_cur[%d] = %d;\n",
+				i, j, ini);
+			fprintf(tc, "\t\treached%d[%d]=1;\n", p->tn, ini);
+
+			/* the default initial claim is first one in model */
+			if (--nrc == 0)	
+			{ fprintf(tc, "\t\t((P%d *)pptr(h))->_t = %d;\n", i, p->tn);
+			  fprintf(tc, "\t\t((P%d *)pptr(h))->_p = %d;\n", i, ini);
+			  fprintf(tc, "\t\t((P%d *)pptr(h))->_n = %d; /* %s */\n",
+				i, j, p->n->name);
+			  fprintf(tc, "\t\tsrc_claim = src_ln%d;\n", p->tn);
+			  fprintf(tc, "#ifndef BFS\n");
+			  fprintf(tc, "\t\tif (whichclaim == -1 && claimname == NULL)\n");
+			  fprintf(tc, "\t\t\tprintf(\"0: Claim %s (%d), from state %d\\n\");\n",
+				p->n->name, p->tn, ini);
+			  fprintf(tc, "#endif\n");
+			}
+	}	}
+	fprintf(tc, "\t\tif (whichclaim != -1)\n");
+	fprintf(tc, "\t\t{	select_claim(whichclaim);\n");
+	fprintf(tc, "\t\t}\n");
+	fprintf(tc, "\t\tbreak;\n\n");
+	fprintf(tc, "#endif\n");
+}
+
 static void
 put_pinit(ProcList *P)
 {	Lextok	*fp, *fpt, *t;
@@ -839,29 +1067,30 @@ put_pinit(ProcList *P)
 	int	 i = P->tn;
 	int	ini, j, k;
 
-	if (i == claimnr
+	if (pid_is_claim(i)
 	&&  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
+	if (!pid_is_claim(i)
 	&&  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);
+	fprintf(tc, "\t\t((P%d *)pptr(h))->_p = %d;\n", i, ini);
+	fprintf(tc, "\t\treached%d[%d]=1;\n", i, ini);
+	if (P->b == N_CLAIM)
+	{	fprintf(tc, "\t\tsrc_claim = src_ln%d;\n", i);
+	}
 
 	if (has_provided)
 	{	fprintf(tt, "\tcase %d: /* %s */\n\t\t", i, s->name);
@@ -879,7 +1108,7 @@ put_pinit(ProcList *P)
 	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)
+		if (t->sym->nel > 1 || t->sym->isarray)
 		{	lineno = t->ln;
 			Fname  = t->fn;
 			fatal("array in parameter list, %s",
@@ -898,10 +1127,10 @@ put_pinit(ProcList *P)
 		fprintf(tc, " = par%d;\n", j);
 	}
 	fprintf(tc, "\t\t/* locals: */\n");
-	k = dolocal(tc, "", INIV, i, s->name);
+	k = dolocal(tc, "", INIV, i, s->name, P->b);
 	if (k > 0)
 	{	fprintf(tc, "#ifdef VAR_RANGES\n");
-		(void) dolocal(tc, "logval(\"", LOGV, i, s->name);
+		(void) dolocal(tc, "logval(\"", LOGV, i, s->name, P->b);
 		fprintf(tc, "#endif\n");
 	}
 
@@ -939,7 +1168,7 @@ huntele(Element *f, int o, int stopat)
 	int cnt=0; /* a precaution against loops */
 
 	if (e)
-	for (cnt = 0; cnt < 200 && e->n; cnt++)
+	for ( ; cnt < 200 && e->n; cnt++)
 	{
 		if (e->seqno == stopat)
 			break;
@@ -992,7 +1221,7 @@ typ2c(Symbol *sp)
 		nBits += sp->nbits;
 		break;
 	case BIT:
-		if (sp->nel == 1 && !(sp->hidden&1))
+		if (sp->nel == 1 && sp->isarray == 0 && !(sp->hidden&1))
 		{	fprintf(th, "\tunsigned %s : 1", sp->name);
 			LstSet = sp; 
 			nBits++;
@@ -1031,7 +1260,7 @@ typ2c(Symbol *sp)
 		fatal("variable %s undeclared", sp->name);
 	}
 
-	if (sp->nel != 1)
+	if (sp->nel > 1 || sp->isarray)
 		fprintf(th, "[%d]", sp->nel);
 	fprintf(th, ";\n");
 }
@@ -1124,6 +1353,22 @@ genaddqueue(void)
 
 	ntimes(tc, 0, 1, Addq1);
 
+	fprintf(tc, "#ifdef TRIX\n");
+	fprintf(tc, "int\nwhat_p_size(int t)\n{\tint j;\n");
+	fprintf(tc, "	switch (t) {\n");
+	ntimes(tc, 0, nrRdy+1, R5); /* +1 for np_ */
+	fprintf(tc, "	default: Uerror(\"bad proctype\");\n");
+	fprintf(tc, "	}\n	return j;\n}\n\n");
+
+	fprintf(tc, "int\nwhat_q_size(int t)\n{\tint j;\n");
+	fprintf(tc, "	switch (t) {\n");
+	for (j = 0; j < nqs+1; j++)
+	{	fprintf(tc, "	case %d: j = sizeof(Q%d); break;\n", j, j);
+	}
+	fprintf(tc, "	default: Uerror(\"bad qtype\");\n");
+	fprintf(tc, "	}\n	return j;\n}\n");
+	fprintf(tc, "#endif\n\n");
+
 	if (has_random)
 	{	fprintf(th, "int Q_has(int");
 		for (j = 0; j < Mpars; j++)
@@ -1159,7 +1404,7 @@ genaddqueue(void)
 	fprintf(tc, "void\nqsend(int into, int sorted");
 	for (j = 0; j < Mpars; j++)
 		fprintf(tc, ", int fld%d", j);
-	fprintf(tc, ")\n");
+	fprintf(tc, ", int args_given)\n");
 	ntimes(tc, 0, 1, Addq11);
 
 	for (q = qtab; q; q = q->nxt)
@@ -1202,6 +1447,12 @@ genaddqueue(void)
 		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);
@@ -1266,9 +1517,10 @@ genaddqueue(void)
 	fprintf(th, "void qsend(int, int");
 	for (j = 0; j < Mpars; j++)
 		fprintf(th, ", int");
-	fprintf(th, ");\n");
+	fprintf(th, ", int);\n");
 
-	fprintf(th, "#define Addproc(x)	addproc(x");
+	fprintf(th, "#define Addproc(x)	addproc(256, x");
+	/* 256 is param outside the range of valid pids */
 	for (j = 0; j < Npars; j++)
 		fprintf(th, ", 0");
 	fprintf(th, ")\n");

File diff suppressed because it is too large
+ 513 - 112
sys/src/cmd/spin/pangen1.h


File diff suppressed because it is too large
+ 318 - 167
sys/src/cmd/spin/pangen2.c


+ 391 - 96
sys/src/cmd/spin/pangen2.h

@@ -1,6 +1,6 @@
 /***** spin: pangen2.h *****/
 
-/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories.     */
+/* 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  */
@@ -8,20 +8,7 @@
 /* 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 *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,
-};
+/* (c) 2007: small additions for V5.0 to support multi-core verifications */
 
 static char *Pre0[] = {
 "#ifdef SC",
@@ -38,11 +25,12 @@ static char *Pre0[] = {
 		"#include <time.h>",
 	"#else",
 		"#include <unistd.h>",
-		"#include <sys/times.h>",	/* new 4.3.0 */
+		"#include <sys/times.h>",
 	"#endif",
 	"#include <sys/types.h>",	/* defines off_t */
 	"#include <sys/stat.h>",
 	"#include <fcntl.h>",
+
 	"#define Offsetof(X, Y)	((unsigned long)(&(((X *)0)->Y)))",
 	"#ifndef max",
 	"#define max(a,b) (((a)<(b)) ? (b) : (a))",
@@ -55,10 +43,13 @@ static char *Pre0[] = {
 
 static char *Preamble[] = {
 
+	"#ifdef RANDOMIZE",
+	"	#define T_RAND  RANDOMIZE",
+	"#endif",
 	"#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]--",
+	"	#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)",
@@ -68,13 +59,13 @@ static char *Preamble[] = {
 		 * 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;",
+	"	#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",
+	"	#define V_A	0",
+	"	#define A_V	0",
+	"	#define S_A	0",
 	"#endif",
 
 "#ifdef MA",
@@ -93,32 +84,62 @@ static char *Preamble[] = {
 	"	}",
 	"#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)",
+	"	#if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY)",
 		"	unsigned int proviso;", /* uses just 1 bit 0/1 */
-		"#endif",
+	"	#endif",
 	"#endif",
 	"#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK))",
 	"	unsigned long st_id;",
 	"#endif",
+	"#if !defined(SAFETY) || defined(REACH)",
+	"	unsigned int D;",
+	"#endif",
+	"#ifdef BCS",
+	"	#ifndef CONSERVATIVE",
+	"	#define CONSERVATIVE	1 /* good for up to 8 processes */",
+	"	#endif",
+	"	#ifdef CONSERVATIVE",
+	"		#if CONSERVATIVE <= 0 || CONSERVATIVE>32",
+	"		#error sensible values for CONSERVATIVE are 1..32 (256/8 = 32)",
+	"		#endif",
+	"	uchar ctx_pid[CONSERVATIVE];", /* pids setting lowest value */
+	"	#endif",
+	"	uchar ctx_low;", /* lowest nr of context switches seen so far */
+	"#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",
+	"	#if VECTORSZ<65536",
 	"	unsigned short ln;",	/* length of vector */
-		"#else",
+	"	#else",
 	"	unsigned long ln;",	/* length of vector */
-		"#endif",
+	"	#endif",
 	"#endif",
-	"#if !defined(SAFETY) || defined(REACH)",
-	"	unsigned int D;",
+	"#if defined(AUTO_RESIZE) && !defined(BITSTATE)",
+	"	unsigned long m_K1;",
 	"#endif",
-	"	unsigned state;",
+	"	unsigned long state;",
 	"} **H_tab, **S_Tab;\n",
 
 	"typedef struct Trail {",
 	"	int   st;	/* current state */",
+	"	int   o_tt;",
 	"	uchar pr;	/* process id */",
 	"	uchar tau;	/* 8 bit-flags */",
 	"	uchar o_pm;	/* 8 more bit-flags */",
@@ -142,6 +163,9 @@ static char *Preamble[] = {
 	"	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",
@@ -150,23 +174,22 @@ static char *Preamble[] = {
 	"#endif",
 	"	uchar  o_m;",
 	"#ifdef EVENT_TRACE",
-		"#if nstates_event<256",
-	"	uchar o_event;",
-		"#else",
-	"	unsigned short o_event;",
-		"#endif",
+	"	#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",
+	"	#ifdef T_RAND",
+	"		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)",
+	"#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[] */
@@ -174,6 +197,23 @@ static char *Preamble[] = {
 	"	long	j6, j7;",
 	"#endif",
 	"	Trans *o_t;",	/* transition fct, next state   */
+
+	"#if !defined(BFS) && !defined(TRIX_ORIG)",
+	"	char *p_bup;",
+	"	char *q_bup;",
+	"#endif",
+
+	"#ifdef BCS",
+	"	unsigned short sched_limit;",
+	"	unsigned char  bcs; /* phase 1 or 2, or forced 4 */",
+	"	unsigned char  b_pno; /* preferred pid */",
+	"#endif",
+
+	"#ifdef P_RAND",	/* process scheduling randomization */
+	"	unsigned char p_left;	/* nr of procs left to explore */",
+	"	short p_skip;	/* to find starting point in list */",
+	"#endif",
+
 	"#ifdef HAS_SORTED",
 	"	short ipt;",	/* insertion slot in q */
 	"#endif",
@@ -188,6 +228,22 @@ static char *Preamble[] = {
 	"uchar	*this;",
 	"long	maxdepth=10000;",
 	"long	omaxdepth=10000;",
+	"#ifdef BCS",
+	"	/* bitflags in trpt->bcs */",
+	"	#define B_PHASE1	1",
+	"	#define B_PHASE2	2",
+	"	#define B_FORCED	4",
+	"int	sched_max = 0;",
+	"#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",
@@ -195,7 +251,10 @@ static char *Preamble[] = {
 	"uchar	HASH_NR = 0;",
 	"",
 	"double memcnt = (double) 0;",
-	"double memlim = (double) (1<<30);",
+	"double memlim = (double) (1<<30); /* 1 GB */",
+	"#if NCORE>1",
+	"double mem_reserved = (double) 0;",
+	"#endif",
 	"",
 	"/* for emalloc: */",
 	"static char *have;",
@@ -203,6 +262,62 @@ static char *Preamble[] = {
 	"static double fragment = (double) 0;",
 	"static unsigned long grow;",
 	"",
+#if 1
+	"unsigned int HASH_CONST[] = {",
+	"	/* asuming 4 bytes per int */",
+	"	0x100d4e63,	0x0fc22f87,",
+	"	0x3ff0c3ff,	0x38e84cd7,",
+	"	0x02b148e9,	0x98b2e49d,",
+	"	0xb616d379,	0xa5247fd9,",
+	"	0xbae92a15,	0xb91c8bc5,",
+	"	0x8e5880f3,	0xacd7c069,",
+	"	0xb4c44bb3,	0x2ead1fb7,",
+	"	0x8e428171,	0xdbebd459,",
+	"	0x00400007,	0x04c11db7,",
+	"	0x828ae611,	0x6cb25933,",
+	"	0x86cdd651,	0x9e8f5f21,",
+	"	0xd5f8d8e7,	0x9c4e956f,",
+	"	0xb5cf2c71,	0x2e805a6d,",
+	"	0x33fc3a55,	0xaf203ed1,",
+	"	0xe31f5909,	0x5276db35,",
+	"	0x0c565ef7,	0x273d1aa5,",
+	"	0x8923b1dd,	0xa9acaac5,",
+	"	0xd1f69207,	0xedfd944b,",
+	"	0x9a68e46b,	0x5355e13f,",
+	"	0x7eeb44f9,	0x932beea9,",
+	"	0x330c4cd3,	0x87f34e5f,",
+	"	0x1b5851b7,	0xb9ca6447,",
+	"	0x58f96a8f,	0x1b3b5307,",
+	"	0x31c387b3,	0xf35f0f35,",
+	"	0xa0acc4df,	0xf3140303,",
+	"	0x2446245d,	0xe4b8f4ef,",
+	"	0x5c007383,	0x68e648af,",
+	"	0x1814fba7,	0xcdf731b5,",
+	"	0xd09ccb4b,	0xb92d0eff,",
+	"	0xcc3c6b67,	0xd3af6a57,",
+	"	0xf44fc3f5,	0x5bb67623,",
+	"	0xaeb9c953,	0x5e0ac739,",
+	"	0x3a7fda09,	0x5edf39eb,",
+	"	0x661eefd9,	0x6423f0d1,",
+	"	0x910fe413,	0x9ec92297,",
+	"	0x4bd8159d,	0xa7b16ee1,",
+	"	0x89d484e9,	0x7f305cb3,",
+	"	0xc5f303e7,	0x415deeef,",
+	"	0x09986f89,	0x7e9c4117,",
+	"	0x0b7cbedb,	0xf9ed7561,",
+	"	0x7a20ac99,	0xf05adef3,",
+	"	0x5893d75b,	0x44d73327,",
+	"	0xb583c873,	0x324d2145,",
+	"	0x7fa3829b,	0xe4b47a23,",
+	"	0xe256d94f,	0xb1fd8959,",
+	"	0xe561a321,	0x1435ac09,",
+	"	0xdd62408b,	0x02ec0bcb,",
+	"	0x5469b785,	0x2f4f50bb,",
+	"	0x20f19395,	0xf96ba085,",
+	"	0x2381f937,	0x768e2a11,",
+	"	0",
+	"};",
+#else
 	"unsigned int HASH_CONST[] = {",
 	"	/* asuming 4 bytes per int */",
 	"	0x88888EEF,	0x00400007,",
@@ -224,9 +339,23 @@ static char *Preamble[] = {
 	"	0x273d1aa5,	0x8923b1dd,",
 	"	0",
 	"};",
-	"int	mreached=0, done=0, errors=0, Nrun=1;",
+#endif
+	"#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;",
+	"#ifdef PUTPID",
+	"char *progname;",
+	"#endif",
+	"#if defined(ZAPH) && defined(BITSTATE)",
+	"double zstates = 0;",
+	"#endif",
+	"int	c_init_run;",
 	"#ifdef BFS",
 	"double midrv=0, failedrv=0, revrv=0;",
 	"#endif",
@@ -236,9 +365,11 @@ static char *Preamble[] = {
 	"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, whichtrail = 0, onlyproc = -1, silent = 0;",
+	"int	gui = 0, coltrace = 0, readtrail = 0;",
+	"int	whichtrail = 0, whichclaim = -1, onlyproc = -1, silent = 0;",
+	"char	*claimname;",
 	"#endif",
-	"int	state_tables=0, fairness=0, no_rck=0, Nr_Trails=0;",
+	"int	state_tables=0, fairness=0, no_rck=0, Nr_Trails=0, dodot=0;",
 	"char	simvals[128];",
 	"#ifndef INLINE",
 	"int	TstOnly=0;",
@@ -251,7 +382,7 @@ static char *Preamble[] = {
 	"#endif",
 	"int	hmax=0, svmax=0, smax=0;",
 	"int	Maxbody=0, XX;",
-	"uchar	*noptr;	/* used by macro Pptr(x) */",
+	"uchar	*noptr, *noqptr;	/* used by Pptr(x) and Qptr(x) */",
 	"#ifdef VAR_RANGES",
 	"void logval(char *, int);",
 	"void dumpranges(void);",
@@ -264,8 +395,15 @@ static char *Preamble[] = {
 	"extern int  dfa_store(uchar *);",
 	"unsigned int	maxgs = 0;",
 	"#endif",
-
-	"State	comp_now;	/* compressed state vector */",
+	"",
+	"#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",
@@ -273,23 +411,30 @@ static char *Preamble[] = {
 	"static char	*scratch = (char *) &comp_tmp;",
 	"#endif",
 
-	"Stack	*stack; 	/* for queues, processes */",
+	"_Stack	*stack; 	/* for queues, processes */",
 	"Svtack	*svtack;	/* for old state vectors */",
 	"#ifdef BITSTATE",
-	"static unsigned hfns = 3;	/* new default */",
+	"static unsigned int hfns = 3;	/* new default */",
 	"#endif",
-	"static unsigned long j1;",
+	"static unsigned long j1_spin; /* 5.2.1: avoid nameclash with math.h */",
 	"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;",	/* not static to support -S2 option, but possible clash with embedded code */
-	"static uchar	warned = 0, iterative = 0, like_java = 0, every_error = 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 defined(T_RAND) || defined(P_RAND) || defined(RANDSTOR)",
+	"unsigned int s_rand = 123;	/* default seed */",
+	"#endif",
+
 	"#if SYNC>0 && ASYNC==0",
 	"void set_recvs(void);",
 	"int  no_recvs(int);",
@@ -304,15 +449,13 @@ static char *Preamble[] = {
 	"#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 iniglobals(int);",
 	"void stopped(int);",
 	"void wrapup(void);",
 	"int *grab_ints(int);",
@@ -425,9 +568,9 @@ static char *Tail[] = {
 	"}",
 	"#endif",
 	"void",
-	"retrans(int n, int m, int is, short srcln[], uchar reach[])",
+	"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;",
+	"{	Trans *T0, *T1, *T2, *T3, *T4, *T5;",
 	"	int i, k;",
 	"#ifndef NOREDUCE",
 	"	int g, h, j, aa;",
@@ -436,7 +579,7 @@ static char *Tail[] = {
 	"	int p;",
 	"#endif",
 	"	if (state_tables >= 4)",
-	"	{	printf(\"STEP 1 proctype %%s\\n\", ",
+	"	{	printf(\"STEP 1 %%s\\n\", ",
 	"			procname[n]);",
 	"		for (i = 1; i < m; i++)",
 	"		for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
@@ -477,7 +620,7 @@ static char *Tail[] = {
 	"	} while (cnt);",
 
 	"	if (state_tables >= 3)",
-	"	{	printf(\"STEP 2 proctype %%s\\n\", ",
+	"	{	printf(\"STEP 2 %%s\\n\", ",
 	"			procname[n]);",
 	"		for (i = 1; i < m; i++)",
 	"		for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
@@ -490,7 +633,9 @@ static char *Tail[] = {
 	"#if 0",
 	"			printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",",
 	"			T1->st, T1->forw, i);",
-	"#endif",
+	"#endif",		/* pull linenumber ref as well: */
+	"			srcln[i] = srcln[T1->st];	/* Oyvind Teig, 5.2.0 */",
+	"",
 	"			if (!trans[n][T1->st]) continue;",
 	"			T0 = cpytr(trans[n][T1->st]);",
 	"			trans[n][i] = T0;",
@@ -502,6 +647,7 @@ static char *Tail[] = {
 	"			printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",",
 	"				T1->st, T1->forw, i);",
 	"#endif",
+	"		/*		srcln[i] = srcln[T1->st];  gh: not useful */",
 	"				if (!trans[n][T1->st]) continue;",
 	"				T0->nxt = cpytr(trans[n][T1->st]);",
 	"				T0 = T0->nxt;",
@@ -509,7 +655,7 @@ static char *Tail[] = {
 	"				imed(T0, T1->st, n, i);",
 	"	}	}	}",
 	"	if (state_tables >= 2)",
-	"	{	printf(\"STEP 3 proctype %%s\\n\", ",
+	"	{	printf(\"STEP 3 %%s\\n\", ",
 	"			procname[n]);",
 	"		for (i = 1; i < m; i++)",
 	"		for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
@@ -525,8 +671,8 @@ static char *Tail[] = {
 	"		 * and prepend them to the transition-",
 	"		 * list of state i",
 	"		 */",
-	" if (!like_java) /* the default */",
-	" {		for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
+	"	 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)",
@@ -537,9 +683,8 @@ static char *Tail[] = {
 	"				T2->nxt = trans[n][i];",
 	"				trans[n][i] = T2;",
 	"		}	}",
-	" } else /* outermost unless checked first */",
-	" {		Trans *T4;",
-	"		T4 = T3 = (Trans *) 0;",
+	"	 } else /* outermost unless checked first */",
+	"	 {	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])",
@@ -557,22 +702,21 @@ static char *Tail[] = {
 	"		{	T3->nxt = trans[n][i];",
 	"			trans[n][i] = T4;",
 	"		}",
-	" }",
+	"	 }",
 	"	}",
 	"#endif",
 
 	"#ifndef NOREDUCE",
 	"	for (i = 1; i < m; i++)",
-	"	{",
-	"		if (a_cycles)",
+	"	{	if (a_cycles)",
 	"		{ /* moves through these states are visible */",
-	"#if PROG_LAB>0 && defined(HAS_NP)",
+	"	#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",
+	"	#endif",
 	"			if (accpstate[n][i] || visstate[n][i])",
 	"				goto degrade;",
 	"			for (T1 = trans[n][i]; T1; T1 = T1->nxt)",
@@ -668,19 +812,43 @@ static char *Tail[] = {
 	"			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]);",
+	"	if (state_tables && !verbose)",
+	"	{	if (dodot)",
+	"		{	char buf[256], *q = buf, *p = procname[n];",
+	"			while (*p != '\\0')",
+	"			{	if (*p != ':')",
+	"				{	*q++ = *p;",
+	"				}",
+	"				p++;",
+	"			}",
+	"			*q = '\\0';",
+	"			printf(\"digraph \");",
+	"			switch (Btypes[n]) {",
+	"			case I_PROC:  printf(\"init {\\n\"); break;",
+	"			case N_CLAIM: printf(\"claim_%%s {\\n\", buf); break;",
+	"			case E_TRACE: printf(\"notrace {\\n\"); break;",
+	"			case N_TRACE: printf(\"trace {\\n\"); break;",
+	"			default:      printf(\"p_%%s {\\n\", buf); break;",
+	"			}",
+	"			printf(\"size=\\\"8,10\\\";\\n\");",
+	"			printf(\"  GT [shape=box,style=dotted,label=\\\"%%s\\\"];\\n\", buf);",
+	"		} else",
+	"		{	switch (Btypes[n]) {",
+	"			case I_PROC:  printf(\"init\\n\"); break;",
+	"			case N_CLAIM: printf(\"claim %%s\\n\", procname[n]); break;",
+	"			case E_TRACE: printf(\"notrace assertion\\n\"); break;",
+	"			case N_TRACE: printf(\"trace assertion\\n\"); break;",
+	"			default:      printf(\"proctype %%s\\n\", procname[n]); break;",
+	"		}	}",
 	"		for (i = 1; i < m; i++)",
-	"			reach[i] = 1;",
+	"		{	reach[i] = 1;",
+	"		}",
 	"		tagtable(n, m, is, srcln, reach);",
+	"		if (dodot) printf(\"}\\n\");",
 	"	} else",
 	"	for (i = 1; i < m; i++)",
 	"	{	int nrelse;",
-	"		if (strcmp(procname[n], \":never:\") != 0)",
+	"		if (Btypes[n] != N_CLAIM)",
 	"		{	for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
 	"			{	if (T0->st == i",
 	"				&& strcmp(T0->tp, \"(1)\") == 0)",
@@ -703,17 +871,75 @@ static char *Tail[] = {
 	"		  	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));",
+	"#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 && Btypes[n] != N_CLAIM)",
+	"	{	for (i = 1; i < m; i++)", /* for each state */
+	"		{	Trans *Tx = (Trans *) 0; /* list of escapes */",
+	"			Trans *Ty = (Trans *) 0; /* its tail element */",
+	"			T1 = (Trans *) 0; /* reversed list */",
+	"			T2 = (Trans *) 0; /* its tail */",
+	"			T3 = (Trans *) 0; /* remembers possible 'else' */",
+	"",
+	"			/* find unless-escapes, they should go first */",
+	"			T4 = T5 = T0 = trans[n][i];",
+	"#ifdef HAS_UNLESS",
+	"			while (T4 && T4->e_trans) /* escapes are first in orig list */",
+	"			{	T5 = T4;	  /* remember predecessor */",
+	"				T4 = T4->nxt;",
+	"			}",
+	"#endif",
+	"			/* T4 points to first non-escape, T5 to its parent, T0 to original list */",
+	"			if (T4 != T0)		 /* there was at least one escape */",	
+	"			{	T3 = T5->nxt;		 /* start of non-escapes */",
+	"				T5->nxt = (Trans *) 0;	 /* separate */",
+	"				Tx = T0;		 /* start of the escapes */",
+	"				Ty = T5;		 /* its tail */",
+	"				T0 = T3;		 /* the rest, to be reversed */",
+	"			}",
+	"			/* T0 points to first non-escape, Tx to the list of escapes, Ty to its tail */",
+	"",
+	"			/* first tail-add non-escape transitions, reversed */",
+	"			T3 = (Trans *) 0;", /* remember a possible 'else' */
+	"			for (T5 = T0; T5; T5 = T4)",
+	"			{	T4 = T5->nxt;",
+	"#ifdef HAS_UNLESS",
+	"				if (T5->e_trans)",
+	"				{	printf(\"error: cannot happen!\\n\");",
+	"					continue;",
+	"				}",
+	"#endif",
+	"				if (strcmp(T5->tp, \"else\") == 0)",
+	"				{	T3 = T5;",
+	"					T5->nxt = (Trans *) 0;",
+	"				} else",
+	"				{	T5->nxt = T1;",
+	"					if (!T1) { T2 = T5; }",
+	"					T1 = T5;",
+	"			}	}",
+	"			/* T3 points to a possible else, which is removed from the list */",
+	"			/* T1 points to the reversed list so far (without escapes) */",
+	"			/* T2 points to the tail element -- where the else should go */",
+	"			if (T2 && T3) { T2->nxt = T3; }	/* add else */",
+	"",
+	"			/* add in the escapes, to that they appear at the front */",
+	"			if (Tx && Ty) { Ty->nxt = T1; T1 = Tx; }",
+	"",
+	"			trans[n][i] = T1;",
+	"			/* reversed, with escapes first and else last */",
+	"	}	}",
+	"	if (state_tables && verbose)",
+	"	{	printf(\"FINAL proctype %%s\\n\", ",
+	"			procname[n]);",
 	"		for (i = 1; i < m; i++)",
 	"		for (T0 = trans[n][i]; T0; T0 = T0->nxt)",
-	"			frm_st0[T0->forw] = i;",
+	"			crack(n, i, T0, srcln);",
 	"	}",
+	"#endif",
 	"}",
 	"void",
 	"imed(Trans *T, int v, int n, int j)	/* set intermediate state */",
@@ -731,7 +957,12 @@ static char *Tail[] = {
 	"	reach[is] = 0;",
 	"	if (state_tables)",
 	"	for (z = trans[n][is]; z; z = z->nxt)",
-	"		crack(n, is, z, srcln);",
+	"	{	if (dodot)",
+	"			dot_crack(n, is, z);",
+	"		else",
+	"			crack(n, is, z, srcln);",
+	"	}",
+	"",
 	"	for (z = trans[n][is]; z; z = z->nxt)",
 	"	{",
 	"#ifdef HAS_UNLESS",
@@ -748,6 +979,44 @@ static char *Tail[] = {
 	"	}",
 	"}",
 	"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 && verbose)",
+	"			{	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;",
@@ -765,19 +1034,19 @@ static char *Tail[] = {
 	"	}",
 	"#endif",
 	"	printf(\"]\");",
-	"	printf(\" [%%s%%s%%s%%s%%s] line %%d => \",",
+	"	printf(\" [%%s%%s%%s%%s%%s] %%s:%%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]);",
+	"		PanSource, 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])",
+	"	if (verbose && z->qu[0])",
 	"	{	printf(\"\\t[\");",
 	"		for (i = 0; i < 6; i++)",
 	"			if (z->qu[i])",
@@ -788,6 +1057,32 @@ static char *Tail[] = {
 	"	printf(\"\\n\");",
 	"	fflush(stdout);",
 	"}",
+	"/* spin -a m.pml; cc -o pan pan.c; ./pan -D | dot -Tps > foo.ps; ps2pdf foo.ps */",
+	"void",
+	"dot_crack(int n, int j, Trans *z)",
+	"{	int i;\n",
+	"	if (!z) return;",
+	"	printf(\"\tS%%d -> S%%d  [color=black\", j, z->st);",
+	"",
+	"	if (z->atom&6) printf(\",style=dashed\");",		/* A */
+	"	else if (z->atom&32) printf(\",style=dotted\");",	/* D */
+	"	else if (z->atom&8) printf(\",style=solid\");",		/* L */
+	"	else printf(\",style=bold\");",				/* G */
+		/* other styles: filled dotted */
+	"",
+	"	printf(\",label=\\\"\");",
+	"	for (i = 0; z->tp[i]; i++)",
+	"	{	if (z->tp[i] == \'\\\\\'",
+	"		&&  z->tp[i+1] == \'n\')",
+	"		{	i++; printf(\" \");",
+	"		} else",
+	"		{	putchar(z->tp[i]);",
+	"	}	}",
+	"	printf(\"\\\"];\\n\");",
+	"	if (accpstate[n][j]) printf(\"  S%%d [color=red,style=bold];\\n\", j);",
+	"	else if (progstate[n][j]) printf(\"  S%%d [color=green,style=bold];\\n\", j);",
+	"	if (stopstate[n][j]) printf(\"  S%%d [color=blue,style=bold,shape=box];\\n\", j);",
+	"}",
 	"",
 	"#ifdef VAR_RANGES",
 	"#define BYTESIZE	32	/* 2^8 : 2^3 = 256:8 = 32 */",

+ 56 - 17
sys/src/cmd/spin/pangen3.c

@@ -13,10 +13,10 @@
 #include "y.tab.h"
 
 extern FILE	*th;
-extern int	claimnr, eventmapnr;
+extern int	eventmapnr;
 
 typedef struct SRC {
-	short ln, st;	/* linenr, statenr */
+	int ln, st;	/* linenr, statenr */
 	Symbol *fn;	/* filename */
 	struct SRC *nxt;
 } SRC;
@@ -28,6 +28,8 @@ static int	lastfrom;
 static SRC	*frst = (SRC *) 0;
 static SRC	*skip = (SRC *) 0;
 
+extern int	ltl_mode;
+
 extern void	sr_mesg(FILE *, int, int);
 
 static void
@@ -47,7 +49,7 @@ putfnm(int j, Symbol *s)
 		return;
 
 	if (lastfnm)
-		fprintf(th, "{ %s, %d, %d },\n\t",
+		fprintf(th, "{ \"%s\", %d, %d },\n\t",
 			lastfnm->name,
 			lastfrom,
 			j-1);
@@ -59,7 +61,7 @@ static void
 putfnm_flush(int j)
 {
 	if (lastfnm)
-		fprintf(th, "{ %s, %d, %d }\n",
+		fprintf(th, "{ \"%s\", %d, %d }\n",
 			lastfnm->name,
 			lastfrom, j);
 }
@@ -72,7 +74,7 @@ putskip(int m)	/* states that need not be reached */
 		if (tmp->st == m)
 			return;
 	tmp = (SRC *) emalloc(sizeof(SRC));
-	tmp->st = (short) m;
+	tmp->st  = m;
 	tmp->nxt = skip;
 	skip = tmp;
 }
@@ -85,7 +87,7 @@ unskip(int m)	/* a state that needs to be reached after all */
 		if (tmp->st == m)
 		{	if (tmp == skip)
 				skip = skip->nxt;
-			else
+			else if (lst)	/* always true, but helps coverity */
 				lst->nxt = tmp->nxt;
 			break;
 		}
@@ -104,13 +106,13 @@ putsrc(Element *e)	/* match states to source lines */
 	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,
+			printf("putsrc mismatch seqno %d, line %d - %d, file %s\n", m, n,
 				tmp->ln, tmp->fn->name);
 			return;
 		}
 	tmp = (SRC *) emalloc(sizeof(SRC));
-	tmp->ln = (short) n;
-	tmp->st = (short) m;
+	tmp->ln = n;
+	tmp->st = m;
 	tmp->fn = e->n->fn;
 	tmp->nxt = frst;
 	frst = tmp;
@@ -137,8 +139,9 @@ dumpskip(int n, int m)
 			putnr(0);
 	}
 	fprintf(th, "};\n");
-	if (m == claimnr)
-		fprintf(th, "#define reached_claim	reached%d\n", m);
+
+	fprintf(th, "uchar *loopstate%d;\n", m);
+
 	if (m == eventmapnr)
 		fprintf(th, "#define reached_event	reached%d\n", m);
 
@@ -149,6 +152,7 @@ void
 dumpsrc(int n, int m)
 {	SRC *tmp, *lst;
 	int j;
+	static int did_claim = 0;
 
 	fprintf(th, "short src_ln%d [] = {\n\t", m);
 	for (j = 0, col = 0; j <= n; j++)
@@ -164,7 +168,7 @@ dumpsrc(int n, int m)
 	fprintf(th, "};\n");
 
 	lastfnm = (Symbol *) 0;
-	lastdef.name = "\"-\"";
+	lastdef.name = "-";
 	fprintf(th, "S_F_MAP src_file%d [] = {\n\t", m);
 	for (j = 0, col = 0; j <= n; j++)
 	{	lst = (SRC *) 0;
@@ -183,8 +187,10 @@ dumpsrc(int n, int m)
 	putfnm_flush(j);
 	fprintf(th, "};\n");
 
-	if (m == claimnr)
-		fprintf(th, "#define src_claim	src_ln%d\n", m);
+	if (pid_is_claim(m) && !did_claim)
+	{	fprintf(th, "short *src_claim;\n");
+		did_claim++;
+	}
 	if (m == eventmapnr)
 		fprintf(th, "#define src_event	src_ln%d\n", m);
 
@@ -237,7 +243,27 @@ comwork(FILE *fd, Lextok *now, int m)
 	case GT:	Cat1(">"); break;
 	case LT:	Cat1("<"); break;
 	case NE:	Cat1("!="); break;
-	case EQ:	Cat1("=="); break;
+	case EQ:
+			if (ltl_mode
+			&&  now->lft->ntyp == 'p'
+			&&  now->rgt->ntyp == 'q')	/* remote ref */
+			{	Lextok *p = now->lft->lft;
+
+				fprintf(fd, "(");
+				fprintf(fd, "%s", p->sym->name);
+				if (p->lft)
+				{	fprintf(fd, "[");
+					putstmnt(fd, p->lft, 0); /* pid */
+					fprintf(fd, "]");
+				}
+				fprintf(fd, "@");
+				fprintf(fd, "%s", now->rgt->sym->name);
+				fprintf(fd, ")");
+				break;
+			}
+			Cat1("==");
+			break;
+
 	case OR:	Cat1("||"); break;
 	case AND:	Cat1("&&"); break;
 	case LSHIFT:	Cat1("<<"); break;
@@ -349,9 +375,22 @@ comwork(FILE *fd, Lextok *now, int m)
 			comwork(fd, now->lft, m);
 			fprintf(fd, ")");
 			break;
-	case NAME:	putname(fd, "", now, m, "");
+	case NAME:
+			putname(fd, "", now, m, "");
 			break;
-	case   'p':	putremote(fd, now, m);
+
+	case   'p':	if (ltl_mode)
+			{	fprintf(fd, "%s", now->lft->sym->name); /* proctype */
+				if (now->lft->lft)
+				{	fprintf(fd, "[");
+					putstmnt(fd, now->lft->lft, 0); /* pid */
+					fprintf(fd, "]");
+				}
+				fprintf(fd, ":");	/* remote varref */
+				fprintf(fd, "%s", now->sym->name);	/* varname */
+				break;
+			}
+			putremote(fd, now, m);
 			break;
 	case   'q':	fprintf(fd, "%s", now->sym->name);
 			break;

+ 397 - 153
sys/src/cmd/spin/pangen3.h

@@ -8,15 +8,16 @@
 /* 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 */
+	"	#undef REACH",	/* redundant with bfs */
 	"#endif",
 	"#ifdef VERI",
-		"#define BASE	1",
+	"	#define BASE	1",
 	"#else",
-		"#define BASE	0",
+	"	#define BASE	0",
 	"#endif",
 	"typedef struct Trans {",
 	"	short atom;	/* if &2 = atomic trans; if &8 local */",
@@ -37,108 +38,140 @@ static char *Head0[] = {
 	"	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)",	*/
+
+	"#ifdef TRIX",
+	"	#define qptr(x)	(channels[x]->body)",
+	"	#define pptr(x)	(processes[x]->body)",
+	"#else",
+	"	#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)",	*/
+	"#endif",
 	"extern uchar *Pptr(int);",
+	"extern uchar *Qptr(int);",
 
-	"#define q_sz(x)	(((Q0 *)qptr(x))->Qlen)\n",
-	"#ifndef VECTORSZ",
-	"#define VECTORSZ	1024           /* sv   size in bytes */",
+	"#define q_sz(x)	(((Q0 *)qptr(x))->Qlen)",
+	"",
+	"#ifdef TRIX",
+	"	#ifdef VECTORSZ",
+	"		#undef VECTORSZ",	/* backward compatibility */
+	"	#endif",
+	"	#if WS==4",
+	"		#define VECTORSZ	2056	/* ((MAXPROC+MAXQ+4)*sizeof(uchar *)) */",
+	"	#else",
+	"		#define VECTORSZ	4112	/* the formula causes probs in preprocessing */",
+	"	#endif",
+	"#else",
+	"	#ifndef VECTORSZ",
+	"		#define VECTORSZ	1024	/* sv size in bytes */",
+	"	#endif",
 	"#endif\n",
 	0,
 };
 
 static char *Header[] = {
 	"#ifdef VERBOSE",
-		"#ifndef CHECK",
-		"#define CHECK",
-		"#endif",
-		"#ifndef DEBUG",
-		"#define DEBUG",
-		"#endif",
+	"	#ifndef CHECK",
+	"		#define CHECK",
+	"	#endif",
+	"	#ifndef DEBUG",
+	"		#define DEBUG",
+	"	#endif",
 	"#endif",
 	"#ifdef SAFETY",
-		"#ifndef NOFAIR",
-			"#define NOFAIR",
-		"#endif",
+	"	#ifndef NOFAIR",
+	"		#define NOFAIR",
+	"	#endif",
 	"#endif",
 	"#ifdef NOREDUCE",
-		"#ifndef XUSAFE",
-		"#define XUSAFE",
-		"#endif",
-		"#if !defined(SAFETY) && !defined(MA)",
-			"#define FULLSTACK",
-		"#endif",
+	"	#ifndef XUSAFE",
+	"		#define XUSAFE",
+	"	#endif",
+	"	#if !defined(SAFETY) && !defined(MA)",
+	"		#define FULLSTACK",
+	"	#endif",
 	"#else",
-		"#ifdef BITSTATE",
-			"#ifdef SAFETY && !defined(HASH64)",
-				"#define CNTRSTACK",
-			"#else",
-				"#define FULLSTACK",
-			"#endif",
-		"#else",
-			"#define FULLSTACK",
-		"#endif",
+	"	#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",
+	"	#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",
+	"	/* accept the above for backward compatibility */",
+	"	#define COLLAPSE",
 	"#endif",
 	"#ifdef HC",
-	"#undef HC",
-	"#define HC4",
+	"	#undef HC",
+	"	#define HC4",
 	"#endif",
 	"#ifdef HC0",	/* 32 bits */
-	"#define HC	0",
+	"	#define HC	0",
 	"#endif",
 	"#ifdef HC1",	/* 32+8 bits */
-	"#define HC	1",
+	"	#define HC	1",
 	"#endif",
 	"#ifdef HC2",	/* 32+16 bits */
-	"#define HC	2",
+	"	#define HC	2",
 	"#endif",
 	"#ifdef HC3",	/* 32+24 bits */
-	"#define HC	3",
+	"	#define HC	3",
 	"#endif",
 	"#ifdef HC4",	/* 32+32 bits - combine with -DMA=8 */
-	"#define HC	4",
+	"	#define HC	4",
 	"#endif",
 	"#ifdef COLLAPSE",
-	"unsigned long ncomps[256+2];",
+	"	#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(long)   /* word size in bytes */",
-	"typedef struct Stack  {	 /* for queues and processes */",
+	"",
+	"typedef struct _Stack  {	 /* for queues and processes */",
 	"#if VECTORSZ>32000",
 	"	int o_delta;",
-	"	int o_offset;",
-	"	int o_skip;",
+	"	#ifndef TRIX",
+	"		int o_offset;",
+	"		int o_skip;",
+	"	#endif",
 	"	int o_delqs;",
 	"#else",
 	"	short o_delta;",
-	"	short o_offset;",
-	"	short o_skip;",
+	"	#ifndef TRIX",
+	"		short o_offset;",
+	"		short o_skip;",
+	"	#endif",
 	"	short o_delqs;",
 	"#endif",
 	"	short o_boq;",
+	"#ifdef TRIX",
+	"	short parent;",
+	"	char *b_ptr;",	/* used in delq/q_restor and delproc/p_restor */
+	"#else",
+	"	char *body;",	/* full copy of state vector in non-trix mode */
+	"#endif",
 	"#ifndef XUSAFE",
 	"	char *o_name;",
 	"#endif",
-	"	char *body;",
-	"	struct Stack *nxt;",
-	"	struct Stack *lst;",
-	"} Stack;\n",
+	"	struct _Stack *nxt;",
+	"	struct _Stack *lst;",
+	"} _Stack;\n",
 	"typedef struct Svtack { /* for complete state vector */",
 	"#if VECTORSZ>32000",
 	"	int o_delta;",
@@ -159,22 +192,24 @@ static char *Header0[] = {
 	"	struct Svtack *lst;",
 	"} Svtack;\n",
 	"Trans ***trans;	/* 1 ptr per state per proctype */\n",
-	"#if defined(FULLSTACK) || defined(BFS)",
 	"struct H_el *Lstate;",
-	"#endif",
 	"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];",
+
+	"#ifndef TRIX",
+	"	#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];",
 	"#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 */",
+	"	int vprefix=0, svfd;	/* runtime option -pN */",
 	"#endif",
 	"char *tprefix = \"trail\";	/* runtime option -tsuffix */",
 	"short boq = -1;		/* blocked_on_queue status */",
@@ -214,12 +249,17 @@ static char *Head1[] = {
 	"#ifdef HAS_LAST",	/* cannot go before _cnt - see hstore() */
 	"	uchar  _last;	/* pid executed in last step */",
 	"#endif",
+
+	"#if defined(BITSTATE) && defined(BCS) && defined(STORE_CTX)",
+	"	uchar  _ctx;	/* nr of context switches so far */",
+	"#endif",
+
 	"#ifdef EVENT_TRACE",
-		"#if nstates_event<256",
-	"	uchar _event;",
-		"#else",
-	"	unsigned short _event;",
-		"#endif",
+	"	#if nstates_event<256",
+	"		uchar _event;",
+	"	#else",
+	"		unsigned short _event;",
+	"	#endif",
 	"#endif",
 	0,
 };
@@ -235,12 +275,15 @@ static char *Addp0[] = {
 	"	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)",
+	"	/* 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\");",
+	"#ifdef V_TRIX",
+	"	printf(\"%%4d: add process %%d\\n\", depth, h);",
+	"#endif",
 	"	switch (n) {",
 	"	case 0: j = sizeof(P0); break;",
 	0,
@@ -249,31 +292,46 @@ static char *Addp0[] = {
 static char *Addp1[] = {
 	"	default: Uerror(\"bad proc - addproc\");",
 	"	}",
+
+	"#ifdef TRIX",
+	"	vsize += sizeof(struct H_el *);",
+	"#else",
 	"	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",
+	"	#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",
+	"	vsize += j;",
+	"	#if defined(SVDUMP) && defined(VERBOSE)",
 	"	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",
+	"	#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 */",
+	"	#else",
+	"		write(svfd, (uchar *) &proc_offset[h], sizeof(short));",
+	"		write(svfd, (uchar *) &now, vprefix-3*sizeof(int)-sizeof(short)); /* padd */",
+	"	#endif",
 	"	}",
+	"	#endif",
 	"#endif",
+
 	"	now._nr_pr += 1;",
+	"#if defined(BCS) && defined(CONSERVATIVE)",
+	"	if (now._nr_pr >= CONSERVATIVE*8)",
+	"	{	printf(\"pan: error: too many processes -- recompile with \");",
+	"		printf(\"-DCONSERVATIVE=%%d\\n\", CONSERVATIVE+1);",
+	"		pan_exit(1);",
+	"	}",
+	"#endif",
 	"	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\",",
@@ -282,22 +340,38 @@ static char *Addp1[] = {
 	"			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);",
+
+	"#ifdef TRIX",
+	"	#ifndef BFS",
+	"		if (freebodies)",
+	"		{	processes[h] = freebodies;",
+	"			freebodies = freebodies->nxt;",
+	"		} else",
+	"		{	processes[h] = (TRIX_v6 *) emalloc(sizeof(TRIX_v6));",
+	"			processes[h]->body = (uchar *) emalloc(Maxbody * sizeof(char));",
+	"		}",
+	"		processes[h]->modified = 1;	/* addproc */",
+	"	#endif",
+	"	processes[h]->psize = j;",
+	"	processes[h]->parent_pid = calling_pid;",
+	"	processes[h]->nxt = (TRIX_v6 *) 0;",
+	"#else",
+	"	#ifndef NOCOMP",
+	"		for (k = 1; k <= Air[n]; k++)",
+	"			Mask[vsize - k] = 1; /* pad */",
+	"		Mask[vsize-j] = 1; /* _pid */",
+	"	#endif",
 	"	if (vsize >= VECTORSZ)",
 	"	{	printf(\"pan: error, VECTORSZ too small, recompile pan.c\");",
-	"		printf(\" with -DVECTORSZ=N with N>%%d\\n\", vsize);",
+	"		printf(\" with -DVECTORSZ=N with N>%%d\\n\", (int) vsize);",
 	"		Uerror(\"aborting\");",
 	"	}",
+	"#endif",
+
 	"	memset((char *)pptr(h), 0, j);",
 	"	this = pptr(h);",
 	"	if (BASE > 0 && h > 0)",
@@ -310,13 +384,16 @@ static char *Addp1[] = {
 
 static char *Addq0[] = {
 	"int",
-	"addqueue(int n, int is_rv)",
+	"addqueue(int calling_pid, int n, int is_rv)",
 	"{	int j=0, i = now._nr_qs;",
-	"#ifndef NOCOMP",
+	"#if !defined(NOCOMP) && !defined(TRIX)",
 	"	int k;",
 	"#endif",
 	"	if (i >= MAXQ)",
 	"		Uerror(\"too many queues\");",
+	"#ifdef V_TRIX",
+	"	printf(\"%%4d: add queue %%d\\n\", depth, i);",
+	"#endif",
 	"	switch (n) {",
 	0,
 };
@@ -324,29 +401,55 @@ static char *Addq0[] = {
 static char *Addq1[] = {
 	"	default: Uerror(\"bad queue - addqueue\");",
 	"	}",
+
+	"#ifdef TRIX",
+	"	vsize += sizeof(struct H_el *);",
+	"#else",
 	"	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",
+	"	#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;",
+	"#endif",
+
+	"	now._nr_qs += 1;",
 	"#ifndef NOVSZ",
 	"	now._vsz = vsize;",
 	"#endif",
 	"	hmax = max(hmax, vsize);",
+
+	"#ifdef TRIX",
+	"	#ifndef BFS",
+	"		if (freebodies)",
+	"		{	channels[i] = freebodies;",
+	"			freebodies = freebodies->nxt;",
+	"		} else",
+	"		{	channels[i] = (TRIX_v6 *) emalloc(sizeof(TRIX_v6));",
+	"			channels[i]->body = (uchar *) emalloc(Maxbody * sizeof(char));",
+	"		}",
+	"		channels[i]->modified = 1;	/* addq */",
+	"	#endif",
+	"	channels[i]->psize = j;",
+	"	channels[i]->parent_pid = calling_pid;",
+	"	channels[i]->nxt = (TRIX_v6 *) 0;",
+	"#else",
 	"	if (vsize >= VECTORSZ)",
 	"		Uerror(\"VECTORSZ is too small, edit pan.h\");",
-	"	memset((char *)qptr(i), 0, j);",
+	"#endif",
+
+	"	if (j > 0)", /* zero if there are no queues */
+	"	{	memset((char *)qptr(i), 0, j);",
+	"	}",
 	"	((Q0 *)qptr(i))->_t = n;",
 	"	return i+1;",
 	"}\n",
@@ -362,6 +465,19 @@ static char *Addq11[] = {
 	"	uerror(\"ref to uninitialized chan name (sending)\");",
 	"	if (into >= (int) now._nr_qs || into < 0)",
 	"		Uerror(\"qsend bad queue#\");",
+	"#if defined(TRIX) && !defined(BFS)",
+	"	#ifndef TRIX_ORIG",
+	"		(trpt+1)->q_bup = now._ids_[now._nr_pr+into];",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d s save %%p from %%d\\n\",",
+	"			depth, into, (trpt+1)->q_bup, now._nr_pr+into);",
+	"		#endif",
+	"	#endif",
+	"	channels[into]->modified = 1;	/* qsend */",
+	"	#ifdef V_TRIX",
+	"		printf(\"%%4d: channel %%d modified\\n\", depth, into);",
+	"	#endif",
+	"#endif",
 	"	z = qptr(into);",
 	"	j = ((Q0 *)qptr(into))->Qlen;",
 	"	switch (((Q0 *)qptr(into))->_t) {",
@@ -509,6 +625,19 @@ static char *Addq4[] = {
 	"	int j, k, r=0;\n",
 	"	if (!from--)",
 	"	uerror(\"ref to uninitialized chan name (receiving)\");",
+	"#if defined(TRIX) && !defined(BFS)",
+	"	#ifndef TRIX_ORIG",
+	"		(trpt+1)->q_bup = now._ids_[now._nr_pr+from];",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d r save %%p from %%d\\n\",",
+	"			depth, from, (trpt+1)->q_bup, now._nr_pr+from);",
+	"		#endif",
+	"	#endif",
+	"	channels[from]->modified = 1;	/* qrecv */",
+	"	#ifdef V_TRIX",
+	"		printf(\"%%4d: channel %%d modified\\n\", depth, from);",
+	"	#endif",
+	"#endif",
 	"	if (from >= (int) now._nr_qs || from < 0)",
 	"		Uerror(\"qrecv bad queue#\");",
 	"	z = qptr(from);",
@@ -547,6 +676,13 @@ static char *Code0[] = {
 	"#ifndef NOVSZ",
 	"	now._vsz = vsize;",
 	"#endif",
+	"#ifdef TRIX",
+	"	if (VECTORSZ != sizeof(now._ids_))",
+	"	{	printf(\"VECTORSZ is %%d, but should be %%d in this mode\\n\",",
+	"			VECTORSZ, sizeof(now._ids_));",
+	"		Uerror(\"VECTORSZ set incorrectly, recompile Spin (not pan.c)\");",
+	"	}",
+	"#endif",
 	"/* optional provisioning statements, e.g. to */",
 	"/* set hidden variables, used as constants */",
 	"#ifdef PROV",
@@ -557,10 +693,11 @@ static char *Code0[] = {
 };
 
 static char *R0[] = {
-	"	Maxbody = max(Maxbody, sizeof(P%d));",
+	"	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));",
@@ -572,30 +709,42 @@ static char *R0[] = {
 };
 
 static char *R0a[] = {
-	"	retrans(%d, nstates%d, start%d, src_ln%d, reached%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);",
-	"	}",
+	"	retrans(%d, nstates%d, start%d, src_ln%d, reached%d, loopstate%d);",
 	0,
 };
 
 static char *Code1[] = {
 	"#ifdef NP",
-	"#define ACCEPT_LAB	1 /* at least 1 in np_ */",
+	"	#define ACCEPT_LAB	1 /* at least 1 in np_ */",
 	"#else",
-	"#define ACCEPT_LAB	%d /* user-defined accept labels */",
+	"	#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,
 };
@@ -608,17 +757,18 @@ static char *Code3[] = {
 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];",
+	"	int NrStates[%d];",
 	"#endif",
 	0,
 };
 static char *R3[] = {
-	"	Maxbody = max(Maxbody, sizeof(Q%d));",
+	"	Maxbody = max(Maxbody, ((int) sizeof(Q%d)));",
 	0,
 };
 static char *R4[] = {
@@ -632,19 +782,22 @@ static char *R5[] = {
 static char *R6[] = {
 	"	}",
 	"	this = o_this;",
+	"#ifdef TRIX",
+	"	re_mark_all(1); /* addproc */",
+	"#endif",
 	"	return h-BASE;",
 	"#ifndef NOBOUNDCHECK",
-	"#undef Index",
-	"#define Index(x, y)	Boundcheck(x, y, II, tt, t)",
+	"	#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; }",
+	"	/* 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",
+	"	#ifdef COLLAPSE",
 	"long",
 	"col_p(int i, char *z)",
 	"{	int j, k; unsigned long ordinal(char *, long, short);",
@@ -669,7 +822,7 @@ static char *R8a[] = {
 	"	if (z) return (long) (x - z);",
 	"	return ordinal(scratch, x-scratch, (short) (2+ptr->_t));",
 	"}",
-	"#endif",
+	"	#endif",
 	"#endif",
 	0,
 };
@@ -691,7 +844,7 @@ static char *R8b[] = {
 	"	if (z) return (long) (x - z);",
 	"	return ordinal(scratch, x-scratch, 1); /* chan */",
 	"}",
-	"#endif",
+	"	#endif",
 	"#endif",
 	0,
 };
@@ -709,6 +862,20 @@ char *R13[] = {
 	"#endif",
 	"	if (!into--)",
 	"		uerror(\"ref to uninitialized chan (unsend)\");",
+	"#if defined(TRIX) && !defined(BFS)",
+	"	#ifndef TRIX_ORIG",
+	"		now._ids_[now._nr_pr+into] = trpt->q_bup;",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d s restore %%p into %%d\\n\",",
+	"				depth, into, trpt->q_bup, now._nr_pr+into);",
+	"		#endif",
+	"	#else",
+	"		channels[into]->modified = 1;	/* unsend */",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d unmodify\\n\", depth, into);",
+	"		#endif",
+	"	#endif",
+	"#endif",
 	"	z = qptr(into);",
 	"	j = ((Q0 *)z)->Qlen;",
 	"	((Q0 *)z)->Qlen = --j;",
@@ -725,6 +892,20 @@ char *R14[] = {
 	"{	int j; uchar *z;\n",
 	"	if (!from--)",
 	"		uerror(\"ref to uninitialized chan (unrecv)\");",
+	"#if defined(TRIX) && !defined(BFS)",
+	"	#ifndef TRIX_ORIG",
+	"		now._ids_[now._nr_pr+from] = trpt->q_bup;",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d r restore %%p into %%d\\n\",",
+	"				depth, from, trpt->q_bup, now._nr_pr+from);",
+	"		#endif",
+	"	#else",
+	"		channels[from]->modified = 1;	/* unrecv */",
+	"		#ifdef V_TRIX",
+	"			printf(\"%%4d: channel %%d unmodify\\n\", depth, from);",
+	"		#endif",
+	"	#endif",
+	"#endif",
 	"	z = qptr(from);",
 	"	j = ((Q0 *)z)->Qlen;",
 	"	if (strt) ((Q0 *)z)->Qlen = j+1;",
@@ -743,14 +924,14 @@ static char *Proto[] = {
 	"char *emalloc(unsigned long);",
 	"char *Malloc(unsigned long);",
 	"int Boundcheck(int, int, int, int, Trans *);",
-	"int addqueue(int, int);",
+	"int addqueue(int, 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);",
+	"	int creat(char *, unsigned short);",
+	"	int write(int, void *, unsigned);",
 	"#endif",
 #endif
 	"int delproc(int, int);",
@@ -767,16 +948,19 @@ static char *Proto[] = {
 	"int unsend(int);",
 	"/* void *sbrk(int); */",
 	"void Uerror(char *);",
-	"void assert(int, char *, int, int, Trans *);",
+	"void spin_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 dot_crack(int, int, Trans *);",
 	"void do_reach(void);",
 	"void pan_exit(int);",
 	"void exit(int);",
@@ -787,28 +971,29 @@ static char *Proto[] = {
 	"void putpeg(int, int);",
 	"void putrail(void);",
 	"void q_restor(void);",
-	"void retrans(int, int, int, short *, uchar *);",
+	"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);",
+	"	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];",
+	"	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,
 };
@@ -825,6 +1010,35 @@ static char *SvMap[] = {
 	"#ifdef NOVSZ",
 	"	strcat(ctd, \"-DNOVSZ \");",
 	"#endif",
+	"#ifdef REVERSE",
+	"	strcat(ctd, \"-DREVERSE \");",
+	"#endif",
+	"#ifdef T_REVERSE",
+	"	strcat(ctd, \"-DT_REVERSE \");",
+	"#endif",
+	"#ifdef T_RAND",
+	"	#if T_RAND>0",
+	"	sprintf(carg, \"-DT_RAND=%%d \", T_RAND);",
+	"	strcat(ctd, carg);",
+	"	#else",
+	"	strcat(ctd, \"-DT_RAND \");",
+	"	#endif",
+	"#endif",
+	"#ifdef P_RAND",
+	"	#if P_RAND>0",
+	"	sprintf(carg, \"-DP_RAND=%%d \", P_RAND);",
+	"	strcat(ctd, carg);",
+	"	#else",
+	"	strcat(ctd, \"-DP_RAND \");",
+	"	#endif",
+	"#endif",
+	"#ifdef BCS",
+	"	sprintf(carg, \"-DBCS=%%d \", BCS);",
+	"	strcat(ctd, carg);",
+	"#endif",
+	"#ifdef BFS",
+	"	strcat(ctd, \"-DBFS \");",
+	"#endif",
 	"#ifdef MEMLIM",
 	"	sprintf(carg, \"-DMEMLIM=%%d \", MEMLIM);",
 	"	strcat(ctd, carg);",
@@ -888,6 +1102,9 @@ static char *SvMap[] = {
 	"#ifdef CTL",
 	"	strcat(ctd, \"-DCTL \");",
 	"#endif",
+	"#ifdef TRIX",
+	"	strcat(ctd, \"-DTRIX \");",
+	"#endif",
 	"#ifdef NIBIS",
 	"	strcat(ctd, \"-DNIBIS \");",
 	"#endif",
@@ -916,7 +1133,7 @@ static char *SvMap[] = {
 	"#ifdef SVDUMP",
 	"	strcat(ctd, \"-DSVDUMP \");",
 	"#endif",
-	"#ifdef VECTORSZ",
+	"#if defined(VECTORSZ) && !defined(TRIX)",
 	"	if (VECTORSZ != 1024)",
 	"	{	sprintf(carg, \"-DVECTORSZ=%%d \", VECTORSZ);",
 	"		strcat(ctd, carg);",
@@ -931,8 +1148,35 @@ static char *SvMap[] = {
 	"#ifdef SDUMP",
 	"	strcat(ctd, \"-DSDUMP \");",
 	"#endif",
-	"#ifdef COVEST",
-	"	strcat(ctd, \"-DCOVEST \");",
+	"#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);",
 	"}",

+ 8 - 1
sys/src/cmd/spin/pangen4.c

@@ -307,6 +307,8 @@ genunio(void)
 	ntimes(tc, 0, 1, R15);
 }
 
+extern void explain(int);
+
 int
 proper_enabler(Lextok *n)
 {
@@ -321,7 +323,9 @@ proper_enabler(Lextok *n)
 			return 1;
 		return (!(n->sym->context));
 
-	case CONST:	case TIMEOUT:
+	case C_EXPR:
+	case CONST:
+	case TIMEOUT:
 		has_provided = 1;
 		return 1;
 
@@ -340,5 +344,8 @@ proper_enabler(Lextok *n)
 	default:
 		break;
 	}
+	printf("spin: saw ");
+	explain(n->ntyp);
+	printf("\n");
 	return 0;
 }

+ 19 - 20
sys/src/cmd/spin/pangen5.c

@@ -211,13 +211,18 @@ popbuild(void)
 static int
 build_step(FSM_trans *v)
 {	FSM_state *f;
-	Element	*el = v->step;
+	Element	*el;
 #if 0
 	Lextok	*lt = ZN;
 #endif
-	int	st  = v->to;
+	int	st;
 	int	r;
 
+	if (!v) return -1;
+
+	el = v->step;
+	st = v->to;
+
 	if (!el) return -1;
 
 	if (v->step->merge)
@@ -234,9 +239,7 @@ build_step(FSM_trans *v)
 	lt = v->step->n;
 	if (verbose&32)
 	{	if (++howdeep == 1)
-			printf("spin: %s, line %3d, merge:\n",
-				lt->fn->name,
-				lt->ln);
+			printf("spin: %s:%d, merge:\n", lt->fn->name, lt->ln);
 		printf("\t[%d] <seqno %d>\t", howdeep, el->seqno);
 		comment(stdout, lt, 0);
 		printf(";\n");
@@ -257,7 +260,7 @@ build_step(FSM_trans *v)
 }
 
 static void
-FSM_MERGER(char *pname_unused)	/* find candidates for safely merging steps */
+FSM_MERGER(/* char *pname */ void)	/* find candidates for safely merging steps */
 {	FSM_state *f, *g;
 	FSM_trans *t;
 	Lextok	*lt;
@@ -281,14 +284,14 @@ FSM_MERGER(char *pname_unused)	/* find candidates for safely merging steps */
 			continue;
 
 		g = fsm_tbl[t->to];
-		if (!eligible(g->t))
+		if (!g || !eligible(g->t))
 		{
 #define SINGLES
 #ifdef SINGLES
 			t->step->merge_single = t->to;
 #if 0
 			if ((verbose&32))
-			{	printf("spin: %s, line %3d, merge_single:\n\t<seqno %d>\t",
+			{	printf("spin: %s:%d, merge_single:\n\t<seqno %d>\t",
 					t->step->n->fn->name,
 					t->step->n->ln,
 					t->step->seqno);
@@ -346,9 +349,8 @@ FSM_MERGER(char *pname_unused)	/* find candidates for safely merging steps */
 #if 0
 			if ((verbose&32)
 			&& t->step->merge_start)
-			{	printf("spin: %s, line %3d, merge_START:\n\t<seqno %d>\t",
-						lt->fn->name,
-						lt->ln,
+			{	printf("spin: %s:%d, merge_START:\n\t<seqno %d>\t",
+						lt->fn->name, lt->ln,
 						t->step->seqno);
 				comment(stdout, lt, 0);
 				printf(";\n");
@@ -679,7 +681,8 @@ ana_stmnt(FSM_trans *t, Lextok *now, int usage)
 		break;
 
 	default:
-		printf("spin: bad node type %d line %d (ana_stmnt)\n", now->ntyp, now->ln);
+		printf("spin: %s:%d, bad node type %d (ana_stmnt)\n",
+			now->fn->name, now->ln, now->ntyp);
 		fatal("aborting", (char *) 0);
 	}
 }
@@ -692,10 +695,7 @@ ana_src(int dataflow, int merger)	/* called from main.c and guided.c */
 	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();
 
@@ -711,7 +711,7 @@ ana_src(int dataflow, int merger)	/* called from main.c and guided.c */
 		{	FSM_ANA();
 		}
 		if (merger)
-		{	FSM_MERGER(p->n->name);
+		{	FSM_MERGER(/* p->n->name */);
 			huntele(e, e->status, -1)->merge_in = 1; /* start-state */
 #if 0
 			printf("\n");
@@ -726,8 +726,7 @@ ana_src(int dataflow, int merger)	/* called from main.c and guided.c */
 	{
 		if (!(e->status&DONE) && (verbose&32))
 		{	printf("unreachable code: ");
-			printf("%s, line %3d:  ",
-				e->n->fn->name, e->n->ln);
+			printf("%s:%3d  ", e->n->fn->name, e->n->ln);
 			comment(stdout, e->n, 0);
 			printf("\n");
 		}
@@ -735,7 +734,7 @@ ana_src(int dataflow, int merger)	/* called from main.c and guided.c */
 	}
 	if (export_ast)
 	{	AST_slice();
-		exit(0);
+		alldone(0);	/* changed in 5.3.0: was exit(0) */
 	}
 }
 

+ 2 - 2
sys/src/cmd/spin/pangen5.h

@@ -80,7 +80,7 @@ static char *Xpt[] = {
 	"	int i, j;  uchar c;",
 	"	static uchar xwarned = 0;",
 	"",
-	"	sprintf(nm, \"%%s.xpt\", Source);",
+	"	sprintf(nm, \"%%s.xpt\", PanSource);",
 	"	if ((fd = creat(nm, 0666)) <= 0)",
 	"	if (!xwarned)",
 	"	{	xwarned = 1;",
@@ -372,7 +372,7 @@ static char *Xpt[] = {
 	"	int i, j;",
 	"",
 	"	wcnt = 0;",
-	"	sprintf(nm, \"%%s.xpt\", Source);",
+	"	sprintf(nm, \"%%s.xpt\", PanSource);",
 	"	if ((fd = open(nm, 0)) < 0)	/* O_RDONLY */",
 	"		Uerror(\"cannot open checkpoint file\");",
 	"",

+ 31 - 42
sys/src/cmd/spin/pangen6.c

@@ -90,7 +90,6 @@ static Slicer	*slicer;
 static Slicer	*rel_vars;	/* all relevant variables */
 static int	AST_Changes;
 static int	AST_Round;
-static FSM_state no_state;
 static RPN	*rpn;
 static int	in_recv = 0;
 
@@ -145,7 +144,7 @@ name_def_indices(Lextok *n, int code)
 {
 	if (!n || !n->sym) return;
 
-	if (n->sym->nel != 1)
+	if (n->sym->nel > 1 || n->sym->isarray)
 		def_use(n->lft, code);		/* process the index */
 
 	if (n->sym->type == STRUCT		/* and possible deeper ones */
@@ -503,7 +502,7 @@ AST_mutual(Lextok *a, Lextok *b, int toplevel)
 	if (strcmp(as->name, bs->name) != 0)
 		return 0;
 
-	if (as->type == STRUCT && a->rgt && b->rgt)
+	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;
@@ -545,14 +544,14 @@ AST_other(AST *a)	/* check chan params in asgns and recvs */
 			case 'r':
 				/* guess sends where name may originate */
 				for (cl = chanlist; cl; cl = cl->nxt)	/* all sends */
-				{	int a = AST_nrpar(cl->s);
-					int b = AST_nrpar(t->step->n);
-					if (a != b)	/* matching nrs of params */
+				{	int aa = AST_nrpar(cl->s);
+					int bb = AST_nrpar(t->step->n);
+					if (aa != bb)	/* matching nrs of params */
 						continue;
 
-					a = AST_ord(cl->s, cl->n);
-					b = AST_ord(t->step->n, u->n);
-					if (a != b)	/* same position in parlist */
+					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 */
@@ -692,9 +691,7 @@ AST_relevant(Lextok *n)
 	}
 
 	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)
+	{	if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
 		for (f = a->fsm; f; f = f->nxt)
 		for (t = f->t; t; t = t->nxt)
 		{	if (!(t->relevant&1))
@@ -786,10 +783,8 @@ AST_tagruns(void)
 	 */
 
 	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)
+	{	if (a->p->b == N_CLAIM || a->p->b == I_PROC
+		||  a->p->b == E_TRACE || a->p->b == N_TRACE)
 		{	a->relevant |= 1;	/* the proctype is relevant */
 			continue;
 		}
@@ -823,9 +818,9 @@ AST_report(AST *a, Element *e)	/* ALSO deduce irrelevant vars */
 		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,
+	printf("      %s:%d (state %d)",
 		e->n?e->n->fn->name:"-",
+		e->n?e->n->ln:-1,
 		e->seqno);
 	printf("	[");
 	comment(stdout, e->n, 0);
@@ -1075,7 +1070,7 @@ AST_track(Lextok *now, int code)	/* called from main.c */
 
 	case NAME:
 		name_AST_track(now, code);
-		if (now->sym->nel != 1)
+		if (now->sym->nel > 1 || now->sym->isarray)
 			AST_track(now->lft, USE|code);	/* index */
 		break;
 
@@ -1569,7 +1564,8 @@ AST_ctrl(AST *a)
 			{	t->relevant &= ~2;	/* clear mark */
 				if (verbose&32)
 				{	printf("\t\tnomark ");
-					comment(stdout, t->step->n, 0);
+					if (t->step && t->step->n)
+						comment(stdout, t->step->n, 0);
 					printf("\n");
 	}		}	}
 
@@ -1601,7 +1597,8 @@ AST_ctrl(AST *a)
 			t->relevant |= 2;	/* lift */
 			if (verbose&32)
 			{	printf("\t\t\tliftmark ");
-				comment(stdout, t->step->n, 0);
+				if (t->step && t->step->n)
+					comment(stdout, t->step->n, 0);
 				printf("\n");
 			}
 			AST_spread(a, t->to);	/* and spread to all guards */
@@ -1621,10 +1618,9 @@ 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);
+	{	if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
+		{	AST_ctrl(a);
+	}	}
 }
 
 static void
@@ -1634,9 +1630,7 @@ AST_prelabel(void)
 	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)
+	{	if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
 		for (f = a->fsm; f; f = f->nxt)
 		for (t = f->t; t; t = t->nxt)
 		{	if (t->step
@@ -1692,8 +1686,7 @@ AST_slice(void)
 	int spurious = 0;
 
 	if (!slicer)
-	{	non_fatal("no slice criteria (or no claim) specified",
-		(char *) 0);
+	{	printf("spin: warning: no slice criteria found (no assertions and no claim)\n");
 		spurious = 1;
 	}
 	AST_dorelevant();		/* mark procs refered to in remote refs */
@@ -1730,9 +1723,7 @@ 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)
+	if (p->b != N_CLAIM && p->b != E_TRACE && p->b != N_TRACE)
 	{	n_ast = (AST *) emalloc(sizeof(AST));
 		n_ast->p = p;
 		n_ast->i_st = start_state;
@@ -1809,12 +1800,10 @@ AST_par_init(void)	/* parameter passing -- hidden assignments */
 	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 */
-
+	{	if (a->p->b == N_CLAIM || a->p->b == I_PROC
+		||  a->p->b == E_TRACE || a->p->b == N_TRACE)
+		{	continue;			/* has no params */
+		}
 		cnt = 0;
 		for (f = a->p->p; f; f = f->rgt)	/* types */
 		for (t = f->lft; t; t = t->rgt)		/* formals */
@@ -1845,9 +1834,8 @@ AST_var_init(void)		/* initialized vars (not chans) - hidden assignments */
 	}	}
 
 	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 */
+	{	if (a->p->b != N_CLAIM
+		&&  a->p->b != E_TRACE && a->p->b != N_TRACE)	/* has no locals */
 		for (walk = all_names; walk; walk = walk->next)	
 		{	sp = walk->entry;
 			if (sp
@@ -2261,6 +2249,7 @@ AST_dominant(void)
 	FSM_trans *t;
 	AST *a;
 	int oi;
+	static FSM_state no_state;
 #if 0
 	find dominators
 	Aho, Sethi, & Ullman, Compilers - principles, techniques, and tools

+ 2840 - 0
sys/src/cmd/spin/pangen6.h

@@ -0,0 +1,2840 @@
+/***** spin: pangen6.h *****/
+
+/* Copyright (c) 2006-2007 by the California Institute of Technology.     */
+/* ALL RIGHTS RESERVED. United States Government Sponsorship acknowledged */
+/* Supporting routines for a multi-core extension of the SPIN software    */
+/* Developed as part of Reliable Software Engineering Project ESAS/6G     */
+/* Like all SPIN Software this software is for educational purposes only. */
+/* No guarantee whatsoever is expressed or implied by the distribution of */
+/* this code.  Permission is given to distribute this code provided that  */
+/* this introductory message is not removed and no monies are exchanged.  */
+/* Any commercial use must be negotiated with the Office of Technology    */
+/* Transfer at the California Institute of Technology.                    */
+/* Software written by Gerard J. Holzmann.  For tool documentation see:   */
+/*             http://spinroot.com/                                       */
+/* Bug-reports and/or questions can be send to: bugs@spinroot.com         */
+
+static char *Code2c[] = { /* multi-core option - Spin 5.0 and later */
+	"#if NCORE>1",
+	"#if defined(WIN32) || defined(WIN64)",
+	"	#ifndef _CONSOLE",
+	"		#define _CONSOLE",
+	"	#endif",
+	"	#ifdef WIN64",
+	"		#undef long",
+	"	#endif",
+	"	#include <windows.h>",
+	"	#ifdef WIN64",
+	"		#define long	long long",
+	"	#endif",
+	"#else",
+	"	#include <sys/ipc.h>",
+	"	#include <sys/sem.h>",
+	"	#include <sys/shm.h>",
+	"#endif",
+	"",
+	"/* code common to cygwin/linux and win32/win64: */",
+	"",
+	"#ifdef VERBOSE",
+	"	#define VVERBOSE	(1)",
+	"#else",
+	"	#define VVERBOSE	(0)",
+	"#endif",
+	"",
+	"/* the following values must be larger than 256 and must fit in an int */",
+	"#define QUIT		1024	/* terminate now command */",
+	"#define QUERY		 512	/* termination status query message */",
+	"#define QUERY_F	 513	/* query failed, cannot quit */",
+	"",
+	"#define GN_FRAMES	(int) (GWQ_SIZE / (double) sizeof(SM_frame))",
+	"#define LN_FRAMES	(int) (LWQ_SIZE / (double) sizeof(SM_frame))",
+	"",
+	"#ifndef VMAX",
+	"	#define VMAX	VECTORSZ",
+	"#endif",
+	"#ifndef PMAX",
+	"	#define PMAX	64",
+	"#endif",
+	"#ifndef QMAX",
+	"	#define QMAX	64",
+	"#endif",
+	"",
+	"#if VECTORSZ>32000",
+	"	#define OFFT	int",
+	"#else",
+	"	#define OFFT	short",
+	"#endif",
+	"",
+	"#ifdef SET_SEG_SIZE",
+	"	/* no longer usefule -- being recomputed for local heap size anyway */",
+	"	double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.);",
+	"#else",
+	"	double SEG_SIZE = (1048576.*1024.);	/* 1GB default shared memory pool segments */",
+	"#endif",
+	"",
+	"double LWQ_SIZE = 0.; /* initialized in main */",
+	"",
+	"#ifdef SET_WQ_SIZE",
+	"	#ifdef NGQ",
+	"	#warning SET_WQ_SIZE applies to global queue -- ignored",
+	"	double GWQ_SIZE = 0.;",
+	"	#else",
+	"	double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.);",
+	"	/* must match the value in pan_proxy.c, if used */",
+	"	#endif",
+	"#else",
+	"	#ifdef NGQ",
+	"	double GWQ_SIZE = 0.;",
+	"	#else",
+	"	double GWQ_SIZE = (128.*1048576.);	/* 128 MB default queue sizes */",
+	"	#endif",
+	"#endif",
+	"",
+	"/* Crash Detection Parameters */",
+	"#ifndef ONESECOND",
+	"	#define ONESECOND	(1<<25)", /* name is somewhat of a misnomer */
+	"#endif",
+	"#ifndef SHORT_T",
+	"	#define SHORT_T	(0.1)",
+	"#endif",
+	"#ifndef LONG_T",
+	"	#define LONG_T	(600)",
+	"#endif",
+	"",
+	"double OneSecond   = (double) (ONESECOND); /* waiting for a free slot -- checks crash */",
+	"double TenSeconds  = 10. * (ONESECOND);    /* waiting for a lock -- check for a crash */",
+	"",
+	"/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */",
+	"double Delay       = ((double) SHORT_T) * (ONESECOND);	/* termination detection trigger */",
+	"double OneHour     = ((double) LONG_T) * (ONESECOND);	/* timeout termination detection */",
+	"",
+	"typedef struct SM_frame     SM_frame;",
+	"typedef struct SM_results   SM_results;",
+	"typedef struct sh_Allocater sh_Allocater;",
+	"",
+	"struct SM_frame {			/* about 6K per slot */",
+	"	volatile int	m_vsize;	/* 0 means free slot */",
+	"	volatile int	m_boq;		/* >500 is a control message */",
+	"#ifdef FULL_TRAIL",
+	"	volatile struct Stack_Tree *m_stack;	/* ptr to previous state */",
+	"#endif",
+	"	volatile uchar	m_tau;",
+	"	volatile uchar	m_o_pm;",
+	"	volatile int	nr_handoffs;	/* to compute real_depth */",
+	"	volatile char	m_now     [VMAX];",
+	"	volatile char	m_Mask    [(VMAX + 7)/8];",
+	"	volatile OFFT	m_p_offset[PMAX];",
+	"	volatile OFFT	m_q_offset[QMAX];",
+	"	volatile uchar	m_p_skip  [PMAX];",
+	"	volatile uchar	m_q_skip  [QMAX];",
+	"#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+	"	volatile uchar	m_c_stack [StackSize];",
+		 /* captures contents of c_stack[] for unmatched objects */
+	"#endif",
+	"};",
+	"",
+	"int	proxy_pid;		/* id of proxy if nonzero -- receive half */",
+	"int	store_proxy_pid;",
+	"short	remote_party;",
+	"int	proxy_pid_snd;		/* id of proxy if nonzero -- send half */",
+	"char	o_cmdline[512];		/* to pass options to children */",
+	"",
+	"int	iamin[CS_NR+NCORE];		/* non-shared */",
+	"",
+"#if defined(WIN32) || defined(WIN64)",
+	"int tas(volatile LONG *);",
+	"",
+	"HANDLE		proxy_handle_snd;	/* for Windows Create and Terminate */",
+	"",
+	"struct sh_Allocater {			/* shared memory for states */",
+	"	volatile char	*dc_arena;	/* to allocate states from */",
+	"	volatile long	 pattern;	/* to detect overruns */",
+	"	volatile long	 dc_size;	/* nr of bytes left */",
+	"	volatile void	*dc_start;	/* where memory segment starts */",
+	"	volatile void	*dc_id;		/* to attach, detach, remove shared memory segments */",
+	"	volatile sh_Allocater *nxt;	/* linked list of pools */",
+	"};",
+	"DWORD		worker_pids[NCORE];	/* root mem of pids of all workers created */",
+	"HANDLE		worker_handles[NCORE];	/* for windows Create and Terminate */",
+	"void *		shmid      [NR_QS];	/* return value from CreateFileMapping */",
+	"void *		shmid_M;		/* shared mem for state allocation in hashtable */",
+	"",
+	"#ifdef SEP_STATE",
+	"	void *shmid_X;",
+	"#else",
+	"	void *shmid_S;			/* shared bitstate arena or hashtable */",
+	"#endif",
+"#else",
+	"int tas(volatile int *);",
+	"",
+	"struct sh_Allocater {			/* shared memory for states */",
+	"	volatile char	*dc_arena;	/* to allocate states from */",
+	"	volatile long	 pattern;	/* to detect overruns */",
+	"	volatile long	 dc_size;	/* nr of bytes left */",
+	"	volatile char	*dc_start;	/* where memory segment starts */",
+	"	volatile int	dc_id;		/* to attach, detach, remove shared memory segments */",
+	"	volatile sh_Allocater *nxt;	/* linked list of pools */",
+	"};",
+	"",
+	"int	worker_pids[NCORE];	/* root mem of pids of all workers created */",
+	"int	shmid      [NR_QS];	/* return value from shmget */",
+	"int	nibis = 0;		/* set after shared mem has been released */",
+	"int	shmid_M;		/* shared mem for state allocation in hashtable */",
+	"#ifdef SEP_STATE",
+	"	long	shmid_X;",
+	"#else",
+	"	int	shmid_S;	/* shared bitstate arena or hashtable */",
+	"	volatile sh_Allocater	*first_pool;	/* of shared state memory */",
+	"	volatile sh_Allocater	*last_pool;",
+	"#endif", /* SEP_STATE */
+"#endif", /* WIN32 || WIN64 */
+	"",
+	"struct SM_results {			/* for shuttling back final stats */",
+	"	volatile int	m_vsize;	/* avoid conflicts with frames */",
+	"	volatile int	m_boq;		/* these 2 fields are not written in record_info */",
+	"	/* probably not all fields really need to be volatile */",
+	"	volatile double	m_memcnt;",
+	"	volatile double	m_nstates;",
+	"	volatile double	m_truncs;",
+	"	volatile double	m_truncs2;",
+	"	volatile double	m_nShadow;",
+	"	volatile double	m_nlinks;",
+	"	volatile double	m_ngrabs;",
+	"	volatile double	m_nlost;",
+	"	volatile double	m_hcmp;",
+	"	volatile double	m_frame_wait;",
+	"	volatile int	m_hmax;",
+	"	volatile int	m_svmax;",
+	"	volatile int	m_smax;",
+	"	volatile int	m_mreached;",
+	"	volatile int	m_errors;",
+	"	volatile int	m_VMAX;",
+	"	volatile short	m_PMAX;",
+	"	volatile short	m_QMAX;",
+	"	volatile uchar	m_R;		/* reached info for all proctypes */",
+	"};",
+	"",
+	"int		core_id = 0;		/* internal process nr, to know which q to use */",
+	"unsigned long	nstates_put = 0;	/* statistics */",
+	"unsigned long	nstates_get = 0;",
+	"int		query_in_progress = 0;	/* termination detection */",
+	"",
+	"double		free_wait  = 0.;	/* waiting for a free frame */",
+	"double		frame_wait = 0.;	/* waiting for a full frame */",
+	"double		lock_wait  = 0.;	/* waiting for access to cs */",
+	"double		glock_wait[3];	/* waiting for access to global lock */",
+	"",
+	"char		*sprefix = \"rst\";",
+	"uchar		was_interrupted, issued_kill, writing_trail;",
+	"",
+	"static SM_frame cur_Root;		/* current root, to be safe with error trails */",
+	"",
+	"SM_frame	*m_workq   [NR_QS];	/* per cpu work queues + global q */",
+	"char		*shared_mem[NR_QS];	/* return value from shmat */",
+	"#ifdef SEP_HEAP",
+	"char		*my_heap;",
+	"long		 my_size;",
+	"#endif",
+	"volatile sh_Allocater	*dc_shared;	/* assigned at initialization */",
+	"",
+	"static int	vmax_seen, pmax_seen, qmax_seen;",
+	"static double	gq_tries, gq_hasroom, gq_hasnoroom;",
+	"",
+	"volatile int *prfree;",	/* [NCORE] */
+	"volatile int *prfull;",	/* [NCORE] */
+	"volatile int *prcnt;",		/* [NCORE] */
+	"volatile int *prmax;",		/* [NCORE] */
+	"",
+	"volatile int	*sh_lock;	/* mutual exclusion locks - in shared memory */",
+	"volatile double *is_alive;	/* to detect when processes crash */",
+	"volatile int    *grfree, *grfull, *grcnt, *grmax;	/* access to shared global q */",
+	"volatile double *gr_readmiss, *gr_writemiss;",
+	"static   int	lrfree;		/* used for temporary recording of slot */",
+	"static   int dfs_phase2;",
+	"",
+	"void mem_put(int);		/* handoff state to other cpu */",
+	"void mem_put_acc(void);	/* liveness mode */",
+	"void mem_get(void);		/* get state from work queue  */",
+	"void sudden_stop(char *);",
+	"#if 0",
+	"void enter_critical(int);",
+	"void leave_critical(int);",
+	"#endif",
+	"",
+	"void",
+	"record_info(SM_results *r)",
+	"{	int i;",
+	"	uchar *ptr;",
+	"",
+	"#ifdef SEP_STATE",
+	"	if (0)",
+	"	{	cpu_printf(\"nstates %%g nshadow %%g -- memory %%-6.3f Mb\\n\",",
+	"			nstates, nShadow, memcnt/(1048576.));",
+	"	}",
+	"	r->m_memcnt = 0;",
+	"#else",
+	"	#ifdef BITSTATE",
+		"	r->m_memcnt = 0; /* it's shared */",
+	"	#endif",
+	"	r->m_memcnt = memcnt;",
+	"#endif",
+	"	if (a_cycles && core_id == 1)",
+	"	{	r->m_nstates  = nstates;",
+	"		r->m_nShadow  = nstates;",
+	"	} else",
+	"	{	r->m_nstates  = nstates;",
+	"		r->m_nShadow  = nShadow;",
+	"	}",
+	"	r->m_truncs   = truncs;",
+	"	r->m_truncs2  = truncs2;",
+	"	r->m_nlinks   = nlinks;",
+	"	r->m_ngrabs   = ngrabs;",
+	"	r->m_nlost    = nlost;",
+	"	r->m_hcmp     = hcmp;",
+	"	r->m_frame_wait = frame_wait;",
+	"	r->m_hmax     = hmax;",
+	"	r->m_svmax    = svmax;",
+	"	r->m_smax     = smax;",
+	"	r->m_mreached = mreached;",
+	"	r->m_errors   = errors;",
+	"	r->m_VMAX     = vmax_seen;",
+	"	r->m_PMAX     = (short) pmax_seen;",
+	"	r->m_QMAX     = (short) qmax_seen;",
+	"	ptr = (uchar *) &(r->m_R);",
+	"	for (i = 0; i <= _NP_; i++)	/* all proctypes */",
+	"	{	memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar));",
+	"		ptr += NrStates[i]*sizeof(uchar);",
+	"	}",
+	"	if (verbose>1)",
+	"	{	cpu_printf(\"Put Results nstates %%g (sz %%d)\\n\", nstates, ptr - &(r->m_R));",
+	"	}",
+	"}",
+	"",
+	"void snapshot(void);",
+	"",
+	"void",
+	"retrieve_info(SM_results *r)",
+	"{	int i, j;",
+	"	volatile uchar *ptr;",
+	"",
+	"	snapshot();	/* for a final report */",
+	"",
+	"	enter_critical(GLOBAL_LOCK);",
+	"#ifdef SEP_HEAP",
+	"	if (verbose)",
+	"	{	printf(\"cpu%%d: local heap-left %%ld KB (%%d MB)\\n\",",
+	"			core_id, (long) (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\", (int) (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>%%ld)\\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., (int) (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\",",
+	"				(int) 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 %%ld states from queue\\n\",",
+	"			core_id, nstates_get);",
+	"	}",
+	"}",
+	"",
+	"extern void give_up(int);",
+	"extern void Read_Queue(int);",
+	"",
+	"void",
+	"mem_get(void)",
+	"{	SM_frame   *f;",
+	"	int is_parent;",
+	"",
+	"#if defined(MA) && !defined(SEP_STATE)",
+	"	#error MA without SEP_STATE is not supported with multi-core",
+	"#endif",
+	"#ifdef BFS",
+	"	#error BFS is not supported with multi-core",
+	"#endif",
+	"#ifdef SC",
+	"	#error SC is not supported with multi-core",
+	"#endif",
+	"	init_shm();	/* we are single threaded when this starts */",
+	"",
+	"	if (core_id == 0 && verbose)",
+	"	{	printf(\"cpu0: step 4: calling fork()\\n\");",
+	"	}",
+	"	fflush(stdout);",
+	"",
+	"/*	if NCORE > 1 the child or the parent should fork N-1 more times",
+	" *	the parent is the only process with core_id == 0 and is_parent > 0",
+	" *	the workers have is_parent = 0 and core_id = 1..NCORE-1",
+	" */",
+	"	if (core_id == 0)",
+	"	{	worker_pids[0] = getpid();	/* for completeness */",
+	"		while (++core_id < NCORE)	/* first worker sees core_id = 1 */",
+	"		{	is_parent = fork();",
+	"			if (is_parent == -1)",
+	"			{	Uerror(\"fork failed\");",
+	"			}",
+	"			if (is_parent == 0)	/* this is a worker process */",
+	"			{	if (proxy_pid == core_id)	/* always non-zero */",
+	"				{	start_proxy(\"-r\", 0);	/* no return */",
+	"				}",
+	"				goto adapt;	/* root process continues spawning */",
+	"			}",
+	"			worker_pids[core_id] = is_parent;",
+	"		}",
+	"		/* note that core_id is now NCORE */",
+	"		if (proxy_pid > 0 && proxy_pid < NCORE)", /* add send-half of proxy */
+	"		{	proxy_pid_snd = fork();",
+	"			if (proxy_pid_snd == -1)",
+	"			{	Uerror(\"proxy fork failed\");",
+	"			}",
+	"			if (proxy_pid_snd == 0)",
+	"			{	start_proxy(\"-s\", worker_pids[proxy_pid]); /* no return */",
+	"		}	} /* else continue */",
+
+	"		if (is_parent > 0)",
+	"		{	core_id = 0;	/* reset core_id for root process */",
+	"		}",
+	"	} else	/* worker */",
+	"	{	static char db0[16];	/* good for up to 10^6 cores */",
+	"		static char db1[16];",
+	"adapt:		tprefix = db0; sprefix = db1;",
+	"		sprintf(tprefix, \"cpu%%d_trail\", core_id);",
+	"		sprintf(sprefix, \"cpu%%d_rst\", core_id);",
+	"		memcnt = 0;	/* count only additionally allocated memory */",
+	"	}",
+	"	signal(SIGINT, give_up);",
+	"",
+	"	if (proxy_pid == 0)		/* not in a cluster setup, pan_proxy must attach */",
+	"	{	rm_shared_segments();	/* mark all shared segments for removal on exit */",
+	"	}", /* doing it early means less chance of being unable to do this */
+	"	if (verbose)",
+	"	{	cpu_printf(\"starting core_id %%d -- pid %%d\\n\", core_id, getpid());",
+	"	}",
+
+	"#if defined(SEP_HEAP) && !defined(SEP_STATE)",	/* set my_heap and adjust dc_shared */
+	"	{	int i;",
+	"		volatile sh_Allocater *ptr;",
+	"		ptr = first_pool;",
+	"		for (i = 0; i < NCORE  && ptr != NULL; i++)",
+	"		{	if (i == core_id)",
+	"			{	my_heap = (char *) ptr->dc_arena;",
+	"				my_size = (long) ptr->dc_size;",
+	"				if (verbose)",
+	"				cpu_printf(\"local heap %%ld MB\\n\", my_size/(1048576));",
+	"				break;",
+	"			}",
+	"			ptr = ptr->nxt; /* local */",
+	"		}",
+	"		if (my_heap == NULL)",
+	"		{	printf(\"cpu%%d: no local heap\\n\", core_id);",
+	"			pan_exit(1);",
+	"		} /* else */",
+	"	#if defined(CYGWIN) || defined(__CYGWIN__)",
+	"		ptr = first_pool;",
+	"		for (i = 0; i < NCORE  && ptr != NULL; i++)",
+	"		{	ptr = ptr->nxt; /* local */",
+	"		}",
+	"		dc_shared = ptr; /* any remainder */",
+	"	#else",
+	"		dc_shared = NULL; /* used all mem for local heaps */",
+	"	#endif",
+	"	}",
+	"#endif",
+
+	"	if (core_id == 0 && !remote_party)",
+	"	{	new_state();		/* cpu0 explores root */",
+	"		if (verbose)",
+	"		cpu_printf(\"done with 1st dfs, nstates %%g (put %%d states), read q\\n\",",
+	"			nstates, nstates_put);",
+	"		dfs_phase2 = 1;",
+	"	}",
+	"	Read_Queue(core_id);	/* all cores */",
+	"",
+	"	if (verbose)",
+	"	{	cpu_printf(\"put %%6d states into queue -- got %%6d\\n\",",
+	"			nstates_put, nstates_get);",
+	"	}",
+	"	if (proxy_pid != 0)",
+	"	{	rm_shared_segments();",
+	"	}",
+	"	done = 1;",
+	"	wrapup();",
+	"	exit(0);",
+	"}",
+	"",
+	"#else",
+	"int unpack_state(SM_frame *, int);",
+	"#endif",
+	"",
+	"struct H_el *",
+	"grab_shared(int n)",
+	"{",
+	"#ifndef SEP_STATE",
+	"	char *rval = (char *) 0;",
+	"",
+	"	if (n == 0)",
+	"	{	printf(\"cpu%%d: grab shared zero\\n\", core_id); fflush(stdout);",
+	"		return (struct H_el *) rval;",
+	"	} else if (n&(sizeof(void *)-1))",
+	"	{	n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */",
+	"	}",
+	"",
+	"#ifdef SEP_HEAP",
+	"	/* no locking */",
+	"	if (my_heap != NULL && my_size > n)",
+	"	{	rval = my_heap;",
+	"		my_heap += n;",
+	"		my_size -= n;",
+	"		goto done;",
+	"	}",
+	"#endif",
+	"",
+	"	if (!dc_shared)",
+	"	{	sudden_stop(\"pan: out of memory\");",
+	"	}",
+	"",
+	"	/* another lock is always already in effect when this is called */",
+	"	/* but not always the same lock -- i.e., on different parts of the hashtable */",
+	"	enter_critical(GLOBAL_LOCK);	/* this must be independently mutex */",
+	"#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64)",
+	"	{	static int noted = 0;",
+	"		if (!noted)",
+	"		{	noted = 1;",
+	"			printf(\"cpu%%d: global heap has %%ld bytes left, needed %%d\\n\",",
+	"				core_id, dc_shared?dc_shared->dc_size:0, n);",
+	"	}	}",
+	"#endif",
+	"#if 0",	/* for debugging */
+	"		if (dc_shared->pattern != 1234567)",
+	"		{	leave_critical(GLOBAL_LOCK);",
+	"			Uerror(\"overrun -- memory corruption\");",
+	"		}",
+	"#endif",
+	"		if (dc_shared->dc_size < n)",
+	"		{	if (verbose)",
+	"			{ printf(\"Next Pool %%g Mb + %%d\\n\", memcnt/(1048576.), n);",
+	"			}",
+	"			if (dc_shared->nxt == NULL",
+	"			||  dc_shared->nxt->dc_arena == NULL",
+	"			||  dc_shared->nxt->dc_size < n)",
+	"			{	printf(\"cpu%%d: memcnt %%g Mb + wanted %%d bytes more\\n\",",
+	"					core_id, memcnt / (1048576.), n);",
+	"				leave_critical(GLOBAL_LOCK);",
+	"				sudden_stop(\"out of memory -- aborting\");",
+	"				wrapup();	/* exits */",
+	"			} else",
+	"			{	dc_shared = (sh_Allocater *) dc_shared->nxt;",
+	"		}	}",
+	"",
+	"		rval = (char *) dc_shared->dc_arena;",
+	"		dc_shared->dc_arena += n;",
+	"		dc_shared->dc_size  -= (long) n;",
+	"#if 0",
+	"		if (VVERBOSE)",
+	"		printf(\"cpu%%d grab shared (%%d bytes) -- %%ld left\\n\",",
+	"			core_id, n, dc_shared->dc_size);",
+	"#endif",
+	"	leave_critical(GLOBAL_LOCK);",
+	"done:",
+	"	memset(rval, 0, n);",
+	"	memcnt += (double) n;",
+	"",
+	"	return (struct H_el *) rval;",
+	"#else",
+	"	return (struct H_el *) emalloc(n);",
+	"#endif",
+	"}",
+	"",
+	"SM_frame *",
+	"Get_Full_Frame(int n)",
+	"{	SM_frame *f;",
+	"	double cnt_start = frame_wait;",
+	"",
+	"	f = &m_workq[n][prfull[n]];",
+	"	while (f->m_vsize == 0)	/* await full slot LOCK : full frame */",
+	"	{	iam_alive();",
+	"#ifndef NGQ",
+	"	#ifndef SAFETY",
+	"		if (!a_cycles || core_id != 0)",
+	"	#endif",
+	"		if (*grcnt > 0)	/* accessed outside lock, but safe even if wrong */",
+	"		{	enter_critical(GQ_RD);	/* gq - read access */",
+	"			if (*grcnt > 0)		/* could have changed */",
+	"			{	f = &m_workq[NCORE][*grfull];	/* global q */",
+	"				if (f->m_vsize == 0)",
+	"				{	/* writer is still filling the slot */",
+	"					*gr_writemiss++;",
+	"					f = &m_workq[n][prfull[n]]; /* reset */",
+	"				} else",
+	"				{	*grfull = (*grfull+1) %% (GN_FRAMES);",
+	"						enter_critical(GQ_WR);",
+	"						*grcnt = *grcnt - 1;",
+	"						leave_critical(GQ_WR);",
+	"					leave_critical(GQ_RD);",
+	"					return f;",
+	"			}	}",
+	"			leave_critical(GQ_RD);",
+	"		}",
+	"#endif",
+	"		if (frame_wait++ - cnt_start > Delay)",
+	"		{	if (0)", /* too frequent to enable this one */
+	"			{	cpu_printf(\"timeout on q%%d -- %%u -- query %%d\\n\",",
+	"					n, f, query_in_progress);",
+	"			}",
+	"			return (SM_frame *) 0;	/* timeout */",
+	"	}	}",
+	"	iam_alive();",
+	"	if (VVERBOSE) cpu_printf(\"got frame from q%%d\\n\", n);",
+	"	prfull[n] = (prfull[n] + 1) %% (LN_FRAMES);",
+	"	enter_critical(QLOCK(n));",
+	"		prcnt[n]--; /* lock out increments */",
+	"	leave_critical(QLOCK(n));",
+	"	return f;",
+	"}",
+	"",
+	"SM_frame *",
+	"Get_Free_Frame(int n)",
+	"{	SM_frame *f;",
+	"	double cnt_start = free_wait;",
+	"",
+	"	if (VVERBOSE) { cpu_printf(\"get free frame from q%%d\\n\", n); }",
+	"",
+	"	if (n == NCORE)	/* global q */",
+	"	{	f = &(m_workq[n][lrfree]);",
+	"	} else",
+	"	{	f = &(m_workq[n][prfree[n]]);",
+	"	}",
+	"	while (f->m_vsize != 0)	/* await free slot LOCK : free slot */",
+	"	{	iam_alive();",
+	"		if (free_wait++ - cnt_start > OneSecond)",
+	"		{	if (verbose)",
+	"			{	cpu_printf(\"timeout waiting for free slot q%%d\\n\", n);",
+	"			}",
+	"			cnt_start = free_wait;",
+	"			if (someone_crashed(1))",
+	"			{	printf(\"cpu%%d: search terminated\\n\", core_id);",
+	"				sudden_stop(\"get free frame\");",
+	"				pan_exit(1);",
+	"	}	}	}",
+	"	if (n != NCORE)",
+	"	{	prfree[n] = (prfree[n] + 1) %% (LN_FRAMES);",
+	"		enter_critical(QLOCK(n));",
+	"			prcnt[n]++; /* lock out decrements */",
+	"			if (prmax[n] < prcnt[n])",
+	"			{	prmax[n] = prcnt[n];",
+	"			}",
+	"		leave_critical(QLOCK(n));",
+	"	}",
+	"	return f;",
+	"}",
+	"",
+	"#ifndef NGQ",
+	"int",
+	"GlobalQ_HasRoom(void)",
+	"{	int rval = 0;",
+	"",
+	"	gq_tries++;",
+	"	if (*grcnt < GN_FRAMES) /* there seems to be room */",
+	"	{	enter_critical(GQ_WR);	/* gq write access */",
+	"		if (*grcnt < GN_FRAMES)",
+	"		{	if (m_workq[NCORE][*grfree].m_vsize != 0)",
+	"			{	/* can happen if reader is slow emptying slot */",
+	"				*gr_readmiss++;",
+	"				goto out; /* dont wait: release lock and return */",
+	"			}",
+	"			lrfree = *grfree;	/* Get_Free_Frame use lrfree in this mode */",
+	"			*grfree = (*grfree + 1) %% GN_FRAMES;",	/* next process looks at next slot */
+	"			*grcnt = *grcnt + 1;	/* count nr of slots filled -- no additional lock needed */",
+	"			if (*grmax < *grcnt) *grmax = *grcnt;",
+	"			leave_critical(GQ_WR);	/* for short lock duration */",
+	"			gq_hasroom++;",
+	"			mem_put(NCORE);		/* copy state into reserved slot */",
+	"			rval = 1;		/* successfull handoff */",
+	"		} else",
+	"		{	gq_hasnoroom++;",
+	"out:			leave_critical(GQ_WR);",	/* should be rare */
+	"	}	}",
+	"	return rval;",
+	"}",
+	"#endif",
+	"",
+	"int",
+	"unpack_state(SM_frame *f, int from_q)",
+	"{	int i, j;",
+	"	static struct H_el D_State;",
+	"",
+	"	if (f->m_vsize > 0)",
+	"	{	boq   = f->m_boq;",
+	"		if (boq > 256)",
+	"		{	cpu_printf(\"saw control %%d, expected state\\n\", boq);",
+	"			return 0;",
+	"		}",
+	"		vsize = f->m_vsize;",
+	"correct:",
+	"		memcpy((uchar *) &now, (uchar *) f->m_now, vsize);",
+	"		for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+	"		{	Mask[i] = (f->m_Mask[i/8] & (1<<j)) ? 1 : 0;",
+	"		}",
+	"		if (now._nr_pr > 0)",
+	"		{	memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT));",
+	"			memcpy((uchar *) proc_skip,   (uchar *) f->m_p_skip,   now._nr_pr * sizeof(uchar));",
+	"		}",
+	"		if (now._nr_qs > 0)",
+	"		{	memcpy((uchar *) q_offset,    (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT));",
+	"			memcpy((uchar *) q_skip,      (uchar *) f->m_q_skip,   now._nr_qs * sizeof(uchar));",
+	"		}",
+	"#ifndef NOVSZ",
+	"		if (vsize != now._vsz)",
+	"		{	cpu_printf(\"vsize %%d != now._vsz %%d (type %%d) %%d\\n\",",
+	"				vsize, now._vsz, f->m_boq, f->m_vsize);",
+	"			vsize = now._vsz;",
+	"			goto correct;	/* rare event: a race */",
+	"		}",
+	"#endif",
+	"		hmax = max(hmax, vsize);",
+	"",
+	"		if (f != &cur_Root)",
+	"		{	memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame));",
+	"		}",
+	"",
+	"		if (((now._a_t) & 1) == 1)	/* i.e., when starting nested DFS */",
+	"		{	A_depth = depthfound = 0;",
+	"			memcpy((uchar *)&A_Root, (uchar *)&now, vsize);",
+	"		}",
+	"		nr_handoffs = f->nr_handoffs;",
+	"	} else",
+	"	{	cpu_printf(\"pan: state empty\\n\");",
+	"	}",
+	"",
+	"	depth = 0;",
+	"	trpt = &trail[1];",
+	"	trpt->tau    = f->m_tau;",
+	"	trpt->o_pm   = f->m_o_pm;",
+	"",
+	"	(trpt-1)->ostate = &D_State; /* stub */",
+	"	trpt->ostate = &D_State;",
+	"",
+	"#ifdef FULL_TRAIL",
+	"	if (upto > 0)",
+	"	{	stack_last[core_id] = (Stack_Tree *) f->m_stack;",
+	"	}",
+	"	#if defined(VERBOSE)",
+	"	if (stack_last[core_id])",
+	"	{	cpu_printf(\"%%d: UNPACK -- SET m_stack %%u (%%d,%%d)\\n\",",
+	"			depth, stack_last[core_id], stack_last[core_id]->pr,",
+	"			stack_last[core_id]->t_id);",
+	"	}",
+	"	#endif",
+	"#endif",
+	"",
+	"	if (!trpt->o_t)",
+	"	{	static Trans D_Trans;",
+	"		trpt->o_t = &D_Trans;",
+	"	}",
+	"",
+	"	#ifdef VERI",
+	"	if ((trpt->tau & 4) != 4)",
+	"	{	trpt->tau |= 4;	/* the claim moves first */",
+	"		cpu_printf(\"warning: trpt was not up to date\\n\");",
+	"	}",
+	"	#endif",
+	"",
+	"	for (i = 0; i < (int) now._nr_pr; i++)",
+	"	{	P0 *ptr = (P0 *) pptr(i);",
+	"	#ifndef NP",
+	"		if (accpstate[ptr->_t][ptr->_p])",
+	"		{	trpt->o_pm |= 2;",
+	"		}",
+	"	#else",
+	"		if (progstate[ptr->_t][ptr->_p])",
+	"		{	trpt->o_pm |= 4;",
+	"		}",
+	"	#endif",
+	"	}",
+	"",
+	"	#ifdef EVENT_TRACE",
+	"		#ifndef NP",
+	"			if (accpstate[EVENT_TRACE][now._event])",
+	"			{	trpt->o_pm |= 2;",
+	"			}",
+	"		#else",
+	"			if (progstate[EVENT_TRACE][now._event])",
+	"			{	trpt->o_pm |= 4;",
+	"			}",
+	"		#endif",
+	"	#endif",
+	"",
+	"	#if defined(C_States) && (HAS_TRACK==1)",
+	"		/* restore state of tracked C objects */",
+	"		c_revert((uchar *) &(now.c_state[0]));",
+	"		#if (HAS_STACK==1)",
+	"		c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */",
+	"		#endif",
+	"	#endif",
+	"	return 1;",
+	"}",
+	"",
+	"void",
+	"write_root(void)	/* for trail file */",
+	"{	int fd;",
+	"",
+	"	if (iterative == 0 && Nr_Trails > 1)",
+	"		sprintf(fnm, \"%%s%%d.%%s\", TrailFile, Nr_Trails-1, sprefix);",
+	"	else",
+	"		sprintf(fnm, \"%%s.%%s\", TrailFile, sprefix);",
+	"",
+	"	if (cur_Root.m_vsize == 0)",
+	"	{	(void) unlink(fnm); /* remove possible old copy */",
+	"		return;	/* its the default initial state */",
+	"	}",
+	"",
+	"	if ((fd = creat(fnm, TMODE)) < 0)",
+	"	{	char *q;",
+	"		if ((q = strchr(TrailFile, \'.\')))",
+	"		{	*q = \'\\0\';		/* strip .pml */",
+	"			if (iterative == 0 && Nr_Trails-1 > 0)",
+	"				sprintf(fnm, \"%%s%%d.%%s\", TrailFile, Nr_Trails-1, sprefix);",
+	"			else",
+	"				sprintf(fnm, \"%%s.%%s\", TrailFile, sprefix);",
+	"			*q = \'.\';",
+	"			fd = creat(fnm, TMODE);",
+	"		}",
+	"		if (fd < 0)",
+	"		{	cpu_printf(\"pan: cannot create %%s\\n\", fnm);",
+	"			perror(\"cause\");",
+	"			return;",
+	"	}	}",
+	"",
+	"	if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))",
+	"	{	cpu_printf(\"pan: error writing %%s\\n\", fnm);",
+	"	} else",
+	"	{	cpu_printf(\"pan: wrote %%s\\n\", fnm);",
+	"	}",
+	"	close(fd);",
+	"}",
+	"",
+	"void",
+	"set_root(void)",
+	"{	int fd;",
+	"	char *q;",
+	"	char MyFile[512];",	/* enough to hold a reasonable pathname */
+	"	char MySuffix[16];",
+	"	char *ssuffix = \"rst\";",
+	"	int  try_core = 1;",
+	"",
+	"	strcpy(MyFile, TrailFile);",
+	"try_again:",
+	"	if (whichtrail > 0)",
+	"	{	sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, ssuffix);",
+	"		fd = open(fnm, O_RDONLY, 0);",
+	"		if (fd < 0 && (q = strchr(MyFile, \'.\')))",
+	"		{	*q = \'\\0\';	/* strip .pml */",
+	"			sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, ssuffix);",
+	"			*q = \'.\';",
+	"			fd = open(fnm, O_RDONLY, 0);",
+	"		}",
+	"	} else",
+	"	{	sprintf(fnm, \"%%s.%%s\", MyFile, ssuffix);",
+	"		fd = open(fnm, O_RDONLY, 0);",
+	"		if (fd < 0 && (q = strchr(MyFile, \'.\')))",
+	"		{	*q = \'\\0\';	/* strip .pml */",
+	"			sprintf(fnm, \"%%s.%%s\", MyFile, ssuffix);",
+	"			*q = \'.\';",
+	"			fd = open(fnm, O_RDONLY, 0);",
+	"	}	}",
+	"",
+	"	if (fd < 0)",
+	"	{	if (try_core < NCORE)",
+	"		{	ssuffix = MySuffix;",
+	"			sprintf(ssuffix, \"cpu%%d_rst\", try_core++);",
+	"			goto try_again;",
+	"		}",
+	"		cpu_printf(\"no file '%%s.rst' or '%%s' (not an error)\\n\", MyFile, fnm);",
+	"	} else",
+	"	{	if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))",
+	"		{	cpu_printf(\"read error %%s\\n\", fnm);",
+	"			close(fd);",
+	"			pan_exit(1);",
+	"		}",
+	"		close(fd);",
+	"		(void) unpack_state(&cur_Root, -2);",
+	"#ifdef SEP_STATE",
+	"		cpu_printf(\"partial trail -- last few steps only\\n\");",
+	"#endif",
+	"		cpu_printf(\"restored root from '%%s'\\n\", fnm);",
+	"		printf(\"=====State:=====\\n\");",
+	"		{	int i, j; P0 *z;",
+	"			for (i = 0; i < now._nr_pr; i++)",
+	"			{	z = (P0 *)pptr(i);",
+	"				printf(\"proc %%2d (%%s) \", i, procname[z->_t]);",
+
+	"				for (j = 0; src_all[j].src; j++)",
+	"				if (src_all[j].tp == (int) z->_t)",
+	"				{	printf(\" %%s:%%d \",",
+	"						PanSource, src_all[j].src[z->_p]);",
+	"					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\", (int) vsize);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"	if (now._nr_pr > PMAX)",
+	"	{	printf(\"pan: recompile with -DPMAX=N with N >= %%d\\n\", now._nr_pr);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"	if (now._nr_qs > QMAX)",
+	"	{	printf(\"pan: recompile with -DQMAX=N with N >= %%d\\n\", now._nr_qs);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"	if (vsize > vmax_seen) vmax_seen = vsize;",
+	"	if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr;",
+	"	if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs;",
+	"",
+	"	f = Get_Free_Frame(q);	/* not called in likely deadlock states */",
+	"	if (!f) { Uerror(\"Fatal3: no free slot\"); }",
+	"",
+	"	if (VVERBOSE) cpu_printf(\"putting state into q%%d\\n\", q);",
+	"",
+	"	memcpy((uchar *) f->m_now,  (uchar *) &now, vsize);",
+	"	memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char));",
+	"	for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+	"	{	if (Mask[i])",
+	"		{	f->m_Mask[i/8] |= (1<<j);",
+	"	}	}",
+	"",
+	"	if (now._nr_pr > 0)",
+	"	{ memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT));",
+	"	  memcpy((uchar *) f->m_p_skip,   (uchar *) proc_skip,   now._nr_pr * sizeof(uchar));",
+	"	}",
+	"	if (now._nr_qs > 0)",
+	"	{ memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT));",
+	"	  memcpy((uchar *) f->m_q_skip,   (uchar *) q_skip,   now._nr_qs * sizeof(uchar));",
+	"	}",
+	"#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+	"	c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */",
+	"#endif",
+	"#ifdef FULL_TRAIL",
+	"	f->m_stack = stack_last[core_id];",
+	"#endif",
+	"	f->nr_handoffs = nr_handoffs+1;",
+	"	f->m_tau    = trpt->tau;",
+	"	f->m_o_pm   = trpt->o_pm;",
+	"	f->m_boq    = boq;",
+	"	f->m_vsize  = vsize;	/* must come last - now the other cpu can see it */",
+	"",
+	"	if (query_in_progress == 1)",
+	"		query_in_progress = 2;	/* make sure we know, if a query makes the rounds */",
+	"	nstates_put++;",
+	"}",
+	"",
+	"#ifdef USE_DISK",
+	"int Dsk_W_Nr, Dsk_R_Nr;",
+	"int dsk_file = -1, dsk_read = -1;",
+	"unsigned long dsk_written, dsk_drained;",
+	"char dsk_name[512];",
+	"",
+	"#ifndef BFS_DISK",
+	"#if defined(WIN32) || defined(WIN64)",
+	"	#define RFLAGS	(O_RDONLY|O_BINARY)",
+	"	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)",
+	"#else",
+	"	#define RFLAGS	(O_RDONLY)",
+	"	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC)",
+	"#endif",
+	"#endif",
+	"",
+	"void",
+	"dsk_stats(void)",
+	"{	int i;",
+	"",
+	"	if (dsk_written > 0)",
+	"	{	cpu_printf(\"dsk_written %%d states in %%d files\\ncpu%%d: dsk_drained %%6d states\\n\",",
+	"			dsk_written, Dsk_W_Nr, core_id, dsk_drained);",
+	"		close(dsk_read);",
+	"		close(dsk_file);",
+	"		for (i = 0; i < Dsk_W_Nr; i++)",
+	"		{	sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", i, core_id);",
+	"			unlink(dsk_name);",
+	"	}	}",
+	"}",
+	"",
+	"void",
+	"mem_drain(void)",
+	"{	SM_frame *f, g;",
+	"	int q = (core_id + 1) %% NCORE;	/* target q */",
+	"	int sz;",
+	"",
+	"	if (dsk_read < 0",
+	"	||  dsk_written <= dsk_drained)",
+	"	{	return;",
+	"	}",
+	"",
+	"	while (dsk_written > dsk_drained",
+	"	&& TargetQ_NotFull(q))",
+	"	{	f = Get_Free_Frame(q);",
+	"		if (!f) { Uerror(\"Fatal: unhandled condition\"); }",
+	"",
+	"		if ((dsk_drained+1)%%MAX_DSK_FILE == 0)	/* 100K states max per file */",
+	"		{	(void) close(dsk_read); 	/* close current read handle */",
+	"			sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_R_Nr++, core_id);",
+	"			(void) unlink(dsk_name);	/* remove current file */",
+	"			sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_R_Nr, core_id);",
+	"			cpu_printf(\"reading %%s\\n\", dsk_name);",
+	"			dsk_read = open(dsk_name, RFLAGS); /* open next file */",
+	"			if (dsk_read < 0)",
+	"			{	Uerror(\"could not open dsk file\");",
+	"		}	}",
+	"		if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame))",
+	"		{	Uerror(\"bad dsk file read\");",
+	"		}",
+	"		sz = g.m_vsize;",
+	"		g.m_vsize = 0;",
+	"		memcpy(f, &g, sizeof(SM_frame));",
+	"		f->m_vsize = sz;	/* last */",
+	"",
+	"		dsk_drained++;",
+	"	}",
+	"}",
+	"",
+	"void",
+	"mem_file(void)",
+	"{	SM_frame f;",
+	"	int i, j, q = (core_id + 1) %% NCORE;	/* target q */",
+	"",
+	"	if (vsize > VMAX)",
+	"	{	printf(\"pan: recompile with -DVMAX=N with N >= %%d\\n\", vsize);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"	if (now._nr_pr > PMAX)",
+	"	{	printf(\"pan: recompile with -DPMAX=N with N >= %%d\\n\", now._nr_pr);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"	if (now._nr_qs > QMAX)",
+	"	{	printf(\"pan: recompile with -DQMAX=N with N >= %%d\\n\", now._nr_qs);",
+	"		Uerror(\"aborting\");",
+	"	}",
+	"",
+	"	if (VVERBOSE) cpu_printf(\"filing state for q%%d\\n\", q);",
+	"",
+	"	memcpy((uchar *) f.m_now,  (uchar *) &now, vsize);",
+	"	memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char));",
+	"	for (i = j = 0; i < VMAX; i++, j = (j+1)%%8)",
+	"	{	if (Mask[i])",
+	"		{	f.m_Mask[i/8] |= (1<<j);",
+	"	}	}",
+	"",
+	"	if (now._nr_pr > 0)",
+	"	{	memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT));",
+	"		memcpy((uchar *)f.m_p_skip,   (uchar *)proc_skip,   now._nr_pr*sizeof(uchar));",
+	"	}",
+	"	if (now._nr_qs > 0)",
+	"	{	memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT));",
+	"		memcpy((uchar *) f.m_q_skip,   (uchar *) q_skip,   now._nr_qs*sizeof(uchar));",
+	"	}",
+	"#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)",
+	"	c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */",
+	"#endif",
+	"#ifdef FULL_TRAIL",
+	"	f.m_stack  = stack_last[core_id];",
+	"#endif",
+	"	f.nr_handoffs = nr_handoffs+1;",
+	"	f.m_tau    = trpt->tau;",
+	"	f.m_o_pm   = trpt->o_pm;",
+	"	f.m_boq    = boq;",
+	"	f.m_vsize  = vsize;",
+	"",
+	"	if (query_in_progress == 1)",
+	"	{	query_in_progress = 2;",
+	"	}",
+	"	if (dsk_file < 0)",
+	"	{	sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_W_Nr, core_id);",
+	"		dsk_file = open(dsk_name, WFLAGS, 0644);",
+	"		dsk_read = open(dsk_name, RFLAGS);",
+	"		if (dsk_file < 0 || dsk_read < 0)",
+	"		{	cpu_printf(\"File: <%%s>\\n\", dsk_name);",
+	"			Uerror(\"cannot open diskfile\");",
+	"		}",
+	"		Dsk_W_Nr++; /* nr of next file to open */",
+	"		cpu_printf(\"created temporary diskfile %%s\\n\", dsk_name);",
+	"	} else if ((dsk_written+1)%%MAX_DSK_FILE == 0)",
+	"	{	close(dsk_file); /* close write handle */",
+	"		sprintf(dsk_name, \"Q%%.3d_%%.3d.tmp\", Dsk_W_Nr++, core_id);",
+	"		dsk_file = open(dsk_name, WFLAGS, 0644);",
+	"		if (dsk_file < 0)",
+	"		{	cpu_printf(\"File: <%%s>\\n\", dsk_name);",
+	"			Uerror(\"aborting: cannot open new diskfile\");",
+	"		}",
+	"		cpu_printf(\"created temporary diskfile %%s\\n\", dsk_name);",
+	"	}",
+	"	if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame))",
+	"	{	Uerror(\"aborting -- disk write failed (disk full?)\");",
+	"	}",
+	"	nstates_put++;",
+	"	dsk_written++;",
+	"}",
+	"#endif",
+	"",
+	"int",
+	"mem_hand_off(void)",
+	"{",
+	"	if (search_terminated == NULL",
+	"	||  *search_terminated != 0)	/* not a full crash check */",
+	"	{	pan_exit(0);",
+	"	}",
+	"	iam_alive();		/* on every transition of Down */",
+	"#ifdef USE_DISK",
+	"	mem_drain();		/* maybe call this also on every Up */",
+	"#endif",
+	"	if (depth > z_handoff	/* above handoff limit */",
+	"#ifndef SAFETY",
+	"	&&  !a_cycles		/* not in liveness mode */",
+	"#endif",
+	"#if SYNC",
+	"	&&  boq == -1		/* not mid-rv */",
+	"#endif",
+	"#ifdef VERI",
+	"	&&  (trpt->tau&4)	 /* claim moves first  */",
+	"	&&  !((trpt-1)->tau&128) /* not a stutter move */",
+	"#endif",
+	"	&&  !(trpt->tau&8))	/* not an atomic move */",
+	"	{	int q = (core_id + 1) %% NCORE;	/* circular handoff */",
+	"	#ifdef GENEROUS",
+	"		if (prcnt[q] < LN_FRAMES)", /* not the best strategy */
+	"	#else",
+	"		if (TargetQ_NotFull(q)",
+	"		&& (dfs_phase2 == 0 || prcnt[core_id] > 0))", /* not locked, ok if race */
+	"	#endif",
+	"		{	mem_put(q);",	/* only 1 writer: lock-free */
+	"			return 1;",
+	"		}",
+	"		{	int rval;",
+	"	#ifndef NGQ",
+	"			rval = GlobalQ_HasRoom();",
+	"	#else",
+	"			rval = 0;",
+	"	#endif",
+	"	#ifdef USE_DISK",
+	"			if (rval == 0)",
+	"			{	void mem_file(void);",
+	"				mem_file();",
+	"				rval = 1;",
+	"			}",
+	"	#endif",
+	"			return rval;",
+	"		}",
+	"	}",
+	"	return 0; /* i.e., no handoff */",
+	"}",
+	"",
+	"void",
+	"mem_put_acc(void)	/* liveness mode */",
+	"{	int q = (core_id + 1) %% NCORE;",
+	"",
+	"	if (search_terminated == NULL",
+	"	||  *search_terminated != 0)",
+	"	{	pan_exit(0);",
+	"	}",
+	"#ifdef USE_DISK",
+	"	mem_drain();",
+	"#endif",
+	"	/* some tortured use of preprocessing: */",
+	"#if !defined(NGQ) || defined(USE_DISK)",
+	"	if (TargetQ_Full(q))",
+	"	{",
+	"#endif",
+	"#ifndef NGQ",
+	"		if (GlobalQ_HasRoom())",
+	"		{	return;",
+	"		}",
+	"#endif",
+	"#ifdef USE_DISK",
+	"		mem_file();",
+	"	} else",
+	"#else",
+	"	#if !defined(NGQ) || defined(USE_DISK)",
+	"	}",
+	"	#endif",
+	"#endif",
+	"	{	mem_put(q);",
+	"	}",
+	"}",
+	"",
+	"#if defined(WIN32) || defined(WIN64)", /* visual studio */
+	"void",
+	"init_shm(void)		/* initialize shared work-queues */",
+	"{	char	key[512];",
+	"	int	n, m;",
+	"	int	must_exit = 0;",
+	"",
+	"	if (core_id == 0 && verbose)",
+	"	{	printf(\"cpu0: step 3: allocate shared work-queues %%g Mb\\n\",",
+	"			((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.));",
+	"	}",
+	"	for (m = 0; m < NR_QS; m++)	/* last q is global 1 */",
+	"	{	double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;",
+	"		sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, m);",
+	"		if (core_id == 0)",	/* root process creates shared memory segments */
+	"		{	shmid[m] = CreateFileMapping(",
+	"				INVALID_HANDLE_VALUE,	/* use paging file */",
+	"				NULL,			/* default security */",
+	"				PAGE_READWRITE,		/* access permissions */",
+	"				0,			/* high-order 4 bytes */",
+	"				qsize,			/* low-order bytes, size in bytes */",
+	"				key);			/* name */",
+	"		} else			/* worker nodes just open these segments */",
+	"		{	shmid[m] = OpenFileMapping(",
+	"				FILE_MAP_ALL_ACCESS,	/* read/write access */",
+	"				FALSE,			/* children do not inherit handle */",
+	"				key);",
+	"		}",
+	"		if (shmid[m] == NULL)",
+	"		{	fprintf(stderr, \"cpu%%d: could not create or open shared queues\\n\",",
+	"				core_id);",
+	"			must_exit = 1;",
+	"			break;",
+	"		}",
+	"		/* attach: */",
+	"		shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0);",
+	"		if (shared_mem[m] == NULL)",
+	"		{ fprintf(stderr, \"cpu%%d: cannot attach shared q%%d (%%d Mb)\\n\",",
+	"			core_id, m+1, (int) (qsize/(1048576.)));",
+	"		  must_exit = 1;",
+	"		  break;",
+	"		}",
+	"",
+	"		memcnt += qsize;",
+	"",
+	"		m_workq[m] = (SM_frame *) shared_mem[m];",
+	"		if (core_id == 0)",
+	"		{	int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;",
+	"			for (n = 0; n < nframes; n++)",
+	"			{	m_workq[m][n].m_vsize = 0;",
+	"				m_workq[m][n].m_boq = 0;",
+	"	}	}	}",
+	"",
+	"	if (must_exit)",
+	"	{	fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+	"		pan_exit(1);	/* calls cleanup_shm */",
+	"	}",
+	"}",
+	"",
+	"static uchar *",
+	"prep_shmid_S(size_t n)		/* either sets SS or H_tab, WIN32/WIN64 */",
+	"{	char	*rval;",
+	"#ifndef SEP_STATE",
+	"	char	key[512];",
+	"",
+	"	if (verbose && core_id == 0)",
+	"	{",
+	"	#ifdef BITSTATE",
+	"		printf(\"cpu0: step 1: allocate shared bitstate %%g Mb\\n\",",
+	"			(double) n / (1048576.));",
+	"	#else",
+	"		printf(\"cpu0: step 1: allocate shared hastable %%g Mb\\n\",",
+	"			(double) n / (1048576.));",
+	"	#endif",
+	"	}",
+	"	#ifdef MEMLIM",
+	"	if (memcnt + (double) n > memlim)",
+	"	{	printf(\"cpu%%d: S %%8g + %%d Kb exceeds memory limit of %%8g Mb\\n\",",
+	"			core_id, memcnt/1024., n/1024, memlim/(1048576.));",
+	"		printf(\"cpu%%d: insufficient memory -- aborting\\n\", core_id);",
+	"		exit(1);",
+	"	}",
+	"	#endif",
+	"",
+	"	/* make key different from queues: */",
+	"	sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, NCORE+2); /* different from qs */",
+	"",
+	"	if (core_id == 0)	/* root */",
+	"	{	shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,",
+	"#ifdef WIN64",
+	"			PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);",
+	"#else",
+	"			PAGE_READWRITE, 0, n, key);",
+	"#endif",
+	"		memcnt += (double) n;",
+	"	} else			/* worker */",
+	"	{	shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);",
+	"	}",
+
+	"	if (shmid_S == NULL)",
+	"	{",
+	"	#ifdef BITSTATE",
+	"		fprintf(stderr, \"cpu%%d: cannot %%s shared bitstate\",",
+	"			core_id, core_id?\"open\":\"create\");",
+	"	#else",
+	"		fprintf(stderr, \"cpu%%d: cannot %%s shared hashtable\",",
+	"			core_id, core_id?\"open\":\"create\");",
+	"	#endif",
+	"		fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+	"		pan_exit(1);",
+	"	}",
+	"",
+	"	rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0);	/* attach */",
+	"	if ((char *) rval == NULL)",
+	"	{ fprintf(stderr, \"cpu%%d: cannot attach shared bitstate or hashtable\\n\", core_id);",
+	"	  fprintf(stderr, \"pan: check './pan --' for usage details\\n\");",
+	"	  pan_exit(1);",
+	"	}",
+	"#else",
+	"	rval = (char *) emalloc(n);",
+	"#endif",
+	"	return (uchar *) rval;",
+	"}",
+	"",
+	"static uchar *",
+	"prep_state_mem(size_t n)		/* WIN32/WIN64 sets memory arena for states */",
+	"{	char	*rval;",
+	"	char	key[512];",
+	"	static int cnt = 3;		/* start larger than earlier ftok calls */",
+	"",
+	"	if (verbose && core_id == 0)",
+	"	{	printf(\"cpu0: step 2+: pre-allocate memory arena %%d of %%g Mb\\n\",",
+	"			cnt-3, (double) n / (1048576.));",
+	"	}",
+	"	#ifdef MEMLIM",
+	"	if (memcnt + (double) n > memlim)",
+	"	{	printf(\"cpu%%d: error: M %%.0f + %%.0f exceeds memory limit of %%.0f Kb\\n\",",
+	"			core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0);",
+	"		return NULL;",
+	"	}",
+	"	#endif",
+	"",
+	"	sprintf(key, \"Global\\\\pan_%%s_%%.3d\", PanSource, NCORE+cnt); cnt++;",
+	"",
+	"	if (core_id == 0)",
+	"	{	shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,",
+	"#ifdef WIN64",
+	"			PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);",
+	"#else",
+	"			PAGE_READWRITE, 0, n, key);",
+	"#endif",
+	"	} else",
+	"	{	shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);",
+	"	}",
+	"	if (shmid_M == NULL)",
+	"	{	printf(\"cpu%%d: failed to get pool of shared memory nr %%d of size %%d\\n\",",
+	"			core_id, cnt-3, n);",
+	"		printf(\"pan: check './pan --' for usage details\\n\");",
+	"		return NULL;",
+	"	}",
+	"	rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0);	/* attach */",
+	"",
+	"	if (rval == NULL)",
+	"	{ printf(\"cpu%%d: failed to attach pool of shared memory nr %%d of size %%d\\n\",",
+	"		core_id, cnt-3, n);",
+	"	  return NULL;",
+	"	}",
+	"	return (uchar *) rval;",
+	"}",
+	"",
+	"void",
+	"init_HT(unsigned long n)	/* WIN32/WIN64 version */",
+	"{	volatile char	*x;",
+	"	double  get_mem;",
+	"#ifndef SEP_STATE",
+	"	char	*dc_mem_start;",
+	"#endif",
+	"	if (verbose) printf(\"cpu%%d: initialization for Windows\\n\", core_id);",
+	"",
+"#ifdef SEP_STATE",
+	" #ifndef MEMLIM",
+	"	if (verbose)",
+	"	{	printf(\"cpu0: steps 0,1: no -DMEMLIM set\\n\");",
+	"	}",
+	" #else",
+	"	if (verbose)",
+	"	printf(\"cpu0: steps 0,1: -DMEMLIM=%%d Mb - (hashtable %%g Mb + workqueues %%g Mb)\\n\",",
+	"		MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.));",
+	"#endif",
+	"	get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double);",
+	"	/* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */",
+	"	get_mem += 4 * NCORE * sizeof(void *);", /* prfree, prfull, prcnt, prmax */
+	" #ifdef FULL_TRAIL",
+	"	get_mem += (NCORE) * sizeof(Stack_Tree *);",
+	"	/* NCORE * stack_last */",
+	" #endif",
+	"	x = (volatile char *) prep_state_mem((size_t) get_mem);",
+	"	shmid_X = (void *) x;",
+	"	if (x == NULL)",
+	"	{	printf(\"cpu0: could not allocate shared memory, see ./pan --\\n\");",
+	"		exit(1);",
+	"	}",
+	"	search_terminated = (volatile unsigned int *) x; /* comes first */",
+	"	x += sizeof(void *); /* maintain alignment */",
+	"",
+	"	is_alive   = (volatile double *) x;",
+	"	x += NCORE * sizeof(double);",
+	"",
+	"	sh_lock   = (volatile int *) x;",
+	"	x += CS_NR * sizeof(void *); /* allow 1 word per entry */",
+	"",
+	"	grfree    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grfull    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grcnt    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grmax    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	prfree = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prfull = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prcnt = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prmax = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	gr_readmiss    = (volatile double *) x;",
+	"	x += sizeof(double);",
+	"	gr_writemiss    = (volatile double *) x;",
+	"	x += sizeof(double);",
+	"",
+	"	#ifdef FULL_TRAIL",
+	"		stack_last = (volatile Stack_Tree **) x;",
+	"		x += NCORE * sizeof(Stack_Tree *);",
+	"	#endif",
+	"",
+	"	#ifndef BITSTATE",
+	"		H_tab = (struct H_el **) emalloc(n);",
+	"	#endif",
+"#else",
+	"	#ifndef MEMLIM",
+	"		#warning MEMLIM not set",	/* cannot happen */
+	"		#define MEMLIM	(2048)",
+	"	#endif",
+	"",
+	"	if (core_id == 0 && verbose)",
+	"		printf(\"cpu0: step 0: -DMEMLIM=%%d Mb - (hashtable %%g Mb + workqueues %%g Mb) = %%g Mb for state storage\\n\",",
+	"		MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),",
+	"		(memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));",
+	"	#ifndef BITSTATE",
+	"		H_tab = (struct H_el **) prep_shmid_S((size_t) n);	/* hash_table */",
+	"	#endif",
+	"	get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE;",
+	"	if (get_mem <= 0)",
+	"	{	Uerror(\"internal error -- shared state memory\");",
+	"	}",
+	"",
+	"	if (core_id == 0 && verbose)",
+	"	{	printf(\"cpu0: step 2: shared state memory %%g Mb\\n\",",
+	"			get_mem/(1048576.));",
+	"	}",
+	"	x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem);	/* for states */",
+	"	if (x == NULL)",
+	"	{	printf(\"cpu%%d: insufficient memory -- aborting\\n\", core_id);",
+	"		exit(1);",
+	"	}",
+	"",
+	"	search_terminated = (volatile unsigned int *) x; /* comes first */",
+	"	x += sizeof(void *); /* maintain alignment */",
+	"",
+	"	is_alive   = (volatile double *) x;",
+	"	x += NCORE * sizeof(double);",
+	"",
+	"	sh_lock   = (volatile int *) x;",
+	"	x += CS_NR * sizeof(int);",
+	"",
+	"	grfree    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grfull    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grcnt    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	grmax    = (volatile int *) x;",
+	"	x += sizeof(void *);",
+	"	prfree = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prfull = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prcnt = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	prmax = (volatile int *) x;",
+	"	x += NCORE * sizeof(void *);",
+	"	gr_readmiss = (volatile double *) x;",
+	"	x += sizeof(double);",
+	"	gr_writemiss = (volatile double *) x;",
+	"	x += sizeof(double);",
+	"",
+	" #ifdef FULL_TRAIL",
+	"	stack_last = (volatile Stack_Tree **) x;",
+	"	x += NCORE * sizeof(Stack_Tree *);",
+	" #endif",
+	"	if (((long)x)&(sizeof(void *)-1))	/* word alignment */",
+	"	{	x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */",
+	"	}",
+	"",
+	"	#ifdef COLLAPSE",
+	"	ncomps = (unsigned long *) x;",
+	"	x += (256+2) * sizeof(unsigned long);",
+	"	#endif",
+	"",
+	"	dc_shared = (sh_Allocater *) x; /* in shared memory */",
+	"	x += sizeof(sh_Allocater);",
+	"",
+	"	if (core_id == 0)	/* root only */",
+	"	{	dc_shared->dc_id     = shmid_M;",
+	"		dc_shared->dc_start  = (void *) dc_mem_start;",
+	"		dc_shared->dc_arena  = x;",
+	"		dc_shared->pattern   = 1234567;",
+	"		dc_shared->dc_size   = (long) get_mem - (long) (x - dc_mem_start);",
+	"		dc_shared->nxt       = NULL;",
+	"	}",
+"#endif",
+	"}",
+	"",
+	"#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__)",
+	"extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit);",
+	"int",
+	"tas(volatile LONG *s)", /* atomic test and set */
+	"{	return InterlockedBitTestAndSet(s, 1);",
+	"}",
+	"#else",
+	"	#error missing definition of test and set operation for this platform",
+	"#endif",
+	"",
+	"void",
+	"cleanup_shm(int val)",
+	"{	int m;",
+	"	static int nibis = 0;",
+	"",
+	"	if (nibis != 0)",
+	"	{	printf(\"cpu%%d: Redundant call to cleanup_shm(%%d)\\n\", core_id, val);",
+	"		return;",
+	"	} else",
+	"	{	nibis = 1;",
+	"	}",
+	"	if (search_terminated != NULL)",
+	"	{	*search_terminated |= 16; /* cleanup_shm */",
+	"	}",
+	"",
+	"	for (m = 0; m < NR_QS; m++)",
+	"	{	if (shmid[m] != NULL)",
+	"		{	UnmapViewOfFile((char *) shared_mem[m]);",
+	"			CloseHandle(shmid[m]);",
+	"	}	}",
+	"#ifdef SEP_STATE",
+	"	UnmapViewOfFile((void *) shmid_X);",
+	"	CloseHandle((void *) shmid_M);",
+	"#else",
+	"	#ifdef BITSTATE",
+	"		if (shmid_S != NULL)",
+	"		{	UnmapViewOfFile(SS);",
+	"			CloseHandle(shmid_S);",
+	"		}",
+	"	#else",
+	"		if (core_id == 0 && verbose)",
+	"		{	printf(\"cpu0: done, %%ld Mb of shared state memory left\\n\",",
+	"				dc_shared->dc_size / (long)(1048576));",
+	"		}",
+	"		if (shmid_S != NULL)",
+	"		{	UnmapViewOfFile(H_tab);",
+	"			CloseHandle(shmid_S);",
+	"		}",
+	"		shmid_M = (void *) (dc_shared->dc_id);",
+	"		UnmapViewOfFile((char *) dc_shared->dc_start);",
+	"		CloseHandle(shmid_M);",
+	"	#endif",
+	"#endif",
+	"	/* detached from shared memory - so cannot use cpu_printf */",
+	"	if (verbose)",
+	"	{	printf(\"cpu%%d: done -- got %%d states from queue\\n\",",
+	"			core_id, nstates_get);",
+	"	}",
+	"}",
+	"",
+	"void",
+	"mem_get(void)",
+	"{	SM_frame   *f;",
+	"	int is_parent;",
+	"",
+	"#if defined(MA) && !defined(SEP_STATE)",
+	"	#error MA requires SEP_STATE in multi-core mode",
+	"#endif",
+	"#ifdef BFS",
+	"	#error BFS is not supported in multi-core mode",
+	"#endif",
+	"#ifdef SC",
+	"	#error SC is not supported in multi-core mode",
+	"#endif",
+	"	init_shm();	/* we are single threaded when this starts */",
+	"	signal(SIGINT, give_up);	/* windows control-c interrupt */",
+	"",
+	"	if (core_id == 0 && verbose)",
+	"	{	printf(\"cpu0: step 4: creating additional workers (proxy %%d)\\n\",",
+	"			proxy_pid);",
+	"	}",
+	"#if 0",
+	"	if NCORE > 1 the child or the parent should fork N-1 more times",
+	"	the parent is the only process with core_id == 0 and is_parent > 0",
+	"	the others (workers) have is_parent = 0 and core_id = 1..NCORE-1",
+	"#endif",
+	"	if (core_id == 0)			/* root starts up the workers */",
+	"	{	worker_pids[0] = (DWORD) getpid();	/* for completeness */",
+	"		while (++core_id < NCORE)	/* first worker sees core_id = 1 */",
+	"		{	char cmdline[64];",
+	"			STARTUPINFO si = { sizeof(si) };",
+	"			PROCESS_INFORMATION pi;",
+	"",
+	"			if (proxy_pid == core_id)	/* always non-zero */",
+	"			{	sprintf(cmdline, \"pan_proxy.exe -r %%s-Q%%d -Z%%d\",",
+	"					o_cmdline, getpid(), core_id);",
+	"			} else",
+	"			{	sprintf(cmdline, \"pan.exe %%s-Q%%d -Z%%d\",",
+	"					o_cmdline, getpid(), core_id);",
+	"			}",
+	"			if (verbose) printf(\"cpu%%d: spawn %%s\\n\", core_id, cmdline);",
+	"",
+	"			is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi);",
+	"			if (is_parent == 0)",
+	"			{	Uerror(\"fork failed\");",
+	"			}",
+	"			worker_pids[core_id] = pi.dwProcessId;",
+	"			worker_handles[core_id] = pi.hProcess;",
+	"			if (verbose)",
+	"			{	cpu_printf(\"created core %%d, pid %%d\\n\",",
+	"					core_id, pi.dwProcessId);",
+	"			}",
+	"			if (proxy_pid == core_id)	/* we just created the receive half */",
+	"			{	/* add proxy send, store pid in proxy_pid_snd */",
+	"				sprintf(cmdline, \"pan_proxy.exe -s %%s-Q%%d -Z%%d -Y%%d\",",
+	"					o_cmdline, getpid(), core_id, worker_pids[proxy_pid]);",
+	"				if (verbose) printf(\"cpu%%d: spawn %%s\\n\", core_id, cmdline);",
+	"				is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi);",
+	"				if (is_parent == 0)",
+	"				{	Uerror(\"fork failed\");",
+	"				}",
+	"				proxy_pid_snd = pi.dwProcessId;",
+	"				proxy_handle_snd = pi.hProcess;",
+	"				if (verbose)",
+	"				{	cpu_printf(\"created core %%d, pid %%d (send proxy)\\n\",",
+	"						core_id, pi.dwProcessId);",
+	"		}	}	}",
+	"		core_id = 0;		/* reset core_id for root process */",
+	"	} else	/* worker */",
+	"	{	static char db0[16];	/* good for up to 10^6 cores */",
+	"		static char db1[16];",
+	"		tprefix = db0; sprefix = db1;",
+	"		sprintf(tprefix, \"cpu%%d_trail\", core_id);	/* avoid conflicts on file access */",
+	"		sprintf(sprefix, \"cpu%%d_rst\", core_id);",
+	"		memcnt = 0;	/* count only additionally allocated memory */",
+	"	}",
+	"	if (verbose)",
+	"	{	cpu_printf(\"starting core_id %%d -- pid %%d\\n\", core_id, getpid());",
+	"	}",
+	"	if (core_id == 0 && !remote_party)",
+	"	{	new_state();	/* root starts the search */",
+	"		if (verbose)",
+	"		cpu_printf(\"done with 1st dfs, nstates %%g (put %%d states), start reading q\\n\",",
+	"			nstates, nstates_put);",
+	"		dfs_phase2 = 1;",
+	"	}",
+	"	Read_Queue(core_id);	/* all cores */",
+	"",
+	"	if (verbose)",
+	"	{	cpu_printf(\"put %%6d states into queue -- got %%6d\\n\",",
+	"			nstates_put, nstates_get);",
+	"	}",
+	"	done = 1;",
+	"	wrapup();",
+	"	exit(0);",
+	"}",
+	"#endif", /* WIN32 || WIN64 */
+	"",
+	"#ifdef BITSTATE",
+	"void",
+	"init_SS(unsigned long n)",
+	"{",
+	"	SS = (uchar *) prep_shmid_S((size_t) n);",
+	"	init_HT(0L);", /* locks and shared memory for Stack_Tree allocations */
+	"}",
+	"#endif", /* BITSTATE */
+	"",
+	"#endif", /* NCORE>1 */
+	0,
+};

+ 925 - 0
sys/src/cmd/spin/pangen7.c

@@ -0,0 +1,925 @@
+/***** spin: pangen7.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            */
+/* pangen7.c: Version 5.3.0 2010, synchronous product of never claims     */
+
+#include <stdlib.h>
+#include "spin.h"
+#include "y.tab.h"
+#include <assert.h>
+#ifdef PC
+extern int unlink(const char *);
+#else
+#include <unistd.h>
+#endif
+
+extern ProcList	*rdy;
+extern Element *Al_El;
+extern int nclaims, verbose, Strict;
+
+typedef struct Succ_List Succ_List;
+typedef struct SQueue SQueue;
+typedef struct OneState OneState;
+typedef struct State_Stack State_Stack;
+typedef struct Guard Guard;
+
+struct Succ_List {
+	SQueue	*s;
+	Succ_List *nxt;
+};
+
+struct OneState {
+	int	*combo;	/* the combination of claim states */
+	Succ_List	*succ;	/* list of ptrs to immediate successor states */
+};
+
+struct SQueue {
+	OneState	state;
+	SQueue	*nxt;
+};
+
+struct State_Stack {
+	int *n;
+	State_Stack *nxt;
+};
+
+struct Guard {
+	Lextok *t;
+	Guard *nxt;
+};
+
+SQueue	*sq, *sd, *render;	/* states move from sq to sd to render to holding */
+SQueue	*holding, *lasthold;
+State_Stack *dsts;
+
+int nst;		/* max nr of states in claims */
+int *Ist;		/* initial states */
+int *Nacc;		/* number of accept states in claim */
+int *Nst;		/* next states */
+int **reached;		/* n claims x states */
+int unfolding;		/* to make sure all accept states are reached */
+int is_accept;		/* remember if the current state is accepting in any claim */
+int not_printing;	/* set during explore_product */
+
+Element ****matrix;	/* n x two-dimensional arrays state x state */
+Element **Selfs;	/* self-loop states at end of claims */
+
+static void get_seq(int, Sequence *);
+static void set_el(int n, Element *e);
+static void gen_product(void);
+static void print_state_nm(char *, int *, char *);
+static SQueue *find_state(int *);
+static SQueue *retrieve_state(int *);
+
+static int
+same_state(int *a, int *b)
+{	int i;
+
+	for (i = 0; i < nclaims; i++)
+	{	if (a[i] != b[i])
+		{	return 0;
+	}	}
+	return 1;
+}
+
+static int
+in_stack(SQueue *s, SQueue *in)
+{	SQueue *q;
+
+	for (q = in; q; q = q->nxt)
+	{	if (same_state(q->state.combo, s->state.combo))
+		{	return 1;
+	}	}
+	return 0;
+}
+
+static void
+to_render(SQueue *s)
+{	SQueue *a, *q, *last; /* find in sd/sq and move to render, if not already there */
+	int n;
+
+	for (n = 0; n < nclaims; n++)
+	{	reached[n][ s->state.combo[n] ] |= 2;
+	}
+
+	for (q = render; q; q = q->nxt)
+	{	if (same_state(q->state.combo, s->state.combo))
+		{	return;
+	}	}
+	for (q = holding; q; q = q->nxt)
+	{	if (same_state(q->state.combo, s->state.combo))
+		{	return;
+	}	}
+
+	a = sd;
+more:
+	for (q = a, last = 0; q; last = q, q = q->nxt)
+	{	if (same_state(q->state.combo, s->state.combo))
+		{	if (!last)
+			{	if (a == sd)
+				{	sd = q->nxt;
+				} else if (a == sq)
+				{	sq = q->nxt;
+				} else
+				{	holding = q->nxt;
+				}
+			} else
+			{	last->nxt = q->nxt;
+			}
+			q->nxt = render;
+			render = q;
+			return;
+	}	}
+	if (verbose)
+	{	print_state_nm("looking for: ", s->state.combo, "\n");
+	}
+	(void) find_state(s->state.combo);	/* creates it in sq */
+	if (a != sq)
+	{	a = sq;
+		goto more;
+	}
+	fatal("cannot happen, to_render", 0);
+}
+
+static void
+wrap_text(char *pre, Lextok *t, char *post)
+{
+	printf(pre);
+	comment(stdout, t, 0);
+	printf(post);
+}
+
+static State_Stack *
+push_dsts(int *n)
+{	State_Stack *s;
+	int i;
+
+	for (s = dsts; s; s = s->nxt)
+	{	if (same_state(s->n, n))
+		{	if (verbose&64)
+			{	printf("\n");
+				for (s = dsts; s; s = s->nxt)
+				{	print_state_nm("\t", s->n, "\n");
+				}
+				print_state_nm("\t", n, "\n");
+			}
+			return s;
+	}	}
+
+	s = (State_Stack *) emalloc(sizeof(State_Stack));
+	s->n = (int *) emalloc(nclaims * sizeof(int));
+	for (i = 0; i < nclaims; i++)
+		s->n[i] = n[i];
+	s->nxt = dsts;
+	dsts = s;
+	return 0;
+}
+
+static void
+pop_dsts(void)
+{
+	assert(dsts);
+	dsts = dsts->nxt;
+}
+
+static void
+complete_transition(Succ_List *sl, Guard *g)
+{	Guard *w;
+	int cnt = 0;
+
+	printf("	:: ");
+	for (w = g; w; w = w->nxt)
+	{	if (w->t->ntyp == CONST
+		&&  w->t->val == 1)
+		{	continue;
+		} else if (w->t->ntyp == 'c'
+		&&  w->t->lft->ntyp == CONST
+		&&  w->t->lft->val == 1)
+		{	continue; /* 'true' */
+		}
+
+		if (cnt > 0)
+		{	printf(" && ");
+		}
+		wrap_text("", w->t, "");
+		cnt++;
+	}
+	if (cnt == 0)
+	{	printf("true");
+	}
+	print_state_nm(" -> goto ", sl->s->state.combo, "");
+
+	if (is_accept > 0)
+	{	printf("_U%d\n", (unfolding+1)%nclaims);
+	} else
+	{	printf("_U%d\n", unfolding);
+	}
+}
+
+static void
+state_body(OneState *s, Guard *guard)
+{	Succ_List *sl;
+	State_Stack *y;
+	Guard *g;
+	int i, once;
+
+	for (sl = s->succ; sl; sl = sl->nxt)
+	{	once = 0;
+
+		for (i = 0; i < nclaims; i++)
+		{	Element *e;
+			e = matrix[i][s->combo[i]][sl->s->state.combo[i]];
+
+			/* if one of the claims has a DO or IF move
+			   then pull its target state forward, once
+			 */
+
+			if (!e
+			|| e->n->ntyp == NON_ATOMIC
+			||  e->n->ntyp == DO
+			||  e->n->ntyp == IF)
+			{	s = &(sl->s->state);
+				y = push_dsts(s->combo);
+				if (!y)
+				{	if (once++ == 0)
+					{	assert(s->succ);
+						state_body(s, guard);
+					}
+					pop_dsts();
+				} else if (!y->nxt)	/* self-loop transition */
+				{	if (!not_printing) printf(" /* self-loop */\n");
+				} else
+				{	/* non_fatal("loop in state body", 0); ** maybe ok */
+				}
+				continue;
+			} else
+			{	g = (Guard *) emalloc(sizeof(Guard));
+				g->t = e->n;
+				g->nxt = guard;
+				guard = g;
+		}	}
+
+		if (guard && !once)
+		{	if (!not_printing) complete_transition(sl, guard);
+			to_render(sl->s);
+	}	}
+}
+
+static struct X {
+	char *s;	int n;
+} spl[] = {
+	{"end",		3 },
+	{"accept",	6 },
+	{0,		0 },
+};
+
+static int slcnt;
+extern Label *labtab;
+
+static ProcList *
+locate_claim(int n)
+{	ProcList *p;
+	int i;
+
+	for (p = rdy, i = 0; p; p = p->nxt, i++) /* find claim name */
+	{	if (i == n)
+		{	break;
+	}	}
+	assert(p && p->b == N_CLAIM);
+
+	return p;
+}
+
+static void
+elim_lab(Element *e)
+{	Label *l, *lst;
+
+	for (l = labtab, lst = NULL; l; lst = l, l = l->nxt)
+	{	if (l->e == e)
+		{	if (lst)
+			{	lst->nxt = l->nxt;
+			} else
+			{	labtab = l->nxt;
+			}
+			break;
+	}	}
+}
+
+static int
+claim_has_accept(ProcList *p)
+{	Label *l;
+
+	for (l = labtab; l; l = l->nxt)
+	{	if (strcmp(l->c->name, p->n->name) == 0
+		&&  strncmp(l->s->name, "accept", 6) == 0)
+		{	return 1;
+	}	}
+	return 0;
+}
+
+static void
+prune_accept(void)
+{	int n;
+
+	for (n = 0; n < nclaims; n++)
+	{	if ((reached[n][Selfs[n]->seqno] & 2) == 0)
+		{	if (verbose)
+			{	printf("claim %d: selfloop not reachable\n", n);
+			}
+			elim_lab(Selfs[n]);
+			Nacc[n] = claim_has_accept(locate_claim(n));
+	}	}
+}
+
+static void
+mk_accepting(int n, Element *e)
+{	ProcList *p;
+	Label *l;
+	int i;
+
+	assert(!Selfs[n]);
+	Selfs[n] = e;
+
+	l = (Label *) emalloc(sizeof(Label));
+	l->s = (Symbol *) emalloc(sizeof(Symbol));
+	l->s->name = "accept00";
+	l->c = (Symbol *) emalloc(sizeof(Symbol));
+	l->uiid = 0;	/* this is not in an inline */
+
+	for (p = rdy, i = 0; p; p = p->nxt, i++) /* find claim name */
+	{	if (i == n)
+		{	l->c->name = p->n->name;
+			break;
+	}	}
+	assert(p && p->b == N_CLAIM);
+	Nacc[n] = 1;
+
+	l->e = e;
+	l->nxt = labtab;
+	labtab = l;
+}
+
+static void
+check_special(int *nrs)
+{	ProcList *p;
+	Label *l;
+	int i, j, nmatches;
+	int any_accepts = 0;
+
+	for (i = 0; i < nclaims; i++)
+	{	any_accepts += Nacc[i];
+	}
+
+	is_accept = 0;
+	for (j = 0; spl[j].n; j++) /* 2 special label prefixes */
+	{	nmatches = 0;
+		for (p = rdy, i = 0; p; p = p->nxt, i++) /* check each claim */
+		{	if (p->b != N_CLAIM)
+			{	continue;
+			}
+			/* claim i in state nrs[i], type p->tn, name p->n->name
+			 * either the state has an accept label, or the claim has none,
+			 * so that all its states should be considered accepting
+			 * --- but only if other claims do have accept states!
+			 */
+			if (Strict == 0 && j == 1 && Nacc[i] == 0 && any_accepts > 0)
+			{	if ((verbose&32) && i == unfolding)
+				{	printf("	/* claim %d pseudo-accept */\n", i);
+				}
+				goto is_accepting;
+			}
+			for (l = labtab; l; l = l->nxt)	/* check its labels */
+			{	if (strcmp(l->c->name, p->n->name) == 0  /* right claim */
+				&&  l->e->seqno == nrs[i]		 /* right state */
+				&&  strncmp(l->s->name, spl[j].s, spl[j].n) == 0)
+				{	if (j == 1)	/* accept state */
+					{	char buf[32];
+is_accepting:					if (strchr(p->n->name, ':'))
+						{	sprintf(buf, "N%d", i);
+						} else
+						{	strcpy(buf, p->n->name);
+						}
+						if (unfolding == 0 && i == 0)
+						{	if (!not_printing)
+							printf("%s_%s_%d:\n",	/* true accept */
+								spl[j].s, buf, slcnt++);
+						} else if (verbose&32)
+						{	if (!not_printing)
+							printf("%s_%s%d:\n",
+								buf, spl[j].s, slcnt++);
+						}
+						if (i == unfolding)
+						{	is_accept++; /* move to next unfolding */
+						}
+					} else
+					{	nmatches++;
+					}
+					break;
+		}	}	}
+		if (j == 0 && nmatches == nclaims)	/* end-state */
+		{	if (!not_printing)
+			{	printf("%s%d:\n", spl[j].s, slcnt++);
+	}	}	}
+}
+
+static int
+render_state(SQueue *q)
+{
+	if (!q || !q->state.succ)
+	{	if (verbose&64)
+		{	printf("	no exit\n");
+		}
+		return 0;
+	}
+
+	check_special(q->state.combo); /* accept or end-state labels */
+
+	dsts = (State_Stack *) 0;
+	push_dsts(q->state.combo);	/* to detect loops */
+
+	if (!not_printing)
+	{	print_state_nm("", q->state.combo, "");	/* the name */
+		printf("_U%d:\n\tdo\n", unfolding);
+	}
+
+	state_body(&(q->state), (Guard *) 0);
+
+	if (!not_printing)
+	{	printf("\tod;\n");
+	}
+	pop_dsts();
+	return 1;
+}
+
+static void
+explore_product(void)
+{	SQueue *q;
+
+	/* all states are in the sd queue */
+
+	q = retrieve_state(Ist);	/* retrieve from the sd q */
+	q->nxt = render;		/* put in render q */
+	render = q;
+	do {
+		q = render;
+		render = render->nxt;
+		q->nxt = 0;		/* remove from render q */
+
+		if (verbose&64)
+		{	print_state_nm("explore: ", q->state.combo, "\n");
+		}
+
+		not_printing = 1;
+		render_state(q);	/* may add new states */
+		not_printing = 0;
+
+		if (lasthold)
+		{	lasthold->nxt = q;
+			lasthold = q;
+		} else
+		{	holding = lasthold = q;
+		}
+	} while (render);
+	assert(!dsts);
+	
+}
+
+static void
+print_product(void)
+{	SQueue *q;
+	int cnt;
+
+	if (unfolding == 0)
+	{	printf("never Product {\n");	/* name expected by iSpin */
+		q = find_state(Ist);	/* should find it in the holding q */
+		assert(q);
+		q->nxt = holding;	/* put it at the front */
+		holding = q;
+	}
+	render = holding;
+	holding = lasthold = 0;
+
+	printf("/* ============= U%d ============= */\n", unfolding);
+	cnt = 0;
+	do {
+		q = render;
+		render = render->nxt;
+		q->nxt = 0;
+		if (verbose&64)
+		{	print_state_nm("print: ", q->state.combo, "\n");
+		}
+		cnt += render_state(q);
+
+		if (lasthold)
+		{	lasthold->nxt = q;
+			lasthold = q;
+		} else
+		{	holding = lasthold = q;
+		}
+	} while (render);
+	assert(!dsts);
+
+	if (cnt == 0)
+	{	printf("	0;\n");
+	}
+
+	if (unfolding == nclaims-1)
+	{	printf("}\n");
+	}
+}
+
+static void
+prune_dead(void)
+{	Succ_List *sl, *last;
+	SQueue *q;
+	int cnt;
+
+	do {	cnt = 0;
+		for (q = sd; q; q = q->nxt)
+		{	/* if successor is deadend, remove it
+			 * unless it's a move to the end-state of the claim
+			 */
+			last = (Succ_List *) 0;
+			for (sl = q->state.succ; sl; last = sl, sl = sl->nxt)
+			{	if (!sl->s->state.succ)	/* no successor */
+				{	if (!last)
+					{	q->state.succ = sl->nxt;
+					} else
+					{	last->nxt = sl->nxt;
+					}
+					cnt++;
+		}	}	}
+	} while (cnt > 0);
+}
+
+static void
+print_raw(void)
+{	int i, j, n;
+
+	printf("#if 0\n");
+	for (n = 0; n < nclaims; n++)
+	{	printf("C%d:\n", n);
+		for (i = 0; i < nst; i++)
+		{	if (reached[n][i])
+			for (j = 0; j < nst; j++)
+			{	if (matrix[n][i][j])
+				{	if (reached[n][i] & 2) printf("+");
+					if (i == Ist[n]) printf("*");
+					printf("\t%d", i);
+					wrap_text(" -[", matrix[n][i][j]->n, "]->\t");
+					printf("%d\n", j);
+	}	}	}	}
+	printf("#endif\n\n");
+	fflush(stdout);
+}
+
+void
+sync_product(void)
+{	ProcList *p;
+	Element *e;
+	int n, i;
+
+	if (nclaims <= 1) return;
+
+	(void) unlink("pan.pre");
+
+	Ist  = (int *) emalloc(sizeof(int) * nclaims);
+	Nacc = (int *) emalloc(sizeof(int) * nclaims);
+	Nst  = (int *) emalloc(sizeof(int) * nclaims);
+	reached = (int **) emalloc(sizeof(int *) * nclaims);
+	Selfs   = (Element **) emalloc(sizeof(Element *) * nclaims);
+	matrix  = (Element ****) emalloc(sizeof(Element ***) * nclaims); /* claims */
+
+	for (p = rdy, i = 0; p; p = p->nxt, i++)
+	{	if (p->b == N_CLAIM)
+		{	nst = max(p->s->maxel, nst);
+			Nacc[i] = claim_has_accept(p);
+	}	}
+
+	for (n = 0; n < nclaims; n++)
+	{	reached[n] = (int *) emalloc(sizeof(int) * nst);
+		matrix[n] = (Element ***) emalloc(sizeof(Element **) * nst);	/* rows */
+		for (i = 0; i < nst; i++)					/* cols */
+		{	matrix[n][i] = (Element **) emalloc(sizeof(Element *) * nst);
+	}	}
+
+	for (e = Al_El; e; e = e->Nxt)
+	{	e->status &= ~DONE;
+	}
+
+	for (p = rdy, n=0; p; p = p->nxt, n++)
+	{	if (p->b == N_CLAIM)
+		{	/* fill in matrix[n] */
+			e = p->s->frst;
+			Ist[n] = huntele(e, e->status, -1)->seqno;
+
+			reached[n][Ist[n]] = 1|2;
+			get_seq(n, p->s);
+	}	}
+
+	if (verbose)	/* show only the input automata */
+	{	print_raw();
+	}
+
+	gen_product();	/* create product automaton */
+}
+
+static int
+nxt_trans(int n, int cs, int frst)
+{	int j;
+
+	for (j = frst; j < nst; j++)
+	{	if (reached[n][cs]
+		&&  matrix[n][cs][j])
+		{	return j;
+	}	}
+	return -1;
+}
+
+static void
+print_state_nm(char *p, int *s, char *a)
+{	int i;
+	printf("%sP", p);
+	for (i = 0; i < nclaims; i++)
+	{	printf("_%d", s[i]);
+	}
+	printf("%s", a);
+}
+
+static void
+create_transition(OneState *s, SQueue *it)
+{	int n, from, upto;
+	int *F = s->combo;
+	int *T = it->state.combo;
+	Succ_List *sl;
+	Lextok *t;
+
+	if (verbose&64)
+	{	print_state_nm("", F, " ");
+		print_state_nm("-> ", T, "\t");
+	}
+
+	/* check if any of the claims is blocked */
+	/* which makes the state a dead-end */
+	for (n = 0; n < nclaims; n++)
+	{	from = F[n];
+		upto = T[n];
+		t = matrix[n][from][upto]->n;
+		if (verbose&64)
+		{	wrap_text("", t, " ");
+		}
+		if (t->ntyp == 'c'
+		&&  t->lft->ntyp == CONST)
+		{	if (t->lft->val == 0)	/* i.e., false */
+			{	goto done;
+	}	}	}
+
+	sl = (Succ_List *) emalloc(sizeof(Succ_List));
+	sl->s = it;
+	sl->nxt = s->succ;
+	s->succ = sl;
+done:
+	if (verbose&64)
+	{	printf("\n");
+	}
+}
+
+static SQueue *
+find_state(int *cs)
+{	SQueue *nq, *a = sq;
+	int i;
+
+again:	/* check in nq, sq, and then in the render q */
+	for (nq = a; nq; nq = nq->nxt)
+	{	if (same_state(nq->state.combo, cs))
+		{	return nq;	/* found */
+	}	}
+	if (a == sq && sd)
+	{	a = sd;
+		goto again; /* check the other stack too */
+	} else if (a == sd && render)
+	{	a = render;
+		goto again;
+	}
+
+	nq = (SQueue *) emalloc(sizeof(SQueue));
+	nq->state.combo = (int *) emalloc(nclaims * sizeof(int));
+	for (i = 0; i < nclaims; i++)
+	{	nq->state.combo[i] = cs[i];
+	}
+	nq->nxt = sq;	/* add to sq stack */
+	sq = nq;
+
+	return nq;
+}
+
+static SQueue *
+retrieve_state(int *s)
+{	SQueue *nq, *last = NULL;
+
+	for (nq = sd; nq; last = nq, nq = nq->nxt)
+	{	if (same_state(nq->state.combo, s))
+		{	if (last)
+			{	last->nxt = nq->nxt;
+			} else
+			{	sd = nq;
+			}
+			return nq;	/* found */
+	}	}
+
+	fatal("cannot happen: retrieve_state", 0);
+	return (SQueue *) 0;
+}
+
+static void
+all_successors(int n, OneState *cur)
+{	int i, j = 0;
+
+	if (n >= nclaims)
+	{	create_transition(cur, find_state(Nst));
+	} else
+	{	i = cur->combo[n];
+		for (;;)
+		{	j = nxt_trans(n, i, j);
+			if (j < 0) break;
+			Nst[n] = j;
+			all_successors(n+1, cur);
+			j++;
+	}	}
+}
+
+static void
+gen_product(void)
+{	OneState *cur_st;
+	SQueue *q;
+
+	find_state(Ist);	/* create initial state */
+
+	while (sq)
+	{	if (in_stack(sq, sd))
+		{	sq = sq->nxt;
+			continue;
+		}
+		cur_st = &(sq->state);
+
+		q = sq;
+		sq = sq->nxt;	/* delete from sq stack */
+		q->nxt = sd;	/* and move to done stack */
+		sd = q;
+
+		all_successors(0, cur_st);
+	}
+	/* all states are in the sd queue now */
+	prune_dead();
+	explore_product();	/* check if added accept-self-loops are reachable */
+	prune_accept();
+
+	if (verbose)
+	{	print_raw();
+	}
+
+	/* PM: merge states with identical successor lists */
+
+	/* all outgoing transitions from accept-states
+	   from claim n in copy n connect to states in copy (n+1)%nclaims
+	   only accept states from claim 0 in copy 0 are true accept states
+	   in the product
+
+	   PM: what about claims that have no accept states (e.g., restrictions)
+	*/
+
+	for (unfolding = 0; unfolding < nclaims; unfolding++)
+	{	print_product();
+	}
+}
+
+static void
+t_record(int n, Element *e, Element *g)
+{	int from = e->seqno, upto = g?g->seqno:0;
+
+	assert(from >= 0 && from < nst);
+	assert(upto >= 0 && upto < nst);
+
+	matrix[n][from][upto] = e;
+	reached[n][upto] |= 1;
+}
+
+static void
+get_sub(int n, Element *e)
+{
+	if (e->n->ntyp == D_STEP
+	||  e->n->ntyp == ATOMIC)
+	{	fatal("atomic or d_step in never claim product", 0);
+	} 
+	/* NON_ATOMIC */
+	e->n->sl->this->last->nxt = e->nxt;
+	get_seq(n, e->n->sl->this);
+
+	t_record(n, e, e->n->sl->this->frst);
+
+}
+
+static void
+set_el(int n, Element *e)
+{	Element *g;
+
+	if (e->n->ntyp == '@')	/* change to self-loop */
+	{	e->n->ntyp = CONST;
+		e->n->val = 1;	/* true */
+		e->nxt = e;
+		g = e;
+		mk_accepting(n, e);
+	} else
+
+	if (e->n->ntyp == GOTO)
+	{	g = get_lab(e->n, 1);
+		g = huntele(g, e->status, -1);
+	} else if (e->nxt)
+	{	g = huntele(e->nxt, e->status, -1);
+	} else
+	{	g = NULL;
+	}
+
+	t_record(n, e, g);
+}
+
+static void
+get_seq(int n, Sequence *s)
+{	SeqList *h;
+	Element *e;
+
+	e = huntele(s->frst, s->frst->status, -1);
+	for ( ; e; e = e->nxt)
+	{	if (e->status & DONE)
+		{	goto checklast;
+		}
+		e->status |= DONE;
+
+		if (e->n->ntyp == UNLESS)
+		{	fatal("unless stmnt in never claim product", 0);
+		}
+
+		if (e->sub)	/* IF or DO */
+		{	Lextok *x = NULL;
+			Lextok *y = NULL;
+			Lextok *haselse = NULL;
+
+			for (h = e->sub; h; h = h->nxt)
+			{	Lextok *t = h->this->frst->n;
+				if (t->ntyp == ELSE)
+				{	if (verbose&64) printf("else at line %d\n", t->ln);
+					haselse = t;
+					continue;
+				}
+				if (t->ntyp != 'c')
+				{	fatal("product, 'else' combined with non-condition", 0);
+				}
+
+				if (t->lft->ntyp == CONST	/* true */
+				&&  t->lft->val == 1
+				&&  y == NULL)
+				{	y = nn(ZN, CONST, ZN, ZN);
+					y->val = 0;
+				} else
+				{	if (!x)
+						x = t;
+					else
+						x = nn(ZN, OR, x, t);
+					if (verbose&64)
+					{	wrap_text(" [", x, "]\n");
+			}	}	}
+			if (haselse)
+			{	if (!y)
+				{	y = nn(ZN, '!', x, ZN);
+				}
+				if (verbose&64)
+				{	wrap_text(" [else: ", y, "]\n");
+				}
+				haselse->ntyp = 'c';	/* replace else */
+				haselse->lft = y;
+			}
+
+			for (h = e->sub; h; h = h->nxt)
+			{	t_record(n, e, h->this->frst);
+				get_seq(n, h->this);
+			}
+		} else
+		{	if (e->n->ntyp == ATOMIC
+			||  e->n->ntyp == D_STEP
+			||  e->n->ntyp == NON_ATOMIC)
+			{	get_sub(n, e);
+			} else 
+			{	set_el(n, e);
+			}
+		}
+checklast:	if (e == s->last)
+			break;
+	}
+}

+ 53 - 9
sys/src/cmd/spin/pc_zpp.c

@@ -16,14 +16,15 @@
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#include "spin.h"
 
 #ifdef PC
 enum cstate { PLAIN, IN_STRING, IN_QUOTE, S_COMM, COMMENT, E_COMM };
 
 #define MAXNEST	32
 #define MAXDEF	128
-#define MAXLINE	512
-#define GENEROUS 4096
+#define MAXLINE	2048
+#define GENEROUS 8192
 
 #define debug(x,y)	if (verbose) printf(x,y)
 
@@ -43,7 +44,7 @@ static struct Defines {
 static int process(char *, int, char *);
 static int zpp_do(char *);
 
-extern char *emalloc(int);	/* main.c */
+extern char *emalloc(size_t);	/* main.c */
 
 static int
 do_define(char *p)
@@ -116,7 +117,7 @@ more:		in2 = strstr(startat, d[i].src);
 
 			if (strlen(in1)+strlen(d[i].trg)+strlen(in2+j) >= GENEROUS)
 			{
-				printf("spin: circular macro expansion %s -> %s ?\n",
+				printf("spin: macro expansion overflow %s -> %s ?\n",
 					d[i].src, d[i].trg);
 				return in1;
 			}
@@ -230,8 +231,9 @@ do_if(char *p)
 }
 
 static int
-do_else(char *unused)
+do_else(char *p)
 {
+	debug("zpp: do_else %s", p);
 	if_truth[if_depth] = 1-if_truth[if_depth];
 	printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth];
 
@@ -311,14 +313,30 @@ in_comment(char *p)
 	return (state == COMMENT);
 }
 
+static int
+strip_cpp_comments(char *p)
+{	char *q;
+
+	q = strstr(p, "//");
+	if (q)
+	{	if (q > p && *(q-1) == '\\')
+		{	return strip_cpp_comments(q+1);
+		}
+		*q = '\n';
+		*(q+1) = '\0';
+		return 1;
+	}
+	return 0;
+}
+
 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 */
+	{	fprintf(stdout, "spin: error: No file '%s'\n", fnm);
+		exit(1);	/* 4.1.2 was stderr */
 	}
 	printing[0] = if_truth[0] = 1;
 	fprintf(outpp, "#line %d \"%s\"\n", lno+1, fnm);
@@ -327,7 +345,8 @@ zpp_do(char *fnm)
 		on = 0; nw_lno = 0;
 		while (n > 2 && buf[n-2] == '\\')
 		{	buf[n-2] = '\0';
-feedme:			if (!fgets(buf2, MAXLINE, inp))
+feedme:
+			if (!fgets(buf2, MAXLINE, inp))
 			{	debug("zpp: unexpected EOF ln %d\n", lno);
 				return 0;	/* switch to cpp */
 			}
@@ -339,6 +358,10 @@ feedme:			if (!fgets(buf2, MAXLINE, inp))
 			strcat(buf, buf2);
 			n = (int) strlen(buf);
 		}
+
+		if (strip_cpp_comments(&buf[on]))
+			n = (int) strlen(buf);
+
 		if (in_comment(&buf[on]))
 		{	buf[n-1] = '\0'; /* eat newline */
 			on = n-1; nw_lno = 1;
@@ -360,7 +383,7 @@ feedme:			if (!fgets(buf2, MAXLINE, inp))
 int
 try_zpp(char *fnm, char *onm)
 {	int r;
-	if ((outpp = fopen(onm, "w")) == NULL)
+	if ((outpp = fopen(onm, MFLAGS)) == NULL)
 		return 0;
 	r = zpp_do(fnm);
 	fclose(outpp);
@@ -390,6 +413,27 @@ process(char *q, int lno, char *fnm)
 	for (p = q; *p; p++)
 		if (*p != ' ' && *p != '\t')
 			break;
+
+	if (strncmp(p, "line", 4) == 0)
+	{	p += 4;
+		while (*p == ' ' || *p == '\t')
+		{	p++;
+		}
+		lno = atoi(p);
+		return 1;	/* line directive */
+	}
+	if (isdigit((int) *p))
+	{	lno = atoi(p);
+		return 1;
+	}
+	if (strncmp(p, "error", 5) == 0)
+	{	printf("spin: %s", p);
+		exit(1);
+	}
+	if (strncmp(p, "warning", 7) == 0)
+	{	printf("spin: %s", p);
+		return 1;
+	}
 	for (i = 0; i < (int) (sizeof(s)/sizeof(struct Directives)); i++)
 		if (!strncmp(s[i].directive, p, s[i].len))
 		{	if (s[i].interp

+ 16 - 18
sys/src/cmd/spin/ps_msc.c

@@ -12,14 +12,12 @@
 /* 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.       */
+/* option -M for generating a Postscript file from a simulation run.      */
 
 #include "spin.h"
 #include "version.h"
 
-#ifdef PC
-extern void free(void *);
-#endif
+/* extern void free(void *); */
 
 static char *PsPre[] = {
 	"%%%%Pages: (atend)",
@@ -110,8 +108,8 @@ static char *PsPre[] = {
 	0,
 };
 
-int MH  = 600;		/* page height - can be scaled */
-int oMH = 600;		/* page height - not scaled */
+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 */
@@ -146,7 +144,7 @@ putlegend(void)
 	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 ",
-		Version, oFname?oFname->name:"", pspno);
+		SpinVersion, oFname?oFname->name:"", pspno);
 	fprintf(pfd, "false DrawText\ngrestore\n");
 }
 
@@ -181,11 +179,11 @@ putprelude(void)
 {	char snap[256]; FILE *fd;
 
 	sprintf(snap, "%s.ps", oFname?oFname->name:"msc");
-	if (!(pfd = fopen(snap, "w")))
+	if (!(pfd = fopen(snap, MFLAGS)))
 		fatal("cannot create file '%s'", snap);
 
 	fprintf(pfd, "%%!PS-Adobe-2.0\n");
-	fprintf(pfd, "%%%%Creator: %s\n", Version);
+	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);
@@ -204,6 +202,7 @@ putprelude(void)
 		while (fgets(snap, 256, fd)) TotSteps++;
 		fclose(fd);
 	}
+	TotSteps += 10;
 	R = (int   *) emalloc(TotSteps * sizeof(int));
 	D = (int   *) emalloc(TotSteps * sizeof(int));
 	M = (short *) emalloc(TotSteps * sizeof(short));
@@ -227,6 +226,7 @@ putpostlude(void)
 		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;
@@ -260,11 +260,10 @@ 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);
+	{	if (!ProcLine[i])
+		{	psline(i, 0, i, MH-1, (float) (0.4), (float) (0.4), (float) (1.0), 1);
 			ProcLine[i] = 1;
-		}
+	}	}
 }
 
 void
@@ -320,7 +319,7 @@ spitbox(int x, int dx, int y, char *s)
 	} else
 	{	r = (float) 1.; g = (float) 1.; b = (float) 0.;
 		if (!dx
-		&&  sscanf(s, "%d:%s", &a, d) == 2	/* was &d */
+		&&  sscanf(s, "%d:%250s", &a, d) == 2	/* was &d */
 		&&  a >= 0 && a < TotSteps)
 		{	if (!I[a]
 			||  strlen(I[a]) <= strlen(s))
@@ -386,7 +385,7 @@ putpages(void)
 		}
 		if (L[i])
 		{	spitbox(M[i], 0, i, L[i]);
-			free(L[i]);
+			/* free(L[i]); */
 			lasti = i;
 		}
 	}
@@ -397,10 +396,9 @@ void
 putbox(int x)
 {
 	if (ldepth >= TotSteps)
-	{	putpostlude();
-		fprintf(stderr, "max length of %d steps exceeded\n",
+	{	fprintf(stderr, "max length of %d steps exceeded - ps file truncated\n",
 			TotSteps);
-		fatal("postscript file truncated", (char *) 0);
+		putpostlude();
 	}
 	M[ldepth] = x;
 	if (x > maxx) maxx = x;

+ 1 - 1
sys/src/cmd/spin/reprosrc.c

@@ -96,7 +96,7 @@ repro_seq(Sequence *s)
 				doindent();
 				if (e->n->ntyp == C_CODE)
 				{	printf("c_code ");
-					plunk_inline(stdout, e->n->sym->name, 1);
+					plunk_inline(stdout, e->n->sym->name, 1, 1);
 				} else if (e->n->ntyp == 'c'
 				       &&  e->n->lft->ntyp == C_EXPR)
 				{	printf("c_expr { ");

+ 52 - 21
sys/src/cmd/spin/run.c

@@ -18,7 +18,7 @@ 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;
+extern int	analyze, nproc, nstop, no_print, like_java;
 
 static long	Seed = 1;
 static int	E_Check = 0, Escape_Check = 0;
@@ -57,9 +57,9 @@ Element *
 eval_sub(Element *e)
 {	Element *f, *g;
 	SeqList *z;
-	int i, j, k;
+	int i, j, k, only_pos;
 
-	if (!e->n)
+	if (!e || !e->n)
 		return ZE;
 #ifdef DEBUG
 	printf("\n\teval_sub(%d %s: line %d) ",
@@ -69,8 +69,13 @@ eval_sub(Element *e)
 #endif
 	if (e->n->ntyp == GOTO)
 	{	if (Rvous) return ZE;
-		LastStep = e; f = get_lab(e->n, 1);
+		LastStep = e;
+		f = get_lab(e->n, 1);
+		f = huntele(f, e->status, -1); /* 5.2.3: was missing */
 		cross_dsteps(e->n, f->n);
+#ifdef DEBUG
+		printf("GOTO leads to %d\n", f->seqno);
+#endif
 		return f;
 	}
 	if (e->n->ntyp == UNLESS)
@@ -80,6 +85,7 @@ eval_sub(Element *e)
 	{	Element *has_else = ZE;
 		Element *bas_else = ZE;
 		int nr_else = 0, nr_choices = 0;
+		only_pos = -1;
 
 		if (interactive
 		&& !MadeChoice && !E_Check
@@ -89,8 +95,10 @@ eval_sub(Element *e)
 		{	printf("Select stmnt (");
 			whoruns(0); printf(")\n");
 			if (nproc-nstop > 1)
-			printf("\tchoice 0: other process\n");
-		}
+			{	printf("\tchoice 0: other process\n");
+				nr_choices++;
+				only_pos = 0;
+		}	}
 		for (z = e->sub, j=0; z; z = z->nxt)
 		{	j++;
 			if (interactive
@@ -113,13 +121,21 @@ eval_sub(Element *e)
 				if (!Enabled0(z->this->frst))
 					printf("unexecutable, ");
 				else
-					nr_choices++;
+				{	nr_choices++;
+					only_pos = j;
+				}
 				comment(stdout, z->this->frst->n, 0);
 				printf("\n");
 		}	}
 
 		if (nr_choices == 0 && has_else)
-			printf("\tchoice %d: (else)\n", nr_else);
+		{	printf("\tchoice %d: (else)\n", nr_else);
+			only_pos = nr_else;
+		}
+
+		if (nr_choices <= 1 && only_pos != -1 && !MadeChoice)
+		{	MadeChoice = only_pos;
+		}
 
 		if (interactive && depth >= jumpsteps
 		&& !Escape_Check
@@ -132,8 +148,11 @@ eval_sub(Element *e)
 				else
 					printf("Select [0-%d]: ", j);
 				fflush(stdout);
-				scanf("%s", buf);
-				if (isdigit(buf[0]))
+				if (scanf("%64s", buf) <= 0)
+				{	printf("no input\n");
+					return ZE;
+				}
+				if (isdigit((int)buf[0]))
 					k = atoi(buf);
 				else
 				{	if (buf[0] == 'q')
@@ -155,6 +174,7 @@ eval_sub(Element *e)
 			else
 				k = Rand()%j;	/* nondeterminism */
 		}
+
 		has_else = ZE;
 		bas_else = ZE;
 		for (i = 0, z = e->sub; i < j+k; i++)
@@ -233,6 +253,9 @@ eval_sub(Element *e)
 #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
+#if 1
+			if (!s_trail)	/* trail determines selections, new 5.2.5 */
 #endif
 			{	Escape_Check++;
 				if (like_java)
@@ -246,7 +269,11 @@ eval_sub(Element *e)
 				{	for (x = e->esc; x; x = x->nxt)
 					{	if ((g = eval_sub(x->this->frst)) != ZE)
 						{	if (verbose&4)
-								printf("\tEscape taken\n");
+							{	printf("\tEscape taken ");
+								if (g->n && g->n->fn)
+									printf("%s:%d", g->n->fn->name, g->n->ln);
+								printf("\n");
+							}
 							Escape_Check--;
 							return g;
 				}	}	}
@@ -386,13 +413,17 @@ eval(Lextok *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);
+	case C_CODE: if (!analyze)
+		     {	printf("%s:\t", now->sym->name);
+		     	plunk_inline(stdout, now->sym->name, 0, 1);
+		     }
 		     return 1; /* uninterpreted */
 
-	case C_EXPR: printf("%s:\t", now->sym->name);
-		     plunk_expr(stdout, now->sym->name);
-		     printf("\n");
+	case C_EXPR: if (!analyze)
+		     {	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;
@@ -426,7 +457,6 @@ printm(FILE *fd, Lextok *n)
 			j = n->lft->val;
 		else
 			j = eval(n->lft);
-		Buf[0] = '\0';
 		sr_buf(j, 1);
 		dotag(fd, Buf);
 	}
@@ -437,9 +467,9 @@ int
 interprint(FILE *fd, Lextok *n)
 {	Lextok *tmp = n->lft;
 	char c, *s = n->sym->name;
-	int i, j; char lbuf[512];
-	extern char Buf[];
-	char tBuf[4096];
+	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)
@@ -490,7 +520,7 @@ append:			 strcat(Buf, lbuf);
 		}
 		dotag(fd, Buf);
 	}
-	if (strlen(Buf) > 4096) fatal("printf string too long", 0);
+	if (strlen(Buf) >= 4096) fatal("printf string too long", 0);
 	return 1;
 }
 
@@ -600,3 +630,4 @@ pc_enabled(Lextok *n)
 		}
 	return result;
 }
+

+ 101 - 33
sys/src/cmd/spin/sched.c

@@ -20,7 +20,7 @@ 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;
+extern int	limited_vis, old_scope_rules, product, nclaims;
 
 RunList		*X   = (RunList  *) 0;
 RunList		*run = (RunList  *) 0;
@@ -42,13 +42,16 @@ runnable(ProcList *p, int weight, int noparams)
 
 	r->n  = p->n;
 	r->tn = p->tn;
+	r->b  = p->b;
 	r->pid = nproc++ - nstop + Skip_claim;
 
-	if ((verbose&4) || (verbose&32))
-		printf("Starting %s with pid %d\n", p->n->name, r->pid);
+	if (!noparams && ((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:"--");
+		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;
@@ -59,13 +62,15 @@ runnable(ProcList *p, int weight, int noparams)
 	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)
+ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov, enum btypes b)
 	/* n=name, p=formals, s=body det=deterministic prov=provided */
 {	ProcList *r = (ProcList *) emalloc(sizeof(ProcList));
 	Lextok *fp, *fpt; int j; extern int Npars;
@@ -73,8 +78,12 @@ ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov)
 	r->n = n;
 	r->p = p;
 	r->s = s;
+	r->b = b;
 	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;
@@ -135,6 +144,7 @@ announce(char *w)
 
 	if (dumptab
 	||  analyze
+	||  product
 	||  s_trail
 	|| !(verbose&4))
 		return;
@@ -208,26 +218,29 @@ start_claim(int n)
 	RunList  *r, *q = (RunList *) 0;
 
 	for (p = rdy; p; p = p->nxt)
-		if (p->tn == n
-		&&  strcmp(p->n->name, ":never:") == 0)
+		if (p->tn == n && p->b == N_CLAIM)
 		{	runnable(p, 1, 1);
 			goto found;
 		}
-	printf("spin: couldn't find claim (ignored)\n");
+	printf("spin: couldn't find claim %d (ignored)\n", n);
+	if (verbose&32)
+	for (p = rdy; p; p = p->nxt)
+		printf("\t%d = %s\n", p->tn, p->n->name);
+
 	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:");
+	{	extern char Buf[];
+		depth = 0;
+		sprintf(Buf, "%d:%s", 0, p->n->name);
+		pstext(0, Buf);
 		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 (r->b != N_CLAIM)
+			{	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 */
 
@@ -457,9 +470,12 @@ try_more:	for (X = run, k = 1; X; X = X->nxt)
 		} else
 		{	char buf[256];
 			fflush(stdout);
-			scanf("%s", buf);
+			if (scanf("%64s", buf) == 0)
+			{	printf("\tno input\n");
+				goto try_again;
+			}
 			j = -1;
-			if (isdigit(buf[0]))
+			if (isdigit((int) buf[0]))
 				j = atoi(buf);
 			else
 			{	if (buf[0] == 'q')
@@ -483,6 +499,24 @@ try_more:	for (X = run, k = 1; X; X = X->nxt)
 	return Y;
 }
 
+void
+multi_claims(void)
+{	ProcList *p, *q = NULL;
+
+	if (nclaims > 1)
+	{	printf("  the model contains %d never claims:", nclaims);
+		for (p = rdy; p; p = p->nxt)
+		{	if (p->b == N_CLAIM)
+			{	printf("%s%s", q?", ":" ", p->n->name);
+				q = p;
+		}	}
+		printf("\n");
+		printf("  only one claim is used in a verification run\n");
+		printf("  choose which one with ./pan -N name (defaults to -N %s)\n",
+			q?q->n->name:"--");
+	}
+}
+
 void
 sched(void)
 {	Element *e;
@@ -504,10 +538,16 @@ sched(void)
 		printf("models with synchronous channels.\n");
 		nr_errs++;
 	}
+	if (product)
+	{	sync_product();
+		alldone(0);
+	}
 	if (analyze)
 	{	gensrc();
+		multi_claims();
 		return;
-	} else if (s_trail)
+	}
+	if (s_trail)
 	{	match_trail();
 		return;
 	}
@@ -543,7 +583,7 @@ sched(void)
 		depth++; LastStep = ZE;
 		oX = X;	/* a rendezvous could change it */
 		go = 1;
-		if (X && X->prov && X->pc
+		if (X->prov && X->pc
 		&& !(X->pc->status & D_ATOM)
 		&& !eval(X->prov))
 		{	if (!xspin && ((verbose&32) || (verbose&4)))
@@ -570,7 +610,8 @@ sched(void)
 				if (xspin)
 					printf("\n");
 			}
-			if (oX != X)
+			if (oX != X
+			||  (X->pc->status & (ATOM|D_ATOM)))		/* new 5.0 */
 			{	e = silent_moves(e);
 				notbeyond = 0;
 			}
@@ -587,10 +628,12 @@ sched(void)
 			}
 		} else
 		{	depth--;
-			if (oX->pc->status & D_ATOM)
-			 non_fatal("stmnt in d_step blocks", (char *)0);
-
-			if (X->pc->n->ntyp == '@'
+			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;
@@ -618,6 +661,9 @@ sched(void)
 						dotag(stdout, "timeout\n");
 						X = oX;
 		}	}	}	}
+
+		if (!run || !X) break;	/* new 5.0 */
+
 		Y = pickproc(X);
 		notbeyond = 0;
 	}
@@ -692,7 +738,9 @@ addsymbol(RunList *r, Symbol  *s)
 	int i;
 
 	for (t = r->symtab; t; t = t->next)
-		if (strcmp(t->name, s->name) == 0)
+		if (strcmp(t->name, s->name) == 0
+		&& (old_scope_rules
+		 || strcmp((const char *)t->bscp, (const char *)s->bscp) == 0))
 			return;		/* it's already there */
 
 	t = (Symbol *) emalloc(sizeof(Symbol));
@@ -704,13 +752,18 @@ addsymbol(RunList *r, Symbol  *s)
 	t->ini  = s->ini;
 	t->setat = depth;
 	t->context = r->n;
+
+	t->bscp  = (unsigned char *) emalloc(strlen((const char *)s->bscp)+1);
+	strcpy((char *)t->bscp, (const char *)s->bscp);
+
 	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 */
+		{	(void) checkvar(t, 0);	/* initialize it */
+		}
 	} else
 	{	if (s->Sval)
 			fatal("saw preinitialized struct %s", s->name);
@@ -759,7 +812,7 @@ oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p)
 
 	if (!a)
 		fatal("missing actual parameters: '%s'", p->n->name);
-	if (t->sym->nel != 1)
+	if (t->sym->nel > 1 || t->sym->isarray)
 		fatal("array in parameter list, %s", t->sym->name);
 	k = eval(a->lft);
 
@@ -768,7 +821,7 @@ oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p)
 	ft = Sym_typ(t);
 
 	if (at != ft && (at == CHAN || ft == CHAN))
-	{	char buf[128], tag1[64], tag2[64];
+	{	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)",
@@ -809,7 +862,8 @@ findloc(Symbol *s)
 		return ZS;
 	}
 	for (r = X->symtab; r; r = r->next)
-		if (strcmp(r->name, s->name) == 0)
+		if (strcmp(r->name, s->name) == 0
+		&& (old_scope_rules || strcmp((const char *)r->bscp, (const char *)s->bscp) == 0))
 			break;
 	if (!r)
 	{	addsymbol(X, s);
@@ -895,6 +949,7 @@ talk(RunList *r)
 void
 p_talk(Element *e, int lnr)
 {	static int lastnever = -1;
+	static char nbuf[128];
 	int newnever = -1;
 
 	if (e && e->n)
@@ -918,9 +973,22 @@ p_talk(Element *e, int lnr)
 
 	whoruns(lnr);
 	if (e)
-	{	printf("line %3d %s (state %d)",
+	{	if (e->n)
+		{	char *ptr = e->n->fn->name;
+			char *qtr = nbuf;
+			while (*ptr != '\0')
+			{	if (*ptr != '"')
+				{	*qtr++ = *ptr;
+				}
+				ptr++;
+			}
+			*qtr = '\0';
+		} else
+		{	strcpy(nbuf, "-");
+		}
+		printf("%s:%d (state %d)",
+			nbuf,
 			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 */
@@ -978,7 +1046,7 @@ remotevar(Lextok *n)
 		printf("	%s: i=%d, prno=%d, ->pid=%d\n", Y->n->name, i, prno, Y->pid);
 	}
 #endif
-	i = nproc - nstop;
+	i = nproc - nstop + Skip_claim;	/* 6.0: added Skip_claim */
 	for (Y = run; Y; Y = Y->nxt)
 	if (--i == prno)
 	{	if (strcmp(Y->n->name, n->lft->sym->name) != 0)

+ 30 - 9
sys/src/cmd/spin/spin.h

@@ -1,6 +1,6 @@
 /***** spin: spin.h *****/
 
-/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories.     */
+/* Copyright (c) 1989-2009 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  */
@@ -16,12 +16,16 @@
 #include <string.h>
 #include <ctype.h>
 
+enum	    { INIV, PUTV, LOGV };	/* for pangen[14].c */
+enum btypes { NONE, N_CLAIM, I_PROC, A_PROC, P_PROC, E_TRACE, N_TRACE };
+
 typedef struct Lextok {
 	unsigned short	ntyp;	/* node type */
 	short	ismtyp;		/* CONST derived from MTYP */
 	int	val;		/* value attribute */
 	int	ln;		/* line number */
 	int	indstep;	/* part of d_step sequence */
+	int	uiid;		/* inline id, if non-zero */
 	struct Symbol	*fn;	/* file name */
 	struct Symbol	*sym;	/* symbol reference */
         struct Sequence *sq;	/* sequence */
@@ -54,6 +58,8 @@ typedef struct Symbol {
 				  64=treat as if local; 128=read at least once
 				 */
 	unsigned char	colnr;	/* for use with xspin during simulation */
+	unsigned char	isarray; /* set if decl specifies array bound */
+	unsigned char	*bscp;	/* block scope */
 	int	nbits;		/* optional width specifier */
 	int	nel;		/* 1 if scalar, >1 if array   */
 	int	setat;		/* last depth value changed   */
@@ -124,7 +130,7 @@ typedef struct Element {
 	int	merge_single;
 	short	merge_in;	/* nr of incoming edges */
 	short	merge_mark;	/* state was generated in merge sequence */
-	unsigned char	status;	/* used by analyzer generator  */
+	unsigned int	status;	/* used by analyzer generator  */
 	struct FSM_use	*dead;	/* optional dead variable list */
 	struct SeqList	*sub;	/* subsequences, for compounds */
 	struct SeqList	*esc;	/* zero or more escape sequences */
@@ -148,6 +154,7 @@ typedef struct Label {
 	Symbol	*s;
 	Symbol	*c;
 	Element	*e;
+	int	uiid;		/* non-zero if label appears in an inline */
 	int	visible;	/* label referenced in claim (slice relevant) */
 	struct Label	*nxt;
 } Label;
@@ -162,6 +169,7 @@ typedef struct RunList {
 	int	tn;		/* ordinal of type */
 	int	pid;		/* process id      */
 	int	priority;	/* for simulations only */
+	enum btypes b;		/* the type of process */
 	Element	*pc;		/* current stmnt   */
 	Sequence *ps;		/* used by analyzer generator */
 	Lextok	*prov;		/* provided clause */
@@ -174,8 +182,10 @@ typedef struct ProcList {
 	Lextok	*p;		/* parameters */
 	Sequence *s;		/* body       */
 	Lextok	*prov;		/* provided clause */
+	enum btypes b;		/* e.g., claim, trace, proc */
 	short	tn;		/* ordinal number */
-	short	det;		/* deterministic */
+	unsigned char	det;	/* deterministic */
+	unsigned char   unsafe;	/* contains global var inits */
 	struct ProcList	*nxt;	/* linked list */
 } ProcList;
 
@@ -194,7 +204,8 @@ typedef	Lextok *Lexptr;
 #define DONE2	 16		/* used in putcode and main*/
 #define D_ATOM	 32		/* deterministic atomic    */
 #define ENDSTATE 64		/* normal endstate         */
-#define CHECK2	128
+#define CHECK2	128		/* status bits for remote ref check */
+#define CHECK3	256		/* status bits for atomic jump check */
 
 #define Nhash	255    		/* slots in symbol hash-table */
 
@@ -216,12 +227,17 @@ typedef	Lextok *Lexptr;
 
 #define SOMETHINGBIG	65536
 #define RATHERSMALL	512
+#define MAXSCOPESZ	1024
 
 #ifndef max
 #define max(a,b) (((a)<(b)) ? (b) : (a))
 #endif
 
-enum	{ INIV, PUTV, LOGV };	/* for pangen[14].c */
+#ifdef PC
+	#define MFLAGS	"wb"
+#else
+	#define MFLAGS	"w"
+#endif
 
 /***** prototype definitions *****/
 Element	*eval_sub(Element *);
@@ -239,7 +255,7 @@ Lextok	*rem_lab(Symbol *, Lextok *, Symbol *);
 Lextok	*rem_var(Symbol *, Lextok *, Symbol *, Lextok *);
 Lextok	*tail_add(Lextok *, Lextok *);
 
-ProcList *ready(Symbol *, Lextok *, Sequence *, int, Lextok *);
+ProcList *ready(Symbol *, Lextok *, Sequence *, int, Lextok *, enum btypes);
 
 SeqList	*seqlist(Sequence *, SeqList *);
 Sequence *close_seq(int);
@@ -250,7 +266,7 @@ Symbol	*has_lab(Element *, int);
 Symbol	*lookup(char *);
 Symbol	*prep_inline(Symbol *, Lextok *);
 
-char	*emalloc(int);
+char	*emalloc(size_t);
 long	Rand(void);
 
 int	any_oper(Lextok *, int);
@@ -274,12 +290,14 @@ int	has_typ(Lextok *, int);
 int	in_bound(Symbol *, int);
 int	interprint(FILE *, Lextok *);
 int	printm(FILE *, Lextok *);
+int	is_inline(void);
 int	ismtype(char *);
 int	isproctype(char *);
 int	isutype(char *);
 int	Lval_struct(Lextok *, Symbol *, int, int);
 int	main(int, char **);
 int	pc_value(Lextok *);
+int	pid_is_claim(int);
 int	proper_enabler(Lextok *);
 int	putcode(FILE *, Sequence *, Element *, int, int, int);
 int	q_is_sync(Lextok *);
@@ -321,6 +339,7 @@ void	check_param_count(int, Lextok *);
 void	checkrun(Symbol *, int);
 void	comment(FILE *, Lextok *, int);
 void	cross_dsteps(Lextok *, Lextok *);
+void	disambiguate(void);
 void	doq(Symbol *, int, RunList *);
 void	dotag(FILE *, char *);
 void	do_locinits(FILE *);
@@ -355,7 +374,7 @@ void	pickup_inline(Symbol *, Lextok *);
 void	plunk_c_decls(FILE *);
 void	plunk_c_fcts(FILE *);
 void	plunk_expr(FILE *, char *);
-void	plunk_inline(FILE *, char *, int);
+void	plunk_inline(FILE *, char *, int, int);
 void	prehint(Symbol *);
 void	preruse(FILE *, Lextok *);
 void	prune_opts(Lextok *);
@@ -378,15 +397,17 @@ void	setptype(Lextok *, int, Lextok *);
 void	setuname(Lextok *);
 void	setutype(Lextok *, Symbol *, Lextok *);
 void	setxus(Lextok *, int);
+void	show_lab(void);
 void	Srand(unsigned);
 void	start_claim(int);
 void	struct_name(Lextok *, Symbol *, int, char *);
 void	symdump(void);
 void	symvar(Symbol *);
+void	sync_product(void);
 void	trackchanuse(Lextok *, Lextok *, int);
 void	trackvar(Lextok *, Lextok *);
 void	trackrun(Lextok *);
-void	trapwonly(Lextok *, char *);	/* spin.y and main.c */
+void	trapwonly(Lextok * /* , char * */);	/* spin.y and main.c */
 void	typ2c(Symbol *);
 void	typ_ck(int, int, char *);
 void	undostmnt(Lextok *, int);

+ 272 - 47
sys/src/cmd/spin/spin.y

@@ -11,26 +11,44 @@
 
 %{
 #include "spin.h"
+#include <sys/types.h>
+#ifndef PC
+#include <unistd.h>
+#endif
 #include <stdarg.h>
 
 #define YYDEBUG	0
 #define Stop	nn(ZN,'@',ZN,ZN)
+#define PART0	"place initialized var decl of "
+#define PART1	"place initialized chan decl of "
+#define PART2	" at start of proctype "
+
+static	Lextok *ltl_to_string(Lextok *);
 
 extern  Symbol	*context, *owner;
-extern  int	u_sync, u_async, dumptab;
+extern	Lextok *for_body(Lextok *, int);
+extern	void for_setup(Lextok *, Lextok *, Lextok *);
+extern	Lextok *for_index(Lextok *, Lextok *);
+extern	Lextok *sel_index(Lextok *, Lextok *, Lextok *);
+extern  int	u_sync, u_async, dumptab, scope_level;
+extern	int	initialization_ok, split_decl;
 extern	short	has_sorted, has_random, has_enabled, has_pcvalue, has_np;
 extern	short	has_code, has_state, has_io;
 extern	void	count_runs(Lextok *);
 extern	void	no_internals(Lextok *);
 extern	void	any_runs(Lextok *);
+extern	void	ltl_list(char *, char *);
 extern	void	validref(Lextok *, Lextok *);
 extern	char	yytext[];
 
 int	Mpars = 0;	/* max nr of message parameters  */
-int	runsafe = 1;	/* 1 if all run stmnts are in init */
+int	nclaims = 0;	/* nr of never claims */
+int	ltl_mode = 0;	/* set when parsing an ltl formula */
 int	Expand_Ok = 0, realread = 1, IArgs = 0, NamesNotAdded = 0;
+int	in_for = 0;
 char	*claimproc = (char *) 0;
 char	*eventmap = (char *) 0;
+static	char *ltl_name;
 
 static	int	Embedded = 0, inEventMap = 0, has_ini = 0;
 
@@ -41,7 +59,7 @@ static	int	Embedded = 0, inEventMap = 0, has_ini = 0;
 %token	RUN LEN ENABLED EVAL PC_VAL
 %token	TYPEDEF MTYPE INLINE LABEL OF
 %token	GOTO BREAK ELSE SEMI
-%token	IF FI DO OD SEP
+%token	IF FI DO OD FOR SELECT IN SEP DOTDOT
 %token	ATOMIC NON_ATOMIC D_STEP UNLESS
 %token  TIMEOUT NONPROGRESS
 %token	ACTIVE PROCTYPE D_PROCTYPE
@@ -50,12 +68,16 @@ static	int	Embedded = 0, inEventMap = 0, has_ini = 0;
 %token	FULL EMPTY NFULL NEMPTY
 %token	CONST TYPE XU			/* val */
 %token	NAME UNAME PNAME INAME		/* sym */
-%token	STRING CLAIM TRACE INIT		/* sym */
+%token	STRING CLAIM TRACE INIT	LTL	/* sym */
 
 %right	ASGN
 %left	SND O_SND RCV R_RCV /* SND doubles as boolean negation */
+%left	IMPLIES EQUIV			/* ltl */
 %left	OR
 %left	AND
+%left	ALWAYS EVENTUALLY		/* ltl */
+%left	UNTIL WEAK_UNTIL RELEASE	/* ltl */
+%right	NEXT				/* ltl */
 %left	'|'
 %left	'^'
 %left	'&'
@@ -81,6 +103,7 @@ units	: unit
 unit	: proc		/* proctype { }       */
 	| init		/* init { }           */
 	| claim		/* never claim        */
+	| ltl		/* ltl formula        */
 	| events	/* event assertions   */
 	| one_decl	/* variables, chans   */
 	| utype		/* user defined types */
@@ -91,7 +114,7 @@ unit	: proc		/* proctype { }       */
 	;
 
 proc	: inst		/* optional instantiator */
-	  proctype NAME	{
+	  proctype NAME	{ 
 			  setptype($3, PROCTYPE, ZN);
 			  setpname($3);
 			  context = $3->sym;
@@ -106,13 +129,22 @@ proc	: inst		/* optional instantiator */
 	  Opt_priority
 	  Opt_enabler
 	  body		{ ProcList *rl;
-			  rl = ready($3->sym, $6, $11->sq, $2->val, $10);
 			  if ($1 != ZN && $1->val > 0)
 			  {	int j;
+				rl = ready($3->sym, $6, $11->sq, $2->val, $10, A_PROC);
 			  	for (j = 0; j < $1->val; j++)
-				runnable(rl, $9?$9->val:1, 1);
+				{	runnable(rl, $9?$9->val:1, 1);
+				}
 				announce(":root:");
 				if (dumptab) $3->sym->ini = $1;
+			  } else
+			  {	rl = ready($3->sym, $6, $11->sq, $2->val, $10, P_PROC);
+			  }
+			  if (rl && has_ini == 1)	/* global initializations, unsafe */
+			  {	/* printf("proctype %s has initialized data\n",
+					$3->sym->name);
+				 */
+				rl->unsafe = 1;
 			  }
 			  context = ZS;
 			}
@@ -133,10 +165,10 @@ inst	: /* empty */	{ $$ = ZN; }
 			  $$ = nn(ZN,CONST,ZN,ZN);
 			  $$->val = 0;
 			  if (!$3->sym->type)
-				non_fatal("undeclared variable %s",
+				fatal("undeclared variable %s",
 					$3->sym->name);
 			  else if ($3->sym->ini->ntyp != CONST)
-				non_fatal("need constant initializer for %s\n",
+				fatal("need constant initializer for %s\n",
 					$3->sym->name);
 			  else
 				$$->val = $3->sym->ini->val;
@@ -146,30 +178,68 @@ inst	: /* empty */	{ $$ = ZN; }
 init	: INIT		{ context = $1->sym; }
 	  Opt_priority
 	  body		{ ProcList *rl;
-			  rl = ready(context, ZN, $4->sq, 0, ZN);
+			  rl = ready(context, ZN, $4->sq, 0, ZN, I_PROC);
 			  runnable(rl, $3?$3->val:1, 1);
 			  announce(":root:");
 			  context = ZS;
         		}
 	;
 
-claim	: CLAIM		{ context = $1->sym;
-			  if (claimproc)
-				non_fatal("claim %s redefined", claimproc);
+ltl	: LTL optname2		{ ltl_mode = 1; ltl_name = $2->sym->name; }
+	  ltl_body		{ if ($4) ltl_list($2->sym->name, $4->sym->name);
+			  ltl_mode = 0;
+			}
+	;
+
+ltl_body: '{' full_expr OS '}' { $$ = ltl_to_string($2); }
+	| error		{ $$ = NULL; }
+	;
+
+claim	: CLAIM	optname	{ if ($2 != ZN)
+			  {	$1->sym = $2->sym;	/* new 5.3.0 */
+			  }
+			  nclaims++;
+			  context = $1->sym;
+			  if (claimproc && !strcmp(claimproc, $1->sym->name))
+			  {	fatal("claim %s redefined", claimproc);
+			  }
 			  claimproc = $1->sym->name;
 			}
-	  body		{ (void) ready($1->sym, ZN, $3->sq, 0, ZN);
+	  body		{ (void) ready($1->sym, ZN, $4->sq, 0, ZN, N_CLAIM);
         		  context = ZS;
         		}
 	;
 
+optname : /* empty */	{ char tb[32];
+			  memset(tb, 0, 32);
+			  sprintf(tb, "never_%d", nclaims);
+			  $$ = nn(ZN, NAME, ZN, ZN);
+			  $$->sym = lookup(tb);
+			}
+	| NAME		{ $$ = $1; }
+	;
+
+optname2 : /* empty */ { char tb[32]; static int nltl = 0;
+			  memset(tb, 0, 32);
+			  sprintf(tb, "ltl_%d", nltl++);
+			  $$ = nn(ZN, NAME, ZN, ZN);
+			  $$->sym = lookup(tb);
+			}
+	| NAME		{ $$ = $1; }
+	;
+
 events : TRACE		{ context = $1->sym;
 			  if (eventmap)
 				non_fatal("trace %s redefined", eventmap);
 			  eventmap = $1->sym->name;
 			  inEventMap++;
 			}
-	  body		{ (void) ready($1->sym, ZN, $3->sq, 0, ZN);
+	  body		{
+			  if (strcmp($1->sym->name, ":trace:") == 0)
+			  {	(void) ready($1->sym, ZN, $3->sq, 0, ZN, E_TRACE);
+			  } else
+			  {	(void) ready($1->sym, ZN, $3->sq, 0, ZN, N_TRACE);
+			  }
         		  context = ZS;
 			  inEventMap--;
 			}
@@ -252,7 +322,12 @@ cexpr	: C_EXPR		{ Symbol *s;
 
 body	: '{'			{ open_seq(1); }
           sequence OS		{ add_seq(Stop); }
-          '}'			{ $$->sq = close_seq(0); }
+          '}'			{ $$->sq = close_seq(0);
+				  if (scope_level != 0)
+				  {	non_fatal("missing '}' ?", 0);
+					scope_level = 0;
+				  }
+				}
 	;
 
 sequence: step			{ if ($1) add_seq($1); }
@@ -277,7 +352,9 @@ asgn:	/* empty */
 	| ASGN
 	;
 
-one_decl: vis TYPE var_list	{ setptype($3, $2->val, $1); $$ = $3; }
+one_decl: vis TYPE var_list	{ setptype($3, $2->val, $1);
+				  $$ = $3;
+				}
 	| vis UNAME var_list	{ setutype($3, $2->sym, $1);
 				  $$ = expand($3, Expand_Ok);
 				}
@@ -314,17 +391,34 @@ ivar    : vardcl           	{ $$ = $1;
 				  $1->sym->ini = nn(ZN,CONST,ZN,ZN);
 				  $1->sym->ini->val = 0;
 				}
-	| vardcl ASGN expr   	{ $1->sym->ini = $3; $$ = $1;
-				  trackvar($1,$3); has_ini = 1;
+	| vardcl ASGN expr   	{ $$ = $1;
+				  $1->sym->ini = $3;
+				  trackvar($1,$3);
+				  if ($3->ntyp == CONST
+				  || ($3->ntyp == NAME && $3->sym->context))
+				  {	has_ini = 2; /* local init */
+				  } else
+				  {	has_ini = 1; /* possibly global */
+				  }
+				  if (!initialization_ok && split_decl)
+				  {	nochan_manip($1, $3, 0);
+				  	no_internals($1);
+					non_fatal(PART0 "'%s'" PART2, $1->sym->name);
+				  }
 				}
 	| vardcl ASGN ch_init	{ $1->sym->ini = $3;
 				  $$ = $1; has_ini = 1;
+				  if (!initialization_ok && split_decl)
+				  {	non_fatal(PART1 "'%s'" PART2, $1->sym->name);
+				  }
 				}
 	;
 
 ch_init : '[' CONST ']' OF
-	  '{' typ_list '}'	{ if ($2->val) u_async++;
-				  else u_sync++;
+	  '{' typ_list '}'	{ if ($2->val)
+					u_async++;
+				  else
+					u_sync++;
         			  {	int i = cnt_mpars($6);
 					Mpars = max(Mpars, i);
 				  }
@@ -342,13 +436,18 @@ vardcl  : NAME  		{ $1->sym->nel = 1; $$ = $1; }
 				  }
 				  $1->sym->nel = 1; $$ = $1;
 				}
-	| NAME '[' CONST ']'	{ $1->sym->nel = $3->val; $$ = $1; }
+	| NAME '[' CONST ']'	{ $1->sym->nel = $3->val; $1->sym->isarray = 1; $$ = $1; }
 	;
 
 varref	: cmpnd			{ $$ = mk_explicit($1, Expand_Ok, NAME); }
 	;
 
-pfld	: NAME			{ $$ = nn($1, NAME, ZN, ZN); }
+pfld	: NAME			{ $$ = nn($1, NAME, ZN, ZN);
+				  if ($1->sym->isarray && !in_for)
+				  {	non_fatal("missing array index for '%s'",
+						$1->sym->name);
+				  }
+				}
 	| NAME			{ owner = ZS; }
 	  '[' expr ']'		{ $$ = nn($1, NAME, $4, ZN); }
 	;
@@ -363,7 +462,7 @@ cmpnd	: pfld			{ Embedded++;
 				  Embedded--;
 				  if (!Embedded && !NamesNotAdded
 				  &&  !$1->sym->type)
-				   non_fatal("undeclared variable: %s",
+				   fatal("undeclared variable: %s",
 						$1->sym->name);
 				  if ($3) validref($1, $3->lft);
 				  owner = ZS;
@@ -374,13 +473,19 @@ sfld	: /* empty */		{ $$ = ZN; }
 	| '.' cmpnd %prec DOT	{ $$ = nn(ZN, '.', $2, ZN); }
 	;
 
-stmnt	: Special		{ $$ = $1; }
-	| Stmnt			{ $$ = $1;
+stmnt	: Special		{ $$ = $1; initialization_ok = 0; }
+	| Stmnt			{ $$ = $1; initialization_ok = 0;
 				  if (inEventMap)
 				   non_fatal("not an event", (char *)0);
 				}
 	;
 
+for_pre : FOR '('				{ in_for = 1; }
+	  varref			{ $$ = $4; }
+	;
+
+for_post: '{' sequence OS '}' ;
+
 Special : varref RCV		{ Expand_Ok++; }
 	  rargs			{ Expand_Ok--; has_io++;
 				  $$ = nn($1,  'r', $1, $4);
@@ -392,6 +497,18 @@ Special : varref RCV		{ Expand_Ok++; }
 				  $$->val=0; trackchanuse($4, ZN, 'S');
 				  any_runs($4);
 				}
+	| for_pre ':' expr DOTDOT expr ')'	{
+				  for_setup($1, $3, $5); in_for = 0;
+				}
+	  for_post			{ $$ = for_body($1, 1);
+				}
+	| for_pre IN varref ')'	{ $$ = for_index($1, $3); in_for = 0;
+				}
+	  for_post			{ $$ = for_body($5, 1);
+				}
+	| SELECT '(' varref ':' expr DOTDOT expr ')' {
+				  $$ = sel_index($3, $5, $7);
+				}
 	| IF options FI 	{ $$ = nn($1, IF, ZN, ZN);
         			  $$->sl = $2->sl;
 				  prune_opts($$);
@@ -422,7 +539,7 @@ Special : varref RCV		{ Expand_Ok++; }
 				}
 	;
 
-Stmnt	: varref ASGN expr	{ $$ = nn($1, ASGN, $1, $3);
+Stmnt	: varref ASGN full_expr	{ $$ = nn($1, ASGN, $1, $3);
 				  trackvar($1, $3);
 				  nochan_manip($1, $3, 0);
 				  no_internals($1);
@@ -482,7 +599,9 @@ Stmnt	: varref ASGN expr	{ $$ = nn($1, ASGN, $1, $3);
         			  $$->sl = seqlist(close_seq(3), 0);
         			  make_atomic($$->sl->this, 0);
         			}
-	| D_STEP '{'		{ open_seq(0); rem_Seq(); }
+	| D_STEP '{'		{ open_seq(0);
+				  rem_Seq();
+				}
           sequence OS '}'   	{ $$ = nn($1, D_STEP, ZN, ZN);
         			  $$->sl = seqlist(close_seq(4), 0);
         			  make_atomic($$->sl->this, D_ATOM);
@@ -548,10 +667,8 @@ expr    : '(' expr ')'		{ $$ = $2; }
 
 	| RUN aname		{ Expand_Ok++;
 				  if (!context)
-				  fatal("used 'run' outside proctype",
+				   fatal("used 'run' outside proctype",
 					(char *) 0);
-				  if (strcmp(context->name, ":init:") != 0)
-					runsafe = 0;
 				}
 	  '(' args ')'
 	  Opt_priority		{ Expand_Ok--;
@@ -572,7 +689,7 @@ expr    : '(' expr ')'		{ $$ = $2; }
 				  $$ = nn($1, 'R', $1, $5);
 				  $$->val = has_random = 1;
 				}
-	| varref		{ $$ = $1; trapwonly($1, "varref"); }
+	| varref		{ $$ = $1; trapwonly($1 /*, "varref" */); }
 	| cexpr			{ $$ = $1; }
 	| CONST 		{ $$ = nn(ZN,CONST,ZN,ZN);
 				  $$->ismtyp = $1->ismtyp;
@@ -591,6 +708,7 @@ expr    : '(' expr ')'		{ $$ = $2; }
 	  			{ $$ = rem_var($1->sym, $3, $6->sym, $6->lft); }
 	| PNAME '@' NAME	{ $$ = rem_lab($1->sym, ZN, $3->sym); }
 	| PNAME ':' pfld	{ $$ = rem_var($1->sym, ZN, $3->sym, $3->lft); }
+	| ltl_expr		{ $$ = $1; }
 	;
 
 Opt_priority:	/* none */	{ $$ = ZN; }
@@ -598,21 +716,22 @@ Opt_priority:	/* none */	{ $$ = ZN; }
 	;
 
 full_expr:	expr		{ $$ = $1; }
-	|	Expr		{ $$ = $1; }
+	| Expr		{ $$ = $1; }
 	;
 
-Opt_enabler:	/* none */	{ $$ = ZN; }
-	| PROVIDED '(' full_expr ')'	{ if (!proper_enabler($3))
-				  {	non_fatal("invalid PROVIDED clause",
-						(char *)0);
-					$$ = ZN;
-				  } else
-					$$ = $3;
-				 }
-	| PROVIDED error	{ $$ = ZN;
-				  non_fatal("usage: provided ( ..expr.. )",
-					(char *)0);
+ltl_expr: expr UNTIL expr	{ $$ = nn(ZN, UNTIL,   $1, $3); }
+	| expr RELEASE expr	{ $$ = nn(ZN, RELEASE, $1, $3); }
+	| expr WEAK_UNTIL expr	{ $$ = nn(ZN, ALWAYS, $1, ZN);
+				  $$ = nn(ZN, OR, $$, nn(ZN, UNTIL, $1, $3));
 				}
+	| expr IMPLIES expr	{
+				$$ = nn(ZN, '!', $1, ZN);
+				$$ = nn(ZN, OR,  $$, $3);
+				}
+	| expr EQUIV expr	{ $$ = nn(ZN, EQUIV,   $1, $3); }
+	| NEXT expr       %prec NEG { $$ = nn(ZN, NEXT,  $2, ZN); }
+	| ALWAYS expr     %prec NEG { $$ = nn(ZN, ALWAYS,$2, ZN); }
+	| EVENTUALLY expr %prec NEG { $$ = nn(ZN, EVENTUALLY, $2, ZN); }
 	;
 
 	/* an Expr cannot be negated - to protect Probe expressions */
@@ -620,9 +739,9 @@ Expr	: Probe			{ $$ = $1; }
 	| '(' Expr ')'		{ $$ = $2; }
 	| Expr AND Expr		{ $$ = nn(ZN, AND, $1, $3); }
 	| Expr AND expr		{ $$ = nn(ZN, AND, $1, $3); }
+	| expr AND Expr		{ $$ = nn(ZN, AND, $1, $3); }
 	| Expr OR  Expr		{ $$ = nn(ZN,  OR, $1, $3); }
 	| Expr OR  expr		{ $$ = nn(ZN,  OR, $1, $3); }
-	| expr AND Expr		{ $$ = nn(ZN, AND, $1, $3); }
 	| expr OR  Expr		{ $$ = nn(ZN,  OR, $1, $3); }
 	;
 
@@ -632,6 +751,20 @@ Probe	: FULL '(' varref ')'	{ $$ = nn($3,  FULL, $3, ZN); }
 	| NEMPTY '(' varref ')'	{ $$ = nn($3,NEMPTY, $3, ZN); }
 	;
 
+Opt_enabler:	/* none */	{ $$ = ZN; }
+	| PROVIDED '(' full_expr ')'	{ if (!proper_enabler($3))
+				  {	non_fatal("invalid PROVIDED clause",
+						(char *)0);
+					$$ = ZN;
+				  } else
+					$$ = $3;
+				 }
+	| PROVIDED error	{ $$ = ZN;
+				  non_fatal("usage: provided ( ..expr.. )",
+					(char *)0);
+				}
+	;
+
 basetype: TYPE			{ $$->sym = ZS;
 				  $$->val = $1->val;
 				  if ($$->val == UNSIGNED)
@@ -676,9 +809,9 @@ arg     : expr			{ if ($1->ntyp == ',')
 	;
 
 rarg	: varref		{ $$ = $1; trackvar($1, $1);
-				  trapwonly($1, "rarg"); }
+				  trapwonly($1 /*, "rarg" */); }
 	| EVAL '(' expr ')'	{ $$ = nn(ZN,EVAL,$3,ZN);
-				  trapwonly($1, "eval rarg"); }
+				  trapwonly($1 /*, "eval rarg" */); }
 	| CONST 		{ $$ = nn(ZN,CONST,ZN,ZN);
 				  $$->ismtyp = $1->ismtyp;
 				  $$->val = $1->val;
@@ -715,6 +848,98 @@ nlst	: NAME			{ $$ = nn($1, NAME, ZN, ZN);
 	;
 %%
 
+#define binop(n, sop)	fprintf(fd, "("); recursive(fd, n->lft); \
+			fprintf(fd, ") %s (", sop); recursive(fd, n->rgt); \
+			fprintf(fd, ")");
+#define unop(n, sop)	fprintf(fd, "%s (", sop); recursive(fd, n->lft); \
+			fprintf(fd, ")");
+
+static void
+recursive(FILE *fd, Lextok *n)
+{
+	if (n)
+	switch (n->ntyp) {
+	case NEXT:
+		unop(n, "X");
+		break;
+	case ALWAYS:
+		unop(n, "[]");
+		break;
+	case EVENTUALLY:
+		unop(n, "<>");
+		break;
+	case '!':
+		unop(n, "!");
+		break;
+	case UNTIL:
+		binop(n, "U");
+		break;
+	case WEAK_UNTIL:
+		binop(n, "W");
+		break;
+	case RELEASE: /* see http://en.wikipedia.org/wiki/Linear_temporal_logic */
+		binop(n, "V");
+		break;
+	case OR:
+		binop(n, "||");
+		break;
+	case AND:
+		binop(n, "&&");
+		break;
+	case IMPLIES:
+		binop(n, "->");
+		break;
+	case EQUIV:
+		binop(n, "<->");
+		break;
+	default:
+		comment(fd, n, 0);
+		break;
+	}
+}
+
+#define TMP_FILE	"_S_p_I_n_.tmp"
+
+extern int unlink(const char *);
+
+static Lextok *
+ltl_to_string(Lextok *n)
+{	Lextok *m = nn(ZN, 0, ZN, ZN);
+	char *retval;
+	char formula[1024];
+	FILE *tf = fopen(TMP_FILE, "w+"); /* tmpfile() fails on Windows 7 */
+
+	/* convert the parsed ltl to a string
+	   by writing into a file, using existing functions,
+	   and then passing it to the existing interface for
+	   conversion into a never claim
+	  (this means parsing everything twice, which is
+	   a little redundant, but adds only miniscule overhead)
+	 */
+
+	if (!tf)
+	{	fatal("cannot create temporary file", (char *) 0);
+	}
+	recursive(tf, n);
+	(void) fseek(tf, 0L, SEEK_SET);
+
+	memset(formula, 0, sizeof(formula));
+	retval = fgets(formula, sizeof(formula), tf);
+	fclose(tf);
+	(void) unlink(TMP_FILE);
+
+	if (!retval)
+	{	printf("%p\n", retval);
+		fatal("could not translate ltl formula", 0);
+	}
+
+	if (1) printf("ltl %s: %s\n", ltl_name, formula);
+
+	m->sym = lookup(formula);
+
+	return m;
+}
+
 void
 yyerror(char *fmt, ...)
 {

+ 215 - 78
sys/src/cmd/spin/spinlex.c

@@ -23,6 +23,7 @@ typedef struct IType {
 	Lextok *params;		/* formal pars if any */
 	char   **anms;		/* literal text for actual pars */
 	char   *prec;		/* precondition for c_code or c_expr */
+	int    uiid;		/* unique inline id */
 	int    dln, cln;	/* def and call linenr */
 	Symbol *dfn, *cfn;	/* def and call filename */
 	struct IType *nxt;	/* linked list */
@@ -37,14 +38,16 @@ typedef struct C_Added {
 
 extern RunList	*X;
 extern ProcList	*rdy;
-extern Symbol	*Fname;
+extern Symbol	*Fname, *oFname;
 extern Symbol	*context, *owner;
 extern YYSTYPE	yylval;
 extern short	has_last, has_code;
-extern int	verbose, IArgs, hastrack, separate;
+extern int	verbose, IArgs, hastrack, separate, ltl_mode;
 
 short	has_stack = 0;
 int	lineno  = 1;
+int	scope_seq[128], scope_level = 0;
+char	CurScope[MAXSCOPESZ];
 char	yytext[2048];
 FILE	*yyin, *yyout;
 
@@ -81,20 +84,23 @@ static void	uninline(void);
 
 #if 1
 #define Getchar()	((Inlining<0)?getc(yyin):getinline())
-#define Ungetch(c)	{if (Inlining<0) ungetc(c,yyin); else uninline(); }
+#define Ungetch(c)	{if (Inlining<0) ungetc(c,yyin); else uninline();}
 
 #else
 
 static int
 Getchar(void)
 {	int c;
+
 	if (Inlining<0)
 		c = getc(yyin);
 	else
 		c = getinline();
-#if 1
-	printf("<%c>", c);
-#endif
+	if (0)
+	{	printf("<%c:%d>[%d] ", c, c, Inlining);
+	} else
+	{	printf("%c", c);
+	}
 	return c;
 }
 
@@ -105,12 +111,17 @@ Ungetch(int c)
 		ungetc(c,yyin);
 	else
 		uninline();
-#if 1
-	printf("<bs>");
-#endif
+	if (0)
+	{	printf("<bs>");
+	}
 }
 #endif
 
+static int
+notdollar(int c)
+{	return (c != '$' && c != '\n');
+}
+
 static int
 notquote(int c)
 {	return (c != '\"' && c != '\n');
@@ -133,14 +144,15 @@ isdigit_(int c)
 
 static void
 getword(int first, int (*tst)(int))
-{	int i=0; char c;
+{	int i=0, c;
 
 	yytext[i++]= (char) first;
 	while (tst(c = Getchar()))
-	{	yytext[i++] = c;
+	{	yytext[i++] = (char) c;
 		if (c == '\\')
-			yytext[i++] = Getchar();	/* no tst */
-	}
+		{	c = Getchar();
+			yytext[i++] = (char) c;	/* no tst */
+	}	}
 	yytext[i] = '\0';
 	Ungetch(c);
 }
@@ -161,10 +173,11 @@ static IType *seqnames;
 static void
 def_inline(Symbol *s, int ln, char *ptr, char *prc, Lextok *nms)
 {	IType *tmp;
-	char *nw = (char *) emalloc((int) strlen(ptr)+1);
+	int  cnt = 0;
+	char *nw = (char *) emalloc(strlen(ptr)+1);
 	strcpy(nw, ptr);
 
-	for (tmp = seqnames; tmp; tmp = tmp->nxt)
+	for (tmp = seqnames; tmp; cnt++, tmp = tmp->nxt)
 		if (!strcmp(s->name, tmp->nm->name))
 		{	non_fatal("procedure name %s redefined",
 				tmp->nm->name);
@@ -179,11 +192,12 @@ def_inline(Symbol *s, int ln, char *ptr, char *prc, Lextok *nms)
 	tmp->cn = (Lextok *) nw;
 	tmp->params = nms;
 	if (strlen(prc) > 0)
-	{	tmp->prec = (char *) emalloc((int) strlen(prc)+1);
+	{	tmp->prec = (char *) emalloc(strlen(prc)+1);
 		strcpy(tmp->prec, prc);
 	}
 	tmp->dln = ln;
 	tmp->dfn = Fname;
+	tmp->uiid = cnt+1;	/* so that 0 means: not an inline */
 	tmp->nxt = seqnames;
 	seqnames = tmp;
 }
@@ -269,8 +283,8 @@ getinline(void)
 		Inlining--;
 #if 0
 		if (verbose&32)
-		printf("spin: line %d, done inlining %s\n",
-			lineno, Inline_stub[Inlining+1]->nm->name);
+		printf("spin: %s:%d, done inlining %s\n",
+			Fname, lineno, Inline_stub[Inlining+1]->nm->name);
 #endif
 		return Getchar();
 	}
@@ -286,6 +300,16 @@ uninline(void)
 		Inliner[Inlining]--;
 }
 
+int
+is_inline(void)
+{
+	if (Inlining < 0)
+		return 0;	/* i.e., not an inline */
+	if (Inline_stub[Inlining] == NULL)
+		fatal("unexpected, inline_stub not set", 0);
+	return Inline_stub[Inlining]->uiid;
+}
+
 IType *
 find_inline(char *s)
 {	IType *tmp;
@@ -295,6 +319,7 @@ find_inline(char *s)
 			break;
 	if (!tmp)
 		fatal("cannot happen, missing inline def %s", s);
+
 	return tmp;
 }
 
@@ -329,7 +354,7 @@ c_track(Symbol *s, Symbol *t, Symbol *stackonly)	/* name, size */
 		     &&  strcmp(stackonly->name, "\"StackOnly\"") != 0)
 			non_fatal("expecting '[Un]Matched', saw %s", stackonly->name);
 		else
-			has_stack = 1;
+			has_stack = 1;	/* unmatched stack */
 	}
 }
 
@@ -514,32 +539,38 @@ c_add_sv(FILE *fd)	/* 1+2 -- called in pangen1.c */
 }
 
 void
-c_add_stack(FILE *fd)
+c_stack_size(FILE *fd)
 {	C_Added *r;
 	int cnt = 0;
 
-	if ((!c_added && !c_tracked) || !has_stack) return;
-
-
 	for (r = c_tracked; r; r = r->nxt)
 		if (r->ival != ZS)
+		{	fprintf(fd, "%s%s",
+				(cnt==0)?"":"+", r->t->name);
 			cnt++;
+		}
+	if (cnt == 0)
+	{	fprintf(fd, "WS");
+	}
+}
 
-	if (cnt == 0) return;
+void
+c_add_stack(FILE *fd)
+{	C_Added *r;
+	int cnt = 0;
 
-	fprintf(fd, "	uchar c_stack[");
+	if ((!c_added && !c_tracked) || !has_stack)
+	{	return;
+	}
 
-	cnt = 0;
 	for (r = c_tracked; r; r = r->nxt)
-	{	if (r->ival == ZS) continue;
+		if (r->ival != ZS)
+		{	cnt++;
+		}
 
-		fprintf(fd, "%s%s",
-			(cnt==0)?"":"+", r->t->name);
-		cnt++;
+	if (cnt > 0)
+	{	fprintf(fd, "	uchar c_stack[StackSize];\n");
 	}
-
-	if (cnt == 0) fprintf(fd, "WS"); /* can't happen */
-	fprintf(fd, "];\n");
 }
 
 void
@@ -610,9 +641,10 @@ c_add_def(FILE *fd)	/* 3 - called in plunk_c_fcts() */
 	}
 
 	if (has_stack)
-	{	fprintf(fd, "void\nc_stack(uchar *p_t_r)\n{\n");
+	{	fprintf(fd, "int cpu_printf(const char *, ...);\n");
+		fprintf(fd, "void\nc_stack(uchar *p_t_r)\n{\n");
 		fprintf(fd, "#ifdef VERBOSE\n");
-		fprintf(fd, "	printf(\"c_stack %%u\\n\", p_t_r);\n");
+		fprintf(fd, "	cpu_printf(\"c_stack %%u\\n\", p_t_r);\n");
 		fprintf(fd, "#endif\n");
 		for (r = c_tracked; r; r = r->nxt)
 		{	if (r->ival == ZS) continue;
@@ -660,7 +692,7 @@ c_add_def(FILE *fd)	/* 3 - called in plunk_c_fcts() */
 	if (has_stack)
 	{	fprintf(fd, "void\nc_unstack(uchar *p_t_r)\n{\n");
 		fprintf(fd, "#ifdef VERBOSE\n");
-		fprintf(fd, "	printf(\"c_unstack %%u\\n\", p_t_r);\n");
+		fprintf(fd, "	cpu_printf(\"c_unstack %%u\\n\", p_t_r);\n");
 		fprintf(fd, "#endif\n");
 		for (r = c_tracked; r; r = r->nxt)
 		{	if (r->ival == ZS) continue;
@@ -792,7 +824,7 @@ preruse(FILE *fd, Lextok *n)	/* check a condition for c_expr with preconditions
 		if (tmp->prec)
 		{	fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec);
 			fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;");
-			fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec);
+			fprintf(fd, "trpt->st = tt; uerror(\"%s\"); continue; } ", tmp->prec);
 			fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec);
 			fprintf(fd, "_m = 3; goto P999; } } \n\t\t");
 		}
@@ -814,7 +846,7 @@ glob_inline(char *s)
 }
 
 void
-plunk_inline(FILE *fd, char *s, int how)	/* c_code with precondition */
+plunk_inline(FILE *fd, char *s, int how, int gencode)	/* c_code with precondition */
 {	IType *tmp;
 
 	tmp = find_inline(s);
@@ -822,12 +854,19 @@ plunk_inline(FILE *fd, char *s, int how)	/* c_code with precondition */
 
 	fprintf(fd, "{ ");
 	if (how && tmp->prec)
-	{	fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec);
-		fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;");
-		fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec);
-		fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec);
-		fprintf(fd, "_m = 3; goto P999; } } ");
+	{	fprintf(fd, "if (!(%s)) { if (!readtrail) {",
+			tmp->prec);
+		fprintf(fd, " uerror(\"%s\"); continue; ",
+			tmp->prec);
+		fprintf(fd, "} else { ");
+		fprintf(fd, "printf(\"pan: precondition false: %s\\n\"); _m = 3; goto P999; } } ",
+			tmp->prec);
+	}
+
+	if (!gencode)	/* not in d_step */
+	{	fprintf(fd, "\n\t\tsv_save();");
 	}
+
 	fprintf(fd, "%s", (char *) tmp->cn);
 	fprintf(fd, " }\n");
 }
@@ -875,8 +914,7 @@ pickup_inline(Symbol *t, Lextok *apars)
 	tmp = find_inline(t->name);
 
 	if (++Inlining >= MAXINL)
-		fatal("inline fcts too deeply nested", 0);
-
+		fatal("inlines nested too deeply", 0);
 	tmp->cln = lineno;	/* remember calling point */
 	tmp->cfn = Fname;	/* and filename */
 
@@ -887,7 +925,7 @@ pickup_inline(Symbol *t, Lextok *apars)
 
 	tmp->anms  = (char **) emalloc(j * sizeof(char *));
 	for (p = apars, j = 0; p; p = p->rgt, j++)
-	{	tmp->anms[j] = (char *) emalloc((int) strlen(IArg_cont[j])+1);
+	{	tmp->anms[j] = (char *) emalloc(strlen(IArg_cont[j])+1);
 		strcpy(tmp->anms[j], IArg_cont[j]);
 	}
 
@@ -897,8 +935,8 @@ pickup_inline(Symbol *t, Lextok *apars)
 	Inline_stub[Inlining] = tmp;
 #if 0
 	if (verbose&32)
-	printf("spin: line %d, file %s, inlining '%s' (from line %d, file %s)\n",
-		tmp->cln, tmp->cfn->name, t->name, tmp->dln, tmp->dfn->name);
+	printf("spin: %s:%d, inlining '%s' (from %s:%d)\n",
+		tmp->cfn->name, tmp->cln, t->name, tmp->dfn->name, tmp->dln);
 #endif
 	for (j = 0; j < Inlining; j++)
 		if (Inline_stub[j] == Inline_stub[Inlining])
@@ -930,13 +968,15 @@ do_directive(int first)
 		fatal("malformed preprocessor directive - .fname", 0);
 
 	if ((c = Getchar()) != '\"')
-		fatal("malformed preprocessor directive - .fname", 0);
+	{	printf("got %c, expected \" -- lineno %d\n", c, lineno);
+		fatal("malformed preprocessor directive - .fname (%s)", yytext);
+	}
 
-	getword(c, notquote);
+	getword(Getchar(), notquote);	/* was getword(c, notquote); */
 	if (Getchar() != '\"')
 		fatal("malformed preprocessor directive - fname.", 0);
 
-	strcat(yytext, "\"");
+	/* strcat(yytext, "\""); */
 	Fname = lookup(yytext);
 done:
 	while (Getchar() != '\n')
@@ -965,41 +1005,43 @@ precondition(char *q)
 			break;
 		}
 	}
-	fatal("cannot happen", (char *) 0);			
+	fatal("cannot happen", (char *) 0); /* unreachable */
 }
 
+
 Symbol *
 prep_inline(Symbol *s, Lextok *nms)
 {	int c, nest = 1, dln, firstchar, cnr;
-	char *p, buf[SOMETHINGBIG], buf2[RATHERSMALL];
+	char *p;
 	Lextok *t;
+	static char Buf1[SOMETHINGBIG], Buf2[RATHERSMALL];
 	static int c_code = 1;
 
 	for (t = nms; t; t = t->rgt)
 		if (t->lft)
 		{	if (t->lft->ntyp != NAME)
-			fatal("bad param to inline %s", s->name);
+			fatal("bad param to inline %s", s?s->name:"--");
 			t->lft->sym->hidden |= 32;
 		}
 
 	if (!s)	/* C_Code fragment */
 	{	s = (Symbol *) emalloc(sizeof(Symbol));
-		s->name = (char *) emalloc((int) strlen("c_code")+26);
+		s->name = (char *) emalloc(strlen("c_code")+26);
 		sprintf(s->name, "c_code%d", c_code++);
 		s->context = context;
 		s->type = CODE_FRAG;
 	} else
 		s->type = PREDEF;
 
-	p = &buf[0];
-	buf2[0] = '\0';
+	p = &Buf1[0];
+	Buf2[0] = '\0';
 	for (;;)
 	{	c = Getchar();
 		switch (c) {
 		case '[':
 			if (s->type != CODE_FRAG)
 				goto bad;
-			precondition(&buf2[0]);	/* e.g., c_code [p] { r = p-r; } */
+			precondition(&Buf2[0]);	/* e.g., c_code [p] { r = p-r; } */
 			continue;
 		case '{':
 			break;
@@ -1017,19 +1059,20 @@ bad:			 fatal("bad inline: %s", s->name);
 	dln = lineno;
 	if (s->type == CODE_FRAG)
 	{	if (verbose&32)
-			sprintf(buf, "\t/* line %d %s */\n\t\t",
+			sprintf(Buf1, "\t/* line %d %s */\n\t\t",
 				lineno, Fname->name);
 		else
-			strcpy(buf, "");
+			strcpy(Buf1, "");
 	} else
-		sprintf(buf, "\n#line %d %s\n{", lineno, Fname->name);
-	p += strlen(buf);
+		sprintf(Buf1, "\n#line %d \"%s\"\n{", lineno, Fname->name);
+	p += strlen(Buf1);
 	firstchar = 1;
 
 	cnr = 1; /* not zero */
 more:
-	*p++ = c = Getchar();
-	if (p - buf >= SOMETHINGBIG)
+	c = Getchar();
+	*p++ = (char) c;
+	if (p - Buf1 >= SOMETHINGBIG)
 		fatal("inline text too long", 0);
 	switch (c) {
 	case '\n':
@@ -1046,8 +1089,8 @@ more:
 		{	*p = '\0';
 			if (s->type == CODE_FRAG)
 				*--p = '\0';	/* remove trailing '}' */	
-			def_inline(s, dln, &buf[0], &buf2[0], nms);
-			if (firstchar && s)
+			def_inline(s, dln, &Buf1[0], &Buf2[0], nms);
+			if (firstchar)
 				printf("%3d: %s, warning: empty inline definition (%s)\n",
 					dln, Fname->name, s->name);
 			return s;	/* normal return */
@@ -1057,19 +1100,38 @@ more:
 		if (cnr == 0)
 		{	p--;
 			do_directive(c); /* reads to newline */
-			break;
-		} /* else fall through */
-	default:
-		firstchar = 0;
+		} else
+		{	firstchar = 0;
+			cnr++;
+		}
+		break;
 	case '\t':
 	case ' ':
 	case '\f':
 		cnr++;
 		break;
+	default:
+		firstchar = 0;
+		cnr++;
+		break;
 	}
 	goto more;
 }
 
+static void
+set_cur_scope(void)
+{	int i;
+	char tmpbuf[256];
+
+	strcpy(CurScope, "_");
+
+	if (context)
+	for (i = 0; i < scope_level; i++)
+	{	sprintf(tmpbuf, "%d_", scope_seq[i]);
+		strcat(CurScope, tmpbuf);
+	}
+}
+
 static int
 lex(void)
 {	int c;
@@ -1079,6 +1141,8 @@ again:
 	yytext[0] = (char) c;
 	yytext[1] = '\0';
 	switch (c) {
+	case EOF:
+		return c;
 	case '\n':		/* newline */
 		lineno++;
 	case '\r':		/* carriage return */
@@ -1099,6 +1163,13 @@ again:
 		strcat(yytext, "\"");
 		SymToken(lookup(yytext), STRING)
 
+	case '$':
+		getword('\"', notdollar);
+		if (Getchar() != '$')
+			fatal("ltl definition not terminated", yytext);
+		strcat(yytext, "\""); 
+		SymToken(lookup(yytext), STRING)
+
 	case '\'':	/* new 3.0.9 */
 		c = Getchar();
 		if (c == '\\')
@@ -1131,6 +1202,26 @@ again:
 		goto again;
 	}
 
+	if (ltl_mode)
+	{	switch (c) {
+		case '-': c = follow('>', IMPLIES,    '-'); break;
+		case '[': c = follow(']', ALWAYS,     '['); break;
+		case '<': c = follow('>', EVENTUALLY, '<');
+			  if (c == '<')
+			  {	c = Getchar();
+				if (c == '-')
+				{	c = follow('>', EQUIV, '-');
+					if (c == '-')
+					{	Ungetch(c);
+						c = '<';
+					}
+				} else
+				{	Ungetch(c);
+					c = '<';
+			  }	}
+		default:  break;
+	}	}
+
 	switch (c) {
 	case '/': c = follow('*', 0, '/');
 		  if (!c) { in_comment = 1; goto again; }
@@ -1149,11 +1240,34 @@ again:
 	case '&': c = follow('&', AND, '&'); break;
 	case '|': c = follow('|', OR, '|'); break;
 	case ';': c = SEMI; break;
+	case '.': c = follow('.', DOTDOT, '.'); break;
+	case '{': scope_seq[scope_level++]++; set_cur_scope(); break;
+	case '}': scope_level--; set_cur_scope(); break;
 	default : break;
 	}
 	Token(c)
 }
 
+static struct {
+	char *s;	int tok;
+} LTL_syms[] = {
+	/* [], <>, ->, and <-> are intercepted in lex() */
+	{ "U",		UNTIL   },
+	{ "V",		RELEASE },
+	{ "W",		WEAK_UNTIL },
+	{ "X",		NEXT    },
+	{ "always",	ALWAYS  },
+	{ "eventually",	EVENTUALLY },
+	{ "until",	UNTIL   },
+	{ "stronguntil",UNTIL   },
+	{ "weakuntil",	WEAK_UNTIL   },
+	{ "release",	RELEASE },
+	{ "next",	NEXT    },
+	{ "implies",	IMPLIES },
+	{ "equivalent",	EQUIV   },
+	{ 0, 		0       },
+};
+
 static struct {
 	char *s;	int tok;	int val;	char *sym;
 } Names[] = {
@@ -1178,14 +1292,18 @@ static struct {
 	{"eval",	EVAL,		0,		0},
 	{"false",	CONST,		0,		0},
 	{"fi",		FI,		0,		0},
+	{"for",		FOR,		0,		0},
 	{"full",	FULL,		0,		0},
 	{"goto",	GOTO,		0,		0},
 	{"hidden",	HIDDEN,		0,		":hide:"},
 	{"if",		IF,		0,		0},
+	{"in",		IN,		0,		0},
 	{"init",	INIT,		0,		":init:"},
+	{"inline",	INLINE,		0,		0},
 	{"int",		TYPE,		INT,		0},
 	{"len",		LEN,		0,		0},
 	{"local",	ISLOCAL,	0,		":local:"},
+	{"ltl",		LTL,		0,		":ltl:"},
 	{"mtype",	TYPE,		MTYPE,		0},
 	{"nempty",	NEMPTY,		0,		0},
 	{"never",	CLAIM,		0,		":never:"},
@@ -1203,7 +1321,7 @@ static struct {
 	{"provided",	PROVIDED,	0,		0},
 	{"run",		RUN,		0,		0},
 	{"d_step",	D_STEP,		0,		0},
-	{"inline",	INLINE,		0,		0},
+	{"select",	SELECT,		0,	0},
 	{"short",	TYPE,		SHORT,		0},
 	{"skip",	CONST,		1,		0},
 	{"timeout",	TIMEOUT,	0,		0},
@@ -1223,13 +1341,20 @@ check_name(char *s)
 {	int i;
 
 	yylval = nn(ZN, 0, ZN, ZN);
+
+	if (ltl_mode)
+	{	for (i = 0; LTL_syms[i].s; i++)
+		{	if (strcmp(s, LTL_syms[i].s) == 0)
+			{	return LTL_syms[i].tok;
+	}	}	}
+
 	for (i = 0; Names[i].s; i++)
-		if (strcmp(s, Names[i].s) == 0)
+	{	if (strcmp(s, Names[i].s) == 0)
 		{	yylval->val = Names[i].val;
 			if (Names[i].sym)
 				yylval->sym = lookup(Names[i].sym);
 			return Names[i].tok;
-		}
+	}	}
 
 	if ((yylval->val = ismtype(s)) != 0)
 	{	yylval->ismtyp = 1;
@@ -1253,19 +1378,29 @@ check_name(char *s)
 				Inline_stub[Inlining]->nm->name,
 				Inline_stub[Inlining]->anms[i]);
 #endif
-			
 			for (tt = Inline_stub[Inlining]->params; tt; tt = tt->rgt)
 				if (!strcmp(Inline_stub[Inlining]->anms[i],
 					tt->lft->sym->name))
 				{	/* would be cyclic if not caught */
-					printf("spin: line %d replacement value: %s\n",
-						lineno, tt->lft->sym->name);
-					fatal("formal par of %s matches replacement value",
+					printf("spin: %s:%d replacement value: %s\n",
+						oFname->name?oFname->name:"--", lineno, tt->lft->sym->name);
+					fatal("formal par of %s contains replacement value",
 						Inline_stub[Inlining]->nm->name);
 					yylval->ntyp = tt->lft->ntyp;
 					yylval->sym = lookup(tt->lft->sym->name);
 					return NAME;
 				}
+
+			/* check for occurrence of param as field of struct */
+			{ char *ptr = Inline_stub[Inlining]->anms[i];
+				while ((ptr = strstr(ptr, s)) != NULL)
+				{	if (*(ptr-1) == '.'
+					||  *(ptr+strlen(s)) == '.')
+					{	fatal("formal par of %s used in structure name",
+						Inline_stub[Inlining]->nm->name);
+					}
+					ptr++;
+			}	}
 			ReDiRect = Inline_stub[Inlining]->anms[i];
 			return 0;
 	}	 }
@@ -1353,6 +1488,9 @@ yylex(void)
 		} else if (c == CONST && yytext[0] == '\'')
 		{	sprintf(yytext, "'%c'", yylval->val);
 			strcat(IArg_cont[IArgno], yytext);
+		} else if (c == CONST)
+		{	sprintf(yytext, "%d", yylval->val);
+			strcat(IArg_cont[IArgno], yytext);
 		} else
 		{
 			switch (c) {
@@ -1379,6 +1517,5 @@ yylex(void)
 			strcat(IArg_cont[IArgno], yytext);
 		}
 	}
-
 	return c;
 }

+ 36 - 27
sys/src/cmd/spin/structs.c

@@ -19,7 +19,7 @@ typedef struct UType {
 } UType;
 
 extern	Symbol	*Fname;
-extern	int	lineno, depth, Expand_Ok;
+extern	int	lineno, depth, Expand_Ok, has_hidden, in_for;
 
 Symbol	*owner;
 
@@ -33,14 +33,16 @@ void
 setuname(Lextok *n)
 {	UType *tmp;
 
+	if (!owner)
+		fatal("illegal reference inside typedef", (char *) 0);
+
 	for (tmp = Unames; tmp; tmp = tmp->nxt)
 		if (!strcmp(owner->name, tmp->nm->name))
 		{	non_fatal("typename %s was defined before",
 				tmp->nm->name);
 			return;
 		}
-	if (!owner) fatal("illegal reference inside typedef",
-		(char *) 0);
+
 	tmp = (UType *) emalloc(sizeof(UType));
 	tmp->nm = owner;
 	tmp->cn = n;
@@ -102,21 +104,22 @@ setutype(Lextok *p, Symbol *t, Lextok *vis)	/* user-defined types */
 	{	lineno = n->ln;
 		Fname = n->fn;
 		if (n->sym->type)
-		non_fatal("redeclaration of '%s'", n->sym->name);
+			fatal("redeclaration of '%s'", n->sym->name);
 
 		if (n->sym->nbits > 0)
-		non_fatal("(%s) only an unsigned can have width-field",
-			n->sym->name);
+			non_fatal("(%s) only an unsigned can have width-field",
+				n->sym->name);
 
 		if (Expand_Ok)
 			n->sym->hidden |= (4|8|16); /* formal par */
 
 		if (vis)
-		{	if (strncmp(vis->sym->name, ":hide:", 6) == 0)
-				n->sym->hidden |= 1;
-			else if (strncmp(vis->sym->name, ":show:", 6) == 0)
+		{	if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0)
+			{	n->sym->hidden |= 1;
+				has_hidden++;
+			} else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0)
 				n->sym->hidden |= 2;
-			else if (strncmp(vis->sym->name, ":local:", 7) == 0)
+			else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0)
 				n->sym->hidden |= 64;
 		}
 		n->sym->type = STRUCT;	/* classification   */
@@ -196,6 +199,7 @@ Rval_struct(Lextok *n, Symbol *v, int xinit)	/* n varref, v valref */
 		fatal("non-zero 'rgt' on non-structure", 0);
 
 	ix = eval(tmp->lft);
+/*	printf("%d: ix: %d (%d) %d\n", depth, ix, tl->nel, tl->val[ix]); */
 	if (ix >= tl->nel || ix < 0)
 		fatal("indexing error \'%s\'", tl->name);
 
@@ -222,10 +226,12 @@ Lval_struct(Lextok *n, Symbol *v, int xinit, int a)  /* a = assigned value */
 		fatal("indexing error \'%s\'", tl->name);
 
 	if (tl->nbits > 0)
-		a = (a & ((1<<tl->nbits)-1));	
-	tl->val[ix] = a;
-	tl->setat = depth;
+		a = (a & ((1<<tl->nbits)-1));
 
+	if (a != tl->val[ix])
+	{	tl->val[ix] = a;
+		tl->setat = depth;
+	}
 	return 1;
 }
 
@@ -246,7 +252,7 @@ is_lst:
 	for (fp = n; fp; fp = fp->rgt)
 	for (tl = fp->lft; tl; tl = tl->rgt)
 	{	if (tl->sym->type == STRUCT)
-		{	if (tl->sym->nel != 1)
+		{	if (tl->sym->nel > 1 || tl->sym->isarray)
 				fatal("array of structures in param list, %s",
 					tl->sym->name);
 			cnt += Cnt_flds(tl->sym->Slst);
@@ -266,9 +272,9 @@ Sym_typ(Lextok *t)
 		return s->type;
 
 	if (!t->rgt
-	||  !t->rgt->ntyp == '.'
+	||   t->rgt->ntyp != '.'	/* gh: had ! in wrong place */
 	||  !t->rgt->lft)
-		return STRUCT;	/* not a field reference */
+		return STRUCT;		/* not a field reference */
 
 	return Sym_typ(t->rgt->lft);
 }
@@ -315,6 +321,7 @@ cpnn(Lextok *s, int L, int R, int S)
 	if (!s) return ZN;
 
 	d = (Lextok *) emalloc(sizeof(Lextok));
+	d->uiid = s->uiid;
 	d->ntyp = s->ntyp;
 	d->val  = s->val;
 	d->ln   = s->ln;
@@ -353,7 +360,7 @@ full_name(FILE *fd, Lextok *n, Symbol *v, int xinit)
 		goto out;
 	}
 	fprintf(fd, ".%s", tl->name);
-out:	if (tmp->sym->nel > 1)
+out:	if (tmp->sym->nel > 1 || tmp->sym->isarray == 1)
 	{	fprintf(fd, "[%d]", eval(tmp->lft));
 		hiddenarrays = 1;
 	}
@@ -391,7 +398,7 @@ struct_name(Lextok *n, Symbol *v, int xinit, char *buf)
 	}
 	sprintf(lbuf, ".%s", tl->name);
 	strcat(buf, lbuf);
-	if (tmp->sym->nel > 1)
+	if (tmp->sym->nel > 1 || tmp->sym->isarray == 1)
 	{	sprintf(lbuf, "[%d]", eval(tmp->lft));
 		strcat(buf, lbuf);
 	}
@@ -405,10 +412,10 @@ walk2_struct(char *s, Symbol *z)
 	extern void Done_case(char *, Symbol *);
 
 	ini_struct(z);
-	if (z->nel == 1)
+	if (z->nel == 1 && z->isarray == 0)
 		sprintf(eprefix, "%s%s.", s, z->name);
 	for (ix = 0; ix < z->nel; ix++)
-	{	if (z->nel > 1)
+	{	if (z->nel > 1 || z->isarray == 1)
 			sprintf(eprefix, "%s%s[%d].", s, z->name, ix);
 		for (fp = z->Sval[ix]; fp; fp = fp->rgt)
 		for (tl = fp->lft; tl; tl = tl->rgt)
@@ -426,10 +433,10 @@ walk_struct(FILE *ofd, int dowhat, char *s, Symbol *z, char *a, char *b, char *c
 	int ix;
 
 	ini_struct(z);
-	if (z->nel == 1)
+	if (z->nel == 1 && z->isarray == 0)
 		sprintf(eprefix, "%s%s.", s, z->name);
 	for (ix = 0; ix < z->nel; ix++)
-	{	if (z->nel > 1)
+	{	if (z->nel > 1 || z->isarray == 1)
 			sprintf(eprefix, "%s%s[%d].", s, z->name, ix);
 		for (fp = z->Sval[ix]; fp; fp = fp->rgt)
 		for (tl = fp->lft; tl; tl = tl->rgt)
@@ -443,7 +450,7 @@ walk_struct(FILE *ofd, int dowhat, char *s, Symbol *z, char *a, char *b, char *c
 void
 c_struct(FILE *fd, char *ipref, Symbol *z)
 {	Lextok *fp, *tl;
-	char pref[256], eprefix[256];
+	char pref[256], eprefix[300];
 	int ix;
 
 	ini_struct(z);
@@ -452,7 +459,7 @@ c_struct(FILE *fd, char *ipref, Symbol *z)
 	for (fp = z->Sval[ix]; fp; fp = fp->rgt)
 	for (tl = fp->lft; tl; tl = tl->rgt)
 	{	strcpy(eprefix, ipref);
-		if (z->nel > 1)
+		if (z->nel > 1 || z->isarray == 1)
 		{	/* insert index before last '.' */
 			eprefix[strlen(eprefix)-1] = '\0';
 			sprintf(pref, "[ %d ].", ix);
@@ -476,7 +483,7 @@ dump_struct(Symbol *z, char *prefix, RunList *r)
 	ini_struct(z);
 
 	for (ix = 0; ix < z->nel; ix++)
-	{	if (z->nel > 1)
+	{	if (z->nel > 1 || z->isarray == 1)
 			sprintf(eprefix, "%s[%d]", prefix, ix);
 		else
 			strcpy(eprefix, prefix);
@@ -484,7 +491,7 @@ dump_struct(Symbol *z, char *prefix, RunList *r)
 		for (fp = z->Sval[ix]; fp; fp = fp->rgt)
 		for (tl = fp->lft; tl; tl = tl->rgt)
 		{	if (tl->sym->type == STRUCT)
-			{	char pref[256];
+			{	char pref[300];
 				strcpy(pref, eprefix);
 				strcat(pref, ".");
 				strcat(pref, tl->sym->name);
@@ -498,7 +505,7 @@ dump_struct(Symbol *z, char *prefix, RunList *r)
 					if (r)
 					printf("%s(%d):", r->n->name, r->pid);
 					printf("%s.%s", eprefix, tl->sym->name);
-					if (tl->sym->nel > 1)
+					if (tl->sym->nel > 1 || tl->sym->isarray == 1)
 						printf("[%d]", jx);
 					printf(" = ");
 					sr_mesg(stdout, tl->sym->val[jx],
@@ -579,9 +586,11 @@ mk_explicit(Lextok *n, int Ok, int Ntyp)
 	int i, cnt; extern int IArgs;
 
 	if (n->sym->type != STRUCT
+	||  in_for
 	||  is_explicit(n))
 		return n;
 
+
 	if (n->rgt
 	&&  n->rgt->ntyp == '.'
 	&&  n->rgt->lft

+ 91 - 29
sys/src/cmd/spin/sym.c

@@ -14,14 +14,19 @@
 
 extern Symbol	*Fname, *owner;
 extern int	lineno, depth, verbose, NamesNotAdded, deadvar;
+extern int	has_hidden, m_loss, old_scope_rules;
 extern short	has_xu;
+extern char	CurScope[MAXSCOPESZ];
 
 Symbol	*context = ZS;
 Ordered	*all_names = (Ordered *)0;
 int	Nid = 0;
 
+Lextok *Mtype = (Lextok *) 0;
+
 static Ordered	*last_name = (Ordered *)0;
 static Symbol	*symtab[Nhash+1];
+static Lextok *runstmnts = ZN;
 
 static int
 samename(Symbol *a, Symbol *b)
@@ -44,31 +49,77 @@ hash(char *s)
 	return h&Nhash;
 }
 
+void
+disambiguate(void)
+{	Ordered *walk;
+	Symbol *sp;
+
+	if (old_scope_rules)
+		return;
+
+	/* if the same name appears in two different scopes,
+	   prepend the scope_prefix to the names */
+
+	for (walk = all_names; walk; walk = walk->next)
+	{	sp = walk->entry;
+		if (sp->type != 0
+		&&  sp->type != LABEL
+		&&  strlen((const char *)sp->bscp) > 1)
+		{	char *n = (char *) emalloc(strlen((const char *)sp->name)
+				+ strlen((const char *)sp->bscp) + 1);
+			sprintf(n, "%s%s", sp->bscp, sp->name);
+			sp->name = n;	/* discord the old memory */
+	}	}
+}
+
 Symbol *
 lookup(char *s)
 {	Symbol *sp; Ordered *no;
 	int h = hash(s);
 
+	if (old_scope_rules)
+	{	/* same scope - global refering to global or local to local */
+		for (sp = symtab[h]; sp; sp = sp->next)
+		{	if (strcmp(sp->name, s) == 0
+			&&  samename(sp->context, context)
+			&&  samename(sp->owner, owner))
+			{	return sp;		/* found */
+		}	}
+	} else
+	{	/* added 6.0.0: more traditional, scope rule */
+		for (sp = symtab[h]; sp; sp = sp->next)
+		{	if (strcmp(sp->name, s) == 0
+			&&  samename(sp->context, context)
+			&&  (strcmp((const char *)sp->bscp, CurScope) == 0
+			||   strncmp((const char *)sp->bscp, CurScope, strlen((const char *)sp->bscp)) == 0)
+			&&  samename(sp->owner, owner))
+			{
+				if (!samename(sp->owner, owner))
+				{	printf("spin: different container %s\n", sp->name);
+					printf("	old: %s\n", sp->owner?sp->owner->name:"--");
+					printf("	new: %s\n", owner?owner->name:"--");
+				/*	alldone(1);	*/
+				}
+				return sp;		/* found */
+	}	}	}
+
+	if (context)				/* in proctype, refers to global */
 	for (sp = symtab[h]; sp; sp = sp->next)
-		if (strcmp(sp->name, s) == 0
-		&&  samename(sp->context, context)
-		&&  samename(sp->owner, owner))
-			return sp;		/* found */
-
-	if (context)				/* in proctype */
-	for (sp = symtab[h]; sp; sp = sp->next)
-		if (strcmp(sp->name, s) == 0
+	{	if (strcmp(sp->name, s) == 0
 		&& !sp->context
 		&&  samename(sp->owner, owner))
-			return sp;		/* global */
+		{	return sp;		/* global */
+	}	}
 
 	sp = (Symbol *) emalloc(sizeof(Symbol));
-	sp->name = (char *) emalloc((int) strlen(s) + 1);
+	sp->name = (char *) emalloc(strlen(s) + 1);
 	strcpy(sp->name, s);
 	sp->nel = 1;
 	sp->setat = depth;
 	sp->context = context;
 	sp->owner = owner;			/* if fld in struct */
+	sp->bscp = (unsigned char *) emalloc(strlen((const char *)CurScope)+1);
+	strcpy((char *)sp->bscp, CurScope);
 
 	if (NamesNotAdded == 0)
 	{	sp->next = symtab[h];
@@ -109,8 +160,6 @@ trackvar(Lextok *n, Lextok *m)
 	}
 }
 
-Lextok *runstmnts = ZN;
-
 void
 trackrun(Lextok *n)
 {
@@ -151,11 +200,11 @@ checkrun(Symbol *parnm, int posno)
 		strcpy(buf2, (!(res&4))?"bit":"byte");
 		sputtype(buf, parnm->type);
 		i = (int) strlen(buf);
-		while (buf[--i] == ' ') buf[i] = '\0';
-		if (strcmp(buf, buf2) == 0) return;
+		while (i > 0 && buf[--i] == ' ') buf[i] = '\0';
+		if (i == 0 || strcmp(buf, buf2) == 0) return;
 		prehint(parnm);
 		printf("proctype %s, '%s %s' could be declared",
-			parnm->context->name, buf, parnm->name);
+			parnm->context?parnm->context->name:"", buf, parnm->name);
 		printf(" '%s %s'\n", buf2, parnm->name);
 	}
 }
@@ -178,9 +227,9 @@ setptype(Lextok *n, int t, Lextok *vis)	/* predefined types */
 
 	while (n)
 	{	if (n->sym->type && !(n->sym->hidden&32))
-		{ lineno = n->ln; Fname = n->fn;
-		  non_fatal("redeclaration of '%s'", n->sym->name);
-		  lineno = oln;
+		{	lineno = n->ln; Fname = n->fn;
+			fatal("redeclaration of '%s'", n->sym->name);
+			lineno = oln;
 		}
 		n->sym->type = (short) t;
 
@@ -201,14 +250,15 @@ setptype(Lextok *n, int t, Lextok *vis)	/* predefined types */
 				n->sym->name);
 		}
 		if (vis)
-		{	if (strncmp(vis->sym->name, ":hide:", 6) == 0)
+		{	if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0)
 			{	n->sym->hidden |= 1;
+				has_hidden++;
 				if (t == BIT)
 				fatal("bit variable (%s) cannot be hidden",
 					n->sym->name);
-			} else if (strncmp(vis->sym->name, ":show:", 6) == 0)
+			} else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0)
 			{	n->sym->hidden |= 2;
-			} else if (strncmp(vis->sym->name, ":local:", 7) == 0)
+			} else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0)
 			{	n->sym->hidden |= 64;
 			}
 		}
@@ -268,7 +318,13 @@ void
 setxus(Lextok *p, int t)
 {	Lextok *m, *n;
 
-	has_xu = 1; 
+	has_xu = 1;
+
+	if (m_loss && t == XS)
+	{	printf("spin: warning, %s:%d, xs tag not compatible with -m (message loss)\n",
+			(p->fn != NULL) ? p->fn->name : "stdin", p->ln);
+	}
+
 	if (!context)
 	{	lineno = p->ln;
 		Fname = p->fn;
@@ -276,6 +332,7 @@ setxus(Lextok *p, int t)
 	}
 	for (m = p; m; m = m->rgt)
 	{	Lextok *Xu_new = (Lextok *) emalloc(sizeof(Lextok));
+		Xu_new->uiid = p->uiid;
 		Xu_new->val = t;
 		Xu_new->lft = m->lft;
 		Xu_new->sym = context;
@@ -297,8 +354,6 @@ setxus(Lextok *p, int t)
 	}
 }
 
-Lextok *Mtype = (Lextok *) 0;
-
 void
 setmtype(Lextok *m)
 {	Lextok *n;
@@ -332,7 +387,7 @@ setmtype(Lextok *m)
 	}
 	lineno = oln;
 	if (cnt > 256)
-	fatal("too many mtype elements (>255)", (char *)0);
+		fatal("too many mtype elements (>255)", (char *)0);
 }
 
 int
@@ -389,11 +444,11 @@ symvar(Symbol *sp)
 	printf("\t");
 	if (sp->owner) printf("%s.", sp->owner->name);
 	printf("%s", sp->name);
-	if (sp->nel > 1) printf("[%d]", sp->nel);
+	if (sp->nel > 1 || sp->isarray == 1) printf("[%d]", sp->nel);
 
 	if (sp->type == CHAN)
 		printf("\t%d", (sp->ini)?sp->ini->val:0);
-	else if (sp->type == STRUCT) /* Frank Weil, 2.9.8 */
+	else if (sp->type == STRUCT && sp->Snm != NULL) /* Frank Weil, 2.9.8 */
 		printf("\t%s", sp->Snm->name);
 	else
 		printf("\t%d", eval(sp->ini));
@@ -408,8 +463,13 @@ symvar(Symbol *sp)
 
 	if (sp->Nid < 0)	/* formal parameter */
 		printf("\t<parameter %d>", -(sp->Nid));
+	else if (sp->type == MTYPE)
+		printf("\t<constant>");
+	else if (sp->isarray)
+		printf("\t<array>");
 	else
 		printf("\t<variable>");
+
 	if (sp->type == CHAN && sp->ini)
 	{	int i;
 		for (m = sp->ini->rgt, i = 0; m; m = m->rgt)
@@ -423,6 +483,9 @@ symvar(Symbol *sp)
 			if (m->rgt) printf("\t");
 		}
 	}
+
+if (1)	printf("\t{scope %s}", sp->bscp);
+
 	printf("\n");
 }
 
@@ -440,11 +503,10 @@ chname(Symbol *sp)
 	if (sp->context) printf("%s-", sp->context->name);
 	if (sp->owner) printf("%s.", sp->owner->name);
 	printf("%s", sp->name);
-	if (sp->nel > 1) printf("[%d]", sp->nel);
+	if (sp->nel > 1 || sp->isarray == 1) printf("[%d]", sp->nel);
 	printf("\t");
 }
 
-
 static struct X {
 	int typ; char *nm;
 } xx[] = {

+ 5 - 1
sys/src/cmd/spin/tl.h

@@ -82,7 +82,7 @@ Symbol	*tl_lookup(char *);
 Symbol	*getsym(Symbol *);
 Symbol	*DoDump(Node *);
 
-char	*emalloc(int);	/* in main.c */
+extern char	*emalloc(size_t);	/* in main.c */
 
 int	anywhere(int, Node *, Node *);
 int	dump_cond(Node *, Node *, int);
@@ -100,6 +100,10 @@ void	exit(int);
 void	Fatal(char *, char *);
 void	fatal(char *, char *);
 void	fsm_print(void);
+void	ini_buchi(void);
+void	ini_cache(void);
+void	ini_rewrt(void);
+void	ini_trans(void);
 void	releasenode(int, Node *);
 void	tfree(void *);
 void	tl_explain(int);

+ 10 - 2
sys/src/cmd/spin/tl_buchi.c

@@ -15,6 +15,7 @@
 #include "tl.h"
 
 extern int tl_verbose, tl_clutter, Total, Max_Red;
+extern char *claim_name;
 
 FILE	*tl_out;	/* if standalone: = stdout; */
 
@@ -38,6 +39,13 @@ typedef struct State {
 static State *never = (State *) 0;
 static int hitsall;
 
+void
+ini_buchi(void)
+{
+	never = (State *) 0;
+	hitsall = 0;
+}
+
 static int
 sametrans(Transition *s, Transition *t)
 {
@@ -626,13 +634,13 @@ fsm_print(void)
 	if (tl_clutter) clutter();
 
 	b = findstate("T0_init");
-	if (Max_Red == 0)
+	if (b && (Max_Red == 0))
 		b->accepting = 1;
 
 	mergestates(0); 
 	b = findstate("T0_init");
 
-	fprintf(tl_out, "never {    /* ");
+	fprintf(tl_out, "never %s {    /* ", claim_name?claim_name:"");
 		put_uform();
 	fprintf(tl_out, " */\n");
 

+ 8 - 0
sys/src/cmd/spin/tl_cache.c

@@ -28,6 +28,14 @@ static int	ismatch(Node *, Node *);
 extern void fatal(char *, char *);
 int	sameform(Node *, Node *);
 
+void
+ini_cache(void)
+{
+	stored = (Cache *) 0;
+	Caches = 0;
+	CacheHits = 0;
+}
+
 #if 0
 void
 cache_dump(void)

+ 98 - 1
sys/src/cmd/spin/tl_lex.c

@@ -18,6 +18,7 @@
 
 static Symbol	*symtab[Nhash+1];
 static int	tl_lex(void);
+extern int tl_peek(int);
 
 extern YYSTYPE	tl_yylval;
 extern char	yytext[];
@@ -54,11 +55,78 @@ int
 tl_yylex(void)
 {	int c = tl_lex();
 #if 0
-	printf("c = %d\n", c);
+	printf("c = %c (%d)\n", c, c);
 #endif
 	return c;
 }
 
+static int
+is_predicate(int z)
+{	char c, c_prev = z;
+	char want = (z == '{') ? '}' : ')';
+	int i = 0, j, nesting = 0;
+	char peek_buf[512];
+
+	c = tl_peek(i++);	/* look ahead without changing position */
+	while ((c != want || nesting > 0) && c != -1 && i < 2047)
+	{	if (islower((int) c))
+		{	peek_buf[0] = c;
+			j = 1;
+			while (j < (int) sizeof(peek_buf) && isalnum((int)(c = tl_peek(i))))
+			{	peek_buf[j++] = c;
+				i++;
+			}
+			c = 0;	/* make sure we don't match on z or want on the peekahead */
+			if (j >= (int) sizeof(peek_buf))
+			{	peek_buf[j-1] = '\0';
+				fatal("name '%s' in ltl formula too long", peek_buf);
+			}
+			peek_buf[j] = '\0';
+			if (strcmp(peek_buf, "always") == 0
+			||  strcmp(peek_buf, "eventually") == 0
+			||  strcmp(peek_buf, "until") == 0
+			||  strcmp(peek_buf, "next") == 0)
+			{	return 0;
+			}
+		} else
+		{	char c_nxt = tl_peek(i);
+			if (((c == 'U' || c == 'V' || c == 'X') && !isalnum_(c_prev) && !isalnum_(c_nxt))
+			||  (c == '<' && c_nxt == '>')
+			||  (c == '[' && c_nxt == ']'))
+			{	return 0;
+		}	}
+
+		if (c == z)
+		{	nesting++;
+		}
+		if (c == want)
+		{	nesting--;
+		}
+		c_prev = c;
+		c = tl_peek(i++);
+	}
+	return 1;
+}
+
+static void
+read_upto_closing(int z)
+{	char c, want = (z == '{') ? '}' : ')';
+	int i = 0, nesting = 0;
+
+	c = tl_Getchar();
+	while ((c != want || nesting > 0) && c != -1 && i < 2047) /* yytext is 2048 */
+	{	yytext[i++] = c;
+		if (c == z)
+		{	nesting++;
+		}
+		if (c == want)
+		{	nesting--;
+		}
+		c = tl_Getchar();
+	}
+	yytext[i] = '\0';
+}
+
 static int
 tl_lex(void)
 {	int c;
@@ -74,6 +142,17 @@ tl_lex(void)
 
 	} while (c == ' ');	/* '\t' is removed in tl_main.c */
 
+	if (c == '{' || c == '(')	/* new 6.0.0 */
+	{	if (is_predicate(c))
+		{	read_upto_closing(c);
+			tl_yylval = tl_nn(PREDICATE,ZN,ZN);
+			tl_yylval->sym = tl_lookup(yytext);
+			return PREDICATE;
+	}	}
+
+	if (c == '}')
+	{	tl_yyerror("unexpected '}'");
+	}
 	if (islower(c))
 	{	tl_getword(c, isalnum_);
 		if (strcmp("true", yytext) == 0)
@@ -82,10 +161,28 @@ tl_lex(void)
 		if (strcmp("false", yytext) == 0)
 		{	Token(FALSE);
 		}
+		if (strcmp("always", yytext) == 0)
+		{	Token(ALWAYS);
+		}
+		if (strcmp("eventually", yytext) == 0)
+		{	Token(EVENTUALLY);
+		}
+		if (strcmp("until", yytext) == 0)
+		{	Token(U_OPER);
+		}
+#ifdef NXT
+		if (strcmp("next", yytext) == 0)
+		{	Token(NEXT);
+		}
+#endif
+		if (strcmp("not", yytext) == 0)
+		{	Token(NOT);
+		}
 		tl_yylval = tl_nn(PREDICATE,ZN,ZN);
 		tl_yylval->sym = tl_lookup(yytext);
 		return PREDICATE;
 	}
+
 	if (c == '<')
 	{	c = tl_Getchar();
 		if (c == '>')

+ 37 - 4
sys/src/cmd/spin/tl_main.c

@@ -21,7 +21,10 @@ int	tl_errs    = 0;
 int	tl_verbose = 0;
 int	tl_terse   = 0;
 int	tl_clutter = 0;
+int	state_cnt = 0;
+
 unsigned long	All_Mem = 0;
+char	*claim_name;
 
 static char	uform[4096];
 static int	hasuform=0, cnt=0;
@@ -38,6 +41,15 @@ tl_Getchar(void)
 	return -1;
 }
 
+int
+tl_peek(int n)
+{
+	if (cnt+n < hasuform)
+	{	return uform[cnt+n];
+	}
+	return -1;
+}
+
 void
 tl_balanced(void)
 {	int i;
@@ -79,16 +91,33 @@ tl_stats(void)
 int
 tl_main(int argc, char *argv[])
 {	int i;
-	extern int verbose, xspin;
-	tl_verbose = verbose;
+	extern int /* verbose, */ xspin;
+
+	tl_verbose = 0; /* was: tl_verbose = verbose; */
 	tl_clutter = 1-xspin;	/* use -X -f to turn off uncluttering */
 
+	newstates  = 0;
+	state_cnt  = 0;
+	tl_errs    = 0;
+	tl_terse   = 0;
+	All_Mem = 0;
+	memset(uform, 0, sizeof(uform));
+	hasuform=0;
+	cnt=0;
+	claim_name = (char *) 0;
+
+	ini_buchi();
+	ini_cache();
+	ini_rewrt();
+	ini_trans();
+
 	while (argc > 1 && argv[1][0] == '-')
-	{	switch (argv[1][1]) {
+	{
+		switch (argv[1][1]) {
 		case 'd':	newstates = 1;	/* debugging mode */
 				break;
 		case 'f':	argc--; argv++;
-				for (i = 0; i < argv[1][i]; i++)
+				for (i = 0; argv[1][i]; i++)
 				{	if (argv[1][i] == '\t'
 					||  argv[1][i] == '\"'
 					||  argv[1][i] == '\n')
@@ -101,6 +130,10 @@ tl_main(int argc, char *argv[])
 				break;
 		case 'n':	tl_terse = 1;
 				break;
+		case 'c':	argc--; argv++;
+				claim_name = (char *) emalloc(strlen(argv[1])+1);
+				strcpy(claim_name, argv[1]);
+				break;
 		default :	printf("spin -f: saw '-%c'\n", argv[1][1]);
 				goto nogood;
 		}

+ 4 - 2
sys/src/cmd/spin/tl_parse.c

@@ -48,7 +48,6 @@ tl_factor(void)
 		break;
 	case ALWAYS:
 		tl_yychar = tl_yylex();
-
 		ptr = tl_factor();
 #ifndef NO_OPT
 		if (ptr->ntyp == FALSE
@@ -385,7 +384,10 @@ tl_formula(void)
 
 void
 tl_parse(void)
-{	Node *n = tl_formula();
+{	Node *n;
+
+	/* tl_verbose = 1; */
+	n = tl_formula();
 	if (tl_verbose)
 	{	printf("formula: ");
 		dump(n);

+ 10 - 1
sys/src/cmd/spin/tl_rewrt.c

@@ -18,6 +18,12 @@ extern int	tl_verbose;
 
 static Node	*can = ZN;
 
+void
+ini_rewrt(void)
+{
+	can = ZN;
+}
+
 Node *
 right_linked(Node *n)
 {
@@ -146,7 +152,10 @@ addcan(int tok, Node *n)
 	prev = ZN;
 	for (m = can; m->ntyp == tok && m->rgt; prev = m, m = m->rgt)
 	{	t = DoDump(m->lft);
-		cmp = strcmp(s->name, t->name);
+		if (t != ZS)
+			cmp = strcmp(s->name, t->name);
+		else
+			cmp = 0;
 		if (cmp == 0)	/* duplicate */
 			return;
 		if (cmp < 0)

+ 25 - 5
sys/src/cmd/spin/tl_trans.c

@@ -15,7 +15,7 @@
 #include "tl.h"
 
 extern FILE	*tl_out;
-extern int	tl_errs, tl_verbose, tl_terse, newstates;
+extern int	tl_errs, tl_verbose, tl_terse, newstates, state_cnt;
 
 int	Stack_mx=0, Max_Red=0, Total=0;
 
@@ -51,6 +51,24 @@ static void	ng(Symbol *, Symbol *, Node *, Node *, Node *);
 static void	push_stack(Graph *);
 static void	sdump(Node *);
 
+void
+ini_trans(void)
+{
+	Stack_mx = 0;
+	Max_Red = 0;
+	Total = 0;
+
+	Mapped = (Mapping *) 0;
+	Nodes_Set = (Graph *) 0;
+	Nodes_Stack = (Graph *) 0;
+
+	memset(dumpbuf, 0, sizeof(dumpbuf));
+	Red_cnt  = 0;
+	Lab_cnt  = 0;
+	Base     = 0;
+	Stack_sz = 0;
+}
+
 static void
 dump_graph(Graph *g)
 {	Node *n1;
@@ -101,9 +119,8 @@ pop_stack(void)
 
 static char *
 newname(void)
-{	static int cnt = 0;
-	static char buf[32];
-	sprintf(buf, "S%d", cnt++);
+{	static char buf[32];
+	sprintf(buf, "S%d", state_cnt++);
 	return buf;
 }
 
@@ -342,7 +359,7 @@ static void
 fsm_trans(Graph *p, int count, char *curnm)
 {	Graph	*r;
 	Symbol	*s;
-	char	prefix[128], nwnm[128];
+	char	prefix[128], nwnm[256];
 
 	if (!p->outgoing)
 		addtrans(p, curnm, False, "accept_all");
@@ -722,6 +739,8 @@ out:
 		push_stack(g);
 		break;
 	case V_OPER:
+		Assert(now->rgt != ZN, now->ntyp);
+		Assert(now->lft != ZN, now->ntyp);
 		Assert(now->rgt->nxt == ZN, now->ntyp);
 		Assert(now->lft->nxt == ZN, now->ntyp);
 		n1 = now->rgt;
@@ -759,6 +778,7 @@ out:
 
 #ifdef NXT
 	case NEXT:
+		Assert(now->lft != ZN, now->ntyp);
 		nx = dupnode(now->lft);
 		nx->nxt = g->Next;
 		g->Next = nx;

+ 22 - 11
sys/src/cmd/spin/vars.c

@@ -110,6 +110,7 @@ checkvar(Symbol *s, int n)
 	}	}
 	lineno = oln;
 	Fname  = ofnm;
+
 	return 1;
 }
 
@@ -145,7 +146,7 @@ cast_val(int t, int v, int w)
 	}
 
 	if (v != i+s+ (int) u)
-	{	char buf[32]; sprintf(buf, "%d->%d (%d)", v, i+s+u, t);
+	{	char buf[64]; sprintf(buf, "%d->%d (%d)", v, i+s+u, t);
 		non_fatal("value (%s) truncated in assignment", buf);
 	}
 	return (int)(i+s+u);
@@ -159,9 +160,12 @@ setglobal(Lextok *v, int m)
 	else
 	{	int n = eval(v->lft);
 		if (checkvar(v->sym, n))
-		{	v->sym->val[n] = cast_val(v->sym->type, m, v->sym->nbits);
-			v->sym->setat = depth;
-	}	}
+		{	int oval = v->sym->val[n];
+			int nval = cast_val(v->sym->type, m, v->sym->nbits);
+			v->sym->val[n] = nval;
+			if (oval != nval)
+			{	v->sym->setat = depth;
+	}	}	}
 	return 1;
 }
 
@@ -215,7 +219,12 @@ dumpglobals(void)
 			continue;
 
 		if (sp->type == STRUCT)
-		{	dump_struct(sp, sp->name, 0);
+		{	if ((verbose&4) && !(verbose&64)
+			&&  (sp->setat < depth
+			&&   jumpsteps != depth))
+			{	continue;
+			}
+			dump_struct(sp, sp->name, 0);
 			continue;
 		}
 		for (j = 0; j < sp->nel; j++)
@@ -227,13 +236,15 @@ dumpglobals(void)
 			if ((verbose&4) && !(verbose&64)
 			&&  (sp->setat < depth
 			&&   jumpsteps != depth))
-				continue;
+			{	continue;
+			}
+
 			dummy->sym = sp;
 			dummy->lft->val = j;
 			/* in case of cast_val warnings, do this first: */
 			prefetch = getglobal(dummy);
 			printf("\t\t%s", sp->name);
-			if (sp->nel > 1) printf("[%d]", j);
+			if (sp->nel > 1 || sp->isarray) printf("[%d]", j);
 			printf(" = ");
 			sr_mesg(stdout, prefetch,
 				sp->type == MTYPE);
@@ -266,7 +277,7 @@ dumpglobals(void)
 					depth, colpos);
 				printf("(state 0)\t[printf('MSC: globvar\\\\n')]\n");
 				printf("\t\t%s", sp->name);
-				if (sp->nel > 1) printf("[%d]", j);
+				if (sp->nel > 1 || sp->isarray) printf("[%d]", j);
 				printf(" = %s\n", Buf);
 	}	}	}
 }
@@ -303,8 +314,8 @@ dumplocal(RunList *r)
 			dummy->lft->val = i;
 
 			printf("\t\t%s(%d):%s",
-				r->n->name, r->pid, z->name);
-			if (z->nel > 1) printf("[%d]", i);
+				r->n->name, r->pid - Have_claim, z->name);
+			if (z->nel > 1 || z->isarray) printf("[%d]", i);
 			printf(" = ");
 			sr_mesg(stdout, getval(dummy), z->type == MTYPE);
 			printf("\n");
@@ -341,7 +352,7 @@ dumplocal(RunList *r)
 				printf("(state 0)\t[printf('MSC: locvar\\\\n')]\n");
 				printf("\t\t%s(%d):%s",
 					r->n->name, r->pid, z->name);
-				if (z->nel > 1) printf("[%d]", i);
+				if (z->nel > 1 || z->isarray) printf("[%d]", i);
 				printf(" = %s\n", Buf);
 	}	}	}
 }

+ 1 - 1
sys/src/cmd/spin/version.h

@@ -1 +1 @@
-#define Version	"Spin Version 4.3.0 -- 22 June 2007"
+#define SpinVersion	"Spin Version 6.1.0 -- 4 May 2011"

Some files were not shown because too many files changed in this diff