diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/spin |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/spin')
39 files changed, 29175 insertions, 0 deletions
diff --git a/sys/src/cmd/spin/README b/sys/src/cmd/spin/README new file mode 100755 index 000000000..5e419923e --- /dev/null +++ b/sys/src/cmd/spin/README @@ -0,0 +1,11 @@ +the latest version of spin is always +available via: + http://spinroot.com/spin/whatispin.html + +to make the sources compile with the mkfile on Plan 9 +the following changes were made: + + omitted memory.h from spin.h + changed /lib/cpp to /bin/cpp in main.c + simplified non_fatal() in main.c to remove + use of yychar diff --git a/sys/src/cmd/spin/dstep.c b/sys/src/cmd/spin/dstep.c new file mode 100755 index 000000000..f24860309 --- /dev/null +++ b/sys/src/cmd/spin/dstep.c @@ -0,0 +1,411 @@ +/***** spin: dstep.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +#define MAXDSTEP 1024 /* was 512 */ + +char *NextLab[64]; +int Level=0, GenCode=0, IsGuard=0, TestOnly=0; + +static int Tj=0, Jt=0, LastGoto=0; +static int Tojump[MAXDSTEP], Jumpto[MAXDSTEP], Special[MAXDSTEP]; +static void putCode(FILE *, Element *, Element *, Element *, int); + +extern int Pid, claimnr, separate, OkBreak; + +static void +Sourced(int n, int special) +{ int i; + for (i = 0; i < Tj; i++) + if (Tojump[i] == n) + return; + if (Tj >= MAXDSTEP) + fatal("d_step sequence too long", (char *)0); + Special[Tj] = special; + Tojump[Tj++] = n; +} + +static void +Dested(int n) +{ int i; + for (i = 0; i < Tj; i++) + if (Tojump[i] == n) + return; + for (i = 0; i < Jt; i++) + if (Jumpto[i] == n) + return; + if (Jt >= MAXDSTEP) + fatal("d_step sequence too long", (char *)0); + Jumpto[Jt++] = n; + LastGoto = 1; +} + +static void +Mopup(FILE *fd) +{ int i, j; + + for (i = 0; i < Jt; i++) + { for (j = 0; j < Tj; j++) + if (Tojump[j] == Jumpto[i]) + break; + if (j == Tj) + { char buf[12]; + if (Jumpto[i] == OkBreak) + { if (!LastGoto) + fprintf(fd, "S_%.3d_0: /* break-dest */\n", + OkBreak); + } else { + sprintf(buf, "S_%.3d_0", Jumpto[i]); + non_fatal("goto %s breaks from d_step seq", buf); + } } } + for (j = 0; j < Tj; j++) + { for (i = 0; i < Jt; i++) + if (Tojump[j] == Jumpto[i]) + break; +#ifdef DEBUG + if (i == Jt && !Special[i]) + fprintf(fd, "\t\t/* no goto's to S_%.3d_0 */\n", + Tojump[j]); +#endif + } + for (j = i = 0; j < Tj; j++) + if (Special[j]) + { Tojump[i] = Tojump[j]; + Special[i] = 2; + if (i >= MAXDSTEP) + fatal("cannot happen (dstep.c)", (char *)0); + i++; + } + Tj = i; /* keep only the global exit-labels */ + Jt = 0; +} + +static int +FirstTime(int n) +{ int i; + for (i = 0; i < Tj; i++) + if (Tojump[i] == n) + return (Special[i] <= 1); + return 1; +} + +static void +illegal(Element *e, char *str) +{ + printf("illegal operator in 'd_step:' '"); + comment(stdout, e->n, 0); + printf("'\n"); + fatal("'%s'", str); +} + +static void +filterbad(Element *e) +{ + switch (e->n->ntyp) { + case ASSERT: + case PRINT: + case 'c': + /* run cannot be completely undone + * with sv_save-sv_restor + */ + if (any_oper(e->n->lft, RUN)) + illegal(e, "run operator in d_step"); + + /* remote refs inside d_step sequences + * would be okay, but they cannot always + * be interpreted by the simulator the + * same as by the verifier (e.g., for an + * error trail) + */ + if (any_oper(e->n->lft, 'p')) + illegal(e, "remote reference in d_step"); + break; + case '@': + illegal(e, "process termination"); + break; + case D_STEP: + illegal(e, "nested d_step sequence"); + break; + case ATOMIC: + illegal(e, "nested atomic sequence"); + break; + default: + break; + } +} + +static int +CollectGuards(FILE *fd, Element *e, int inh) +{ SeqList *h; Element *ee; + + for (h = e->sub; h; h = h->nxt) + { ee = huntstart(h->this->frst); + filterbad(ee); + switch (ee->n->ntyp) { + case NON_ATOMIC: + inh += CollectGuards(fd, ee->n->sl->this->frst, inh); + break; + case IF: + inh += CollectGuards(fd, ee, inh); + break; + case '.': + if (ee->nxt->n->ntyp == DO) + inh += CollectGuards(fd, ee->nxt, inh); + break; + case ELSE: + if (inh++ > 0) fprintf(fd, " || "); +/* 4.2.5 */ if (Pid != claimnr) + fprintf(fd, "(boq == -1 /* else */)"); + else + fprintf(fd, "(1 /* else */)"); + break; + case 'R': + if (inh++ > 0) fprintf(fd, " || "); + fprintf(fd, "("); TestOnly=1; + putstmnt(fd, ee->n, ee->seqno); + fprintf(fd, ")"); TestOnly=0; + break; + case 'r': + if (inh++ > 0) fprintf(fd, " || "); + fprintf(fd, "("); TestOnly=1; + putstmnt(fd, ee->n, ee->seqno); + fprintf(fd, ")"); TestOnly=0; + break; + case 's': + if (inh++ > 0) fprintf(fd, " || "); + fprintf(fd, "("); TestOnly=1; +/* 4.2.1 */ if (Pid != claimnr) fprintf(fd, "(boq == -1) && "); + putstmnt(fd, ee->n, ee->seqno); + fprintf(fd, ")"); TestOnly=0; + break; + case 'c': + if (inh++ > 0) fprintf(fd, " || "); + fprintf(fd, "("); TestOnly=1; + if (Pid != claimnr) + fprintf(fd, "(boq == -1 && "); + putstmnt(fd, ee->n->lft, e->seqno); + if (Pid != claimnr) + fprintf(fd, ")"); + fprintf(fd, ")"); TestOnly=0; + break; + } } + return inh; +} + +int +putcode(FILE *fd, Sequence *s, Element *nxt, int justguards, int ln, int seqno) +{ int isg=0; char buf[64]; + + NextLab[0] = "continue"; + filterbad(s->frst); + + switch (s->frst->n->ntyp) { + case UNLESS: + non_fatal("'unless' inside d_step - ignored", (char *) 0); + return putcode(fd, s->frst->n->sl->this, nxt, 0, ln, seqno); + case NON_ATOMIC: + (void) putcode(fd, s->frst->n->sl->this, ZE, 1, ln, seqno); + break; + case IF: + fprintf(fd, "if (!("); + if (!CollectGuards(fd, s->frst, 0)) /* what about boq? */ + fprintf(fd, "1"); + fprintf(fd, "))\n\t\t\tcontinue;"); + isg = 1; + break; + case '.': + if (s->frst->nxt->n->ntyp == DO) + { fprintf(fd, "if (!("); + if (!CollectGuards(fd, s->frst->nxt, 0)) + fprintf(fd, "1"); + fprintf(fd, "))\n\t\t\tcontinue;"); + isg = 1; + } + break; + case 'R': /* <- can't really happen (it's part of a 'c') */ + fprintf(fd, "if (!("); TestOnly=1; + putstmnt(fd, s->frst->n, s->frst->seqno); + fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0; + break; + case 'r': + fprintf(fd, "if (!("); TestOnly=1; + putstmnt(fd, s->frst->n, s->frst->seqno); + fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0; + break; + case 's': + fprintf(fd, "if ("); +#if 1 +/* 4.2.1 */ if (Pid != claimnr) fprintf(fd, "(boq != -1) || "); +#endif + fprintf(fd, "!("); TestOnly=1; + putstmnt(fd, s->frst->n, s->frst->seqno); + fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0; + break; + case 'c': + fprintf(fd, "if (!("); + if (Pid != claimnr) fprintf(fd, "boq == -1 && "); + TestOnly=1; + putstmnt(fd, s->frst->n->lft, s->frst->seqno); + fprintf(fd, "))\n\t\t\tcontinue;"); TestOnly=0; + break; + case ELSE: + fprintf(fd, "if (boq != -1 || ("); + if (separate != 2) fprintf(fd, "trpt->"); + fprintf(fd, "o_pm&1))\n\t\t\tcontinue;"); + break; + case ASGN: /* new 3.0.8 */ + fprintf(fd, "IfNotBlocked"); + break; + } + if (justguards) return 0; + + fprintf(fd, "\n\t\tsv_save();\n\t\t"); +#if 1 + fprintf(fd, "reached[%d][%d] = 1;\n\t\t", Pid, seqno); + fprintf(fd, "reached[%d][t->st] = 1;\n\t\t", Pid); /* true next state */ + fprintf(fd, "reached[%d][tt] = 1;\n", Pid); /* true current state */ +#endif + sprintf(buf, "Uerror(\"block in d_step seq, line %d\")", ln); + NextLab[0] = buf; + putCode(fd, s->frst, s->extent, nxt, isg); + + if (nxt) + { extern Symbol *Fname; + extern int lineno; + + if (FirstTime(nxt->Seqno) + && (!(nxt->status & DONE2) || !(nxt->status & D_ATOM))) + { fprintf(fd, "S_%.3d_0: /* 1 */\n", nxt->Seqno); + nxt->status |= DONE2; + LastGoto = 0; + } + Sourced(nxt->Seqno, 1); + lineno = ln; + Fname = nxt->n->fn; + Mopup(fd); + } + unskip(s->frst->seqno); + return LastGoto; +} + +static void +putCode(FILE *fd, Element *f, Element *last, Element *next, int isguard) +{ Element *e, *N; + SeqList *h; int i; + char NextOpt[64]; + static int bno = 0; + + for (e = f; e; e = e->nxt) + { if (e->status & DONE2) + continue; + e->status |= DONE2; + + if (!(e->status & D_ATOM)) + { if (!LastGoto) + { fprintf(fd, "\t\tgoto S_%.3d_0;\n", + e->Seqno); + Dested(e->Seqno); + } + break; + } + fprintf(fd, "S_%.3d_0: /* 2 */\n", e->Seqno); + LastGoto = 0; + Sourced(e->Seqno, 0); + + if (!e->sub) + { filterbad(e); + switch (e->n->ntyp) { + case NON_ATOMIC: + h = e->n->sl; + putCode(fd, h->this->frst, + h->this->extent, e->nxt, 0); + break; + case BREAK: + if (LastGoto) break; + if (e->nxt) + { i = target( huntele(e->nxt, + e->status, -1))->Seqno; + fprintf(fd, "\t\tgoto S_%.3d_0; ", i); + fprintf(fd, "/* 'break' */\n"); + Dested(i); + } else + { if (next) + { fprintf(fd, "\t\tgoto S_%.3d_0;", + next->Seqno); + fprintf(fd, " /* NEXT */\n"); + Dested(next->Seqno); + } else + fatal("cannot interpret d_step", 0); + } + break; + case GOTO: + if (LastGoto) break; + i = huntele( get_lab(e->n,1), + e->status, -1)->Seqno; + fprintf(fd, "\t\tgoto S_%.3d_0; ", i); + fprintf(fd, "/* 'goto' */\n"); + Dested(i); + break; + case '.': + if (LastGoto) break; + if (e->nxt && (e->nxt->status & DONE2)) + { i = e->nxt?e->nxt->Seqno:0; + fprintf(fd, "\t\tgoto S_%.3d_0;", i); + fprintf(fd, " /* '.' */\n"); + Dested(i); + } + break; + default: + putskip(e->seqno); + GenCode = 1; IsGuard = isguard; + fprintf(fd, "\t\t"); + putstmnt(fd, e->n, e->seqno); + fprintf(fd, ";\n"); + GenCode = IsGuard = isguard = LastGoto = 0; + break; + } + i = e->nxt?e->nxt->Seqno:0; + if (e->nxt && e->nxt->status & DONE2 && !LastGoto) + { fprintf(fd, "\t\tgoto S_%.3d_0; ", i); + fprintf(fd, "/* ';' */\n"); + Dested(i); + break; + } + } else + { for (h = e->sub, i=1; h; h = h->nxt, i++) + { sprintf(NextOpt, "goto S_%.3d_%d", + e->Seqno, i); + NextLab[++Level] = NextOpt; + N = (e->n && e->n->ntyp == DO) ? e : e->nxt; + putCode(fd, h->this->frst, + h->this->extent, N, 1); + Level--; + fprintf(fd, "%s: /* 3 */\n", &NextOpt[5]); + LastGoto = 0; + } + if (!LastGoto) + { fprintf(fd, "\t\tUerror(\"blocking sel "); + fprintf(fd, "in d_step (nr.%d, near line %d)\");\n", + bno++, (e->n)?e->n->ln:0); + LastGoto = 0; + } + } + if (e == last) + { if (!LastGoto && next) + { fprintf(fd, "\t\tgoto S_%.3d_0;\n", + next->Seqno); + Dested(next->Seqno); + } + break; + } } +} diff --git a/sys/src/cmd/spin/flow.c b/sys/src/cmd/spin/flow.c new file mode 100755 index 000000000..ac3e4e4b6 --- /dev/null +++ b/sys/src/cmd/spin/flow.c @@ -0,0 +1,794 @@ +/***** spin: flow.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +extern Symbol *Fname; +extern int nr_errs, lineno, verbose; +extern short has_unless, has_badelse; + +Element *Al_El = ZE; +Label *labtab = (Label *) 0; +int Unique=0, Elcnt=0, DstepStart = -1; + +static Lbreak *breakstack = (Lbreak *) 0; +static Lextok *innermost; +static SeqList *cur_s = (SeqList *) 0; +static int break_id=0; + +static Element *if_seq(Lextok *); +static Element *new_el(Lextok *); +static Element *unless_seq(Lextok *); +static void add_el(Element *, Sequence *); +static void attach_escape(Sequence *, Sequence *); +static void mov_lab(Symbol *, Element *, Element *); +static void walk_atomic(Element *, Element *, int); + +void +open_seq(int top) +{ SeqList *t; + Sequence *s = (Sequence *) emalloc(sizeof(Sequence)); + + t = seqlist(s, cur_s); + cur_s = t; + if (top) Elcnt = 1; +} + +void +rem_Seq(void) +{ + DstepStart = Unique; +} + +void +unrem_Seq(void) +{ + DstepStart = -1; +} + +static int +Rjumpslocal(Element *q, Element *stop) +{ Element *lb, *f; + SeqList *h; + + /* allow no jumps out of a d_step sequence */ + for (f = q; f && f != stop; f = f->nxt) + { if (f && f->n && f->n->ntyp == GOTO) + { lb = get_lab(f->n, 0); + if (!lb || lb->Seqno < DstepStart) + { lineno = f->n->ln; + Fname = f->n->fn; + return 0; + } } + for (h = f->sub; h; h = h->nxt) + { if (!Rjumpslocal(h->this->frst, h->this->last)) + return 0; + + } } + return 1; +} + +void +cross_dsteps(Lextok *a, Lextok *b) +{ + if (a && b + && a->indstep != b->indstep) + { lineno = a->ln; + Fname = a->fn; + fatal("jump into d_step sequence", (char *) 0); + } +} + +int +is_skip(Lextok *n) +{ + return (n->ntyp == PRINT + || n->ntyp == PRINTM + || (n->ntyp == 'c' + && n->lft + && n->lft->ntyp == CONST + && n->lft->val == 1)); +} + +void +check_sequence(Sequence *s) +{ Element *e, *le = ZE; + Lextok *n; + int cnt = 0; + + for (e = s->frst; e; le = e, e = e->nxt) + { n = e->n; + if (is_skip(n) && !has_lab(e, 0)) + { cnt++; + if (cnt > 1 + && n->ntyp != PRINT + && n->ntyp != PRINTM) + { if (verbose&32) + printf("spin: line %d %s, redundant skip\n", + n->ln, n->fn->name); + if (e != s->frst + && e != s->last + && e != s->extent) + { e->status |= DONE; /* not unreachable */ + le->nxt = e->nxt; /* remove it */ + e = le; + } + } + } else + cnt = 0; + } +} + +void +prune_opts(Lextok *n) +{ SeqList *l; + extern Symbol *context; + extern char *claimproc; + + if (!n + || (context && claimproc && strcmp(context->name, claimproc) == 0)) + return; + + for (l = n->sl; l; l = l->nxt) /* find sequences of unlabeled skips */ + check_sequence(l->this); +} + +Sequence * +close_seq(int nottop) +{ Sequence *s = cur_s->this; + Symbol *z; + + if (nottop > 0 && (z = has_lab(s->frst, 0))) + { printf("error: (%s:%d) label %s placed incorrectly\n", + (s->frst->n)?s->frst->n->fn->name:"-", + (s->frst->n)?s->frst->n->ln:0, + z->name); + switch (nottop) { + case 1: + printf("=====> stmnt unless Label: stmnt\n"); + printf("sorry, cannot jump to the guard of an\n"); + printf("escape (it is not a unique state)\n"); + break; + case 2: + printf("=====> instead of "); + printf("\"Label: stmnt unless stmnt\"\n"); + printf("=====> always use "); + printf("\"Label: { stmnt unless stmnt }\"\n"); + break; + case 3: + printf("=====> instead of "); + printf("\"atomic { Label: statement ... }\"\n"); + printf("=====> always use "); + printf("\"Label: atomic { statement ... }\"\n"); + break; + case 4: + printf("=====> instead of "); + printf("\"d_step { Label: statement ... }\"\n"); + printf("=====> always use "); + printf("\"Label: d_step { statement ... }\"\n"); + break; + case 5: + printf("=====> instead of "); + printf("\"{ Label: statement ... }\"\n"); + printf("=====> always use "); + printf("\"Label: { statement ... }\"\n"); + break; + case 6: + printf("=====>instead of\n"); + printf(" do (or if)\n"); + printf(" :: ...\n"); + printf(" :: Label: statement\n"); + printf(" od (of fi)\n"); + printf("=====>always use\n"); + printf("Label: do (or if)\n"); + printf(" :: ...\n"); + printf(" :: statement\n"); + printf(" od (or fi)\n"); + break; + case 7: + printf("cannot happen - labels\n"); + break; + } + alldone(1); + } + + if (nottop == 4 + && !Rjumpslocal(s->frst, s->last)) + fatal("non_local jump in d_step sequence", (char *) 0); + + cur_s = cur_s->nxt; + s->maxel = Elcnt; + s->extent = s->last; + if (!s->last) + fatal("sequence must have at least one statement", (char *) 0); + return s; +} + +Lextok * +do_unless(Lextok *No, Lextok *Es) +{ SeqList *Sl; + Lextok *Re = nn(ZN, UNLESS, ZN, ZN); + Re->ln = No->ln; + Re->fn = No->fn; + + has_unless++; + if (Es->ntyp == NON_ATOMIC) + Sl = Es->sl; + else + { open_seq(0); add_seq(Es); + Sl = seqlist(close_seq(1), 0); + } + + if (No->ntyp == NON_ATOMIC) + { No->sl->nxt = Sl; + Sl = No->sl; + } else if (No->ntyp == ':' + && (No->lft->ntyp == NON_ATOMIC + || No->lft->ntyp == ATOMIC + || No->lft->ntyp == D_STEP)) + { + int tok = No->lft->ntyp; + + No->lft->sl->nxt = Sl; + Re->sl = No->lft->sl; + + open_seq(0); add_seq(Re); + Re = nn(ZN, tok, ZN, ZN); + Re->sl = seqlist(close_seq(7), 0); + Re->ln = No->ln; + Re->fn = No->fn; + + Re = nn(No, ':', Re, ZN); /* lift label */ + Re->ln = No->ln; + Re->fn = No->fn; + return Re; + } else + { open_seq(0); add_seq(No); + Sl = seqlist(close_seq(2), Sl); + } + + Re->sl = Sl; + return Re; +} + +SeqList * +seqlist(Sequence *s, SeqList *r) +{ SeqList *t = (SeqList *) emalloc(sizeof(SeqList)); + + t->this = s; + t->nxt = r; + return t; +} + +static Element * +new_el(Lextok *n) +{ Element *m; + + if (n) + { if (n->ntyp == IF || n->ntyp == DO) + return if_seq(n); + if (n->ntyp == UNLESS) + return unless_seq(n); + } + m = (Element *) emalloc(sizeof(Element)); + m->n = n; + m->seqno = Elcnt++; + m->Seqno = Unique++; + m->Nxt = Al_El; Al_El = m; + return m; +} + +static int +has_chanref(Lextok *n) +{ + if (!n) return 0; + + switch (n->ntyp) { + case 's': case 'r': +#if 0 + case 'R': case LEN: +#endif + case FULL: case NFULL: + case EMPTY: case NEMPTY: + return 1; + default: + break; + } + if (has_chanref(n->lft)) + return 1; + + return has_chanref(n->rgt); +} + +void +loose_ends(void) /* properly tie-up ends of sub-sequences */ +{ Element *e, *f; + + for (e = Al_El; e; e = e->Nxt) + { if (!e->n + || !e->nxt) + continue; + switch (e->n->ntyp) { + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + f = e->nxt; + while (f && f->n->ntyp == '.') + f = f->nxt; + if (0) printf("link %d, {%d .. %d} -> %d (ntyp=%d) was %d\n", + e->seqno, + e->n->sl->this->frst->seqno, + e->n->sl->this->last->seqno, + f?f->seqno:-1, f?f->n->ntyp:-1, + e->n->sl->this->last->nxt?e->n->sl->this->last->nxt->seqno:-1); + if (!e->n->sl->this->last->nxt) + e->n->sl->this->last->nxt = f; + else + { if (e->n->sl->this->last->nxt->n->ntyp != GOTO) + { if (!f || e->n->sl->this->last->nxt->seqno != f->seqno) + non_fatal("unexpected: loose ends", (char *)0); + } else + e->n->sl->this->last = e->n->sl->this->last->nxt; + /* + * fix_dest can push a goto into the nxt position + * in that case the goto wins and f is not needed + * but the last fields needs adjusting + */ + } + break; + } } +} + +static Element * +if_seq(Lextok *n) +{ int tok = n->ntyp; + SeqList *s = n->sl; + Element *e = new_el(ZN); + Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */ + SeqList *z, *prev_z = (SeqList *) 0; + SeqList *move_else = (SeqList *) 0; /* to end of optionlist */ + int ref_chans = 0; + + for (z = s; z; z = z->nxt) + { if (!z->this->frst) + continue; + if (z->this->frst->n->ntyp == ELSE) + { if (move_else) + fatal("duplicate `else'", (char *) 0); + if (z->nxt) /* is not already at the end */ + { move_else = z; + if (prev_z) + prev_z->nxt = z->nxt; + else + s = n->sl = z->nxt; + continue; + } + } else + ref_chans |= has_chanref(z->this->frst->n); + prev_z = z; + } + if (move_else) + { move_else->nxt = (SeqList *) 0; + /* if there is no prev, then else was at the end */ + if (!prev_z) fatal("cannot happen - if_seq", (char *) 0); + prev_z->nxt = move_else; + prev_z = move_else; + } + if (prev_z + && ref_chans + && prev_z->this->frst->n->ntyp == ELSE) + { prev_z->this->frst->n->val = 1; + has_badelse++; + non_fatal("dubious use of 'else' combined with i/o,", + (char *)0); + nr_errs--; + } + + e->n = nn(n, tok, ZN, ZN); + e->n->sl = s; /* preserve as info only */ + e->sub = s; + for (z = s; z; prev_z = z, z = z->nxt) + add_el(t, z->this); /* append target */ + if (tok == DO) + { add_el(t, cur_s->this); /* target upfront */ + t = new_el(nn(n, BREAK, ZN, ZN)); /* break target */ + set_lab(break_dest(), t); /* new exit */ + breakstack = breakstack->nxt; /* pop stack */ + } + add_el(e, cur_s->this); + add_el(t, cur_s->this); + return e; /* destination node for label */ +} + +static void +escape_el(Element *f, Sequence *e) +{ SeqList *z; + + for (z = f->esc; z; z = z->nxt) + if (z->this == e) + return; /* already there */ + + /* cover the lower-level escapes of this state */ + for (z = f->esc; z; z = z->nxt) + attach_escape(z->this, e); + + /* now attach escape to the state itself */ + + f->esc = seqlist(e, f->esc); /* in lifo order... */ +#ifdef DEBUG + printf("attach %d (", e->frst->Seqno); + comment(stdout, e->frst->n, 0); + printf(") to %d (", f->Seqno); + comment(stdout, f->n, 0); + printf(")\n"); +#endif + switch (f->n->ntyp) { + case UNLESS: + attach_escape(f->sub->this, e); + break; + case IF: + case DO: + for (z = f->sub; z; z = z->nxt) + attach_escape(z->this, e); + break; + case D_STEP: + /* attach only to the guard stmnt */ + escape_el(f->n->sl->this->frst, e); + break; + case ATOMIC: + case NON_ATOMIC: + /* attach to all stmnts */ + attach_escape(f->n->sl->this, e); + break; + } +} + +static void +attach_escape(Sequence *n, Sequence *e) +{ Element *f; + + for (f = n->frst; f; f = f->nxt) + { escape_el(f, e); + if (f == n->extent) + break; + } +} + +static Element * +unless_seq(Lextok *n) +{ SeqList *s = n->sl; + Element *e = new_el(ZN); + Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */ + SeqList *z; + + e->n = nn(n, UNLESS, ZN, ZN); + e->n->sl = s; /* info only */ + e->sub = s; + + /* need 2 sequences: normal execution and escape */ + if (!s || !s->nxt || s->nxt->nxt) + fatal("unexpected unless structure", (char *)0); + + /* append the target state to both */ + for (z = s; z; z = z->nxt) + add_el(t, z->this); + + /* attach escapes to all states in normal sequence */ + attach_escape(s->this, s->nxt->this); + + add_el(e, cur_s->this); + add_el(t, cur_s->this); +#ifdef DEBUG + printf("unless element (%d,%d):\n", e->Seqno, t->Seqno); + for (z = s; z; z = z->nxt) + { Element *x; printf("\t%d,%d,%d :: ", + z->this->frst->Seqno, + z->this->extent->Seqno, + z->this->last->Seqno); + for (x = z->this->frst; x; x = x->nxt) + printf("(%d)", x->Seqno); + printf("\n"); + } +#endif + return e; +} + +Element * +mk_skip(void) +{ Lextok *t = nn(ZN, CONST, ZN, ZN); + t->val = 1; + return new_el(nn(ZN, 'c', t, ZN)); +} + +static void +add_el(Element *e, Sequence *s) +{ + if (e->n->ntyp == GOTO) + { Symbol *z = has_lab(e, (1|2|4)); + if (z) + { Element *y; /* insert a skip */ + y = mk_skip(); + mov_lab(z, e, y); /* inherit label */ + add_el(y, s); + } } +#ifdef DEBUG + printf("add_el %d after %d -- ", + e->Seqno, (s->last)?s->last->Seqno:-1); + comment(stdout, e->n, 0); + printf("\n"); +#endif + if (!s->frst) + s->frst = e; + else + s->last->nxt = e; + s->last = e; +} + +static Element * +colons(Lextok *n) +{ + if (!n) + return ZE; + if (n->ntyp == ':') + { Element *e = colons(n->lft); + set_lab(n->sym, e); + return e; + } + innermost = n; + return new_el(n); +} + +void +add_seq(Lextok *n) +{ Element *e; + + if (!n) return; + innermost = n; + e = colons(n); + if (innermost->ntyp != IF + && innermost->ntyp != DO + && innermost->ntyp != UNLESS) + add_el(e, cur_s->this); +} + +void +set_lab(Symbol *s, Element *e) +{ Label *l; extern Symbol *context; + + if (!s) return; + for (l = labtab; l; l = l->nxt) + if (l->s == s && l->c == context) + { non_fatal("label %s redeclared", s->name); + break; + } + l = (Label *) emalloc(sizeof(Label)); + l->s = s; + l->c = context; + l->e = e; + l->nxt = labtab; + labtab = l; +} + +Element * +get_lab(Lextok *n, int md) +{ Label *l; + Symbol *s = n->sym; + + for (l = labtab; l; l = l->nxt) + if (s == l->s) + return (l->e); + + lineno = n->ln; + Fname = n->fn; + if (md) fatal("undefined label %s", s->name); + return ZE; +} + +Symbol * +has_lab(Element *e, int special) +{ Label *l; + + for (l = labtab; l; l = l->nxt) + { if (e != l->e) + continue; + if (special == 0 + || ((special&1) && !strncmp(l->s->name, "accept", 6)) + || ((special&2) && !strncmp(l->s->name, "end", 3)) + || ((special&4) && !strncmp(l->s->name, "progress", 8))) + return (l->s); + } + return ZS; +} + +static void +mov_lab(Symbol *z, Element *e, Element *y) +{ Label *l; + + for (l = labtab; l; l = l->nxt) + if (e == l->e) + { l->e = y; + return; + } + if (e->n) + { lineno = e->n->ln; + Fname = e->n->fn; + } + fatal("cannot happen - mov_lab %s", z->name); +} + +void +fix_dest(Symbol *c, Symbol *a) /* c:label name, a:proctype name */ +{ Label *l; extern Symbol *context; + +#if 0 + printf("ref to label '%s' in proctype '%s', search:\n", + c->name, a->name); + for (l = labtab; l; l = l->nxt) + printf(" %s in %s\n", l->s->name, l->c->name); +#endif + + for (l = labtab; l; l = l->nxt) + { if (strcmp(c->name, l->s->name) == 0 + && strcmp(a->name, l->c->name) == 0) /* ? */ + break; + } + if (!l) + { printf("spin: label '%s' (proctype %s)\n", c->name, a->name); + non_fatal("unknown label '%s'", c->name); + if (context == a) + printf("spin: cannot remote ref a label inside the same proctype\n"); + return; + } + if (!l->e || !l->e->n) + fatal("fix_dest error (%s)", c->name); + if (l->e->n->ntyp == GOTO) + { Element *y = (Element *) emalloc(sizeof(Element)); + int keep_ln = l->e->n->ln; + Symbol *keep_fn = l->e->n->fn; + + /* insert skip - or target is optimized away */ + y->n = l->e->n; /* copy of the goto */ + y->seqno = find_maxel(a); /* unique seqno within proc */ + y->nxt = l->e->nxt; + y->Seqno = Unique++; y->Nxt = Al_El; Al_El = y; + + /* turn the original element+seqno into a skip */ + l->e->n = nn(ZN, 'c', nn(ZN, CONST, ZN, ZN), ZN); + l->e->n->ln = l->e->n->lft->ln = keep_ln; + l->e->n->fn = l->e->n->lft->fn = keep_fn; + l->e->n->lft->val = 1; + l->e->nxt = y; /* append the goto */ + } + l->e->status |= CHECK2; /* treat as if global */ + if (l->e->status & (ATOM | L_ATOM | D_ATOM)) + { non_fatal("cannot reference label inside atomic or d_step (%s)", + c->name); + } +} + +int +find_lab(Symbol *s, Symbol *c, int markit) +{ Label *l; + + for (l = labtab; l; l = l->nxt) + { if (strcmp(s->name, l->s->name) == 0 + && strcmp(c->name, l->c->name) == 0) + { l->visible |= markit; + return (l->e->seqno); + } } + return 0; +} + +void +pushbreak(void) +{ Lbreak *r = (Lbreak *) emalloc(sizeof(Lbreak)); + Symbol *l; + char buf[64]; + + sprintf(buf, ":b%d", break_id++); + l = lookup(buf); + r->l = l; + r->nxt = breakstack; + breakstack = r; +} + +Symbol * +break_dest(void) +{ + if (!breakstack) + fatal("misplaced break statement", (char *)0); + return breakstack->l; +} + +void +make_atomic(Sequence *s, int added) +{ Element *f; + + walk_atomic(s->frst, s->last, added); + + f = s->last; + switch (f->n->ntyp) { /* is last step basic stmnt or sequence ? */ + case NON_ATOMIC: + case ATOMIC: + /* redo and search for the last step of that sequence */ + make_atomic(f->n->sl->this, added); + break; + + case UNLESS: + /* escapes are folded into main sequence */ + make_atomic(f->sub->this, added); + break; + + default: + f->status &= ~ATOM; + f->status |= L_ATOM; + break; + } +} + +static void +walk_atomic(Element *a, Element *b, int added) +{ Element *f; Symbol *ofn; int oln; + SeqList *h; + + ofn = Fname; + oln = lineno; + for (f = a; ; f = f->nxt) + { f->status |= (ATOM|added); + switch (f->n->ntyp) { + case ATOMIC: + if (verbose&32) + printf("spin: warning, line %3d %s, atomic inside %s (ignored)\n", + f->n->ln, f->n->fn->name, (added)?"d_step":"atomic"); + goto mknonat; + case D_STEP: + if (!(verbose&32)) + { if (added) goto mknonat; + break; + } + printf("spin: warning, line %3d %s, d_step inside ", + f->n->ln, f->n->fn->name); + if (added) + { printf("d_step (ignored)\n"); + goto mknonat; + } + printf("atomic\n"); + break; + case NON_ATOMIC: +mknonat: f->n->ntyp = NON_ATOMIC; /* can jump here */ + h = f->n->sl; + walk_atomic(h->this->frst, h->this->last, added); + break; + case UNLESS: + if (added) + { printf("spin: error, line %3d %s, unless in d_step (ignored)\n", + f->n->ln, f->n->fn->name); + } + } + for (h = f->sub; h; h = h->nxt) + walk_atomic(h->this->frst, h->this->last, added); + if (f == b) + break; + } + Fname = ofn; + lineno = oln; +} + +void +dumplabels(void) +{ Label *l; + + for (l = labtab; l; l = l->nxt) + if (l->c != 0 && l->s->name[0] != ':') + printf("label %s %d <%s>\n", + l->s->name, l->e->seqno, l->c->name); +} diff --git a/sys/src/cmd/spin/guided.c b/sys/src/cmd/spin/guided.c new file mode 100755 index 000000000..0bc4dcbc7 --- /dev/null +++ b/sys/src/cmd/spin/guided.c @@ -0,0 +1,313 @@ +/***** spin: guided.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include <sys/types.h> +#include <sys/stat.h> +#include "y.tab.h" + +extern RunList *run, *X; +extern Element *Al_El; +extern Symbol *Fname, *oFname; +extern int verbose, lineno, xspin, jumpsteps, depth, merger, cutoff; +extern int nproc, nstop, Tval, ntrail, columns; +extern short Have_claim, Skip_claim; +extern void ana_src(int, int); + +int TstOnly = 0, pno; + +static int lastclaim = -1; +static FILE *fd; +static void lost_trail(void); + +static void +whichproc(int p) +{ RunList *oX; + + for (oX = run; oX; oX = oX->nxt) + if (oX->pid == p) + { printf("(%s) ", oX->n->name); + break; + } +} + +static int +newer(char *f1, char *f2) +{ +#if defined(WIN32) || defined(WIN64) + struct _stat x, y; +#else + struct stat x, y; +#endif + + if (stat(f1, (struct stat *)&x) < 0) return 0; + if (stat(f2, (struct stat *)&y) < 0) return 1; + if (x.st_mtime < y.st_mtime) return 0; + + return 1; +} + +void +hookup(void) +{ Element *e; + + for (e = Al_El; e; e = e->Nxt) + if (e->n + && (e->n->ntyp == ATOMIC + || e->n->ntyp == NON_ATOMIC + || e->n->ntyp == D_STEP)) + (void) huntstart(e); +} + +int +not_claim(void) +{ + return (!Have_claim || !X || X->pid != 0); +} + +void +match_trail(void) +{ int i, a, nst; + Element *dothis; + char snap[512], *q; + + /* + * if source model name is leader.pml + * look for the trail file under these names: + * leader.pml.trail + * leader.pml.tra + * leader.trail + * leader.tra + */ + + if (ntrail) + sprintf(snap, "%s%d.trail", oFname->name, ntrail); + else + sprintf(snap, "%s.trail", oFname->name); + + if ((fd = fopen(snap, "r")) == NULL) + { snap[strlen(snap)-2] = '\0'; /* .tra */ + if ((fd = fopen(snap, "r")) == NULL) + { if ((q = strchr(oFname->name, '.')) != NULL) + { *q = '\0'; + if (ntrail) + sprintf(snap, "%s%d.trail", + oFname->name, ntrail); + else + sprintf(snap, "%s.trail", + oFname->name); + *q = '.'; + + if ((fd = fopen(snap, "r")) != NULL) + goto okay; + + snap[strlen(snap)-2] = '\0'; /* last try */ + if ((fd = fopen(snap, "r")) != NULL) + goto okay; + } + printf("spin: cannot find trail file\n"); + alldone(1); + } } +okay: + if (xspin == 0 && newer(oFname->name, snap)) + printf("spin: warning, \"%s\" is newer than %s\n", + oFname->name, snap); + + Tval = 1; + + /* + * sets Tval because timeouts may be part of trail + * this used to also set m_loss to 1, but that is + * better handled with the runtime -m flag + */ + + hookup(); + + while (fscanf(fd, "%d:%d:%d\n", &depth, &pno, &nst) == 3) + { if (depth == -2) { start_claim(pno); continue; } + if (depth == -4) { merger = 1; ana_src(0, 1); continue; } + if (depth == -1) + { if (verbose) + { if (columns == 2) + dotag(stdout, " CYCLE>\n"); + else + dotag(stdout, "<<<<<START OF CYCLE>>>>>\n"); + } + continue; + } + + if (cutoff > 0 && depth >= cutoff) + { printf("-------------\n"); + printf("depth-limit (-u%d steps) reached\n", cutoff); + break; + } + + if (Skip_claim && pno == 0) continue; + + for (dothis = Al_El; dothis; dothis = dothis->Nxt) + { if (dothis->Seqno == nst) + break; + } + if (!dothis) + { printf("%3d: proc %d, no matching stmnt %d\n", + depth, pno - Have_claim, nst); + lost_trail(); + } + + i = nproc - nstop + Skip_claim; + + if (dothis->n->ntyp == '@') + { if (pno == i-1) + { run = run->nxt; + nstop++; + if (verbose&4) + { if (columns == 2) + { dotag(stdout, "<end>\n"); + continue; + } + if (Have_claim && pno == 0) + printf("%3d: claim terminates\n", + depth); + else + printf("%3d: proc %d terminates\n", + depth, pno - Have_claim); + } + continue; + } + if (pno <= 1) continue; /* init dies before never */ + printf("%3d: stop error, ", depth); + printf("proc %d (i=%d) trans %d, %c\n", + pno - Have_claim, i, nst, dothis->n->ntyp); + lost_trail(); + } + 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(); + } + X->pc = dothis; + lineno = dothis->n->ln; + Fname = dothis->n->fn; + + if (dothis->n->ntyp == D_STEP) + { Element *g, *og = dothis; + do { + g = eval_sub(og); + if (g && depth >= jumpsteps + && ((verbose&32) || ((verbose&4) && not_claim()))) + { if (columns != 2) + { p_talk(og, 1); + + if (og->n->ntyp == D_STEP) + og = og->n->sl->this->frst; + + printf("\t["); + comment(stdout, og->n, 0); + printf("]\n"); + } + if (verbose&1) dumpglobals(); + if (verbose&2) dumplocal(X); + if (xspin) printf("\n"); + } + og = g; + } while (g && g != dothis->nxt); + if (X != NULL) + { X->pc = g?huntele(g, 0, -1):g; + } + } else + { +keepgoing: if (dothis->merge_start) + a = dothis->merge_start; + else + a = dothis->merge; + + if (X != NULL) + { X->pc = eval_sub(dothis); + if (X->pc) X->pc = huntele(X->pc, 0, a); + } + + if (depth >= jumpsteps + && ((verbose&32) || ((verbose&4) && not_claim()))) /* -v or -p */ + { if (columns != 2) + { p_talk(dothis, 1); + + if (dothis->n->ntyp == D_STEP) + dothis = dothis->n->sl->this->frst; + + printf("\t["); + comment(stdout, dothis->n, 0); + printf("]"); + if (a && (verbose&32)) + printf("\t<merge %d now @%d>", + dothis->merge, + (X && X->pc)?X->pc->seqno:-1); + printf("\n"); + } + if (verbose&1) dumpglobals(); + if (verbose&2) dumplocal(X); + if (xspin) printf("\n"); + + if (X && !X->pc) + { X->pc = dothis; + printf("\ttransition failed\n"); + a = 0; /* avoid inf loop */ + } + } + if (a && X && X->pc && X->pc->seqno != a) + { dothis = X->pc; + goto keepgoing; + } } + + if (Have_claim && X && X->pid == 0 + && dothis && dothis->n + && lastclaim != dothis->n->ln) + { lastclaim = dothis->n->ln; + if (columns == 2) + { char t[128]; + sprintf(t, "#%d", lastclaim); + pstext(0, t); + } else + { + printf("Never claim moves to line %d\t[", lastclaim); + comment(stdout, dothis->n, 0); + printf("]\n"); + } } } + printf("spin: trail ends after %d steps\n", depth); + wrapup(0); +} + +static void +lost_trail(void) +{ int d, p, n, l; + + while (fscanf(fd, "%d:%d:%d:%d\n", &d, &p, &n, &l) == 4) + { printf("step %d: proc %d ", d, p); whichproc(p); + printf("(state %d) - d %d\n", n, l); + } + wrapup(1); /* no return */ +} + +int +pc_value(Lextok *n) +{ int i = nproc - nstop; + int pid = eval(n); + RunList *Y; + + for (Y = run; Y; Y = Y->nxt) + { if (--i == pid) + return Y->pc->seqno; + } + return 0; +} diff --git a/sys/src/cmd/spin/main.c b/sys/src/cmd/spin/main.c new file mode 100755 index 000000000..d70164424 --- /dev/null +++ b/sys/src/cmd/spin/main.c @@ -0,0 +1,778 @@ +/***** spin: main.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include <stdlib.h> +#include "spin.h" +#include "version.h" +#include <signal.h> +/* #include <malloc.h> */ +#include <time.h> +#ifdef PC +#include <io.h> +extern int unlink(const char *); +#else +#include <unistd.h> +#endif +#include "y.tab.h" + +extern int DstepStart, lineno, tl_terse; +extern FILE *yyin, *yyout, *tl_out; +extern Symbol *context; +extern char *claimproc; +extern void repro_src(void); +extern void qhide(int); + +Symbol *Fname, *oFname; + +int Etimeouts; /* nr timeouts in program */ +int Ntimeouts; /* nr timeouts in never claim */ +int analyze, columns, dumptab, has_remote, has_remvar; +int interactive, jumpsteps, m_loss, nr_errs, cutoff; +int s_trail, ntrail, verbose, xspin, notabs, rvopt; +int no_print, no_wrapup, Caccess, limited_vis, like_java; +int separate; /* separate compilation */ +int export_ast; /* pangen5.c */ +int inlineonly; /* show inlined code */ +int seedy; /* be verbose about chosen seed */ + +int dataflow = 1, merger = 1, deadvar = 1, ccache = 1; + +static int preprocessonly, SeedUsed; + +#if 0 +meaning of flags on verbose: + 1 -g global variable values + 2 -l local variable values + 4 -p all process actions + 8 -r receives + 16 -s sends + 32 -v verbose + 64 -w very verbose +#endif + +static char Operator[] = "operator: "; +static char Keyword[] = "keyword: "; +static char Function[] = "function-name: "; +static char **add_ltl = (char **)0; +static char **ltl_file = (char **)0; +static char **nvr_file = (char **)0; +static char *PreArg[64]; +static int PreCnt = 0; +static char out1[64]; +static void explain(int); + +#ifndef CPP + /* OS2: "spin -Picc -E/Pd+ -E/Q+" */ + /* Visual C++: "spin -PCL -E/E */ +#ifdef PC +#define CPP "gcc -E -x c" /* most systems have gcc anyway */ + /* else use "cpp" */ +#else +#ifdef SOLARIS +#define CPP "/usr/ccs/lib/cpp" +#else +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#define CPP "cpp" +#else +#define CPP "/bin/cpp" /* classic Unix systems */ +#endif +#endif +#endif + +#endif +static char *PreProc = CPP; +extern int depth; /* at least some steps were made */ + +void +alldone(int estatus) +{ + if (preprocessonly == 0 + && strlen(out1) > 0) + (void) unlink((const char *)out1); + + if (seedy && !analyze && !export_ast + && !s_trail && !preprocessonly && depth > 0) + printf("seed used: %d\n", SeedUsed); + + if (xspin && (analyze || s_trail)) + { if (estatus) + printf("spin: %d error(s) - aborting\n", + estatus); + else + printf("Exit-Status 0\n"); + } + exit(estatus); +} + +void +preprocess(char *a, char *b, int a_tmp) +{ char precmd[512], cmd[1024]; int i; +#ifdef PC + extern int try_zpp(char *, char *); + if (PreCnt == 0 && try_zpp(a, b)) goto out; +#endif + strcpy(precmd, PreProc); + for (i = 1; i <= PreCnt; i++) + { strcat(precmd, " "); + strcat(precmd, PreArg[i]); + } + sprintf(cmd, "%s %s > %s", precmd, a, b); + if (system((const char *)cmd)) + { (void) unlink((const char *) b); + if (a_tmp) (void) unlink((const char *) a); + fprintf(stdout, "spin: preprocessing failed\n"); /* 4.1.2 was stderr */ + alldone(1); /* no return, error exit */ + } +#ifdef PC +out: +#endif + if (a_tmp) (void) unlink((const char *) a); +} + +FILE * +cpyfile(char *src, char *tgt) +{ FILE *inp, *out; + char buf[1024]; + + inp = fopen(src, "r"); + out = fopen(tgt, "w"); + if (!inp || !out) + { printf("spin: cannot cp %s to %s\n", src, tgt); + alldone(1); + } + while (fgets(buf, 1024, inp)) + fprintf(out, "%s", buf); + fclose(inp); + return out; +} + +void +usage(void) +{ + printf("use: spin [-option] ... [-option] file\n"); + printf("\tNote: file must always be the last argument\n"); + printf("\t-A apply slicing algorithm\n"); + printf("\t-a generate a verifier in pan.c\n"); + printf("\t-B no final state details in simulations\n"); + printf("\t-b don't execute printfs in simulation\n"); + printf("\t-C print channel access info (combine with -g etc.)\n"); + printf("\t-c columnated -s -r simulation output\n"); + printf("\t-d produce symbol-table information\n"); + printf("\t-Dyyy pass -Dyyy to the preprocessor\n"); + printf("\t-Eyyy pass yyy to the preprocessor\n"); + printf("\t-f \"..formula..\" translate LTL "); + printf("into never claim\n"); + printf("\t-F file like -f, but with the LTL "); + printf("formula stored in a 1-line file\n"); + printf("\t-g print all global variables\n"); + printf("\t-h at end of run, print value of seed for random nr generator used\n"); + printf("\t-i interactive (random simulation)\n"); + printf("\t-I show result of inlining and preprocessing\n"); + printf("\t-J reverse eval order of nested unlesses\n"); + printf("\t-jN skip the first N steps "); + printf("in simulation trail\n"); + printf("\t-l print all local variables\n"); + printf("\t-M print msc-flow in Postscript\n"); + printf("\t-m lose msgs sent to full queues\n"); + printf("\t-N file use never claim stored in file\n"); + printf("\t-nN seed for random nr generator\n"); + printf("\t-o1 turn off dataflow-optimizations in verifier\n"); + printf("\t-o2 don't hide write-only variables in verifier\n"); + printf("\t-o3 turn off statement merging in verifier\n"); + printf("\t-Pxxx use xxx for preprocessing\n"); + printf("\t-p print all statements\n"); + printf("\t-qN suppress io for queue N in printouts\n"); + printf("\t-r print receive events\n"); + printf("\t-S1 and -S2 separate pan source for claim and model\n"); + printf("\t-s print send events\n"); + printf("\t-T do not indent printf output\n"); + printf("\t-t[N] follow [Nth] simulation trail\n"); + printf("\t-Uyyy pass -Uyyy to the preprocessor\n"); + printf("\t-uN stop a simulation run after N steps\n"); + printf("\t-v verbose, more warnings\n"); + printf("\t-w very verbose (when combined with -l or -g)\n"); + printf("\t-[XYZ] reserved for use by xspin interface\n"); + printf("\t-V print version number and exit\n"); + alldone(1); +} + +void +optimizations(char nr) +{ + switch (nr) { + case '1': + dataflow = 1 - dataflow; /* dataflow */ + if (verbose&32) + printf("spin: dataflow optimizations turned %s\n", + dataflow?"on":"off"); + break; + case '2': + /* dead variable elimination */ + deadvar = 1 - deadvar; + if (verbose&32) + printf("spin: dead variable elimination turned %s\n", + deadvar?"on":"off"); + break; + case '3': + /* statement merging */ + merger = 1 - merger; + if (verbose&32) + printf("spin: statement merging turned %s\n", + merger?"on":"off"); + break; + + case '4': + /* rv optimization */ + rvopt = 1 - rvopt; + if (verbose&32) + printf("spin: rendezvous optimization turned %s\n", + rvopt?"on":"off"); + break; + case '5': + /* case caching */ + ccache = 1 - ccache; + if (verbose&32) + printf("spin: case caching turned %s\n", + ccache?"on":"off"); + break; + default: + printf("spin: bad or missing parameter on -o\n"); + usage(); + break; + } +} + +#if 0 +static int +Rename(const char *old, char *new) +{ FILE *fo, *fn; + char buf[1024]; + + if ((fo = fopen(old, "r")) == NULL) + { printf("spin: cannot open %s\n", old); + return 1; + } + if ((fn = fopen(new, "w")) == NULL) + { printf("spin: cannot create %s\n", new); + fclose(fo); + return 2; + } + while (fgets(buf, 1024, fo)) + fputs(buf, fn); + + fclose(fo); + fclose(fn); + + return 0; /* success */ +} +#endif + +int +main(int argc, char *argv[]) +{ Symbol *s; + int T = (int) time((time_t *)0); + int usedopts = 0; + extern void ana_src(int, int); + + yyin = stdin; + yyout = stdout; + tl_out = stdout; + + /* unused flags: e, w, x, y, z, A, G, I, L, O, Q, R, S, T, W */ + while (argc > 1 && argv[1][0] == '-') + { switch (argv[1][1]) { + + /* generate code for separate compilation: S1 or S2 */ + case 'S': separate = atoi(&argv[1][2]); + /* fall through */ + case 'a': analyze = 1; break; + + case 'A': export_ast = 1; break; + case 'B': no_wrapup = 1; break; + case 'b': no_print = 1; break; + case 'C': Caccess = 1; break; + case 'c': columns = 1; break; + case 'D': PreArg[++PreCnt] = (char *) &argv[1][0]; + break; /* define */ + case 'd': dumptab = 1; break; + case 'E': PreArg[++PreCnt] = (char *) &argv[1][2]; + break; + case 'F': ltl_file = (char **) (argv+2); + argc--; argv++; break; + case 'f': add_ltl = (char **) argv; + argc--; argv++; break; + case 'g': verbose += 1; break; + case 'h': seedy = 1; break; + case 'i': interactive = 1; break; + case 'I': inlineonly = 1; break; + case 'J': like_java = 1; break; + case 'j': jumpsteps = atoi(&argv[1][2]); break; + case 'l': verbose += 2; break; + case 'M': columns = 2; break; + case 'm': m_loss = 1; break; + case 'N': nvr_file = (char **) (argv+2); + argc--; argv++; break; + case 'n': T = atoi(&argv[1][2]); tl_terse = 1; break; + case 'o': optimizations(argv[1][2]); + usedopts = 1; break; + case 'P': PreProc = (char *) &argv[1][2]; break; + case 'p': verbose += 4; break; + case 'q': if (isdigit(argv[1][2])) + qhide(atoi(&argv[1][2])); + break; + case 'r': verbose += 8; break; + case 's': verbose += 16; break; + case 'T': notabs = 1; break; + case 't': s_trail = 1; + if (isdigit(argv[1][2])) + ntrail = atoi(&argv[1][2]); + break; + case 'U': PreArg[++PreCnt] = (char *) &argv[1][0]; + break; /* undefine */ + case 'u': cutoff = atoi(&argv[1][2]); break; /* new 3.4.14 */ + case 'v': verbose += 32; break; + case 'V': printf("%s\n", Version); + alldone(0); + break; + case 'w': verbose += 64; break; + case 'X': xspin = notabs = 1; +#ifndef PC + signal(SIGPIPE, alldone); /* not posix... */ +#endif + break; + case 'Y': limited_vis = 1; break; /* used by xspin */ + case 'Z': preprocessonly = 1; break; /* used by xspin */ + + default : usage(); break; + } + argc--; argv++; + } + if (usedopts && !analyze) + printf("spin: warning -o[123] option ignored in simulations\n"); + + if (ltl_file) + { char formula[4096]; + add_ltl = ltl_file-2; add_ltl[1][1] = 'f'; + if (!(tl_out = fopen(*ltl_file, "r"))) + { printf("spin: cannot open %s\n", *ltl_file); + alldone(1); + } + fgets(formula, 4096, tl_out); + fclose(tl_out); + tl_out = stdout; + *ltl_file = (char *) formula; + } + if (argc > 1) + { char cmd[128], out2[64]; + + /* must remain in current dir */ + strcpy(out1, "pan.pre"); + + if (add_ltl || nvr_file) + strcpy(out2, "pan.___"); + + if (add_ltl) + { tl_out = cpyfile(argv[1], out2); + nr_errs = tl_main(2, add_ltl); /* in tl_main.c */ + fclose(tl_out); + preprocess(out2, out1, 1); + } else if (nvr_file) + { FILE *fd; char buf[1024]; + + if ((fd = fopen(*nvr_file, "r")) == NULL) + { printf("spin: cannot open %s\n", + *nvr_file); + alldone(1); + } + tl_out = cpyfile(argv[1], out2); + while (fgets(buf, 1024, fd)) + fprintf(tl_out, "%s", buf); + fclose(tl_out); + fclose(fd); + preprocess(out2, out1, 1); + } else + preprocess(argv[1], out1, 0); + + if (preprocessonly) + alldone(0); + + if (!(yyin = fopen(out1, "r"))) + { printf("spin: cannot open %s\n", out1); + alldone(1); + } + + if (strncmp(argv[1], "progress", 8) == 0 + || strncmp(argv[1], "accept", 6) == 0) + sprintf(cmd, "_%s", argv[1]); + else + strcpy(cmd, argv[1]); + oFname = Fname = lookup(cmd); + if (oFname->name[0] == '\"') + { int i = (int) strlen(oFname->name); + oFname->name[i-1] = '\0'; + oFname = lookup(&oFname->name[1]); + } + } else + { oFname = Fname = lookup("<stdin>"); + if (add_ltl) + { if (argc > 0) + exit(tl_main(2, add_ltl)); + printf("spin: missing argument to -f\n"); + alldone(1); + } + printf("%s\n", Version); + printf("reading input from stdin:\n"); + fflush(stdout); + } + if (columns == 2) + { extern void putprelude(void); + if (xspin || verbose&(1|4|8|16|32)) + { printf("spin: -c precludes all flags except -t\n"); + alldone(1); + } + putprelude(); + } + if (columns && !(verbose&8) && !(verbose&16)) + verbose += (8+16); + if (columns == 2 && limited_vis) + verbose += (1+4); + Srand(T); /* defined in run.c */ + SeedUsed = T; + s = lookup("_"); s->type = PREDEF; /* write-only global var */ + s = lookup("_p"); s->type = PREDEF; + s = lookup("_pid"); s->type = PREDEF; + s = lookup("_last"); s->type = PREDEF; + s = lookup("_nr_pr"); s->type = PREDEF; /* new 3.3.10 */ + + yyparse(); + fclose(yyin); + loose_ends(); + + if (inlineonly) + { repro_src(); + return 0; + } + + chanaccess(); + if (!Caccess) + { if (!s_trail && (dataflow || merger)) + ana_src(dataflow, merger); + sched(); + alldone(nr_errs); + } + return 0; +} + +int +yywrap(void) /* dummy routine */ +{ + return 1; +} + +void +non_fatal(char *s1, char *s2) +{ extern char yytext[]; + + printf("spin: line %3d %s, Error: ", + lineno, Fname?Fname->name:"nofilename"); + if (s2) + printf(s1, s2); + else + printf(s1); + if (yytext && strlen(yytext)>1) + printf(" near '%s'", yytext); + printf("\n"); + nr_errs++; +} + +void +fatal(char *s1, char *s2) +{ + non_fatal(s1, s2); + alldone(1); +} + +char * +emalloc(int n) +{ char *tmp; + + if (n == 0) + return NULL; /* robert shelton 10/20/06 */ + + if (!(tmp = (char *) malloc(n))) + fatal("not enough memory", (char *)0); + memset(tmp, 0, n); + return tmp; +} + +void +trapwonly(Lextok *n, char *unused) +{ extern int realread; + short i = (n->sym)?n->sym->type:0; + + if (i != MTYPE + && i != BIT + && i != BYTE + && i != SHORT + && i != INT + && i != UNSIGNED) + return; + + if (realread) + n->sym->hidden |= 128; /* var is read at least once */ +} + +void +setaccess(Symbol *sp, Symbol *what, int cnt, int t) +{ Access *a; + + for (a = sp->access; a; a = a->lnk) + if (a->who == context + && a->what == what + && a->cnt == cnt + && a->typ == t) + return; + + a = (Access *) emalloc(sizeof(Access)); + a->who = context; + a->what = what; + a->cnt = cnt; + a->typ = t; + a->lnk = sp->access; + sp->access = a; +} + +Lextok * +nn(Lextok *s, int t, Lextok *ll, Lextok *rl) +{ Lextok *n = (Lextok *) emalloc(sizeof(Lextok)); + static int warn_nn = 0; + + n->ntyp = (short) t; + if (s && s->fn) + { n->ln = s->ln; + n->fn = s->fn; + } else if (rl && rl->fn) + { n->ln = rl->ln; + n->fn = rl->fn; + } else if (ll && ll->fn) + { n->ln = ll->ln; + n->fn = ll->fn; + } else + { n->ln = lineno; + n->fn = Fname; + } + if (s) n->sym = s->sym; + n->lft = ll; + n->rgt = rl; + n->indstep = DstepStart; + + if (t == TIMEOUT) Etimeouts++; + + if (!context) return n; + + if (t == 'r' || t == 's') + setaccess(n->sym, ZS, 0, t); + if (t == 'R') + setaccess(n->sym, ZS, 0, 'P'); + + if (context->name == claimproc) + { int forbidden = separate; + switch (t) { + case ASGN: + printf("spin: Warning, never claim has side-effect\n"); + break; + case 'r': case 's': + non_fatal("never claim contains i/o stmnts",(char *)0); + break; + case TIMEOUT: + /* never claim polls timeout */ + if (Ntimeouts && Etimeouts) + forbidden = 0; + Ntimeouts++; Etimeouts--; + break; + case LEN: case EMPTY: case FULL: + case 'R': case NFULL: case NEMPTY: + /* status becomes non-exclusive */ + if (n->sym && !(n->sym->xu&XX)) + { n->sym->xu |= XX; + if (separate == 2) { + printf("spin: warning, make sure that the S1 model\n"); + printf(" also polls channel '%s' in its claim\n", + n->sym->name); + } } + forbidden = 0; + break; + case 'c': + AST_track(n, 0); /* register as a slice criterion */ + /* fall thru */ + default: + forbidden = 0; + break; + } + if (forbidden) + { printf("spin: never, saw "); explain(t); printf("\n"); + fatal("incompatible with separate compilation",(char *)0); + } + } else if ((t == ENABLED || t == PC_VAL) && !(warn_nn&t)) + { printf("spin: Warning, using %s outside never claim\n", + (t == ENABLED)?"enabled()":"pc_value()"); + warn_nn |= t; + } else if (t == NONPROGRESS) + { fatal("spin: Error, using np_ outside never claim\n", (char *)0); + } + return n; +} + +Lextok * +rem_lab(Symbol *a, Lextok *b, Symbol *c) /* proctype name, pid, label name */ +{ Lextok *tmp1, *tmp2, *tmp3; + + has_remote++; + c->type = LABEL; /* refered to in global context here */ + fix_dest(c, a); /* in case target of rem_lab is jump */ + tmp1 = nn(ZN, '?', b, ZN); tmp1->sym = a; + tmp1 = nn(ZN, 'p', tmp1, ZN); + tmp1->sym = lookup("_p"); + tmp2 = nn(ZN, NAME, ZN, ZN); tmp2->sym = a; + tmp3 = nn(ZN, 'q', tmp2, ZN); tmp3->sym = c; + return nn(ZN, EQ, tmp1, tmp3); +#if 0 + .---------------EQ-------. + / \ + 'p' -sym-> _p 'q' -sym-> c (label name) + / / + '?' -sym-> a (proctype) NAME -sym-> a (proctype name) + / + b (pid expr) +#endif +} + +Lextok * +rem_var(Symbol *a, Lextok *b, Symbol *c, Lextok *ndx) +{ Lextok *tmp1; + + has_remote++; + has_remvar++; + dataflow = 0; /* turn off dead variable resets 4.2.5 */ + tmp1 = nn(ZN, '?', b, ZN); tmp1->sym = a; + tmp1 = nn(ZN, 'p', tmp1, ndx); + tmp1->sym = c; + return tmp1; +#if 0 + cannot refer to struct elements + only to scalars and arrays + + 'p' -sym-> c (variable name) + / \______ possible arrayindex on c + / + '?' -sym-> a (proctype) + / + b (pid expr) +#endif +} + +static void +explain(int n) +{ FILE *fd = stdout; + switch (n) { + default: if (n > 0 && n < 256) + fprintf(fd, "'%c' = '", n); + fprintf(fd, "%d'", n); + break; + case '\b': fprintf(fd, "\\b"); break; + case '\t': fprintf(fd, "\\t"); break; + case '\f': fprintf(fd, "\\f"); break; + case '\n': fprintf(fd, "\\n"); break; + case '\r': fprintf(fd, "\\r"); break; + case 'c': fprintf(fd, "condition"); break; + case 's': fprintf(fd, "send"); break; + case 'r': fprintf(fd, "recv"); break; + case 'R': fprintf(fd, "recv poll %s", Operator); break; + case '@': fprintf(fd, "@"); break; + case '?': fprintf(fd, "(x->y:z)"); break; + case ACTIVE: fprintf(fd, "%sactive", Keyword); break; + case AND: fprintf(fd, "%s&&", Operator); break; + case ASGN: fprintf(fd, "%s=", Operator); break; + case ASSERT: fprintf(fd, "%sassert", Function); break; + case ATOMIC: fprintf(fd, "%satomic", Keyword); break; + case BREAK: fprintf(fd, "%sbreak", Keyword); break; + case C_CODE: fprintf(fd, "%sc_code", Keyword); break; + case C_DECL: fprintf(fd, "%sc_decl", Keyword); break; + case C_EXPR: fprintf(fd, "%sc_expr", Keyword); break; + case C_STATE: fprintf(fd, "%sc_state",Keyword); break; + case C_TRACK: fprintf(fd, "%sc_track",Keyword); break; + case CLAIM: fprintf(fd, "%snever", Keyword); break; + case CONST: fprintf(fd, "a constant"); break; + case DECR: fprintf(fd, "%s--", Operator); break; + case D_STEP: fprintf(fd, "%sd_step", Keyword); break; + case D_PROCTYPE: fprintf(fd, "%sd_proctype", Keyword); break; + case DO: fprintf(fd, "%sdo", Keyword); break; + case DOT: fprintf(fd, "."); break; + case ELSE: fprintf(fd, "%selse", Keyword); break; + case EMPTY: fprintf(fd, "%sempty", Function); break; + case ENABLED: fprintf(fd, "%senabled",Function); break; + case EQ: fprintf(fd, "%s==", Operator); break; + case EVAL: fprintf(fd, "%seval", Function); break; + case FI: fprintf(fd, "%sfi", Keyword); break; + case FULL: fprintf(fd, "%sfull", Function); break; + case GE: fprintf(fd, "%s>=", Operator); break; + case GOTO: fprintf(fd, "%sgoto", Keyword); break; + case GT: fprintf(fd, "%s>", Operator); break; + case HIDDEN: fprintf(fd, "%shidden", Keyword); break; + case IF: fprintf(fd, "%sif", Keyword); break; + case INCR: fprintf(fd, "%s++", Operator); break; + case INAME: fprintf(fd, "inline name"); break; + case INLINE: fprintf(fd, "%sinline", Keyword); break; + case INIT: fprintf(fd, "%sinit", Keyword); break; + case ISLOCAL: fprintf(fd, "%slocal", Keyword); break; + case LABEL: fprintf(fd, "a label-name"); break; + case LE: fprintf(fd, "%s<=", Operator); break; + case LEN: fprintf(fd, "%slen", Function); break; + case LSHIFT: fprintf(fd, "%s<<", Operator); break; + case LT: fprintf(fd, "%s<", Operator); break; + case MTYPE: fprintf(fd, "%smtype", Keyword); break; + case NAME: fprintf(fd, "an identifier"); break; + case NE: fprintf(fd, "%s!=", Operator); break; + case NEG: fprintf(fd, "%s! (not)",Operator); break; + case NEMPTY: fprintf(fd, "%snempty", Function); break; + case NFULL: fprintf(fd, "%snfull", Function); break; + case NON_ATOMIC: fprintf(fd, "sub-sequence"); break; + case NONPROGRESS: fprintf(fd, "%snp_", Function); break; + case OD: fprintf(fd, "%sod", Keyword); break; + case OF: fprintf(fd, "%sof", Keyword); break; + case OR: fprintf(fd, "%s||", Operator); break; + case O_SND: fprintf(fd, "%s!!", Operator); break; + case PC_VAL: fprintf(fd, "%spc_value",Function); break; + case PNAME: fprintf(fd, "process name"); break; + case PRINT: fprintf(fd, "%sprintf", Function); break; + case PRINTM: fprintf(fd, "%sprintm", Function); break; + case PRIORITY: fprintf(fd, "%spriority", Keyword); break; + case PROCTYPE: fprintf(fd, "%sproctype",Keyword); break; + case PROVIDED: fprintf(fd, "%sprovided",Keyword); break; + case RCV: fprintf(fd, "%s?", Operator); break; + case R_RCV: fprintf(fd, "%s??", Operator); break; + case RSHIFT: fprintf(fd, "%s>>", Operator); break; + case RUN: fprintf(fd, "%srun", Operator); break; + case SEP: fprintf(fd, "token: ::"); break; + case SEMI: fprintf(fd, ";"); break; + case SHOW: fprintf(fd, "%sshow", Keyword); break; + case SND: fprintf(fd, "%s!", Operator); break; + case STRING: fprintf(fd, "a string"); break; + case TRACE: fprintf(fd, "%strace", Keyword); break; + case TIMEOUT: fprintf(fd, "%stimeout",Keyword); break; + case TYPE: fprintf(fd, "data typename"); break; + case TYPEDEF: fprintf(fd, "%stypedef",Keyword); break; + case XU: fprintf(fd, "%sx[rs]", Keyword); break; + case UMIN: fprintf(fd, "%s- (unary minus)", Operator); break; + case UNAME: fprintf(fd, "a typename"); break; + case UNLESS: fprintf(fd, "%sunless", Keyword); break; + } +} diff --git a/sys/src/cmd/spin/mesg.c b/sys/src/cmd/spin/mesg.c new file mode 100755 index 000000000..4b2806b40 --- /dev/null +++ b/sys/src/cmd/spin/mesg.c @@ -0,0 +1,644 @@ +/***** spin: mesg.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +#ifndef MAXQ +#define MAXQ 2500 /* default max # queues */ +#endif + +extern RunList *X; +extern Symbol *Fname; +extern Lextok *Mtype; +extern int verbose, TstOnly, s_trail, analyze, columns; +extern int lineno, depth, xspin, m_loss, jumpsteps; +extern int nproc, nstop; +extern short Have_claim; + +Queue *qtab = (Queue *) 0; /* linked list of queues */ +Queue *ltab[MAXQ]; /* linear list of queues */ +int nqs = 0, firstrow = 1; +char Buf[4096]; + +static Lextok *n_rem = (Lextok *) 0; +static Queue *q_rem = (Queue *) 0; + +static int a_rcv(Queue *, Lextok *, int); +static int a_snd(Queue *, Lextok *); +static int sa_snd(Queue *, Lextok *); +static int s_snd(Queue *, Lextok *); +extern void sr_buf(int, int); +extern void sr_mesg(FILE *, int, int); +extern void putarrow(int, int); +static void sr_talk(Lextok *, int, char *, char *, int, Queue *); + +int +cnt_mpars(Lextok *n) +{ Lextok *m; + int i=0; + + for (m = n; m; m = m->rgt) + i += Cnt_flds(m); + return i; +} + +int +qmake(Symbol *s) +{ Lextok *m; + Queue *q; + int i; + + if (!s->ini) + return 0; + + if (nqs >= MAXQ) + { lineno = s->ini->ln; + Fname = s->ini->fn; + fatal("too many queues (%s)", s->name); + } + if (analyze && nqs >= 255) + { fatal("too many channel types", (char *)0); + } + + if (s->ini->ntyp != CHAN) + return eval(s->ini); + + q = (Queue *) emalloc(sizeof(Queue)); + q->qid = ++nqs; + q->nslots = s->ini->val; + q->nflds = cnt_mpars(s->ini->rgt); + q->setat = depth; + + i = max(1, q->nslots); /* 0-slot qs get 1 slot minimum */ + + q->contents = (int *) emalloc(q->nflds*i*sizeof(int)); + q->fld_width = (int *) emalloc(q->nflds*sizeof(int)); + q->stepnr = (int *) emalloc(i*sizeof(int)); + + for (m = s->ini->rgt, i = 0; m; m = m->rgt) + { if (m->sym && m->ntyp == STRUCT) + i = Width_set(q->fld_width, i, getuname(m->sym)); + else + q->fld_width[i++] = m->ntyp; + } + q->nxt = qtab; + qtab = q; + ltab[q->qid-1] = q; + + return q->qid; +} + +int +qfull(Lextok *n) +{ int whichq = eval(n->lft)-1; + + if (whichq < MAXQ && whichq >= 0 && ltab[whichq]) + return (ltab[whichq]->qlen >= ltab[whichq]->nslots); + return 0; +} + +int +qlen(Lextok *n) +{ int whichq = eval(n->lft)-1; + + if (whichq < MAXQ && whichq >= 0 && ltab[whichq]) + return ltab[whichq]->qlen; + return 0; +} + +int +q_is_sync(Lextok *n) +{ int whichq = eval(n->lft)-1; + + if (whichq < MAXQ && whichq >= 0 && ltab[whichq]) + return (ltab[whichq]->nslots == 0); + return 0; +} + +int +qsend(Lextok *n) +{ int whichq = eval(n->lft)-1; + + if (whichq == -1) + { printf("Error: sending to an uninitialized chan\n"); + whichq = 0; + return 0; + } + if (whichq < MAXQ && whichq >= 0 && ltab[whichq]) + { ltab[whichq]->setat = depth; + if (ltab[whichq]->nslots > 0) + return a_snd(ltab[whichq], n); + else + return s_snd(ltab[whichq], n); + } + return 0; +} + +int +qrecv(Lextok *n, int full) +{ int whichq = eval(n->lft)-1; + + if (whichq == -1) + { if (n->sym && !strcmp(n->sym->name, "STDIN")) + { Lextok *m; + + if (TstOnly) return 1; + + for (m = n->rgt; m; m = m->rgt) + if (m->lft->ntyp != CONST && m->lft->ntyp != EVAL) + { int c = getchar(); + (void) setval(m->lft, c); + } else + fatal("invalid use of STDIN", (char *)0); + + whichq = 0; + return 1; + } + printf("Error: receiving from an uninitialized chan %s\n", + n->sym?n->sym->name:""); + whichq = 0; + return 0; + } + if (whichq < MAXQ && whichq >= 0 && ltab[whichq]) + { ltab[whichq]->setat = depth; + return a_rcv(ltab[whichq], n, full); + } + return 0; +} + +static int +sa_snd(Queue *q, Lextok *n) /* sorted asynchronous */ +{ Lextok *m; + int i, j, k; + int New, Old; + + for (i = 0; i < q->qlen; i++) + for (j = 0, m = n->rgt; m && j < q->nflds; m = m->rgt, j++) + { New = cast_val(q->fld_width[j], eval(m->lft), 0); + Old = q->contents[i*q->nflds+j]; + if (New == Old) continue; + if (New > Old) break; /* inner loop */ + if (New < Old) goto found; + } +found: + for (j = q->qlen-1; j >= i; j--) + for (k = 0; k < q->nflds; k++) + { q->contents[(j+1)*q->nflds+k] = + q->contents[j*q->nflds+k]; /* shift up */ + if (k == 0) + q->stepnr[j+1] = q->stepnr[j]; + } + return i*q->nflds; /* new q offset */ +} + +void +typ_ck(int ft, int at, char *s) +{ + if ((verbose&32) && ft != at + && (ft == CHAN || at == CHAN)) + { char buf[128], tag1[64], tag2[64]; + (void) sputtype(tag1, ft); + (void) sputtype(tag2, at); + sprintf(buf, "type-clash in %s, (%s<-> %s)", s, tag1, tag2); + non_fatal("%s", buf); + } +} + +static int +a_snd(Queue *q, Lextok *n) +{ Lextok *m; + int i = q->qlen*q->nflds; /* q offset */ + int j = 0; /* q field# */ + + if (q->nslots > 0 && q->qlen >= q->nslots) + return m_loss; /* q is full */ + + if (TstOnly) return 1; + + if (n->val) i = sa_snd(q, n); /* sorted insert */ + + q->stepnr[i/q->nflds] = depth; + + for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++) + { int New = eval(m->lft); + q->contents[i+j] = cast_val(q->fld_width[j], New, 0); + if ((verbose&16) && depth >= jumpsteps) + sr_talk(n, New, "Send ", "->", j, q); + typ_ck(q->fld_width[j], Sym_typ(m->lft), "send"); + } + if ((verbose&16) && depth >= jumpsteps) + { for (i = j; i < q->nflds; i++) + sr_talk(n, 0, "Send ", "->", i, q); + if (j < q->nflds) + printf("%3d: warning: missing params in send\n", + depth); + if (m) + printf("%3d: warning: too many params in send\n", + depth); + } + q->qlen++; + return 1; +} + +static int +a_rcv(Queue *q, Lextok *n, int full) +{ Lextok *m; + int i=0, oi, j, k; + extern int Rvous; + + if (q->qlen == 0) + return 0; /* q is empty */ +try_slot: + /* test executability */ + for (m = n->rgt, j=0; m && j < q->nflds; m = m->rgt, j++) + if ((m->lft->ntyp == CONST + && q->contents[i*q->nflds+j] != m->lft->val) + || (m->lft->ntyp == EVAL + && q->contents[i*q->nflds+j] != eval(m->lft->lft))) + { if (n->val == 0 /* fifo recv */ + || n->val == 2 /* fifo poll */ + || ++i >= q->qlen) /* last slot */ + return 0; /* no match */ + goto try_slot; + } + if (TstOnly) return 1; + + if (verbose&8) + { if (j < q->nflds) + printf("%3d: warning: missing params in next recv\n", + depth); + else if (m) + printf("%3d: warning: too many params in next recv\n", + depth); + } + + /* set the fields */ + if (Rvous) + { n_rem = n; + q_rem = q; + } + + oi = q->stepnr[i]; + for (m = n->rgt, j = 0; m && j < q->nflds; m = m->rgt, j++) + { if (columns && !full) /* was columns == 1 */ + continue; + if ((verbose&8) && !Rvous && depth >= jumpsteps) + { sr_talk(n, q->contents[i*q->nflds+j], + (full && n->val < 2)?"Recv ":"[Recv] ", "<-", j, q); + } + if (!full) + continue; /* test */ + if (m && m->lft->ntyp != CONST && m->lft->ntyp != EVAL) + { (void) setval(m->lft, q->contents[i*q->nflds+j]); + typ_ck(q->fld_width[j], Sym_typ(m->lft), "recv"); + } + if (n->val < 2) /* not a poll */ + for (k = i; k < q->qlen-1; k++) + { q->contents[k*q->nflds+j] = + q->contents[(k+1)*q->nflds+j]; + if (j == 0) + q->stepnr[k] = q->stepnr[k+1]; + } + } + + if ((!columns || full) + && (verbose&8) && !Rvous && depth >= jumpsteps) + for (i = j; i < q->nflds; i++) + { sr_talk(n, 0, + (full && n->val < 2)?"Recv ":"[Recv] ", "<-", i, q); + } + if (columns == 2 && full && !Rvous && depth >= jumpsteps) + putarrow(oi, depth); + + if (full && n->val < 2) + q->qlen--; + return 1; +} + +static int +s_snd(Queue *q, Lextok *n) +{ Lextok *m; + RunList *rX, *sX = X; /* rX=recvr, sX=sendr */ + int i, j = 0; /* q field# */ + + for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++) + { q->contents[j] = cast_val(q->fld_width[j], eval(m->lft), 0); + typ_ck(q->fld_width[j], Sym_typ(m->lft), "rv-send"); + } + q->qlen = 1; + if (!complete_rendez()) + { q->qlen = 0; + return 0; + } + if (TstOnly) + { q->qlen = 0; + return 1; + } + q->stepnr[0] = depth; + if ((verbose&16) && depth >= jumpsteps) + { m = n->rgt; + rX = X; X = sX; + for (j = 0; m && j < q->nflds; m = m->rgt, j++) + sr_talk(n, eval(m->lft), "Sent ", "->", j, q); + for (i = j; i < q->nflds; i++) + sr_talk(n, 0, "Sent ", "->", i, q); + if (j < q->nflds) + printf("%3d: warning: missing params in rv-send\n", + depth); + else if (m) + printf("%3d: warning: too many params in rv-send\n", + depth); + X = rX; /* restore receiver's context */ + if (!s_trail) + { if (!n_rem || !q_rem) + fatal("cannot happen, s_snd", (char *) 0); + m = n_rem->rgt; + for (j = 0; m && j < q->nflds; m = m->rgt, j++) + { if (m->lft->ntyp != NAME + || strcmp(m->lft->sym->name, "_") != 0) + i = eval(m->lft); + else i = 0; + + if (verbose&8) + sr_talk(n_rem,i,"Recv ","<-",j,q_rem); + } + if (verbose&8) + for (i = j; i < q->nflds; i++) + sr_talk(n_rem, 0, "Recv ", "<-", j, q_rem); + if (columns == 2) + putarrow(depth, depth); + } + n_rem = (Lextok *) 0; + q_rem = (Queue *) 0; + } + return 1; +} + +void +channm(Lextok *n) +{ char lbuf[512]; + + if (n->sym->type == CHAN) + strcat(Buf, n->sym->name); + else if (n->sym->type == NAME) + strcat(Buf, lookup(n->sym->name)->name); + else if (n->sym->type == STRUCT) + { Symbol *r = n->sym; + if (r->context) + r = findloc(r); + ini_struct(r); + printf("%s", r->name); + strcpy(lbuf, ""); + struct_name(n->lft, r, 1, lbuf); + strcat(Buf, lbuf); + } else + strcat(Buf, "-"); + if (n->lft->lft) + { sprintf(lbuf, "[%d]", eval(n->lft->lft)); + strcat(Buf, lbuf); + } +} + +static void +difcolumns(Lextok *n, char *tr, int v, int j, Queue *q) +{ extern int pno; + + if (j == 0) + { Buf[0] = '\0'; + channm(n); + strcat(Buf, (strncmp(tr, "Sen", 3))?"?":"!"); + } else + strcat(Buf, ","); + if (tr[0] == '[') strcat(Buf, "["); + sr_buf(v, q->fld_width[j] == MTYPE); + if (j == q->nflds - 1) + { int cnr; + if (s_trail) cnr = pno; else cnr = X?X->pid - Have_claim:0; + if (tr[0] == '[') strcat(Buf, "]"); + pstext(cnr, Buf); + } +} + +static void +docolumns(Lextok *n, char *tr, int v, int j, Queue *q) +{ int i; + + if (firstrow) + { printf("q\\p"); + for (i = 0; i < nproc-nstop - Have_claim; i++) + printf(" %3d", i); + printf("\n"); + firstrow = 0; + } + if (j == 0) + { printf("%3d", q->qid); + if (X) + for (i = 0; i < X->pid - Have_claim; i++) + printf(" ."); + printf(" "); + Buf[0] = '\0'; + channm(n); + printf("%s%c", Buf, (strncmp(tr, "Sen", 3))?'?':'!'); + } else + printf(","); + if (tr[0] == '[') printf("["); + sr_mesg(stdout, v, q->fld_width[j] == MTYPE); + if (j == q->nflds - 1) + { if (tr[0] == '[') printf("]"); + printf("\n"); + } +} + +typedef struct QH { + int n; + struct QH *nxt; +} QH; +QH *qh; + +void +qhide(int q) +{ QH *p = (QH *) emalloc(sizeof(QH)); + p->n = q; + p->nxt = qh; + qh = p; +} + +int +qishidden(int q) +{ QH *p; + for (p = qh; p; p = p->nxt) + if (p->n == q) + return 1; + return 0; +} + +static void +sr_talk(Lextok *n, int v, char *tr, char *a, int j, Queue *q) +{ char s[64]; + + if (qishidden(eval(n->lft))) + return; + + if (columns) + { if (columns == 2) + difcolumns(n, tr, v, j, q); + else + docolumns(n, tr, v, j, q); + return; + } + if (xspin) + { if ((verbose&4) && tr[0] != '[') + sprintf(s, "(state -)\t[values: %d", + eval(n->lft)); + else + sprintf(s, "(state -)\t[%d", eval(n->lft)); + if (strncmp(tr, "Sen", 3) == 0) + strcat(s, "!"); + else + strcat(s, "?"); + } else + { strcpy(s, tr); + } + + if (j == 0) + { whoruns(1); + printf("line %3d %s %s", + n->ln, n->fn->name, s); + } else + printf(","); + sr_mesg(stdout, v, q->fld_width[j] == MTYPE); + + if (j == q->nflds - 1) + { if (xspin) + { printf("]\n"); + if (!(verbose&4)) printf("\n"); + return; + } + printf("\t%s queue %d (", a, eval(n->lft)); + Buf[0] = '\0'; + channm(n); + printf("%s)\n", Buf); + } + fflush(stdout); +} + +void +sr_buf(int v, int j) +{ int cnt = 1; Lextok *n; + char lbuf[512]; + + for (n = Mtype; n && j; n = n->rgt, cnt++) + if (cnt == v) + { if(strlen(n->lft->sym->name) >= sizeof(lbuf)) + { non_fatal("mtype name %s too long", n->lft->sym->name); + break; + } + sprintf(lbuf, "%s", n->lft->sym->name); + strcat(Buf, lbuf); + return; + } + sprintf(lbuf, "%d", v); + strcat(Buf, lbuf); +} + +void +sr_mesg(FILE *fd, int v, int j) +{ Buf[0] ='\0'; + sr_buf(v, j); + fprintf(fd, Buf); +} + +void +doq(Symbol *s, int n, RunList *r) +{ Queue *q; + int j, k; + + if (!s->val) /* uninitialized queue */ + return; + for (q = qtab; q; q = q->nxt) + if (q->qid == s->val[n]) + { if (xspin > 0 + && (verbose&4) + && q->setat < depth) + continue; + if (q->nslots == 0) + continue; /* rv q always empty */ + printf("\t\tqueue %d (", q->qid); + if (r) + printf("%s(%d):", r->n->name, r->pid - Have_claim); + if (s->nel != 1) + printf("%s[%d]): ", s->name, n); + else + printf("%s): ", s->name); + for (k = 0; k < q->qlen; k++) + { printf("["); + for (j = 0; j < q->nflds; j++) + { if (j > 0) printf(","); + sr_mesg(stdout, q->contents[k*q->nflds+j], + q->fld_width[j] == MTYPE); + } + printf("]"); + } + printf("\n"); + break; + } +} + +void +nochan_manip(Lextok *p, Lextok *n, int d) +{ int e = 1; + + if (d == 0 && p->sym && p->sym->type == CHAN) + { setaccess(p->sym, ZS, 0, 'L'); + + if (n && n->ntyp == CONST) + fatal("invalid asgn to chan", (char *) 0); + + if (n && n->sym && n->sym->type == CHAN) + { setaccess(n->sym, ZS, 0, 'V'); + return; + } + } + + if (!n || n->ntyp == LEN || n->ntyp == RUN) + return; + + if (n->sym && n->sym->type == CHAN) + { if (d == 1) + fatal("invalid use of chan name", (char *) 0); + else + setaccess(n->sym, ZS, 0, 'V'); + } + + if (n->ntyp == NAME + || n->ntyp == '.') + e = 0; /* array index or struct element */ + + nochan_manip(p, n->lft, e); + nochan_manip(p, n->rgt, 1); +} + +void +no_internals(Lextok *n) +{ char *sp; + + if (!n->sym + || !n->sym->name) + return; + + sp = n->sym->name; + + if ((strlen(sp) == strlen("_nr_pr") && strcmp(sp, "_nr_pr") == 0) + || (strlen(sp) == strlen("_p") && strcmp(sp, "_p") == 0)) + { fatal("attempt to assign value to system variable %s", sp); + } +} diff --git a/sys/src/cmd/spin/mkfile b/sys/src/cmd/spin/mkfile new file mode 100755 index 000000000..abbef6239 --- /dev/null +++ b/sys/src/cmd/spin/mkfile @@ -0,0 +1,56 @@ +</$objtype/mkfile + +TARG=spin + +SPIN_OS=\ + dstep.$O\ + flow.$O\ + guided.$O\ + main.$O\ + mesg.$O\ + pangen1.$O\ + pangen2.$O\ + pangen3.$O\ + pangen4.$O\ + pangen5.$O\ + pangen6.$O\ + pc_zpp.$O\ + ps_msc.$O\ + reprosrc.$O\ + run.$O\ + sched.$O\ + spinlex.$O\ + structs.$O\ + sym.$O\ + vars.$O\ + y.tab.$O\ + +TL_OS=\ + tl_buchi.$O\ + tl_cache.$O\ + tl_lex.$O\ + tl_main.$O\ + tl_mem.$O\ + tl_parse.$O\ + tl_rewrt.$O\ + tl_trans.$O\ + +OFILES=$SPIN_OS $TL_OS + +YFILES=spin.y + +HFILES=y.tab.h + +BIN=/$objtype/bin +</sys/src/cmd/mkone + +CC=pcc -c +CFLAGS=-B -D_POSIX_SOURCE +YFLAGS=-S -d + +$SPIN_OS: spin.h +$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 diff --git a/sys/src/cmd/spin/pangen1.c b/sys/src/cmd/spin/pangen1.c new file mode 100755 index 000000000..cbef9dba5 --- /dev/null +++ b/sys/src/cmd/spin/pangen1.c @@ -0,0 +1,1275 @@ +/***** spin: pangen1.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" +#include "pangen1.h" +#include "pangen3.h" + +extern FILE *tc, *th, *tt; +extern Label *labtab; +extern Ordered *all_names; +extern ProcList *rdy; +extern Queue *qtab; +extern Symbol *Fname; +extern int lineno, verbose, Pid, separate; +extern int nrRdy, nqs, mst, Mpars, claimnr, eventmapnr; +extern short has_sorted, has_random, has_provided; + +int Npars=0, u_sync=0, u_async=0, hastrack = 1; +short has_io = 0; +short has_state=0; /* code contains c_state */ + +static Symbol *LstSet=ZS; +static int acceptors=0, progressors=0, nBits=0; +static int Types[] = { UNSIGNED, BIT, BYTE, CHAN, MTYPE, SHORT, INT, STRUCT }; + +static int doglobal(char *, int); +static void dohidden(void); +static void do_init(FILE *, Symbol *); +static void end_labs(Symbol *, int); +static void put_ptype(char *, int, int, int); +static void tc_predef_np(void); +static void put_pinit(ProcList *); + void walk_struct(FILE *, int, char *, Symbol *, char *, char *, char *); + +static void +reverse_names(ProcList *p) +{ + if (!p) return; + reverse_names(p->nxt); + fprintf(th, " \"%s\",\n", p->n->name); +} + +void +genheader(void) +{ ProcList *p; int i; + + if (separate == 2) + { putunames(th); + goto here; + } + + fprintf(th, "#define SYNC %d\n", u_sync); + fprintf(th, "#define ASYNC %d\n\n", u_async); + + putunames(th); + + fprintf(tc, "short Air[] = { "); + for (p = rdy, i=0; p; p = p->nxt, i++) + fprintf(tc, "%s (short) Air%d", (p!=rdy)?",":"", i); + fprintf(tc, ", (short) Air%d", i); /* np_ */ + fprintf(tc, " };\n"); + + fprintf(th, "char *procname[] = {\n"); + reverse_names(rdy); + fprintf(th, " \":np_:\",\n"); + fprintf(th, "};\n\n"); + +here: + for (p = rdy; p; p = p->nxt) + put_ptype(p->n->name, p->tn, mst, nrRdy+1); + /* +1 for np_ */ + put_ptype("np_", nrRdy, mst, nrRdy+1); + + ntimes(th, 0, 1, Head0); + + if (separate != 2) + { extern void c_add_stack(FILE *); + + ntimes(th, 0, 1, Header); + c_add_stack(th); + ntimes(th, 0, 1, Header0); + } + ntimes(th, 0, 1, Head1); + + LstSet = ZS; + (void) doglobal("", PUTV); + + hastrack = c_add_sv(th); + + fprintf(th, " uchar sv[VECTORSZ];\n"); + fprintf(th, "} State"); +#ifdef SOLARIS + fprintf(th,"\n#ifdef GCC\n"); + fprintf(th, "\t__attribute__ ((aligned(8)))"); + fprintf(th, "\n#endif\n\t"); +#endif + fprintf(th, ";\n\n"); + + fprintf(th, "#define HAS_TRACK %d\n", hastrack); + + if (separate != 2) + dohidden(); +} + +void +genaddproc(void) +{ ProcList *p; + int i = 0; + + if (separate ==2) goto shortcut; + + fprintf(tc, "int\naddproc(int n"); + for (i = 0; i < Npars; i++) + fprintf(tc, ", int par%d", i); + + ntimes(tc, 0, 1, Addp0); + ntimes(tc, 1, nrRdy+1, R5); /* +1 for np_ */ + ntimes(tc, 0, 1, Addp1); + + if (has_provided) + { fprintf(tt, "\nint\nprovided(int II, unsigned char ot, "); + fprintf(tt, "int tt, Trans *t)\n"); + fprintf(tt, "{\n\tswitch(ot) {\n"); + } +shortcut: + tc_predef_np(); + for (p = rdy; p; p = p->nxt) + { Pid = p->tn; + put_pinit(p); + } + if (separate == 2) return; + + Pid = 0; + if (has_provided) + { fprintf(tt, "\tdefault: return 1; /* e.g., a claim */\n"); + fprintf(tt, "\t}\n\treturn 0;\n}\n"); + } + + ntimes(tc, i, i+1, R6); + if (separate == 0) + ntimes(tc, 1, nrRdy+1, R5); /* +1 for np_ */ + else + ntimes(tc, 1, nrRdy, R5); + ntimes(tc, 0, 1, R8a); +} + +void +do_locinits(FILE *fd) +{ ProcList *p; + + for (p = rdy; p; p = p->nxt) + c_add_locinit(fd, p->tn, p->n->name); +} + +void +genother(void) +{ ProcList *p; + + switch (separate) { + case 2: + if (claimnr >= 0) + ntimes(tc, claimnr, claimnr+1, R0); /* claim only */ + break; + case 1: + ntimes(tc, 0, 1, Code0); + ntimes(tc, 0, claimnr, R0); /* all except claim */ + ntimes(tc, claimnr+1, nrRdy, R0); + break; + case 0: + ntimes(tc, 0, 1, Code0); + ntimes(tc, 0, nrRdy+1, R0); /* +1 for np_ */ + break; + } + + for (p = rdy; p; p = p->nxt) + end_labs(p->n, p->tn); + + switch (separate) { + case 2: + if (claimnr >= 0) + ntimes(tc, claimnr, claimnr+1, R0a); /* claim only */ + return; + case 1: + ntimes(tc, 0, claimnr, R0a); /* all except claim */ + ntimes(tc, claimnr+1, nrRdy, R0a); + fprintf(tc, " if (state_tables)\n"); + fprintf(tc, " ini_claim(%d, 0);\n", claimnr); + break; + case 0: + ntimes(tc, 0, nrRdy, R0a); /* all */ + break; + } + + ntimes(tc, 0, 1, R0b); + if (separate == 1 && acceptors == 0) + acceptors = 1; /* assume at least 1 acceptstate */ + ntimes(th, acceptors, acceptors+1, Code1); + ntimes(th, progressors, progressors+1, Code3); + ntimes(th, nrRdy+1, nrRdy+2, R2); /* +1 for np_ */ + + fprintf(tc, " iniglobals();\n"); + ntimes(tc, 0, 1, Code2a); + ntimes(tc, 0, 1, Code2b); /* bfs option */ + ntimes(tc, 0, 1, Code2c); + ntimes(tc, 0, nrRdy, R4); + fprintf(tc, "}\n\n"); + + fprintf(tc, "void\n"); + fprintf(tc, "iniglobals(void)\n{\n"); + if (doglobal("", INIV) > 0) + { fprintf(tc, "#ifdef VAR_RANGES\n"); + (void) doglobal("logval(\"", LOGV); + fprintf(tc, "#endif\n"); + } + ntimes(tc, 1, nqs+1, R3); + fprintf(tc, "\tMaxbody = max(Maxbody, sizeof(State)-VECTORSZ);"); + fprintf(tc, "\n}\n\n"); +} + +void +gensvmap(void) +{ + ntimes(tc, 0, 1, SvMap); +} + +static struct { + char *s, *t; int n, m, p; +} ln[] = { + {"end", "stopstate", 3, 0, 0}, + {"progress", "progstate", 8, 0, 1}, + {"accept", "accpstate", 6, 1, 0}, + {0, 0, 0, 0, 0}, +}; + +static void +end_labs(Symbol *s, int i) +{ int oln = lineno; + Symbol *ofn = Fname; + Label *l; + int j; char foo[128]; + + if ((i == claimnr && separate == 1) + || (i != claimnr && separate == 2)) + return; + + for (l = labtab; l; l = l->nxt) + for (j = 0; ln[j].n; j++) + if (strncmp(l->s->name, ln[j].s, ln[j].n) == 0 + && strcmp(l->c->name, s->name) == 0) + { fprintf(tc, "\t%s[%d][%d] = 1;\n", + ln[j].t, i, l->e->seqno); + acceptors += ln[j].m; + progressors += ln[j].p; + if (l->e->status & D_ATOM) + { sprintf(foo, "%s label inside d_step", + ln[j].s); + goto complain; + } + if (j > 0 && (l->e->status & ATOM)) + { sprintf(foo, "%s label inside atomic", + ln[j].s); + complain: lineno = l->e->n->ln; + Fname = l->e->n->fn; + printf("spin: %3d:%s, warning, %s - is invisible\n", + lineno, Fname?Fname->name:"-", foo); + } + } + /* visible states -- through remote refs: */ + for (l = labtab; l; l = l->nxt) + if (l->visible + && strcmp(l->s->context->name, s->name) == 0) + fprintf(tc, "\tvisstate[%d][%d] = 1;\n", + i, l->e->seqno); + + lineno = oln; + Fname = ofn; +} + +void +ntimes(FILE *fd, int n, int m, char *c[]) +{ + int i, j; + for (j = 0; c[j]; j++) + for (i = n; i < m; i++) + { fprintf(fd, c[j], i, i, i, i, i, i); + fprintf(fd, "\n"); + } +} + +void +prehint(Symbol *s) +{ Lextok *n; + + printf("spin: warning, "); + if (!s) return; + + n = (s->context != ZS)?s->context->ini:s->ini; + if (n) + printf("line %3d %s, ", n->ln, n->fn->name); +} + +void +checktype(Symbol *sp, char *s) +{ char buf[128]; int i; + + if (!s + || (sp->type != BYTE + && sp->type != SHORT + && sp->type != INT)) + return; + + if (sp->hidden&16) /* formal parameter */ + { ProcList *p; Lextok *f, *t; + int posnr = 0; + for (p = rdy; p; p = p->nxt) + if (p->n->name + && strcmp(s, p->n->name) == 0) + break; + if (p) + for (f = p->p; f; f = f->rgt) /* list of types */ + for (t = f->lft; t; t = t->rgt, posnr++) + if (t->sym + && strcmp(t->sym->name, sp->name) == 0) + { checkrun(sp, posnr); + return; + } + + } else if (!(sp->hidden&4)) + { if (!(verbose&32)) return; + sputtype(buf, sp->type); + i = (int) strlen(buf); + while (buf[--i] == ' ') buf[i] = '\0'; + prehint(sp); + if (sp->context) + printf("proctype %s:", s); + else + printf("global"); + printf(" '%s %s' could be declared 'bit %s'\n", + buf, sp->name, sp->name); + } else if (sp->type != BYTE && !(sp->hidden&8)) + { if (!(verbose&32)) return; + sputtype(buf, sp->type); + i = (int) strlen(buf); + while (buf[--i] == ' ') buf[i] = '\0'; + prehint(sp); + if (sp->context) + printf("proctype %s:", s); + else + printf("global"); + printf(" '%s %s' could be declared 'byte %s'\n", + buf, sp->name, sp->name); + } +} + +int +dolocal(FILE *ofd, char *pre, int dowhat, int p, char *s) +{ int h, j, k=0; extern int nr_errs; + Ordered *walk; + Symbol *sp; + char buf[64], buf2[128], buf3[128]; + + if (dowhat == INIV) + { /* initialize in order of declaration */ + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp->context + && !sp->owner + && strcmp(s, sp->context->name) == 0) + { checktype(sp, s); /* fall through */ + if (!(sp->hidden&16)) + { sprintf(buf, "((P%d *)pptr(h))->", p); + do_var(ofd, dowhat, buf, sp, "", " = ", ";\n"); + } + k++; + } } + } else + { for (j = 0; j < 8; j++) + for (h = 0; h <= 1; h++) + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp->context + && !sp->owner + && sp->type == Types[j] + && ((h == 0 && sp->nel == 1) || (h == 1 && sp->nel > 1)) + && strcmp(s, sp->context->name) == 0) + { switch (dowhat) { + case LOGV: + if (sp->type == CHAN + && verbose == 0) + break; + sprintf(buf, "%s%s:", pre, s); + { sprintf(buf2, "\", ((P%d *)pptr(h))->", p); + sprintf(buf3, ");\n"); + } + do_var(ofd, dowhat, "", sp, buf, buf2, buf3); + break; + case PUTV: + sprintf(buf, "((P%d *)pptr(h))->", p); + do_var(ofd, dowhat, buf, sp, "", " = ", ";\n"); + k++; + break; + } + if (strcmp(s, ":never:") == 0) + { printf("error: %s defines local %s\n", + s, sp->name); + nr_errs++; + } } } } + + return k; +} + +void +c_chandump(FILE *fd) +{ Queue *q; + char buf[256]; + int i; + + if (!qtab) + { fprintf(fd, "void\nc_chandump(int unused) "); + fprintf(fd, "{ unused = unused++; /* avoid complaints */ }\n"); + return; + } + + fprintf(fd, "void\nc_chandump(int from)\n"); + fprintf(fd, "{ uchar *z; int slot;\n"); + + fprintf(fd, " from--;\n"); + fprintf(fd, " if (from >= (int) now._nr_qs || from < 0)\n"); + fprintf(fd, " { printf(\"pan: bad qid %%d\\n\", from+1);\n"); + fprintf(fd, " return;\n"); + fprintf(fd, " }\n"); + fprintf(fd, " z = qptr(from);\n"); + fprintf(fd, " switch (((Q0 *)z)->_t) {\n"); + + for (q = qtab; q; q = q->nxt) + { fprintf(fd, " case %d:\n\t\t", q->qid); + sprintf(buf, "((Q%d *)z)->", q->qid); + + fprintf(fd, "for (slot = 0; slot < %sQlen; slot++)\n\t\t", buf); + fprintf(fd, "{ printf(\" [\");\n\t\t"); + for (i = 0; i < q->nflds; i++) + { if (q->fld_width[i] == MTYPE) + { fprintf(fd, "\tprintm(%scontents[slot].fld%d);\n\t\t", + buf, i); + } else + fprintf(fd, "\tprintf(\"%%d,\", %scontents[slot].fld%d);\n\t\t", + buf, i); + } + fprintf(fd, " printf(\"],\");\n\t\t"); + fprintf(fd, "}\n\t\t"); + fprintf(fd, "break;\n"); + } + fprintf(fd, " }\n"); + fprintf(fd, " printf(\"\\n\");\n}\n"); +} + +void +c_var(FILE *fd, char *pref, Symbol *sp) +{ char buf[256]; + int i; + + switch (sp->type) { + case STRUCT: + /* c_struct(fd, pref, sp); */ + fprintf(fd, "\t\tprintf(\"\t(struct %s)\\n\");\n", + sp->name); + sprintf(buf, "%s%s.", pref, sp->name); + c_struct(fd, buf, sp); + break; + case BIT: case BYTE: + case SHORT: case INT: + case UNSIGNED: + sputtype(buf, sp->type); + if (sp->nel == 1) + { fprintf(fd, "\tprintf(\"\t%s %s:\t%%d\\n\", %s%s);\n", + buf, sp->name, pref, sp->name); + } else + { fprintf(fd, "\t{\tint l_in;\n"); + fprintf(fd, "\t\tfor (l_in = 0; l_in < %d; l_in++)\n", sp->nel); + fprintf(fd, "\t\t{\n"); + fprintf(fd, "\t\t\tprintf(\"\t%s %s[%%d]:\t%%d\\n\", l_in, %s%s[l_in]);\n", + buf, sp->name, pref, sp->name); + fprintf(fd, "\t\t}\n"); + fprintf(fd, "\t}\n"); + } + break; + case CHAN: + if (sp->nel == 1) + { fprintf(fd, "\tprintf(\"\tchan %s (=%%d):\tlen %%d:\\t\", ", + sp->name); + fprintf(fd, "%s%s, q_len(%s%s));\n", + pref, sp->name, pref, sp->name); + fprintf(fd, "\tc_chandump(%s%s);\n", pref, sp->name); + } else + for (i = 0; i < sp->nel; i++) + { fprintf(fd, "\tprintf(\"\tchan %s[%d] (=%%d):\tlen %%d:\\t\", ", + sp->name, i); + fprintf(fd, "%s%s[%d], q_len(%s%s[%d]));\n", + pref, sp->name, i, pref, sp->name, i); + fprintf(fd, "\tc_chandump(%s%s[%d]);\n", + pref, sp->name, i); + } + break; + } +} + +int +c_splurge_any(ProcList *p) +{ Ordered *walk; + Symbol *sp; + + if (strcmp(p->n->name, ":never:") != 0 + && strcmp(p->n->name, ":trace:") != 0 + && strcmp(p->n->name, ":notrace:") != 0) + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (!sp->context + || sp->type == 0 + || strcmp(sp->context->name, p->n->name) != 0 + || sp->owner || (sp->hidden&1) + || (sp->type == MTYPE && ismtype(sp->name))) + continue; + + return 1; + } + return 0; +} + +void +c_splurge(FILE *fd, ProcList *p) +{ Ordered *walk; + Symbol *sp; + char pref[64]; + + if (strcmp(p->n->name, ":never:") != 0 + && strcmp(p->n->name, ":trace:") != 0 + && strcmp(p->n->name, ":notrace:") != 0) + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (!sp->context + || sp->type == 0 + || strcmp(sp->context->name, p->n->name) != 0 + || sp->owner || (sp->hidden&1) + || (sp->type == MTYPE && ismtype(sp->name))) + continue; + + sprintf(pref, "((P%d *)pptr(pid))->", p->tn); + c_var(fd, pref, sp); + } +} + +void +c_wrapper(FILE *fd) /* allow pan.c to print out global sv entries */ +{ Ordered *walk; + ProcList *p; + Symbol *sp; + Lextok *n; + extern Lextok *Mtype; + int j; + + fprintf(fd, "void\nc_globals(void)\n{\t/* int i; */\n"); + fprintf(fd, " printf(\"global vars:\\n\");\n"); + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp->context || sp->owner || (sp->hidden&1) + || (sp->type == MTYPE && ismtype(sp->name))) + continue; + + c_var(fd, "now.", sp); + } + fprintf(fd, "}\n"); + + fprintf(fd, "void\nc_locals(int pid, int tp)\n{\t/* int i; */\n"); + fprintf(fd, " switch(tp) {\n"); + for (p = rdy; p; p = p->nxt) + { fprintf(fd, " case %d:\n", p->tn); + 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); + c_splurge(fd, p); + } else + { fprintf(fd, " \t/* none */\n"); + } + fprintf(fd, " \tbreak;\n"); + } + fprintf(fd, " }\n}\n"); + + fprintf(fd, "void\nprintm(int x)\n{\n"); + fprintf(fd, " switch (x) {\n"); + for (n = Mtype, j = 1; n && j; n = n->rgt, j++) + fprintf(fd, "\tcase %d: Printf(\"%s\"); break;\n", + j, n->lft->sym->name); + fprintf(fd, " default: Printf(\"%%d\", x);\n"); + fprintf(fd, " }\n"); + fprintf(fd, "}\n"); +} + +static int +doglobal(char *pre, int dowhat) +{ Ordered *walk; + Symbol *sp; + int j, cnt = 0; + + for (j = 0; j < 8; j++) + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (!sp->context + && !sp->owner + && sp->type == Types[j]) + { if (Types[j] != MTYPE || !ismtype(sp->name)) + switch (dowhat) { + case LOGV: + if (sp->type == CHAN + && verbose == 0) + break; + if (sp->hidden&1) + break; + do_var(tc, dowhat, "", sp, + pre, "\", now.", ");\n"); + break; + case INIV: + checktype(sp, (char *) 0); + cnt++; /* fall through */ + case PUTV: + do_var(tc, dowhat, (sp->hidden&1)?"":"now.", sp, + "", " = ", ";\n"); + break; + } } } + return cnt; +} + +static void +dohidden(void) +{ Ordered *walk; + Symbol *sp; + int j; + + for (j = 0; j < 8; j++) + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if ((sp->hidden&1) + && sp->type == Types[j]) + { if (sp->context || sp->owner) + fatal("cannot hide non-globals (%s)", sp->name); + if (sp->type == CHAN) + fatal("cannot hide channels (%s)", sp->name); + fprintf(th, "/* hidden variable: */"); + typ2c(sp); + } } + fprintf(th, "int _; /* a predefined write-only variable */\n\n"); +} + +void +do_var(FILE *ofd, int dowhat, char *s, Symbol *sp, + char *pre, char *sep, char *ter) +{ int i; + + switch(dowhat) { + case PUTV: + + if (sp->hidden&1) break; + + typ2c(sp); + break; + case LOGV: + case INIV: + if (sp->type == STRUCT) + { /* struct may contain a chan */ + walk_struct(ofd, dowhat, s, sp, pre, sep, ter); + break; + } + if (!sp->ini && dowhat != LOGV) /* it defaults to 0 */ + break; + if (sp->nel == 1) + { fprintf(ofd, "\t\t%s%s%s%s", + pre, s, sp->name, sep); + if (dowhat == LOGV) + fprintf(ofd, "%s%s", s, sp->name); + else + do_init(ofd, sp); + fprintf(ofd, "%s", ter); + } else + { if (sp->ini && sp->ini->ntyp == CHAN) + { for (i = 0; i < sp->nel; i++) + { fprintf(ofd, "\t\t%s%s%s[%d]%s", + pre, s, sp->name, i, sep); + if (dowhat == LOGV) + fprintf(ofd, "%s%s[%d]", + s, sp->name, i); + else + do_init(ofd, sp); + fprintf(ofd, "%s", ter); + } + } else + { fprintf(ofd, "\t{\tint l_in;\n"); + fprintf(ofd, "\t\tfor (l_in = 0; l_in < %d; l_in++)\n", sp->nel); + fprintf(ofd, "\t\t{\n"); + fprintf(ofd, "\t\t\t%s%s%s[l_in]%s", + pre, s, sp->name, sep); + if (dowhat == LOGV) + fprintf(ofd, "%s%s[l_in]", s, sp->name); + else + putstmnt(ofd, sp->ini, 0); + fprintf(ofd, "%s", ter); + fprintf(ofd, "\t\t}\n"); + fprintf(ofd, "\t}\n"); + } } + break; + } +} + +static void +do_init(FILE *ofd, Symbol *sp) +{ int i; extern Queue *ltab[]; + + if (sp->ini + && sp->type == CHAN + && ((i = qmake(sp)) > 0)) + { if (sp->ini->ntyp == CHAN) + fprintf(ofd, "addqueue(%d, %d)", + i, ltab[i-1]->nslots == 0); + else + fprintf(ofd, "%d", i); + } else + putstmnt(ofd, sp->ini, 0); +} + +static int +blog(int n) /* for small log2 without rounding problems */ +{ int m=1, r=2; + + while (r < n) { m++; r *= 2; } + return 1+m; +} + +static void +put_ptype(char *s, int i, int m0, int m1) +{ int k; + + if (strcmp(s, ":init:") == 0) + fprintf(th, "#define Pinit ((P%d *)this)\n", i); + + if (strcmp(s, ":never:") != 0 + && strcmp(s, ":trace:") != 0 + && strcmp(s, ":notrace:") != 0 + && strcmp(s, ":init:") != 0 + && strcmp(s, "_:never_template:_") != 0 + && strcmp(s, "np_") != 0) + fprintf(th, "#define P%s ((P%d *)this)\n", s, i); + + fprintf(th, "typedef struct P%d { /* %s */\n", i, s); + fprintf(th, " unsigned _pid : 8; /* 0..255 */\n"); + fprintf(th, " unsigned _t : %d; /* proctype */\n", blog(m1)); + fprintf(th, " unsigned _p : %d; /* state */\n", blog(m0)); + LstSet = ZS; + nBits = 8 + blog(m1) + blog(m0); + k = dolocal(tc, "", PUTV, i, s); /* includes pars */ + + c_add_loc(th, s); + + fprintf(th, "} P%d;\n", i); + if ((!LstSet && k > 0) || has_state) + fprintf(th, "#define Air%d 0\n", i); + else + { fprintf(th, "#define Air%d (sizeof(P%d) - ", i, i); + if (k == 0) + { fprintf(th, "%d", (nBits+7)/8); + goto done; + } + if ((LstSet->type != BIT && LstSet->type != UNSIGNED) + || LstSet->nel != 1) + { fprintf(th, "Offsetof(P%d, %s) - %d*sizeof(", + i, LstSet->name, LstSet->nel); + } + switch(LstSet->type) { + case UNSIGNED: + fprintf(th, "%d", (nBits+7)/8); + break; + case BIT: + if (LstSet->nel == 1) + { fprintf(th, "%d", (nBits+7)/8); + break; + } /* else fall through */ + case MTYPE: case BYTE: case CHAN: + fprintf(th, "uchar)"); break; + case SHORT: + fprintf(th, "short)"); break; + case INT: + fprintf(th, "int)"); break; + default: + fatal("cannot happen Air %s", + LstSet->name); + } +done: fprintf(th, ")\n"); + } +} + +static void +tc_predef_np(void) +{ int i = nrRdy; /* 1+ highest proctype nr */ + + fprintf(th, "#define _NP_ %d\n", i); +/* if (separate == 2) fprintf(th, "extern "); */ + fprintf(th, "uchar reached%d[3]; /* np_ */\n", i); + + fprintf(th, "#define nstates%d 3 /* np_ */\n", i); + fprintf(th, "#define endstate%d 2 /* np_ */\n\n", i); + fprintf(th, "#define start%d 0 /* np_ */\n", i); + + fprintf(tc, "\tcase %d: /* np_ */\n", i); + if (separate == 1) + { fprintf(tc, "\t\tini_claim(%d, h);\n", i); + } else + { fprintf(tc, "\t\t((P%d *)pptr(h))->_t = %d;\n", i, i); + fprintf(tc, "\t\t((P%d *)pptr(h))->_p = 0;\n", i); + fprintf(tc, "\t\treached%d[0] = 1;\n", i); + fprintf(tc, "\t\taccpstate[%d][1] = 1;\n", i); + } + fprintf(tc, "\t\tbreak;\n"); +} + +static void +put_pinit(ProcList *P) +{ Lextok *fp, *fpt, *t; + Element *e = P->s->frst; + Symbol *s = P->n; + Lextok *p = P->p; + int i = P->tn; + int ini, j, k; + + if (i == claimnr + && separate == 1) + { fprintf(tc, "\tcase %d: /* %s */\n", i, s->name); + fprintf(tc, "\t\tini_claim(%d, h);\n", i); + fprintf(tc, "\t\tbreak;\n"); + return; + } + if (i != claimnr + && separate == 2) + return; + + ini = huntele(e, e->status, -1)->seqno; + fprintf(th, "#define start%d %d\n", i, ini); + if (i == claimnr) + fprintf(th, "#define start_claim %d\n", ini); + if (i == eventmapnr) + fprintf(th, "#define start_event %d\n", ini); + + fprintf(tc, "\tcase %d: /* %s */\n", i, s->name); + + fprintf(tc, "\t\t((P%d *)pptr(h))->_t = %d;\n", i, i); + fprintf(tc, "\t\t((P%d *)pptr(h))->_p = %d;", i, ini); + fprintf(tc, " reached%d[%d]=1;\n", i, ini); + + if (has_provided) + { fprintf(tt, "\tcase %d: /* %s */\n\t\t", i, s->name); + if (P->prov) + { fprintf(tt, "if ("); + putstmnt(tt, P->prov, 0); + fprintf(tt, ")\n\t\t\t"); + } + fprintf(tt, "return 1;\n"); + if (P->prov) + fprintf(tt, "\t\tbreak;\n"); + } + + fprintf(tc, "\t\t/* params: */\n"); + for (fp = p, j=0; fp; fp = fp->rgt) + for (fpt = fp->lft; fpt; fpt = fpt->rgt, j++) + { t = (fpt->ntyp == ',') ? fpt->lft : fpt; + if (t->sym->nel != 1) + { lineno = t->ln; + Fname = t->fn; + fatal("array in parameter list, %s", + t->sym->name); + } + fprintf(tc, "\t\t((P%d *)pptr(h))->", i); + if (t->sym->type == STRUCT) + { if (full_name(tc, t, t->sym, 1)) + { lineno = t->ln; + Fname = t->fn; + fatal("hidden array in parameter %s", + t->sym->name); + } + } else + fprintf(tc, "%s", t->sym->name); + fprintf(tc, " = par%d;\n", j); + } + fprintf(tc, "\t\t/* locals: */\n"); + k = dolocal(tc, "", INIV, i, s->name); + if (k > 0) + { fprintf(tc, "#ifdef VAR_RANGES\n"); + (void) dolocal(tc, "logval(\"", LOGV, i, s->name); + fprintf(tc, "#endif\n"); + } + + fprintf(tc, "#ifdef HAS_CODE\n"); + fprintf(tc, "\t\tlocinit%d(h);\n", i); + fprintf(tc, "#endif\n"); + + dumpclaims(tc, i, s->name); + fprintf(tc, "\t break;\n"); +} + +Element * +huntstart(Element *f) +{ Element *e = f; + Element *elast = (Element *) 0; + int cnt = 0; + + while (elast != e && cnt++ < 200) /* new 4.0.8 */ + { elast = e; + if (e->n) + { if (e->n->ntyp == '.' && e->nxt) + e = e->nxt; + else if (e->n->ntyp == UNLESS) + e = e->sub->this->frst; + } } + + if (cnt >= 200 || !e) + fatal("confusing control structure", (char *) 0); + return e; +} + +Element * +huntele(Element *f, int o, int stopat) +{ Element *g, *e = f; + int cnt=0; /* a precaution against loops */ + + if (e) + for (cnt = 0; cnt < 200 && e->n; cnt++) + { + if (e->seqno == stopat) + break; + + switch (e->n->ntyp) { + case GOTO: + g = get_lab(e->n,1); + cross_dsteps(e->n, g->n); + break; + case '.': + case BREAK: + if (!e->nxt) + return e; + g = e->nxt; + break; + case UNLESS: + g = huntele(e->sub->this->frst, o, stopat); + break; + case D_STEP: + case ATOMIC: + case NON_ATOMIC: + default: + return e; + } + if ((o & ATOM) && !(g->status & ATOM)) + return e; + e = g; + } + if (cnt >= 200 || !e) + fatal("confusing control structure", (char *) 0); + return e; +} + +void +typ2c(Symbol *sp) +{ int wsbits = sizeof(long)*8; /* wordsize in bits */ + switch (sp->type) { + case UNSIGNED: + if (sp->hidden&1) + fprintf(th, "\tuchar %s;", sp->name); + else + fprintf(th, "\tunsigned %s : %d", + sp->name, sp->nbits); + LstSet = sp; + if (nBits%wsbits > 0 + && wsbits - nBits%wsbits < sp->nbits) + { /* must padd to a word-boundary */ + nBits += wsbits - nBits%wsbits; + } + nBits += sp->nbits; + break; + case BIT: + if (sp->nel == 1 && !(sp->hidden&1)) + { fprintf(th, "\tunsigned %s : 1", sp->name); + LstSet = sp; + nBits++; + break; + } /* else fall through */ + if (!(sp->hidden&1) && (verbose&32)) + printf("spin: warning: bit-array %s[%d] mapped to byte-array\n", + sp->name, sp->nel); + nBits += 8*sp->nel; /* mapped onto array of uchars */ + case MTYPE: + case BYTE: + case CHAN: /* good for up to 255 channels */ + fprintf(th, "\tuchar %s", sp->name); + LstSet = sp; + break; + case SHORT: + fprintf(th, "\tshort %s", sp->name); + LstSet = sp; + break; + case INT: + fprintf(th, "\tint %s", sp->name); + LstSet = sp; + break; + case STRUCT: + if (!sp->Snm) + fatal("undeclared structure element %s", sp->name); + fprintf(th, "\tstruct %s %s", + sp->Snm->name, + sp->name); + LstSet = ZS; + break; + case CODE_FRAG: + case PREDEF: + return; + default: + fatal("variable %s undeclared", sp->name); + } + + if (sp->nel != 1) + fprintf(th, "[%d]", sp->nel); + fprintf(th, ";\n"); +} + +static void +ncases(FILE *fd, int p, int n, int m, char *c[]) +{ int i, j; + + for (j = 0; c[j]; j++) + for (i = n; i < m; i++) + { fprintf(fd, c[j], i, p, i); + fprintf(fd, "\n"); + } +} + +void +qlen_type(int qmax) +{ + fprintf(th, "\t"); + if (qmax < 256) + fprintf(th, "uchar"); + else if (qmax < 65535) + fprintf(th, "ushort"); + else + fprintf(th, "uint"); + fprintf(th, " Qlen; /* q_size */\n"); +} + +void +genaddqueue(void) +{ char buf0[256]; + int j, qmax = 0; + Queue *q; + + ntimes(tc, 0, 1, Addq0); + if (has_io && !nqs) + fprintf(th, "#define NQS 1 /* nqs=%d, but has_io */\n", nqs); + else + fprintf(th, "#define NQS %d\n", nqs); + fprintf(th, "short q_flds[%d];\n", nqs+1); + fprintf(th, "short q_max[%d];\n", nqs+1); + + for (q = qtab; q; q = q->nxt) + if (q->nslots > qmax) + qmax = q->nslots; + + for (q = qtab; q; q = q->nxt) + { j = q->qid; + fprintf(tc, "\tcase %d: j = sizeof(Q%d);", j, j); + fprintf(tc, " q_flds[%d] = %d;", j, q->nflds); + fprintf(tc, " q_max[%d] = %d;", j, max(1,q->nslots)); + fprintf(tc, " break;\n"); + + fprintf(th, "typedef struct Q%d {\n", j); + qlen_type(qmax); /* 4.2.2 */ + fprintf(th, " uchar _t; /* q_type */\n"); + fprintf(th, " struct {\n"); + + for (j = 0; j < q->nflds; j++) + { switch (q->fld_width[j]) { + case BIT: + if (q->nflds != 1) + { fprintf(th, "\t\tunsigned"); + fprintf(th, " fld%d : 1;\n", j); + break; + } /* else fall through: smaller struct */ + case MTYPE: + case CHAN: + case BYTE: + fprintf(th, "\t\tuchar fld%d;\n", j); + break; + case SHORT: + fprintf(th, "\t\tshort fld%d;\n", j); + break; + case INT: + fprintf(th, "\t\tint fld%d;\n", j); + break; + default: + fatal("bad channel spec", ""); + } + } + fprintf(th, " } contents[%d];\n", max(1, q->nslots)); + fprintf(th, "} Q%d;\n", q->qid); + } + + fprintf(th, "typedef struct Q0 {\t/* generic q */\n"); + qlen_type(qmax); /* 4.2.2 */ + fprintf(th, " uchar _t;\n"); + fprintf(th, "} Q0;\n"); + + ntimes(tc, 0, 1, Addq1); + + if (has_random) + { fprintf(th, "int Q_has(int"); + for (j = 0; j < Mpars; j++) + fprintf(th, ", int, int"); + fprintf(th, ");\n"); + + fprintf(tc, "int\nQ_has(int into"); + for (j = 0; j < Mpars; j++) + fprintf(tc, ", int want%d, int fld%d", j, j); + fprintf(tc, ")\n"); + fprintf(tc, "{ int i;\n\n"); + fprintf(tc, " if (!into--)\n"); + fprintf(tc, " uerror(\"ref to unknown chan "); + fprintf(tc, "(recv-poll)\");\n\n"); + fprintf(tc, " if (into >= now._nr_qs || into < 0)\n"); + fprintf(tc, " Uerror(\"qrecv bad queue#\");\n\n"); + fprintf(tc, " for (i = 0; i < ((Q0 *)qptr(into))->Qlen;"); + fprintf(tc, " i++)\n"); + fprintf(tc, " {\n"); + for (j = 0; j < Mpars; j++) + { fprintf(tc, " if (want%d && ", j); + fprintf(tc, "qrecv(into+1, i, %d, 0) != fld%d)\n", + j, j); + fprintf(tc, " continue;\n"); + } + fprintf(tc, " return i+1;\n"); + fprintf(tc, " }\n"); + fprintf(tc, " return 0;\n"); + fprintf(tc, "}\n"); + } + + fprintf(tc, "#if NQS>0\n"); + fprintf(tc, "void\nqsend(int into, int sorted"); + for (j = 0; j < Mpars; j++) + fprintf(tc, ", int fld%d", j); + fprintf(tc, ")\n"); + ntimes(tc, 0, 1, Addq11); + + for (q = qtab; q; q = q->nxt) + { sprintf(buf0, "((Q%d *)z)->", q->qid); + fprintf(tc, "\tcase %d:%s\n", q->qid, + (q->nslots)?"":" /* =rv= */"); + if (q->nslots == 0) /* reset handshake point */ + fprintf(tc, "\t\t(trpt+2)->o_m = 0;\n"); + + if (has_sorted) + { fprintf(tc, "\t\tif (!sorted) goto append%d;\n", q->qid); + fprintf(tc, "\t\tfor (j = 0; j < %sQlen; j++)\n", buf0); + fprintf(tc, "\t\t{\t/* find insertion point */\n"); + sprintf(buf0, "((Q%d *)z)->contents[j].fld", q->qid); + for (j = 0; j < q->nflds; j++) + { fprintf(tc, "\t\t\tif (fld%d > %s%d) continue;\n", + j, buf0, j); + fprintf(tc, "\t\t\tif (fld%d < %s%d) ", j, buf0, j); + fprintf(tc, "goto found%d;\n\n", q->qid); + } + fprintf(tc, "\t\t}\n"); + fprintf(tc, "\tfound%d:\n", q->qid); + sprintf(buf0, "((Q%d *)z)->", q->qid); + fprintf(tc, "\t\tfor (k = %sQlen - 1; k >= j; k--)\n", buf0); + fprintf(tc, "\t\t{\t/* shift up */\n"); + for (j = 0; j < q->nflds; j++) + { fprintf(tc, "\t\t\t%scontents[k+1].fld%d = ", + buf0, j); + fprintf(tc, "%scontents[k].fld%d;\n", + buf0, j); + } + fprintf(tc, "\t\t}\n"); + fprintf(tc, "\tappend%d:\t/* insert in slot j */\n", q->qid); + } + + fprintf(tc, "#ifdef HAS_SORTED\n"); + fprintf(tc, "\t\t(trpt+1)->ipt = j;\n"); /* ipt was bup.oval */ + fprintf(tc, "#endif\n"); + fprintf(tc, "\t\t%sQlen = %sQlen + 1;\n", buf0, buf0); + sprintf(buf0, "((Q%d *)z)->contents[j].fld", q->qid); + for (j = 0; j < q->nflds; j++) + fprintf(tc, "\t\t%s%d = fld%d;\n", buf0, j, j); + fprintf(tc, "\t\tbreak;\n"); + } + ntimes(tc, 0, 1, Addq2); + + for (q = qtab; q; q = q->nxt) + fprintf(tc, "\tcase %d: return %d;\n", q->qid, (!q->nslots)); + + ntimes(tc, 0, 1, Addq3); + + for (q = qtab; q; q = q->nxt) + fprintf(tc, "\tcase %d: return (q_sz(from) == %d);\n", + q->qid, max(1, q->nslots)); + + ntimes(tc, 0, 1, Addq4); + for (q = qtab; q; q = q->nxt) + { sprintf(buf0, "((Q%d *)z)->", q->qid); + fprintf(tc, " case %d:%s\n\t\t", + q->qid, (q->nslots)?"":" /* =rv= */"); + if (q->nflds == 1) + { fprintf(tc, "if (fld == 0) r = %s", buf0); + fprintf(tc, "contents[slot].fld0;\n"); + } else + { fprintf(tc, "switch (fld) {\n"); + ncases(tc, q->qid, 0, q->nflds, R12); + fprintf(tc, "\t\tdefault: Uerror"); + fprintf(tc, "(\"too many fields in recv\");\n"); + fprintf(tc, "\t\t}\n"); + } + fprintf(tc, "\t\tif (done)\n"); + if (q->nslots == 0) + { fprintf(tc, "\t\t{ j = %sQlen - 1;\n", buf0); + fprintf(tc, "\t\t %sQlen = 0;\n", buf0); + sprintf(buf0, "\t\t\t((Q%d *)z)->contents", q->qid); + } else + { fprintf(tc, "\t\t{ j = %sQlen;\n", buf0); + fprintf(tc, "\t\t %sQlen = --j;\n", buf0); + fprintf(tc, "\t\t for (k=slot; k<j; k++)\n"); + fprintf(tc, "\t\t {\n"); + sprintf(buf0, "\t\t\t((Q%d *)z)->contents", q->qid); + for (j = 0; j < q->nflds; j++) + { fprintf(tc, "\t%s[k].fld%d = \n", buf0, j); + fprintf(tc, "\t\t%s[k+1].fld%d;\n", buf0, j); + } + fprintf(tc, "\t\t }\n"); + } + + for (j = 0; j < q->nflds; j++) + fprintf(tc, "%s[j].fld%d = 0;\n", buf0, j); + fprintf(tc, "\t\t\tif (fld+1 != %d)\n\t\t\t", q->nflds); + fprintf(tc, "\tuerror(\"missing pars in receive\");\n"); + /* incompletely received msgs cannot be unrecv'ed */ + fprintf(tc, "\t\t}\n"); + fprintf(tc, "\t\tbreak;\n"); + } + ntimes(tc, 0, 1, Addq5); + for (q = qtab; q; q = q->nxt) + fprintf(tc, " case %d: j = sizeof(Q%d); break;\n", + q->qid, q->qid); + ntimes(tc, 0, 1, R8b); + + ntimes(th, 0, 1, Proto); /* tag on function prototypes */ + fprintf(th, "void qsend(int, int"); + for (j = 0; j < Mpars; j++) + fprintf(th, ", int"); + fprintf(th, ");\n"); + + fprintf(th, "#define Addproc(x) addproc(x"); + for (j = 0; j < Npars; j++) + fprintf(th, ", 0"); + fprintf(th, ")\n"); +} diff --git a/sys/src/cmd/spin/pangen1.h b/sys/src/cmd/spin/pangen1.h new file mode 100755 index 000000000..81facb646 --- /dev/null +++ b/sys/src/cmd/spin/pangen1.h @@ -0,0 +1,5139 @@ +/***** spin: pangen1.h *****/ + +/* Copyright (c) 1989-2005 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +static char *Code2a[] = { /* the tail of procedure run() */ + "#if defined(VERI) && !defined(NOREDUCE) && !defined(NP)", + " if (!state_tables", + "#ifdef HAS_CODE", + " && !readtrail", + "#endif", + " )", + " { printf(\"warning: for p.o. reduction to be valid \");", + " printf(\"the never claim must be stutter-invariant\\n\");", + " printf(\"(never claims generated from LTL \");", + " printf(\"formulae are stutter-invariant)\\n\");", + " }", + "#endif", + " UnBlock; /* disable rendez-vous */", + "#ifdef BITSTATE", +#ifndef POWOW + " if (udmem)", + " { udmem *= 1024L*1024L;", + " SS = (uchar *) emalloc(udmem);", + " bstore = bstore_mod;", + " } else", +#endif + " SS = (uchar *) emalloc(1L<<(ssize-3));", + "#else", + " hinit();", + "#endif", + "#if defined(FULLSTACK) && defined(BITSTATE)", + " onstack_init();", + "#endif", + "#if defined(CNTRSTACK) && !defined(BFS)", + " LL = (uchar *) emalloc(1L<<(ssize-3));", + "#endif", + " stack = ( Stack *) emalloc(sizeof(Stack));", + " svtack = (Svtack *) emalloc(sizeof(Svtack));", + " /* a place to point for Pptr of non-running procs: */", + " noptr = (uchar *) emalloc(Maxbody * sizeof(char));", + "#ifdef SVDUMP", + " if (vprefix > 0)", + " write(svfd, (uchar *) &vprefix, sizeof(int));", + "#endif", + "#ifdef VERI", + " Addproc(VERI); /* never - pid = 0 */", + "#endif", + " active_procs(); /* started after never */", + "#ifdef EVENT_TRACE", + " now._event = start_event;", + " reached[EVENT_TRACE][start_event] = 1;", + "#endif", + + "#ifdef HAS_CODE", + " globinit();", + "#endif", + "#ifdef BITSTATE", + "go_again:", + "#endif", + " do_the_search();", + "#ifdef BITSTATE", + " if (--Nrun > 0 && HASH_CONST[++HASH_NR])", + " { printf(\"Run %%d:\\n\", HASH_NR);", + " wrap_stats();", + " printf(\"\\n\");", + " memset(SS, 0, 1L<<(ssize-3));", + "#if defined(CNTRSTACK)", + " memset(LL, 0, 1L<<(ssize-3));", + "#endif", + "#if defined(FULLSTACK)", + " memset((uchar *) S_Tab, 0, ", + " maxdepth*sizeof(struct H_el *));", + "#endif", + " nstates=nlinks=truncs=truncs2=ngrabs = 0;", + " nlost=nShadow=hcmp = 0;", + " Fa=Fh=Zh=Zn = 0;", + " PUT=PROBE=ZAPS=Ccheck=Cholds = 0;", + " goto go_again;", + " }", + "#endif", + "}", + "#ifdef HAS_PROVIDED", + "int provided(int, uchar, int, Trans *);", + "#endif", + +#ifndef PRINTF + "int", + "Printf(const char *fmt, ...)", + "{ /* Make sure the args to Printf", + " * are always evaluated (e.g., they", + " * could contain a run stmnt)", + " * but do not generate the output", + " * during verification runs", + " * unless explicitly wanted", + " * If this fails on your system", + " * compile SPIN itself -DPRINTF", + " * and this code is not generated", + " */", + "#ifdef HAS_CODE", + " if (readtrail)", + " { va_list args;", + " va_start(args, fmt);", + " vprintf(fmt, args);", + " va_end(args);", + " return 1;", + " }", + "#endif", + "#ifdef PRINTF", + " va_list args;", + " va_start(args, fmt);", + " vprintf(fmt, args);", + " va_end(args);", + "#endif", + " return 1;", + "}", +#endif + "extern void printm(int);", + + "#ifndef SC", + "#define getframe(i) &trail[i];", + "#else", + "static long HHH, DDD, hiwater;", + "static long CNT1, CNT2;", + "static int stackwrite;", + "static int stackread;", + "static Trail frameptr;", + "Trail *", + "getframe(int d)", + "{", + " if (CNT1 == CNT2)", + " return &trail[d];", + "", + " if (d >= (CNT1-CNT2)*DDD)", + " return &trail[d - (CNT1-CNT2)*DDD];", + "", + " if (!stackread", + " && (stackread = open(stackfile, 0)) < 0)", + " { printf(\"getframe: cannot open %%s\\n\", stackfile);", + " wrapup();", + " }", + " if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1", + " || read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail))", + " { printf(\"getframe: frame read error\\n\");", + " wrapup();", + " }", + " return &frameptr;", + "}", + "#endif", + + "#if !defined(SAFETY) && !defined(BITSTATE)", + "#if !defined(FULLSTACK) || defined(MA)", + "#define depth_of(x) A_depth /* an estimate */", + "#else", + "int", + "depth_of(struct H_el *s)", + "{ Trail *t; int d;", + " for (d = 0; d <= A_depth; d++)", + " { t = getframe(d);", + " if (s == t->ostate)", + " return d;", + " }", + " printf(\"pan: cannot happen, depth_of\\n\");", + " return depthfound;", + "}", + "#endif", + "#endif", + + "void", + "pan_exit(int val)", + "{ if (signoff) printf(\"--end of output--\\n\");", + " exit(val);", + "}", + + "#ifdef HAS_CODE", + "char *", + "transmognify(char *s)", + "{ char *v, *w;", + " static char buf[2][2048];", + " int i, toggle = 0;", + + " if (!s || strlen(s) > 2047) return s;", + " memset(buf[0], 0, 2048);", + " memset(buf[1], 0, 2048);", + " strcpy(buf[toggle], s);", + " while ((v = strstr(buf[toggle], \"{c_code\")))", /* assign v */ + " { *v = '\\0'; v++;", + " strcpy(buf[1-toggle], buf[toggle]);", + " for (w = v; *w != '}' && *w != '\\0'; w++) /* skip */;", + " if (*w != '}') return s;", + " *w = '\\0'; w++;", + " for (i = 0; code_lookup[i].c; i++)", + " if (strcmp(v, code_lookup[i].c) == 0", + " && strlen(v) == strlen(code_lookup[i].c))", + " { if (strlen(buf[1-toggle])", + " + strlen(code_lookup[i].t)", + " + strlen(w) > 2047)", + " return s;", + " strcat(buf[1-toggle], code_lookup[i].t);", + " break;", + " }", + " strcat(buf[1-toggle], w);", + " toggle = 1 - toggle;", + " }", + " buf[toggle][2047] = '\\0';", + " return buf[toggle];", + "}", + "#else", + "char * transmognify(char *s) { return s; }", + "#endif", + + "#ifdef HAS_CODE", + "void", + "add_src_txt(int ot, int tt)", + "{ Trans *t;", + " char *q;", + "", + " for (t = trans[ot][tt]; t; t = t->nxt)", + " { printf(\"\\t\\t\");", + + " q = transmognify(t->tp);", + " for ( ; q && *q; q++)", + " if (*q == '\\n')", + " printf(\"\\\\n\");", + " else", + " putchar(*q);", + " printf(\"\\n\");", + " }", + "}", + "void", + "wrap_trail(void)", + "{ static int wrap_in_progress = 0;", + " int i; short II;", + " P0 *z;", + "", + " if (wrap_in_progress++) return;", + "", + " printf(\"spin: trail ends after %%ld steps\\n\", depth);", + " if (onlyproc >= 0)", + " { if (onlyproc >= now._nr_pr) pan_exit(0);", + " II = onlyproc;", + " z = (P0 *)pptr(II);", + " printf(\"%%3ld:\tproc %%d (%%s) \",", + " depth, II, procname[z->_t]);", + " for (i = 0; src_all[i].src; i++)", + " if (src_all[i].tp == (int) z->_t)", + " { printf(\" line %%3d\",", + " src_all[i].src[z->_p]);", + " break;", + " }", + " printf(\" (state %%2d)\", z->_p);", + " if (!stopstate[z->_t][z->_p])", + " printf(\" (invalid end state)\");", + " printf(\"\\n\");", + " add_src_txt(z->_t, z->_p);", + " pan_exit(0);", + " }", + " printf(\"#processes %%d:\\n\", now._nr_pr);", + " if (depth < 0) depth = 0;", + " for (II = 0; II < now._nr_pr; II++)", + " { z = (P0 *)pptr(II);", + " printf(\"%%3ld:\tproc %%d (%%s) \",", + " depth, II, procname[z->_t]);", + " for (i = 0; src_all[i].src; i++)", + " if (src_all[i].tp == (int) z->_t)", + " { printf(\" line %%3d\",", + " src_all[i].src[z->_p]);", + " break;", + " }", + " printf(\" (state %%2d)\", z->_p);", + " if (!stopstate[z->_t][z->_p])", + " printf(\" (invalid end state)\");", + " printf(\"\\n\");", + " add_src_txt(z->_t, z->_p);", + " }", + " c_globals();", + " for (II = 0; II < now._nr_pr; II++)", + " { z = (P0 *)pptr(II);", + " c_locals(II, z->_t);", + " }", + "#ifdef ON_EXIT", + " ON_EXIT;", + "#endif", + " pan_exit(0);", + "}", + "FILE *", + "findtrail(void)", + "{ FILE *fd;", + " char fnm[512], *q;", + " char MyFile[512];", + "", + " strcpy(MyFile, TrailFile);", /* avoid problem with non-writable strings */ + "", + " if (whichtrail)", + " { sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, tprefix);", + " fd = fopen(fnm, \"r\");", + " if (fd == NULL && (q = strchr(MyFile, \'.\')))", + " { *q = \'\\0\';", /* e.g., strip .pml on original file */ + " sprintf(fnm, \"%%s%%d.%%s\", MyFile, whichtrail, tprefix);", + " *q = \'.\';", + " fd = fopen(fnm, \"r\");", + " if (fd == NULL)", + " { printf(\"pan: cannot find %%s%%d.%%s or %%s\\n\", ", + " MyFile, whichtrail, tprefix, fnm);", + " pan_exit(1);", + " } }", + " } else", + " { sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);", + " fd = fopen(fnm, \"r\");", + " if (fd == NULL && (q = strchr(MyFile, \'.\')))", + " { *q = \'\\0\';", /* e.g., strip .pml on original file */ + " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);", + " *q = \'.\';", + " fd = fopen(fnm, \"r\");", + " if (fd == NULL)", + " { printf(\"pan: cannot find %%s.%%s or %%s\\n\", ", + " MyFile, tprefix, fnm);", + " pan_exit(1);", + " } } }", + " if (fd == NULL)", + " { printf(\"pan: cannot find trailfile %%s\\n\", fnm);", + " pan_exit(1);", + " }", + " return fd;", + "}", + "", + "uchar do_transit(Trans *, short);", + "", + "void", + "getrail(void)", + "{ FILE *fd;", + " char *q;", + " int i, t_id, lastnever=-1; short II;", + " Trans *t;", + " P0 *z;", + "", + " fd = findtrail(); /* exits if unsuccessful */", + " while (fscanf(fd, \"%%ld:%%d:%%d\\n\", &depth, &i, &t_id) == 3)", + " { if (depth == -1)", + " printf(\"<<<<<START OF CYCLE>>>>>\\n\");", + " if (depth < 0)", + " continue;", + " if (i > now._nr_pr)", + " { printf(\"pan: Error, proc %%d invalid pid \", i);", + " printf(\"transition %%d\\n\", t_id);", + " break;", + " }", + " II = i;", + "", + " z = (P0 *)pptr(II);", + " for (t = trans[z->_t][z->_p]; t; t = t->nxt)", + " if (t->t_id == t_id)", + " break;", + " if (!t)", + " { for (i = 0; i < NrStates[z->_t]; i++)", + " { t = trans[z->_t][i];", + " if (t && t->t_id == t_id)", + " { printf(\" Recovered at state %%d\\n\", i);", + " z->_p = i;", + " goto recovered;", + " } }", + " printf(\"pan: Error, proc %%d type %%d state %%d: \",", + " II, z->_t, z->_p);", + " printf(\"transition %%d not found\\n\", t_id);", + " for (t = trans[z->_t][z->_p]; t; t = t->nxt)", + " printf(\" t_id %%d -- case %%d, [%%s]\\n\",", + " t->t_id, t->forw, t->tp);", + " break; /* pan_exit(1); */", + " }", + "recovered:", + " q = transmognify(t->tp);", + " if (gui) simvals[0] = \'\\0\';", + + " this = pptr(II);", + " trpt->tau |= 1;", /* timeout always possible */ + " if (!do_transit(t, II))", + " { if (onlyproc >= 0 && II != onlyproc)", + " goto moveon;", + " printf(\"pan: error, next transition UNEXECUTABLE on replay\\n\");", + " printf(\" most likely causes: missing c_track statements\\n\");", + " printf(\" or illegal side-effects in c_expr statements\\n\");", + " }", + + " if (onlyproc >= 0 && II != onlyproc)", + " goto moveon;", + + " if (verbose)", + " { printf(\"depth: %%3ld proc: %%3d trans: %%3d (%%d procs) \",", + " depth, II, t_id, now._nr_pr);", + " printf(\"forw=%%3d [%%s]\\n\", t->forw, q);", + "", + " c_globals();", + " for (i = 0; i < now._nr_pr; i++)", + " { c_locals(i, ((P0 *)pptr(i))->_t);", + " }", + " } else", + " if (strcmp(procname[z->_t], \":never:\") == 0)", + " { if (lastnever != (int) z->_p)", + " { for (i = 0; src_all[i].src; i++)", + " if (src_all[i].tp == (int) z->_t)", + " { printf(\"MSC: ~G %%d\\n\",", + " src_all[i].src[z->_p]);", + " break;", + " }", + " if (!src_all[i].src)", + " printf(\"MSC: ~R %%d\\n\", z->_p);", + " }", + " lastnever = z->_p;", + " goto sameas;", + " } else", + " if (strcmp(procname[z->_t], \":np_:\") != 0)", + " {", + "sameas: if (no_rck) goto moveon;", + " if (coltrace)", + " { printf(\"%%ld: \", depth);", + " for (i = 0; i < II; i++)", + " printf(\"\\t\\t\");", + " printf(\"%%s(%%d):\", procname[z->_t], II);", + " printf(\"[%%s]\\n\", q?q:\"\");", + " } else if (!silent)", + " { if (strlen(simvals) > 0) {", + " printf(\"%%3ld: proc %%2d (%%s)\", ", + " depth, II, procname[z->_t]);", + " for (i = 0; src_all[i].src; i++)", + " if (src_all[i].tp == (int) z->_t)", + " { printf(\" line %%3d \\\"pan_in\\\" \",", + " src_all[i].src[z->_p]);", + " break;", + " }", + " printf(\"(state %%d)\t[values: %%s]\\n\", z->_p, simvals);", + " }", + " printf(\"%%3ld: proc %%2d (%%s)\", ", + " depth, II, procname[z->_t]);", + " for (i = 0; src_all[i].src; i++)", + " if (src_all[i].tp == (int) z->_t)", + " { printf(\" line %%3d \\\"pan_in\\\" \",", + " src_all[i].src[z->_p]);", + " break;", + " }", + " printf(\"(state %%d)\t[%%s]\\n\", z->_p, q?q:\"\");", + " printf(\"\\n\");", + " } }", + "moveon: z->_p = t->st;", + " }", + " wrap_trail();", + "}", + "#endif", + "int", + "f_pid(int pt)", + "{ int i;", + " P0 *z;", + " for (i = 0; i < now._nr_pr; i++)", + " { z = (P0 *)pptr(i);", + " if (z->_t == (unsigned) pt)", + " return BASE+z->_pid;", + " }", + " return -1;", + "}", + "#ifdef VERI", + "void check_claim(int);", + "#endif", + "", + "#ifdef BITSTATE", +#ifndef POWOW + "int", + "bstore_mod(char *v, int n) /* hasharray size not a power of two */", + "{ unsigned long x, y;", + " unsigned int i = 1;", + "", + " d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */", + " x = K2; y = j3;", + " for (;;)", + " { if (!(SS[x%%udmem]&(1<<y))) break;", /* take the hit in speed */ + " if (i == hfns) {", + "#ifdef DEBUG", + " printf(\"Old bitstate\\n\");", + "#endif", + " return 1;", + " }", + " x = (x + K1 + i);", /* no mask, using mod */ + " y = (y + j4) & 7;", + " i++;", + " }", + "#ifdef RANDSTOR", + " if (rand()%%100 > RANDSTOR) return 0;", + "#endif", + " for (;;)", + " { SS[x%%udmem] |= (1<<y);", + " if (i == hfns) break;", /* done */ + " x = (x + K1 + i);", /* no mask */ + " y = (y + j4) & 7;", + " i++;", + " }", + "#ifdef DEBUG", + " printf(\"New bitstate\\n\");", + "#endif", + " if (now._a_t&1)", + " { nShadow++;", + " }", + " return 0;", + "}", +#endif + "int", + "bstore_reg(char *v, int n) /* extended hashing, Peter Dillinger, 2004 */", + "{ unsigned long x, y;", + " unsigned int i = 1;", + "", + " d_hash((uchar *) v, n); /* sets j1-j4 */", + " x = j2; y = j3;", + " for (;;)", + " { if (!(SS[x]&(1<<y))) break;", /* at least one bit not set */ + " if (i == hfns) {", + "#ifdef DEBUG", + " printf(\"Old bitstate\\n\");", + "#endif", + " return 1;", + " }", + " x = (x + j1 + i) & nmask;", + " y = (y + j4) & 7;", + " i++;", + " }", + "#ifdef RANDSTOR", + " if (rand()%%100 > RANDSTOR) return 0;", + "#endif", + " for (;;)", + " { SS[x] |= (1<<y);", + " if (i == hfns) break;", /* done */ + " x = (x + j1 + i) & nmask;", + " y = (y + j4) & 7;", + " i++;", + " }", + "#ifdef DEBUG", + " printf(\"New bitstate\\n\");", + "#endif", + " if (now._a_t&1)", + " { nShadow++;", + " }", + " return 0;", + "}", + "#endif", + "unsigned long TMODE = 0666; /* file permission bits for trail files */", + "", + "int trcnt=1;", + "char snap[64], fnm[512];", + "", + "int", + "make_trail(void)", + "{ int fd;", + " char *q;", + " char MyFile[512];", + "", + " q = strrchr(TrailFile, \'/\');", + " if (q == NULL) q = TrailFile; else q++;", + " strcpy(MyFile, q); /* TrailFile is not a writable string */", + "", + " if (iterative == 0 && Nr_Trails++ > 0)", + " sprintf(fnm, \"%%s%%d.%%s\",", + " MyFile, Nr_Trails-1, tprefix);", + " else", + " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);", + "", + " if ((fd = creat(fnm, TMODE)) < 0)", + " { if ((q = strchr(MyFile, \'.\')))", + " { *q = \'\\0\';", /* strip .pml */ + " if (iterative == 0 && Nr_Trails-1 > 0)", + " sprintf(fnm, \"%%s%%d.%%s\",", + " MyFile, Nr_Trails-1, tprefix);", + " else", + " sprintf(fnm, \"%%s.%%s\", MyFile, tprefix);", + " *q = \'.\';", + " fd = creat(fnm, TMODE);", + " } }", + " if (fd < 0)", + " { printf(\"pan: cannot create %%s\\n\", fnm);", + " perror(\"cause\");", + " } else", + " { printf(\"pan: wrote %%s\\n\", fnm);", + " }", + " return fd;", + "}", + 0 +}; + +static char *Code2b[] = { /* breadth-first search option */ + "#ifdef BFS", + "#define Q_PROVISO", + "#ifndef INLINE_REV", + "#define INLINE_REV", + "#endif", + "", + "typedef struct SV_Hold {", + " State *sv;", + " int sz;", + " struct SV_Hold *nxt;", + "} SV_Hold;", + "", + "typedef struct EV_Hold {", + " char *sv;", /* Mask */ + " int sz;", /* vsize */ + " int nrpr;", + " int nrqs;", + "#if VECTORSZ>32000", + " int *po;", + "#else", + " short *po;", + "#endif", + " int *qo;", + " uchar *ps, *qs;", + " struct EV_Hold *nxt;", + "} EV_Hold;", + "", + "typedef struct BFS_Trail {", + " Trail *frame;", + " SV_Hold *onow;", + " EV_Hold *omask;", + "#ifdef Q_PROVISO", + " struct H_el *lstate;", + "#endif", + " short boq;", + " struct BFS_Trail *nxt;", + "} BFS_Trail;", + "", + "BFS_Trail *bfs_trail, *bfs_bot, *bfs_free;", + "", + "SV_Hold *svhold, *svfree;", + "", + "uchar do_reverse(Trans *, short, uchar);", + "void snapshot(void);", + "", + "SV_Hold *", + "getsv(int n)", + "{ SV_Hold *h = (SV_Hold *) 0, *oh;", + "", + " oh = (SV_Hold *) 0;", + " for (h = svfree; h; oh = h, h = h->nxt)", + " { if (n == h->sz)", + " { if (!oh)", + " svfree = h->nxt;", + " else", + " oh->nxt = h->nxt;", + " h->nxt = (SV_Hold *) 0;", + " break;", + " }", + " if (n < h->sz)", + " { h = (SV_Hold *) 0;", + " break;", + " }", + " /* else continue */", + " }", + "", + " if (!h)", + " { h = (SV_Hold *) emalloc(sizeof(SV_Hold));", + " h->sz = n;", + " h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n);", + " }", + " return h;", + "}", + "", + "EV_Hold *", + "getsv_mask(int n)", + "{ EV_Hold *h;", + " static EV_Hold *kept = (EV_Hold *) 0;", + "", + " for (h = kept; h; h = h->nxt)", + " if (n == h->sz", + " && (memcmp((char *) Mask, (char *) h->sv, n) == 0)", + " && (now._nr_pr == h->nrpr)", + " && (now._nr_qs == h->nrqs)", + "#if VECTORSZ>32000", + " && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0)", + " && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0)", + "#else", + " && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0)", + " && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0)", + "#endif", + " && (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0)", + " && (memcmp((char *) q_skip, (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0))", + " break;", + " if (!h)", + " { h = (EV_Hold *) emalloc(sizeof(EV_Hold));", + " h->sz = n;", + " h->nrpr = now._nr_pr;", + " h->nrqs = now._nr_qs;", + "", + " h->sv = (char *) emalloc(n * sizeof(char));", + " memcpy((char *) h->sv, (char *) Mask, n);", + "", + " if (now._nr_pr > 0)", + " { h->po = (int *) emalloc(now._nr_pr * sizeof(int));", + " h->ps = (int *) emalloc(now._nr_pr * sizeof(int));", + "#if VECTORSZ>32000", + " memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int));", + "#else", + " memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short));", + "#endif", + " memcpy((char *) h->ps, (char *) proc_skip, now._nr_pr * sizeof(uchar));", + " }", + " if (now._nr_qs > 0)", + " { h->qo = (int *) emalloc(now._nr_qs * sizeof(int));", + " h->qs = (int *) emalloc(now._nr_qs * sizeof(int));", + "#if VECTORSZ>32000", + " memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int));", + "#else", + " memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short));", + "#endif", + " memcpy((char *) h->qs, (char *) q_skip, now._nr_qs * sizeof(uchar));", + " }", + "", + " h->nxt = kept;", + " kept = h;", + " }", + " return h;", + "}", + "", + "void", + "freesv(SV_Hold *p)", + "{ SV_Hold *h, *oh;", + "", + " oh = (SV_Hold *) 0;", + " for (h = svfree; h; oh = h, h = h->nxt)", + " if (h->sz >= p->sz)", + " break;", + "", + " if (!oh)", + " { p->nxt = svfree;", + " svfree = p;", + " } else", + " { p->nxt = h;", + " oh->nxt = p;", + " }", + "}", + "", + "BFS_Trail *", + "get_bfs_frame(void)", + "{ BFS_Trail *t;", + "", + " if (bfs_free)", + " { t = bfs_free;", + " bfs_free = bfs_free->nxt;", + " t->nxt = (BFS_Trail *) 0;", + " } else", + " { t = (BFS_Trail *) emalloc(sizeof(BFS_Trail));", + " }", + " t->frame = (Trail *) emalloc(sizeof(Trail));", /* always new */ + " return t;", + "}", + "", + "void", + "push_bfs(Trail *f, int d)", + "{ BFS_Trail *t;", + "", + " t = get_bfs_frame();", + " memcpy((char *)t->frame, (char *)f, sizeof(Trail));", + " t->frame->o_tt = d; /* depth */", + "", + " t->boq = boq;", + " t->onow = getsv(vsize);", + " memcpy((char *)t->onow->sv, (char *)&now, vsize);", + " t->omask = getsv_mask(vsize);", + "#if defined(FULLSTACK) && defined(Q_PROVISO)", + " t->lstate = Lstate;", + "#endif", + " if (!bfs_bot)", + " { bfs_bot = bfs_trail = t;", + " } else", + " { bfs_bot->nxt = t;", + " bfs_bot = t;", + " }", + "#ifdef CHECK", + " printf(\"PUSH %%u (%%d)\\n\", t->frame, d);", + "#endif", + "}", + "", + "Trail *", + "pop_bfs(void)", + "{ BFS_Trail *t;", + "", + " if (!bfs_trail)", + " return (Trail *) 0;", + "", + " t = bfs_trail;", + " bfs_trail = t->nxt;", + " if (!bfs_trail)", + " bfs_bot = (BFS_Trail *) 0;", + "#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE)", + " if (t->lstate) t->lstate->tagged = 0;", + "#endif", + "", + " t->nxt = bfs_free;", + " bfs_free = t;", + "", + " vsize = t->onow->sz;", + " boq = t->boq;", + "", + " memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize);", + " memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize);", + + " if (now._nr_pr > 0)", + "#if VECTORSZ>32000", + " { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int));", + "#else", + " { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short));", + "#endif", + " memcpy((char *)proc_skip, (char *)t->omask->ps, now._nr_pr * sizeof(uchar));", + " }", + " if (now._nr_qs > 0)", + "#if VECTORSZ>32000", + " { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int));", + "#else", + " { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short));", + "#endif", + " memcpy((uchar *)q_skip, (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar));", + " }", + + " freesv(t->onow); /* omask not freed */", + "#ifdef CHECK", + " printf(\"POP %%u (%%d)\\n\", t->frame, t->frame->o_tt);", + "#endif", + " return t->frame;", + "}", + "", + "void", + "store_state(Trail *ntrpt, int shortcut, short oboq)", + "{", + "#ifdef VERI", + " Trans *t2 = (Trans *) 0;", + " uchar ot; int tt, E_state;", + " uchar o_opm = trpt->o_pm, *othis = this;", + "", + " if (shortcut)", + " {", + "#ifdef VERBOSE", + " printf(\"claim: shortcut\\n\");", + "#endif", + " goto store_it; /* no claim move */", + " }", + "", + " this = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */", + " trpt->o_pm = 0;", /* to interpret else in never claim */ + "", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + "", + "#ifdef HAS_UNLESS", + " E_state = 0;", + "#endif", + " for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0)", + " {", + "#ifdef HAS_UNLESS", + " if (E_state > 0", + " && E_state != t2->e_trans)", + " break;", + "#endif", + " if (do_transit(t2, 0))", + " {", + "#ifdef VERBOSE", + " if (!reached[ot][t2->st])", + " printf(\"depth: %%d -- claim move from %%d -> %%d\\n\",", + " trpt->o_tt, ((P0 *)this)->_p, t2->st);", + "#endif", + "#ifdef HAS_UNLESS", + " E_state = t2->e_trans;", + "#endif", + " if (t2->st > 0)", + " { ((P0 *)this)->_p = t2->st;", + " reached[ot][t2->st] = 1;", + "#ifndef NOCLAIM", + " check_claim(t2->st);", + "#endif", + " }", + " if (now._nr_pr == 0) /* claim terminated */", + " uerror(\"end state in claim reached\");", + "", + "#ifdef PEG", + " peg[t2->forw]++;", + "#endif", + " trpt->o_pm |= 1;", + " if (t2->atom&2)", /* atomic in claim */ + " Uerror(\"atomic in claim not supported in BFS mode\");", + "store_it:", + "", + "#endif", /* VERI */ + "", + "#ifdef BITSTATE", + " if (!bstore((char *)&now, vsize))", + "#else", + "#ifdef MA", + " if (!gstore((char *)&now, vsize, 0))", + "#else", + " if (!hstore((char *)&now, vsize))", + "#endif", + "#endif", + " { nstates++;", + "#ifndef NOREDUCE", + " trpt->tau |= 64;", /* succ definitely outside stack */ + "#endif", + "#if SYNC", + " if (boq != -1)", + " midrv++;", + " else if (oboq != -1)", + " { Trail *x;", + " x = (Trail *) trpt->ostate; /* pre-rv state */", + " if (x) x->o_pm |= 4; /* mark success */", + " }", + "#endif", + " push_bfs(ntrpt, trpt->o_tt+1);", + " } else", + " { truncs++;", + + "#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO)", + "#if !defined(QLIST) && !defined(BITSTATE)", + " if (Lstate && Lstate->tagged) trpt->tau |= 64;", + "#else", + " if (trpt->tau&32)", + " { BFS_Trail *tprov;", + " for (tprov = bfs_trail; tprov; tprov = tprov->nxt)", + " if (!memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize))", + " { trpt->tau |= 64;", + " break; /* state is in queue */", + " } }", + "#endif", + "#endif", + " }", + "#ifdef VERI", + " ((P0 *)this)->_p = tt; /* reset claim */", + " if (t2)", + " do_reverse(t2, 0, 0);", + " else", + " break;", + " } }", + " this = othis;", + " trpt->o_pm = o_opm;", + "#endif", + "}", + "", + "Trail *ntrpt;", /* 4.2.8 */ + "", + "void", + "bfs(void)", + "{ Trans *t; Trail *otrpt, *x;", + " uchar _n, _m, ot, nps = 0;", + " int tt, E_state;", + " short II, From = (short) (now._nr_pr-1), To = BASE;", + " short oboq = boq;", + "", + " ntrpt = (Trail *) emalloc(sizeof(Trail));", + " trpt->ostate = (struct H_el *) 0;", + " trpt->tau = 0;", + "", + " trpt->o_tt = -1;", + " store_state(ntrpt, 0, oboq); /* initial state */", + "", + " while ((otrpt = pop_bfs())) /* also restores now */", + " { memcpy((char *) trpt, (char *) otrpt, sizeof(Trail));", + "#if defined(C_States) && (HAS_TRACK==1)", + " c_revert((uchar *) &(now.c_state[0]));", + "#endif", + " if (trpt->o_pm & 4)", + " {", + "#ifdef VERBOSE", + " printf(\"Revisit of atomic not needed (%%d)\\n\",", + " trpt->o_pm);", /* at least 1 rv succeeded */ + "#endif", + " continue;", + " }", + "#ifndef NOREDUCE", + " nps = 0;", + "#endif", + " if (trpt->o_pm == 8)", + " { revrv++;", + " if (trpt->tau&8)", + " {", + "#ifdef VERBOSE", + " printf(\"Break atomic (pm:%%d,tau:%%d)\\n\",", + " trpt->o_pm, trpt->tau);", + "#endif", + " trpt->tau &= ~8;", + " }", + "#ifndef NOREDUCE", + " else if (trpt->tau&32)", /* was a preselected move */ + " {", + "#ifdef VERBOSE", + " printf(\"Void preselection (pm:%%d,tau:%%d)\\n\",", + " trpt->o_pm, trpt->tau);", + "#endif", + " trpt->tau &= ~32;", + " nps = 1; /* no preselection in repeat */", + " }", + "#endif", + " }", + " trpt->o_pm &= ~(4|8);", + " if (trpt->o_tt > mreached)", + " { mreached = trpt->o_tt;", + " if (mreached%%10 == 0)", + " { printf(\"Depth= %%7d States= %%7g \", mreached, nstates);", + " printf(\"Transitions= %%7g \", nstates+truncs);", + "#ifdef MA", + " printf(\"Nodes= %%7d \", nr_states);", + "#endif", + " printf(\"Memory= %%-6.3f\\n\", memcnt/1000000.);", + " fflush(stdout);", + " } }", + " depth = trpt->o_tt;", + + " if (depth >= maxdepth)", + " {", + "#if SYNC", + " Trail *x;", + " if (boq != -1)", + " { x = (Trail *) trpt->ostate;", + " if (x) x->o_pm |= 4; /* not failing */", + " }", + "#endif", + " truncs++;", + " if (!warned)", + " { warned = 1;", + " printf(\"error: max search depth too small\\n\");", + " }", + " if (bounded)", + " uerror(\"depth limit reached\");", + " continue;", + " }", + +/* PO */ + "#ifndef NOREDUCE", + " if (boq == -1 && !(trpt->tau&8) && nps == 0)", + " for (II = now._nr_pr-1; II >= BASE; II -= 1)", + " {", + "Pickup: this = pptr(II);", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + " if (trans[ot][tt]->atom & 8)", /* safe */ + " { t = trans[ot][tt];", + " if (t->qu[0] != 0)", + " { Ccheck++;", + " if (!q_cond(II, t))", + " continue;", + " Cholds++;", + " }", + " From = To = II;", + " trpt->tau |= 32; /* preselect marker */", + "#ifdef DEBUG", + " printf(\"%%3d: proc %%d PreSelected (tau=%%d)\\n\", ", + " depth, II, trpt->tau);", + "#endif", + " goto MainLoop;", + " } }", + " trpt->tau &= ~32;", /* not preselected */ + "#endif", +/* PO */ + "Repeat:", + " if (trpt->tau&8) /* atomic */", + " { From = To = (short ) trpt->pr;", + " nlinks++;", + " } else", + " { From = now._nr_pr-1;", + " To = BASE;", + " }", + "MainLoop:", + " _n = _m = 0;", + " for (II = From; II >= To; II -= 1)", + " {", + " this = (((uchar *)&now)+proc_offset[II]);", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + "#if SYNC", + " /* no rendezvous with same proc */", + " if (boq != -1 && trpt->pr == II) continue;", + "#endif", + " ntrpt->pr = (uchar) II;", + " ntrpt->st = tt; ", + " trpt->o_pm &= ~1; /* no move yet */", + "#ifdef EVENT_TRACE", + " trpt->o_event = now._event;", + "#endif", + "#ifdef HAS_PROVIDED", + " if (!provided(II, ot, tt, t)) continue;", + "#endif", + "#ifdef HAS_UNLESS", + " E_state = 0;", + "#endif", + " for (t = trans[ot][tt]; t; t = t->nxt)", + " {", + "#ifdef HAS_UNLESS", + " if (E_state > 0", + " && E_state != t->e_trans)", + " break;", + "#endif", + " ntrpt->o_t = t;", + "", + " oboq = boq;", + "", + " if (!(_m = do_transit(t, II)))", + " continue;", + "", + " trpt->o_pm |= 1; /* we moved */", + " (trpt+1)->o_m = _m; /* for unsend */", + "#ifdef PEG", + " peg[t->forw]++;", + "#endif", + "#ifdef CHECK", + " printf(\"%%3d: proc %%d exec %%d, \",", + " depth, II, t->forw);", + " printf(\"%%d to %%d, %%s %%s %%s\",", + " tt, t->st, t->tp,", + " (t->atom&2)?\"atomic\":\"\",", + " (boq != -1)?\"rendez-vous\":\"\");", + "#ifdef HAS_UNLESS", + " if (t->e_trans)", + " printf(\" (escapes to state %%d)\", t->st);", + "#endif", + " printf(\" %%saccepting [tau=%%d]\\n\",", + " (trpt->o_pm&2)?\"\":\"non-\", trpt->tau);", + "#endif", + "#ifdef HAS_UNLESS", + " E_state = t->e_trans;", + "#if SYNC>0", + " if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */))", + " { fprintf(efd, \"error:\tthe use of rendezvous stmnt in the escape clause\\n\");", + " fprintf(efd, \"\tof an unless stmnt is not compatible with -DBFS\\n\");", + " pan_exit(1);", + " }", + "#endif", + "#endif", + " if (t->st > 0) ((P0 *)this)->_p = t->st;", + "", + " /* ptr to pred: */ ntrpt->ostate = (struct H_el *) otrpt;", + " ntrpt->st = tt;", + " if (boq == -1 && (t->atom&2)) /* atomic */", + " ntrpt->tau = 8; /* record for next move */", + " else", + " ntrpt->tau = 0;", + "", + " store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq);", + "#ifdef EVENT_TRACE", + " now._event = trpt->o_event;", + "#endif", + "", + " /* undo move and continue */", + " trpt++; /* this is where ovals and ipt are set */", + " do_reverse(t, II, _m); /* restore now. */", + " trpt--;", + "#ifdef CHECK", + " printf(\"%%3d: proc %%d \", depth, II);", + " printf(\"reverses %%d, %%d to %%d,\",", + " t->forw, tt, t->st);", + " printf(\" %%s [abit=%%d,adepth=%%d,\",", + " t->tp, now._a_t, A_depth);", + " printf(\"tau=%%d,%%d]\\n\",", + " trpt->tau, (trpt-1)->tau);", + "#endif", + " reached[ot][t->st] = 1;", + " reached[ot][tt] = 1;", + "", + " ((P0 *)this)->_p = tt;", + " _n |= _m;", + " } }", +/* PO */ + "#ifndef NOREDUCE", + " /* preselected - no succ definitely outside stack */", + " if ((trpt->tau&32) && !(trpt->tau&64))", + " { From = now._nr_pr-1; To = BASE;", + "#ifdef DEBUG", + " printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ", + " depth, II+1, (int) _n, trpt->tau);", + "#endif", + " _n = 0; trpt->tau &= ~32;", + " if (II >= BASE)", + " goto Pickup;", + " goto MainLoop;", + " }", + " trpt->tau &= ~(32|64);", + "#endif", +/* PO */ + " if (_n != 0)", + " continue;", + "#ifdef DEBUG", + " printf(\"%%3d: no move [II=%%d, tau=%%d, boq=%%d, _nr_pr=%%d]\\n\",", + " depth, II, trpt->tau, boq, now._nr_pr);", + "#endif", + " if (boq != -1)", + " { failedrv++;", + " x = (Trail *) trpt->ostate; /* pre-rv state */", + " if (!x) continue; /* root state */", + " if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */", + " { x->o_pm |= 8; /* mark failure */", + " this = (((uchar *)&now)+proc_offset[otrpt->pr]);", + "#ifdef VERBOSE", + " printf(\"\\treset state of %%d from %%d to %%d\\n\",", + " otrpt->pr, ((P0 *)this)->_p, otrpt->st);", + "#endif", + " ((P0 *)this)->_p = otrpt->st;", + " unsend(boq); /* retract rv offer */", + " boq = -1;", + + " push_bfs(x, x->o_tt);", + "#ifdef VERBOSE", + " printf(\"failed rv, repush with %%d\\n\", x->o_pm);", + "#endif", + " }", + "#ifdef VERBOSE", + " else printf(\"failed rv, tau at parent: %%d\\n\", x->tau);", + "#endif", + " } else if (now._nr_pr > 0)", + " {", + " if ((trpt->tau&8)) /* atomic */", + " { trpt->tau &= ~(1|8); /* 1=timeout, 8=atomic */", + "#ifdef DEBUG", + " printf(\"%%3d: atomic step proc %%d blocks\\n\",", + " depth, II+1);", + "#endif", + " goto Repeat;", + " }", + "", + " if (!(trpt->tau&1)) /* didn't try timeout yet */", + " { trpt->tau |= 1;", + "#ifdef DEBUG", + " printf(\"%%d: timeout\\n\", depth);", + "#endif", + " goto MainLoop;", + " }", + "#ifndef VERI", + " if (!noends && !a_cycles && !endstate())", + " uerror(\"invalid end state\");", + "#endif", + " } }", + "}", + "", + "void", + "putter(Trail *trpt, int fd)", + "{ long j;", + "", + " if (!trpt) return;", + "", + " if (trpt != (Trail *) trpt->ostate)", + " putter((Trail *) trpt->ostate, fd);", + "", + " if (trpt->o_t)", + " { sprintf(snap, \"%%d:%%d:%%d\\n\",", + " trcnt++, trpt->pr, trpt->o_t->t_id);", + " j = strlen(snap);", + " if (write(fd, snap, j) != j)", + " { printf(\"pan: error writing %%s\\n\", fnm);", + " pan_exit(1);", + " } }", + "}", + "", + "void", + "nuerror(char *str)", + "{ int fd = make_trail();", + " int j;", + "", + " if (fd < 0) return;", + "#ifdef VERI", + " sprintf(snap, \"-2:%%d:-2\\n\", VERI);", + " write(fd, snap, strlen(snap));", + "#endif", + "#ifdef MERGED", + " sprintf(snap, \"-4:-4:-4\\n\");", + " write(fd, snap, strlen(snap));", + "#endif", + " trcnt = 1;", + " putter(trpt, fd);", + " if (ntrpt->o_t)", /* 4.2.8 -- Alex example, missing last transition */ + " { sprintf(snap, \"%%d:%%d:%%d\\n\",", + " trcnt++, ntrpt->pr, ntrpt->o_t->t_id);", + " j = strlen(snap);", + " if (write(fd, snap, j) != j)", + " { printf(\"pan: error writing %%s\\n\", fnm);", + " pan_exit(1);", + " } }", + " close(fd);", + " if (errors >= upto && upto != 0)", + " {", + " wrapup();", + " }", + "}", + "#endif", /* BFS */ + 0, +}; + +static char *Code2c[] = { + "void", + "do_the_search(void)", + "{ int i;", + " depth = mreached = 0;", + " trpt = &trail[0];", + "#ifdef VERI", + " trpt->tau |= 4; /* the claim moves first */", + "#endif", + " for (i = 0; i < (int) now._nr_pr; i++)", + " { P0 *ptr = (P0 *) pptr(i);", + "#ifndef NP", + " if (!(trpt->o_pm&2)", + " && accpstate[ptr->_t][ptr->_p])", + " { trpt->o_pm |= 2;", + " }", + "#else", + " if (!(trpt->o_pm&4)", + " && progstate[ptr->_t][ptr->_p])", + " { trpt->o_pm |= 4;", + " }", + "#endif", + " }", + "#ifdef EVENT_TRACE", + "#ifndef NP", + " if (accpstate[EVENT_TRACE][now._event])", + " { trpt->o_pm |= 2;", + " }", + "#else", + " if (progstate[EVENT_TRACE][now._event])", + " { trpt->o_pm |= 4;", + " }", + "#endif", + "#endif", + "#ifndef NOCOMP", + " Mask[0] = Mask[1] = 1; /* _nr_pr, _nr_qs */", + " if (!a_cycles)", + " { i = &(now._a_t) - (uchar *) &now;", + " Mask[i] = 1; /* _a_t */", + " }", + "#ifndef NOFAIR", + " if (!fairness)", + " { int j = 0;", + " i = &(now._cnt[0]) - (uchar *) &now;", + " while (j++ < NFAIR)", + " Mask[i++] = 1; /* _cnt[] */", + " }", + "#endif", + "#endif", + "#ifndef NOFAIR", + " if (fairness", + " && (a_cycles && (trpt->o_pm&2)))", + " { now._a_t = 2; /* set the A-bit */", + " now._cnt[0] = now._nr_pr + 1;", /* NEW: +1 */ + "#ifdef VERBOSE", + " printf(\"%%3d: fairness Rule 1, cnt=%%d, _a_t=%%d\\n\",", + " depth, now._cnt[now._a_t&1], now._a_t);", + "#endif", + " }", + "#endif", + + "#if defined(C_States) && (HAS_TRACK==1)", + " /* capture initial state of tracked C objects */", + " c_update((uchar *) &(now.c_state[0]));", + "#endif", + + "#ifdef HAS_CODE", + " if (readtrail) getrail(); /* no return */", + "#endif", + "#ifdef BFS", + " bfs();", + "#else", + "#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)", + " /* initial state of tracked & unmatched objects */", + " c_stack((uchar *) &(svtack->c_stack[0]));", + "#endif", + "#ifdef RANDOMIZE", + " srand(123);", + "#endif", + " new_state(); /* start 1st DFS */", + "#endif", + "}", + + "#ifdef INLINE_REV", + "uchar", + "do_reverse(Trans *t, short II, uchar M)", + "{ uchar _m = M;", + " int tt = (int) ((P0 *)this)->_p;", + "#include REVERSE_MOVES", + "R999: return _m;", + "}", + "#endif", + + "#ifndef INLINE", + "#ifdef EVENT_TRACE", + "static char _tp = 'n'; static int _qid = 0;", + "#endif", + "uchar", + "do_transit(Trans *t, short II)", + "{ uchar _m = 0;", + " int tt = (int) ((P0 *)this)->_p;", + "#ifdef M_LOSS", + " uchar delta_m = 0;", + "#endif", + "#ifdef EVENT_TRACE", + " short oboq = boq;", + " uchar ot = (uchar) ((P0 *)this)->_t;", + " if (ot == EVENT_TRACE) boq = -1;", + "#define continue { boq = oboq; return 0; }", + "#else", + "#define continue return 0", + "#ifdef SEPARATE", + " uchar ot = (uchar) ((P0 *)this)->_t;", + "#endif", + "#endif", + "#include FORWARD_MOVES", + "P999:", + "#ifdef EVENT_TRACE", + " if (ot == EVENT_TRACE) boq = oboq;", + "#endif", + " return _m;", + "#undef continue", + "}", + + "#ifdef EVENT_TRACE", + "void", + "require(char tp, int qid)", + "{ Trans *t;", + " _tp = tp; _qid = qid;", + "", + " if (now._event != endevent)", + " for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt)", + " { if (do_transit(t, EVENT_TRACE))", + " { now._event = t->st;", + " reached[EVENT_TRACE][t->st] = 1;", + "#ifdef VERBOSE", + " printf(\" event_trace move to -> %%d\\n\", t->st);", + "#endif", + "#ifndef BFS", + "#ifndef NP", + " if (accpstate[EVENT_TRACE][now._event])", + " (trpt+1)->o_pm |= 2;", + "#else", + " if (progstate[EVENT_TRACE][now._event])", + " (trpt+1)->o_pm |= 4;", + "#endif", + "#endif", + "#ifdef NEGATED_TRACE", + " if (now._event == endevent)", + " {", + "#ifndef BFS", + " depth++; trpt++;", + "#endif", + " uerror(\"event_trace error (all events matched)\");", + "#ifndef BFS", + " trpt--; depth--;", + "#endif", + " break;", + " }", + "#endif", + " for (t = t->nxt; t; t = t->nxt)", + " { if (do_transit(t, EVENT_TRACE))", + " Uerror(\"non-determinism in event-trace\");", + " }", + " return;", + " }", + "#ifdef VERBOSE", + " else", + " printf(\" event_trace miss '%%c' -- %%d, %%d, %%d\\n\",", + " tp, qid, now._event, t->forw);", + "#endif", + " }", + "#ifdef NEGATED_TRACE", + " now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */", + "#else", + "#ifndef BFS", + " depth++; trpt++;", + "#endif", + " uerror(\"event_trace error (no matching event)\");", + "#ifndef BFS", + " trpt--; depth--;", + "#endif", + "#endif", + "}", + "#endif", + + "int", + "enabled(int iam, int pid)", + "{ Trans *t; uchar *othis = this;", + " int res = 0; int tt; uchar ot;", + "#ifdef VERI", + " /* if (pid > 0) */ pid++;", + "#endif", + " if (pid == iam)", + " Uerror(\"used: enabled(pid=thisproc)\");", + " if (pid < 0 || pid >= (int) now._nr_pr)", + " return 0;", + " this = pptr(pid);", + " TstOnly = 1;", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + " for (t = trans[ot][tt]; t; t = t->nxt)", + " if (do_transit(t, (short) pid))", + " { res = 1;", + " break;", + " }", + " TstOnly = 0;", + " this = othis;", + " return res;", + "}", + "#endif", + "void", + "snapshot(void)", + "{ static long sdone = (long) 0;", + " long ndone = (unsigned long) nstates/1000000;", + " if (ndone == sdone) return;", + " sdone = ndone;", + " printf(\"Depth= %%7d States= %%7g \", mreached, nstates);", + " printf(\"Transitions= %%7g \", nstates+truncs);", + "#ifdef MA", + " printf(\"Nodes= %%7d \", nr_states);", + "#endif", + " printf(\"Memory= %%-6.3f\\n\", memcnt/1000000.);", + " fflush(stdout);", + "}", + + "#ifdef SC", + "void", + "stack2disk(void)", + "{", + " if (!stackwrite", + " && (stackwrite = creat(stackfile, TMODE)) < 0)", + " Uerror(\"cannot create stackfile\");", + "", + " if (write(stackwrite, trail, DDD*sizeof(Trail))", + " != DDD*sizeof(Trail))", + " Uerror(\"stackfile write error -- disk is full?\");", + "", + " memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail));", + " memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail));", + " CNT1++;", + "}", + "void", + "disk2stack(void)", + "{ long have;", + "", + " CNT2++;", + " memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail));", + "", + " if (!stackwrite", + " || lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1)", + " Uerror(\"disk2stack lseek error\");", + "", + " if (!stackread", + " && (stackread = open(stackfile, 0)) < 0)", + " Uerror(\"cannot open stackfile\");", + "", + " if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1)", + " Uerror(\"disk2stack lseek error\");", + "", + " have = read(stackread, trail, DDD*sizeof(Trail));", + " if (have != DDD*sizeof(Trail))", + " Uerror(\"stackfile read error\");", + "}", + "#endif", + + "uchar *", + "Pptr(int x)", /* as a fct, to avoid a problem with the p9 compiler */ + "{ if (x < 0 || x >= MAXPROC || !proc_offset[x])", /* does not exist */ + " return noptr;", + " else", + " return (uchar *) pptr(x);", + "}", + "int qs_empty(void);", + + "/*", + " * new_state() is the main DFS search routine in the verifier", + " * it has a lot of code ifdef-ed together to support", + " * different search modes, which makes it quite unreadable.", + " * if you are studying the code, first use the C preprocessor", + " * to generate a specific version from the pan.c source,", + " * e.g. by saying:", + " * gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c", + " * and then study the resulting file, rather than this one", + " */", + "#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA))", + "void", + "new_state(void)", + "{ Trans *t;", + " uchar _n, _m, ot;", + "#ifdef RANDOMIZE", + " short ooi, eoi;", + "#endif", + "#ifdef M_LOSS", + " uchar delta_m = 0;", + "#endif", + " short II, JJ = 0, kk;", + " int tt;", + " short From = now._nr_pr-1, To = BASE;", + "Down:", + "#ifdef CHECK", + " printf(\"%%d: Down - %%s\",", + " depth, (trpt->tau&4)?\"claim\":\"program\");", + " printf(\" %%saccepting [pids %%d-%%d]\\n\",", + " (trpt->o_pm&2)?\"\":\"non-\", From, To);", + "#endif", + + "#ifdef SC", + " if (depth > hiwater)", + " { stack2disk();", + " maxdepth += DDD;", + " hiwater += DDD;", + " trpt -= DDD;", + " if(verbose)", + " printf(\"zap %%d: %%d (maxdepth now %%d)\\n\",", + " CNT1, hiwater, maxdepth);", + " }", + "#endif", + + " trpt->tau &= ~(16|32|64); /* make sure these are off */", + "#if defined(FULLSTACK) && defined(MA)", + " trpt->proviso = 0;", + "#endif", + " if (depth >= maxdepth)", + " { truncs++;", + "#if SYNC", + " (trpt+1)->o_n = 1; /* not a deadlock */", + "#endif", + " if (!warned)", + " { warned = 1;", + " printf(\"error: max search depth too small\\n\");", + " }", + " if (bounded) uerror(\"depth limit reached\");", + " (trpt-1)->tau |= 16; /* worstcase guess */", + " goto Up;", + " }", + "AllOver:", + "#if defined(FULLSTACK) && !defined(MA)", + " /* if atomic or rv move, carry forward previous state */", + " trpt->ostate = (trpt-1)->ostate;", /* was: = (struct H_el *) 0;*/ + "#endif", + "#ifdef VERI", + " if ((trpt->tau&4) || ((trpt-1)->tau&128))", + "#endif", + " if (boq == -1) { /* if not mid-rv */", + "#ifndef SAFETY", + " /* this check should now be redundant", + " * because the seed state also appears", + " * on the 1st dfs stack and would be", + " * matched in hstore below", + " */", + " if ((now._a_t&1) && depth > A_depth)", + " { if (!memcmp((char *)&A_Root, ", + " (char *)&now, vsize))", + " {", + " depthfound = A_depth;", + "#ifdef CHECK", + " printf(\"matches seed\\n\");", + "#endif", + "#ifdef NP", + " uerror(\"non-progress cycle\");", + "#else", + " uerror(\"acceptance cycle\");", + "#endif", + " goto Up;", + " }", + "#ifdef CHECK", + " printf(\"not seed\\n\");", + "#endif", + " }", + "#endif", + " if (!(trpt->tau&8)) /* if no atomic move */", + " {", + "#ifdef BITSTATE", + "#ifdef CNTRSTACK", /* -> bitstate, reduced, safety */ + " II = bstore((char *)&now, vsize);", + " trpt->j6 = j1; trpt->j7 = j2;", + " JJ = LL[j1] && LL[j2];", + "#else", + "#ifdef FULLSTACK", + " JJ = onstack_now();", /* sets j1 */ + "#else", + "#ifndef NOREDUCE", + " JJ = II; /* worstcase guess for p.o. */", + "#endif", + "#endif", + " II = bstore((char *)&now, vsize);", /* sets j1-j4 */ + "#endif", + "#else", + "#ifdef MA", + " II = gstore((char *)&now, vsize, 0);", + "#ifndef FULLSTACK", + " JJ = II;", + "#else", + " JJ = (II == 2)?1:0;", + "#endif", + "#else", + " II = hstore((char *)&now, vsize);", + "#ifdef FULLSTACK", + " JJ = (II == 2)?1:0;", + "#endif", + "#endif", + "#endif", + " kk = (II == 1 || II == 2);", + /* II==0 new state */ + /* II==1 old state */ + /* II==2 on current dfs stack */ + /* II==3 on 1st dfs stack */ + "#ifndef SAFETY", + + " if (!fairness && a_cycles)", + " if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2)))", + " { II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */", + "#ifdef VERBOSE", + " printf(\"state match on dfs stack\\n\");", + "#endif", + " goto same_case;", + " }", + + + "#if defined(FULLSTACK) && defined(BITSTATE)", + " if (!JJ && (now._a_t&1) && depth > A_depth)", + " { int oj1 = j1;", + " uchar o_a_t = now._a_t;", + " now._a_t &= ~(1|16|32);", /* 1st stack */ + " if (onstack_now())", /* changes j1 */ + " { II = 3;", + "#ifdef VERBOSE", + " printf(\"state match on 1st dfs stack\\n\");", + "#endif", + " }", + " now._a_t = o_a_t;", /* restore */ + " j1 = oj1;", + " }", + "#endif", + " if (II == 3 && a_cycles && (now._a_t&1))", + " {", + "#ifndef NOFAIR", + " if (fairness && now._cnt[1] > 1) /* was != 0 */", + " {", + "#ifdef VERBOSE", + " printf(\"\tfairness count non-zero\\n\");", + "#endif", + " II = 0;", /* treat as new state */ + " } else", + "#endif", + " {", + "#ifndef BITSTATE", + " nShadow--;", + "#endif", + "same_case: if (Lstate) depthfound = Lstate->D;", + "#ifdef NP", + " uerror(\"non-progress cycle\");", + "#else", + " uerror(\"acceptance cycle\");", + "#endif", + " goto Up;", + " }", + " }", + "#endif", + + "#ifndef NOREDUCE", + "#ifndef SAFETY", + " if ((II && JJ) || (II == 3))", + " { /* marker for liveness proviso */", + " (trpt-1)->tau |= 16;", + " truncs2++;", + " }", + "#else", + " if (!II || !JJ)", + " { /* successor outside stack */", + " (trpt-1)->tau |= 64;", + " }", + "#endif", + "#endif", + " if (II)", + " { truncs++;", + " goto Up;", + " }", + " if (!kk)", + " { nstates++;", + " if ((unsigned long) nstates%%1000000 == 0)", + " snapshot();", + "#ifdef SVDUMP", + " if (vprefix > 0)", + " if (write(svfd, (uchar *) &now, vprefix) != vprefix)", + " { fprintf(efd, \"writing %%s.svd failed\\n\", Source);", + " wrapup();", + " }", + "#endif", + "#if defined(MA) && defined(W_XPT)", + " if ((unsigned long) nstates%%W_XPT == 0)", + " { void w_xpoint(void);", + " w_xpoint();", + " }", + "#endif", + " }", + "#if defined(FULLSTACK) || defined(CNTRSTACK)", + " onstack_put();", + "#ifdef DEBUG2", + "#if defined(FULLSTACK) && !defined(MA)", + " printf(\"%%d: putting %%u (%%d)\\n\", depth,", + " trpt->ostate, ", + " (trpt->ostate)?trpt->ostate->tagged:0);", + "#else", + " printf(\"%%d: putting\\n\", depth);", + "#endif", + "#endif", + "#endif", + " } }", + + + " if (depth > mreached)", + " mreached = depth;", + "#ifdef VERI", + " if (trpt->tau&4)", + "#endif", + " trpt->tau &= ~(1|2); /* timeout and -request off */", + " _n = 0;", + "#if SYNC", + " (trpt+1)->o_n = 0;", + "#endif", + "#ifdef VERI", + " if (now._nr_pr == 0) /* claim terminated */", + " uerror(\"end state in claim reached\");", + " check_claim(((P0 *)pptr(0))->_p);", + "Stutter:", + " if (trpt->tau&4) /* must make a claimmove */", + " {", + + "#ifndef NOFAIR", + " if ((now._a_t&2) /* A-bit set */", + " && now._cnt[now._a_t&1] == 1)", + " { now._a_t &= ~2;", + " now._cnt[now._a_t&1] = 0;", + " trpt->o_pm |= 16;", + "#ifdef DEBUG", + " printf(\"%%3d: fairness Rule 3.: _a_t = %%d\\n\",", + " depth, now._a_t);", + "#endif", + " }", + "#endif", + + " II = 0; /* never */", + " goto Veri0;", + " }", + "#endif", + "#ifndef NOREDUCE", + " /* Look for a process with only safe transitions */", + " /* (special rules apply in the 2nd dfs) */", +"#ifdef SAFETY", + " if (boq == -1 && From != To)", +"#else", + "/* implied: #ifdef FULLSTACK */", + " if (boq == -1 && From != To", + " && (!(now._a_t&1)", + " || (a_cycles &&", + "#ifndef BITSTATE", + "#ifdef MA", + "#ifdef VERI", + " !((trpt-1)->proviso))", + "#else", + " !(trpt->proviso))", + "#endif", + "#else", + "#ifdef VERI", + " (trpt-1)->ostate &&", + " !(((char *)&((trpt-1)->ostate->state))[0] & 128))", + "#else", + " !(((char *)&(trpt->ostate->state))[0] & 128))", + "#endif", + "#endif", + "#else", + "#ifdef VERI", + " (trpt-1)->ostate &&", + " (trpt-1)->ostate->proviso == 0)", + "#else", + " trpt->ostate->proviso == 0)", + "#endif", + "#endif", + " ))", + "/* #endif */", +"#endif", + " for (II = From; II >= To; II -= 1)", + " {", + "Resume: /* pick up here if preselect fails */", + " this = pptr(II);", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + " if (trans[ot][tt]->atom & 8)", + " { t = trans[ot][tt];", + " if (t->qu[0] != 0)", + " { Ccheck++;", + " if (!q_cond(II, t))", + " continue;", + " Cholds++;", + " }", + " From = To = II;", + "#ifdef NIBIS", + " t->om = 0;", + "#endif", + " trpt->tau |= 32; /* preselect marker */", + "#ifdef DEBUG", + "#ifdef NIBIS", + " printf(\"%%3d: proc %%d Pre\", depth, II);", + " printf(\"Selected (om=%%d, tau=%%d)\\n\", ", + " t->om, trpt->tau);", + "#else", + " printf(\"%%3d: proc %%d PreSelected (tau=%%d)\\n\", ", + " depth, II, trpt->tau);", + "#endif", + "#endif", + " goto Again;", + " }", + " }", + " trpt->tau &= ~32;", + "#endif", + "#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI))", + "Again:", + "#endif", + " /* The Main Expansion Loop over Processes */", + + " trpt->o_pm &= ~(8|16|32|64); /* fairness-marks */", + + "#ifndef NOFAIR", + " if (fairness && boq == -1", + "#ifdef VERI", + " && (!(trpt->tau&4) && !((trpt-1)->tau&128))", + "#endif", + " && !(trpt->tau&8))", + " { /* A_bit = 1; Cnt = N in acc states with A_bit 0 */", + " if (!(now._a_t&2))", /* A-bit not set */ + " {", + " if (a_cycles && (trpt->o_pm&2))", + " { /* Accepting state */", + " now._a_t |= 2;", + " now._cnt[now._a_t&1] = now._nr_pr + 1;", /* NEW +1 */ + " trpt->o_pm |= 8;", + "#ifdef DEBUG", + " printf(\"%%3d: fairness Rule 1: cnt=%%d, _a_t=%%d\\n\",", + " depth, now._cnt[now._a_t&1], now._a_t);", + "#endif", + " }", + " } else", /* A-bit set */ + " { /* A_bit = 0 when Cnt 0 */", + " if (now._cnt[now._a_t&1] == 1)", /* NEW: 1 iso 0 */ + " { now._a_t &= ~2;", /* reset a-bit */ + " now._cnt[now._a_t&1] = 0;", /* NEW: reset cnt */ + " trpt->o_pm |= 16;", + "#ifdef DEBUG", + " printf(\"%%3d: fairness Rule 3: _a_t = %%d\\n\",", + " depth, now._a_t);", + "#endif", + " } } }", + "#endif", + + " for (II = From; II >= To; II -= 1)", + " {", + "#if SYNC", + " /* no rendezvous with same proc */", + " if (boq != -1 && trpt->pr == II) continue;", + "#endif", + "#ifdef VERI", + "Veri0:", + "#endif", + " this = pptr(II);", + " tt = (int) ((P0 *)this)->_p;", + " ot = (uchar) ((P0 *)this)->_t;", + + "#ifdef NIBIS", + " /* don't repeat a previous preselected expansion */", + " /* could hit this if reduction proviso was false */", + " t = trans[ot][tt];", + " if (!(trpt->tau&4)", /* not claim */ + " && !(trpt->tau&1)", /* not timeout */ + " && !(trpt->tau&32)", /* not preselected */ + " && (t->atom & 8)", /* local */ + " && boq == -1", /* not inside rendezvous */ + " && From != To)", /* not inside atomic seq */ + " { if (t->qu[0] == 0", /* unconditional */ + " || q_cond(II, t))", /* true condition */ + " { _m = t->om;", + " if (_m>_n||(_n>3&&_m!=0)) _n=_m;", + " continue; /* did it before */", + " } }", + "#endif", + " trpt->o_pm &= ~1; /* no move in this pid yet */", + "#ifdef EVENT_TRACE", + " (trpt+1)->o_event = now._event;", + "#endif", + " /* Fairness: Cnt++ when Cnt == II */", + "#ifndef NOFAIR", + " trpt->o_pm &= ~64; /* didn't apply rule 2 */", + " if (fairness", + " && boq == -1", /* not mid rv - except rcv - NEW 3.0.8 */ + " && !(trpt->o_pm&32)", /* Rule 2 not in effect */ + " && (now._a_t&2)", /* A-bit is set */ + " && now._cnt[now._a_t&1] == II+2)", + " { now._cnt[now._a_t&1] -= 1;", + "#ifdef VERI", + " /* claim need not participate */", + " if (II == 1)", + " now._cnt[now._a_t&1] = 1;", + "#endif", + "#ifdef DEBUG", + " printf(\"%%3d: proc %%d fairness \", depth, II);", + " printf(\"Rule 2: --cnt to %%d (%%d)\\n\",", + " now._cnt[now._a_t&1], now._a_t);", + "#endif", + " trpt->o_pm |= (32|64);", + " }", + "#endif", + "#ifdef HAS_PROVIDED", + " if (!provided(II, ot, tt, t)) continue;", + "#endif", + " /* check all trans of proc II - escapes first */", + "#ifdef HAS_UNLESS", + " trpt->e_state = 0;", + "#endif", + " (trpt+1)->pr = (uchar) II;", /* for uerror */ + " (trpt+1)->st = tt;", + + "#ifdef RANDOMIZE", + " for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++)", + " if (strcmp(t->tp, \"else\") == 0)", + " eoi++;", + "", + " if (eoi)", + " { t = trans[ot][tt];", + "#ifdef VERBOSE", + " printf(\"randomizer: suppressed, saw else\\n\");", + "#endif", + " } else", + " { eoi = rand()%%ooi;", + "#ifdef VERBOSE", + " printf(\"randomizer: skip %%d in %%d\\n\", eoi, ooi);", + "#endif", + " for (t = trans[ot][tt]; t; t = t->nxt)", + " if (eoi-- <= 0) break;", + " }", + "DOMORE:", + " for ( ; t && ooi > 0; t = t->nxt, ooi--)", + "#else", + " for (t = trans[ot][tt]; t; t = t->nxt)", + "#endif", + " {", + "#ifdef HAS_UNLESS", + " /* exploring all transitions from", + " * a single escape state suffices", + " */", + " if (trpt->e_state > 0", + " && trpt->e_state != t->e_trans)", + " {", + "#ifdef DEBUG", + " printf(\"skip 2nd escape %%d (did %%d before)\\n\",", + " t->e_trans, trpt->e_state);", + "#endif", + " break;", + " }", + "#endif", + " (trpt+1)->o_t = t;", /* for uerror */ + "#ifdef INLINE", + "#include FORWARD_MOVES", + "P999: /* jumps here when move succeeds */", + "#else", + " if (!(_m = do_transit(t, II))) continue;", + "#endif", + " if (boq == -1)", +"#ifdef CTL", + " /* for branching-time, can accept reduction only if */", + " /* the persistent set contains just 1 transition */", + " { if ((trpt->tau&32) && (trpt->o_pm&1))", + " trpt->tau |= 16;", + " trpt->o_pm |= 1; /* we moved */", + " }", +"#else", + " trpt->o_pm |= 1; /* we moved */", +"#endif", + "#ifdef PEG", + " peg[t->forw]++;", + "#endif", + + "#if defined(VERBOSE) || defined(CHECK)", + "#if defined(SVDUMP)", + " printf(\"%%3d: proc %%d exec %%d \\n\", ", + " depth, II, t->t_id);", + "#else", + " printf(\"%%3d: proc %%d exec %%d, \", ", + " depth, II, t->forw);", + " printf(\"%%d to %%d, %%s %%s %%s\", ", + " tt, t->st, t->tp,", + " (t->atom&2)?\"atomic\":\"\",", + " (boq != -1)?\"rendez-vous\":\"\");", + "#ifdef HAS_UNLESS", + " if (t->e_trans)", + " printf(\" (escapes to state %%d)\",", + " t->st);", + "#endif", + " printf(\" %%saccepting [tau=%%d]\\n\",", + " (trpt->o_pm&2)?\"\":\"non-\", trpt->tau);", + "#endif", + "#ifdef RANDOMIZE", + " printf(\" randomizer %%d\\n\", ooi);", + "#endif", + "#endif", + + "#ifdef HAS_LAST", + "#ifdef VERI", + " if (II != 0)", + "#endif", + " now._last = II - BASE;", + "#endif", + "#ifdef HAS_UNLESS", + " trpt->e_state = t->e_trans;", + "#endif", + + " depth++; trpt++;", + " trpt->pr = (uchar) II;", + " trpt->st = tt;", + " trpt->o_pm &= ~(2|4);", + " if (t->st > 0)", + " { ((P0 *)this)->_p = t->st;", + "/* moved down reached[ot][t->st] = 1; */", + " }", + "#ifndef SAFETY", + " if (a_cycles)", + " {", + "#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP))", + " int ii;", + "#endif", + "#define P__Q ((P0 *)pptr(ii))", + "#if ACCEPT_LAB>0", + "#ifdef NP", + " /* state 1 of np_ claim is accepting */", + " if (((P0 *)pptr(0))->_p == 1)", + " trpt->o_pm |= 2;", + "#else", + " for (ii = 0; ii < (int) now._nr_pr; ii++)", + " { if (accpstate[P__Q->_t][P__Q->_p])", + " { trpt->o_pm |= 2;", + " break;", + " } }", + "#endif", + "#endif", + "#if defined(HAS_NP) && PROG_LAB>0", + " for (ii = 0; ii < (int) now._nr_pr; ii++)", + " { if (progstate[P__Q->_t][P__Q->_p])", + " { trpt->o_pm |= 4;", + " break;", + " } }", + "#endif", + "#undef P__Q", + " }", + "#endif", + " trpt->o_t = t; trpt->o_n = _n;", + " trpt->o_ot = ot; trpt->o_tt = tt;", + " trpt->o_To = To; trpt->o_m = _m;", + " trpt->tau = 0;", +"#ifdef RANDOMIZE", + " trpt->oo_i = ooi;", +"#endif", + " if (boq != -1 || (t->atom&2))", + " { trpt->tau |= 8;", + "#ifdef VERI", + " /* atomic sequence in claim */", + " if((trpt-1)->tau&4)", + " trpt->tau |= 4;", + " else", + " trpt->tau &= ~4;", + " } else", + " { if ((trpt-1)->tau&4)", + " trpt->tau &= ~4;", + " else", + " trpt->tau |= 4;", + " }", + " /* if claim allowed timeout, so */", + " /* does the next program-step: */", + " if (((trpt-1)->tau&1) && !(trpt->tau&4))", + " trpt->tau |= 1;", + "#else", + " } else", + " trpt->tau &= ~8;", + "#endif", + " if (boq == -1 && (t->atom&2))", + " { From = To = II; nlinks++;", + " } else", + " { From = now._nr_pr-1; To = BASE;", + " }", + " goto Down; /* pseudo-recursion */", + "Up:", + "#ifdef CHECK", + " printf(\"%%d: Up - %%s\\n\", depth,", + " (trpt->tau&4)?\"claim\":\"program\");", + "#endif", + "#ifdef MA", + " if (depth <= 0) return;", + " /* e.g., if first state is old, after a restart */", + "#endif", + + "#ifdef SC", + " if (CNT1 > CNT2", + " && depth < hiwater - (HHH-DDD) + 2)", + " {", + " trpt += DDD;", + " disk2stack();", + " maxdepth -= DDD;", + " hiwater -= DDD;", + "if(verbose)", + "printf(\"unzap %%d: %%d\\n\", CNT2, hiwater);", + " }", + "#endif", + + "#ifndef NOFAIR", + " if (trpt->o_pm&128) /* fairness alg */", + " { now._cnt[now._a_t&1] = trpt->bup.oval;", + " _n = 1; trpt->o_pm &= ~128;", + " depth--; trpt--;", + "#if defined(VERBOSE) || defined(CHECK)", + " printf(\"%%3d: reversed fairness default move\\n\", depth);", + "#endif", + " goto Q999;", + " }", + "#endif", + + "#ifdef HAS_LAST", + "#ifdef VERI", + " { int d; Trail *trl;", + " now._last = 0;", + " for (d = 1; d < depth; d++)", + " { trl = getframe(depth-d); /* was (trpt-d) */", + " if (trl->pr != 0)", + " { now._last = trl->pr - BASE;", + " break;", + " } } }", + "#else", + " now._last = (depth<1)?0:(trpt-1)->pr;", + "#endif", + "#endif", + "#ifdef EVENT_TRACE", + " now._event = trpt->o_event;", + "#endif", + "#ifndef SAFETY", + " if ((now._a_t&1) && depth <= A_depth)", + " return; /* to checkcycles() */", + "#endif", + " t = trpt->o_t; _n = trpt->o_n;", + " ot = trpt->o_ot; II = trpt->pr;", + " tt = trpt->o_tt; this = pptr(II);", + " To = trpt->o_To; _m = trpt->o_m;", +"#ifdef RANDOMIZE", + " ooi = trpt->oo_i;", +"#endif", + "#ifdef INLINE_REV", + " _m = do_reverse(t, II, _m);", + "#else", + "#include REVERSE_MOVES", + "R999: /* jumps here when done */", + "#endif", + + "#ifdef VERBOSE", + " printf(\"%%3d: proc %%d \", depth, II);", + " printf(\"reverses %%d, %%d to %%d,\",", + " t->forw, tt, t->st);", + " printf(\" %%s [abit=%%d,adepth=%%d,\", ", + " t->tp, now._a_t, A_depth);", + " printf(\"tau=%%d,%%d]\\n\", ", + " trpt->tau, (trpt-1)->tau);", + "#endif", + "#ifndef NOREDUCE", + " /* pass the proviso tags */", + " if ((trpt->tau&8) /* rv or atomic */", + " && (trpt->tau&16))", + " (trpt-1)->tau |= 16;", + "#ifdef SAFETY", + " if ((trpt->tau&8) /* rv or atomic */", + " && (trpt->tau&64))", + " (trpt-1)->tau |= 64;", + "#endif", + "#endif", + " depth--; trpt--;", + "#ifdef NIBIS", + " (trans[ot][tt])->om = _m; /* head of list */", + "#endif", + + " /* i.e., not set if rv fails */", + " if (_m)", + " {", + "#if defined(VERI) && !defined(NP)", + " if (II == 0 && verbose && !reached[ot][t->st])", + " {", + " printf(\"depth %%d: Claim reached state %%d (line %%d)\\n\",", + " depth, t->st, src_claim [t->st]);", + " fflush(stdout);", + " }", + "#endif", + " reached[ot][t->st] = 1;", + " reached[ot][tt] = 1;", + " }", + "#ifdef HAS_UNLESS", + " else trpt->e_state = 0; /* undo */", + "#endif", + + " if (_m>_n||(_n>3&&_m!=0)) _n=_m;", + " ((P0 *)this)->_p = tt;", + " } /* all options */", + + "#ifdef RANDOMIZE", + " if (!t && ooi > 0)", /* means we skipped some initial options */ + " { t = trans[ot][tt];", + "#ifdef VERBOSE", + " printf(\"randomizer: continue for %%d more\\n\", ooi);", + "#endif", + " goto DOMORE;", + " }", + "#ifdef VERBOSE", + " else", + " printf(\"randomizer: done\\n\");", + "#endif", + "#endif", + + "#ifndef NOFAIR", + " /* Fairness: undo Rule 2 */", + " if ((trpt->o_pm&32)",/* rule 2 was applied */ + " && (trpt->o_pm&64))",/* by this process II */ + " { if (trpt->o_pm&1)",/* it didn't block */ + " {", + "#ifdef VERI", + " if (now._cnt[now._a_t&1] == 1)", /* NEW: 1 iso 0 */ + " now._cnt[now._a_t&1] = 2;", /* NEW: 2 iso 1*/ + "#endif", + " now._cnt[now._a_t&1] += 1;", + "#ifdef VERBOSE", + " printf(\"%%3d: proc %%d fairness \", depth, II);", + " printf(\"undo Rule 2, cnt=%%d, _a_t=%%d\\n\",", + " now._cnt[now._a_t&1], now._a_t);", + "#endif", + " trpt->o_pm &= ~(32|64);", + " } else", /* process blocked */ + " { if (_n > 0)", /* a prev proc didn't */ + " {", /* start over */ + " trpt->o_pm &= ~64;", + " II = From+1;", + " } } }", + "#endif", + + "#ifdef VERI", + " if (II == 0) break; /* never claim */", + "#endif", + " } /* all processes */", + + "#ifndef NOFAIR", + " /* Fairness: undo Rule 2 */", + " if (trpt->o_pm&32) /* remains if proc blocked */", + " {", + "#ifdef VERI", + " if (now._cnt[now._a_t&1] == 1)", /* NEW: 1 iso 0 */ + " now._cnt[now._a_t&1] = 2;", /* NEW: 2 iso 1 */ + "#endif", + " now._cnt[now._a_t&1] += 1;", + "#ifdef VERBOSE", + " printf(\"%%3d: proc -- fairness \", depth);", + " printf(\"undo Rule 2, cnt=%%d, _a_t=%%d\\n\",", + " now._cnt[now._a_t&1], now._a_t);", + "#endif", + " trpt->o_pm &= ~32;", + " }", +"#ifndef NP", + /* 12/97 non-progress cycles cannot be created + * by stuttering extension, here or elsewhere + */ + " if (fairness", + " && _n == 0 /* nobody moved */", + "#ifdef VERI", + " && !(trpt->tau&4) /* in program move */", + "#endif", + " && !(trpt->tau&8) /* not an atomic one */", + "#ifdef OTIM", + " && ((trpt->tau&1) || endstate())", + "#else", + "#ifdef ETIM", + " && (trpt->tau&1) /* already tried timeout */", + "#endif", + "#endif", + "#ifndef NOREDUCE", + " /* see below */", + " && !((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))", + "#endif", + " && now._cnt[now._a_t&1] > 0) /* needed more procs */", + " { depth++; trpt++;", + " trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4));", + " trpt->bup.oval = now._cnt[now._a_t&1];", + " now._cnt[now._a_t&1] = 1;", + "#ifdef VERI", + " trpt->tau = 4;", + "#else", + " trpt->tau = 0;", + "#endif", + " From = now._nr_pr-1; To = BASE;", + "#if defined(VERBOSE) || defined(CHECK)", + " printf(\"%%3d: fairness default move \", depth);", + " printf(\"(all procs block)\\n\");", + "#endif", + " goto Down;", + " }", +"#endif", + "Q999: /* returns here with _n>0 when done */;", + + " if (trpt->o_pm&8)", + " { now._a_t &= ~2;", + " now._cnt[now._a_t&1] = 0;", + " trpt->o_pm &= ~8;", + "#ifdef VERBOSE", + " printf(\"%%3d: fairness undo Rule 1, _a_t=%%d\\n\",", + " depth, now._a_t);", + "#endif", + " }", + " if (trpt->o_pm&16)", + " { now._a_t |= 2;", /* restore a-bit */ + " now._cnt[now._a_t&1] = 1;", /* NEW: restore cnt */ + " trpt->o_pm &= ~16;", + "#ifdef VERBOSE", + " printf(\"%%3d: fairness undo Rule 3, _a_t=%%d\\n\",", + " depth, now._a_t);", + "#endif", + " }", + "#endif", + + "#ifndef NOREDUCE", +"#ifdef SAFETY", + " /* preselected move - no successors outside stack */", + " if ((trpt->tau&32) && !(trpt->tau&64))", + " { From = now._nr_pr-1; To = BASE;", + "#ifdef DEBUG", + " printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ", + " depth, II+1, _n, trpt->tau);", + "#endif", + " _n = 0; trpt->tau &= ~(16|32|64);", + " if (II >= BASE) /* II already decremented */", + " goto Resume;", + " else", + " goto Again;", + " }", +"#else", + " /* at least one move that was preselected at this */", + " /* level, blocked or truncated at the next level */", + "/* implied: #ifdef FULLSTACK */", + " if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))", + " {", + "#ifdef DEBUG", + " printf(\"%%3d: proc %%d UnSelected (_n=%%d, tau=%%d)\\n\", ", + " depth, II+1, (int) _n, trpt->tau);", + "#endif", + " if (a_cycles && (trpt->tau&16))", + " { if (!(now._a_t&1))", + " {", + "#ifdef DEBUG", + " printf(\"%%3d: setting proviso bit\\n\", depth);", + "#endif", + "#ifndef BITSTATE", + "#ifdef MA", + "#ifdef VERI", + " (trpt-1)->proviso = 1;", + "#else", + " trpt->proviso = 1;", + "#endif", + "#else", + "#ifdef VERI", + " if ((trpt-1)->ostate)", + " ((char *)&((trpt-1)->ostate->state))[0] |= 128;", + "#else", + " ((char *)&(trpt->ostate->state))[0] |= 128;", + "#endif", + "#endif", + "#else", + "#ifdef VERI", + " if ((trpt-1)->ostate)", + " (trpt-1)->ostate->proviso = 1;", + "#else", + " trpt->ostate->proviso = 1;", + "#endif", + "#endif", + " From = now._nr_pr-1; To = BASE;", + " _n = 0; trpt->tau &= ~(16|32|64);", + " goto Again; /* do full search */", + " } /* else accept reduction */", + " } else", + " { From = now._nr_pr-1; To = BASE;", + " _n = 0; trpt->tau &= ~(16|32|64);", + " if (II >= BASE) /* already decremented */", + " goto Resume;", + " else", + " goto Again;", + " } }", + "/* #endif */", +"#endif", + "#endif", + + " if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2)))", + " {", + "#ifdef DEBUG", + " printf(\"%%3d: no move [II=%%d, tau=%%d, boq=%%d]\\n\",", + " depth, II, trpt->tau, boq);", + "#endif", + "#if SYNC", + " /* ok if a rendez-vous fails: */", + " if (boq != -1) goto Done;", + "#endif", + " /* ok if no procs or we're at maxdepth */", + " if ((now._nr_pr == 0 && (!strict || qs_empty()))", + "#ifdef OTIM", + " || endstate()", + "#endif", + " || depth >= maxdepth-1) goto Done;", + + " if ((trpt->tau&8) && !(trpt->tau&4))", + " { trpt->tau &= ~(1|8);", + " /* 1=timeout, 8=atomic */", + " From = now._nr_pr-1; To = BASE;", + "#ifdef DEBUG", + " printf(\"%%3d: atomic step proc %%d \", depth, II+1);", + " printf(\"unexecutable\\n\");", + "#endif", + "#ifdef VERI", + " trpt->tau |= 4; /* switch to claim */", + "#endif", + " goto AllOver;", + " }", + + "#ifdef ETIM", + " if (!(trpt->tau&1)) /* didn't try timeout yet */", + " {", + "#ifdef VERI", + " if (trpt->tau&4)", + " {", + "#ifndef NTIM", + " if (trpt->tau&2) /* requested */", + "#endif", + " { trpt->tau |= 1;", + " trpt->tau &= ~2;", + "#ifdef DEBUG", + " printf(\"%%d: timeout\\n\", depth);", + "#endif", + " goto Stutter;", + " } }", + " else", + " { /* only claim can enable timeout */", + " if ((trpt->tau&8)", + " && !((trpt-1)->tau&4))", + "/* blocks inside an atomic */ goto BreakOut;", + "#ifdef DEBUG", + " printf(\"%%d: req timeout\\n\",", + " depth);", + "#endif", + " (trpt-1)->tau |= 2; /* request */", + " goto Up;", + " }", + "#else", + + "#ifdef DEBUG", + " printf(\"%%d: timeout\\n\", depth);", + "#endif", + " trpt->tau |= 1;", + " goto Again;", + "#endif", + " }", + "#endif", + + /* old location of atomic block code */ + "#ifdef VERI", + "BreakOut:", + "#ifndef NOSTUTTER", + " if (!(trpt->tau&4))", + " { trpt->tau |= 4; /* claim stuttering */", + " trpt->tau |= 128; /* stutter mark */", + "#ifdef DEBUG", + " printf(\"%%d: claim stutter\\n\", depth);", + "#endif", + " goto Stutter;", + " }", + "#else", + " ;", + "#endif", + "#else", + " if (!noends && !a_cycles && !endstate())", + " { depth--; trpt--; /* new 4.2.3 */", + " uerror(\"invalid end state\");", + " depth++; trpt++;", + " }", + "#ifndef NOSTUTTER", + " else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */", + " { depth--; trpt--;", + " uerror(\"accept stutter\");", + " depth++; trpt++;", + " }", + "#endif", + "#endif", + " }", + "Done:", + " if (!(trpt->tau&8)) /* not in atomic seqs */", + " {", + "#ifndef SAFETY", + " if (_n != 0", /* we made a move */ + "#ifdef VERI", + " /* --after-- a program-step, i.e., */", + " /* after backtracking a claim-step */", + " && (trpt->tau&4)", + " /* with at least one running process */", + " /* unless in a stuttered accept state */", + " && ((now._nr_pr > 1) || (trpt->o_pm&2))", + "#endif", + " && !(now._a_t&1))", /* not in 2nd DFS */ + " {", + "#ifndef NOFAIR", + " if (fairness)", /* implies a_cycles */ + " {", + "#ifdef VERBOSE", + " printf(\"Consider check %%d %%d...\\n\",", + " now._a_t, now._cnt[0]);", + "#endif", +#if 0 + the a-bit is set, which means that the fairness + counter is running -- it was started in an accepting state. + we check that the counter reached 1, which means that all + processes moved least once. + this means we can start the search for cycles - + to be able to return to this state, we should be able to + run down the counter to 1 again -- which implies a visit to + the accepting state -- even though the Seed state for this + search is itself not necessarily accepting +#endif + " if ((now._a_t&2) /* A-bit */", + " && (now._cnt[0] == 1))", + " checkcycles();", + " } else", + "#endif", + " if (a_cycles && (trpt->o_pm&2))", + " checkcycles();", + " }", + "#endif", +"#ifndef MA", + "#if defined(FULLSTACK) || defined(CNTRSTACK)", + "#ifdef VERI", + " if (boq == -1", + " && (((trpt->tau&4) && !(trpt->tau&128))", + " || ( (trpt-1)->tau&128)))", + "#else", + " if (boq == -1)", + "#endif", + " {", + "#ifdef DEBUG2", + "#if defined(FULLSTACK)", + " printf(\"%%d: zapping %%u (%%d)\\n\",", + " depth, trpt->ostate,", + " (trpt->ostate)?trpt->ostate->tagged:0);", + "#endif", + "#endif", + " onstack_zap();", + " }", + "#endif", +"#else", + "#ifdef VERI", + " if (boq == -1", + " && (((trpt->tau&4) && !(trpt->tau&128))", + " || ( (trpt-1)->tau&128)))", + "#else", + " if (boq == -1)", + "#endif", + " {", + "#ifdef DEBUG", + " printf(\"%%d: zapping\\n\", depth);", + "#endif", + " onstack_zap();", + "#ifndef NOREDUCE", + " if (trpt->proviso)", + " gstore((char *) &now, vsize, 1);", + "#endif", + " }", +"#endif", + " }", + " if (depth > 0) goto Up;", + "}\n", + "#else", + "void new_state(void) { /* place holder */ }", + "#endif", /* BFS */ + "", + "void", + "assert(int a, char *s, int ii, int tt, Trans *t)", + "{", + " if (!a && !noasserts)", + " { char bad[1024];", + " strcpy(bad, \"assertion violated \");", + " if (strlen(s) > 1000)", + " { strncpy(&bad[19], (const char *) s, 1000);", + " bad[1019] = '\\0';", + " } else", + " strcpy(&bad[19], s);", + " uerror(bad);", + " }", + "}", + "#ifndef NOBOUNDCHECK", + "int", + "Boundcheck(int x, int y, int a1, int a2, Trans *a3)", + "{", + " assert((x >= 0 && x < y), \"- invalid array index\",", + " a1, a2, a3);", + " return x;", + "}", + "#endif", + "void", + "wrap_stats(void)", + "{", + " if (nShadow>0)", + " printf(\"%%8g states, stored (%%g visited)\\n\",", + " nstates - nShadow, nstates);", + " else", + " printf(\"%%8g states, stored\\n\", nstates);", + "#ifdef BFS", + "#if SYNC", + " printf(\" %%8g nominal states (- rv and atomic)\\n\", nstates-midrv-nlinks+revrv);", + " printf(\" %%8g rvs succeeded\\n\", midrv-failedrv);", + "#else", + " printf(\" %%8g nominal states (stored-atomic)\\n\", nstates-nlinks);", + "#endif", + "#ifdef DEBUG", + " printf(\" %%8g midrv\\n\", midrv);", + " printf(\" %%8g failedrv\\n\", failedrv);", + " printf(\" %%8g revrv\\n\", revrv);", + "#endif", + "#endif", + " printf(\"%%8g states, matched\\n\", truncs);", + "#ifdef CHECK", + " printf(\"%%8g matches within stack\\n\",truncs2);", + "#endif", + " if (nShadow>0)", + " printf(\"%%8g transitions (= visited+matched)\\n\",", + " nstates+truncs);", + " else", + " printf(\"%%8g transitions (= stored+matched)\\n\",", + " nstates+truncs);", + " printf(\"%%8g atomic steps\\n\", nlinks);", + " if (nlost) printf(\"%%g lost messages\\n\", (double) nlost);", + "", + "#ifndef BITSTATE", + " printf(\"hash conflicts: %%g (resolved)\\n\", hcmp);", + "#else", + "#ifdef CHECK", + " printf(\"%%8g states allocated for dfs stack\\n\", ngrabs);", + "#endif", + " printf(\"\\nhash factor: %%4g (best if > 100.)\\n\\n\",", + " (double)(1<<(ssize-8)) / (double) nstates * 256.0);", + " printf(\"bits set per state: %%u (-k%%u)\\n\", hfns, hfns);", + "#if 0", +#ifndef POWOW + " if (udmem)", + " printf(\"total bits available: %%8g (-M%%ld)\\n\",", + " ((double) udmem) * 8.0, udmem/(1024L*1024L));", + " else", +#endif + " printf(\"total bits available: %%8g (-w%%d)\\n\",", + " ((double) (1L << (ssize-4)) * 16.0), ssize);", + "#endif", +"#ifdef COVEST", + " /* this code requires compilation with -lm on some systems */", + " { double pow(double, double);", + " double log(double);", + " unsigned int best_k;", + " double i, n = 30000.0L;", + " double f, p, q, m, c, est = 0.0L, k = (double)hfns;", + " c = (double) nstates / n;", + " m = (double) (1<<(ssize-8)) * 256.0L / c;", + " p = 1.0L - (k / m); q = 1.0L;", + " for (i = 0.0L; i - est < n; i += 1.0L)", + " { q *= p;", + " est += pow(1.0L - q, k);", + " }", + " f = m/i;", + " est *= c;", + " i *= c;", + " /* account for loss from enhanced double hashing */", + " if (hfns > 2) est += i * pow(0.5, (double) ssize * 2.0);", + "", + " if (f < 1.134) best_k = 1;", + " else if (f < 2.348) best_k = 2;", + " else if (f < 3.644) best_k = 3;", + " else best_k = (unsigned int) (pow(3.8L,1.0L/(f+4.2L))*f*.69315L + 0.99999999L);", + "", + " if (best_k != hfns && best_k > ssize)", + " best_k = (unsigned int) 1.0 + ssize/log((double)best_k / (double)ssize + 3.0);", + "", + " if (best_k > 32)", + " best_k = 1 + (unsigned int) (32.0/log((double)best_k/35.0));", + "", + " if (est * (double) nstates < 1.0)", + " { printf(\"prob. of omissions: negligible\\n\");", + " return; /* no hints needed */", + " }", + "", + " if (best_k != hfns)", + " { printf(\"hint: repeating the search with -k%%u \", best_k);", + " printf(\"may increase accuracy\\n\");", + " } else", + " { printf(\"hint: the current setting for -k (-k%%d) \", hfns);", + " printf(\"is likely to be optimal for -w%%d\\n\", ssize);", + " }", + " if (ssize < 32)", + " { printf(\"hint: increasing -w above -w%%d \", ssize);", + " printf(\"will increase accuracy (max is -w34)\\n\");", + " printf(\"(in xspin, increase Estimated State Space Size)\\n\");", + " } }", +"#endif", + "#endif", + "}", + "void", + "wrapup(void)", + "{", + "#if defined(BITSTATE) || !defined(NOCOMP)", + " double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0;", + "#if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM))", + " int mverbose = 1;", + "#else", + " int mverbose = verbose;", + "#endif", + "#endif", + + " signal(SIGINT, SIG_DFL);", + " printf(\"(%%s)\\n\", Version);", + " if (!done) printf(\"Warning: Search not completed\\n\");", + "#ifdef SC", + " (void) unlink((const char *)stackfile);", + "#endif", + "#ifdef BFS", + " printf(\" + Using Breadth-First Search\\n\");", + "#endif", + "#ifndef NOREDUCE", + " printf(\" + Partial Order Reduction\\n\");", + "#endif", +#if 0 + "#ifdef Q_PROVISO", + " printf(\" + Queue Proviso\\n\");", + "#endif", +#endif + "#ifdef COLLAPSE", + " printf(\" + Compression\\n\");", + "#endif", + "#ifdef MA", + " printf(\" + Graph Encoding (-DMA=%%d)\\n\", MA);", + "#ifdef R_XPT", + " printf(\" Restarted from checkpoint %%s.xpt\\n\", Source);", + "#endif", + "#endif", + "#ifdef CHECK", + "#ifdef FULLSTACK", + " printf(\" + FullStack Matching\\n\");", + "#endif", + "#ifdef CNTRSTACK", + " printf(\" + CntrStack Matching\\n\");", + "#endif", + "#endif", + "#ifdef BITSTATE", + " printf(\"\\nBit statespace search for:\\n\");", + "#else", + "#ifdef HC", + " printf(\"\\nHash-Compact %%d search for:\\n\", HC);", + "#else", + " printf(\"\\nFull statespace search for:\\n\");", + "#endif", + "#endif", + "#ifdef EVENT_TRACE", + "#ifdef NEGATED_TRACE", + " printf(\"\tnotrace assertion \t+\\n\");", + "#else", + " printf(\"\ttrace assertion \t+\\n\");", + "#endif", + "#endif", + "#ifdef VERI", + " printf(\"\tnever claim \t+\\n\");", + " printf(\"\tassertion violations\t\");", + " if (noasserts)", + " printf(\"- (disabled by -A flag)\\n\");", + " else", + " printf(\"+ (if within scope of claim)\\n\");", + "#else", + "#ifdef NOCLAIM", + " printf(\"\tnever claim \t- (not selected)\\n\");", + "#else", + " printf(\"\tnever claim \t- (none specified)\\n\");", + "#endif", + " printf(\"\tassertion violations\t\");", + " if (noasserts)", + " printf(\"- (disabled by -A flag)\\n\");", + " else", + " printf(\"+\\n\");", + "#endif", + "#ifndef SAFETY", + "#ifdef NP", + " printf(\"\tnon-progress cycles \t\");", + "#else", + " printf(\"\tacceptance cycles \t\");", + "#endif", + " if (a_cycles)", + " printf(\"+ (fairness %%sabled)\\n\",", + " fairness?\"en\":\"dis\");", + " else printf(\"- (not selected)\\n\");", + "#else", + " printf(\"\tcycle checks \t- (disabled by -DSAFETY)\\n\");", + "#endif", + "#ifdef VERI", + " printf(\"\tinvalid end states\t- \");", + " printf(\"(disabled by \");", + " if (noends)", + " printf(\"-E flag)\\n\\n\");", + " else", + " printf(\"never claim)\\n\\n\");", + "#else", + " printf(\"\tinvalid end states\t\");", + " if (noends)", + " printf(\"- (disabled by -E flag)\\n\\n\");", + " else", + " printf(\"+\\n\\n\");", + "#endif", + " printf(\"State-vector %%d byte, depth reached %%d\", ", + " hmax, mreached);", + " printf(\", errors: %%d\\n\", errors);", + "#ifdef MA", + " if (done)", + " { extern void dfa_stats(void);", + " if (maxgs+a_cycles+2 < MA)", + " printf(\"MA stats: -DMA=%%d is sufficient\\n\",", + " maxgs+a_cycles+2);", + " dfa_stats();", + " }", + "#endif", + " wrap_stats();", + "#ifdef CHECK", + " printf(\"stackframes: %%d/%%d\\n\\n\", smax, svmax);", + " printf(\"stats: fa %%d, fh %%d, zh %%d, zn %%d - \",", + " Fa, Fh, Zh, Zn);", + " printf(\"check %%d holds %%d\\n\", Ccheck, Cholds);", + " printf(\"stack stats: puts %%d, probes %%d, zaps %%d\\n\",", + " PUT, PROBE, ZAPS);", + "#else", + " printf(\"\\n\");", + "#endif", + "", + "#if defined(BITSTATE) || !defined(NOCOMP)", + " nr1 = (nstates-nShadow)*", + " (double)(hmax+sizeof(struct H_el)-sizeof(unsigned));", + "#ifdef BFS", + " nr2 = 0.0;", + "#else", + " nr2 = (double) ((maxdepth+3)*sizeof(Trail));", + "#endif", + + "#ifndef BITSTATE", + "#if !defined(MA) || defined(COLLAPSE)", + " nr3 = (double) (1L<<ssize)*sizeof(struct H_el *);", + "#endif", + "#else", +#ifndef POWOW + " if (udmem)", + " nr3 = (double) (udmem);", + " else", +#endif + " nr3 = (double) (1L<<(ssize-3));", + "#ifdef CNTRSTACK", + " nr3 += (double) (1L<<(ssize-3));", + "#endif", + "#ifdef FULLSTACK", + " nr5 = (double) (maxdepth*sizeof(struct H_el *));", + "#endif", + "#endif", + " nr4 = (double) (svmax * (sizeof(Svtack) + hmax))", + " + (double) (smax * (sizeof(Stack) + Maxbody));", + "#ifndef MA", + " if (mverbose || memcnt < nr1+nr2+nr3+nr4+nr5)", + "#endif", + " { double remainder = memcnt;", + " double tmp_nr = memcnt-nr3-nr4-(nr2-fragment)-nr5;", + " if (tmp_nr < 0.0) tmp_nr = 0.;", + " printf(\"Stats on memory usage (in Megabytes):\\n\");", + " printf(\"%%-6.3f\tequivalent memory usage for states\",", + " nr1/1000000.);", + " printf(\" (stored*(State-vector + overhead))\\n\");", + "#ifdef BITSTATE", +#ifndef POWOW + " if (udmem)", + " printf(\"%%-6.3f\tmemory used for hash array (-M%%ld)\\n\",", + " nr3/1000000., udmem/(1024L*1024L));", + " else", +#endif + " printf(\"%%-6.3f\tmemory used for hash array (-w%%d)\\n\",", + " nr3/1000000., ssize);", + " if (nr5 > 0.0)", + " printf(\"%%-6.3f\tmemory used for bit stack\\n\",", + " nr5/1000000.);", + " remainder = remainder - nr3 - nr5;", + "#else", + " printf(\"%%-6.3f\tactual memory usage for states\",", + " tmp_nr/1000000.);", + " remainder = remainder - tmp_nr;", + " printf(\" (\");", + " if (tmp_nr > 0.)", + " { if (tmp_nr > nr1) printf(\"unsuccessful \");", + " printf(\"compression: %%.2f%%%%)\\n\",", + " (100.0*tmp_nr)/nr1);", + " } else", + " printf(\"less than 1k)\\n\");", + "#ifndef MA", + " if (tmp_nr > 0.)", + " { printf(\"\tState-vector as stored = %%.0f byte\",", + " (tmp_nr)/(nstates-nShadow) -", + " (double) (sizeof(struct H_el) - sizeof(unsigned)));", + " printf(\" + %%ld byte overhead\\n\",", + " sizeof(struct H_el)-sizeof(unsigned));", + " }", + "#endif", + "#if !defined(MA) || defined(COLLAPSE)", + " printf(\"%%-6.3f\tmemory used for hash table (-w%%d)\\n\",", + " nr3/1000000., ssize);", + " remainder = remainder - nr3;", + "#endif", + "#endif", + "#ifndef BFS", + " printf(\"%%-6.3f\tmemory used for DFS stack (-m%%ld)\\n\",", + " nr2/1000000., maxdepth);", + " remainder = remainder - nr2;", + "#endif", + " if (remainder - fragment > 0.0)", + " printf(\"%%-6.3f\tother (proc and chan stacks)\\n\",", + " (remainder-fragment)/1000000.);", + " if (fragment > 0.0)", + " printf(\"%%-6.3f\tmemory lost to fragmentation\\n\",", + " fragment/1000000.);", + " printf(\"%%-6.3f\ttotal actual memory usage\\n\\n\",", + " memcnt/1000000.);", + " }", + "#ifndef MA", + " else", + "#endif", + "#endif", + "#ifndef MA", + " printf(\"%%-6.3f\tmemory usage (Mbyte)\\n\\n\",", + " memcnt/1000000.);", + "#endif", + "#ifdef COLLAPSE", + " printf(\"nr of templates: [ globals chans procs ]\\n\");", + " printf(\"collapse counts: [ \");", + " { int i; for (i = 0; i < 256+2; i++)", + " if (ncomps[i] != 0)", + " printf(\"%%d \", ncomps[i]);", + " printf(\"]\\n\");", + " }", + "#endif", + + " if ((done || verbose) && !no_rck) do_reach();", + "#ifdef PEG", + " { int i;", + " printf(\"\\nPeg Counts (transitions executed):\\n\");", + " for (i = 1; i < NTRANS; i++)", + " { if (peg[i]) putpeg(i, peg[i]);", + " } }", + "#endif", + "#ifdef VAR_RANGES", + " dumpranges();", + "#endif", + "#ifdef SVDUMP", + " if (vprefix > 0) close(svfd);", + "#endif", + " pan_exit(0);", + "}\n", + "void", + "stopped(int arg)", + "{ printf(\"Interrupted\\n\");", + " wrapup();", + " pan_exit(0);", + "}", + "/*", + " * based on Bob Jenkins hash-function from 1996", + " * see: http://www.burtleburtle.net/bob/", + " */", + "", +"#if defined(HASH64) || defined(WIN64)", + /* 64-bit Jenkins hash: http://burtleburtle.net/bob/c/lookup8.c */ + "#define mix(a,b,c) \\", + "{ a -= b; a -= c; a ^= (c>>43); \\", + " b -= c; b -= a; b ^= (a<<9); \\", + " c -= a; c -= b; c ^= (b>>8); \\", + " a -= b; a -= c; a ^= (c>>38); \\", + " b -= c; b -= a; b ^= (a<<23); \\", + " c -= a; c -= b; c ^= (b>>5); \\", + " a -= b; a -= c; a ^= (c>>35); \\", + " b -= c; b -= a; b ^= (a<<49); \\", + " c -= a; c -= b; c ^= (b>>11); \\", + " a -= b; a -= c; a ^= (c>>12); \\", + " b -= c; b -= a; b ^= (a<<18); \\", + " c -= a; c -= b; c ^= (b>>22); \\", + "}", +"#else", + "#define mix(a,b,c) \\", + "{ a -= b; a -= c; a ^= (c>>13); \\", + " b -= c; b -= a; b ^= (a<<8); \\", + " c -= a; c -= b; c ^= (b>>13); \\", + " a -= b; a -= c; a ^= (c>>12); \\", + " b -= c; b -= a; b ^= (a<<16); \\", + " c -= a; c -= b; c ^= (b>>5); \\", + " a -= b; a -= c; a ^= (c>>3); \\", + " b -= c; b -= a; b ^= (a<<10); \\", + " c -= a; c -= b; c ^= (b>>15); \\", + "}", +"#endif", + "void", + "d_hash(uchar *Cp, int Om) /* double bit hash - Jenkins */", + "{ unsigned long a = 0x9e3779b9, b, c = 0, len, length;", + " unsigned long *k = (unsigned long *) Cp;", + "", + " length = len = (unsigned long) ((unsigned long) Om + WS-1)/WS;", + "", + " b = HASH_CONST[HASH_NR];", + " while (len >= 3)", + " { a += k[0];", + " b += k[1];", + " c += k[2];", + " mix(a,b,c);", + " k += 3; len -= 3;", + " }", + " c += length;", + " switch (len) {", + " case 2: b += k[1];", + " case 1: a += k[0];", + " }", + " mix(a,b,c);", + " j1 = c&nmask; j3 = a&7;", /* 1st bit */ + " j2 = b&nmask; j4 = (a>>3)&7;", /* 2nd bit */ + " K1 = c; K2 = b;", /* no nmask */ + "}", + "void", + "s_hash(uchar *cp, int om)", + "{ d_hash(cp, om); /* sets K1 and K2 */", + "#ifdef BITSTATE", + " if (S_Tab == H_tab)", /* state stack in bitstate search */ + " j1 = K1 %% omaxdepth;", + " else", + "#endif", /* if (S_Tab != H_Tab) */ + " if (ssize < 8*WS)", + " j1 = K1&mask;", + " else", + " j1 = K1;", + "}", + "#ifndef RANDSTOR", + "int *prerand;", + "void", + "inirand(void)", + "{ int i;", + " srand(123); /* fixed startpoint */", + " prerand = (int *) emalloc((omaxdepth+3)*sizeof(int));", + " for (i = 0; i < omaxdepth+3; i++)", + " prerand[i] = rand();", + "}", + "int", + "pan_rand(void)", + "{ if (!prerand) inirand();", + " return prerand[depth];", + "}", + "#endif", + "", + "int", + "main(int argc, char *argv[])", + "{ void to_compile(void);\n", + " efd = stderr; /* default */", + "#ifdef BITSTATE", + " bstore = bstore_reg; /* default */", + "#endif", + " while (argc > 1 && argv[1][0] == '-')", + " { switch (argv[1][1]) {", + "#ifndef SAFETY", + "#ifdef NP", + " case 'a': fprintf(efd, \"error: -a disabled\");", + " usage(efd); break;", + "#else", + " case 'a': a_cycles = 1; break;", + "#endif", + "#endif", + " case 'A': noasserts = 1; break;", + " case 'b': bounded = 1; break;", + " case 'c': upto = atoi(&argv[1][2]); break;", + " case 'd': state_tables++; break;", + " case 'e': every_error = 1; Nr_Trails = 1; break;", + " case 'E': noends = 1; break;", + "#ifdef SC", + " case 'F': if (strlen(argv[1]) > 2)", + " stackfile = &argv[1][2];", + " break;", + "#endif", + "#if !defined(SAFETY) && !defined(NOFAIR)", + " case 'f': fairness = 1; break;", + "#endif", + " case 'h': if (!argv[1][2]) usage(efd); else", + " HASH_NR = atoi(&argv[1][2])%%33; break;", + " case 'I': iterative = 2; every_error = 1; break;", + " case 'i': iterative = 1; every_error = 1; break;", + " case 'J': like_java = 1; break; /* Klaus Havelund */", + "#ifdef BITSTATE", + " case 'k': hfns = atoi(&argv[1][2]); break;", + "#endif", + "#ifndef SAFETY", + "#ifdef NP", + " case 'l': a_cycles = 1; break;", + "#else", + " case 'l': fprintf(efd, \"error: -l disabled\");", + " usage(efd); break;", + "#endif", + "#endif", +#ifndef POWOW + "#ifdef BITSTATE", + " case 'M': udmem = atoi(&argv[1][2]); break;", + " case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break;", + "#else", + " case 'M': case 'G':", + " fprintf(stderr, \"-M and -G affect only -DBITSTATE\\n\");", + " break;", + "#endif", +#endif + " case 'm': maxdepth = atoi(&argv[1][2]); break;", + " case 'n': no_rck = 1; break;", + "#ifdef SVDUMP", + " case 'p': vprefix = atoi(&argv[1][2]); break;", + "#endif", + " case 'q': strict = 1; break;", + "#ifdef HAS_CODE", + " case 'r':", + "samething: readtrail = 1;", + " if (isdigit(argv[1][2]))", + " whichtrail = atoi(&argv[1][2]);", + " break;", + " case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]); break;", + " case 'C': coltrace = 1; goto samething;", + " case 'g': gui = 1; goto samething;", + " case 'S': silent = 1; break;", + "#endif", + " case 'R': Nrun = atoi(&argv[1][2]); break;", + "#ifdef BITSTATE", + " case 's': hfns = 1; break;", + "#endif", + " case 'T': TMODE = 0444; break;", + " case 't': if (argv[1][2]) tprefix = &argv[1][2]; break;", + " case 'V': printf(\"Generated by %%s\\n\", Version);", + " to_compile(); pan_exit(0); break;", + " case 'v': verbose = 1; break;", + " case 'w': ssize = atoi(&argv[1][2]); break;", + " case 'Y': signoff = 1; break;", + " case 'X': efd = stdout; break;", + " default : fprintf(efd, \"saw option -%%c\\n\", argv[1][1]); usage(efd); break;", + " }", + " argc--; argv++;", + " }", + " if (iterative && TMODE != 0666)", + " { TMODE = 0666;", + " fprintf(efd, \"warning: -T ignored when -i or -I is used\\n\");", + " }", + "#if defined(WIN32) || defined(WIN64)", + " if (TMODE == 0666)", + " TMODE = _S_IWRITE | _S_IREAD;", + " else", + " TMODE = _S_IREAD;", + "#endif", + "#ifdef OHASH", + " fprintf(efd, \"warning: -DOHASH no longer supported (directive ignored)\\n\");", + "#endif", + "#ifdef JHASH", + " fprintf(efd, \"warning: -DJHASH no longer supported (directive ignored)\\n\");", + "#endif", + "#ifdef HYBRID_HASH", + " fprintf(efd, \"warning: -DHYBRID_HASH no longer supported (directive ignored)\\n\");", + "#endif", + "#ifdef NOCOVEST", + " fprintf(efd, \"warning: -DNOCOVEST no longer supported (directive ignored)\\n\");", + "#endif", + "#ifdef BITSTATE", + "#ifdef BCOMP", + " fprintf(efd, \"warning: -DBCOMP no longer supported (directive ignored)\\n\");", + "#endif", + " if (hfns <= 0)", + " { hfns = 1;", + " fprintf(efd, \"warning: using -k%%d as minimal usable value\\n\", hfns);", + " }", + "#endif", + " omaxdepth = maxdepth;", + "#ifdef BITSTATE", + " if (WS == 4 && ssize > 34)", /* 32-bit word size */ + " { ssize = 34;", + " fprintf(efd, \"warning: using -w%%d as max usable value\\n\", ssize);", + "/*", + " * -w35 would not work: 35-3 = 32 but 1^31 is the largest", + " * power of 2 that can be represented in an unsigned long", + " */", + " }", + "#else", + " if (WS == 4 && ssize > 27)", + " { ssize = 27;", + " fprintf(efd, \"warning: using -w%%d as max usable value\\n\", ssize);", + "/*", + " * for emalloc, the lookup table size multiplies by 4 for the pointers", + " * the largest power of 2 that can be represented in a ulong is 1^31", + " * hence the largest number of lookup table slots is 31-4 = 27", + " */", + " }", + + "#endif", + "#ifdef SC", + " hiwater = HHH = maxdepth-10;", + " DDD = HHH/2;", + " if (!stackfile)", + " { stackfile = (char *) emalloc(strlen(Source)+4+1);", + " sprintf(stackfile, \"%%s._s_\", Source);", + " }", + " if (iterative)", + " { fprintf(efd, \"error: cannot use -i or -I with -DSC\\n\");", + " pan_exit(1);", + " }", + "#endif", + + "#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA)", + " fprintf(efd, \"error: -D?_XPT requires -DMA\\n\");", + " exit(1);", + "#endif", + + " if (iterative && a_cycles)", + " fprintf(efd, \"warning: -i or -I work for safety properties only\\n\");", + + "#ifdef BFS", + "#if defined(SC)", + " fprintf(efd, \"error: -DBFS not compatible with -DSC\\n\");", + " exit(1);", + "#endif", + "#if defined(HAS_LAST)", + " fprintf(efd, \"error: -DBFS not compatible with _last\\n\");", + " exit(1);", + "#endif", + "#if defined(REACH)", + " fprintf(efd, \"warning: -DREACH redundant when -DBFS is used\\n\");", + "#endif", + "#if defined(HAS_STACK)", + " fprintf(efd, \"error: cannot use UnMatched qualifier on c_track with BFS\\n\");", + " exit(1);", + "#endif", + "#endif", + + "#if defined(MERGED) && defined(PEG)", + " fprintf(efd, \"error: to allow -DPEG use: spin -o3 -a %%s\\n\", Source);", + " fprintf(efd, \" to turn off transition merge optimization\\n\");", + " pan_exit(1);", + "#endif", + "#ifdef HC", + "#ifdef NOCOMP", + " fprintf(efd, \"error: cannot combine -DHC and -DNOCOMP\\n\");", + " pan_exit(1);", + "#endif", + "#ifdef BITSTATE", + " fprintf(efd, \"error: cannot combine -DHC and -DBITSTATE\\n\");", + " pan_exit(1);", + "#endif", + "#endif", + "#if defined(SAFETY) && defined(NP)", + " fprintf(efd, \"error: cannot combine -DNP and -DSAFETY\\n\");", + " pan_exit(1);", + "#endif", + "#ifdef MA", + "#ifdef BITSTATE", + " fprintf(efd, \"error: cannot combine -DMA and -DBITSTATE\\n\");", + " pan_exit(1);", + "#endif", + " if (MA <= 0)", + " { fprintf(efd, \"usage: -DMA=N with N > 0 and < VECTORSZ\\n\");", + " pan_exit(1);", + " }", + "#endif", + "#ifdef COLLAPSE", + "#if defined(BITSTATE)", + " fprintf(efd, \"error: cannot combine -DBITSTATE and -DCOLLAPSE\\n\");", + " pan_exit(1);", + "#endif", + "#if defined(NOCOMP)", + " fprintf(efd, \"error: cannot combine -DNOCOMP and -DCOLLAPSE\\n\");", + " pan_exit(1);", + "#endif", + "#endif", + " if (maxdepth <= 0 || ssize <= 1) usage(efd);", + "#if SYNC>0 && !defined(NOREDUCE)", + " if (a_cycles && fairness)", + " { fprintf(efd, \"error: p.o. reduction not compatible with \");", + " fprintf(efd, \"fairness (-f) in models\\n\");", + " fprintf(efd, \" with rendezvous operations: \");", + " fprintf(efd, \"recompile with -DNOREDUCE\\n\");", + " pan_exit(1);", + " }", + "#endif", + "#if defined(REM_VARS) && !defined(NOREDUCE)", + " { fprintf(efd, \"warning: p.o. reduction not compatible with \");", + " fprintf(efd, \"remote varrefs (use -DNOREDUCE)\\n\");", + " }", + "#endif", + "#if defined(NOCOMP) && !defined(BITSTATE)", + " if (a_cycles)", + " { fprintf(efd, \"error: -DNOCOMP voids -l and -a\\n\");", + " pan_exit(1);", + " }", + "#endif", + + "#ifdef MEMLIM", /* MEMLIM setting takes precedence */ + " memlim = (double) MEMLIM * (double) (1<<20); /* size in Mbyte */", + "#else", + "#ifdef MEMCNT", + "#if MEMCNT<31", + " memlim = (double) (1<<MEMCNT);", + "#else", + " memlim = (double) (1<<30);", + " memlim *= (double) (1<<(MEMCNT-30));", + "#endif", + "#endif", + "#endif", + + "#ifndef BITSTATE", + " if (Nrun > 1) HASH_NR = Nrun - 1;", + "#endif", + " if (Nrun < 1 || Nrun > 32)", + " { fprintf(efd, \"error: invalid arg for -R\\n\");", + " usage(efd);", + " }", + "#ifndef SAFETY", + " if (fairness && !a_cycles)", + " { fprintf(efd, \"error: -f requires -a or -l\\n\");", + " usage(efd);", + " }", + "#if ACCEPT_LAB==0", + " if (a_cycles)", + "#ifndef VERI", + " { fprintf(efd, \"error: no accept labels defined \");", + " fprintf(efd, \"in model (for option -a)\\n\");", + " usage(efd);", + " }", + "#else", + " { fprintf(efd, \"warning: no explicit accept labels \");", + " fprintf(efd, \"defined in model (for -a)\\n\");", + " }", + "#endif", + "#endif", + "#endif", + "#if !defined(NOREDUCE)", + "#if defined(HAS_ENABLED)", + " fprintf(efd, \"error: reduced search precludes \");", + " fprintf(efd, \"use of 'enabled()'\\n\");", + " pan_exit(1);", + "#endif", + "#if defined(HAS_PCVALUE)", + " fprintf(efd, \"error: reduced search precludes \");", + " fprintf(efd, \"use of 'pcvalue()'\\n\");", + " pan_exit(1);", + "#endif", + "#if defined(HAS_BADELSE)", + " fprintf(efd, \"error: reduced search precludes \");", + " fprintf(efd, \"using 'else' combined with i/o stmnts\\n\");", + " pan_exit(1);", + "#endif", + "#if defined(HAS_LAST)", + " fprintf(efd, \"error: reduced search precludes \");", + " fprintf(efd, \"use of _last\\n\");", + " pan_exit(1);", + "#endif", + "#endif", + + "#if SYNC>0 && !defined(NOREDUCE)", + "#ifdef HAS_UNLESS", + " fprintf(efd, \"warning: use of a rendezvous stmnts in the escape\\n\");", + " fprintf(efd, \"\tof an unless clause, if present, could make p.o. reduction\\n\");", + " fprintf(efd, \"\tinvalid (use -DNOREDUCE to avoid this)\\n\");", + "#ifdef BFS", + " fprintf(efd, \"\t(this type of rv is also not compatible with -DBFS)\\n\");", + "#endif", + "#endif", + "#endif", + "#if SYNC>0 && defined(BFS)", + " fprintf(efd, \"warning: use of rendezvous in BFS mode \");", + " fprintf(efd, \"does not preserve all invalid endstates\\n\");", + "#endif", + "#if !defined(REACH) && !defined(BITSTATE)", + " if (iterative != 0 && a_cycles == 0)", + " fprintf(efd, \"warning: -i and -I need -DREACH to work accurately\\n\");", + "#endif", + "#if defined(BITSTATE) && defined(REACH)", + " fprintf(efd, \"warning: -DREACH voided by -DBITSTATE\\n\");", + "#endif", + "#if defined(MA) && defined(REACH)", + " fprintf(efd, \"warning: -DREACH voided by -DMA\\n\");", + "#endif", + "#if defined(FULLSTACK) && defined(CNTRSTACK)", + " fprintf(efd, \"error: cannot combine\");", + " fprintf(efd, \" -DFULLSTACK and -DCNTRSTACK\\n\");", + " pan_exit(1);", + "#endif", + "#if defined(VERI)", + "#if ACCEPT_LAB>0", + "#ifndef BFS", + " if (!a_cycles", + "#ifdef HAS_CODE", + " && !readtrail", + "#endif", + " && !state_tables)", + " { fprintf(efd, \"warning: never claim + accept labels \");", + " fprintf(efd, \"requires -a flag to fully verify\\n\");", + " }", + "#else", + " if (", + "#ifdef HAS_CODE", + " !readtrail", + "#endif", + " && !state_tables)", + " { fprintf(efd, \"warning: verification in BFS mode \");", + " fprintf(efd, \"is restricted to safety properties\\n\");", + " }", + "#endif", + "#endif", + "#endif", + "#ifndef SAFETY", + " if (!a_cycles", + "#ifdef HAS_CODE", + " && !readtrail", + "#endif", + " && !state_tables)", + " { fprintf(efd, \"hint: this search is more efficient \");", + " fprintf(efd, \"if pan.c is compiled -DSAFETY\\n\");", + " }", + "#ifndef NOCOMP", + " if (!a_cycles)", + " S_A = 0;", + " else", + " { if (!fairness)", + " S_A = 1; /* _a_t */", + "#ifndef NOFAIR", + " else /* _a_t and _cnt[NFAIR] */", + " S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2;", + " /* -2 because first two uchars in now are masked */", + "#endif", + " }", + "#endif", + "#endif", + " signal(SIGINT, stopped);", + + /******************* 4.2.5 ********************/ + " if (WS == 4 && ssize >= 32)", + " { mask = 0xffffffff;", + "#ifdef BITSTATE", + " switch (ssize) {", + " case 34: nmask = (mask>>1); break;", + " case 33: nmask = (mask>>2); break;", + " default: nmask = (mask>>3); break;", + " }", + "#else", + " nmask = mask;", + "#endif", + " } else if (WS == 8)", + " { mask = ((1L<<ssize)-1); /* hash init */", + "#ifdef BITSTATE", + " nmask = mask>>3;", + "#else", + " nmask = mask;", + "#endif", + " } else if (WS != 4)", + " { fprintf(stderr, \"pan: wordsize %%ld not supported\\n\", WS);", + " exit(1);", + " } else /* WS == 4 and ssize < 32 */", + " { mask = ((1L<<ssize)-1); /* hash init */", + " nmask = (mask>>3);", + " }", + /****************** end **********************/ + + "#ifdef BFS", + " trail = (Trail *) emalloc(6*sizeof(Trail));", + " trail += 3;", + "#else", + " trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail));", + " trail++; /* protect trpt-1 refs at depth 0 */", + "#endif", + "#ifdef SVDUMP", + " if (vprefix > 0)", + " { char nm[64];", + " sprintf(nm, \"%%s.svd\", Source);", + " if ((svfd = creat(nm, 0666)) < 0)", + " { fprintf(efd, \"couldn't create %%s\\n\", nm);", + " vprefix = 0;", + " } }", + "#endif", + "#ifdef RANDSTOR", + " srand(123);", + "#endif", + "#if SYNC>0 && ASYNC==0", + " set_recvs();", + "#endif", + " run();", + " done = 1;", + " wrapup();", + " return 0;", + "}", /* end of main() */ + "", + "void", + "usage(FILE *fd)", + "{", + " fprintf(fd, \"Valid Options are:\\n\");", + "#ifndef SAFETY", + "#ifdef NP", + " fprintf(fd, \"\t-a -> is disabled by -DNP \");", + " fprintf(fd, \"(-DNP compiles for -l only)\\n\");", + "#else", + " fprintf(fd, \"\t-a find acceptance cycles\\n\");", + "#endif", + "#else", + " fprintf(fd, \"\t-a,-l,-f -> are disabled by -DSAFETY\\n\");", + "#endif", + " fprintf(fd, \"\t-A ignore assert() violations\\n\");", + " fprintf(fd, \"\t-b consider it an error to exceed the depth-limit\\n\");", + " fprintf(fd, \"\t-cN stop at Nth error \");", + " fprintf(fd, \"(defaults to -c1)\\n\");", + " fprintf(fd, \"\t-d print state tables and stop\\n\");", + " fprintf(fd, \"\t-e create trails for all errors\\n\");", + " fprintf(fd, \"\t-E ignore invalid end states\\n\");", + "#ifdef SC", + " fprintf(fd, \"\t-Ffile use 'file' to store disk-stack\\n\");", + "#endif", + "#ifndef NOFAIR", + " fprintf(fd, \"\t-f add weak fairness (to -a or -l)\\n\");", + "#endif", + " fprintf(fd, \"\t-hN use different hash-seed N:1..32\\n\");", + " fprintf(fd, \"\t-i search for shortest path to error\\n\");", + " fprintf(fd, \"\t-I like -i, but approximate and faster\\n\");", + " fprintf(fd, \"\t-J reverse eval order of nested unlesses\\n\");", + "#ifdef BITSTATE", + " fprintf(fd, \"\t-kN set N bits per state (defaults to 3)\\n\");", + "#endif", + "#ifndef SAFETY", + "#ifdef NP", + " fprintf(fd, \"\t-l find non-progress cycles\\n\");", + "#else", + " fprintf(fd, \"\t-l find non-progress cycles -> \");", + " fprintf(fd, \"disabled, requires \");", + " fprintf(fd, \"compilation with -DNP\\n\");", + "#endif", + "#endif", +#ifndef POWOW + "#ifdef BITSTATE", + " fprintf(fd, \"\t-MN use N Megabytes for bitstate hash array\\n\");", + " fprintf(fd, \"\t-GN use N Gigabytes for bitstate hash array\\n\");", + "#endif", +#endif + " fprintf(fd, \"\t-mN max depth N steps (default=10k)\\n\");", + " fprintf(fd, \"\t-n no listing of unreached states\\n\");", + "#ifdef SVDUMP", + " fprintf(fd, \"\t-pN create svfile (save N bytes per state)\\n\");", + "#endif", + " fprintf(fd, \"\t-q require empty chans in valid end states\\n\");", + "#ifdef HAS_CODE", + " fprintf(fd, \"\t-r read and execute trail - can add -v,-n,-PN,-g,-C\\n\");", + " fprintf(fd, \"\t-rN read and execute N-th error trail\\n\");", + " fprintf(fd, \"\t-C read and execute trail - columnated output (can add -v,-n)\\n\");", + " fprintf(fd, \"\t-PN read and execute trail - restrict trail output to proc N\\n\");", + " fprintf(fd, \"\t-g read and execute trail + msc gui support\\n\");", + " fprintf(fd, \"\t-S silent replay: only user defined printfs show\\n\");", + "#endif", + "#ifdef BITSTATE", + " fprintf(fd, \"\t-RN repeat run Nx with N \");", + " fprintf(fd, \"[1..32] independent hash functions\\n\");", + " fprintf(fd, \"\t-s same as -k1 (single bit per state)\\n\");", + "#endif", + " fprintf(fd, \"\t-T create trail files in read-only mode\\n\");", + " fprintf(fd, \"\t-tsuf replace .trail with .suf on trailfiles\\n\");", + " fprintf(fd, \"\t-V print SPIN version number\\n\");", + " fprintf(fd, \"\t-v verbose -- filenames in unreached state listing\\n\");", + " fprintf(fd, \"\t-wN hashtable of 2^N entries \");", + " fprintf(fd, \"(defaults to -w%%d)\\n\", ssize);", + " exit(1);", + "}", + "", + "char *", + "Malloc(unsigned long n)", + "{ char *tmp;", + "#if defined(MEMCNT) || defined(MEMLIM)", + " if (memcnt+ (double) n > memlim) goto err;", + "#endif", +"#if 1", + " tmp = (char *) malloc(n);", + " if (!tmp)", +"#else", + /* on linux machines, a large amount of memory is set aside + * for malloc, whether it is used or not + * using sbrk would make this memory arena inaccessible + * the reason for using sbrk was originally to provide a + * small additional speedup (since this memory is never released) + */ + " tmp = (char *) sbrk(n);", + " if (tmp == (char *) -1L)", +"#endif", + " {", + "#if defined(MEMCNT) || defined(MEMLIM)", + "err:", + "#endif", + " printf(\"pan: out of memory\\n\");", + "#if defined(MEMCNT) || defined(MEMLIM)", + " printf(\"\t%%g bytes used\\n\", memcnt);", + " printf(\"\t%%g bytes more needed\\n\", (double) n);", + " printf(\"\t%%g bytes limit\\n\",", + " memlim);", + "#endif", + "#ifdef COLLAPSE", + " printf(\"hint: to reduce memory, recompile with\\n\");", + "#ifndef MA", + " printf(\" -DMA=%%d # better/slower compression, or\\n\", hmax);", + "#endif", + " printf(\" -DBITSTATE # supertrace, approximation\\n\");", + "#else", + "#ifndef BITSTATE", + " printf(\"hint: to reduce memory, recompile with\\n\");", + "#ifndef HC", + " printf(\" -DCOLLAPSE # good, fast compression, or\\n\");", + "#ifndef MA", + " printf(\" -DMA=%%d # better/slower compression, or\\n\", hmax);", + "#endif", + " printf(\" -DHC # hash-compaction, approximation\\n\");", + "#endif", + " printf(\" -DBITSTATE # supertrace, approximation\\n\");", + "#endif", + "#endif", + " wrapup();", + " }", + " memcnt += (double) n;", + " return tmp;", + "}", + "", + "#define CHUNK (100*VECTORSZ)", + "", + "char *", + "emalloc(unsigned long n) /* never released or reallocated */", + "{ char *tmp;", + " if (n == 0)", + " return (char *) NULL;", + " if (n&(sizeof(void *)-1)) /* for proper alignment */", + " n += sizeof(void *)-(n&(sizeof(void *)-1));", + " if ((unsigned long) left < n)", /* was: (left < (long)n) */ + " { grow = (n < CHUNK) ? CHUNK : n;", +#if 1 + " have = Malloc(grow);", +#else + " /* gcc's sbrk can give non-aligned result */", + " grow += sizeof(void *); /* allow realignment */", + " have = Malloc(grow);", + " if (((unsigned) have)&(sizeof(void *)-1))", + " { have += (long) (sizeof(void *) ", + " - (((unsigned) have)&(sizeof(void *)-1)));", + " grow -= sizeof(void *);", + " }", +#endif + " fragment += (double) left;", + " left = grow;", + " }", + " tmp = have;", + " have += (long) n;", + " left -= (long) n;", + " memset(tmp, 0, n);", + " return tmp;", + "}", + + "void", + "Uerror(char *str)", + "{ /* always fatal */", + " uerror(str);", + " wrapup();", + "}\n", + "#if defined(MA) && !defined(SAFETY)", + "int", + "Unwind(void)", + "{ Trans *t; uchar ot, _m; int tt; short II;", + "#ifdef VERBOSE", + " int i;", + "#endif", + " uchar oat = now._a_t;", + " now._a_t &= ~(1|16|32);", + " memcpy((char *) &comp_now, (char *) &now, vsize);", + " now._a_t = oat;", + "Up:", + "#ifdef SC", + " trpt = getframe(depth);", + "#endif", + "#ifdef VERBOSE", + " printf(\"%%d State: \", depth);", + " for (i = 0; i < vsize; i++) printf(\"%%d%%s,\",", + " ((char *)&now)[i], Mask[i]?\"*\":\"\");", + " printf(\"\\n\");", + "#endif", + "#ifndef NOFAIR", + " if (trpt->o_pm&128) /* fairness alg */", + " { now._cnt[now._a_t&1] = trpt->bup.oval;", + " depth--;", + "#ifdef SC", + " trpt = getframe(depth);", + "#else", + " trpt--;", + "#endif", + " goto Q999;", + " }", + "#endif", + "#ifdef HAS_LAST", + "#ifdef VERI", + " { int d; Trail *trl;", + " now._last = 0;", + " for (d = 1; d < depth; d++)", + " { trl = getframe(depth-d); /* was trl = (trpt-d); */", + " if (trl->pr != 0)", + " { now._last = trl->pr - BASE;", + " break;", + " } } }", + "#else", + " now._last = (depth<1)?0:(trpt-1)->pr;", + "#endif", + "#endif", + "#ifdef EVENT_TRACE", + " now._event = trpt->o_event;", + "#endif", + " if ((now._a_t&1) && depth <= A_depth)", + " { now._a_t &= ~(1|16|32);", + " if (fairness) now._a_t |= 2; /* ? */", + " A_depth = 0;", + " goto CameFromHere; /* checkcycles() */", + " }", + " t = trpt->o_t;", + " ot = trpt->o_ot; II = trpt->pr;", + " tt = trpt->o_tt; this = pptr(II);", + " _m = do_reverse(t, II, trpt->o_m);", + "#ifdef VERBOSE", + " printf(\"%%3d: proc %%d \", depth, II);", + " printf(\"reverses %%d, %%d to %%d,\",", + " t->forw, tt, t->st);", + " printf(\" %%s [abit=%%d,adepth=%%d,\", ", + " t->tp, now._a_t, A_depth);", + " printf(\"tau=%%d,%%d] <unwind>\\n\", ", + " trpt->tau, (trpt-1)->tau);", + "#endif", + " depth--;", + "#ifdef SC", + " trpt = getframe(depth);", + "#else", + " trpt--;", + "#endif", + " /* reached[ot][t->st] = 1; 3.4.13 */", + " ((P0 *)this)->_p = tt;", + "#ifndef NOFAIR", + " if ((trpt->o_pm&32))", + " {", + "#ifdef VERI", + " if (now._cnt[now._a_t&1] == 0)", + " now._cnt[now._a_t&1] = 1;", + "#endif", + " now._cnt[now._a_t&1] += 1;", + " }", + "Q999:", + " if (trpt->o_pm&8)", + " { now._a_t &= ~2;", + " now._cnt[now._a_t&1] = 0;", + " }", + " if (trpt->o_pm&16)", + " now._a_t |= 2;", + "#endif", + "CameFromHere:", + " if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0)", + " return depth;", + " if (depth > 0) goto Up;", + " return 0;", + "}", + "#endif", + "static char unwinding;", + "void", + "uerror(char *str)", + "{ static char laststr[256];", + " int is_cycle;", + "", + " if (unwinding) return; /* 1.4.2 */", + " if (strncmp(str, laststr, 254))", + " printf(\"pan: %%s (at depth %%ld)\\n\", str,", + " (depthfound==-1)?depth:depthfound);", + " strncpy(laststr, str, 254);", + " errors++;", + "#ifdef HAS_CODE", + " if (readtrail) { wrap_trail(); return; }", + "#endif", + " is_cycle = (strstr(str, \" cycle\") != (char *) 0);", + " if (!is_cycle)", + " { depth++; trpt++;", /* include failed step */ + " }", + " if ((every_error != 0)", + " || errors == upto)", + " {", + "#if defined(MA) && !defined(SAFETY)", + " if (is_cycle)", + " { int od = depth;", + " unwinding = 1;", + " depthfound = Unwind();", + " unwinding = 0;", + " depth = od;", + " }", + "#endif", +"#ifdef BFS", + " if (depth > 1) trpt--;", + " nuerror(str);", + " if (depth > 1) trpt++;", +"#else", + " putrail();", +"#endif", + "#if defined(MA) && !defined(SAFETY)", + " if (strstr(str, \" cycle\"))", + " { if (every_error)", + " printf(\"sorry: MA writes 1 trail max\\n\");", + " wrapup(); /* no recovery from unwind */", + " }", + "#endif", + " }", + " if (!is_cycle)", + " { depth--; trpt--; /* undo */", + " }", +"#ifndef BFS", + " if (iterative != 0 && maxdepth > 0)", + " { maxdepth = (iterative == 1)?(depth-1):(depth/2);", + " warned = 1;", + " printf(\"pan: reducing search depth to %%ld\\n\",", + " maxdepth);", + " } else", +"#endif", + " if (errors >= upto && upto != 0)", + " wrapup();", + " depthfound = -1;", + "}\n", + "int", + "xrefsrc(int lno, S_F_MAP *mp, int M, int i)", + "{ Trans *T; int j, retval=1;", + " for (T = trans[M][i]; T; T = T->nxt)", + " if (T && T->tp)", + " { if (strcmp(T->tp, \".(goto)\") == 0", + " || strncmp(T->tp, \"goto :\", 6) == 0)", + " return 1; /* not reported */", + "", + " printf(\"\\tline %%d\", lno);", + " if (verbose)", + " for (j = 0; j < sizeof(mp); j++)", + " if (i >= mp[j].from && i <= mp[j].upto)", + " { printf(\", \\\"%%s\\\"\", mp[j].fnm);", + " break;", + " }", + " printf(\", state %%d\", i);", + " if (strcmp(T->tp, \"\") != 0)", + " { char *q;", + " q = transmognify(T->tp);", + " printf(\", \\\"%%s\\\"\", q?q:\"\");", + " } else if (stopstate[M][i])", + " printf(\", -end state-\");", + " printf(\"\\n\");", + " retval = 0; /* reported */", + " }", + " return retval;", + "}\n", + "void", + "r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp)", + "{ int i, m=0;\n", + "#ifdef VERI", + " if (M == VERI && !verbose) return;", + "#endif", + " printf(\"unreached in proctype %%s\\n\", procname[M]);", + " for (i = 1; i < N; i++)", +#if 0 + " if (which[i] == 0 /* && trans[M][i] */)", +#else + " if (which[i] == 0", + " && (mapstate[M][i] == 0", + " || which[mapstate[M][i]] == 0))", +#endif + " m += xrefsrc((int) src[i], mp, M, i);", + " else", + " m++;", + " printf(\"\t(%%d of %%d states)\\n\", N-1-m, N-1);", + "}\n", + "void", + "putrail(void)", + "{ int fd; long i, j;", + " Trail *trl;", + "#if defined VERI || defined(MERGED)", + " char snap[64];", + "#endif", + "", + " fd = make_trail();", + " if (fd < 0) return;", + "#ifdef VERI", + " sprintf(snap, \"-2:%%d:-2\\n\", VERI);", + " write(fd, snap, strlen(snap));", + "#endif", + "#ifdef MERGED", + " sprintf(snap, \"-4:-4:-4\\n\");", + " write(fd, snap, strlen(snap));", + "#endif", + " for (i = 1; i <= depth; i++)", + " { if (i == depthfound+1)", + " write(fd, \"-1:-1:-1\\n\", 9);", + " trl = getframe(i);", + " if (!trl->o_t) continue;", + " if (trl->o_pm&128) continue;", + " sprintf(snap, \"%%ld:%%d:%%d\\n\", ", + " i, trl->pr, trl->o_t->t_id);", + " j = strlen(snap);", + " if (write(fd, snap, j) != j)", + " { printf(\"pan: error writing trailfile\\n\");", + " close(fd);", + " wrapup();", + " }", + " }", + " close(fd);", + "}\n", + "void", + "sv_save(void) /* push state vector onto save stack */", + "{ if (!svtack->nxt)", + " { svtack->nxt = (Svtack *) emalloc(sizeof(Svtack));", + " svtack->nxt->body = emalloc(vsize*sizeof(char));", + " svtack->nxt->lst = svtack;", + " svtack->nxt->m_delta = vsize;", + " svmax++;", + " } else if (vsize > svtack->nxt->m_delta)", + " { svtack->nxt->body = emalloc(vsize*sizeof(char));", + " svtack->nxt->lst = svtack;", + " svtack->nxt->m_delta = vsize;", + " svmax++;", + " }", + " svtack = svtack->nxt;", + "#if SYNC", + " svtack->o_boq = boq;", + "#endif", + " svtack->o_delta = vsize; /* don't compress */", + " memcpy((char *)(svtack->body), (char *) &now, vsize);", + "#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)", + " c_stack((uchar *) &(svtack->c_stack[0]));", + "#endif", + "#ifdef DEBUG", + " printf(\"%%d: sv_save\\n\", depth);", + "#endif", + "}\n", + "void", + "sv_restor(void) /* pop state vector from save stack */", + "{", + " memcpy((char *)&now, svtack->body, svtack->o_delta);", + "#if SYNC", + " boq = svtack->o_boq;", + "#endif", + + "#if defined(C_States) && (HAS_TRACK==1)", + "#ifdef HAS_STACK", + " c_unstack((uchar *) &(svtack->c_stack[0]));", + "#endif", + " c_revert((uchar *) &(now.c_state[0]));", + "#endif", + + " if (vsize != svtack->o_delta)", + " Uerror(\"sv_restor\");", + " if (!svtack->lst)", + " Uerror(\"error: v_restor\");", + " svtack = svtack->lst;", + "#ifdef DEBUG", + " printf(\" sv_restor\\n\");", + "#endif", + "}\n", + "void", + "p_restor(int h)", + "{ int i; char *z = (char *) &now;\n", + " proc_offset[h] = stack->o_offset;", + " proc_skip[h] = (uchar) stack->o_skip;", + "#ifndef XUSAFE", + " p_name[h] = stack->o_name;", + "#endif", + "#ifndef NOCOMP", + " for (i = vsize + stack->o_skip; i > vsize; i--)", + " Mask[i-1] = 1; /* align */", + "#endif", + " vsize += stack->o_skip;", + " memcpy(z+vsize, stack->body, stack->o_delta);", + " vsize += stack->o_delta;", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + "#ifndef NOCOMP", + " for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++)", + " Mask[vsize - i] = 1; /* pad */", + " Mask[proc_offset[h]] = 1; /* _pid */", + "#endif", + " if (BASE > 0 && h > 0)", + " ((P0 *)pptr(h))->_pid = h-BASE;", + " else", + " ((P0 *)pptr(h))->_pid = h;", + " i = stack->o_delqs;", + " now._nr_pr += 1;", + " if (!stack->lst) /* debugging */", + " Uerror(\"error: p_restor\");", + " stack = stack->lst;", + " this = pptr(h);", + " while (i-- > 0)", + " q_restor();", + "}\n", + "void", + "q_restor(void)", + "{ char *z = (char *) &now;", + "#ifndef NOCOMP", + " int k, k_end;", + "#endif", + " q_offset[now._nr_qs] = stack->o_offset;", + " q_skip[now._nr_qs] = (uchar) stack->o_skip;", + "#ifndef XUSAFE", + " q_name[now._nr_qs] = stack->o_name;", + "#endif", + " vsize += stack->o_skip;", + " memcpy(z+vsize, stack->body, stack->o_delta);", + " vsize += stack->o_delta;", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + " now._nr_qs += 1;", + "#ifndef NOCOMP", + " k_end = stack->o_offset;", + " k = k_end - stack->o_skip;", + "#if SYNC", + "#ifndef BFS", + " if (q_zero(now._nr_qs)) k_end += stack->o_delta;", + "#endif", + "#endif", + " for ( ; k < k_end; k++)", + " Mask[k] = 1;", + "#endif", + " if (!stack->lst) /* debugging */", + " Uerror(\"error: q_restor\");", + " stack = stack->lst;", + "}", + + "typedef struct IntChunks {", + " int *ptr;", + " struct IntChunks *nxt;", + "} IntChunks;", + "IntChunks *filled_chunks[512];", + "IntChunks *empty_chunks[512];", + + "int *", + "grab_ints(int nr)", + "{ IntChunks *z;", + " if (nr >= 512) Uerror(\"cannot happen grab_int\");", + " if (filled_chunks[nr])", + " { z = filled_chunks[nr];", + " filled_chunks[nr] = filled_chunks[nr]->nxt;", + " } else ", + " { z = (IntChunks *) emalloc(sizeof(IntChunks));", + " z->ptr = (int *) emalloc(nr * sizeof(int));", + " }", + " z->nxt = empty_chunks[nr];", + " empty_chunks[nr] = z;", + " return z->ptr;", + "}", + "void", + "ungrab_ints(int *p, int nr)", + "{ IntChunks *z;", + " if (!empty_chunks[nr]) Uerror(\"cannot happen ungrab_int\");", + " z = empty_chunks[nr];", + " empty_chunks[nr] = empty_chunks[nr]->nxt;", + " z->ptr = p;", + " z->nxt = filled_chunks[nr];", + " filled_chunks[nr] = z;", + "}", + "int", + "delproc(int sav, int h)", + "{ int d, i=0;", + "#ifndef NOCOMP", + " int o_vsize = vsize;", + "#endif", + " if (h+1 != (int) now._nr_pr) return 0;\n", + " while (now._nr_qs", + " && q_offset[now._nr_qs-1] > proc_offset[h])", + " { delq(sav);", + " i++;", + " }", + " d = vsize - proc_offset[h];", + " if (sav)", + " { if (!stack->nxt)", + " { stack->nxt = (Stack *)", + " emalloc(sizeof(Stack));", + " stack->nxt->body = ", + " emalloc(Maxbody*sizeof(char));", + " stack->nxt->lst = stack;", + " smax++;", + " }", + " stack = stack->nxt;", + " stack->o_offset = proc_offset[h];", + "#if VECTORSZ>32000", + " stack->o_skip = (int) proc_skip[h];", + "#else", + " stack->o_skip = (short) proc_skip[h];", + "#endif", + "#ifndef XUSAFE", + " stack->o_name = p_name[h];", + "#endif", + " stack->o_delta = d;", + " stack->o_delqs = i;", + " memcpy(stack->body, (char *)pptr(h), d);", + " }", + " vsize = proc_offset[h];", + " now._nr_pr = now._nr_pr - 1;", + " memset((char *)pptr(h), 0, d);", + " vsize -= (int) proc_skip[h];", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + "#ifndef NOCOMP", + " for (i = vsize; i < o_vsize; i++)", + " Mask[i] = 0; /* reset */", + "#endif", + " return 1;", + "}\n", + "void", + "delq(int sav)", + "{ int h = now._nr_qs - 1;", + " int d = vsize - q_offset[now._nr_qs - 1];", + "#ifndef NOCOMP", + " int k, o_vsize = vsize;", + "#endif", + " if (sav)", + " { if (!stack->nxt)", + " { stack->nxt = (Stack *)", + " emalloc(sizeof(Stack));", + " stack->nxt->body = ", + " emalloc(Maxbody*sizeof(char));", + " stack->nxt->lst = stack;", + " smax++;", + " }", + " stack = stack->nxt;", + " stack->o_offset = q_offset[h];", + "#if VECTORSZ>32000", + " stack->o_skip = (int) q_skip[h];", + "#else", + " stack->o_skip = (short) q_skip[h];", + "#endif", + "#ifndef XUSAFE", + " stack->o_name = q_name[h];", + "#endif", + " stack->o_delta = d;", + " memcpy(stack->body, (char *)qptr(h), d);", + " }", + " vsize = q_offset[h];", + " now._nr_qs = now._nr_qs - 1;", + " memset((char *)qptr(h), 0, d);", + " vsize -= (int) q_skip[h];", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + "#ifndef NOCOMP", + " for (k = vsize; k < o_vsize; k++)", + " Mask[k] = 0; /* reset */", + "#endif", + "}\n", + "int", + "qs_empty(void)", + "{ int i;", + " for (i = 0; i < (int) now._nr_qs; i++)", + " { if (q_sz(i) > 0)", + " return 0;", + " }", + " return 1;", + "}\n", + "int", + "endstate(void)", + "{ int i; P0 *ptr;", + " for (i = BASE; i < (int) now._nr_pr; i++)", + " { ptr = (P0 *) pptr(i);", + " if (!stopstate[ptr->_t][ptr->_p])", + " return 0;", + " }", + " if (strict) return qs_empty();", + "#if defined(EVENT_TRACE) && !defined(OTIM)", + " if (!stopstate[EVENT_TRACE][now._event] && !a_cycles)", + " { printf(\"pan: event_trace not completed\\n\");", + " return 0;", + " }", + "#endif", + " return 1;", + "}\n", + "#ifndef SAFETY", + "void", + "checkcycles(void)", + "{ uchar o_a_t = now._a_t;", + "#ifndef NOFAIR", + " uchar o_cnt = now._cnt[1];", + "#endif", + "#ifdef FULLSTACK", + "#ifndef MA", + " struct H_el *sv = trpt->ostate; /* save */", + "#else", + " uchar prov = trpt->proviso; /* save */", + "#endif", + "#endif", + "#ifdef DEBUG", + " { int i; uchar *v = (uchar *) &now;", + " printf(\" set Seed state \");", + "#ifndef NOFAIR", + " if (fairness) printf(\"(cnt = %%d:%%d, nrpr=%%d) \",", + " now._cnt[0], now._cnt[1], now._nr_pr);", + "#endif", + " /* for (i = 0; i < n; i++) printf(\"%%d,\", v[i]); */", + " printf(\"\\n\");", + " }", + " printf(\"%%d: cycle check starts\\n\", depth);", + "#endif", + " now._a_t |= (1|16|32);", + " /* 1 = 2nd DFS; (16|32) to help hasher */", + "#ifndef NOFAIR", +#if 0 + " if (fairness)", + " { now._a_t &= ~2; /* pre-apply Rule 3 */", + " now._cnt[1] = 0;", /* reset both a-bit and cnt=0 */ + " /* avoid matching seed on claim stutter on this state */", + " }", +#else + " now._cnt[1] = now._cnt[0];", +#endif + "#endif", + " memcpy((char *)&A_Root, (char *)&now, vsize);", + " A_depth = depthfound = depth;", + " new_state(); /* start 2nd DFS */", + " now._a_t = o_a_t;", + "#ifndef NOFAIR", + " now._cnt[1] = o_cnt;", + "#endif", + " A_depth = 0; depthfound = -1;", + "#ifdef DEBUG", + " printf(\"%%d: cycle check returns\\n\", depth);", + "#endif", + "#ifdef FULLSTACK", + "#ifndef MA", + " trpt->ostate = sv; /* restore */", + "#else", + " trpt->proviso = prov;", + "#endif", + "#endif", + "}", + "#endif\n", + "#if defined(FULLSTACK) && defined(BITSTATE)", + "struct H_el *Free_list = (struct H_el *) 0;", + "void", + "onstack_init(void) /* to store stack states in a bitstate search */", + "{ S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *));", + "}", + "struct H_el *", + "grab_state(int n)", + "{ struct H_el *v, *last = 0;", + " if (H_tab == S_Tab)", + " { for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt)", + " { if ((int) v->tagged == n)", + " { if (last)", + " last->nxt = v->nxt;", + " else", + "gotcha: Free_list = v->nxt;", + " v->tagged = 0;", + " v->nxt = 0;", + "#ifdef COLLAPSE", + " v->ln = 0;", + "#endif", + " return v;", + " }", + " Fh++; last=v;", + " }", + " /* new: second try */", + " v = Free_list;", /* try to avoid emalloc */ + " if (v && ((int) v->tagged >= n))", + " goto gotcha;", + " ngrabs++;", + " }", + " return (struct H_el *)", + " emalloc(sizeof(struct H_el)+n-sizeof(unsigned));", + "}\n", + "#else", + "#define grab_state(n) (struct H_el *) \\", + " emalloc(sizeof(struct H_el)+n-sizeof(unsigned));", + "#endif", +"#ifdef COLLAPSE", + "unsigned long", + "ordinal(char *v, long n, short tp)", + "{ struct H_el *tmp, *ntmp; long m;", + " struct H_el *olst = (struct H_el *) 0;", + " s_hash((uchar *)v, n);", + " tmp = H_tab[j1];", + " if (!tmp)", + " { tmp = grab_state(n);", + " H_tab[j1] = tmp;", + " } else", + " for ( ;; olst = tmp, tmp = tmp->nxt)", + " { m = memcmp(((char *)&(tmp->state)), v, n);", + " if (n == tmp->ln)", + " {", + " if (m == 0)", + " goto done;", + " if (m < 0)", + " {", + "Insert: ntmp = grab_state(n);", + " ntmp->nxt = tmp;", + " if (!olst)", + " H_tab[j1] = ntmp;", + " else", + " olst->nxt = ntmp;", + " tmp = ntmp;", + " break;", + " } else if (!tmp->nxt)", + " {", + "Append: tmp->nxt = grab_state(n);", + " tmp = tmp->nxt;", + " break;", + " }", + " continue;", + " }", + " if (n < tmp->ln)", + " goto Insert;", + " else if (!tmp->nxt)", + " goto Append;", + " }", + " m = ++ncomps[tp];", + "#ifdef FULLSTACK", + " tmp->tagged = m;", + "#else", + " tmp->st_id = m;", + "#endif", + " memcpy(((char *)&(tmp->state)), v, n);", + " tmp->ln = n;", + "done:", + "#ifdef FULLSTACK", + " return tmp->tagged;", + "#else", + " return tmp->st_id;", + "#endif", + "}", + "", + "int", + "compress(char *vin, int nin) /* collapse compression */", + "{ char *w, *v = (char *) &comp_now;", + " int i, j;", + " unsigned long n;", + " static char *x;", + " static uchar nbytes[513]; /* 1 + 256 + 256 */", + " static unsigned short nbytelen;", + " long col_q(int, char *);", + " long col_p(int, char *);", + "#ifndef SAFETY", + " if (a_cycles)", + " *v++ = now._a_t;", + "#ifndef NOFAIR", + " if (fairness)", + " for (i = 0; i < NFAIR; i++)", + " *v++ = now._cnt[i];", + "#endif", + "#endif", + " nbytelen = 0;", + + "#ifndef JOINPROCS", + " for (i = 0; i < (int) now._nr_pr; i++)", + " { n = col_p(i, (char *) 0);", + "#ifdef NOFIX", + " nbytes[nbytelen] = 0;", + "#else", + " nbytes[nbytelen] = 1;", + " *v++ = ((P0 *) pptr(i))->_t;", + "#endif", + " *v++ = n&255;", + " if (n >= (1<<8))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>8)&255;", + " }", + " if (n >= (1<<16))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>16)&255;", + " }", + " if (n >= (1<<24))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>24)&255;", + " }", + " nbytelen++;", + " }", + "#else", + " x = scratch;", + " for (i = 0; i < (int) now._nr_pr; i++)", + " x += col_p(i, x);", + " n = ordinal(scratch, x-scratch, 2); /* procs */", + " *v++ = n&255;", + " nbytes[nbytelen] = 0;", + " if (n >= (1<<8))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>8)&255;", + " }", + " if (n >= (1<<16))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>16)&255;", + " }", + " if (n >= (1<<24))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>24)&255;", + " }", + " nbytelen++;", + "#endif", + "#ifdef SEPQS", + " for (i = 0; i < (int) now._nr_qs; i++)", + " { n = col_q(i, (char *) 0);", + " nbytes[nbytelen] = 0;", + " *v++ = n&255;", + " if (n >= (1<<8))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>8)&255;", + " }", + " if (n >= (1<<16))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>16)&255;", + " }", + " if (n >= (1<<24))", + " { nbytes[nbytelen]++;", + " *v++ = (n>>24)&255;", + " }", + " nbytelen++;", + " }", + "#endif", + + "#ifdef NOVSZ", + " /* 3 = _a_t, _nr_pr, _nr_qs */", + " w = (char *) &now + 3 * sizeof(uchar);", + "#ifndef NOFAIR", + " w += NFAIR;", + "#endif", + "#else", + "#if VECTORSZ<65536", + " w = (char *) &(now._vsz) + sizeof(unsigned short);", + "#else", + " w = (char *) &(now._vsz) + sizeof(unsigned long);", + "#endif", + "#endif", + " x = scratch;", + " *x++ = now._nr_pr;", + " *x++ = now._nr_qs;", + + " if (now._nr_qs > 0 && qptr(0) < pptr(0))", + " n = qptr(0) - (uchar *) w;", + " else", + " n = pptr(0) - (uchar *) w;", + " j = w - (char *) &now;", + " for (i = 0; i < (int) n; i++, w++)", + " if (!Mask[j++]) *x++ = *w;", + "#ifndef SEPQS", + " for (i = 0; i < (int) now._nr_qs; i++)", + " x += col_q(i, x);", + "#endif", + + " x--;", + " for (i = 0, j = 6; i < nbytelen; i++)", + " { if (j == 6)", + " { j = 0;", + " *(++x) = 0;", + " } else", + " j += 2;", + " *x |= (nbytes[i] << j);", + " }", + " x++;", + " for (j = 0; j < WS-1; j++)", + " *x++ = 0;", + " x -= j; j = 0;", + " n = ordinal(scratch, x-scratch, 0); /* globals */", + " *v++ = n&255;", + " if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; }", + " if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; }", + " if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; }", + " *v++ = j; /* add last count as a byte */", + + " for (i = 0; i < WS-1; i++)", + " *v++ = 0;", + " v -= i;", + "#if 0", + " printf(\"collapse %%d -> %%d\\n\",", + " vsize, v - (char *)&comp_now);", + "#endif", + " return v - (char *)&comp_now;", + "}", + +"#else", +"#if !defined(NOCOMP)", + "int", + "compress(char *vin, int n) /* default compression */", + "{", + "#ifdef HC", + " int delta = 0;", + " s_hash((uchar *)vin, n); /* sets K1 and K2 */", + "#ifndef SAFETY", + " if (S_A)", + " { delta++; /* _a_t */", + "#ifndef NOFAIR", + " if (S_A > NFAIR)", + " delta += NFAIR; /* _cnt[] */", + "#endif", + " }", + "#endif", + " memcpy((char *) &comp_now + delta, (char *) &K1, WS);", + " delta += WS;", + "#if HC>0", + " memcpy((char *) &comp_now + delta, (char *) &K2, HC);", + " delta += HC;", + "#endif", + " return delta;", + "#else", + " char *vv = vin;", + " char *v = (char *) &comp_now;", + " int i;", + " for (i = 0; i < n; i++, vv++)", + " if (!Mask[i]) *v++ = *vv;", + " for (i = 0; i < WS-1; i++)", + " *v++ = 0;", + " v -= i;", + "#if 0", + " printf(\"compress %%d -> %%d\\n\",", + " n, v - (char *)&comp_now);", + "#endif", + " return v - (char *)&comp_now;", + "#endif", + "}", +"#endif", +"#endif", + "#if defined(FULLSTACK) && defined(BITSTATE)", +"#if defined(MA)", + "#if !defined(onstack_now)", + "int onstack_now(void) {}", /* to suppress compiler errors */ + "#endif", + "#if !defined(onstack_put)", + "void onstack_put(void) {}", /* for this invalid combination */ + "#endif", + "#if !defined(onstack_zap)", + "void onstack_zap(void) {}", /* of directives */ + "#endif", +"#else", + "void", + "onstack_zap(void)", + "{ struct H_el *v, *w, *last = 0;", + " struct H_el **tmp = H_tab;", + " char *nv; int n, m;\n", + " H_tab = S_Tab;", + "#ifndef NOCOMP", + " nv = (char *) &comp_now;", + " n = compress((char *)&now, vsize);", + "#else", + "#if defined(BITSTATE) && defined(LC)", + " nv = (char *) &comp_now;", + " n = compact_stack((char *)&now, vsize);", + "#else", + " nv = (char *) &now;", + " n = vsize;", + "#endif", + "#endif", + "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))", + " s_hash((uchar *)nv, n);", + "#endif", + " H_tab = tmp;", + " for (v = S_Tab[j1]; v; Zh++, last=v, v=v->nxt)", + " { m = memcmp(&(v->state), nv, n);", + " if (m == 0)", + " goto Found;", + " if (m < 0)", + " break;", + " }", + "/* NotFound: */", + " Uerror(\"stack out of wack - zap\");", + " return;", + "Found:", + " ZAPS++;", + " if (last)", + " last->nxt = v->nxt;", + " else", + " S_Tab[j1] = v->nxt;", + " v->tagged = (unsigned) n;", + "#if !defined(NOREDUCE) && !defined(SAFETY)", + " v->proviso = 0;", + "#endif", + " v->nxt = last = (struct H_el *) 0;", + " for (w = Free_list; w; Fa++, last=w, w = w->nxt)", + " { if ((int) w->tagged <= n)", + " { if (last)", + " { v->nxt = w; /* was: v->nxt = w->nxt; */", + " last->nxt = v;", + " } else", + " { v->nxt = Free_list;", + " Free_list = v;", + " }", + " return;", + " }", + " if (!w->nxt)", + " { w->nxt = v;", + " return;", + " } }", + " Free_list = v;", + "}", + "void", + "onstack_put(void)", + "{ struct H_el **tmp = H_tab;", + " H_tab = S_Tab;", + " if (hstore((char *)&now, vsize) != 0)", + "#if defined(BITSTATE) && defined(LC)", + " printf(\"pan: warning, double stack entry\\n\");", + "#else", + " Uerror(\"cannot happen - unstack_put\");", + "#endif", + " H_tab = tmp;", + " trpt->ostate = Lstate;", + " PUT++;", + "}", + "int", + "onstack_now(void)", + "{ struct H_el *tmp;", + " struct H_el **tmp2 = H_tab;", + " char *v; int n, m = 1;\n", + " H_tab = S_Tab;", + "#ifdef NOCOMP", + "#if defined(BITSTATE) && defined(LC)", + " v = (char *) &comp_now;", + " n = compact_stack((char *)&now, vsize);", + "#else", + " v = (char *) &now;", + " n = vsize;", + "#endif", + "#else", + " v = (char *) &comp_now;", + " n = compress((char *)&now, vsize);", + "#endif", + "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))", + " s_hash((uchar *)v, n);", + "#endif", + " H_tab = tmp2;", + " for (tmp = S_Tab[j1]; tmp; Zn++, tmp = tmp->nxt)", + " { m = memcmp(((char *)&(tmp->state)),v,n);", + " if (m <= 0)", + " { Lstate = (struct H_el *) tmp;", + " break;", + " } }", + " PROBE++;", + " return (m == 0);", + "}", + "#endif", +"#endif", + + "#ifndef BITSTATE", + "void", + "hinit(void)", + "{", +"#ifdef MA", + "#ifdef R_XPT", + " { void r_xpoint(void);", + " r_xpoint();", + " }", + "#else", + " dfa_init((unsigned short) (MA+a_cycles));", + "#endif", +"#endif", +"#if !defined(MA) || defined(COLLAPSE)", + " H_tab = (struct H_el **)", + " emalloc((1L<<ssize)*sizeof(struct H_el *));", +"#endif", + "}", + "#endif\n", + + "#if !defined(BITSTATE) || defined(FULLSTACK)", + + "#ifdef DEBUG", + "void", + "dumpstate(int wasnew, char *v, int n, int tag)", + "{ int i;", + "#ifndef SAFETY", + " if (S_A)", + " { printf(\"\tstate tags %%d (%%d::%%d): \",", + " V_A, wasnew, v[0]);", + "#ifdef FULLSTACK", + " printf(\" %%d \", tag);", + "#endif", + " printf(\"\\n\");", + " }", + "#endif", + "#ifdef SDUMP", + "#ifndef NOCOMP", + " printf(\"\t State: \");", + " for (i = 0; i < vsize; i++) printf(\"%%d%%s,\",", + " ((char *)&now)[i], Mask[i]?\"*\":\"\");", + "#endif", + " printf(\"\\n\tVector: \");", + " for (i = 0; i < n; i++) printf(\"%%d,\", v[i]);", + " printf(\"\\n\");", + "#endif", + "}", + "#endif", + +"#ifdef MA", + "int", + "gstore(char *vin, int nin, uchar pbit)", + "{ int n, i;", + " uchar *v;", + " static uchar Info[MA+1];", + "#ifndef NOCOMP", + " n = compress(vin, nin);", + " v = (uchar *) &comp_now;", + "#else", + " n = nin;", + " v = vin;", + "#endif", + " if (n >= MA)", + " { printf(\"pan: error, MA too small, recompile pan.c\");", + " printf(\" with -DMA=N with N>%%d\\n\", n);", + " Uerror(\"aborting\");", + " }", + " if (n > (int) maxgs) maxgs = (unsigned int) n;", + + " for (i = 0; i < n; i++)", + " Info[i] = v[i];", + " for ( ; i < MA-1; i++)", + " Info[i] = 0;", + " Info[MA-1] = pbit;", + " if (a_cycles) /* place _a_t at the end */", + " { Info[MA] = Info[0]; Info[0] = 0; }", + " if (!dfa_store(Info))", + " { if (pbit == 0", + " && (now._a_t&1)", + " && depth > A_depth)", + " { Info[MA] &= ~(1|16|32); /* _a_t */", + " if (dfa_member(MA))", /* was !dfa_member(MA) */ + " { Info[MA-1] = 4; /* off-stack bit */", + " nShadow++;", + " if (!dfa_member(MA-1))", + " {", + "#ifdef VERBOSE", + " printf(\"intersected 1st dfs stack\\n\");", + "#endif", + " return 3;", + " } } }", + "#ifdef VERBOSE", + " printf(\"new state\\n\");", + "#endif", + " return 0; /* new state */", + " }", + "#ifdef FULLSTACK", + " if (pbit == 0)", + " { Info[MA-1] = 1; /* proviso bit */", + "#ifndef BFS", + " trpt->proviso = dfa_member(MA-1);", + "#endif", + " Info[MA-1] = 4; /* off-stack bit */", + " if (dfa_member(MA-1)) {", + "#ifdef VERBOSE", + " printf(\"old state\\n\");", + "#endif", + " return 1; /* off-stack */", + " } else {", + "#ifdef VERBOSE", + " printf(\"on-stack\\n\");", + "#endif", + " return 2; /* on-stack */", + " }", + " }", + "#endif", + "#ifdef VERBOSE", + " printf(\"old state\\n\");", + "#endif", + " return 1; /* old state */", + "}", +"#endif", + + "#if defined(BITSTATE) && defined(LC)", + "int", + "compact_stack(char *vin, int n)", /* special case of HC4 */ + "{ int delta = 0;", + " s_hash((uchar *)vin, n); /* sets K1 and K2 */", + "#ifndef SAFETY", + " delta++; /* room for state[0] |= 128 */", + "#endif", + " memcpy((char *) &comp_now + delta, (char *) &K1, WS);", + " delta += WS;", + " memcpy((char *) &comp_now + delta, (char *) &K2, WS);", + " delta += WS; /* use all available bits */", + " return delta;", + "}", + "#endif", + + "int", + "hstore(char *vin, int nin) /* hash table storage */", + "{ struct H_el *tmp, *ntmp, *olst = (struct H_el *) 0;", + " char *v; int n, m=0;", + "#ifdef HC", + " uchar rem_a;", + "#endif", + "#ifdef NOCOMP", /* defined by BITSTATE */ + "#if defined(BITSTATE) && defined(LC)", + " if (S_Tab == H_tab)", + " { v = (char *) &comp_now;", + " n = compact_stack(vin, nin);", + " } else", + " { v = vin; n = nin;", + " }", + "#else", + " v = vin; n = nin;", + "#endif", + "#else", + " v = (char *) &comp_now;", + " #ifdef HC", + " rem_a = now._a_t;", /* 4.3.0 */ + " now._a_t = 0;", /* for hashing/state matching to work right */ + " #endif", + " n = compress(vin, nin);", /* with HC, this calls s_hash */ + " #ifdef HC", + " now._a_t = rem_a;", /* 4.3.0 */ + " #endif", + "#ifndef SAFETY", + " if (S_A)", + " { v[0] = 0; /* _a_t */", + "#ifndef NOFAIR", + " if (S_A > NFAIR)", + " for (m = 0; m < NFAIR; m++)", + " v[m+1] = 0; /* _cnt[] */", + "#endif", + " m = 0;", + " }", + "#endif", + "#endif", + "#if !defined(HC) && !(defined(BITSTATE) && defined(LC))", + " s_hash((uchar *)v, n);", + "#endif", + " tmp = H_tab[j1];", + " if (!tmp)", + " { tmp = grab_state(n);", + " H_tab[j1] = tmp;", + " } else", + " { for (;; hcmp++, olst = tmp, tmp = tmp->nxt)", + " { /* skip the _a_t and the _cnt bytes */", + "#ifdef COLLAPSE", + " if (tmp->ln != 0)", + " { if (!tmp->nxt) goto Append;", + " continue;", + " }", + "#endif", + " m = memcmp(((char *)&(tmp->state)) + S_A, ", + " v + S_A, n - S_A);", + " if (m == 0) {", + "#ifdef SAFETY", + "#define wasnew 0", + "#else", + " int wasnew = 0;", + "#endif", + + "#ifndef SAFETY", + "#ifndef NOCOMP", + " if (S_A)", + " { if ((((char *)&(tmp->state))[0] & V_A) != V_A)", + " { wasnew = 1; nShadow++;", + " ((char *)&(tmp->state))[0] |= V_A;", + " }", + "#ifndef NOFAIR", + " if (S_A > NFAIR)", + " { /* 0 <= now._cnt[now._a_t&1] < MAXPROC */", + " unsigned ci, bp; /* index, bit pos */", + " ci = (now._cnt[now._a_t&1] / 8);", + " bp = (now._cnt[now._a_t&1] - 8*ci);", + " if (now._a_t&1) /* use tail-bits in _cnt */", + " { ci = (NFAIR - 1) - ci;", + " bp = 7 - bp; /* bp = 0..7 */", + " }", + " ci++; /* skip over _a_t */", + " bp = 1 << bp; /* the bit mask */", + " if ((((char *)&(tmp->state))[ci] & bp)==0)", + " { if (!wasnew)", + " { wasnew = 1;", + " nShadow++;", + " }", + " ((char *)&(tmp->state))[ci] |= bp;", + " }", + " }", + " /* else: wasnew == 0, i.e., old state */", + "#endif", + " }", + "#endif", + "#endif", + + "#ifdef FULLSTACK", + "#ifndef SAFETY", /* or else wasnew == 0 */ + " if (wasnew)", + " { Lstate = (struct H_el *) tmp;", + " tmp->tagged |= V_A;", + " if ((now._a_t&1)", + " && (tmp->tagged&A_V)", + " && depth > A_depth)", + " {", + "intersect:", + "#ifdef CHECK", + " printf(\"1st dfs-stack intersected on state %%d+\\n\",", + " (int) tmp->st_id);", + "#endif", + " return 3;", + " }", + "#ifdef CHECK", + " printf(\"\tNew state %%d+\\n\", (int) tmp->st_id);", + "#endif", + "#ifdef DEBUG", + " dumpstate(1, (char *)&(tmp->state),n,tmp->tagged);", + "#endif", + " return 0;", + " } else", + "#endif", + " if ((S_A)?(tmp->tagged&V_A):tmp->tagged)", + " { Lstate = (struct H_el *) tmp;", + "#ifndef SAFETY", + " /* already on current dfs stack */", + " /* but may also be on 1st dfs stack */", + " if ((now._a_t&1)", + " && (tmp->tagged&A_V)", + + " && depth > A_depth", + /* new (Zhang's example) */ + "#ifndef NOFAIR", + " && (!fairness || now._cnt[1] <= 1)", + "#endif", + " )", + + " goto intersect;", + "#endif", + "#ifdef CHECK", + " printf(\"\tStack state %%d\\n\", (int) tmp->st_id);", + "#endif", + "#ifdef DEBUG", + " dumpstate(0, (char *)&(tmp->state),n,tmp->tagged);", + "#endif", + " return 2; /* match on stack */", + " }", + "#else", + " if (wasnew)", + " {", + "#ifdef CHECK", + " printf(\"\tNew state %%d+\\n\", (int) tmp->st_id);", + "#endif", + "#ifdef DEBUG", + " dumpstate(1, (char *)&(tmp->state), n, 0);", + "#endif", + " return 0;", + " }", + "#endif", + "#ifdef CHECK", + " printf(\"\tOld state %%d\\n\", (int) tmp->st_id);", + "#endif", + "#ifdef DEBUG", + " dumpstate(0, (char *)&(tmp->state), n, 0);", + "#endif", + "#ifdef REACH", + " if (tmp->D > depth)", + " { tmp->D = depth;", + "#ifdef CHECK", + " printf(\"\t\tReVisiting (from smaller depth)\\n\");", + "#endif", + " nstates--;", +#if 0 + possible variation of iterative search for shortest counter-example (pan -i + and pan -I) suggested by Pierre Moro (for safety properties): + state revisits on shorter depths do not start until after + the first counter-example is found. this assumes that the max search + depth is set large enough that a first (possibly long) counter-example + can be found + if set too short, this variant can miss the counter-example, even if + it would otherwise be shorter than the depth-limit. + (p.m. unsure if this preserves the guarantee of finding the + shortest counter-example - so not enabled yet) + " if (errors > 0 && iterative)", /* Moro */ +#endif + " return 0;", + " }", + "#endif", + "#if defined(BFS) && defined(Q_PROVISO)", + " Lstate = (struct H_el *) tmp;", + "#endif", + " return 1; /* match outside stack */", + " } else if (m < 0)", + " { /* insert state before tmp */", + " ntmp = grab_state(n);", + " ntmp->nxt = tmp;", + " if (!olst)", + " H_tab[j1] = ntmp;", + " else", + " olst->nxt = ntmp;", + " tmp = ntmp;", + " break;", + " } else if (!tmp->nxt)", + " { /* append after tmp */", + "#ifdef COLLAPSE", + "Append:", + "#endif", + " tmp->nxt = grab_state(n);", + " tmp = tmp->nxt;", + " break;", + " } }", + " }", + "#ifdef CHECK", + " tmp->st_id = (unsigned) nstates;", + "#ifdef BITSTATE", + " printf(\" Push state %%d\\n\", ((int) nstates) - 1);", + "#else", + " printf(\" New state %%d\\n\", (int) nstates);", + "#endif", + "#endif", + "#if !defined(SAFETY) || defined(REACH)", + " tmp->D = depth;", + "#endif", + "#ifndef SAFETY", + "#ifndef NOCOMP", + " if (S_A)", + " { v[0] = V_A;", + "#ifndef NOFAIR", + " if (S_A > NFAIR)", + " { unsigned ci, bp; /* as above */", + " ci = (now._cnt[now._a_t&1] / 8);", + " bp = (now._cnt[now._a_t&1] - 8*ci);", + " if (now._a_t&1)", + " { ci = (NFAIR - 1) - ci;", + " bp = 7 - bp; /* bp = 0..7 */", + " }", + " v[1+ci] = 1 << bp;", + " }", + "#endif", + " }", + "#endif", + "#endif", + " memcpy(((char *)&(tmp->state)), v, n);", + "#ifdef FULLSTACK", + " tmp->tagged = (S_A)?V_A:(depth+1);", + "#ifdef DEBUG", + " dumpstate(-1, v, n, tmp->tagged);", + "#endif", + " Lstate = (struct H_el *) tmp;", + "#else", + "#ifdef DEBUG", + " dumpstate(-1, v, n, 0);", + "#endif", + "#endif", + " return 0;", + "}", + "#endif", + "#include TRANSITIONS", + "void", + "do_reach(void)", + "{", + 0, +}; diff --git a/sys/src/cmd/spin/pangen2.c b/sys/src/cmd/spin/pangen2.c new file mode 100755 index 000000000..05ad9fa1f --- /dev/null +++ b/sys/src/cmd/spin/pangen2.c @@ -0,0 +1,2996 @@ +/***** spin: pangen2.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "version.h" +#include "y.tab.h" +#include "pangen2.h" +#include "pangen4.h" +#include "pangen5.h" + +#define DELTA 500 /* sets an upperbound on nr of chan names */ + +#define blurb(fd, e) { fprintf(fd, "\n"); if (!merger) fprintf(fd, "\t\t/* %s:%d */\n", \ + e->n->fn->name, e->n->ln); } +#define tr_map(m, e) { if (!merger) fprintf(tt, "\t\ttr_2_src(%d, %s, %d);\n", \ + m, e->n->fn->name, e->n->ln); } + +extern ProcList *rdy; +extern RunList *run; +extern Symbol *Fname, *oFname, *context; +extern char *claimproc, *eventmap; +extern int lineno, verbose, Npars, Mpars; +extern int m_loss, has_remote, has_remvar, merger, rvopt, separate; +extern int Ntimeouts, Etimeouts, deadvar; +extern int u_sync, u_async, nrRdy; +extern int GenCode, IsGuard, Level, TestOnly; +extern short has_stack; +extern char *NextLab[]; + +FILE *tc, *th, *tt, *tm, *tb; + +int OkBreak = -1; +short nocast=0; /* to turn off casts in lvalues */ +short terse=0; /* terse printing of varnames */ +short no_arrays=0; +short has_last=0; /* spec refers to _last */ +short has_badelse=0; /* spec contains else combined with chan refs */ +short has_enabled=0; /* spec contains enabled() */ +short has_pcvalue=0; /* spec contains pc_value() */ +short has_np=0; /* spec contains np_ */ +short has_sorted=0; /* spec contains `!!' (sorted-send) operator */ +short has_random=0; /* spec contains `??' (random-recv) operator */ +short has_xu=0; /* spec contains xr or xs assertions */ +short has_unless=0; /* spec contains unless statements */ +short has_provided=0; /* spec contains PROVIDED clauses on procs */ +short has_code=0; /* spec contains c_code, c_expr, c_state */ +short _isok=0; /* checks usage of predefined variable _ */ +short evalindex=0; /* evaluate index of var names */ +short withprocname=0; /* prefix local varnames with procname */ +int mst=0; /* max nr of state/process */ +int claimnr = -1; /* claim process, if any */ +int eventmapnr = -1; /* event trace, if any */ +int Pid; /* proc currently processed */ +int multi_oval; /* set in merges, used also in pangen4.c */ + +#define MAXMERGE 256 /* max nr of bups per merge sequence */ + +static short CnT[MAXMERGE]; +static Lextok XZ, YZ[MAXMERGE]; +static int didcase, YZmax, YZcnt; + +static Lextok *Nn[2]; +static int Det; /* set if deterministic */ +static int T_sum, T_mus, t_cyc; +static int TPE[2], EPT[2]; +static int uniq=1; +static int multi_needed, multi_undo; +static short AllGlobal=0; /* set if process has provided clause */ + +int has_global(Lextok *); +static int getweight(Lextok *); +static int scan_seq(Sequence *); +static void genconditionals(void); +static void mark_seq(Sequence *); +static void patch_atomic(Sequence *); +static void put_seq(Sequence *, int, int); +static void putproc(ProcList *); +static void Tpe(Lextok *); +extern void spit_recvs(FILE *, FILE*); + +static int +fproc(char *s) +{ ProcList *p; + + for (p = rdy; p; p = p->nxt) + if (strcmp(p->n->name, s) == 0) + return p->tn; + + fatal("proctype %s not found", s); + return -1; +} + +static void +reverse_procs(RunList *q) +{ + if (!q) return; + reverse_procs(q->nxt); + fprintf(tc, " Addproc(%d);\n", q->tn); +} + +static void +tm_predef_np(void) +{ + fprintf(th, "#define _T5 %d\n", uniq++); + fprintf(th, "#define _T2 %d\n", uniq++); + fprintf(tm, "\tcase _T5:\t/* np_ */\n"); + + if (separate == 2) + fprintf(tm, "\t\tif (!((!(o_pm&4) && !(tau&128))))\n"); + else + fprintf(tm, "\t\tif (!((!(trpt->o_pm&4) && !(trpt->tau&128))))\n"); + + fprintf(tm, "\t\t\tcontinue;\n"); + fprintf(tm, "\t\t/* else fall through */\n"); + fprintf(tm, "\tcase _T2:\t/* true */\n"); + fprintf(tm, "\t\t_m = 3; goto P999;\n"); +} + +static void +tt_predef_np(void) +{ + fprintf(tt, "\t/* np_ demon: */\n"); + fprintf(tt, "\ttrans[_NP_] = "); + fprintf(tt, "(Trans **) emalloc(2*sizeof(Trans *));\n"); + fprintf(tt, "\tT = trans[_NP_][0] = "); + fprintf(tt, "settr(9997,0,1,_T5,0,\"(np_)\", 1,2,0);\n"); + fprintf(tt, "\t T->nxt = "); + fprintf(tt, "settr(9998,0,0,_T2,0,\"(1)\", 0,2,0);\n"); + fprintf(tt, "\tT = trans[_NP_][1] = "); + fprintf(tt, "settr(9999,0,1,_T5,0,\"(np_)\", 1,2,0);\n"); +} + +static struct { + char *nm[3]; +} Cfile[] = { + { { "pan.c", "pan_s.c", "pan_t.c" } }, + { { "pan.h", "pan_s.h", "pan_t.h" } }, + { { "pan.t", "pan_s.t", "pan_t.t" } }, + { { "pan.m", "pan_s.m", "pan_t.m" } }, + { { "pan.b", "pan_s.b", "pan_t.b" } } +}; + +void +gensrc(void) +{ ProcList *p; + + if (!(tc = fopen(Cfile[0].nm[separate], "w")) /* main routines */ + || !(th = fopen(Cfile[1].nm[separate], "w")) /* header file */ + || !(tt = fopen(Cfile[2].nm[separate], "w")) /* transition matrix */ + || !(tm = fopen(Cfile[3].nm[separate], "w")) /* forward moves */ + || !(tb = fopen(Cfile[4].nm[separate], "w"))) /* backward moves */ + { printf("spin: cannot create pan.[chtmfb]\n"); + alldone(1); + } + + fprintf(th, "#define Version \"%s\"\n", Version); + fprintf(th, "#define Source \"%s\"\n\n", oFname->name); + if (separate != 2) + fprintf(th, "char *TrailFile = Source; /* default */\n"); + + fprintf(th, "#if defined(BFS)\n"); + fprintf(th, "#ifndef SAFETY\n"); + fprintf(th, "#define SAFETY\n"); + fprintf(th, "#endif\n"); + fprintf(th, "#ifndef XUSAFE\n"); + fprintf(th, "#define XUSAFE\n"); + fprintf(th, "#endif\n"); + fprintf(th, "#endif\n"); + + fprintf(th, "#ifndef uchar\n"); + fprintf(th, "#define uchar unsigned char\n"); + fprintf(th, "#endif\n"); + fprintf(th, "#ifndef uint\n"); + fprintf(th, "#define uint unsigned int\n"); + fprintf(th, "#endif\n"); + + if (sizeof(void *) > 4) /* 64 bit machine */ + { fprintf(th, "#ifndef HASH32\n"); + fprintf(th, "#define HASH64\n"); + fprintf(th, "#endif\n"); + } +#if 0 + if (sizeof(long)==sizeof(int)) + fprintf(th, "#define long int\n"); +#endif + if (separate == 1 && !claimproc) + { Symbol *n = (Symbol *) emalloc(sizeof(Symbol)); + Sequence *s = (Sequence *) emalloc(sizeof(Sequence)); + claimproc = n->name = "_:never_template:_"; + ready(n, ZN, s, 0, ZN); + } + if (separate == 2) + { if (has_remote) + { printf("spin: warning, make sure that the S1 model\n"); + printf(" includes the same remote references\n"); + } + fprintf(th, "#ifndef NFAIR\n"); + fprintf(th, "#define NFAIR 2 /* must be >= 2 */\n"); + fprintf(th, "#endif\n"); + if (has_last) + fprintf(th, "#define HAS_LAST %d\n", has_last); + goto doless; + } + + fprintf(th, "#define DELTA %d\n", DELTA); + fprintf(th, "#ifdef MA\n"); + fprintf(th, "#if MA==1\n"); /* user typed -DMA without size */ + fprintf(th, "#undef MA\n#define MA 100\n"); + fprintf(th, "#endif\n#endif\n"); + fprintf(th, "#ifdef W_XPT\n"); + fprintf(th, "#if W_XPT==1\n"); /* user typed -DW_XPT without size */ + fprintf(th, "#undef W_XPT\n#define W_XPT 1000000\n"); + fprintf(th, "#endif\n#endif\n"); + fprintf(th, "#ifndef NFAIR\n"); + fprintf(th, "#define NFAIR 2 /* must be >= 2 */\n"); + fprintf(th, "#endif\n"); + if (Ntimeouts) + fprintf(th, "#define NTIM %d\n", Ntimeouts); + if (Etimeouts) + fprintf(th, "#define ETIM %d\n", Etimeouts); + if (has_remvar) + fprintf(th, "#define REM_VARS 1\n"); + if (has_remote) + fprintf(th, "#define REM_REFS %d\n", has_remote); /* not yet used */ + if (has_last) + fprintf(th, "#define HAS_LAST %d\n", has_last); + if (has_sorted) + fprintf(th, "#define HAS_SORTED %d\n", has_sorted); + if (m_loss) + fprintf(th, "#define M_LOSS\n"); + if (has_random) + fprintf(th, "#define HAS_RANDOM %d\n", has_random); + fprintf(th, "#define HAS_CODE\n"); /* doesn't seem to cause measurable overhead */ + if (has_stack) + fprintf(th, "#define HAS_STACK\n"); + if (has_enabled) + fprintf(th, "#define HAS_ENABLED 1\n"); + if (has_unless) + fprintf(th, "#define HAS_UNLESS %d\n", has_unless); + if (has_provided) + fprintf(th, "#define HAS_PROVIDED %d\n", has_provided); + if (has_pcvalue) + fprintf(th, "#define HAS_PCVALUE %d\n", has_pcvalue); + if (has_badelse) + fprintf(th, "#define HAS_BADELSE %d\n", has_badelse); + if (has_enabled + || has_pcvalue + || has_badelse + || has_last) + { fprintf(th, "#ifndef NOREDUCE\n"); + fprintf(th, "#define NOREDUCE 1\n"); + fprintf(th, "#endif\n"); + } + if (has_np) + fprintf(th, "#define HAS_NP %d\n", has_np); + if (merger) + fprintf(th, "#define MERGED 1\n"); + +doless: + fprintf(th, "#ifdef NP /* includes np_ demon */\n"); + if (!has_np) + fprintf(th, "#define HAS_NP 2\n"); + fprintf(th, "#define VERI %d\n", nrRdy); + fprintf(th, "#define endclaim 3 /* none */\n"); + fprintf(th, "#endif\n"); + if (claimproc) + { claimnr = fproc(claimproc); + /* NP overrides claimproc */ + fprintf(th, "#if !defined(NOCLAIM) && !defined NP\n"); + fprintf(th, "#define VERI %d\n", claimnr); + fprintf(th, "#define endclaim endstate%d\n", claimnr); + fprintf(th, "#endif\n"); + } + if (eventmap) + { eventmapnr = fproc(eventmap); + fprintf(th, "#define EVENT_TRACE %d\n", eventmapnr); + fprintf(th, "#define endevent endstate%d\n", eventmapnr); + if (eventmap[2] == 'o') /* ":notrace:" */ + fprintf(th, "#define NEGATED_TRACE 1\n"); + } + + fprintf(th, "typedef struct S_F_MAP {\n"); + fprintf(th, " char *fnm; int from; int upto;\n"); + fprintf(th, "} S_F_MAP;\n"); + + fprintf(tc, "/*** Generated by %s ***/\n", Version); + fprintf(tc, "/*** From source: %s ***/\n\n", oFname->name); + + ntimes(tc, 0, 1, Pre0); + + plunk_c_decls(tc); /* types can be refered to in State */ + + switch (separate) { + case 0: fprintf(tc, "#include \"pan.h\"\n"); break; + case 1: fprintf(tc, "#include \"pan_s.h\"\n"); break; + case 2: fprintf(tc, "#include \"pan_t.h\"\n"); break; + } + + fprintf(tc, "State A_Root; /* seed-state for cycles */\n"); + fprintf(tc, "State now; /* the full state-vector */\n"); + plunk_c_fcts(tc); /* State can be used in fcts */ + + if (separate != 2) + ntimes(tc, 0, 1, Preamble); + else + fprintf(tc, "extern int verbose; extern long depth;\n"); + + fprintf(tc, "#ifndef NOBOUNDCHECK\n"); + fprintf(tc, "#define Index(x, y)\tBoundcheck(x, y, II, tt, t)\n"); + fprintf(tc, "#else\n"); + fprintf(tc, "#define Index(x, y)\tx\n"); + fprintf(tc, "#endif\n"); + + c_preview(); /* sets hastrack */ + + for (p = rdy; p; p = p->nxt) + mst = max(p->s->maxel, mst); + + if (separate != 2) + { fprintf(tt, "#ifdef PEG\n"); + fprintf(tt, "struct T_SRC {\n"); + fprintf(tt, " char *fl; int ln;\n"); + fprintf(tt, "} T_SRC[NTRANS];\n\n"); + fprintf(tt, "void\ntr_2_src(int m, char *file, int ln)\n"); + fprintf(tt, "{ T_SRC[m].fl = file;\n"); + fprintf(tt, " T_SRC[m].ln = ln;\n"); + fprintf(tt, "}\n\n"); + fprintf(tt, "void\nputpeg(int n, int m)\n"); + fprintf(tt, "{ printf(\"%%5d\ttrans %%4d \", m, n);\n"); + fprintf(tt, " printf(\"file %%s line %%3d\\n\",\n"); + fprintf(tt, " T_SRC[n].fl, T_SRC[n].ln);\n"); + fprintf(tt, "}\n"); + if (!merger) + { fprintf(tt, "#else\n"); + fprintf(tt, "#define tr_2_src(m,f,l)\n"); + } + fprintf(tt, "#endif\n\n"); + fprintf(tt, "void\nsettable(void)\n{\tTrans *T;\n"); + fprintf(tt, "\tTrans *settr(int, int, int, int, int,"); + fprintf(tt, " char *, int, int, int);\n\n"); + fprintf(tt, "\ttrans = (Trans ***) "); + fprintf(tt, "emalloc(%d*sizeof(Trans **));\n", nrRdy+1); + /* +1 for np_ automaton */ + + if (separate == 1) + { + fprintf(tm, " if (II == 0)\n"); + fprintf(tm, " { _m = step_claim(trpt->o_pm, trpt->tau, tt, ot, t);\n"); + fprintf(tm, " if (_m) goto P999; else continue;\n"); + fprintf(tm, " } else\n"); + } + + fprintf(tm, "#define rand pan_rand\n"); + fprintf(tm, "#if defined(HAS_CODE) && defined(VERBOSE)\n"); + fprintf(tm, " printf(\"Pr: %%d Tr: %%d\\n\", II, t->forw);\n"); + fprintf(tm, "#endif\n"); + fprintf(tm, " switch (t->forw) {\n"); + } else + { fprintf(tt, "#ifndef PEG\n"); + fprintf(tt, "#define tr_2_src(m,f,l)\n"); + fprintf(tt, "#endif\n"); + fprintf(tt, "void\nset_claim(void)\n{\tTrans *T;\n"); + fprintf(tt, "\textern Trans ***trans;\n"); + fprintf(tt, "\textern Trans *settr(int, int, int, int, int,"); + fprintf(tt, " char *, int, int, int);\n\n"); + + fprintf(tm, "#define rand pan_rand\n"); + fprintf(tm, "#if defined(HAS_CODE) && defined(VERBOSE)\n"); + fprintf(tm, " printf(\"Pr: %%d Tr: %%d\\n\", II, forw);\n"); + fprintf(tm, "#endif\n"); + fprintf(tm, " switch (forw) {\n"); + } + + fprintf(tm, " default: Uerror(\"bad forward move\");\n"); + fprintf(tm, " case 0: /* if without executable clauses */\n"); + fprintf(tm, " continue;\n"); + fprintf(tm, " case 1: /* generic 'goto' or 'skip' */\n"); + if (separate != 2) + fprintf(tm, " IfNotBlocked\n"); + fprintf(tm, " _m = 3; goto P999;\n"); + fprintf(tm, " case 2: /* generic 'else' */\n"); + if (separate == 2) + fprintf(tm, " if (o_pm&1) continue;\n"); + else + { fprintf(tm, " IfNotBlocked\n"); + fprintf(tm, " if (trpt->o_pm&1) continue;\n"); + } + fprintf(tm, " _m = 3; goto P999;\n"); + uniq = 3; + + if (separate == 1) + fprintf(tb, " if (II == 0) goto R999;\n"); + + fprintf(tb, " switch (t->back) {\n"); + fprintf(tb, " default: Uerror(\"bad return move\");\n"); + fprintf(tb, " case 0: goto R999; /* nothing to undo */\n"); + + for (p = rdy; p; p = p->nxt) + putproc(p); + + + if (separate != 2) + { fprintf(th, "struct {\n"); + fprintf(th, " int tp; short *src;\n"); + fprintf(th, "} src_all[] = {\n"); + for (p = rdy; p; p = p->nxt) + fprintf(th, " { %d, &src_ln%d[0] },\n", + p->tn, p->tn); + fprintf(th, " { 0, (short *) 0 }\n"); + fprintf(th, "};\n"); + fprintf(th, "short *frm_st0;\n"); /* records src states for transitions in never claim */ + } else + { fprintf(th, "extern short *frm_st0;\n"); + } + + gencodetable(th); + + if (separate != 1) + { tm_predef_np(); + tt_predef_np(); + } + fprintf(tt, "}\n\n"); /* end of settable() */ + + fprintf(tm, "#undef rand\n"); + fprintf(tm, " }\n\n"); + fprintf(tb, " }\n\n"); + + if (separate != 2) + { ntimes(tt, 0, 1, Tail); + genheader(); + if (separate == 1) + { fprintf(th, "#define FORWARD_MOVES\t\"pan_s.m\"\n"); + fprintf(th, "#define REVERSE_MOVES\t\"pan_s.b\"\n"); + fprintf(th, "#define SEPARATE\n"); + fprintf(th, "#define TRANSITIONS\t\"pan_s.t\"\n"); + fprintf(th, "extern void ini_claim(int, int);\n"); + } else + { fprintf(th, "#define FORWARD_MOVES\t\"pan.m\"\n"); + fprintf(th, "#define REVERSE_MOVES\t\"pan.b\"\n"); + fprintf(th, "#define TRANSITIONS\t\"pan.t\"\n"); + } + genaddproc(); + genother(); + genaddqueue(); + genunio(); + genconditionals(); + gensvmap(); + if (!run) fatal("no runable process", (char *)0); + fprintf(tc, "void\n"); + fprintf(tc, "active_procs(void)\n{\n"); + reverse_procs(run); + fprintf(tc, "}\n"); + ntimes(tc, 0, 1, Dfa); + ntimes(tc, 0, 1, Xpt); + + fprintf(th, "#define NTRANS %d\n", uniq); + fprintf(th, "#ifdef PEG\n"); + fprintf(th, "long peg[NTRANS];\n"); + fprintf(th, "#endif\n"); + + if (u_sync && !u_async) + spit_recvs(th, tc); + } else + { genheader(); + fprintf(th, "#define FORWARD_MOVES\t\"pan_t.m\"\n"); + fprintf(th, "#define REVERSE_MOVES\t\"pan_t.b\"\n"); + fprintf(th, "#define TRANSITIONS\t\"pan_t.t\"\n"); + fprintf(tc, "extern int Maxbody;\n"); + fprintf(tc, "#if VECTORSZ>32000\n"); + fprintf(tc, "extern int proc_offset[];\n"); + fprintf(tc, "#else\n"); + fprintf(tc, "extern short proc_offset[];\n"); + fprintf(tc, "#endif\n"); + fprintf(tc, "extern uchar proc_skip[];\n"); + fprintf(tc, "extern uchar *reached[];\n"); + fprintf(tc, "extern uchar *accpstate[];\n"); + fprintf(tc, "extern uchar *progstate[];\n"); + fprintf(tc, "extern uchar *stopstate[];\n"); + fprintf(tc, "extern uchar *visstate[];\n\n"); + fprintf(tc, "extern short *mapstate[];\n"); + + fprintf(tc, "void\nini_claim(int n, int h)\n{"); + fprintf(tc, "\textern State now;\n"); + fprintf(tc, "\textern void set_claim(void);\n\n"); + fprintf(tc, "#ifdef PROV\n"); + fprintf(tc, "#include PROV\n"); + fprintf(tc, "#endif\n"); + fprintf(tc, "\tset_claim();\n"); + genother(); + fprintf(tc, "\n\tswitch (n) {\n"); + genaddproc(); + fprintf(tc, "\t}\n"); + fprintf(tc, "\n}\n"); + fprintf(tc, "int\nstep_claim(int o_pm, int tau, int tt, int ot, Trans *t)\n"); + fprintf(tc, "{ int forw = t->forw; int _m = 0; extern char *noptr; int II=0;\n"); + fprintf(tc, " extern State now;\n"); + fprintf(tc, "#define continue return 0\n"); + fprintf(tc, "#include \"pan_t.m\"\n"); + fprintf(tc, "P999:\n\treturn _m;\n}\n"); + fprintf(tc, "#undef continue\n"); + fprintf(tc, "int\nrev_claim(int backw)\n{ return 0; }\n"); + fprintf(tc, "#include TRANSITIONS\n"); + } + if (separate != 1) + ntimes(tc, 0, 1, Nvr1); + + if (separate != 2) + { c_wrapper(tc); + c_chandump(tc); + } +} + +static int +find_id(Symbol *s) +{ ProcList *p; + + if (s) + for (p = rdy; p; p = p->nxt) + if (s == p->n) + return p->tn; + return 0; +} + +static void +dolen(Symbol *s, char *pre, int pid, int ai, int qln) +{ + if (ai > 0) + fprintf(tc, "\n\t\t\t || "); + fprintf(tc, "%s(", pre); + if (!(s->hidden&1)) + { if (s->context) + fprintf(tc, "((P%d *)this)->", pid); + else + fprintf(tc, "now."); + } + fprintf(tc, "%s", s->name); + if (qln > 1) fprintf(tc, "[%d]", ai); + fprintf(tc, ")"); +} + +struct AA { char TT[9]; char CC[8]; }; + +static struct AA BB[4] = { + { "Q_FULL_F", " q_full" }, + { "Q_FULL_T", "!q_full" }, + { "Q_EMPT_F", " !q_len" }, + { "Q_EMPT_T", " q_len" } + }; + +static struct AA DD[4] = { + { "Q_FULL_F", " q_e_f" }, /* empty or full */ + { "Q_FULL_T", "!q_full" }, + { "Q_EMPT_F", " q_e_f" }, + { "Q_EMPT_T", " q_len" } + }; + /* this reduces the number of cases where 's' and 'r' + are considered conditionally safe under the + partial order reduction rules; as a price for + this simple implementation, it also affects the + cases where nfull and nempty can be considered + safe -- since these are labeled the same way as + 's' and 'r' respectively + it only affects reduction, not functionality + */ + +void +bb_or_dd(int j, int which) +{ + if (which) + { if (has_unless) + fprintf(tc, "%s", DD[j].CC); + else + fprintf(tc, "%s", BB[j].CC); + } else + { if (has_unless) + fprintf(tc, "%s", DD[j].TT); + else + fprintf(tc, "%s", BB[j].TT); + } +} + +void +Done_case(char *nm, Symbol *z) +{ int j, k; + int nid = z->Nid; + int qln = z->nel; + + fprintf(tc, "\t\tcase %d: if (", nid); + for (j = 0; j < 4; j++) + { fprintf(tc, "\t(t->ty[i] == "); + bb_or_dd(j, 0); + fprintf(tc, " && ("); + for (k = 0; k < qln; k++) + { if (k > 0) + fprintf(tc, "\n\t\t\t || "); + bb_or_dd(j, 1); + fprintf(tc, "(%s%s", nm, z->name); + if (qln > 1) + fprintf(tc, "[%d]", k); + fprintf(tc, ")"); + } + fprintf(tc, "))\n\t\t\t "); + if (j < 3) + fprintf(tc, "|| "); + else + fprintf(tc, " "); + } + fprintf(tc, ") return 0; break;\n"); +} + +static void +Docase(Symbol *s, int pid, int nid) +{ int i, j; + + fprintf(tc, "\t\tcase %d: if (", nid); + for (j = 0; j < 4; j++) + { fprintf(tc, "\t(t->ty[i] == "); + bb_or_dd(j, 0); + fprintf(tc, " && ("); + if (has_unless) + { for (i = 0; i < s->nel; i++) + dolen(s, DD[j].CC, pid, i, s->nel); + } else + { for (i = 0; i < s->nel; i++) + dolen(s, BB[j].CC, pid, i, s->nel); + } + fprintf(tc, "))\n\t\t\t "); + if (j < 3) + fprintf(tc, "|| "); + else + fprintf(tc, " "); + } + fprintf(tc, ") return 0; break;\n"); +} + +static void +genconditionals(void) +{ Symbol *s; + int last=0, j; + extern Ordered *all_names; + Ordered *walk; + + fprintf(th, "#define LOCAL 1\n"); + fprintf(th, "#define Q_FULL_F 2\n"); + fprintf(th, "#define Q_EMPT_F 3\n"); + fprintf(th, "#define Q_EMPT_T 4\n"); + fprintf(th, "#define Q_FULL_T 5\n"); + fprintf(th, "#define TIMEOUT_F 6\n"); + fprintf(th, "#define GLOBAL 7\n"); + fprintf(th, "#define BAD 8\n"); + fprintf(th, "#define ALPHA_F 9\n"); + + fprintf(tc, "int\n"); + fprintf(tc, "q_cond(short II, Trans *t)\n"); + fprintf(tc, "{ int i = 0;\n"); + fprintf(tc, " for (i = 0; i < 6; i++)\n"); + fprintf(tc, " { if (t->ty[i] == TIMEOUT_F) return %s;\n", + (Etimeouts)?"(!(trpt->tau&1))":"1"); + fprintf(tc, " if (t->ty[i] == ALPHA_F)\n"); + fprintf(tc, "#ifdef GLOB_ALPHA\n"); + fprintf(tc, " return 0;\n"); + fprintf(tc, "#else\n\t\t\treturn "); + fprintf(tc, "(II+1 == (short) now._nr_pr && II+1 < MAXPROC);\n"); + fprintf(tc, "#endif\n"); + + /* we switch on the chan name from the spec (as identified by + * the corresponding Nid number) rather than the actual qid + * because we cannot predict at compile time which specific qid + * will be accessed by the statement at runtime. that is: + * we do not know which qid to pass to q_cond at runtime + * but we do know which name is used. if it's a chan array, we + * must check all elements of the array for compliance (bummer) + */ + fprintf(tc, " switch (t->qu[i]) {\n"); + fprintf(tc, " case 0: break;\n"); + + for (walk = all_names; walk; walk = walk->next) + { s = walk->entry; + if (s->owner) continue; + j = find_id(s->context); + if (s->type == CHAN) + { if (last == s->Nid) continue; /* chan array */ + last = s->Nid; + Docase(s, j, last); + } else if (s->type == STRUCT) + { /* struct may contain a chan */ + char pregat[128]; + extern void walk2_struct(char *, Symbol *); + strcpy(pregat, ""); + if (!(s->hidden&1)) + { if (s->context) + sprintf(pregat, "((P%d *)this)->",j); + else + sprintf(pregat, "now."); + } + walk2_struct(pregat, s); + } + } + fprintf(tc, " \tdefault: Uerror(\"unknown qid - q_cond\");\n"); + fprintf(tc, " \t\t\treturn 0;\n"); + fprintf(tc, " \t}\n"); + fprintf(tc, " }\n"); + fprintf(tc, " return 1;\n"); + fprintf(tc, "}\n"); +} + +static void +putproc(ProcList *p) +{ Pid = p->tn; + Det = p->det; + + if (Pid == claimnr + && separate == 1) + { fprintf(th, "extern uchar reached%d[];\n", Pid); +#if 0 + fprintf(th, "extern short nstates%d;\n", Pid); +#else + fprintf(th, "\n#define nstates%d %d\t/* %s */\n", + Pid, p->s->maxel, p->n->name); +#endif + fprintf(th, "extern short src_ln%d[];\n", Pid); + fprintf(th, "extern S_F_MAP src_file%d[];\n", Pid); + fprintf(th, "#define endstate%d %d\n", + Pid, p->s->last?p->s->last->seqno:0); + fprintf(th, "#define src_claim src_ln%d\n", claimnr); + + return; + } + if (Pid != claimnr + && separate == 2) + { fprintf(th, "extern short src_ln%d[];\n", Pid); + return; + } + + AllGlobal = (p->prov)?1:0; /* process has provided clause */ + + fprintf(th, "\n#define nstates%d %d\t/* %s */\n", + Pid, p->s->maxel, p->n->name); + if (Pid == claimnr) + fprintf(th, "#define nstates_claim nstates%d\n", Pid); + if (Pid == eventmapnr) + fprintf(th, "#define nstates_event nstates%d\n", Pid); + + fprintf(th, "#define endstate%d %d\n", + Pid, p->s->last->seqno); + fprintf(tm, "\n /* PROC %s */\n", p->n->name); + fprintf(tb, "\n /* PROC %s */\n", p->n->name); + fprintf(tt, "\n /* proctype %d: %s */\n", Pid, p->n->name); + fprintf(tt, "\n trans[%d] = (Trans **)", Pid); + fprintf(tt, " emalloc(%d*sizeof(Trans *));\n\n", p->s->maxel); + + if (Pid == eventmapnr) + { fprintf(th, "\n#define in_s_scope(x_y3_) 0"); + fprintf(tc, "\n#define in_r_scope(x_y3_) 0"); + } + + put_seq(p->s, 2, 0); + if (Pid == eventmapnr) + { fprintf(th, "\n\n"); + fprintf(tc, "\n\n"); + } + dumpsrc(p->s->maxel, Pid); +} + +static void +addTpe(int x) +{ int i; + + if (x <= 2) return; + + for (i = 0; i < T_sum; i++) + if (TPE[i] == x) + return; + TPE[(T_sum++)%2] = x; +} + +static void +cnt_seq(Sequence *s) +{ Element *f; + SeqList *h; + + if (s) + for (f = s->frst; f; f = f->nxt) + { Tpe(f->n); /* sets EPT */ + addTpe(EPT[0]); + addTpe(EPT[1]); + for (h = f->sub; h; h = h->nxt) + cnt_seq(h->this); + if (f == s->last) + break; + } +} + +static void +typ_seq(Sequence *s) +{ + T_sum = 0; + TPE[0] = 2; TPE[1] = 0; + cnt_seq(s); + if (T_sum > 2) /* more than one type */ + { TPE[0] = 5*DELTA; /* non-mixing */ + TPE[1] = 0; + } +} + +static int +hidden(Lextok *n) +{ + if (n) + switch (n->ntyp) { + case FULL: case EMPTY: + case NFULL: case NEMPTY: case TIMEOUT: + Nn[(T_mus++)%2] = n; + break; + case '!': case UMIN: case '~': case ASSERT: case 'c': + (void) hidden(n->lft); + break; + case '/': case '*': case '-': case '+': + case '%': case LT: case GT: case '&': case '^': + case '|': case LE: case GE: case NE: case '?': + case EQ: case OR: case AND: case LSHIFT: case RSHIFT: + (void) hidden(n->lft); + (void) hidden(n->rgt); + break; + } + return T_mus; +} + +static int +getNid(Lextok *n) +{ + if (n->sym + && n->sym->type == STRUCT + && n->rgt && n->rgt->lft) + return getNid(n->rgt->lft); + + if (!n->sym || n->sym->Nid == 0) + { fatal("bad channel name '%s'", + (n->sym)?n->sym->name:"no name"); + } + return n->sym->Nid; +} + +static int +valTpe(Lextok *n) +{ int res = 2; + /* + 2 = local + 2+1 .. 2+1*DELTA = nfull, 's' - require q_full==false + 2+1+1*DELTA .. 2+2*DELTA = nempty, 'r' - require q_len!=0 + 2+1+2*DELTA .. 2+3*DELTA = empty - require q_len==0 + 2+1+3*DELTA .. 2+4*DELTA = full - require q_full==true + 5*DELTA = non-mixing (i.e., always makes the selection global) + 6*DELTA = timeout (conditionally safe) + 7*DELTA = @, process deletion (conditionally safe) + */ + switch (n->ntyp) { /* a series of fall-thru cases: */ + case FULL: res += DELTA; /* add 3*DELTA + chan nr */ + case EMPTY: res += DELTA; /* add 2*DELTA + chan nr */ + case 'r': + case NEMPTY: res += DELTA; /* add 1*DELTA + chan nr */ + case 's': + case NFULL: res += getNid(n->lft); /* add channel nr */ + break; + + case TIMEOUT: res = 6*DELTA; break; + case '@': res = 7*DELTA; break; + default: break; + } + return res; +} + +static void +Tpe(Lextok *n) /* mixing in selections */ +{ + EPT[0] = 2; EPT[1] = 0; + + if (!n) return; + + T_mus = 0; + Nn[0] = Nn[1] = ZN; + + if (n->ntyp == 'c') + { if (hidden(n->lft) > 2) + { EPT[0] = 5*DELTA; /* non-mixing */ + EPT[1] = 0; + return; + } + } else + Nn[0] = n; + + if (Nn[0]) EPT[0] = valTpe(Nn[0]); + if (Nn[1]) EPT[1] = valTpe(Nn[1]); +} + +static void +put_escp(Element *e) +{ int n; + SeqList *x; + + if (e->esc /* && e->n->ntyp != GOTO */ && e->n->ntyp != '.') + { for (x = e->esc, n = 0; x; x = x->nxt, n++) + { int i = huntele(x->this->frst, e->status, -1)->seqno; + fprintf(tt, "\ttrans[%d][%d]->escp[%d] = %d;\n", + Pid, e->seqno, n, i); + fprintf(tt, "\treached%d[%d] = 1;\n", + Pid, i); + } + for (x = e->esc, n=0; x; x = x->nxt, n++) + { fprintf(tt, " /* escape #%d: %d */\n", n, + huntele(x->this->frst, e->status, -1)->seqno); + put_seq(x->this, 2, 0); /* args?? */ + } + fprintf(tt, " /* end-escapes */\n"); + } +} + +static void +put_sub(Element *e, int Tt0, int Tt1) +{ Sequence *s = e->n->sl->this; + Element *g = ZE; + int a; + + patch_atomic(s); + putskip(s->frst->seqno); + g = huntstart(s->frst); + a = g->seqno; + + if (0) printf("put_sub %d -> %d -> %d\n", e->seqno, s->frst->seqno, a); + + if ((e->n->ntyp == ATOMIC + || e->n->ntyp == D_STEP) + && scan_seq(s)) + mark_seq(s); + s->last->nxt = e->nxt; + + typ_seq(s); /* sets TPE */ + + if (e->n->ntyp == D_STEP) + { int inherit = (e->status&(ATOM|L_ATOM)); + fprintf(tm, "\tcase %d: ", uniq++); + fprintf(tm, "/* STATE %d - line %d %s - [", + e->seqno, e->n->ln, e->n->fn->name); + comment(tm, e->n, 0); + fprintf(tm, "] */\n\t\t"); + + if (s->last->n->ntyp == BREAK) + OkBreak = target(huntele(s->last->nxt, + s->last->status, -1))->Seqno; + else + OkBreak = -1; + + if (!putcode(tm, s, e->nxt, 0, e->n->ln, e->seqno)) + { + fprintf(tm, "\n#if defined(C_States) && (HAS_TRACK==1)\n"); + fprintf(tm, "\t\tc_update((uchar *) &(now.c_state[0]));\n"); + fprintf(tm, "#endif\n"); + + fprintf(tm, "\t\t_m = %d", getweight(s->frst->n)); + if (m_loss && s->frst->n->ntyp == 's') + fprintf(tm, "+delta_m; delta_m = 0"); + fprintf(tm, "; goto P999;\n\n"); + } + + fprintf(tb, "\tcase %d: ", uniq-1); + fprintf(tb, "/* STATE %d */\n", e->seqno); + fprintf(tb, "\t\tsv_restor();\n"); + fprintf(tb, "\t\tgoto R999;\n"); + if (e->nxt) + a = huntele(e->nxt, e->status, -1)->seqno; + else + a = 0; + tr_map(uniq-1, e); + fprintf(tt, "/*->*/\ttrans[%d][%d]\t= ", + Pid, e->seqno); + fprintf(tt, "settr(%d,%d,%d,%d,%d,\"", + e->Seqno, D_ATOM|inherit, a, uniq-1, uniq-1); + comment(tt, e->n, e->seqno); + fprintf(tt, "\", %d, ", (s->frst->status&I_GLOB)?1:0); + fprintf(tt, "%d, %d);\n", TPE[0], TPE[1]); + put_escp(e); + } else + { /* ATOMIC or NON_ATOMIC */ + fprintf(tt, "\tT = trans[ %d][%d] = ", Pid, e->seqno); + fprintf(tt, "settr(%d,%d,0,0,0,\"", + e->Seqno, (e->n->ntyp == ATOMIC)?ATOM:0); + comment(tt, e->n, e->seqno); + if ((e->status&CHECK2) + || (g->status&CHECK2)) + s->frst->status |= I_GLOB; + fprintf(tt, "\", %d, %d, %d);", + (s->frst->status&I_GLOB)?1:0, Tt0, Tt1); + blurb(tt, e); + fprintf(tt, "\tT->nxt\t= "); + fprintf(tt, "settr(%d,%d,%d,0,0,\"", + e->Seqno, (e->n->ntyp == ATOMIC)?ATOM:0, a); + comment(tt, e->n, e->seqno); + fprintf(tt, "\", %d, ", (s->frst->status&I_GLOB)?1:0); + if (e->n->ntyp == NON_ATOMIC) + { fprintf(tt, "%d, %d);", Tt0, Tt1); + blurb(tt, e); + put_seq(s, Tt0, Tt1); + } else + { fprintf(tt, "%d, %d);", TPE[0], TPE[1]); + blurb(tt, e); + put_seq(s, TPE[0], TPE[1]); + } + } +} + +typedef struct CaseCache { + int m, b, owner; + Element *e; + Lextok *n; + FSM_use *u; + struct CaseCache *nxt; +} CaseCache; + +CaseCache *casing[6]; + +static int +identical(Lextok *p, Lextok *q) +{ + if ((!p && q) || (p && !q)) + return 0; + if (!p) + return 1; + + if (p->ntyp != q->ntyp + || p->ismtyp != q->ismtyp + || p->val != q->val + || p->indstep != q->indstep + || p->sym != q->sym + || p->sq != q->sq + || p->sl != q->sl) + return 0; + + return identical(p->lft, q->lft) + && identical(p->rgt, q->rgt); +} + +static int +samedeads(FSM_use *a, FSM_use *b) +{ FSM_use *p, *q; + + for (p = a, q = b; p && q; p = p->nxt, q = q->nxt) + if (p->var != q->var + || p->special != q->special) + return 0; + return (!p && !q); +} + +static Element * +findnext(Element *f) +{ Element *g; + + if (f->n->ntyp == GOTO) + { g = get_lab(f->n, 1); + return huntele(g, f->status, -1); + } + return f->nxt; +} + +static Element * +advance(Element *e, int stopat) +{ Element *f = e; + + if (stopat) + while (f && f->seqno != stopat) + { f = findnext(f); + switch (f->n->ntyp) { + case GOTO: + case '.': + case PRINT: + case PRINTM: + break; + default: + return f; + } + } + return (Element *) 0; +} + +static int +equiv_merges(Element *a, Element *b) +{ Element *f, *g; + int stopat_a, stopat_b; + + if (a->merge_start) + stopat_a = a->merge_start; + else + stopat_a = a->merge; + + if (b->merge_start) + stopat_b = b->merge_start; + else + stopat_b = b->merge; + + if (!stopat_a && !stopat_b) + return 1; + + for (;;) + { + f = advance(a, stopat_a); + g = advance(b, stopat_b); + if (!f && !g) + return 1; + if (f && g) + return identical(f->n, g->n); + else + return 0; + } + return 1; +} + +static CaseCache * +prev_case(Element *e, int owner) +{ int j; CaseCache *nc; + + switch (e->n->ntyp) { + case 'r': j = 0; break; + case 's': j = 1; break; + case 'c': j = 2; break; + case ASGN: j = 3; break; + case ASSERT: j = 4; break; + default: j = 5; break; + } + for (nc = casing[j]; nc; nc = nc->nxt) + if (identical(nc->n, e->n) + && samedeads(nc->u, e->dead) + && equiv_merges(nc->e, e) + && nc->owner == owner) + return nc; + + return (CaseCache *) 0; +} + +static void +new_case(Element *e, int m, int b, int owner) +{ int j; CaseCache *nc; + + switch (e->n->ntyp) { + case 'r': j = 0; break; + case 's': j = 1; break; + case 'c': j = 2; break; + case ASGN: j = 3; break; + case ASSERT: j = 4; break; + default: j = 5; break; + } + nc = (CaseCache *) emalloc(sizeof(CaseCache)); + nc->owner = owner; + nc->m = m; + nc->b = b; + nc->e = e; + nc->n = e->n; + nc->u = e->dead; + nc->nxt = casing[j]; + casing[j] = nc; +} + +static int +nr_bup(Element *e) +{ FSM_use *u; + Lextok *v; + int nr = 0; + + switch (e->n->ntyp) { + case ASGN: + nr++; + break; + case 'r': + if (e->n->val >= 1) + nr++; /* random recv */ + for (v = e->n->rgt; v; v = v->rgt) + { if ((v->lft->ntyp == CONST + || v->lft->ntyp == EVAL)) + continue; + nr++; + } + break; + default: + break; + } + for (u = e->dead; u; u = u->nxt) + { switch (u->special) { + case 2: /* dead after write */ + if (e->n->ntyp == ASGN + && e->n->rgt->ntyp == CONST + && e->n->rgt->val == 0) + break; + nr++; + break; + case 1: /* dead after read */ + nr++; + break; + } } + return nr; +} + +static int +nrhops(Element *e) +{ Element *f = e, *g; + int cnt = 0; + int stopat; + + if (e->merge_start) + stopat = e->merge_start; + else + stopat = e->merge; +#if 0 + printf("merge: %d merge_start %d - seqno %d\n", + e->merge, e->merge_start, e->seqno); +#endif + do { + cnt += nr_bup(f); + + if (f->n->ntyp == GOTO) + { g = get_lab(f->n, 1); + if (g->seqno == stopat) + f = g; + else + f = huntele(g, f->status, stopat); + } else + { + f = f->nxt; + } + + if (f && !f->merge && !f->merge_single && f->seqno != stopat) + { fprintf(tm, "\n\t\tbad hop %s:%d -- at %d, <", + f->n->fn->name,f->n->ln, f->seqno); + comment(tm, f->n, 0); + fprintf(tm, "> looking for %d -- merge %d:%d:%d\n\t\t", + stopat, f->merge, f->merge_start, f->merge_single); + break; + } + } while (f && f->seqno != stopat); + + return cnt; +} + +static void +check_needed(void) +{ + if (multi_needed) + { fprintf(tm, "(trpt+1)->bup.ovals = grab_ints(%d);\n\t\t", + multi_needed); + multi_undo = multi_needed; + multi_needed = 0; + } +} + +static void +doforward(FILE *tm, Element *e) +{ FSM_use *u; + + putstmnt(tm, e->n, e->seqno); + + if (e->n->ntyp != ELSE && Det) + { fprintf(tm, ";\n\t\tif (trpt->o_pm&1)\n\t\t"); + fprintf(tm, "\tuerror(\"non-determinism in D_proctype\")"); + } + if (deadvar && !has_code) + for (u = e->dead; u; u = u->nxt) + { fprintf(tm, ";\n\t\t/* dead %d: %s */ ", + u->special, u->var->name); + + switch (u->special) { + case 2: /* dead after write -- lval already bupped */ + if (e->n->ntyp == ASGN) /* could be recv or asgn */ + { if (e->n->rgt->ntyp == CONST + && e->n->rgt->val == 0) + continue; /* already set to 0 */ + } + if (e->n->ntyp != 'r') + { XZ.sym = u->var; + fprintf(tm, "\n#ifdef HAS_CODE\n"); + fprintf(tm, "\t\tif (!readtrail)\n"); + fprintf(tm, "#endif\n\t\t\t"); + putname(tm, "", &XZ, 0, " = 0"); + break; + } /* else fall through */ + case 1: /* dead after read -- add asgn of rval -- needs bup */ + YZ[YZmax].sym = u->var; /* store for pan.b */ + CnT[YZcnt]++; /* this step added bups */ + if (multi_oval) + { check_needed(); + fprintf(tm, "(trpt+1)->bup.ovals[%d] = ", + multi_oval-1); + multi_oval++; + } else + fprintf(tm, "(trpt+1)->bup.oval = "); + putname(tm, "", &YZ[YZmax], 0, ";\n"); + fprintf(tm, "#ifdef HAS_CODE\n"); + fprintf(tm, "\t\tif (!readtrail)\n"); + fprintf(tm, "#endif\n\t\t\t"); + putname(tm, "", &YZ[YZmax], 0, " = 0"); + YZmax++; + break; + } } + fprintf(tm, ";\n\t\t"); +} + +static int +dobackward(Element *e, int casenr) +{ + if (!any_undo(e->n) && CnT[YZcnt] == 0) + { YZcnt--; + return 0; + } + + if (!didcase) + { fprintf(tb, "\n\tcase %d: ", casenr); + fprintf(tb, "/* STATE %d */\n\t\t", e->seqno); + didcase++; + } + + _isok++; + while (CnT[YZcnt] > 0) /* undo dead variable resets */ + { CnT[YZcnt]--; + YZmax--; + if (YZmax < 0) + fatal("cannot happen, dobackward", (char *)0); + fprintf(tb, ";\n\t/* %d */\t", YZmax); + putname(tb, "", &YZ[YZmax], 0, " = trpt->bup.oval"); + if (multi_oval > 0) + { multi_oval--; + fprintf(tb, "s[%d]", multi_oval-1); + } + } + + if (e->n->ntyp != '.') + { fprintf(tb, ";\n\t\t"); + undostmnt(e->n, e->seqno); + } + _isok--; + + YZcnt--; + return 1; +} + +static void +lastfirst(int stopat, Element *fin, int casenr) +{ Element *f = fin, *g; + + if (f->n->ntyp == GOTO) + { g = get_lab(f->n, 1); + if (g->seqno == stopat) + f = g; + else + f = huntele(g, f->status, stopat); + } else + f = f->nxt; + + if (!f || f->seqno == stopat + || (!f->merge && !f->merge_single)) + return; + lastfirst(stopat, f, casenr); +#if 0 + fprintf(tb, "\n\t/* merge %d -- %d:%d %d:%d:%d (casenr %d) ", + YZcnt, + f->merge_start, f->merge, + f->seqno, f?f->seqno:-1, stopat, + casenr); + comment(tb, f->n, 0); + fprintf(tb, " */\n"); + fflush(tb); +#endif + dobackward(f, casenr); +} + +static int modifier; + +static void +lab_transfer(Element *to, Element *from) +{ Symbol *ns, *s = has_lab(from, (1|2|4)); + Symbol *oc; + int ltp, usedit=0; + + if (!s) return; + + /* "from" could have all three labels -- rename + * to prevent jumps to the transfered copies + */ + oc = context; /* remember */ + for (ltp = 1; ltp < 8; ltp *= 2) /* 1, 2, and 4 */ + if ((s = has_lab(from, ltp)) != (Symbol *) 0) + { ns = (Symbol *) emalloc(sizeof(Symbol)); + ns->name = (char *) emalloc((int) strlen(s->name) + 4); + sprintf(ns->name, "%s%d", s->name, modifier); + + context = s->context; + set_lab(ns, to); + usedit++; + } + context = oc; /* restore */ + if (usedit) + { if (modifier++ > 990) + fatal("modifier overflow error", (char *) 0); + } +} + +static int +case_cache(Element *e, int a) +{ int bupcase = 0, casenr = uniq, fromcache = 0; + CaseCache *Cached = (CaseCache *) 0; + Element *f, *g; + int j, nrbups, mark, target; + extern int ccache; + + mark = (e->status&ATOM); /* could lose atomicity in a merge chain */ + + if (e->merge_mark > 0 + || (merger && e->merge_in == 0)) + { /* state nominally unreachable (part of merge chains) */ + if (e->n->ntyp != '.' + && e->n->ntyp != GOTO) + { fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno); + fprintf(tt, "settr(0,0,0,0,0,\""); + comment(tt, e->n, e->seqno); + fprintf(tt, "\",0,0,0);\n"); + } else + { fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno); + casenr = 1; /* mhs example */ + j = a; + goto haveit; /* pakula's example */ + } + + return -1; + } + + fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno); + + if (ccache + && Pid != claimnr + && Pid != eventmapnr + && (Cached = prev_case(e, Pid))) + { bupcase = Cached->b; + casenr = Cached->m; + fromcache = 1; + + fprintf(tm, "/* STATE %d - line %d %s - [", + e->seqno, e->n->ln, e->n->fn->name); + comment(tm, e->n, 0); + fprintf(tm, "] (%d:%d - %d) same as %d (%d:%d - %d) */\n", + e->merge_start, e->merge, e->merge_in, + casenr, + Cached->e->merge_start, Cached->e->merge, Cached->e->merge_in); + + goto gotit; + } + + fprintf(tm, "\tcase %d: /* STATE %d - line %d %s - [", + uniq++, e->seqno, e->n->ln, e->n->fn->name); + comment(tm, e->n, 0); + nrbups = (e->merge || e->merge_start) ? nrhops(e) : nr_bup(e); + fprintf(tm, "] (%d:%d:%d - %d) */\n\t\t", + e->merge_start, e->merge, nrbups, e->merge_in); + + if (nrbups > MAXMERGE-1) + fatal("merge requires more than 256 bups", (char *)0); + + if (e->n->ntyp != 'r' && Pid != claimnr && Pid != eventmapnr) + fprintf(tm, "IfNotBlocked\n\t\t"); + + if (multi_needed != 0 || multi_undo != 0) + fatal("cannot happen, case_cache", (char *) 0); + + if (nrbups > 1) + { multi_oval = 1; + multi_needed = nrbups; /* allocated after edge condition */ + } else + multi_oval = 0; + + memset(CnT, 0, sizeof(CnT)); + YZmax = YZcnt = 0; + +/* NEW 4.2.6 */ + if (Pid == claimnr) + { + fprintf(tm, "\n#if defined(VERI) && !defined(NP)\n\t\t"); + fprintf(tm, "{ static int reported%d = 0;\n\t\t", e->seqno); + /* source state changes in retrans and must be looked up in frm_st0[t->forw] */ + fprintf(tm, " if (verbose && !reported%d)\n\t\t", e->seqno); + fprintf(tm, " { printf(\"depth %%d: Claim reached state %%d (line %%d)\\n\",\n\t\t"); + fprintf(tm, " depth, frm_st0[t->forw], src_claim[%d]);\n\t\t", e->seqno); + fprintf(tm, " reported%d = 1;\n\t\t", e->seqno); + fprintf(tm, " fflush(stdout);\n\t\t"); + fprintf(tm, "} }\n"); + fprintf(tm, "#endif\n\t\t"); + } +/* end */ + + /* the src xrefs have the numbers in e->seqno builtin */ + fprintf(tm, "reached[%d][%d] = 1;\n\t\t", Pid, e->seqno); + + doforward(tm, e); + + if (e->merge_start) + target = e->merge_start; + else + target = e->merge; + + if (target) + { f = e; + +more: if (f->n->ntyp == GOTO) + { g = get_lab(f->n, 1); + if (g->seqno == target) + f = g; + else + f = huntele(g, f->status, target); + } else + f = f->nxt; + + + if (f && f->seqno != target) + { if (!f->merge && !f->merge_single) + { fprintf(tm, "/* stop at bad hop %d, %d */\n\t\t", + f->seqno, target); + goto out; + } + fprintf(tm, "/* merge: "); + comment(tm, f->n, 0); + fprintf(tm, "(%d, %d, %d) */\n\t\t", f->merge, f->seqno, target); + fprintf(tm, "reached[%d][%d] = 1;\n\t\t", Pid, f->seqno); + YZcnt++; + lab_transfer(e, f); + mark = f->status&(ATOM|L_ATOM); /* last step wins */ + doforward(tm, f); + if (f->merge_in == 1) f->merge_mark++; + + goto more; + } } +out: + fprintf(tm, "_m = %d", getweight(e->n)); + if (m_loss && e->n->ntyp == 's') fprintf(tm, "+delta_m; delta_m = 0"); + fprintf(tm, "; goto P999; /* %d */\n", YZcnt); + + multi_needed = 0; + didcase = 0; + + if (target) + lastfirst(target, e, casenr); /* mergesteps only */ + + dobackward(e, casenr); /* the original step */ + + fprintf(tb, ";\n\t\t"); + + if (e->merge || e->merge_start) + { if (!didcase) + { fprintf(tb, "\n\tcase %d: ", casenr); + fprintf(tb, "/* STATE %d */", e->seqno); + didcase++; + } else + fprintf(tb, ";"); + } else + fprintf(tb, ";"); + fprintf(tb, "\n\t\t"); + + if (multi_undo) + { fprintf(tb, "ungrab_ints(trpt->bup.ovals, %d);\n\t\t", + multi_undo); + multi_undo = 0; + } + if (didcase) + { fprintf(tb, "goto R999;\n"); + bupcase = casenr; + } + + if (!e->merge && !e->merge_start) + new_case(e, casenr, bupcase, Pid); + +gotit: + j = a; + if (e->merge_start) + j = e->merge_start; + else if (e->merge) + j = e->merge; +haveit: + fprintf(tt, "%ssettr(%d,%d,%d,%d,%d,\"", fromcache?"/* c */ ":"", + e->Seqno, mark, j, casenr, bupcase); + + return (fromcache)?0:casenr; +} + +static void +put_el(Element *e, int Tt0, int Tt1) +{ int a, casenr, Global_ref; + Element *g = ZE; + + if (e->n->ntyp == GOTO) + { g = get_lab(e->n, 1); + g = huntele(g, e->status, -1); + cross_dsteps(e->n, g->n); + a = g->seqno; + } else if (e->nxt) + { g = huntele(e->nxt, e->status, -1); + a = g->seqno; + } else + a = 0; + if (g + && (g->status&CHECK2 /* entering remotely ref'd state */ + || e->status&CHECK2)) /* leaving remotely ref'd state */ + e->status |= I_GLOB; + + /* don't remove dead edges in here, to preserve structure of fsm */ + if (e->merge_start || e->merge) + goto non_generic; + + /*** avoid duplicate or redundant cases in pan.m ***/ + switch (e->n->ntyp) { + case ELSE: + casenr = 2; /* standard else */ + putskip(e->seqno); + goto generic_case; + /* break; */ + case '.': + case GOTO: + case BREAK: + putskip(e->seqno); + casenr = 1; /* standard goto */ +generic_case: fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno); + fprintf(tt, "settr(%d,%d,%d,%d,0,\"", + e->Seqno, e->status&ATOM, a, casenr); + break; +#ifndef PRINTF + case PRINT: + goto non_generic; + case PRINTM: + goto non_generic; +#endif + case 'c': + if (e->n->lft->ntyp == CONST + && e->n->lft->val == 1) /* skip or true */ + { casenr = 1; + putskip(e->seqno); + goto generic_case; + } + goto non_generic; + + default: +non_generic: + casenr = case_cache(e, a); + if (casenr < 0) return; /* unreachable state */ + break; + } + /* tailend of settr(...); */ + Global_ref = (e->status&I_GLOB)?1:has_global(e->n); + comment(tt, e->n, e->seqno); + fprintf(tt, "\", %d, ", Global_ref); + if (Tt0 != 2) + { fprintf(tt, "%d, %d);", Tt0, Tt1); + } else + { Tpe(e->n); /* sets EPT */ + fprintf(tt, "%d, %d);", EPT[0], EPT[1]); + } + if ((e->merge_start && e->merge_start != a) + || (e->merge && e->merge != a)) + { fprintf(tt, " /* m: %d -> %d,%d */\n", + a, e->merge_start, e->merge); + fprintf(tt, " reached%d[%d] = 1;", + Pid, a); /* Sheinman's example */ + } + fprintf(tt, "\n"); + + if (casenr > 2) + tr_map(casenr, e); + put_escp(e); +} + +static void +nested_unless(Element *e, Element *g) +{ struct SeqList *y = e->esc, *z = g->esc; + + for ( ; y && z; y = y->nxt, z = z->nxt) + if (z->this != y->this) + break; + if (!y && !z) + return; + + if (g->n->ntyp != GOTO + && g->n->ntyp != '.' + && e->sub->nxt) + { printf("error: (%s:%d) saw 'unless' on a guard:\n", + (e->n)?e->n->fn->name:"-", + (e->n)?e->n->ln:0); + printf("=====>instead of\n"); + printf(" do (or if)\n"); + printf(" :: ...\n"); + printf(" :: stmnt1 unless stmnt2\n"); + printf(" od (of fi)\n"); + printf("=====>use\n"); + printf(" do (or if)\n"); + printf(" :: ...\n"); + printf(" :: stmnt1\n"); + printf(" od (or fi) unless stmnt2\n"); + printf("=====>or rewrite\n"); + } +} + +static void +put_seq(Sequence *s, int Tt0, int Tt1) +{ SeqList *h; + Element *e, *g; + int a, deadlink; + + if (0) printf("put_seq %d\n", s->frst->seqno); + + for (e = s->frst; e; e = e->nxt) + { + if (0) printf(" step %d\n", e->seqno); + if (e->status & DONE) + { + if (0) printf(" done before\n"); + goto checklast; + } + e->status |= DONE; + + if (e->n->ln) + putsrc(e); + + if (e->n->ntyp == UNLESS) + { + if (0) printf(" an unless\n"); + put_seq(e->sub->this, Tt0, Tt1); + } else if (e->sub) + { + if (0) printf(" has sub\n"); + fprintf(tt, "\tT = trans[%d][%d] = ", + Pid, e->seqno); + fprintf(tt, "settr(%d,%d,0,0,0,\"", + e->Seqno, e->status&ATOM); + comment(tt, e->n, e->seqno); + if (e->status&CHECK2) + e->status |= I_GLOB; + fprintf(tt, "\", %d, %d, %d);", + (e->status&I_GLOB)?1:0, Tt0, Tt1); + blurb(tt, e); + for (h = e->sub; h; h = h->nxt) + { putskip(h->this->frst->seqno); + g = huntstart(h->this->frst); + if (g->esc) + nested_unless(e, g); + a = g->seqno; + + if (g->n->ntyp == 'c' + && g->n->lft->ntyp == CONST + && g->n->lft->val == 0 /* 0 or false */ + && !g->esc) + { fprintf(tt, "#if 0\n\t/* dead link: */\n"); + deadlink = 1; + if (verbose&32) + printf("spin: line %3d %s, Warning: condition is always false\n", + g->n->ln, g->n->fn?g->n->fn->name:""); + } else + deadlink = 0; + if (0) printf(" settr %d %d\n", a, 0); + if (h->nxt) + fprintf(tt, "\tT = T->nxt\t= "); + else + fprintf(tt, "\t T->nxt\t= "); + fprintf(tt, "settr(%d,%d,%d,0,0,\"", + e->Seqno, e->status&ATOM, a); + comment(tt, e->n, e->seqno); + if (g->status&CHECK2) + h->this->frst->status |= I_GLOB; + fprintf(tt, "\", %d, %d, %d);", + (h->this->frst->status&I_GLOB)?1:0, + Tt0, Tt1); + blurb(tt, e); + if (deadlink) + fprintf(tt, "#endif\n"); + } + for (h = e->sub; h; h = h->nxt) + put_seq(h->this, Tt0, Tt1); + } else + { + if (0) printf(" [non]atomic %d\n", e->n->ntyp); + if (e->n->ntyp == ATOMIC + || e->n->ntyp == D_STEP + || e->n->ntyp == NON_ATOMIC) + put_sub(e, Tt0, Tt1); + else + { + if (0) printf(" put_el %d\n", e->seqno); + put_el(e, Tt0, Tt1); + } + } +checklast: if (e == s->last) + break; + } + if (0) printf("put_seq done\n"); +} + +static void +patch_atomic(Sequence *s) /* catch goto's that break the chain */ +{ Element *f, *g; + SeqList *h; + + for (f = s->frst; f ; f = f->nxt) + { + if (f->n && f->n->ntyp == GOTO) + { g = get_lab(f->n,1); + cross_dsteps(f->n, g->n); + if ((f->status & (ATOM|L_ATOM)) + && !(g->status & (ATOM|L_ATOM))) + { f->status &= ~ATOM; + f->status |= L_ATOM; + } + /* bridge atomics */ + if ((f->status & L_ATOM) + && (g->status & (ATOM|L_ATOM))) + { f->status &= ~L_ATOM; + f->status |= ATOM; + } + } else + for (h = f->sub; h; h = h->nxt) + patch_atomic(h->this); + if (f == s->extent) + break; + } +} + +static void +mark_seq(Sequence *s) +{ Element *f; + SeqList *h; + + for (f = s->frst; f; f = f->nxt) + { f->status |= I_GLOB; + + if (f->n->ntyp == ATOMIC + || f->n->ntyp == NON_ATOMIC + || f->n->ntyp == D_STEP) + mark_seq(f->n->sl->this); + + for (h = f->sub; h; h = h->nxt) + mark_seq(h->this); + if (f == s->last) + return; + } +} + +static Element * +find_target(Element *e) +{ Element *f; + + if (!e) return e; + + if (t_cyc++ > 32) + { fatal("cycle of goto jumps", (char *) 0); + } + switch (e->n->ntyp) { + case GOTO: + f = get_lab(e->n,1); + cross_dsteps(e->n, f->n); + f = find_target(f); + break; + case BREAK: + if (e->nxt) + { f = find_target(huntele(e->nxt, e->status, -1)); + break; /* 4.3.0 -- was missing */ + } + /* else fall through */ + default: + f = e; + break; + } + return f; +} + +Element * +target(Element *e) +{ + if (!e) return e; + lineno = e->n->ln; + Fname = e->n->fn; + t_cyc = 0; + return find_target(e); +} + +static int +scan_seq(Sequence *s) +{ Element *f, *g; + SeqList *h; + + for (f = s->frst; f; f = f->nxt) + { if ((f->status&CHECK2) + || has_global(f->n)) + return 1; + if (f->n->ntyp == GOTO) /* may reach other atomic */ + { +#if 0 + /* if jumping from an atomic without globals into + * one with globals, this does the wrong thing + * example by Claus Traulsen, 22 June 2007 + */ + g = target(f); + if (g + && !(f->status & L_ATOM) + && !(g->status & (ATOM|L_ATOM))) +#endif + { fprintf(tt, " /* mark-down line %d */\n", + f->n->ln); + return 1; /* assume worst case */ + } } + for (h = f->sub; h; h = h->nxt) + if (scan_seq(h->this)) + return 1; + if (f == s->last) + break; + } + return 0; +} + +static int +glob_args(Lextok *n) +{ int result = 0; + Lextok *v; + + for (v = n->rgt; v; v = v->rgt) + { if (v->lft->ntyp == CONST) + continue; + if (v->lft->ntyp == EVAL) + result += has_global(v->lft->lft); + else + result += has_global(v->lft); + } + return result; +} + +int +has_global(Lextok *n) +{ Lextok *v; extern int runsafe; + + if (!n) return 0; + if (AllGlobal) return 1; /* global provided clause */ + + switch (n->ntyp) { + case ATOMIC: + case D_STEP: + case NON_ATOMIC: + return scan_seq(n->sl->this); + + case '.': + case BREAK: + case GOTO: + case CONST: + return 0; + + case ELSE: return n->val; /* true if combined with chan refs */ + + case 's': return glob_args(n)!=0 || ((n->sym->xu&(XS|XX)) != XS); + case 'r': return glob_args(n)!=0 || ((n->sym->xu&(XR|XX)) != XR); + case 'R': return glob_args(n)!=0 || (((n->sym->xu)&(XR|XS|XX)) != (XR|XS)); + case NEMPTY: return ((n->sym->xu&(XR|XX)) != XR); + case NFULL: return ((n->sym->xu&(XS|XX)) != XS); + case FULL: return ((n->sym->xu&(XR|XX)) != XR); + case EMPTY: return ((n->sym->xu&(XS|XX)) != XS); + case LEN: return (((n->sym->xu)&(XR|XS|XX)) != (XR|XS)); + + case NAME: + if (n->sym->context + || (n->sym->hidden&64) + || strcmp(n->sym->name, "_pid") == 0 + || strcmp(n->sym->name, "_") == 0) + return 0; + return 1; + + case RUN: return 1-runsafe; + + case C_CODE: case C_EXPR: + return glob_inline(n->sym->name); + + case ENABLED: case PC_VAL: case NONPROGRESS: + case 'p': case 'q': + case TIMEOUT: + return 1; + + /* @ was 1 (global) since 2.8.5 + in 3.0 it is considered local and + conditionally safe, provided: + II is the youngest process + and nrprocs < MAXPROCS + */ + case '@': return 0; + + case '!': case UMIN: case '~': case ASSERT: + return has_global(n->lft); + + case '/': case '*': case '-': case '+': + case '%': case LT: case GT: case '&': case '^': + case '|': case LE: case GE: case NE: case '?': + case EQ: case OR: case AND: case LSHIFT: + case RSHIFT: case 'c': case ASGN: + return has_global(n->lft) || has_global(n->rgt); + + case PRINT: + for (v = n->lft; v; v = v->rgt) + if (has_global(v->lft)) return 1; + return 0; + case PRINTM: + return has_global(n->lft); + } + return 0; +} + +static void +Bailout(FILE *fd, char *str) +{ + if (!GenCode) + fprintf(fd, "continue%s", str); + else if (IsGuard) + fprintf(fd, "%s%s", NextLab[Level], str); + else + fprintf(fd, "Uerror(\"block in step seq\")%s", str); +} + +#define cat0(x) putstmnt(fd,now->lft,m); fprintf(fd, x); \ + putstmnt(fd,now->rgt,m) +#define cat1(x) fprintf(fd,"("); cat0(x); fprintf(fd,")") +#define cat2(x,y) fprintf(fd,x); putstmnt(fd,y,m) +#define cat3(x,y,z) fprintf(fd,x); putstmnt(fd,y,m); fprintf(fd,z) + +void +putstmnt(FILE *fd, Lextok *now, int m) +{ Lextok *v; + int i, j; + + if (!now) { fprintf(fd, "0"); return; } + lineno = now->ln; + Fname = now->fn; + + switch (now->ntyp) { + case CONST: fprintf(fd, "%d", now->val); break; + case '!': cat3(" !(", now->lft, ")"); break; + case UMIN: cat3(" -(", now->lft, ")"); break; + case '~': cat3(" ~(", now->lft, ")"); break; + + case '/': cat1("/"); break; + case '*': cat1("*"); break; + case '-': cat1("-"); break; + case '+': cat1("+"); break; + case '%': cat1("%%"); break; + case '&': cat1("&"); break; + case '^': cat1("^"); break; + case '|': cat1("|"); break; + case LT: cat1("<"); break; + case GT: cat1(">"); break; + case LE: cat1("<="); break; + case GE: cat1(">="); break; + case NE: cat1("!="); break; + case EQ: cat1("=="); break; + case OR: cat1("||"); break; + case AND: cat1("&&"); break; + case LSHIFT: cat1("<<"); break; + case RSHIFT: cat1(">>"); break; + + case TIMEOUT: + if (separate == 2) + fprintf(fd, "((tau)&1)"); + else + fprintf(fd, "((trpt->tau)&1)"); + if (GenCode) + printf("spin: line %3d, warning: 'timeout' in d_step sequence\n", + lineno); + /* is okay as a guard */ + break; + + case RUN: + if (claimproc + && strcmp(now->sym->name, claimproc) == 0) + fatal("claim %s, (not runnable)", claimproc); + if (eventmap + && strcmp(now->sym->name, eventmap) == 0) + fatal("eventmap %s, (not runnable)", eventmap); + + if (GenCode) + fatal("'run' in d_step sequence (use atomic)", + (char *)0); + + fprintf(fd,"addproc(%d", fproc(now->sym->name)); + for (v = now->lft, i = 0; v; v = v->rgt, i++) + { cat2(", ", v->lft); + } + check_param_count(i, now); + + if (i > Npars) + { printf("\t%d parameters used, max %d expected\n", i, Npars); + fatal("too many parameters in run %s(...)", + now->sym->name); + } + for ( ; i < Npars; i++) + fprintf(fd, ", 0"); + fprintf(fd, ")"); + break; + + case ENABLED: + cat3("enabled(II, ", now->lft, ")"); + break; + + case NONPROGRESS: + /* o_pm&4=progress, tau&128=claim stutter */ + if (separate == 2) + fprintf(fd, "(!(o_pm&4) && !(tau&128))"); + else + fprintf(fd, "(!(trpt->o_pm&4) && !(trpt->tau&128))"); + break; + + case PC_VAL: + cat3("((P0 *) Pptr(", now->lft, "+BASE))->_p"); + break; + + case LEN: + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&1) || "); + putname(fd, "q_R_check(", now->lft, m, ""); + fprintf(fd, ", II)) &&\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&2) || "); + putname(fd, "q_S_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + putname(fd, "q_len(", now->lft, m, ")"); + break; + + case FULL: + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&1) || "); + putname(fd, "q_R_check(", now->lft, m, ""); + fprintf(fd, ", II)) &&\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&2) || "); + putname(fd, "q_S_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + putname(fd, "q_full(", now->lft, m, ")"); + break; + + case EMPTY: + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&1) || "); + putname(fd, "q_R_check(", now->lft, m, ""); + fprintf(fd, ", II)) &&\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&2) || "); + putname(fd, "q_S_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + putname(fd, "(q_len(", now->lft, m, ")==0)"); + break; + + case NFULL: + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&2) || "); + putname(fd, "q_S_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + putname(fd, "(!q_full(", now->lft, m, "))"); + break; + + case NEMPTY: + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&1) || "); + putname(fd, "q_R_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + putname(fd, "(q_len(", now->lft, m, ")>0)"); + break; + + case 's': + if (Pid == eventmapnr) + { fprintf(fd, "if ((ot == EVENT_TRACE && _tp != 's') "); + putname(fd, "|| _qid+1 != ", now->lft, m, ""); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + continue; + fprintf(fd, " \\\n\t\t|| qrecv("); + putname(fd, "", now->lft, m, ", "); + putname(fd, "q_len(", now->lft, m, ")-1, "); + fprintf(fd, "%d, 0) != ", i); + if (v->lft->ntyp == CONST) + putstmnt(fd, v->lft, m); + else /* EVAL */ + putstmnt(fd, v->lft->lft, m); + } + fprintf(fd, ")\n"); + fprintf(fd, "\t\t continue"); + putname(th, " || (x_y3_ == ", now->lft, m, ")"); + break; + } + if (TestOnly) + { if (m_loss) + fprintf(fd, "1"); + else + putname(fd, "!q_full(", now->lft, m, ")"); + break; + } + if (has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "if (q_claim[", now->lft, m, "]&2) "); + putname(fd, "q_S_check(", now->lft, m, ", II);"); + fprintf(fd, "\n#endif\n\t\t"); + } + fprintf(fd, "if (q_%s", + (u_sync > 0 && u_async == 0)?"len":"full"); + putname(fd, "(", now->lft, m, "))\n"); + + if (m_loss) + fprintf(fd, "\t\t{ nlost++; delta_m = 1; } else {"); + else + { fprintf(fd, "\t\t\t"); + Bailout(fd, ";"); + } + + if (has_enabled) + fprintf(fd, "\n\t\tif (TstOnly) return 1;"); + + if (u_sync && !u_async && rvopt) + fprintf(fd, "\n\n\t\tif (no_recvs(II)) continue;\n"); + + fprintf(fd, "\n#ifdef HAS_CODE\n"); + fprintf(fd, "\t\tif (readtrail && gui) {\n"); + fprintf(fd, "\t\t\tchar simtmp[32];\n"); + putname(fd, "\t\t\tsprintf(simvals, \"%%d!\", ", now->lft, m, ");\n"); + _isok++; + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft, "); strcat(simvals, simtmp);"); + if (v->rgt) + fprintf(fd, "\t\tstrcat(simvals, \",\");\n"); + } + _isok--; + fprintf(fd, "\t\t}\n"); + fprintf(fd, "#endif\n\t\t"); + + putname(fd, "\n\t\tqsend(", now->lft, m, ""); + fprintf(fd, ", %d", now->val); + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { cat2(", ", v->lft); + } + if (i > Mpars) + { terse++; + putname(stdout, "channel name: ", now->lft, m, "\n"); + terse--; + printf(" %d msg parameters sent, %d expected\n", i, Mpars); + fatal("too many pars in send", ""); + } + for ( ; i < Mpars; i++) + fprintf(fd, ", 0"); + fprintf(fd, ")"); + if (u_sync) + { fprintf(fd, ";\n\t\t"); + if (u_async) + putname(fd, "if (q_zero(", now->lft, m, ")) "); + putname(fd, "{ boq = ", now->lft, m, ""); + if (GenCode) + fprintf(fd, "; Uerror(\"rv-attempt in d_step\")"); + fprintf(fd, "; }"); + } + if (m_loss) + fprintf(fd, ";\n\t\t}\n\t\t"); /* end of m_loss else */ + break; + + case 'r': + if (Pid == eventmapnr) + { fprintf(fd, "if ((ot == EVENT_TRACE && _tp != 'r') "); + putname(fd, "|| _qid+1 != ", now->lft, m, ""); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + continue; + fprintf(fd, " \\\n\t\t|| qrecv("); + putname(fd, "", now->lft, m, ", "); + fprintf(fd, "0, %d, 0) != ", i); + if (v->lft->ntyp == CONST) + putstmnt(fd, v->lft, m); + else /* EVAL */ + putstmnt(fd, v->lft->lft, m); + } + fprintf(fd, ")\n"); + fprintf(fd, "\t\t continue"); + + putname(tc, " || (x_y3_ == ", now->lft, m, ")"); + + break; + } + if (TestOnly) + { fprintf(fd, "(("); + if (u_sync) fprintf(fd, "(boq == -1 && "); + + putname(fd, "q_len(", now->lft, m, ")"); + + if (u_sync && now->val <= 1) + { putname(fd, ") || (boq == ", now->lft,m," && "); + putname(fd, "q_zero(", now->lft,m,"))"); + } + + fprintf(fd, ")"); + if (now->val == 0 || now->val == 2) + { for (v = now->rgt, i=j=0; v; v = v->rgt, i++) + { if (v->lft->ntyp == CONST) + { cat3("\n\t\t&& (", v->lft, " == "); + putname(fd, "qrecv(", now->lft, m, ", "); + fprintf(fd, "0, %d, 0))", i); + } else if (v->lft->ntyp == EVAL) + { cat3("\n\t\t&& (", v->lft->lft, " == "); + putname(fd, "qrecv(", now->lft, m, ", "); + fprintf(fd, "0, %d, 0))", i); + } else + { j++; continue; + } + } + } else + { fprintf(fd, "\n\t\t&& Q_has("); + putname(fd, "", now->lft, m, ""); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp == CONST) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft, m); + } else if (v->lft->ntyp == EVAL) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft->lft, m); + } else + { fprintf(fd, ", 0, 0"); + } } + for ( ; i < Mpars; i++) + fprintf(fd, ", 0, 0"); + fprintf(fd, ")"); + } + fprintf(fd, ")"); + break; + } + if (has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "if (q_claim[", now->lft, m, "]&1) "); + putname(fd, "q_R_check(", now->lft, m, ", II);"); + fprintf(fd, "\n#endif\n\t\t"); + } + if (u_sync) + { if (now->val >= 2) + { if (u_async) + { fprintf(fd, "if ("); + putname(fd, "q_zero(", now->lft,m,"))"); + fprintf(fd, "\n\t\t{\t"); + } + fprintf(fd, "uerror(\"polling "); + fprintf(fd, "rv chan\");\n\t\t"); + if (u_async) + fprintf(fd, " continue;\n\t\t}\n\t\t"); + fprintf(fd, "IfNotBlocked\n\t\t"); + } else + { fprintf(fd, "if ("); + if (u_async == 0) + putname(fd, "boq != ", now->lft,m,") "); + else + { putname(fd, "q_zero(", now->lft,m,"))"); + fprintf(fd, "\n\t\t{\tif (boq != "); + putname(fd, "", now->lft,m,") "); + Bailout(fd, ";\n\t\t} else\n\t\t"); + fprintf(fd, "{\tif (boq != -1) "); + } + Bailout(fd, ";\n\t\t"); + if (u_async) + fprintf(fd, "}\n\t\t"); + } } + putname(fd, "if (q_len(", now->lft, m, ") == 0) "); + Bailout(fd, ""); + + for (v = now->rgt, j=0; v; v = v->rgt) + { if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + j++; /* count settables */ + } + fprintf(fd, ";\n\n\t\tXX=1"); +/* test */ if (now->val == 0 || now->val == 2) + { for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp == CONST) + { fprintf(fd, ";\n\t\t"); + cat3("if (", v->lft, " != "); + putname(fd, "qrecv(", now->lft, m, ", "); + fprintf(fd, "0, %d, 0)) ", i); + Bailout(fd, ""); + } else if (v->lft->ntyp == EVAL) + { fprintf(fd, ";\n\t\t"); + cat3("if (", v->lft->lft, " != "); + putname(fd, "qrecv(", now->lft, m, ", "); + fprintf(fd, "0, %d, 0)) ", i); + Bailout(fd, ""); + } } + } else /* random receive: val 1 or 3 */ + { fprintf(fd, ";\n\t\tif (!(XX = Q_has("); + putname(fd, "", now->lft, m, ""); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp == CONST) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft, m); + } else if (v->lft->ntyp == EVAL) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft->lft, m); + } else + { fprintf(fd, ", 0, 0"); + } } + for ( ; i < Mpars; i++) + fprintf(fd, ", 0, 0"); + fprintf(fd, "))) "); + Bailout(fd, ""); + fprintf(fd, ";\n\t\t"); + if (multi_oval) + { check_needed(); + fprintf(fd, "(trpt+1)->bup.ovals[%d] = ", + multi_oval-1); + multi_oval++; + } else + fprintf(fd, "(trpt+1)->bup.oval = "); + fprintf(fd, "XX"); + } + + if (has_enabled) + fprintf(fd, ";\n\t\tif (TstOnly) return 1"); + + if (j == 0 && now->val >= 2) + { fprintf(fd, ";\n\t\t"); + break; /* poll without side-effect */ + } + + if (!GenCode) + { int jj = 0; + fprintf(fd, ";\n\t\t"); + /* no variables modified */ + if (j == 0 && now->val == 0) + { fprintf(fd, "if (q_flds[((Q0 *)qptr("); + putname(fd, "", now->lft, m, "-1))->_t]"); + fprintf(fd, " != %d)\n\t", i); + fprintf(fd, "\t\tUerror(\"wrong nr of msg fields in rcv\");\n\t\t"); + } + + for (v = now->rgt; v; v = v->rgt) + if ((v->lft->ntyp != CONST + && v->lft->ntyp != EVAL)) + jj++; /* nr of vars needing bup */ + + if (jj) + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { char tempbuf[64]; + + if ((v->lft->ntyp == CONST + || v->lft->ntyp == EVAL)) + continue; + + if (multi_oval) + { check_needed(); + sprintf(tempbuf, "(trpt+1)->bup.ovals[%d] = ", + multi_oval-1); + multi_oval++; + } else + sprintf(tempbuf, "(trpt+1)->bup.oval = "); + + if (v->lft->sym && !strcmp(v->lft->sym->name, "_")) + { fprintf(fd, tempbuf); + putname(fd, "qrecv(", now->lft, m, ""); + fprintf(fd, ", XX-1, %d, 0);\n\t\t", i); + } else + { _isok++; + cat3(tempbuf, v->lft, ";\n\t\t"); + _isok--; + } + } + + if (jj) /* check for double entries q?x,x */ + { Lextok *w; + + for (v = now->rgt; v; v = v->rgt) + { if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL + && v->lft->sym + && v->lft->sym->type != STRUCT /* not a struct */ + && v->lft->sym->nel == 1 /* not an array */ + && strcmp(v->lft->sym->name, "_") != 0) + for (w = v->rgt; w; w = w->rgt) + if (v->lft->sym == w->lft->sym) + { fatal("cannot use var ('%s') in multiple msg fields", + v->lft->sym->name); + } } } + } +/* set */ for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { if ((v->lft->ntyp == CONST + || v->lft->ntyp == EVAL) && v->rgt) + continue; + fprintf(fd, ";\n\t\t"); + + if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL + && strcmp(v->lft->sym->name, "_") != 0) + { nocast=1; + _isok++; + putstmnt(fd, v->lft, m); + _isok--; + nocast=0; + fprintf(fd, " = "); + } + putname(fd, "qrecv(", now->lft, m, ", "); + fprintf(fd, "XX-1, %d, ", i); + fprintf(fd, "%d)", (v->rgt || now->val >= 2)?0:1); + + if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL + && strcmp(v->lft->sym->name, "_") != 0 + && (v->lft->ntyp != NAME + || v->lft->sym->type != CHAN)) + { fprintf(fd, ";\n#ifdef VAR_RANGES"); + fprintf(fd, "\n\t\tlogval(\""); + withprocname = terse = nocast = 1; + _isok++; + putstmnt(fd,v->lft,m); + withprocname = terse = nocast = 0; + fprintf(fd, "\", "); + putstmnt(fd,v->lft,m); + _isok--; + fprintf(fd, ");\n#endif\n"); + fprintf(fd, "\t\t"); + } + } + fprintf(fd, ";\n\t\t"); + + fprintf(fd, "\n#ifdef HAS_CODE\n"); + fprintf(fd, "\t\tif (readtrail && gui) {\n"); + fprintf(fd, "\t\t\tchar simtmp[32];\n"); + putname(fd, "\t\t\tsprintf(simvals, \"%%d?\", ", now->lft, m, ");\n"); + _isok++; + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { if (v->lft->ntyp != EVAL) + { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft, "); strcat(simvals, simtmp);"); + } else + { cat3("\t\tsprintf(simtmp, \"%%d\", ", v->lft->lft, "); strcat(simvals, simtmp);"); + } + if (v->rgt) + fprintf(fd, "\t\tstrcat(simvals, \",\");\n"); + } + _isok--; + fprintf(fd, "\t\t}\n"); + fprintf(fd, "#endif\n\t\t"); + + if (u_sync) + { putname(fd, "if (q_zero(", now->lft, m, "))"); + fprintf(fd, "\n\t\t{ boq = -1;\n"); + + fprintf(fd, "#ifndef NOFAIR\n"); /* NEW 3.0.8 */ + fprintf(fd, "\t\t\tif (fairness\n"); + fprintf(fd, "\t\t\t&& !(trpt->o_pm&32)\n"); + fprintf(fd, "\t\t\t&& (now._a_t&2)\n"); + fprintf(fd, "\t\t\t&& now._cnt[now._a_t&1] == II+2)\n"); + fprintf(fd, "\t\t\t{ now._cnt[now._a_t&1] -= 1;\n"); + fprintf(fd, "#ifdef VERI\n"); + fprintf(fd, "\t\t\t if (II == 1)\n"); + fprintf(fd, "\t\t\t now._cnt[now._a_t&1] = 1;\n"); + fprintf(fd, "#endif\n"); + fprintf(fd, "#ifdef DEBUG\n"); + fprintf(fd, "\t\t\tprintf(\"%%3d: proc %%d fairness \", depth, II);\n"); + fprintf(fd, "\t\t\tprintf(\"Rule 2: --cnt to %%d (%%d)\\n\",\n"); + fprintf(fd, "\t\t\t now._cnt[now._a_t&1], now._a_t);\n"); + fprintf(fd, "#endif\n"); + fprintf(fd, "\t\t\t trpt->o_pm |= (32|64);\n"); + fprintf(fd, "\t\t\t}\n"); + fprintf(fd, "#endif\n"); + + fprintf(fd, "\n\t\t}"); + } + break; + + case 'R': + if (!terse && !TestOnly && has_xu) + { fprintf(fd, "\n#ifndef XUSAFE\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&1) || "); + fprintf(fd, "q_R_check("); + putname(fd, "", now->lft, m, ", II)) &&\n\t\t"); + putname(fd, "(!(q_claim[", now->lft, m, "]&2) || "); + putname(fd, "q_S_check(", now->lft, m, ", II)) &&"); + fprintf(fd, "\n#endif\n\t\t"); + } + if (u_sync>0) + putname(fd, "not_RV(", now->lft, m, ") && \\\n\t\t"); + + for (v = now->rgt, i=j=0; v; v = v->rgt, i++) + if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + { j++; continue; + } + if (now->val == 0 || i == j) + { putname(fd, "(q_len(", now->lft, m, ") > 0"); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + continue; + fprintf(fd, " \\\n\t\t&& qrecv("); + putname(fd, "", now->lft, m, ", "); + fprintf(fd, "0, %d, 0) == ", i); + if (v->lft->ntyp == CONST) + putstmnt(fd, v->lft, m); + else /* EVAL */ + putstmnt(fd, v->lft->lft, m); + } + fprintf(fd, ")"); + } else + { putname(fd, "Q_has(", now->lft, m, ""); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v->lft->ntyp == CONST) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft, m); + } else if (v->lft->ntyp == EVAL) + { fprintf(fd, ", 1, "); + putstmnt(fd, v->lft->lft, m); + } else + fprintf(fd, ", 0, 0"); + } + for ( ; i < Mpars; i++) + fprintf(fd, ", 0, 0"); + fprintf(fd, ")"); + } + break; + + case 'c': + preruse(fd, now->lft); /* preconditions */ + cat3("if (!(", now->lft, "))\n\t\t\t"); + Bailout(fd, ""); + break; + + case ELSE: + if (!GenCode) + { if (separate == 2) + fprintf(fd, "if (o_pm&1)\n\t\t\t"); + else + fprintf(fd, "if (trpt->o_pm&1)\n\t\t\t"); + Bailout(fd, ""); + } else + { fprintf(fd, "/* else */"); + } + break; + + case '?': + if (now->lft) + { cat3("( (", now->lft, ") ? "); + } + if (now->rgt) + { cat3("(", now->rgt->lft, ") : "); + cat3("(", now->rgt->rgt, ") )"); + } + break; + + case ASGN: + if (has_enabled) + fprintf(fd, "if (TstOnly) return 1;\n\t\t"); + _isok++; + + if (!GenCode) + { if (multi_oval) + { char tempbuf[64]; + check_needed(); + sprintf(tempbuf, "(trpt+1)->bup.ovals[%d] = ", + multi_oval-1); + multi_oval++; + cat3(tempbuf, now->lft, ";\n\t\t"); + } else + { cat3("(trpt+1)->bup.oval = ", now->lft, ";\n\t\t"); + } } + nocast = 1; putstmnt(fd,now->lft,m); nocast = 0; + fprintf(fd," = "); + _isok--; + putstmnt(fd,now->rgt,m); + + if (now->sym->type != CHAN + || verbose > 0) + { fprintf(fd, ";\n#ifdef VAR_RANGES"); + fprintf(fd, "\n\t\tlogval(\""); + withprocname = terse = nocast = 1; + _isok++; + putstmnt(fd,now->lft,m); + withprocname = terse = nocast = 0; + fprintf(fd, "\", "); + putstmnt(fd,now->lft,m); + _isok--; + fprintf(fd, ");\n#endif\n"); + fprintf(fd, "\t\t"); + } + break; + + case PRINT: + if (has_enabled) + fprintf(fd, "if (TstOnly) return 1;\n\t\t"); +#ifdef PRINTF + fprintf(fd, "printf(%s", now->sym->name); +#else + fprintf(fd, "Printf(%s", now->sym->name); +#endif + for (v = now->lft; v; v = v->rgt) + { cat2(", ", v->lft); + } + fprintf(fd, ")"); + break; + + case PRINTM: + if (has_enabled) + fprintf(fd, "if (TstOnly) return 1;\n\t\t"); + fprintf(fd, "printm("); + if (now->lft && now->lft->ismtyp) + fprintf(fd, "%d", now->lft->val); + else + putstmnt(fd, now->lft, m); + fprintf(fd, ")"); + break; + + case NAME: + if (!nocast && now->sym && Sym_typ(now) < SHORT) + putname(fd, "((int)", now, m, ")"); + else + putname(fd, "", now, m, ""); + break; + + case 'p': + putremote(fd, now, m); + break; + + case 'q': + if (terse) + fprintf(fd, "%s", now->sym->name); + else + fprintf(fd, "%d", remotelab(now)); + break; + + case C_EXPR: + fprintf(fd, "("); + plunk_expr(fd, now->sym->name); +#if 1 + fprintf(fd, ")"); +#else + fprintf(fd, ") /* %s */ ", now->sym->name); +#endif + break; + + case C_CODE: + fprintf(fd, "/* %s */\n\t\t", now->sym->name); + if (has_enabled) + fprintf(fd, "if (TstOnly) return 1;\n\t\t"); + if (!GenCode) /* not in d_step */ + { fprintf(fd, "sv_save();\n\t\t"); + /* store the old values for reverse moves */ + } + plunk_inline(fd, now->sym->name, 1); + if (!GenCode) + { fprintf(fd, "\n"); /* state changed, capture it */ + fprintf(fd, "#if defined(C_States) && (HAS_TRACK==1)\n"); + fprintf(fd, "\t\tc_update((uchar *) &(now.c_state[0]));\n"); + fprintf(fd, "#endif\n"); + } + break; + + case ASSERT: + if (has_enabled) + fprintf(fd, "if (TstOnly) return 1;\n\t\t"); + + cat3("assert(", now->lft, ", "); + terse = nocast = 1; + cat3("\"", now->lft, "\", II, tt, t)"); + terse = nocast = 0; + break; + + case '.': + case BREAK: + case GOTO: + if (Pid == eventmapnr) + fprintf(fd, "Uerror(\"cannot get here\")"); + putskip(m); + break; + + case '@': + if (Pid == eventmapnr) + { fprintf(fd, "return 0"); + break; + } + + if (has_enabled) + { fprintf(fd, "if (TstOnly)\n\t\t\t"); + fprintf(fd, "return (II+1 == now._nr_pr);\n\t\t"); + } + fprintf(fd, "if (!delproc(1, II)) "); + Bailout(fd, ""); + break; + + default: + printf("spin: bad node type %d (.m) - line %d\n", + now->ntyp, now->ln); + fflush(tm); + alldone(1); + } +} + +void +putname(FILE *fd, char *pre, Lextok *n, int m, char *suff) /* varref */ +{ Symbol *s = n->sym; + lineno = n->ln; Fname = n->fn; + + if (!s) + fatal("no name - putname", (char *) 0); + + if (s->context && context && s->type) + s = findloc(s); /* it's a local var */ + + if (!s) + { fprintf(fd, "%s%s%s", pre, n->sym->name, suff); + return; + } + if (!s->type) /* not a local name */ + s = lookup(s->name); /* must be a global */ + + if (!s->type) + { if (strcmp(pre, ".") != 0) + non_fatal("undeclared variable '%s'", s->name); + s->type = INT; + } + + if (s->type == PROCTYPE) + fatal("proctype-name '%s' used as array-name", s->name); + + fprintf(fd, pre); + if (!terse && !s->owner && evalindex != 1) + { if (s->context + || strcmp(s->name, "_p") == 0 + || strcmp(s->name, "_pid") == 0) + { fprintf(fd, "((P%d *)this)->", Pid); + } else + { int x = strcmp(s->name, "_"); + if (!(s->hidden&1) && x != 0) + fprintf(fd, "now."); + if (x == 0 && _isok == 0) + fatal("attempt to read value of '_'", 0); + } } + + if (withprocname + && s->context + && strcmp(pre, ".")) + fprintf(fd, "%s:", s->context->name); + + if (evalindex != 1) + fprintf(fd, "%s", s->name); + + if (s->nel != 1) + { if (no_arrays) + { + non_fatal("ref to array element invalid in this context", + (char *)0); + printf("\thint: instead of, e.g., x[rs] qu[3], use\n"); + printf("\tchan nm_3 = qu[3]; x[rs] nm_3;\n"); + printf("\tand use nm_3 in sends/recvs instead of qu[3]\n"); + } + /* an xr or xs reference to an array element + * becomes an exclusion tag on the array itself - + * which could result in invalidly labeling + * operations on other elements of this array to + * be also safe under the partial order reduction + * (see procedure has_global()) + */ + + if (evalindex == 2) + { fprintf(fd, "[%%d]"); + } else if (evalindex == 1) + { evalindex = 0; /* no good if index is indexed array */ + fprintf(fd, ", "); + putstmnt(fd, n->lft, m); + evalindex = 1; + } else + { if (terse + || (n->lft + && n->lft->ntyp == CONST + && n->lft->val < s->nel) + || (!n->lft && s->nel > 0)) + { cat3("[", n->lft, "]"); + } else + { cat3("[ Index(", n->lft, ", "); + fprintf(fd, "%d) ]", s->nel); + } + } + } + if (s->type == STRUCT && n->rgt && n->rgt->lft) + { putname(fd, ".", n->rgt->lft, m, ""); + } + fprintf(fd, suff); +} + +void +putremote(FILE *fd, Lextok *n, int m) /* remote reference */ +{ int promoted = 0; + int pt; + + if (terse) + { fprintf(fd, "%s", n->lft->sym->name); /* proctype name */ + if (n->lft->lft) + { fprintf(fd, "["); + putstmnt(fd, n->lft->lft, m); /* pid */ + fprintf(fd, "]"); + } + fprintf(fd, ".%s", n->sym->name); + } else + { if (Sym_typ(n) < SHORT) + { promoted = 1; + fprintf(fd, "((int)"); + } + + pt = fproc(n->lft->sym->name); + fprintf(fd, "((P%d *)Pptr(", pt); + if (n->lft->lft) + { fprintf(fd, "BASE+"); + putstmnt(fd, n->lft->lft, m); + } else + fprintf(fd, "f_pid(%d)", pt); + fprintf(fd, "))->%s", n->sym->name); + } + if (n->rgt) + { fprintf(fd, "["); + putstmnt(fd, n->rgt, m); /* array var ref */ + fprintf(fd, "]"); + } + if (promoted) fprintf(fd, ")"); +} + +static int +getweight(Lextok *n) +{ /* this piece of code is a remnant of early versions + * of the verifier -- in the current version of Spin + * only non-zero values matter - so this could probably + * simply return 1 in all cases. + */ + switch (n->ntyp) { + case 'r': return 4; + case 's': return 2; + case TIMEOUT: return 1; + case 'c': if (has_typ(n->lft, TIMEOUT)) return 1; + } + return 3; +} + +int +has_typ(Lextok *n, int m) +{ + if (!n) return 0; + if (n->ntyp == m) return 1; + return (has_typ(n->lft, m) || has_typ(n->rgt, m)); +} + +static int runcount, opcount; + +static void +do_count(Lextok *n, int checkop) +{ + if (!n) return; + + switch (n->ntyp) { + case RUN: + runcount++; + break; + default: + if (checkop) opcount++; + break; + } + do_count(n->lft, checkop && (n->ntyp != RUN)); + do_count(n->rgt, checkop); +} + +void +count_runs(Lextok *n) +{ + runcount = opcount = 0; + do_count(n, 1); + if (runcount > 1) + fatal("more than one run operator in expression", ""); + if (runcount == 1 && opcount > 1) + fatal("use of run operator in compound expression", ""); +} + +void +any_runs(Lextok *n) +{ + runcount = opcount = 0; + do_count(n, 0); + if (runcount >= 1) + fatal("run operator used in invalid context", ""); +} diff --git a/sys/src/cmd/spin/pangen2.h b/sys/src/cmd/spin/pangen2.h new file mode 100755 index 000000000..2df6a0b42 --- /dev/null +++ b/sys/src/cmd/spin/pangen2.h @@ -0,0 +1,858 @@ +/***** spin: pangen2.h *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +static char *Nvr1[] = { /* allow separate compilation */ + "#ifdef VERI", + "void", + "check_claim(int st)", + "{", + " if (st == endclaim)", + " uerror(\"claim violated!\");", + " if (stopstate[VERI][st])", + " uerror(\"end state in claim reached\");", + "}", + "#endif", + 0, +}; + +static char *Pre0[] = { +"#ifdef SC", + "#define _FILE_OFFSET_BITS 64", /* to allow file sizes greater than 2Gb */ +"#endif", + "#include <stdio.h>", + "#include <signal.h>", + "#include <stdlib.h>", + "#include <stdarg.h>", + "#include <string.h>", + "#include <ctype.h>", + "#include <errno.h>", + "#if defined(WIN32) || defined(WIN64)", + "#include <time.h>", + "#else", + "#include <unistd.h>", + "#include <sys/times.h>", /* new 4.3.0 */ + "#endif", + "#include <sys/types.h>", /* defines off_t */ + "#include <sys/stat.h>", + "#include <fcntl.h>", + "#define Offsetof(X, Y) ((unsigned long)(&(((X *)0)->Y)))", + "#ifndef max", + "#define max(a,b) (((a)<(b)) ? (b) : (a))", + "#endif", + "#ifndef PRINTF", + "int Printf(const char *fmt, ...); /* prototype only */", + "#endif", + 0, +}; + +static char *Preamble[] = { + + "#ifdef CNTRSTACK", + "#define onstack_now() (LL[trpt->j6] && LL[trpt->j7])", + "#define onstack_put() LL[trpt->j6]++; LL[trpt->j7]++", + "#define onstack_zap() LL[trpt->j6]--; LL[trpt->j7]--", + "#endif", + + "#if !defined(SAFETY) && !defined(NOCOMP)", + /* + * V_A identifies states in the current statespace + * A_V identifies states in the 'other' statespace + * S_A remembers how many leading bytes in the sv + * are used for these markers + fairness bits + */ + "#define V_A (((now._a_t&1)?2:1) << (now._a_t&2))", + "#define A_V (((now._a_t&1)?1:2) << (now._a_t&2))", + "int S_A = 0;", + "#else", + "#define V_A 0", + "#define A_V 0", + "#define S_A 0", + "#endif", + +"#ifdef MA", + "#undef onstack_now", + "#undef onstack_put", + "#undef onstack_zap", + "#define onstack_put() ;", + "#define onstack_zap() gstore((char *) &now, vsize, 4)", +"#else", + "#if defined(FULLSTACK) && !defined(BITSTATE)", + "#define onstack_put() trpt->ostate = Lstate", + "#define onstack_zap() { \\", + " if (trpt->ostate) \\", + " trpt->ostate->tagged = \\", + " (S_A)? (trpt->ostate->tagged&~V_A) : 0; \\", + " }", + "#endif", +"#endif", + "struct H_el {", + " struct H_el *nxt;", + "#ifdef FULLSTACK", + " unsigned int tagged;", + "#if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY)", + " unsigned int proviso;", /* uses just 1 bit 0/1 */ + "#endif", + "#endif", + "#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK))", + " unsigned long st_id;", + "#endif", + "#ifdef COLLAPSE", + "#if VECTORSZ<65536", + " unsigned short ln;", /* length of vector */ + "#else", + " unsigned long ln;", /* length of vector */ + "#endif", + "#endif", + "#if !defined(SAFETY) || defined(REACH)", + " unsigned int D;", + "#endif", + " unsigned state;", + "} **H_tab, **S_Tab;\n", + + "typedef struct Trail {", + " int st; /* current state */", + " uchar pr; /* process id */", + " uchar tau; /* 8 bit-flags */", + " uchar o_pm; /* 8 more bit-flags */", + "#if 0", + " Meaning of bit-flags:", + " tau&1 -> timeout enabled", + " tau&2 -> request to enable timeout 1 level up (in claim)", + " tau&4 -> current transition is a claim move", + " tau&8 -> current transition is an atomic move", + " tau&16 -> last move was truncated on stack", + " tau&32 -> current transition is a preselected move", + " tau&64 -> at least one next state is not on the stack", + " tau&128 -> current transition is a stutter move", + + " o_pm&1 -> the current pid moved -- implements else", + " o_pm&2 -> this is an acceptance state", + " o_pm&4 -> this is a progress state", + " o_pm&8 -> fairness alg rule 1 undo mark", + " o_pm&16 -> fairness alg rule 3 undo mark", + " o_pm&32 -> fairness alg rule 2 undo mark", + " o_pm&64 -> the current proc applied rule2", + " o_pm&128 -> a fairness, dummy move - all procs blocked", + "#endif", + "#if defined(FULLSTACK) && defined(MA) && !defined(BFS)", + " uchar proviso;", + "#endif", + "#ifndef BFS", + " uchar o_n, o_ot; /* to save locals */", + "#endif", + " uchar o_m;", + "#ifdef EVENT_TRACE", + "#if nstates_event<256", + " uchar o_event;", + "#else", + " unsigned short o_event;", + "#endif", + "#endif", + " int o_tt;", + "#ifndef BFS", + " short o_To;", + "#ifdef RANDOMIZE", + " short oo_i;", + "#endif", + "#endif", + "#if defined(HAS_UNLESS) && !defined(BFS)", + " int e_state; /* if escape trans - state of origin */", + "#endif", + "#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS)", + " struct H_el *ostate; /* pointer to stored state */", + "#endif", + /* CNTRSTACK when !NOREDUCE && BITSTATE && SAFETY, uses LL[] */ + "#if defined(CNTRSTACK) && !defined(BFS)", + " long j6, j7;", + "#endif", + " Trans *o_t;", /* transition fct, next state */ + "#ifdef HAS_SORTED", + " short ipt;", /* insertion slot in q */ + "#endif", + " union {", + " int oval;", /* single backup value of variable */ + " int *ovals;", /* ptr to multiple values */ + " } bup;", + "} Trail;", + "Trail *trail, *trpt;", + + "FILE *efd;", + "uchar *this;", + "long maxdepth=10000;", + "long omaxdepth=10000;", + "#ifdef SC", /* stack cycling */ + "char *stackfile;", + "#endif", + "uchar *SS, *LL;", + "uchar HASH_NR = 0;", + "", + "double memcnt = (double) 0;", + "double memlim = (double) (1<<30);", + "", + "/* for emalloc: */", + "static char *have;", + "static long left = 0L;", + "static double fragment = (double) 0;", + "static unsigned long grow;", + "", + "unsigned int HASH_CONST[] = {", + " /* asuming 4 bytes per int */", + " 0x88888EEF, 0x00400007,", + " 0x04c11db7, 0x100d4e63,", + " 0x0fc22f87, 0x3ff0c3ff,", + " 0x38e84cd7, 0x02b148e9,", + " 0x98b2e49d, 0xb616d379,", + " 0xa5247fd9, 0xbae92a15,", + " 0xb91c8bc5, 0x8e5880f3,", + " 0xacd7c069, 0xb4c44bb3,", + " 0x2ead1fb7, 0x8e428171,", + " 0xdbebd459, 0x828ae611,", + " 0x6cb25933, 0x86cdd651,", + " 0x9e8f5f21, 0xd5f8d8e7,", + " 0x9c4e956f, 0xb5cf2c71,", + " 0x2e805a6d, 0x33fc3a55,", + " 0xaf203ed1, 0xe31f5909,", + " 0x5276db35, 0x0c565ef7,", + " 0x273d1aa5, 0x8923b1dd,", + " 0", + "};", + "int mreached=0, done=0, errors=0, Nrun=1;", + "double nstates=0, nlinks=0, truncs=0, truncs2=0;", + "double nlost=0, nShadow=0, hcmp=0, ngrabs=0;", + "#ifdef BFS", + "double midrv=0, failedrv=0, revrv=0;", + "#endif", + "unsigned long nr_states=0; /* nodes in DFA */", + "long Fa=0, Fh=0, Zh=0, Zn=0;", + "long PUT=0, PROBE=0, ZAPS=0;", + "long Ccheck=0, Cholds=0;", + "int a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0;", + "#ifdef HAS_CODE", + "int gui = 0, coltrace = 0, readtrail = 0, whichtrail = 0, onlyproc = -1, silent = 0;", + "#endif", + "int state_tables=0, fairness=0, no_rck=0, Nr_Trails=0;", + "char simvals[128];", + "#ifndef INLINE", + "int TstOnly=0;", + "#endif", + "unsigned long mask, nmask;", + "#ifdef BITSTATE", + "int ssize=23; /* 1 Mb */", + "#else", + "int ssize=19; /* 512K slots */", + "#endif", + "int hmax=0, svmax=0, smax=0;", + "int Maxbody=0, XX;", + "uchar *noptr; /* used by macro Pptr(x) */", + "#ifdef VAR_RANGES", + "void logval(char *, int);", + "void dumpranges(void);", + "#endif", + + "#ifdef MA", + "#define INLINE_REV", + "extern void dfa_init(unsigned short);", + "extern int dfa_member(unsigned long);", + "extern int dfa_store(uchar *);", + "unsigned int maxgs = 0;", + "#endif", + + "State comp_now; /* compressed state vector */", + "State comp_msk;", + "uchar *Mask = (uchar *) &comp_msk;", + "#ifdef COLLAPSE", + "State comp_tmp;", + "static char *scratch = (char *) &comp_tmp;", + "#endif", + + "Stack *stack; /* for queues, processes */", + "Svtack *svtack; /* for old state vectors */", + "#ifdef BITSTATE", + "static unsigned hfns = 3; /* new default */", + "#endif", + "static unsigned long j1;", + "static unsigned long K1, K2;", + "static unsigned long j2, j3, j4;", + "#ifdef BITSTATE", +#ifndef POWOW + "static long udmem;", +#endif + "#endif", + "static long A_depth = 0;", + "long depth = 0;", /* not static to support -S2 option, but possible clash with embedded code */ + "static uchar warned = 0, iterative = 0, like_java = 0, every_error = 0;", + "static uchar noasserts = 0, noends = 0, bounded = 0;", + "#if SYNC>0 && ASYNC==0", + "void set_recvs(void);", + "int no_recvs(int);", + "#endif", + "#if SYNC", + "#define IfNotBlocked if (boq != -1) continue;", + "#define UnBlock boq = -1", + "#else", + "#define IfNotBlocked /* cannot block */", + "#define UnBlock /* don't bother */", + "#endif\n", + "#ifdef BITSTATE", + "int (*bstore)(char *, int);", + "int bstore_reg(char *, int);", +#ifndef POWOW + "int bstore_mod(char *, int);", +#endif + "#endif", + "void active_procs(void);", + "void cleanup(void);", + "void do_the_search(void);", + "void find_shorter(int);", + "void iniglobals(void);", + "void stopped(int);", + "void wrapup(void);", + "int *grab_ints(int);", + "void ungrab_ints(int *, int);", + 0, +}; + +static char *Tail[] = { + "Trans *", + "settr( int t_id, int a, int b, int c, int d,", + " char *t, int g, int tpe0, int tpe1)", + "{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));\n", + " tmp->atom = a&(6|32); /* only (2|8|32) have meaning */", + " if (!g) tmp->atom |= 8; /* no global references */", + " tmp->st = b;", + " tmp->tpe[0] = tpe0;", + " tmp->tpe[1] = tpe1;", + " tmp->tp = t;", + " tmp->t_id = t_id;", + " tmp->forw = c;", + " tmp->back = d;", + " return tmp;", + "}\n", + "Trans *", + "cpytr(Trans *a)", + "{ Trans *tmp = (Trans *) emalloc(sizeof(Trans));\n", + " int i;", + " tmp->atom = a->atom;", + " tmp->st = a->st;", + "#ifdef HAS_UNLESS", + " tmp->e_trans = a->e_trans;", + " for (i = 0; i < HAS_UNLESS; i++)", + " tmp->escp[i] = a->escp[i];", + "#endif", + " tmp->tpe[0] = a->tpe[0];", + " tmp->tpe[1] = a->tpe[1];", + " for (i = 0; i < 6; i++)", + " { tmp->qu[i] = a->qu[i];", + " tmp->ty[i] = a->ty[i];", + " }", + " tmp->tp = (char *) emalloc(strlen(a->tp)+1);", + " strcpy(tmp->tp, a->tp);", + " tmp->t_id = a->t_id;", + " tmp->forw = a->forw;", + " tmp->back = a->back;", + " return tmp;", + "}\n", + "#ifndef NOREDUCE", + "int", + "srinc_set(int n)", + "{ if (n <= 2) return LOCAL;", + " if (n <= 2+ DELTA) return Q_FULL_F; /* 's' or nfull */", + " if (n <= 2+2*DELTA) return Q_EMPT_F; /* 'r' or nempty */", + " if (n <= 2+3*DELTA) return Q_EMPT_T; /* empty */", + " if (n <= 2+4*DELTA) return Q_FULL_T; /* full */", + " if (n == 5*DELTA) return GLOBAL;", + " if (n == 6*DELTA) return TIMEOUT_F;", + " if (n == 7*DELTA) return ALPHA_F;", + " Uerror(\"cannot happen srinc_class\");", + " return BAD;", + "}", + "int", + "srunc(int n, int m)", + "{ switch(m) {", + " case Q_FULL_F: return n-2;", + " case Q_EMPT_F: return n-2-DELTA;", + " case Q_EMPT_T: return n-2-2*DELTA;", + " case Q_FULL_T: return n-2-3*DELTA;", + " case ALPHA_F:", + " case TIMEOUT_F: return 257; /* non-zero, and > MAXQ */", + " }", + " Uerror(\"cannot happen srunc\");", + " return 0;", + "}", + "#endif", + "int cnt;", + "#ifdef HAS_UNLESS", + "int", + "isthere(Trans *a, int b)", /* is b already in a's list? */ + "{ Trans *t;", + " for (t = a; t; t = t->nxt)", + " if (t->t_id == b)", + " return 1;", + " return 0;", + "}", + "#endif", + "#ifndef NOREDUCE", + "int", + "mark_safety(Trans *t) /* for conditional safety */", + "{ int g = 0, i, j, k;", + "", + " if (!t) return 0;", + " if (t->qu[0])", + " return (t->qu[1])?2:1; /* marked */", + "", + " for (i = 0; i < 2; i++)", + " { j = srinc_set(t->tpe[i]);", + " if (j >= GLOBAL && j != ALPHA_F)", + " return -1;", + " if (j != LOCAL)", + " { k = srunc(t->tpe[i], j);", + " if (g == 0", + " || t->qu[0] != k", + " || t->ty[0] != j)", + " { t->qu[g] = k;", + " t->ty[g] = j;", + " g++;", + " } } }", + " return g;", + "}", + "#endif", + "void", + "retrans(int n, int m, int is, short srcln[], uchar reach[])", + " /* process n, with m states, is=initial state */", + "{ Trans *T0, *T1, *T2, *T3;", + " int i, k;", + "#ifndef NOREDUCE", + " int g, h, j, aa;", + "#endif", + "#ifdef HAS_UNLESS", + " int p;", + "#endif", + " if (state_tables >= 4)", + " { printf(\"STEP 1 proctype %%s\\n\", ", + " procname[n]);", + " for (i = 1; i < m; i++)", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " crack(n, i, T0, srcln);", + " return;", + " }", + " do {", + " for (i = 1, cnt = 0; i < m; i++)", + " { T2 = trans[n][i];", + " T1 = T2?T2->nxt:(Trans *)0;", + "/* prescan: */ for (T0 = T1; T0; T0 = T0->nxt)", + "/* choice in choice */ { if (T0->st && trans[n][T0->st]", + " && trans[n][T0->st]->nxt)", + " break;", + " }", + "#if 0", + " if (T0)", + " printf(\"\\tstate %%d / %%d: choice in choice\\n\",", + " i, T0->st);", + "#endif", + " if (T0)", + " for (T0 = T1; T0; T0 = T0->nxt)", + " { T3 = trans[n][T0->st];", + " if (!T3->nxt)", + " { T2->nxt = cpytr(T0);", + " T2 = T2->nxt;", + " imed(T2, T0->st, n, i);", + " continue;", + " }", + " do { T3 = T3->nxt;", + " T2->nxt = cpytr(T3);", + " T2 = T2->nxt;", + " imed(T2, T0->st, n, i);", + " } while (T3->nxt);", + " cnt++;", + " }", + " }", + " } while (cnt);", + + " if (state_tables >= 3)", + " { printf(\"STEP 2 proctype %%s\\n\", ", + " procname[n]);", + " for (i = 1; i < m; i++)", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " crack(n, i, T0, srcln);", + " return;", + " }", + " for (i = 1; i < m; i++)", + " { if (trans[n][i] && trans[n][i]->nxt) /* optimize */", + " { T1 = trans[n][i]->nxt;", + "#if 0", + " printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",", + " T1->st, T1->forw, i);", + "#endif", + " if (!trans[n][T1->st]) continue;", + " T0 = cpytr(trans[n][T1->st]);", + " trans[n][i] = T0;", + " reach[T1->st] = 1;", + " imed(T0, T1->st, n, i);", + " for (T1 = T1->nxt; T1; T1 = T1->nxt)", + " {", + "#if 0", + " printf(\"\\t\\tpull %%d (%%d) to %%d\\n\",", + " T1->st, T1->forw, i);", + "#endif", + " if (!trans[n][T1->st]) continue;", + " T0->nxt = cpytr(trans[n][T1->st]);", + " T0 = T0->nxt;", + " reach[T1->st] = 1;", + " imed(T0, T1->st, n, i);", + " } } }", + " if (state_tables >= 2)", + " { printf(\"STEP 3 proctype %%s\\n\", ", + " procname[n]);", + " for (i = 1; i < m; i++)", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " crack(n, i, T0, srcln);", + " return;", + " }", + "#ifdef HAS_UNLESS", + " for (i = 1; i < m; i++)", + " { if (!trans[n][i]) continue;", + " /* check for each state i if an", + " * escape to some state p is defined", + " * if so, copy and mark p's transitions", + " * and prepend them to the transition-", + " * list of state i", + " */", + " if (!like_java) /* the default */", + " { for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " for (k = HAS_UNLESS-1; k >= 0; k--)", + " { if (p = T0->escp[k])", + " for (T1 = trans[n][p]; T1; T1 = T1->nxt)", + " { if (isthere(trans[n][i], T1->t_id))", + " continue;", + " T2 = cpytr(T1);", + " T2->e_trans = p;", + " T2->nxt = trans[n][i];", + " trans[n][i] = T2;", + " } }", + " } else /* outermost unless checked first */", + " { Trans *T4;", + " T4 = T3 = (Trans *) 0;", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " for (k = HAS_UNLESS-1; k >= 0; k--)", + " { if (p = T0->escp[k])", + " for (T1 = trans[n][p]; T1; T1 = T1->nxt)", + " { if (isthere(trans[n][i], T1->t_id))", + " continue;", + " T2 = cpytr(T1);", + " T2->nxt = (Trans *) 0;", + " T2->e_trans = p;", + " if (T3) T3->nxt = T2;", + " else T4 = T2;", + " T3 = T2;", + " } }", + " if (T4)", + " { T3->nxt = trans[n][i];", + " trans[n][i] = T4;", + " }", + " }", + " }", + "#endif", + + "#ifndef NOREDUCE", + " for (i = 1; i < m; i++)", + " {", + " if (a_cycles)", + " { /* moves through these states are visible */", + "#if PROG_LAB>0 && defined(HAS_NP)", + " if (progstate[n][i])", + " goto degrade;", + " for (T1 = trans[n][i]; T1; T1 = T1->nxt)", + " if (progstate[n][T1->st])", + " goto degrade;", + "#endif", + " if (accpstate[n][i] || visstate[n][i])", + " goto degrade;", + " for (T1 = trans[n][i]; T1; T1 = T1->nxt)", + " if (accpstate[n][T1->st])", + " goto degrade;", + " }", + " T1 = trans[n][i];", + " if (!T1) continue;", + " g = mark_safety(T1); /* V3.3.1 */", + " if (g < 0) goto degrade; /* global */", + " /* check if mixing of guards preserves reduction */", + " if (T1->nxt)", + " { k = 0;", + " for (T0 = T1; T0; T0 = T0->nxt)", + " { if (!(T0->atom&8))", + " goto degrade;", + " for (aa = 0; aa < 2; aa++)", + " { j = srinc_set(T0->tpe[aa]);", + " if (j >= GLOBAL && j != ALPHA_F)", + " goto degrade;", + " if (T0->tpe[aa]", + " && T0->tpe[aa]", + " != T1->tpe[0])", + " k = 1;", + " } }", + " /* g = 0; V3.3.1 */", + " if (k) /* non-uniform selection */", + " for (T0 = T1; T0; T0 = T0->nxt)", + " for (aa = 0; aa < 2; aa++)", + " { j = srinc_set(T0->tpe[aa]);", + " if (j != LOCAL)", + " { k = srunc(T0->tpe[aa], j);", + " for (h = 0; h < 6; h++)", + " if (T1->qu[h] == k", + " && T1->ty[h] == j)", + " break;", + " if (h >= 6)", + " { T1->qu[g%%6] = k;", + " T1->ty[g%%6] = j;", + " g++;", + " } } }", + " if (g > 6)", + " { T1->qu[0] = 0; /* turn it off */", + " printf(\"pan: warning, line %%d, \",", + " srcln[i]);", + " printf(\"too many stmnt types (%%d)\",", + " g);", + " printf(\" in selection\\n\");", + " goto degrade;", + " }", + " }", + " /* mark all options global if >=1 is global */", + " for (T1 = trans[n][i]; T1; T1 = T1->nxt)", + " if (!(T1->atom&8)) break;", + " if (T1)", + "degrade: for (T1 = trans[n][i]; T1; T1 = T1->nxt)", + " T1->atom &= ~8; /* mark as unsafe */", + + " /* can only mix 'r's or 's's if on same chan */", + " /* and not mixed with other local operations */", + " T1 = trans[n][i];", + + " if (!T1 || T1->qu[0]) continue;", + + " j = T1->tpe[0];", + " if (T1->nxt && T1->atom&8)", + " { if (j == 5*DELTA)", + " { printf(\"warning: line %%d \", srcln[i]);", + " printf(\"mixed condition \");", + " printf(\"(defeats reduction)\\n\");", + " goto degrade;", + " }", + " for (T0 = T1; T0; T0 = T0->nxt)", + " for (aa = 0; aa < 2; aa++)", + " if (T0->tpe[aa] && T0->tpe[aa] != j)", + " { printf(\"warning: line %%d \", srcln[i]);", + " printf(\"[%%d-%%d] mixed %%stion \",", + " T0->tpe[aa], j, ", + " (j==5*DELTA)?\"condi\":\"selec\");", + " printf(\"(defeats reduction)\\n\");", + " printf(\" '%%s' <-> '%%s'\\n\",", + " T1->tp, T0->tp);", + " goto degrade;", + " } }", + " }", + "#endif", + " for (i = 1; i < m; i++)", /* R */ + " { T2 = trans[n][i];", + " if (!T2", + " || T2->nxt", + " || strncmp(T2->tp, \".(goto)\", 7)", + " || !stopstate[n][i])", + " continue;", + " stopstate[n][T2->st] = 1;", + " }", + " if (state_tables)", + " { printf(\"proctype \");", + " if (!strcmp(procname[n], \":init:\"))", + " printf(\"init\\n\");", + " else", + " printf(\"%%s\\n\", procname[n]);", + " for (i = 1; i < m; i++)", + " reach[i] = 1;", + " tagtable(n, m, is, srcln, reach);", + " } else", + " for (i = 1; i < m; i++)", + " { int nrelse;", + " if (strcmp(procname[n], \":never:\") != 0)", + " { for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " { if (T0->st == i", + " && strcmp(T0->tp, \"(1)\") == 0)", + " { printf(\"error: proctype '%%s' \",", + " procname[n]);", + " printf(\"line %%d, state %%d: has un\",", + " srcln[i], i);", + " printf(\"conditional self-loop\\n\");", + " pan_exit(1);", + " } } }", + " nrelse = 0;", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " { if (strcmp(T0->tp, \"else\") == 0)", + " nrelse++;", + " }", + " if (nrelse > 1)", + " { printf(\"error: proctype '%%s' state\",", + " procname[n]);", + " printf(\" %%d, inherits %%d\", i, nrelse);", + " printf(\" 'else' stmnts\\n\");", + " pan_exit(1);", + " } }", + " if (!state_tables && strcmp(procname[n], \":never:\") == 0)", + " { int h = 0;", + " for (i = 1; i < m; i++)", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " if (T0->forw > h) h = T0->forw;", + " h++;", + " frm_st0 = (short *) emalloc(h * sizeof(short));", + " for (i = 1; i < m; i++)", + " for (T0 = trans[n][i]; T0; T0 = T0->nxt)", + " frm_st0[T0->forw] = i;", + " }", + "}", + "void", + "imed(Trans *T, int v, int n, int j) /* set intermediate state */", + "{ progstate[n][T->st] |= progstate[n][v];", + " accpstate[n][T->st] |= accpstate[n][v];", + " stopstate[n][T->st] |= stopstate[n][v];", + " mapstate[n][j] = T->st;", + "}", + "void", + "tagtable(int n, int m, int is, short srcln[], uchar reach[])", + "{ Trans *z;\n", + " if (is >= m || !trans[n][is]", + " || is <= 0 || reach[is] == 0)", + " return;", + " reach[is] = 0;", + " if (state_tables)", + " for (z = trans[n][is]; z; z = z->nxt)", + " crack(n, is, z, srcln);", + " for (z = trans[n][is]; z; z = z->nxt)", + " {", + "#ifdef HAS_UNLESS", + " int i, j;", + "#endif", + " tagtable(n, m, z->st, srcln, reach);", + "#ifdef HAS_UNLESS", + " for (i = 0; i < HAS_UNLESS; i++)", + " { j = trans[n][is]->escp[i];", + " if (!j) break;", + " tagtable(n, m, j, srcln, reach);", + " }", + "#endif", + " }", + "}", + "void", + "crack(int n, int j, Trans *z, short srcln[])", + "{ int i;\n", + " if (!z) return;", + " printf(\"\tstate %%3d -(tr %%3d)-> state %%3d \",", + " j, z->forw, z->st);", + " printf(\"[id %%3d tp %%3d\", z->t_id, z->tpe[0]);", + " if (z->tpe[1]) printf(\",%%d\", z->tpe[1]);", + "#ifdef HAS_UNLESS", + " if (z->e_trans)", + " printf(\" org %%3d\", z->e_trans);", + " else if (state_tables >= 2)", + " for (i = 0; i < HAS_UNLESS; i++)", + " { if (!z->escp[i]) break;", + " printf(\" esc %%d\", z->escp[i]);", + " }", + "#endif", + " printf(\"]\");", + " printf(\" [%%s%%s%%s%%s%%s] line %%d => \",", + " z->atom&6?\"A\":z->atom&32?\"D\":\"-\",", + " accpstate[n][j]?\"a\" :\"-\",", + " stopstate[n][j]?\"e\" : \"-\",", + " progstate[n][j]?\"p\" : \"-\",", + " z->atom & 8 ?\"L\":\"G\",", + " srcln[j]);", + " for (i = 0; z->tp[i]; i++)", + " if (z->tp[i] == \'\\n\')", + " printf(\"\\\\n\");", + " else", + " putchar(z->tp[i]);", + " if (z->qu[0])", + " { printf(\"\\t[\");", + " for (i = 0; i < 6; i++)", + " if (z->qu[i])", + " printf(\"(%%d,%%d)\",", + " z->qu[i], z->ty[i]);", + " printf(\"]\");", + " }", + " printf(\"\\n\");", + " fflush(stdout);", + "}", + "", + "#ifdef VAR_RANGES", + "#define BYTESIZE 32 /* 2^8 : 2^3 = 256:8 = 32 */", + "", + "typedef struct Vr_Ptr {", + " char *nm;", + " uchar vals[BYTESIZE];", + " struct Vr_Ptr *nxt;", + "} Vr_Ptr;", + "Vr_Ptr *ranges = (Vr_Ptr *) 0;", + "", + "void", + "logval(char *s, int v)", + "{ Vr_Ptr *tmp;", + "", + " if (v<0 || v > 255) return;", + " for (tmp = ranges; tmp; tmp = tmp->nxt)", + " if (!strcmp(tmp->nm, s))", + " goto found;", + " tmp = (Vr_Ptr *) emalloc(sizeof(Vr_Ptr));", + " tmp->nxt = ranges;", + " ranges = tmp;", + " tmp->nm = s;", + "found:", + " tmp->vals[(v)/8] |= 1<<((v)%%8);", + "}", + "", + "void", + "dumpval(uchar X[], int range)", + "{ int w, x, i, j = -1;", + "", + " for (w = i = 0; w < range; w++)", + " for (x = 0; x < 8; x++, i++)", + " {", + "from: if ((X[w] & (1<<x)))", + " { printf(\"%%d\", i);", + " j = i;", + " goto upto;", + " } }", + " return;", + " for (w = 0; w < range; w++)", + " for (x = 0; x < 8; x++, i++)", + " {", + "upto: if (!(X[w] & (1<<x)))", + " { if (i-1 == j)", + " printf(\", \");", + " else", + " printf(\"-%%d, \", i-1);", + " goto from;", + " } }", + " if (j >= 0 && j != 255)", + " printf(\"-255\");", + "}", + "", + "void", + "dumpranges(void)", + "{ Vr_Ptr *tmp;", + " printf(\"\\nValues assigned within \");", + " printf(\"interval [0..255]:\\n\");", + " for (tmp = ranges; tmp; tmp = tmp->nxt)", + " { printf(\"\\t%%s\\t: \", tmp->nm);", + " dumpval(tmp->vals, BYTESIZE);", + " printf(\"\\n\");", + " }", + "}", + "#endif", + 0, +}; diff --git a/sys/src/cmd/spin/pangen3.c b/sys/src/cmd/spin/pangen3.c new file mode 100755 index 000000000..4feb80ab1 --- /dev/null +++ b/sys/src/cmd/spin/pangen3.c @@ -0,0 +1,391 @@ +/***** spin: pangen3.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +extern FILE *th; +extern int claimnr, eventmapnr; + +typedef struct SRC { + short ln, st; /* linenr, statenr */ + Symbol *fn; /* filename */ + struct SRC *nxt; +} SRC; + +static int col; +static Symbol *lastfnm; +static Symbol lastdef; +static int lastfrom; +static SRC *frst = (SRC *) 0; +static SRC *skip = (SRC *) 0; + +extern void sr_mesg(FILE *, int, int); + +static void +putnr(int n) +{ + if (col++ == 8) + { fprintf(th, "\n\t"); + col = 1; + } + fprintf(th, "%3d, ", n); +} + +static void +putfnm(int j, Symbol *s) +{ + if (lastfnm && lastfnm == s && j != -1) + return; + + if (lastfnm) + fprintf(th, "{ %s, %d, %d },\n\t", + lastfnm->name, + lastfrom, + j-1); + lastfnm = s; + lastfrom = j; +} + +static void +putfnm_flush(int j) +{ + if (lastfnm) + fprintf(th, "{ %s, %d, %d }\n", + lastfnm->name, + lastfrom, j); +} + +void +putskip(int m) /* states that need not be reached */ +{ SRC *tmp; + + for (tmp = skip; tmp; tmp = tmp->nxt) + if (tmp->st == m) + return; + tmp = (SRC *) emalloc(sizeof(SRC)); + tmp->st = (short) m; + tmp->nxt = skip; + skip = tmp; +} + +void +unskip(int m) /* a state that needs to be reached after all */ +{ SRC *tmp, *lst=(SRC *)0; + + for (tmp = skip; tmp; lst = tmp, tmp = tmp->nxt) + if (tmp->st == m) + { if (tmp == skip) + skip = skip->nxt; + else + lst->nxt = tmp->nxt; + break; + } +} + +void +putsrc(Element *e) /* match states to source lines */ +{ SRC *tmp; + int n, m; + + if (!e || !e->n) return; + + n = e->n->ln; + m = e->seqno; + + for (tmp = frst; tmp; tmp = tmp->nxt) + if (tmp->st == m) + { if (tmp->ln != n || tmp->fn != e->n->fn) + printf("putsrc mismatch %d - %d, file %s\n", n, + tmp->ln, tmp->fn->name); + return; + } + tmp = (SRC *) emalloc(sizeof(SRC)); + tmp->ln = (short) n; + tmp->st = (short) m; + tmp->fn = e->n->fn; + tmp->nxt = frst; + frst = tmp; +} + +static void +dumpskip(int n, int m) +{ SRC *tmp, *lst; + int j; + + fprintf(th, "uchar reached%d [] = {\n\t", m); + for (j = 0, col = 0; j <= n; j++) + { lst = (SRC *) 0; + for (tmp = skip; tmp; lst = tmp, tmp = tmp->nxt) + if (tmp->st == j) + { putnr(1); + if (lst) + lst->nxt = tmp->nxt; + else + skip = tmp->nxt; + break; + } + if (!tmp) + putnr(0); + } + fprintf(th, "};\n"); + if (m == claimnr) + fprintf(th, "#define reached_claim reached%d\n", m); + if (m == eventmapnr) + fprintf(th, "#define reached_event reached%d\n", m); + + skip = (SRC *) 0; +} + +void +dumpsrc(int n, int m) +{ SRC *tmp, *lst; + int j; + + fprintf(th, "short src_ln%d [] = {\n\t", m); + for (j = 0, col = 0; j <= n; j++) + { lst = (SRC *) 0; + for (tmp = frst; tmp; lst = tmp, tmp = tmp->nxt) + if (tmp->st == j) + { putnr(tmp->ln); + break; + } + if (!tmp) + putnr(0); + } + fprintf(th, "};\n"); + + lastfnm = (Symbol *) 0; + lastdef.name = "\"-\""; + fprintf(th, "S_F_MAP src_file%d [] = {\n\t", m); + for (j = 0, col = 0; j <= n; j++) + { lst = (SRC *) 0; + for (tmp = frst; tmp; lst = tmp, tmp = tmp->nxt) + if (tmp->st == j) + { putfnm(j, tmp->fn); + if (lst) + lst->nxt = tmp->nxt; + else + frst = tmp->nxt; + break; + } + if (!tmp) + putfnm(j, &lastdef); + } + putfnm_flush(j); + fprintf(th, "};\n"); + + if (m == claimnr) + fprintf(th, "#define src_claim src_ln%d\n", m); + if (m == eventmapnr) + fprintf(th, "#define src_event src_ln%d\n", m); + + frst = (SRC *) 0; + dumpskip(n, m); +} + +#define Cat0(x) comwork(fd,now->lft,m); fprintf(fd, x); \ + comwork(fd,now->rgt,m) +#define Cat1(x) fprintf(fd,"("); Cat0(x); fprintf(fd,")") +#define Cat2(x,y) fprintf(fd,x); comwork(fd,y,m) +#define Cat3(x,y,z) fprintf(fd,x); comwork(fd,y,m); fprintf(fd,z) + +static int +symbolic(FILE *fd, Lextok *tv) +{ Lextok *n; extern Lextok *Mtype; + int cnt = 1; + + if (tv->ismtyp) + for (n = Mtype; n; n = n->rgt, cnt++) + if (cnt == tv->val) + { fprintf(fd, "%s", n->lft->sym->name); + return 1; + } + return 0; +} + +static void +comwork(FILE *fd, Lextok *now, int m) +{ Lextok *v; + int i, j; + + if (!now) { fprintf(fd, "0"); return; } + switch (now->ntyp) { + case CONST: sr_mesg(fd, now->val, now->ismtyp); break; + case '!': Cat3("!(", now->lft, ")"); break; + case UMIN: Cat3("-(", now->lft, ")"); break; + case '~': Cat3("~(", now->lft, ")"); break; + + case '/': Cat1("/"); break; + case '*': Cat1("*"); break; + case '-': Cat1("-"); break; + case '+': Cat1("+"); break; + case '%': Cat1("%%"); break; + case '&': Cat1("&"); break; + case '^': Cat1("^"); break; + case '|': Cat1("|"); break; + case LE: Cat1("<="); break; + case GE: Cat1(">="); break; + case GT: Cat1(">"); break; + case LT: Cat1("<"); break; + case NE: Cat1("!="); break; + case EQ: Cat1("=="); break; + case OR: Cat1("||"); break; + case AND: Cat1("&&"); break; + case LSHIFT: Cat1("<<"); break; + case RSHIFT: Cat1(">>"); break; + + case RUN: fprintf(fd, "run %s(", now->sym->name); + for (v = now->lft; v; v = v->rgt) + if (v == now->lft) + { comwork(fd, v->lft, m); + } else + { Cat2(",", v->lft); + } + fprintf(fd, ")"); + break; + + case LEN: putname(fd, "len(", now->lft, m, ")"); + break; + case FULL: putname(fd, "full(", now->lft, m, ")"); + break; + case EMPTY: putname(fd, "empty(", now->lft, m, ")"); + break; + case NFULL: putname(fd, "nfull(", now->lft, m, ")"); + break; + case NEMPTY: putname(fd, "nempty(", now->lft, m, ")"); + break; + + case 's': putname(fd, "", now->lft, m, now->val?"!!":"!"); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v != now->rgt) fprintf(fd,","); + if (!symbolic(fd, v->lft)) + comwork(fd,v->lft,m); + } + break; + case 'r': putname(fd, "", now->lft, m, "?"); + switch (now->val) { + case 0: break; + case 1: fprintf(fd, "?"); break; + case 2: fprintf(fd, "<"); break; + case 3: fprintf(fd, "?<"); break; + } + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v != now->rgt) fprintf(fd,","); + if (!symbolic(fd, v->lft)) + comwork(fd,v->lft,m); + } + if (now->val >= 2) + fprintf(fd, ">"); + break; + case 'R': putname(fd, "", now->lft, m, now->val?"??[":"?["); + for (v = now->rgt, i=0; v; v = v->rgt, i++) + { if (v != now->rgt) fprintf(fd,","); + if (!symbolic(fd, v->lft)) + comwork(fd,v->lft,m); + } + fprintf(fd, "]"); + break; + + case ENABLED: Cat3("enabled(", now->lft, ")"); + break; + + case EVAL: Cat3("eval(", now->lft, ")"); + break; + + case NONPROGRESS: + fprintf(fd, "np_"); + break; + + case PC_VAL: Cat3("pc_value(", now->lft, ")"); + break; + + case 'c': Cat3("(", now->lft, ")"); + break; + + case '?': if (now->lft) + { Cat3("( (", now->lft, ") -> "); + } + if (now->rgt) + { Cat3("(", now->rgt->lft, ") : "); + Cat3("(", now->rgt->rgt, ") )"); + } + break; + + case ASGN: comwork(fd,now->lft,m); + fprintf(fd," = "); + comwork(fd,now->rgt,m); + break; + + case PRINT: { char c, buf[512]; + strncpy(buf, now->sym->name, 510); + for (i = j = 0; i < 510; i++, j++) + { c = now->sym->name[i]; + buf[j] = c; + if (c == '\\') buf[++j] = c; + if (c == '\"') buf[j] = '\''; + if (c == '\0') break; + } + if (now->ntyp == PRINT) + fprintf(fd, "printf"); + else + fprintf(fd, "annotate"); + fprintf(fd, "(%s", buf); + } + for (v = now->lft; v; v = v->rgt) + { Cat2(",", v->lft); + } + fprintf(fd, ")"); + break; + case PRINTM: fprintf(fd, "printm("); + comwork(fd, now->lft, m); + fprintf(fd, ")"); + break; + case NAME: putname(fd, "", now, m, ""); + break; + case 'p': putremote(fd, now, m); + break; + case 'q': fprintf(fd, "%s", now->sym->name); + break; + case C_EXPR: + case C_CODE: fprintf(fd, "{%s}", now->sym->name); + break; + case ASSERT: Cat3("assert(", now->lft, ")"); + break; + case '.': fprintf(fd, ".(goto)"); break; + case GOTO: fprintf(fd, "goto %s", now->sym->name); break; + case BREAK: fprintf(fd, "break"); break; + case ELSE: fprintf(fd, "else"); break; + case '@': fprintf(fd, "-end-"); break; + + case D_STEP: fprintf(fd, "D_STEP"); break; + case ATOMIC: fprintf(fd, "ATOMIC"); break; + case NON_ATOMIC: fprintf(fd, "sub-sequence"); break; + case IF: fprintf(fd, "IF"); break; + case DO: fprintf(fd, "DO"); break; + case UNLESS: fprintf(fd, "unless"); break; + case TIMEOUT: fprintf(fd, "timeout"); break; + default: if (isprint(now->ntyp)) + fprintf(fd, "'%c'", now->ntyp); + else + fprintf(fd, "%d", now->ntyp); + break; + } +} + +void +comment(FILE *fd, Lextok *now, int m) +{ extern short terse, nocast; + + terse=nocast=1; + comwork(fd, now, m); + terse=nocast=0; +} diff --git a/sys/src/cmd/spin/pangen3.h b/sys/src/cmd/spin/pangen3.h new file mode 100755 index 000000000..931a598f5 --- /dev/null +++ b/sys/src/cmd/spin/pangen3.h @@ -0,0 +1,940 @@ +/***** spin: pangen3.h *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +static char *Head0[] = { + "#if defined(BFS) && defined(REACH)", + "#undef REACH", /* redundant with bfs */ + "#endif", + "#ifdef VERI", + "#define BASE 1", + "#else", + "#define BASE 0", + "#endif", + "typedef struct Trans {", + " short atom; /* if &2 = atomic trans; if &8 local */", + "#ifdef HAS_UNLESS", + " short escp[HAS_UNLESS]; /* lists the escape states */", + " short e_trans; /* if set, this is an escp-trans */", + "#endif", + " short tpe[2]; /* class of operation (for reduction) */", + " short qu[6]; /* for conditional selections: qid's */", + " uchar ty[6]; /* ditto: type's */", + "#ifdef NIBIS", + " short om; /* completion status of preselects */", + "#endif", + " char *tp; /* src txt of statement */", + " int st; /* the nextstate */", + " int t_id; /* transition id, unique within proc */", + " int forw; /* index forward transition */", + " int back; /* index return transition */", + " struct Trans *nxt;", + "} Trans;\n", + "#define qptr(x) (((uchar *)&now)+(int)q_offset[x])", + "#define pptr(x) (((uchar *)&now)+(int)proc_offset[x])", +/* "#define Pptr(x) ((proc_offset[x])?pptr(x):noptr)", */ + "extern uchar *Pptr(int);", + + "#define q_sz(x) (((Q0 *)qptr(x))->Qlen)\n", + "#ifndef VECTORSZ", + "#define VECTORSZ 1024 /* sv size in bytes */", + "#endif\n", + 0, +}; + +static char *Header[] = { + "#ifdef VERBOSE", + "#ifndef CHECK", + "#define CHECK", + "#endif", + "#ifndef DEBUG", + "#define DEBUG", + "#endif", + "#endif", + "#ifdef SAFETY", + "#ifndef NOFAIR", + "#define NOFAIR", + "#endif", + "#endif", + "#ifdef NOREDUCE", + "#ifndef XUSAFE", + "#define XUSAFE", + "#endif", + "#if !defined(SAFETY) && !defined(MA)", + "#define FULLSTACK", + "#endif", + "#else", + "#ifdef BITSTATE", + "#ifdef SAFETY && !defined(HASH64)", + "#define CNTRSTACK", + "#else", + "#define FULLSTACK", + "#endif", + "#else", + "#define FULLSTACK", + "#endif", + "#endif", + "#ifdef BITSTATE", + "#ifndef NOCOMP", + "#define NOCOMP", + "#endif", + "#if !defined(LC) && defined(SC)", + "#define LC", + "#endif", + "#endif", + "#if defined(COLLAPSE2) || defined(COLLAPSE3) || defined(COLLAPSE4)", + "/* accept the above for backward compatibility */", + "#define COLLAPSE", + "#endif", + "#ifdef HC", + "#undef HC", + "#define HC4", + "#endif", + "#ifdef HC0", /* 32 bits */ + "#define HC 0", + "#endif", + "#ifdef HC1", /* 32+8 bits */ + "#define HC 1", + "#endif", + "#ifdef HC2", /* 32+16 bits */ + "#define HC 2", + "#endif", + "#ifdef HC3", /* 32+24 bits */ + "#define HC 3", + "#endif", + "#ifdef HC4", /* 32+32 bits - combine with -DMA=8 */ + "#define HC 4", + "#endif", + "#ifdef COLLAPSE", + "unsigned long ncomps[256+2];", + "#endif", + + "#define MAXQ 255", + "#define MAXPROC 255", + "#define WS sizeof(long) /* word size in bytes */", + "typedef struct Stack { /* for queues and processes */", + "#if VECTORSZ>32000", + " int o_delta;", + " int o_offset;", + " int o_skip;", + " int o_delqs;", + "#else", + " short o_delta;", + " short o_offset;", + " short o_skip;", + " short o_delqs;", + "#endif", + " short o_boq;", + "#ifndef XUSAFE", + " char *o_name;", + "#endif", + " char *body;", + " struct Stack *nxt;", + " struct Stack *lst;", + "} Stack;\n", + "typedef struct Svtack { /* for complete state vector */", + "#if VECTORSZ>32000", + " int o_delta;", + " int m_delta;", + "#else", + " short o_delta; /* current size of frame */", + " short m_delta; /* maximum size of frame */", + "#endif", + "#if SYNC", + " short o_boq;", + "#endif", + 0, +}; + +static char *Header0[] = { + " char *body;", + " struct Svtack *nxt;", + " struct Svtack *lst;", + "} Svtack;\n", + "Trans ***trans; /* 1 ptr per state per proctype */\n", + "#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];", + "#endif", + "uchar proc_skip[MAXPROC];", + "uchar q_skip[MAXQ];", + "unsigned long vsize; /* vector size in bytes */", + "#ifdef SVDUMP", + "int vprefix=0, svfd; /* runtime option -pN */", + "#endif", + "char *tprefix = \"trail\"; /* runtime option -tsuffix */", + "short boq = -1; /* blocked_on_queue status */", + 0, +}; + +static char *Head1[] = { + "typedef struct State {", + " uchar _nr_pr;", + " uchar _nr_qs;", + " uchar _a_t; /* cycle detection */", +#if 0 + in _a_t: bits 0,4, and 5 =(1|16|32) are set during a 2nd dfs + bit 1 is used as the A-bit for fairness + bit 7 (128) is the proviso bit, for reduced 2nd dfs (acceptance) +#endif + "#ifndef NOFAIR", + " uchar _cnt[NFAIR]; /* counters, weak fairness */", + "#endif", + + "#ifndef NOVSZ", +#ifdef SOLARIS + "#if 0", + /* v3.4 + * noticed alignment problems with some Solaris + * compilers, if widest field isn't wordsized + */ +#else + "#if VECTORSZ<65536", +#endif + " unsigned short _vsz;", + "#else", + " unsigned long _vsz;", + "#endif", + "#endif", + + "#ifdef HAS_LAST", /* cannot go before _cnt - see hstore() */ + " uchar _last; /* pid executed in last step */", + "#endif", + "#ifdef EVENT_TRACE", + "#if nstates_event<256", + " uchar _event;", + "#else", + " unsigned short _event;", + "#endif", + "#endif", + 0, +}; + +static char *Addp0[] = { + /* addproc(....parlist... */ ")", + "{ int j, h = now._nr_pr;", + "#ifndef NOCOMP", + " int k;", + "#endif", + " uchar *o_this = this;\n", + "#ifndef INLINE", + " if (TstOnly) return (h < MAXPROC);", + "#endif", + "#ifndef NOBOUNDCHECK", + "/* redefine Index only within this procedure */", + "#undef Index", + "#define Index(x, y) Boundcheck(x, y, 0, 0, 0)", + "#endif", + " if (h >= MAXPROC)", + " Uerror(\"too many processes\");", + " switch (n) {", + " case 0: j = sizeof(P0); break;", + 0, +}; + +static char *Addp1[] = { + " default: Uerror(\"bad proc - addproc\");", + " }", + " if (vsize%%WS)", + " proc_skip[h] = WS-(vsize%%WS);", + " else", + " proc_skip[h] = 0;", + "#ifndef NOCOMP", + " for (k = vsize + (int) proc_skip[h]; k > vsize; k--)", + " Mask[k-1] = 1; /* align */", + "#endif", + " vsize += (int) proc_skip[h];", + " proc_offset[h] = vsize;", + "#ifdef SVDUMP", + " if (vprefix > 0)", + " { int dummy = 0;", + " write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */", + " write(svfd, (uchar *) &h, sizeof(int));", + " write(svfd, (uchar *) &n, sizeof(int));", + "#if VECTORSZ>32000", + " write(svfd, (uchar *) &proc_offset[h], sizeof(int));", + "#else", + " write(svfd, (uchar *) &proc_offset[h], sizeof(short));", + "#endif", + " write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */", + " }", + "#endif", + " now._nr_pr += 1;", + " if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2))", + " { printf(\"pan: error: too many processes -- current\");", + " printf(\" max is %%d procs (-DNFAIR=%%d)\\n\",", + " (8*NFAIR)/2 - 2, NFAIR);", + " printf(\"\\trecompile with -DNFAIR=%%d\\n\",", + " NFAIR+1);", + " pan_exit(1);", + " }", + + " vsize += j;", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + "#ifndef NOCOMP", + " for (k = 1; k <= Air[n]; k++)", + " Mask[vsize - k] = 1; /* pad */", + " Mask[vsize-j] = 1; /* _pid */", + "#endif", + " hmax = max(hmax, vsize);", + " if (vsize >= VECTORSZ)", + " { printf(\"pan: error, VECTORSZ too small, recompile pan.c\");", + " printf(\" with -DVECTORSZ=N with N>%%d\\n\", vsize);", + " Uerror(\"aborting\");", + " }", + " memset((char *)pptr(h), 0, j);", + " this = pptr(h);", + " if (BASE > 0 && h > 0)", + " ((P0 *)this)->_pid = h-BASE;", + " else", + " ((P0 *)this)->_pid = h;", + " switch (n) {", + 0, +}; + +static char *Addq0[] = { + "int", + "addqueue(int n, int is_rv)", + "{ int j=0, i = now._nr_qs;", + "#ifndef NOCOMP", + " int k;", + "#endif", + " if (i >= MAXQ)", + " Uerror(\"too many queues\");", + " switch (n) {", + 0, +}; + +static char *Addq1[] = { + " default: Uerror(\"bad queue - addqueue\");", + " }", + " if (vsize%%WS)", + " q_skip[i] = WS-(vsize%%WS);", + " else", + " q_skip[i] = 0;", + "#ifndef NOCOMP", + " k = vsize;", + "#ifndef BFS", + " if (is_rv) k += j;", + "#endif", + " for (k += (int) q_skip[i]; k > vsize; k--)", + " Mask[k-1] = 1;", + "#endif", + " vsize += (int) q_skip[i];", + " q_offset[i] = vsize;", + " now._nr_qs += 1;", + " vsize += j;", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + " hmax = max(hmax, vsize);", + " if (vsize >= VECTORSZ)", + " Uerror(\"VECTORSZ is too small, edit pan.h\");", + " memset((char *)qptr(i), 0, j);", + " ((Q0 *)qptr(i))->_t = n;", + " return i+1;", + "}\n", + 0, +}; + +static char *Addq11[] = { + "{ int j; uchar *z;\n", + "#ifdef HAS_SORTED", + " int k;", + "#endif", + " if (!into--)", + " uerror(\"ref to uninitialized chan name (sending)\");", + " if (into >= (int) now._nr_qs || into < 0)", + " Uerror(\"qsend bad queue#\");", + " z = qptr(into);", + " j = ((Q0 *)qptr(into))->Qlen;", + " switch (((Q0 *)qptr(into))->_t) {", + 0, +}; + +static char *Addq2[] = { + " case 0: printf(\"queue %%d was deleted\\n\", into+1);", + " default: Uerror(\"bad queue - qsend\");", + " }", + "#ifdef EVENT_TRACE", + " if (in_s_scope(into+1))", + " require('s', into);", + "#endif", + "}", + "#endif\n", + "#if SYNC", + "int", + "q_zero(int from)", + "{ if (!from--)", + " { uerror(\"ref to uninitialized chan name (q_zero)\");", + " return 0;", + " }", + " switch(((Q0 *)qptr(from))->_t) {", + 0, +}; + +static char *Addq3[] = { + " case 0: printf(\"queue %%d was deleted\\n\", from+1);", + " }", + " Uerror(\"bad queue q-zero\");", + " return -1;", + "}", + "int", + "not_RV(int from)", + "{ if (q_zero(from))", + " { printf(\"==>> a test of the contents of a rv \");", + " printf(\"channel always returns FALSE\\n\");", + " uerror(\"error to poll rendezvous channel\");", + " }", + " return 1;", + "}", + "#endif", + "#ifndef XUSAFE", + "void", + "setq_claim(int x, int m, char *s, int y, char *p)", + "{ if (x == 0)", + " uerror(\"x[rs] claim on uninitialized channel\");", + " if (x < 0 || x > MAXQ)", + " Uerror(\"cannot happen setq_claim\");", + " q_claim[x] |= m;", + " p_name[y] = p;", + " q_name[x] = s;", + " if (m&2) q_S_check(x, y);", + " if (m&1) q_R_check(x, y);", + "}", + "short q_sender[MAXQ+1];", + "int", + "q_S_check(int x, int who)", + "{ if (!q_sender[x])", + " { q_sender[x] = who+1;", + "#if SYNC", + " if (q_zero(x))", + " { printf(\"chan %%s (%%d), \",", + " q_name[x], x-1);", + " printf(\"sndr proc %%s (%%d)\\n\",", + " p_name[who], who);", + " uerror(\"xs chans cannot be used for rv\");", + " }", + "#endif", + " } else", + " if (q_sender[x] != who+1)", + " { printf(\"pan: xs assertion violated: \");", + " printf(\"access to chan <%%s> (%%d)\\npan: by \",", + " q_name[x], x-1);", + " if (q_sender[x] > 0 && p_name[q_sender[x]-1])", + " printf(\"%%s (proc %%d) and by \",", + " p_name[q_sender[x]-1], q_sender[x]-1);", + " printf(\"%%s (proc %%d)\\n\",", + " p_name[who], who);", + " uerror(\"error, partial order reduction invalid\");", + " }", + " return 1;", + "}", + "short q_recver[MAXQ+1];", + "int", + "q_R_check(int x, int who)", + "{ if (!q_recver[x])", + " { q_recver[x] = who+1;", + "#if SYNC", + " if (q_zero(x))", + " { printf(\"chan %%s (%%d), \",", + " q_name[x], x-1);", + " printf(\"recv proc %%s (%%d)\\n\",", + " p_name[who], who);", + " uerror(\"xr chans cannot be used for rv\");", + " }", + "#endif", + " } else", + " if (q_recver[x] != who+1)", + " { printf(\"pan: xr assertion violated: \");", + " printf(\"access to chan %%s (%%d)\\npan: \",", + " q_name[x], x-1);", + " if (q_recver[x] > 0 && p_name[q_recver[x]-1])", + " printf(\"by %%s (proc %%d) and \",", + " p_name[q_recver[x]-1], q_recver[x]-1);", + " printf(\"by %%s (proc %%d)\\n\",", + " p_name[who], who);", + " uerror(\"error, partial order reduction invalid\");", + " }", + " return 1;", + "}", + "#endif", + "int", + "q_len(int x)", + "{ if (!x--)", + " uerror(\"ref to uninitialized chan name (len)\");", + " return ((Q0 *)qptr(x))->Qlen;", + "}\n", + "int", + "q_full(int from)", + "{ if (!from--)", + " uerror(\"ref to uninitialized chan name (qfull)\");", + " switch(((Q0 *)qptr(from))->_t) {", + 0, +}; + +static char *Addq4[] = { + " case 0: printf(\"queue %%d was deleted\\n\", from+1);", + " }", + " Uerror(\"bad queue - q_full\");", + " return 0;", + "}\n", + "#ifdef HAS_UNLESS", + "int", + "q_e_f(int from)", + "{ /* empty or full */", + " return !q_len(from) || q_full(from);", + "}", + "#endif", + "#if NQS>0", + "int", + "qrecv(int from, int slot, int fld, int done)", + "{ uchar *z;", + " int j, k, r=0;\n", + " if (!from--)", + " uerror(\"ref to uninitialized chan name (receiving)\");", + " if (from >= (int) now._nr_qs || from < 0)", + " Uerror(\"qrecv bad queue#\");", + " z = qptr(from);", + "#ifdef EVENT_TRACE", + " if (done && (in_r_scope(from+1)))", + " require('r', from);", + "#endif", + " switch (((Q0 *)qptr(from))->_t) {", + 0, +}; + +static char *Addq5[] = { + " case 0: printf(\"queue %%d was deleted\\n\", from+1);", + " default: Uerror(\"bad queue - qrecv\");", + " }", + " return r;", + "}", + "#endif\n", + "#ifndef BITSTATE", + "#ifdef COLLAPSE", + "long", + "col_q(int i, char *z)", + "{ int j=0, k;", + " char *x, *y;", + " Q0 *ptr = (Q0 *) qptr(i);", + " switch (ptr->_t) {", + 0, +}; + +static char *Code0[] = { + "void", + "run(void)", + "{ /* int i; */", + " memset((char *)&now, 0, sizeof(State));", + " vsize = (unsigned long) (sizeof(State) - VECTORSZ);", + "#ifndef NOVSZ", + " now._vsz = vsize;", + "#endif", + "/* optional provisioning statements, e.g. to */", + "/* set hidden variables, used as constants */", + "#ifdef PROV", + "#include PROV", + "#endif", + " settable();", + 0, +}; + +static char *R0[] = { + " Maxbody = max(Maxbody, sizeof(P%d));", + " reached[%d] = reached%d;", + " accpstate[%d] = (uchar *) emalloc(nstates%d);", + " progstate[%d] = (uchar *) emalloc(nstates%d);", + " stopstate[%d] = (uchar *) emalloc(nstates%d);", + " visstate[%d] = (uchar *) emalloc(nstates%d);", + " mapstate[%d] = (short *) emalloc(nstates%d * sizeof(short));", + "#ifdef HAS_CODE", + " NrStates[%d] = nstates%d;", + "#endif", + " stopstate[%d][endstate%d] = 1;", + 0, +}; + +static char *R0a[] = { + " retrans(%d, nstates%d, start%d, src_ln%d, reached%d);", + 0, +}; +static char *R0b[] = { + " if (state_tables)", + " { printf(\"\\nTransition Type: \");", + " printf(\"A=atomic; D=d_step; L=local; G=global\\n\");", + " printf(\"Source-State Labels: \");", + " printf(\"p=progress; e=end; a=accept;\\n\");", + "#ifdef MERGED", + " printf(\"Note: statement merging was used. Only the first\\n\");", + " printf(\" stmnt executed in each merge sequence is shown\\n\");", + " printf(\" (use spin -a -o3 to disable statement merging)\\n\");", + "#endif", + " pan_exit(0);", + " }", + 0, +}; + +static char *Code1[] = { + "#ifdef NP", + "#define ACCEPT_LAB 1 /* at least 1 in np_ */", + "#else", + "#define ACCEPT_LAB %d /* user-defined accept labels */", + "#endif", + 0, +}; + +static char *Code3[] = { + "#define PROG_LAB %d /* progress labels */", + 0, +}; + +static char *R2[] = { + "uchar *accpstate[%d];", + "uchar *progstate[%d];", + "uchar *reached[%d];", + "uchar *stopstate[%d];", + "uchar *visstate[%d];", + "short *mapstate[%d];", + "#ifdef HAS_CODE", + "int NrStates[%d];", + "#endif", + 0, +}; +static char *R3[] = { + " Maxbody = max(Maxbody, sizeof(Q%d));", + 0, +}; +static char *R4[] = { + " r_ck(reached%d, nstates%d, %d, src_ln%d, src_file%d);", + 0, +}; +static char *R5[] = { + " case %d: j = sizeof(P%d); break;", + 0, +}; +static char *R6[] = { + " }", + " this = o_this;", + " return h-BASE;", + "#ifndef NOBOUNDCHECK", + "#undef Index", + "#define Index(x, y) Boundcheck(x, y, II, tt, t)", + "#endif", + "}\n", + "#if defined(BITSTATE) && defined(COLLAPSE)", + "/* just to allow compilation, to generate the error */", + "long col_p(int i, char *z) { return 0; }", + "long col_q(int i, char *z) { return 0; }", + "#endif", + "#ifndef BITSTATE", + "#ifdef COLLAPSE", + "long", + "col_p(int i, char *z)", + "{ int j, k; unsigned long ordinal(char *, long, short);", + " char *x, *y;", + " P0 *ptr = (P0 *) pptr(i);", + " switch (ptr->_t) {", + " case 0: j = sizeof(P0); break;", + 0, +}; +static char *R8a[] = { + " default: Uerror(\"bad proctype - collapse\");", + " }", + " if (z) x = z; else x = scratch;", + " y = (char *) ptr; k = proc_offset[i];", + + " for ( ; j > 0; j--, y++)", + " if (!Mask[k++]) *x++ = *y;", + + " for (j = 0; j < WS-1; j++)", + " *x++ = 0;", + " x -= j;", + " if (z) return (long) (x - z);", + " return ordinal(scratch, x-scratch, (short) (2+ptr->_t));", + "}", + "#endif", + "#endif", + 0, +}; +static char *R8b[] = { + " default: Uerror(\"bad qtype - collapse\");", + " }", + " if (z) x = z; else x = scratch;", + " y = (char *) ptr; k = q_offset[i];", + + " /* no need to store the empty slots at the end */", + " j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]);", + + " for ( ; j > 0; j--, y++)", + " if (!Mask[k++]) *x++ = *y;", + + " for (j = 0; j < WS-1; j++)", + " *x++ = 0;", + " x -= j;", + " if (z) return (long) (x - z);", + " return ordinal(scratch, x-scratch, 1); /* chan */", + "}", + "#endif", + "#endif", + 0, +}; + +static char *R12[] = { + "\t\tcase %d: r = ((Q%d *)z)->contents[slot].fld%d; break;", + 0, +}; +char *R13[] = { + "int ", + "unsend(int into)", + "{ int _m=0, j; uchar *z;\n", + "#ifdef HAS_SORTED", + " int k;", + "#endif", + " if (!into--)", + " uerror(\"ref to uninitialized chan (unsend)\");", + " z = qptr(into);", + " j = ((Q0 *)z)->Qlen;", + " ((Q0 *)z)->Qlen = --j;", + " switch (((Q0 *)qptr(into))->_t) {", + 0, +}; +char *R14[] = { + " default: Uerror(\"bad queue - unsend\");", + " }", + " return _m;", + "}\n", + "void", + "unrecv(int from, int slot, int fld, int fldvar, int strt)", + "{ int j; uchar *z;\n", + " if (!from--)", + " uerror(\"ref to uninitialized chan (unrecv)\");", + " z = qptr(from);", + " j = ((Q0 *)z)->Qlen;", + " if (strt) ((Q0 *)z)->Qlen = j+1;", + " switch (((Q0 *)qptr(from))->_t) {", + 0, +}; +char *R15[] = { + " default: Uerror(\"bad queue - qrecv\");", + " }", + "}", + 0, +}; +static char *Proto[] = { + "", + "/** function prototypes **/", + "char *emalloc(unsigned long);", + "char *Malloc(unsigned long);", + "int Boundcheck(int, int, int, int, Trans *);", + "int addqueue(int, int);", + "/* int atoi(char *); */", + "/* int abort(void); */", + "int close(int);", /* should probably remove this */ +#if 0 + "#ifndef SC", + "int creat(char *, unsigned short);", + "int write(int, void *, unsigned);", + "#endif", +#endif + "int delproc(int, int);", + "int endstate(void);", + "int hstore(char *, int);", +"#ifdef MA", + "int gstore(char *, int, uchar);", +"#endif", + "int q_cond(short, Trans *);", + "int q_full(int);", + "int q_len(int);", + "int q_zero(int);", + "int qrecv(int, int, int, int);", + "int unsend(int);", + "/* void *sbrk(int); */", + "void Uerror(char *);", + "void assert(int, char *, int, int, Trans *);", + "void c_chandump(int);", + "void c_globals(void);", + "void c_locals(int, int);", + "void checkcycles(void);", + "void crack(int, int, Trans *, short *);", + "void d_hash(uchar *, int);", + "void s_hash(uchar *, int);", + "void r_hash(uchar *, int);", + "void delq(int);", + "void do_reach(void);", + "void pan_exit(int);", + "void exit(int);", + "void hinit(void);", + "void imed(Trans *, int, int, int);", + "void new_state(void);", + "void p_restor(int);", + "void putpeg(int, int);", + "void putrail(void);", + "void q_restor(void);", + "void retrans(int, int, int, short *, uchar *);", + "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 uerror(char *);", + "void unrecv(int, int, int, int, int);", + "void usage(FILE *);", + "void wrap_stats(void);", + "#if defined(FULLSTACK) && defined(BITSTATE)", + "int onstack_now(void);", + "void onstack_init(void);", + "void onstack_put(void);", + "void onstack_zap(void);", + "#endif", + "#ifndef XUSAFE", + "int q_S_check(int, int);", + "int q_R_check(int, int);", + "uchar q_claim[MAXQ+1];", + "char *q_name[MAXQ+1];", + "char *p_name[MAXPROC+1];", + "#endif", + 0, +}; + +static char *SvMap[] = { + "void", + "to_compile(void)", + "{ char ctd[1024], carg[64];", + "#ifdef BITSTATE", + " strcpy(ctd, \"-DBITSTATE \");", + "#else", + " strcpy(ctd, \"\");", + "#endif", + "#ifdef NOVSZ", + " strcat(ctd, \"-DNOVSZ \");", + "#endif", + "#ifdef MEMLIM", + " sprintf(carg, \"-DMEMLIM=%%d \", MEMLIM);", + " strcat(ctd, carg);", + "#else", + "#ifdef MEMCNT", + " sprintf(carg, \"-DMEMCNT=%%d \", MEMCNT);", + " strcat(ctd, carg);", + "#endif", + "#endif", + "#ifdef NOCLAIM", + " strcat(ctd, \"-DNOCLAIM \");", + "#endif", + "#ifdef SAFETY", + " strcat(ctd, \"-DSAFETY \");", + "#else", + "#ifdef NOFAIR", + " strcat(ctd, \"-DNOFAIR \");", + "#else", + "#ifdef NFAIR", + " if (NFAIR != 2)", + " { sprintf(carg, \"-DNFAIR=%%d \", NFAIR);", + " strcat(ctd, carg);", + " }", + "#endif", + "#endif", + "#endif", + "#ifdef NOREDUCE", + " strcat(ctd, \"-DNOREDUCE \");", + "#else", + "#ifdef XUSAFE", + " strcat(ctd, \"-DXUSAFE \");", + "#endif", + "#endif", + "#ifdef NP", + " strcat(ctd, \"-DNP \");", + "#endif", + "#ifdef PEG", + " strcat(ctd, \"-DPEG \");", + "#endif", + "#ifdef VAR_RANGES", + " strcat(ctd, \"-DVAR_RANGES \");", + "#endif", + "#ifdef HC0", + " strcat(ctd, \"-DHC0 \");", + "#endif", + "#ifdef HC1", + " strcat(ctd, \"-DHC1 \");", + "#endif", + "#ifdef HC2", + " strcat(ctd, \"-DHC2 \");", + "#endif", + "#ifdef HC3", + " strcat(ctd, \"-DHC3 \");", + "#endif", + "#ifdef HC4", + " strcat(ctd, \"-DHC4 \");", + "#endif", + "#ifdef CHECK", + " strcat(ctd, \"-DCHECK \");", + "#endif", + "#ifdef CTL", + " strcat(ctd, \"-DCTL \");", + "#endif", + "#ifdef NIBIS", + " strcat(ctd, \"-DNIBIS \");", + "#endif", + "#ifdef NOBOUNDCHECK", + " strcat(ctd, \"-DNOBOUNDCHECK \");", + "#endif", + "#ifdef NOSTUTTER", + " strcat(ctd, \"-DNOSTUTTER \");", + "#endif", + "#ifdef REACH", + " strcat(ctd, \"-DREACH \");", + "#endif", + "#ifdef PRINTF", + " strcat(ctd, \"-DPRINTF \");", + "#endif", + "#ifdef OTIM", + " strcat(ctd, \"-DOTIM \");", + "#endif", + "#ifdef COLLAPSE", + " strcat(ctd, \"-DCOLLAPSE \");", + "#endif", + "#ifdef MA", + " sprintf(carg, \"-DMA=%%d \", MA);", + " strcat(ctd, carg);", + "#endif", + "#ifdef SVDUMP", + " strcat(ctd, \"-DSVDUMP \");", + "#endif", + "#ifdef VECTORSZ", + " if (VECTORSZ != 1024)", + " { sprintf(carg, \"-DVECTORSZ=%%d \", VECTORSZ);", + " strcat(ctd, carg);", + " }", + "#endif", + "#ifdef VERBOSE", + " strcat(ctd, \"-DVERBOSE \");", + "#endif", + "#ifdef CHECK", + " strcat(ctd, \"-DCHECK \");", + "#endif", + "#ifdef SDUMP", + " strcat(ctd, \"-DSDUMP \");", + "#endif", + "#ifdef COVEST", + " strcat(ctd, \"-DCOVEST \");", + "#endif", + " printf(\"Compiled as: cc -o pan %%span.c\\n\", ctd);", + "}", + 0, +}; diff --git a/sys/src/cmd/spin/pangen4.c b/sys/src/cmd/spin/pangen4.c new file mode 100755 index 000000000..777371fc7 --- /dev/null +++ b/sys/src/cmd/spin/pangen4.c @@ -0,0 +1,344 @@ +/***** spin: pangen4.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +extern FILE *tc, *tb; +extern Queue *qtab; +extern Symbol *Fname; +extern int lineno, m_loss, Pid, eventmapnr, multi_oval; +extern short nocast, has_provided, has_sorted; +extern char *R13[], *R14[], *R15[]; + +static void check_proc(Lextok *, int); + +void +undostmnt(Lextok *now, int m) +{ Lextok *v; + int i, j; + + if (!now) + { fprintf(tb, "0"); + return; + } + lineno = now->ln; + Fname = now->fn; + switch (now->ntyp) { + case CONST: case '!': case UMIN: + case '~': case '/': case '*': + case '-': case '+': case '%': + case LT: case GT: case '&': + case '|': case LE: case GE: + case NE: case EQ: case OR: + case AND: case LSHIFT: case RSHIFT: + case TIMEOUT: case LEN: case NAME: + case FULL: case EMPTY: case 'R': + case NFULL: case NEMPTY: case ENABLED: + case '?': case PC_VAL: case '^': + case C_EXPR: + case NONPROGRESS: + putstmnt(tb, now, m); + break; + + case RUN: + fprintf(tb, "delproc(0, now._nr_pr-1)"); + break; + + case 's': + if (Pid == eventmapnr) break; + + if (m_loss) + fprintf(tb, "if (_m == 2) "); + putname(tb, "_m = unsend(", now->lft, m, ")"); + break; + + case 'r': + if (Pid == eventmapnr) break; + + for (v = now->rgt, i=j=0; v; v = v->rgt, i++) + if (v->lft->ntyp != CONST + && v->lft->ntyp != EVAL) + j++; + if (j == 0 && now->val >= 2) + break; /* poll without side-effect */ + + { int ii = 0, jj; + + for (v = now->rgt; v; v = v->rgt) + if ((v->lft->ntyp != CONST + && v->lft->ntyp != EVAL)) + ii++; /* nr of things bupped */ + if (now->val == 1) + { ii++; + jj = multi_oval - ii - 1; + fprintf(tb, "XX = trpt->bup.oval"); + if (multi_oval > 0) + { fprintf(tb, "s[%d]", jj); + jj++; + } + fprintf(tb, ";\n\t\t"); + } else + { fprintf(tb, "XX = 1;\n\t\t"); + jj = multi_oval - ii - 1; + } + + if (now->val < 2) /* not for channel poll */ + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { switch(v->lft->ntyp) { + case CONST: + case EVAL: + fprintf(tb, "unrecv"); + putname(tb, "(", now->lft, m, ", XX-1, "); + fprintf(tb, "%d, ", i); + if (v->lft->ntyp == EVAL) + undostmnt(v->lft->lft, m); + else + undostmnt(v->lft, m); + fprintf(tb, ", %d);\n\t\t", (i==0)?1:0); + break; + default: + fprintf(tb, "unrecv"); + putname(tb, "(", now->lft, m, ", XX-1, "); + fprintf(tb, "%d, ", i); + if (v->lft->sym + && !strcmp(v->lft->sym->name, "_")) + { fprintf(tb, "trpt->bup.oval"); + if (multi_oval > 0) + fprintf(tb, "s[%d]", jj); + } else + putstmnt(tb, v->lft, m); + + fprintf(tb, ", %d);\n\t\t", (i==0)?1:0); + if (multi_oval > 0) + jj++; + break; + } } + jj = multi_oval - ii - 1; + + if (now->val == 1 && multi_oval > 0) + jj++; /* new 3.4.0 */ + + for (v = now->rgt, i = 0; v; v = v->rgt, i++) + { switch(v->lft->ntyp) { + case CONST: + case EVAL: + break; + default: + if (!v->lft->sym + || strcmp(v->lft->sym->name, "_") != 0) + { nocast=1; putstmnt(tb,v->lft,m); + nocast=0; fprintf(tb, " = trpt->bup.oval"); + if (multi_oval > 0) + fprintf(tb, "s[%d]", jj); + fprintf(tb, ";\n\t\t"); + } + if (multi_oval > 0) + jj++; + break; + } } + multi_oval -= ii; + } + break; + + case '@': + fprintf(tb, "p_restor(II);\n\t\t"); + break; + + case ASGN: + nocast=1; putstmnt(tb,now->lft,m); + nocast=0; fprintf(tb, " = trpt->bup.oval"); + if (multi_oval > 0) + { multi_oval--; + fprintf(tb, "s[%d]", multi_oval-1); + } + check_proc(now->rgt, m); + break; + + case 'c': + check_proc(now->lft, m); + break; + + case '.': + case GOTO: + case ELSE: + case BREAK: + break; + + case C_CODE: + fprintf(tb, "sv_restor();\n"); + break; + + case ASSERT: + case PRINT: + check_proc(now, m); + break; + case PRINTM: + break; + + default: + printf("spin: bad node type %d (.b)\n", now->ntyp); + alldone(1); + } +} + +int +any_undo(Lextok *now) +{ /* is there anything to undo on a return move? */ + if (!now) return 1; + switch (now->ntyp) { + case 'c': return any_oper(now->lft, RUN); + case ASSERT: + case PRINT: return any_oper(now, RUN); + + case PRINTM: + case '.': + case GOTO: + case ELSE: + case BREAK: return 0; + default: return 1; + } +} + +int +any_oper(Lextok *now, int oper) +{ /* check if an expression contains oper operator */ + if (!now) return 0; + if (now->ntyp == oper) + return 1; + return (any_oper(now->lft, oper) || any_oper(now->rgt, oper)); +} + +static void +check_proc(Lextok *now, int m) +{ + if (!now) + return; + if (now->ntyp == '@' || now->ntyp == RUN) + { fprintf(tb, ";\n\t\t"); + undostmnt(now, m); + } + check_proc(now->lft, m); + check_proc(now->rgt, m); +} + +void +genunio(void) +{ char buf1[256]; + Queue *q; int i; + + ntimes(tc, 0, 1, R13); + for (q = qtab; q; q = q->nxt) + { fprintf(tc, "\tcase %d:\n", q->qid); + + if (has_sorted) + { sprintf(buf1, "((Q%d *)z)->contents", q->qid); + fprintf(tc, "#ifdef HAS_SORTED\n"); + fprintf(tc, "\t\tj = trpt->ipt;\n"); /* ipt was bup.oval */ + fprintf(tc, "#endif\n"); + fprintf(tc, "\t\tfor (k = j; k < ((Q%d *)z)->Qlen; k++)\n", + q->qid); + fprintf(tc, "\t\t{\n"); + for (i = 0; i < q->nflds; i++) + fprintf(tc, "\t\t\t%s[k].fld%d = %s[k+1].fld%d;\n", + buf1, i, buf1, i); + fprintf(tc, "\t\t}\n"); + fprintf(tc, "\t\tj = ((Q0 *)z)->Qlen;\n"); + } + + sprintf(buf1, "((Q%d *)z)->contents[j].fld", q->qid); + for (i = 0; i < q->nflds; i++) + fprintf(tc, "\t\t%s%d = 0;\n", buf1, i); + if (q->nslots==0) + { /* check if rendezvous succeeded, 1 level down */ + fprintf(tc, "\t\t_m = (trpt+1)->o_m;\n"); + fprintf(tc, "\t\tif (_m) (trpt-1)->o_pm |= 1;\n"); + fprintf(tc, "\t\tUnBlock;\n"); + } else + fprintf(tc, "\t\t_m = trpt->o_m;\n"); + + fprintf(tc, "\t\tbreak;\n"); + } + ntimes(tc, 0, 1, R14); + for (q = qtab; q; q = q->nxt) + { sprintf(buf1, "((Q%d *)z)->contents", q->qid); + fprintf(tc, " case %d:\n", q->qid); + if (q->nslots == 0) + fprintf(tc, "\t\tif (strt) boq = from+1;\n"); + else if (q->nslots > 1) /* shift */ + { fprintf(tc, "\t\tif (strt && slot<%d)\n", + q->nslots-1); + fprintf(tc, "\t\t{\tfor (j--; j>=slot; j--)\n"); + fprintf(tc, "\t\t\t{"); + for (i = 0; i < q->nflds; i++) + { fprintf(tc, "\t%s[j+1].fld%d =\n\t\t\t", + buf1, i); + fprintf(tc, "\t%s[j].fld%d;\n\t\t\t", + buf1, i); + } + fprintf(tc, "}\n\t\t}\n"); + } + strcat(buf1, "[slot].fld"); + fprintf(tc, "\t\tif (strt) {\n"); + for (i = 0; i < q->nflds; i++) + fprintf(tc, "\t\t\t%s%d = 0;\n", buf1, i); + fprintf(tc, "\t\t}\n"); + if (q->nflds == 1) /* set */ + fprintf(tc, "\t\tif (fld == 0) %s0 = fldvar;\n", + buf1); + else + { fprintf(tc, "\t\tswitch (fld) {\n"); + for (i = 0; i < q->nflds; i++) + { fprintf(tc, "\t\tcase %d:\t%s", i, buf1); + fprintf(tc, "%d = fldvar; break;\n", i); + } + fprintf(tc, "\t\t}\n"); + } + fprintf(tc, "\t\tbreak;\n"); + } + ntimes(tc, 0, 1, R15); +} + +int +proper_enabler(Lextok *n) +{ + if (!n) return 1; + switch (n->ntyp) { + case NEMPTY: case FULL: + case NFULL: case EMPTY: + case LEN: case 'R': + case NAME: + has_provided = 1; + if (strcmp(n->sym->name, "_pid") == 0) + return 1; + return (!(n->sym->context)); + + case CONST: case TIMEOUT: + has_provided = 1; + return 1; + + case ENABLED: case PC_VAL: + return proper_enabler(n->lft); + + case '!': case UMIN: case '~': + return proper_enabler(n->lft); + + case '/': case '*': case '-': case '+': + case '%': case LT: case GT: case '&': case '^': + case '|': case LE: case GE: case NE: case '?': + case EQ: case OR: case AND: case LSHIFT: + case RSHIFT: case 'c': + return proper_enabler(n->lft) && proper_enabler(n->rgt); + default: + break; + } + return 0; +} diff --git a/sys/src/cmd/spin/pangen4.h b/sys/src/cmd/spin/pangen4.h new file mode 100755 index 000000000..d80bdeaac --- /dev/null +++ b/sys/src/cmd/spin/pangen4.h @@ -0,0 +1,727 @@ +/***** spin: pangen4.h *****/ + +/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* The DFA code below was written by Anuj Puri and Gerard J. Holzmann in */ +/* May 1997, and was inspired by earlier work on data compression using */ +/* sharing tree data structures and graph-encoded sets by J-Ch. Gregoire */ +/* (INRS Telecom, Quebec, Canada) and D.Zampunieris (Univ.Namur, Belgium) */ + +/* The splay routine code included here is based on the public domain */ +/* version written by D. Sleator <sleator@cs.cmu.edu> in 1992. */ + +static char *Dfa[] = { + "#ifdef MA", + "/*", + "#include <stdio.h>", + "#define uchar unsigned char", + "*/", + "#define ulong unsigned long", + "#define ushort unsigned short", + "", + "#define TWIDTH 256", + "#define HASH(y,n) (n)*(((long)y))", + "#define INRANGE(e,h) ((h>=e->From && h<=e->To)||(e->s==1 && e->S==h))", + "", + "extern char *emalloc(unsigned long); /* imported routine */", + "extern void dfa_init(ushort); /* 4 exported routines */", + "extern int dfa_member(ulong);", + "extern int dfa_store(uchar *);", + "extern void dfa_stats(void);", + "", + "typedef struct Edge {", + " uchar From, To; /* max range 0..255 */", + " uchar s, S; /* if s=1, S is singleton */", + " struct Vertex *Dst;", + " struct Edge *Nxt;", + "} Edge;", + "", + "typedef struct Vertex {", + " ulong key, num; /* key for splay tree, nr incoming edges */", + " uchar from[2], to[2]; /* in-node predefined edge info */", + " struct Vertex *dst[2];/* most nodes have 2 or more edges */", + " struct Edge *Succ; /* in case there are more edges */", + " struct Vertex *lnk, *left, *right; /* splay tree plumbing */", + "} Vertex;", + "", + "static Edge *free_edges;", + "static Vertex *free_vertices;", + "static Vertex **layers; /* one splay tree of nodes per layer */", + "static Vertex **path; /* run of word in the DFA */", + "static Vertex *R, *F, *NF; /* Root, Final, Not-Final */", + "static uchar *word, *lastword;/* string, and last string inserted */", + "static int dfa_depth, iv=0, nv=0, pfrst=0, Tally;", + "", + "static void insert_it(Vertex *, int); /* splay-tree code */", + "static void delete_it(Vertex *, int);", + "static Vertex *find_it(Vertex *, Vertex *, uchar, int);", + "", + "static void", + "recyc_edges(Edge *e)", + "{", + " if (!e) return;", + " recyc_edges(e->Nxt);", + " e->Nxt = free_edges;", + " free_edges = e;", + "}", + "", + "static Edge *", + "new_edge(Vertex *dst)", + "{ Edge *e;", + "", + " if (free_edges)", + " { e = free_edges;", + " free_edges = e->Nxt;", + " e->From = e->To = e->s = e->S = 0;", + " e->Nxt = (Edge *) 0;", + " } else", + " e = (Edge *) emalloc(sizeof(Edge));", + " e->Dst = dst;", + "", + " return e;", + "}", + "", + "static void", + "recyc_vertex(Vertex *v)", + "{", + " recyc_edges(v->Succ);", + " v->Succ = (Edge *) free_vertices;", + " free_vertices = v;", + " nr_states--;", + "}", + "", + "static Vertex *", + "new_vertex(void)", + "{ Vertex *v;", + "", + " if (free_vertices)", + " { v = free_vertices;", + " free_vertices = (Vertex *) v->Succ;", + " v->Succ = (Edge *) 0;", + " v->num = 0;", + " } else", + " v = (Vertex *) emalloc(sizeof(Vertex));", + "", + " nr_states++;", + " return v; ", + "}", + "", + "static Vertex *", + "allDelta(Vertex *v, int n)", + "{ Vertex *dst = new_vertex();", + "", + " v->from[0] = 0;", + " v->to[0] = 255;", + " v->dst[0] = dst;", + " dst->num = 256;", + " insert_it(v, n);", + " return dst;", + "}", + "", + "static void", + "insert_edge(Vertex *v, Edge *e)", + "{ /* put new edge first */", + " if (!v->dst[0])", + " { v->dst[0] = e->Dst;", + " v->from[0] = e->From;", + " v->to[0] = e->To;", + " recyc_edges(e);", + " return;", + " }", + " if (!v->dst[1])", + " { v->from[1] = v->from[0]; v->from[0] = e->From;", + " v->to[1] = v->to[0]; v->to[0] = e->To;", + " v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;", + " recyc_edges(e);", + " return;", + " } /* shift */", + " { int f = v->from[1];", + " int t = v->to[1];", + " Vertex *d = v->dst[1];", + " v->from[1] = v->from[0]; v->from[0] = e->From;", + " v->to[1] = v->to[0]; v->to[0] = e->To;", + " v->dst[1] = v->dst[0]; v->dst[0] = e->Dst;", + " e->From = f;", + " e->To = t;", + " e->Dst = d;", + " }", + " e->Nxt = v->Succ;", + " v->Succ = e;", + "}", + "", + "static void", + "copyRecursive(Vertex *v, Edge *e)", + "{ Edge *f;", + " if (e->Nxt) copyRecursive(v, e->Nxt);", + " f = new_edge(e->Dst);", + " f->From = e->From;", + " f->To = e->To;", + " f->s = e->s;", + " f->S = e->S;", + " f->Nxt = v->Succ;", + " v->Succ = f;", + "}", + "", + "static void", + "copyEdges(Vertex *to, Vertex *from)", + "{ int i;", + " for (i = 0; i < 2; i++)", + " { to->from[i] = from->from[i];", + " to->to[i] = from->to[i];", + " to->dst[i] = from->dst[i];", + " }", + " if (from->Succ) copyRecursive(to, from->Succ);", + "}", + "", + "static Edge *", + "cacheDelta(Vertex *v, int h, int first)", + "{ static Edge *ov, tmp; int i;", + "", + " if (!first && INRANGE(ov,h))", + " return ov; /* intercepts about 10%% */", + " for (i = 0; i < 2; i++)", + " if (v->dst[i] && h >= v->from[i] && h <= v->to[i])", + " { tmp.From = v->from[i];", + " tmp.To = v->to[i];", + " tmp.Dst = v->dst[i];", + " tmp.s = tmp.S = 0;", + " ov = &tmp;", + " return ov;", + " }", + " for (ov = v->Succ; ov; ov = ov->Nxt)", + " if (INRANGE(ov,h)) return ov;", + "", + " Uerror(\"cannot get here, cacheDelta\");", + " return (Edge *) 0;", + "}", + "", + "static Vertex *", + "Delta(Vertex *v, int h) /* v->delta[h] */", + "{ Edge *e;", + "", + " if (v->dst[0] && h >= v->from[0] && h <= v->to[0])", + " return v->dst[0]; /* oldest edge */", + " if (v->dst[1] && h >= v->from[1] && h <= v->to[1])", + " return v->dst[1];", + " for (e = v->Succ; e; e = e->Nxt)", + " if (INRANGE(e,h))", + " return e->Dst;", + " Uerror(\"cannot happen Delta\");", + " return (Vertex *) 0;", + "}", + "", + "static void", + "numDelta(Vertex *v, int d)", + "{ Edge *e;", + " ulong cnt;", + " int i;", + "", + " for (i = 0; i < 2; i++)", + " if (v->dst[i])", + " { cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]);", + " if (d == 1 && cnt < v->dst[i]->num) goto bad;", + " v->dst[i]->num = cnt;", + " }", + " for (e = v->Succ; e; e = e->Nxt)", + " { cnt = e->Dst->num + d*(1 + e->To - e->From + e->s);", + " if (d == 1 && cnt < e->Dst->num)", + "bad: Uerror(\"too many incoming edges\");", + " e->Dst->num = cnt;", + " }", + "}", + "", + "static void", + "setDelta(Vertex *v, int h, Vertex *newdst) /* v->delta[h] = newdst; */", + "{ Edge *e, *f = (Edge *) 0, *g;", + " int i;", + "", + " /* remove the old entry, if there */", + " for (i = 0; i < 2; i++)", + " if (v->dst[i] && h >= v->from[i] && h <= v->to[i])", + " { if (h == v->from[i])", + " { if (h == v->to[i])", + " { v->dst[i] = (Vertex *) 0;", + " v->from[i] = v->to[i] = 0;", + " } else", + " v->from[i]++;", + " } else if (h == v->to[i])", + " { v->to[i]--;", + " } else", + " { g = new_edge(v->dst[i]);/* same dst */", + " g->From = v->from[i];", + " g->To = h-1; /* left half */", + " v->from[i] = h+1; /* right half */", + " insert_edge(v, g);", + " }", + " goto part2;", + " }", + " for (e = v->Succ; e; f = e, e = e->Nxt)", + " { if (e->s == 1 && e->S == h)", + " { e->s = e->S = 0;", + " goto rem_tst;", + " }", + " if (h >= e->From && h <= e->To)", + " { if (h == e->From)", + " { if (h == e->To)", + " { if (e->s)", + " { e->From = e->To = e->S;", + " e->s = 0;", + " break;", + " } else", + " goto rem_do;", + " } else", + " e->From++;", + " } else if (h == e->To)", + " { e->To--;", + " } else /* split */", + " { g = new_edge(e->Dst); /* same dst */", + " g->From = e->From;", + " g->To = h-1; /* g=left half */", + " e->From = h+1; /* e=right half */", + " g->Nxt = e->Nxt; /* insert g */", + " e->Nxt = g; /* behind e */", + " break; /* done */", + " }", + "", + "rem_tst: if (e->From > e->To)", + " { if (e->s == 0) {", + "rem_do: if (f)", + " f->Nxt = e->Nxt;", + " else", + " v->Succ = e->Nxt;", + " e->Nxt = (Edge *) 0;", + " recyc_edges(e);", + " } else", + " { e->From = e->To = e->S;", + " e->s = 0;", + " } }", + " break;", + " } }", + "part2:", + " /* check if newdst is already there */", + " for (i = 0; i < 2; i++)", + " if (v->dst[i] == newdst)", + " { if (h+1 == (int) v->from[i])", + " { v->from[i] = h;", + " return;", + " }", + " if (h == (int) v->to[i]+1)", + " { v->to[i] = h;", + " return;", + " } }", + " for (e = v->Succ; e; e = e->Nxt)", + " { if (e->Dst == newdst)", + " { if (h+1 == (int) e->From)", + " { e->From = h;", + " if (e->s == 1 && e->S+1 == e->From)", + " { e->From = e->S;", + " e->s = e->S = 0;", + " }", + " return;", + " }", + " if (h == (int) e->To+1)", + " { e->To = h;", + " if (e->s == 1 && e->S == e->To+1)", + " { e->To = e->S;", + " e->s = e->S = 0;", + " }", + " return;", + " }", + " if (e->s == 0)", + " { e->s = 1;", + " e->S = h;", + " return;", + " } } }", + " /* add as a new edge */", + " e = new_edge(newdst);", + " e->From = e->To = h;", + " insert_edge(v, e);", + "}", + "", + "static ulong", + "cheap_key(Vertex *v)", + "{ ulong vk2 = 0;", + "", + " if (v->dst[0])", + " { vk2 = (ulong) v->dst[0];", + " if ((ulong) v->dst[1] > vk2)", + " vk2 = (ulong) v->dst[1];", + " } else if (v->dst[1])", + " vk2 = (ulong) v->dst[1]; ", + " if (v->Succ)", + " { Edge *e;", + " for (e = v->Succ; e; e = e->Nxt)", + " if ((ulong) e->Dst > vk2)", + " vk2 = (ulong) e->Dst;", + " }", + " Tally = (vk2>>2)&(TWIDTH-1);", + " return v->key;", + "}", + "", + "static ulong", + "mk_key(Vertex *v) /* not sensitive to order */", + "{ ulong m = 0, vk2 = 0;", + " Edge *e;", + "", + " if (v->dst[0])", + " { m += HASH(v->dst[0], v->to[0] - v->from[0] + 1);", + " vk2 = (ulong) v->dst[0]; ", + " }", + " if (v->dst[1])", + " { m += HASH(v->dst[1], v->to[1] - v->from[1] + 1);", + " if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1]; ", + " }", + " for (e = v->Succ; e; e = e->Nxt)", + " { m += HASH(e->Dst, e->To - e->From + 1 + e->s);", + " if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst; ", + " }", + " Tally = (vk2>>2)&(TWIDTH-1);", + " return m;", + "}", + "", + "static ulong", + "mk_special(int sigma, Vertex *n, Vertex *v)", + "{ ulong m = 0, vk2 = 0;", + " Edge *f;", + " int i;", + "", + " for (i = 0; i < 2; i++)", + " if (v->dst[i])", + " { if (sigma >= v->from[i] && sigma <= v->to[i])", + " { m += HASH(v->dst[i], v->to[i]-v->from[i]);", + " if ((ulong) v->dst[i] > vk2", + " && v->to[i] > v->from[i])", + " vk2 = (ulong) v->dst[i]; ", + " } else", + " { m += HASH(v->dst[i], v->to[i]-v->from[i]+1);", + " if ((ulong) v->dst[i] > vk2)", + " vk2 = (ulong) v->dst[i]; ", + " } }", + " for (f = v->Succ; f; f = f->Nxt)", + " { if (sigma >= f->From && sigma <= f->To)", + " { m += HASH(f->Dst, f->To - f->From + f->s);", + " if ((ulong) f->Dst > vk2", + " && f->To - f->From + f->s > 0)", + " vk2 = (ulong) f->Dst; ", + " } else if (f->s == 1 && sigma == f->S)", + " { m += HASH(f->Dst, f->To - f->From + 1);", + " if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; ", + " } else", + " { m += HASH(f->Dst, f->To - f->From + 1 + f->s);", + " if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; ", + " } }", + "", + " if ((ulong) n > vk2) vk2 = (ulong) n; ", + " Tally = (vk2>>2)&(TWIDTH-1);", + " m += HASH(n, 1);", + " return m;", + "}", + "", + "void ", + "dfa_init(ushort nr_layers)", + "{ int i; Vertex *r, *t;", + "", + " dfa_depth = nr_layers; /* one byte per layer */", + " path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));", + " layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));", + " lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));", + " lastword[dfa_depth] = lastword[0] = 255;", + " path[0] = R = new_vertex(); F = new_vertex();", + "", + " for (i = 1, r = R; i < dfa_depth; i++, r = t)", + " t = allDelta(r, i-1);", + " NF = allDelta(r, i-1);", + "}", + "", + "#if 0", + "static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; }", + "#endif", + "", + "double", + "tree_stats(Vertex *t)", + "{ Edge *e; double cnt=0.0;", + " if (!t) return 0;", + " if (!t->key) return 0;", + " t->key = 0; /* precaution */", + " if (t->dst[0]) cnt++;", + " if (t->dst[1]) cnt++;", + " for (e = t->Succ; e; e = e->Nxt)", + " cnt++;", + " cnt += tree_stats(t->lnk);", + " cnt += tree_stats(t->left);", + " cnt += tree_stats(t->right);", + " return cnt;", + "}", + "", + "void", + "dfa_stats(void)", + "{ int i, j; double cnt = 0.0;", + " for (j = 0; j < TWIDTH; j++)", + " for (i = 0; i < dfa_depth+1; i++)", + " cnt += tree_stats(layers[i*TWIDTH+j]);", + " printf(\"Minimized Automaton:\t%%6d nodes and %%6g edges\\n\",", + " nr_states, cnt);", + "}", + "", + "int", + "dfa_member(ulong n)", + "{ Vertex **p, **q;", + " uchar *w = &word[n];", + " int i;", + "", + " p = &path[n]; q = (p+1);", + " for (i = n; i < dfa_depth; i++)", + " *q++ = Delta(*p++, *w++);", + " return (*p == F);", + "}", + "", + "int", + "dfa_store(uchar *sv)", + "{ Vertex **p, **q, *s, *y, *old, *new = F;", + " uchar *w, *u = lastword;", + " int i, j, k;", + "", + " w = word = sv;", + " while (*w++ == *u++) /* find first byte that differs */", + " ;", + " pfrst = (int) (u - lastword) - 1;", + " memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst);", + " if (pfrst > iv) pfrst = iv;", + " if (pfrst > nv) pfrst = nv;", + "/* phase1: */", + " p = &path[pfrst]; q = (p+1); w = &word[pfrst];", + " for (i = pfrst; i < dfa_depth; i++)", + " *q++ = Delta(*p++, *w++); /* (*p)->delta[*w++]; */", + "", + " if (*p == F) return 1; /* it's already there */", + "/* phase2: */", + " iv = dfa_depth;", + " do { iv--;", + " old = new;", + " new = find_it(path[iv], old, word[iv], iv);", + " } while (new && iv > 0);", + "", + "/* phase3: */", + " nv = k = 0; s = path[0];", + " for (j = 1; j <= iv; ++j) ", + " if (path[j]->num > 1)", + " { y = new_vertex();", + " copyEdges(y, path[j]);", + " insert_it(y, j);", + " numDelta(y, 1);", + " delete_it(s, j-1);", + " setDelta(s, word[j-1], y);", + " insert_it(s, j-1);", + " y->num = 1; /* initial value 1 */", + " s = y;", + " path[j]->num--; /* only 1 moved from j to y */", + " k = 1;", + " } else", + " { s = path[j];", + " if (!k) nv = j;", + " }", + " y = Delta(s, word[iv]);", + " y->num--;", + " delete_it(s, iv); ", + " setDelta(s, word[iv], old);", + " insert_it(s, iv); ", + " old->num++;", + "", + " for (j = iv+1; j < dfa_depth; j++)", + " if (path[j]->num == 0)", + " { numDelta(path[j], -1);", + " delete_it(path[j], j);", + " recyc_vertex(path[j]);", + " } else", + " break;", + " return 0;", + "}", + "", + "static Vertex *", + "splay(ulong i, Vertex *t)", + "{ Vertex N, *l, *r, *y;", + "", + " if (!t) return t;", + " N.left = N.right = (Vertex *) 0;", + " l = r = &N;", + " for (;;)", + " { if (i < t->key)", + " { if (!t->left) break;", + " if (i < t->left->key)", + " { y = t->left;", + " t->left = y->right;", + " y->right = t;", + " t = y;", + " if (!t->left) break;", + " }", + " r->left = t;", + " r = t;", + " t = t->left;", + " } else if (i > t->key)", + " { if (!t->right) break;", + " if (i > t->right->key)", + " { y = t->right;", + " t->right = y->left;", + " y->left = t;", + " t = y;", + " if (!t->right) break;", + " }", + " l->right = t;", + " l = t;", + " t = t->right;", + " } else", + " break;", + " }", + " l->right = t->left;", + " r->left = t->right;", + " t->left = N.right;", + " t->right = N.left;", + " return t;", + "}", + "", + "static void", + "insert_it(Vertex *v, int L)", + "{ Vertex *new, *t;", + " ulong i; int nr;", + "", + " i = mk_key(v);", + " nr = ((L*TWIDTH)+Tally);", + " t = layers[nr];", + "", + " v->key = i; ", + " if (!t)", + " { layers[nr] = v;", + " return;", + " }", + " t = splay(i, t);", + " if (i < t->key)", + " { new = v;", + " new->left = t->left;", + " new->right = t;", + " t->left = (Vertex *) 0;", + " } else if (i > t->key)", + " { new = v;", + " new->right = t->right;", + " new->left = t;", + " t->right = (Vertex *) 0;", + " } else /* it's already there */", + " { v->lnk = t->lnk; /* put in linked list off v */", + " t->lnk = v;", + " new = t;", + " }", + " layers[nr] = new;", + "}", + "", + "static int", + "checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma)", + "{ Edge *g, *f;", + " int i, k, j = 1;", + "", + " for (k = 0; k < 2; k++)", + " if (h->dst[k])", + " { if (sigma >= h->from[k] && sigma <= h->to[k])", + " { if (h->dst[k] != n) goto no_match;", + " }", + " for (i = h->from[k]; i <= h->to[k]; i++)", + " { if (i == sigma) continue;", + " g = cacheDelta(v, i, j); j = 0;", + " if (h->dst[k] != g->Dst)", + " goto no_match;", + " if (g->s == 0 || g->S != i)", + " i = g->To;", + " } }", + " for (f = h->Succ; f; f = f->Nxt)", + " { if (INRANGE(f,sigma))", + " { if (f->Dst != n) goto no_match;", + " }", + " for (i = f->From; i <= f->To; i++)", + " { if (i == sigma) continue;", + " g = cacheDelta(v, i, j); j = 0;", + " if (f->Dst != g->Dst)", + " goto no_match;", + " if (g->s == 1 && i == g->S)", + " continue;", + " i = g->To;", + " }", + " if (f->s && f->S != sigma)", + " { g = cacheDelta(v, f->S, 1);", + " if (f->Dst != g->Dst)", + " goto no_match;", + " }", + " }", + " if (h->Succ || h->dst[0] || h->dst[1]) return 1;", + "no_match:", + " return 0;", + "}", + "", + "static Vertex *", + "find_it(Vertex *v, Vertex *n, uchar sigma, int L)", + "{ Vertex *z, *t;", + " ulong i; int nr;", + "", + " i = mk_special(sigma,n,v);", + " nr = ((L*TWIDTH)+Tally);", + " t = layers[nr];", + "", + " if (!t) return (Vertex *) 0;", + " layers[nr] = t = splay(i, t);", + " if (i == t->key)", + " for (z = t; z; z = z->lnk)", + " if (checkit(z, v, n, sigma))", + " return z;", + "", + " return (Vertex *) 0;", + "}", + "", + "static void", + "delete_it(Vertex *v, int L)", + "{ Vertex *x, *t;", + " ulong i; int nr;", + "", + " i = cheap_key(v);", + " nr = ((L*TWIDTH)+Tally);", + " t = layers[nr];", + " if (!t) return;", + "", + " t = splay(i, t);", + " if (i == t->key)", + " { Vertex *z, *y = (Vertex *) 0;", + " for (z = t; z && z != v; y = z, z = z->lnk)", + " ;", + " if (z != v) goto bad;", + " if (y)", + " { y->lnk = z->lnk;", + " z->lnk = (Vertex *) 0;", + " layers[nr] = t;", + " return;", + " } else if (z->lnk) /* z == t == v */", + " { y = z->lnk;", + " y->left = t->left;", + " y->right = t->right;", + " t->left = t->right = t->lnk = (Vertex *) 0;", + " layers[nr] = y;", + " return;", + " }", + " /* delete the node itself */", + " if (!t->left)", + " { x = t->right;", + " } else", + " { x = splay(i, t->left);", + " x->right = t->right;", + " }", + " t->left = t->right = t->lnk = (Vertex *) 0;", + " layers[nr] = x;", + " return;", + " }", + "bad: Uerror(\"cannot happen delete\");", + "}", + "#endif", /* MA */ + 0, +}; diff --git a/sys/src/cmd/spin/pangen5.c b/sys/src/cmd/spin/pangen5.c new file mode 100755 index 000000000..25885e19c --- /dev/null +++ b/sys/src/cmd/spin/pangen5.c @@ -0,0 +1,857 @@ +/***** spin: pangen5.c *****/ + +/* Copyright (c) 1999-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +typedef struct BuildStack { + FSM_trans *t; + struct BuildStack *nxt; +} BuildStack; + +extern ProcList *rdy; +extern int verbose, eventmapnr, claimnr, rvopt, export_ast, u_sync; +extern Element *Al_El; + +static FSM_state *fsm_free; +static FSM_trans *trans_free; +static BuildStack *bs, *bf; +static int max_st_id; +static int cur_st_id; +int o_max; +FSM_state *fsm; +FSM_state **fsm_tbl; +FSM_use *use_free; + +static void ana_seq(Sequence *); +static void ana_stmnt(FSM_trans *, Lextok *, int); + +extern void AST_slice(void); +extern void AST_store(ProcList *, int); +extern int has_global(Lextok *); +extern void exit(int); + +static void +fsm_table(void) +{ FSM_state *f; + max_st_id += 2; + /* fprintf(stderr, "omax %d, max=%d\n", o_max, max_st_id); */ + if (o_max < max_st_id) + { o_max = max_st_id; + fsm_tbl = (FSM_state **) emalloc(max_st_id * sizeof(FSM_state *)); + } else + memset((char *)fsm_tbl, 0, max_st_id * sizeof(FSM_state *)); + cur_st_id = max_st_id; + max_st_id = 0; + + for (f = fsm; f; f = f->nxt) + fsm_tbl[f->from] = f; +} + +static int +FSM_DFS(int from, FSM_use *u) +{ FSM_state *f; + FSM_trans *t; + FSM_use *v; + int n; + + if (from == 0) + return 1; + + f = fsm_tbl[from]; + + if (!f) + { printf("cannot find state %d\n", from); + fatal("fsm_dfs: cannot happen\n", (char *) 0); + } + + if (f->seen) + return 1; + f->seen = 1; + + for (t = f->t; t; t = t->nxt) + { + for (n = 0; n < 2; n++) + for (v = t->Val[n]; v; v = v->nxt) + if (u->var == v->var) + return n; /* a read or write */ + + if (!FSM_DFS(t->to, u)) + return 0; + } + return 1; +} + +static void +new_dfs(void) +{ int i; + + for (i = 0; i < cur_st_id; i++) + if (fsm_tbl[i]) + fsm_tbl[i]->seen = 0; +} + +static int +good_dead(Element *e, FSM_use *u) +{ + switch (u->special) { + case 2: /* ok if it's a receive */ + if (e->n->ntyp == ASGN + && e->n->rgt->ntyp == CONST + && e->n->rgt->val == 0) + return 0; + break; + case 1: /* must be able to use oval */ + if (e->n->ntyp != 'c' + && e->n->ntyp != 'r') + return 0; /* can't really happen */ + break; + } + return 1; +} + +#if 0 +static int howdeep = 0; +#endif + +static int +eligible(FSM_trans *v) +{ Element *el = ZE; + Lextok *lt = ZN; + + if (v) el = v->step; + if (el) lt = v->step->n; + + if (!lt /* dead end */ + || v->nxt /* has alternatives */ + || el->esc /* has an escape */ + || (el->status&CHECK2) /* remotely referenced */ + || lt->ntyp == ATOMIC + || lt->ntyp == NON_ATOMIC /* used for inlines -- should be able to handle this */ + || lt->ntyp == IF + || lt->ntyp == C_CODE + || lt->ntyp == C_EXPR + || has_lab(el, 0) /* any label at all */ + + || lt->ntyp == DO + || lt->ntyp == UNLESS + || lt->ntyp == D_STEP + || lt->ntyp == ELSE + || lt->ntyp == '@' + || lt->ntyp == 'c' + || lt->ntyp == 'r' + || lt->ntyp == 's') + return 0; + + if (!(el->status&(2|4))) /* not atomic */ + { int unsafe = (el->status&I_GLOB)?1:has_global(el->n); + if (unsafe) + return 0; + } + + return 1; +} + +static int +canfill_in(FSM_trans *v) +{ Element *el = v->step; + Lextok *lt = v->step->n; + + if (!lt /* dead end */ + || v->nxt /* has alternatives */ + || el->esc /* has an escape */ + || (el->status&CHECK2)) /* remotely referenced */ + return 0; + + if (!(el->status&(2|4)) /* not atomic */ + && ((el->status&I_GLOB) + || has_global(el->n))) /* and not safe */ + return 0; + + return 1; +} + +static int +pushbuild(FSM_trans *v) +{ BuildStack *b; + + for (b = bs; b; b = b->nxt) + if (b->t == v) + return 0; + if (bf) + { b = bf; + bf = bf->nxt; + } else + b = (BuildStack *) emalloc(sizeof(BuildStack)); + b->t = v; + b->nxt = bs; + bs = b; + return 1; +} + +static void +popbuild(void) +{ BuildStack *f; + if (!bs) + fatal("cannot happen, popbuild", (char *) 0); + f = bs; + bs = bs->nxt; + f->nxt = bf; + bf = f; /* freelist */ +} + +static int +build_step(FSM_trans *v) +{ FSM_state *f; + Element *el = v->step; +#if 0 + Lextok *lt = ZN; +#endif + int st = v->to; + int r; + + if (!el) return -1; + + if (v->step->merge) + return v->step->merge; /* already done */ + + if (!eligible(v)) /* non-blocking */ + return -1; + + if (!pushbuild(v)) /* cycle detected */ + return -1; /* break cycle */ + + f = fsm_tbl[st]; +#if 0 + lt = v->step->n; + if (verbose&32) + { if (++howdeep == 1) + printf("spin: %s, line %3d, merge:\n", + lt->fn->name, + lt->ln); + printf("\t[%d] <seqno %d>\t", howdeep, el->seqno); + comment(stdout, lt, 0); + printf(";\n"); + } +#endif + r = build_step(f->t); + v->step->merge = (r == -1) ? st : r; +#if 0 + if (verbose&32) + { printf(" merge value: %d (st=%d,r=%d, line %d)\n", + v->step->merge, st, r, el->n->ln); + howdeep--; + } +#endif + popbuild(); + + return v->step->merge; +} + +static void +FSM_MERGER(char *pname_unused) /* find candidates for safely merging steps */ +{ FSM_state *f, *g; + FSM_trans *t; + Lextok *lt; + + for (f = fsm; f; f = f->nxt) /* all states */ + for (t = f->t; t; t = t->nxt) /* all edges */ + { if (!t->step) continue; /* happens with 'unless' */ + + t->step->merge_in = f->in; /* ?? */ + + if (t->step->merge) + continue; + lt = t->step->n; + + if (lt->ntyp == 'c' + || lt->ntyp == 'r' + || lt->ntyp == 's') /* blocking stmnts */ + continue; /* handled in 2nd scan */ + + if (!eligible(t)) + continue; + + g = fsm_tbl[t->to]; + if (!eligible(g->t)) + { +#define SINGLES +#ifdef SINGLES + t->step->merge_single = t->to; +#if 0 + if ((verbose&32)) + { printf("spin: %s, line %3d, merge_single:\n\t<seqno %d>\t", + t->step->n->fn->name, + t->step->n->ln, + t->step->seqno); + comment(stdout, t->step->n, 0); + printf(";\n"); + } +#endif +#endif + /* t is an isolated eligible step: + * + * a merge_start can connect to a proper + * merge chain or to a merge_single + * a merge chain can be preceded by + * a merge_start, but not by a merge_single + */ + + continue; + } + + (void) build_step(t); + } + + /* 2nd scan -- find possible merge_starts */ + + for (f = fsm; f; f = f->nxt) /* all states */ + for (t = f->t; t; t = t->nxt) /* all edges */ + { if (!t->step || t->step->merge) + continue; + + lt = t->step->n; +#if 0 + 4.1.3: + an rv send operation inside an atomic, *loses* atomicity + when executed + and should therefore never be merged with a subsequent + statement within the atomic sequence + the same is not true for non-rv send operations +#endif + + if (lt->ntyp == 'c' /* potentially blocking stmnts */ + || lt->ntyp == 'r' + || (lt->ntyp == 's' && u_sync == 0)) /* added !u_sync in 4.1.3 */ + { if (!canfill_in(t)) /* atomic, non-global, etc. */ + continue; + + g = fsm_tbl[t->to]; + if (!g || !g->t || !g->t->step) + continue; + if (g->t->step->merge) + t->step->merge_start = g->t->step->merge; +#ifdef SINGLES + else if (g->t->step->merge_single) + t->step->merge_start = g->t->step->merge_single; +#endif +#if 0 + if ((verbose&32) + && t->step->merge_start) + { printf("spin: %s, line %3d, merge_START:\n\t<seqno %d>\t", + lt->fn->name, + lt->ln, + t->step->seqno); + comment(stdout, lt, 0); + printf(";\n"); + } +#endif + } + } +} + +static void +FSM_ANA(void) +{ FSM_state *f; + FSM_trans *t; + FSM_use *u, *v, *w; + int n; + + for (f = fsm; f; f = f->nxt) /* all states */ + for (t = f->t; t; t = t->nxt) /* all edges */ + for (n = 0; n < 2; n++) /* reads and writes */ + for (u = t->Val[n]; u; u = u->nxt) + { if (!u->var->context /* global */ + || u->var->type == CHAN + || u->var->type == STRUCT) + continue; + new_dfs(); + if (FSM_DFS(t->to, u)) /* cannot hit read before hitting write */ + u->special = n+1; /* means, reset to 0 after use */ + } + + if (!export_ast) + for (f = fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + for (n = 0; n < 2; n++) + for (u = t->Val[n], w = (FSM_use *) 0; u; ) + { if (u->special) + { v = u->nxt; + if (!w) /* remove from list */ + t->Val[n] = v; + else + w->nxt = v; +#if q + if (verbose&32) + { printf("%s : %3d: %d -> %d \t", + t->step->n->fn->name, + t->step->n->ln, + f->from, + t->to); + comment(stdout, t->step->n, 0); + printf("\t%c%d: %s\n", n==0?'R':'L', + u->special, u->var->name); + } +#endif + if (good_dead(t->step, u)) + { u->nxt = t->step->dead; /* insert into dead */ + t->step->dead = u; + } + u = v; + } else + { w = u; + u = u->nxt; + } } +} + +void +rel_use(FSM_use *u) +{ + if (!u) return; + rel_use(u->nxt); + u->var = (Symbol *) 0; + u->special = 0; + u->nxt = use_free; + use_free = u; +} + +static void +rel_trans(FSM_trans *t) +{ + if (!t) return; + rel_trans(t->nxt); + rel_use(t->Val[0]); + rel_use(t->Val[1]); + t->Val[0] = t->Val[1] = (FSM_use *) 0; + t->nxt = trans_free; + trans_free = t; +} + +static void +rel_state(FSM_state *f) +{ + if (!f) return; + rel_state(f->nxt); + rel_trans(f->t); + f->t = (FSM_trans *) 0; + f->nxt = fsm_free; + fsm_free = f; +} + +static void +FSM_DEL(void) +{ + rel_state(fsm); + fsm = (FSM_state *) 0; +} + +static FSM_state * +mkstate(int s) +{ FSM_state *f; + + /* fsm_tbl isn't allocated yet */ + for (f = fsm; f; f = f->nxt) + if (f->from == s) + break; + if (!f) + { if (fsm_free) + { f = fsm_free; + memset(f, 0, sizeof(FSM_state)); + fsm_free = fsm_free->nxt; + } else + f = (FSM_state *) emalloc(sizeof(FSM_state)); + f->from = s; + f->t = (FSM_trans *) 0; + f->nxt = fsm; + fsm = f; + if (s > max_st_id) + max_st_id = s; + } + return f; +} + +static FSM_trans * +get_trans(int to) +{ FSM_trans *t; + + if (trans_free) + { t = trans_free; + memset(t, 0, sizeof(FSM_trans)); + trans_free = trans_free->nxt; + } else + t = (FSM_trans *) emalloc(sizeof(FSM_trans)); + + t->to = to; + return t; +} + +static void +FSM_EDGE(int from, int to, Element *e) +{ FSM_state *f; + FSM_trans *t; + + f = mkstate(from); /* find it or else make it */ + t = get_trans(to); + + t->step = e; + t->nxt = f->t; + f->t = t; + + f = mkstate(to); + f->in++; + + if (export_ast) + { t = get_trans(from); + t->step = e; + t->nxt = f->p; /* from is a predecessor of to */ + f->p = t; + } + + if (t->step) + ana_stmnt(t, t->step->n, 0); +} + +#define LVAL 1 +#define RVAL 0 + +static void +ana_var(FSM_trans *t, Lextok *now, int usage) +{ FSM_use *u, *v; + + if (!t || !now || !now->sym) + return; + + if (now->sym->name[0] == '_' + && (strcmp(now->sym->name, "_") == 0 + || strcmp(now->sym->name, "_pid") == 0 + || strcmp(now->sym->name, "_last") == 0)) + return; + + v = t->Val[usage]; + for (u = v; u; u = u->nxt) + if (u->var == now->sym) + return; /* it's already there */ + + if (!now->lft) + { /* not for array vars -- it's hard to tell statically + if the index would, at runtime, evaluate to the + same values at lval and rval references + */ + if (use_free) + { u = use_free; + use_free = use_free->nxt; + } else + u = (FSM_use *) emalloc(sizeof(FSM_use)); + + u->var = now->sym; + u->nxt = t->Val[usage]; + t->Val[usage] = u; + } else + ana_stmnt(t, now->lft, RVAL); /* index */ + + if (now->sym->type == STRUCT + && now->rgt + && now->rgt->lft) + ana_var(t, now->rgt->lft, usage); +} + +static void +ana_stmnt(FSM_trans *t, Lextok *now, int usage) +{ Lextok *v; + + if (!t || !now) return; + + switch (now->ntyp) { + case '.': + case BREAK: + case GOTO: + case CONST: + case TIMEOUT: + case NONPROGRESS: + case ELSE: + case '@': + case 'q': + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + case C_CODE: + case C_EXPR: + break; + + case '!': + case UMIN: + case '~': + case ENABLED: + case PC_VAL: + case LEN: + case FULL: + case EMPTY: + case NFULL: + case NEMPTY: + case ASSERT: + case 'c': + ana_stmnt(t, now->lft, RVAL); + break; + + case '/': + case '*': + case '-': + case '+': + case '%': + case '&': + case '^': + case '|': + case LT: + case GT: + case LE: + case GE: + case NE: + case EQ: + case OR: + case AND: + case LSHIFT: + case RSHIFT: + ana_stmnt(t, now->lft, RVAL); + ana_stmnt(t, now->rgt, RVAL); + break; + + case ASGN: + ana_stmnt(t, now->lft, LVAL); + ana_stmnt(t, now->rgt, RVAL); + break; + + case PRINT: + case RUN: + for (v = now->lft; v; v = v->rgt) + ana_stmnt(t, v->lft, RVAL); + break; + + case PRINTM: + if (now->lft && !now->lft->ismtyp) + ana_stmnt(t, now->lft, RVAL); + break; + + case 's': + ana_stmnt(t, now->lft, RVAL); + for (v = now->rgt; v; v = v->rgt) + ana_stmnt(t, v->lft, RVAL); + break; + + case 'R': + case 'r': + ana_stmnt(t, now->lft, RVAL); + for (v = now->rgt; v; v = v->rgt) + { if (v->lft->ntyp == EVAL) + ana_stmnt(t, v->lft->lft, RVAL); + else + if (v->lft->ntyp != CONST + && now->ntyp != 'R') /* was v->lft->ntyp */ + ana_stmnt(t, v->lft, LVAL); + } + break; + + case '?': + ana_stmnt(t, now->lft, RVAL); + if (now->rgt) + { ana_stmnt(t, now->rgt->lft, RVAL); + ana_stmnt(t, now->rgt->rgt, RVAL); + } + break; + + case NAME: + ana_var(t, now, usage); + break; + + case 'p': /* remote ref */ + ana_stmnt(t, now->lft->lft, RVAL); /* process id */ + ana_var(t, now, RVAL); + ana_var(t, now->rgt, RVAL); + break; + + default: + printf("spin: bad node type %d line %d (ana_stmnt)\n", now->ntyp, now->ln); + fatal("aborting", (char *) 0); + } +} + +void +ana_src(int dataflow, int merger) /* called from main.c and guided.c */ +{ ProcList *p; + Element *e; +#if 0 + int counter = 1; +#endif + for (p = rdy; p; p = p->nxt) + { if (p->tn == eventmapnr + || p->tn == claimnr) + continue; + + ana_seq(p->s); + fsm_table(); + + e = p->s->frst; +#if 0 + if (dataflow || merger) + { printf("spin: %d, optimizing '%s'", + counter++, p->n->name); + fflush(stdout); + } +#endif + if (dataflow) + { FSM_ANA(); + } + if (merger) + { FSM_MERGER(p->n->name); + huntele(e, e->status, -1)->merge_in = 1; /* start-state */ +#if 0 + printf("\n"); +#endif + } + if (export_ast) + AST_store(p, huntele(e, e->status, -1)->seqno); + + FSM_DEL(); + } + for (e = Al_El; e; e = e->Nxt) + { + if (!(e->status&DONE) && (verbose&32)) + { printf("unreachable code: "); + printf("%s, line %3d: ", + e->n->fn->name, e->n->ln); + comment(stdout, e->n, 0); + printf("\n"); + } + e->status &= ~DONE; + } + if (export_ast) + { AST_slice(); + exit(0); + } +} + +void +spit_recvs(FILE *f1, FILE *f2) /* called from pangen2.c */ +{ Element *e; + Sequence *s; + extern int Unique; + + fprintf(f1, "unsigned char Is_Recv[%d];\n", Unique); + + fprintf(f2, "void\nset_recvs(void)\n{\n"); + for (e = Al_El; e; e = e->Nxt) + { if (!e->n) continue; + + switch (e->n->ntyp) { + case 'r': +markit: fprintf(f2, "\tIs_Recv[%d] = 1;\n", e->Seqno); + break; + case D_STEP: + s = e->n->sl->this; + switch (s->frst->n->ntyp) { + case DO: + fatal("unexpected: do at start of d_step", (char *) 0); + case IF: /* conservative: fall through */ + case 'r': goto markit; + } + break; + } + } + fprintf(f2, "}\n"); + + if (rvopt) + { + fprintf(f2, "int\nno_recvs(int me)\n{\n"); + fprintf(f2, " int h; uchar ot; short tt;\n"); + fprintf(f2, " Trans *t;\n"); + fprintf(f2, " for (h = BASE; h < (int) now._nr_pr; h++)\n"); + fprintf(f2, " { if (h == me) continue;\n"); + fprintf(f2, " tt = (short) ((P0 *)pptr(h))->_p;\n"); + fprintf(f2, " ot = (uchar) ((P0 *)pptr(h))->_t;\n"); + fprintf(f2, " for (t = trans[ot][tt]; t; t = t->nxt)\n"); + fprintf(f2, " if (Is_Recv[t->t_id]) return 0;\n"); + fprintf(f2, " }\n"); + fprintf(f2, " return 1;\n"); + fprintf(f2, "}\n"); + } +} + +static void +ana_seq(Sequence *s) +{ SeqList *h; + Sequence *t; + Element *e, *g; + int From, To; + + for (e = s->frst; e; e = e->nxt) + { if (e->status & DONE) + goto checklast; + + e->status |= DONE; + + From = e->seqno; + + if (e->n->ntyp == UNLESS) + ana_seq(e->sub->this); + else if (e->sub) + { for (h = e->sub; h; h = h->nxt) + { g = huntstart(h->this->frst); + To = g->seqno; + + if (g->n->ntyp != 'c' + || g->n->lft->ntyp != CONST + || g->n->lft->val != 0 + || g->esc) + FSM_EDGE(From, To, e); + /* else it's a dead link */ + } + for (h = e->sub; h; h = h->nxt) + ana_seq(h->this); + } else if (e->n->ntyp == ATOMIC + || e->n->ntyp == D_STEP + || e->n->ntyp == NON_ATOMIC) + { + t = e->n->sl->this; + g = huntstart(t->frst); + t->last->nxt = e->nxt; + To = g->seqno; + FSM_EDGE(From, To, e); + + ana_seq(t); + } else + { if (e->n->ntyp == GOTO) + { g = get_lab(e->n, 1); + g = huntele(g, e->status, -1); + To = g->seqno; + } else if (e->nxt) + { g = huntele(e->nxt, e->status, -1); + To = g->seqno; + } else + To = 0; + + FSM_EDGE(From, To, e); + + if (e->esc + && e->n->ntyp != GOTO + && e->n->ntyp != '.') + for (h = e->esc; h; h = h->nxt) + { g = huntstart(h->this->frst); + To = g->seqno; + FSM_EDGE(From, To, ZE); + ana_seq(h->this); + } + } + +checklast: if (e == s->last) + break; + } +} diff --git a/sys/src/cmd/spin/pangen5.h b/sys/src/cmd/spin/pangen5.h new file mode 100755 index 000000000..5313e73b2 --- /dev/null +++ b/sys/src/cmd/spin/pangen5.h @@ -0,0 +1,424 @@ +/***** spin: pangen5.h *****/ + +/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +static char *Xpt[] = { + "#if defined(MA) && (defined(W_XPT) || defined(R_XPT))", + "static Vertex **temptree;", + "static char wbuf[4096];", + "static int WCNT = 4096, wcnt=0;", + "static uchar stacker[MA+1];", + "static ulong stackcnt = 0;", + "extern double nstates, nlinks, truncs, truncs2;", + "", + "static void", + "xwrite(int fd, char *b, int n)", + "{", + " if (wcnt+n >= 4096)", + " { write(fd, wbuf, wcnt);", + " wcnt = 0;", + " }", + " memcpy(&wbuf[wcnt], b, n);", + " wcnt += n;", + "}", + "", + "static void", + "wclose(fd)", + "{", + " if (wcnt > 0)", + " write(fd, wbuf, wcnt);", + " wcnt = 0;", + " close(fd);", + "}", + "", + "static void", + "w_vertex(int fd, Vertex *v)", + "{ char t[3]; int i; Edge *e;", + "", + " xwrite(fd, (char *) &v, sizeof(Vertex *));", + " t[0] = 0;", + " for (i = 0; i < 2; i++)", + " if (v->dst[i])", + " { t[1] = v->from[i], t[2] = v->to[i];", + " xwrite(fd, t, 3);", + " xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *));", + " }", + " for (e = v->Succ; e; e = e->Nxt)", + " { t[1] = e->From, t[2] = e->To;", + " xwrite(fd, t, 3);", + " xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));", + "", + " if (e->s)", + " { t[1] = t[2] = e->S;", + " xwrite(fd, t, 3);", + " xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));", + " } }", + "}", + "", + "static void", + "w_layer(int fd, Vertex *v)", + "{ uchar c=1;", + "", + " if (!v) return;", + " xwrite(fd, (char *) &c, 1);", + " w_vertex(fd, v);", + " w_layer(fd, v->lnk);", + " w_layer(fd, v->left);", + " w_layer(fd, v->right);", + "}", + "", + "void", + "w_xpoint(void)", + "{ int fd; char nm[64];", + " int i, j; uchar c;", + " static uchar xwarned = 0;", + "", + " sprintf(nm, \"%%s.xpt\", Source);", + " if ((fd = creat(nm, 0666)) <= 0)", + " if (!xwarned)", + " { xwarned = 1;", + " printf(\"cannot creat checkpoint file\\n\");", + " return;", + " }", + " xwrite(fd, (char *) &nstates, sizeof(double));", + " xwrite(fd, (char *) &truncs, sizeof(double));", + " xwrite(fd, (char *) &truncs2, sizeof(double));", + " xwrite(fd, (char *) &nlinks, sizeof(double));", + " xwrite(fd, (char *) &dfa_depth, sizeof(int));", + " xwrite(fd, (char *) &R, sizeof(Vertex *));", + " xwrite(fd, (char *) &F, sizeof(Vertex *));", + " xwrite(fd, (char *) &NF, sizeof(Vertex *));", + "", + " for (j = 0; j < TWIDTH; j++)", + " for (i = 0; i < dfa_depth+1; i++)", + " { w_layer(fd, layers[i*TWIDTH+j]);", + " c = 2; xwrite(fd, (char *) &c, 1);", + " }", + " wclose(fd);", + "}", + "", + "static void", + "xread(int fd, char *b, int n)", + "{ int m = wcnt; int delta = 0;", + " if (m < n)", + " { if (m > 0) memcpy(b, &wbuf[WCNT-m], m);", + " delta = m;", + " WCNT = wcnt = read(fd, wbuf, 4096);", + " if (wcnt < n-m)", + " Uerror(\"xread failed -- insufficient data\");", + " n -= m;", + " }", + " memcpy(&b[delta], &wbuf[WCNT-wcnt], n);", + " wcnt -= n;", + "}", + "", + "static void", + "x_cleanup(Vertex *c)", + "{ Edge *e; /* remove the tree and edges from c */", + " if (!c) return;", + " for (e = c->Succ; e; e = e->Nxt)", + " x_cleanup(e->Dst);", + " recyc_vertex(c);", + "}", + "", + "static void", + "x_remove(void)", + "{ Vertex *tmp; int i, s;", + " int r, j;", + " /* double-check: */", + " stacker[dfa_depth-1] = 0; r = dfa_store(stacker);", + " stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1);", + " if (r != 1 || j != 0)", + " { printf(\"%%d: \", stackcnt);", + " for (i = 0; i < dfa_depth; i++)", + " printf(\"%%d,\", stacker[i]);", + " printf(\" -- not a stackstate <o:%%d,4:%%d>\\n\", r, j);", + " return;", + " }", + " stacker[dfa_depth-1] = 1;", + " s = dfa_member(dfa_depth-1);", + "", + " { tmp = F; F = NF; NF = tmp; } /* complement */", + " if (s) dfa_store(stacker);", + " stacker[dfa_depth-1] = 0;", + " dfa_store(stacker);", + " stackcnt++;", + " { tmp = F; F = NF; NF = tmp; }", + "}", + "", + "static void", + "x_rm_stack(Vertex *t, int k)", + "{ int j; Edge *e;", + "", + " if (k == 0)", + " { x_remove();", + " return;", + " }", + " if (t)", + " for (e = t->Succ; e; e = e->Nxt)", + " { for (j = e->From; j <= (int) e->To; j++)", + " { stacker[k] = (uchar) j;", + " x_rm_stack(e->Dst, k-1);", + " }", + " if (e->s)", + " { stacker[k] = e->S;", + " x_rm_stack(e->Dst, k-1);", + " } }", + "}", + "", + "static Vertex *", + "insert_withkey(Vertex *v, int L)", + "{ Vertex *new, *t = temptree[L];", + "", + " if (!t) { temptree[L] = v; return v; }", + " t = splay(v->key, t);", + " if (v->key < t->key)", + " { new = v;", + " new->left = t->left;", + " new->right = t;", + " t->left = (Vertex *) 0;", + " } else if (v->key > t->key)", + " { new = v;", + " new->right = t->right;", + " new->left = t;", + " t->right = (Vertex *) 0;", + " } else", + " { if (t != R && t != F && t != NF)", + " Uerror(\"double insert, bad checkpoint data\");", + " else", + " { recyc_vertex(v);", + " new = t;", + " } }", + " temptree[L] = new;", + "", + " return new;", + "}", + "", + "static Vertex *", + "find_withkey(Vertex *v, int L)", + "{ Vertex *t = temptree[L];", + " if (t)", + " { temptree[L] = t = splay((ulong) v, t);", + " if (t->key == (ulong) v)", + " return t;", + " }", + " Uerror(\"not found error, bad checkpoint data\");", + " return (Vertex *) 0;", + "}", + "", + "void", + "r_layer(int fd, int n)", + "{ Vertex *v;", + " Edge *e;", + " char c, t[2];", + "", + " for (;;)", + " { xread(fd, &c, 1);", + " if (c == 2) break;", + " if (c == 1)", + " { v = new_vertex();", + " xread(fd, (char *) &(v->key), sizeof(Vertex *));", + " v = insert_withkey(v, n);", + " } else /* c == 0 */", + " { e = new_edge((Vertex *) 0);", + " xread(fd, t, 2);", + " e->From = t[0];", + " e->To = t[1];", + " xread(fd, (char *) &(e->Dst), sizeof(Vertex *));", + " insert_edge(v, e);", + " } }", + "}", + "", + "static void", + "v_fix(Vertex *t, int nr)", + "{ int i; Edge *e;", + "", + " if (!t) return;", + "", + " for (i = 0; i < 2; i++)", + " if (t->dst[i])", + " t->dst[i] = find_withkey(t->dst[i], nr);", + "", + " for (e = t->Succ; e; e = e->Nxt)", + " e->Dst = find_withkey(e->Dst, nr);", + " ", + " v_fix(t->left, nr);", + " v_fix(t->right, nr);", + "}", + "", + "static void", + "v_insert(Vertex *t, int nr)", + "{ Edge *e; int i;", + "", + " if (!t) return;", + " v_insert(t->left, nr);", + " v_insert(t->right, nr);", + "", + " /* remove only leafs from temptree */", + " t->left = t->right = t->lnk = (Vertex *) 0;", + " insert_it(t, nr); /* into layers */", + " for (i = 0; i < 2; i++)", + " if (t->dst[i])", + " t->dst[i]->num += (t->to[i] - t->from[i] + 1);", + " for (e = t->Succ; e; e = e->Nxt)", + " e->Dst->num += (e->To - e->From + 1 + e->s);", + "}", + "", + "static void", + "x_fixup(void)", + "{ int i;", + "", + " for (i = 0; i < dfa_depth; i++)", + " v_fix(temptree[i], (i+1));", + "", + " for (i = dfa_depth; i >= 0; i--)", + " v_insert(temptree[i], i);", + "}", + "", + "static Vertex *", + "x_tail(Vertex *t, ulong want)", + "{ int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0;", + "", + " if (!t) return v;", + "", + " yes = no = 0;", + " for (i = 0; i < 2; i++)", + " if ((ulong) t->dst[i] == want)", + " { /* was t->from[i] <= 0 && t->to[i] >= 0 */", + " /* but from and to are uchar */", + " if (t->from[i] == 0)", + " yes = 1;", + " else", + " if (t->from[i] <= 4 && t->to[i] >= 4)", + " no = 1;", + " }", + "", + " for (e = t->Succ; e; e = e->Nxt)", + " if ((ulong) e->Dst == want)", + " { /* was INRANGE(e,0) but From and To are uchar */", + " if ((e->From == 0) || (e->s==1 && e->S==0))", + " yes = 1;", + " else if (INRANGE(e, 4))", + " no = 1;", + " }", + " if (yes && !no) return t;", + " v = x_tail(t->left, want); if (v) return v;", + " v = x_tail(t->right, want); if (v) return v;", + " return (Vertex *) 0;", + "}", + "", + "static void", + "x_anytail(Vertex *t, Vertex *c, int nr)", + "{ int i; Edge *e, *f; Vertex *v;", + "", + " if (!t) return;", + "", + " for (i = 0; i < 2; i++)", + " if ((ulong) t->dst[i] == c->key)", + " { v = new_vertex(); v->key = t->key;", + " f = new_edge(v);", + " f->From = t->from[i];", + " f->To = t->to[i];", + " f->Nxt = c->Succ;", + " c->Succ = f;", + " if (nr > 0)", + " x_anytail(temptree[nr-1], v, nr-1);", + " }", + "", + " for (e = t->Succ; e; e = e->Nxt)", + " if ((ulong) e->Dst == c->key)", + " { v = new_vertex(); v->key = t->key;", + " f = new_edge(v);", + " f->From = e->From;", + " f->To = e->To;", + " f->s = e->s;", + " f->S = e->S;", + " f->Nxt = c->Succ;", + " c->Succ = f;", + " x_anytail(temptree[nr-1], v, nr-1);", + " }", + "", + " x_anytail(t->left, c, nr);", + " x_anytail(t->right, c, nr);", + "}", + "", + "static Vertex *", + "x_cpy_rev(void)", + "{ Vertex *c, *v; /* find 0 and !4 predecessor of F */", + "", + " v = x_tail(temptree[dfa_depth-1], F->key);", + " if (!v) return (Vertex *) 0;", + "", + " c = new_vertex(); c->key = v->key;", + "", + " /* every node on dfa_depth-2 that has v->key as succ */", + " /* make copy and let c point to these (reversing ptrs) */", + "", + " x_anytail(temptree[dfa_depth-2], c, dfa_depth-2);", + " ", + " return c;", + "}", + "", + "void", + "r_xpoint(void)", + "{ int fd; char nm[64]; Vertex *d;", + " int i, j;", + "", + " wcnt = 0;", + " sprintf(nm, \"%%s.xpt\", Source);", + " if ((fd = open(nm, 0)) < 0) /* O_RDONLY */", + " Uerror(\"cannot open checkpoint file\");", + "", + " xread(fd, (char *) &nstates, sizeof(double));", + " xread(fd, (char *) &truncs, sizeof(double));", + " xread(fd, (char *) &truncs2, sizeof(double));", + " xread(fd, (char *) &nlinks, sizeof(double));", + " xread(fd, (char *) &dfa_depth, sizeof(int));", + "", + " if (dfa_depth != MA+a_cycles)", + " Uerror(\"bad dfa_depth in checkpoint file\");", + "", + " path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));", + " layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));", + " temptree = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *));", + " lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));", + " lastword[dfa_depth] = lastword[0] = 255; ", + "", + " path[0] = R = new_vertex();", + " xread(fd, (char *) &R->key, sizeof(Vertex *));", + " R = insert_withkey(R, 0);", + "", + " F = new_vertex();", + " xread(fd, (char *) &F->key, sizeof(Vertex *));", + " F = insert_withkey(F, dfa_depth);", + "", + " NF = new_vertex();", + " xread(fd, (char *) &NF->key, sizeof(Vertex *));", + " NF = insert_withkey(NF, dfa_depth);", + "", + " for (j = 0; j < TWIDTH; j++)", + " for (i = 0; i < dfa_depth+1; i++)", + " r_layer(fd, i);", + "", + " if (wcnt != 0) Uerror(\"bad count in checkpoint file\");", + "", + " d = x_cpy_rev();", + " x_fixup();", + " stacker[dfa_depth-1] = 0;", + " x_rm_stack(d, dfa_depth-2);", + " x_cleanup(d);", + " close(fd);", + "", + " printf(\"pan: removed %%d stackstates\\n\", stackcnt);", + " nstates -= (double) stackcnt;", + "}", + "#endif", + 0, +}; diff --git a/sys/src/cmd/spin/pangen6.c b/sys/src/cmd/spin/pangen6.c new file mode 100755 index 000000000..50b587749 --- /dev/null +++ b/sys/src/cmd/spin/pangen6.c @@ -0,0 +1,2352 @@ +/***** spin: pangen6.c *****/ + +/* Copyright (c) 2000-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Abstract syntax tree analysis / slicing (spin option -A) */ +/* AST_store stores the fsms's for each proctype */ +/* AST_track keeps track of variables used in properties */ +/* AST_slice starts the slicing algorithm */ +/* it first collects more info and then calls */ +/* AST_criteria to process the slice criteria */ + +#include "spin.h" +#include "y.tab.h" + +extern Ordered *all_names; +extern FSM_use *use_free; +extern FSM_state **fsm_tbl; +extern FSM_state *fsm; +extern int verbose, o_max; + +static FSM_trans *cur_t; +static FSM_trans *expl_par; +static FSM_trans *expl_var; +static FSM_trans *explicit; + +extern void rel_use(FSM_use *); + +#define ulong unsigned long + +typedef struct Pair { + FSM_state *h; + int b; + struct Pair *nxt; +} Pair; + +typedef struct AST { + ProcList *p; /* proctype decl */ + int i_st; /* start state */ + int nstates, nwords; + int relevant; + Pair *pairs; /* entry and exit nodes of proper subgraphs */ + FSM_state *fsm; /* proctype body */ + struct AST *nxt; /* linked list */ +} AST; + +typedef struct RPN { /* relevant proctype names */ + Symbol *rn; + struct RPN *nxt; +} RPN; + +typedef struct ALIAS { /* channel aliasing info */ + Lextok *cnm; /* this chan */ + int origin; /* debugging - origin of the alias */ + struct ALIAS *alias; /* can be an alias for these other chans */ + struct ALIAS *nxt; /* linked list */ +} ALIAS; + +typedef struct ChanList { + Lextok *s; /* containing stmnt */ + Lextok *n; /* point of reference - could be struct */ + struct ChanList *nxt; /* linked list */ +} ChanList; + +/* a chan alias can be created in one of three ways: + assignement to chan name + a = b -- a is now an alias for b + passing chan name as parameter in run + run x(b) -- proctype x(chan a) + passing chan name through channel + x!b -- x?a + */ + +#define USE 1 +#define DEF 2 +#define DEREF_DEF 4 +#define DEREF_USE 8 + +static AST *ast; +static ALIAS *chalcur; +static ALIAS *chalias; +static ChanList *chanlist; +static Slicer *slicer; +static Slicer *rel_vars; /* all relevant variables */ +static int AST_Changes; +static int AST_Round; +static FSM_state no_state; +static RPN *rpn; +static int in_recv = 0; + +static int AST_mutual(Lextok *, Lextok *, int); +static void AST_dominant(void); +static void AST_hidden(void); +static void AST_setcur(Lextok *); +static void check_slice(Lextok *, int); +static void curtail(AST *); +static void def_use(Lextok *, int); +static void name_AST_track(Lextok *, int); +static void show_expl(void); + +static int +AST_isini(Lextok *n) /* is this an initialized channel */ +{ Symbol *s; + + if (!n || !n->sym) return 0; + + s = n->sym; + + if (s->type == CHAN) + return (s->ini->ntyp == CHAN); /* freshly instantiated */ + + if (s->type == STRUCT && n->rgt) + return AST_isini(n->rgt->lft); + + return 0; +} + +static void +AST_var(Lextok *n, Symbol *s, int toplevel) +{ + if (!s) return; + + if (toplevel) + { if (s->context && s->type) + printf(":%s:L:", s->context->name); + else + printf("G:"); + } + printf("%s", s->name); /* array indices ignored */ + + if (s->type == STRUCT && n && n->rgt && n->rgt->lft) + { printf(":"); + AST_var(n->rgt->lft, n->rgt->lft->sym, 0); + } +} + +static void +name_def_indices(Lextok *n, int code) +{ + if (!n || !n->sym) return; + + if (n->sym->nel != 1) + def_use(n->lft, code); /* process the index */ + + if (n->sym->type == STRUCT /* and possible deeper ones */ + && n->rgt) + name_def_indices(n->rgt->lft, code); +} + +static void +name_def_use(Lextok *n, int code) +{ FSM_use *u; + + if (!n) return; + + if ((code&USE) + && cur_t->step + && cur_t->step->n) + { switch (cur_t->step->n->ntyp) { + case 'c': /* possible predicate abstraction? */ + n->sym->colnr |= 2; /* yes */ + break; + default: + n->sym->colnr |= 1; /* no */ + break; + } + } + + for (u = cur_t->Val[0]; u; u = u->nxt) + if (AST_mutual(n, u->n, 1) + && u->special == code) + return; + + if (use_free) + { u = use_free; + use_free = use_free->nxt; + } else + u = (FSM_use *) emalloc(sizeof(FSM_use)); + + u->n = n; + u->special = code; + u->nxt = cur_t->Val[0]; + cur_t->Val[0] = u; + + name_def_indices(n, USE|(code&(~DEF))); /* not def, but perhaps deref */ +} + +static void +def_use(Lextok *now, int code) +{ Lextok *v; + + if (now) + switch (now->ntyp) { + case '!': + case UMIN: + case '~': + case 'c': + case ENABLED: + case ASSERT: + case EVAL: + def_use(now->lft, USE|code); + break; + + case LEN: + case FULL: + case EMPTY: + case NFULL: + case NEMPTY: + def_use(now->lft, DEREF_USE|USE|code); + break; + + case '/': + case '*': + case '-': + case '+': + case '%': + case '&': + case '^': + case '|': + case LE: + case GE: + case GT: + case LT: + case NE: + case EQ: + case OR: + case AND: + case LSHIFT: + case RSHIFT: + def_use(now->lft, USE|code); + def_use(now->rgt, USE|code); + break; + + case ASGN: + def_use(now->lft, DEF|code); + def_use(now->rgt, USE|code); + break; + + case TYPE: /* name in parameter list */ + name_def_use(now, code); + break; + + case NAME: + name_def_use(now, code); + break; + + case RUN: + name_def_use(now, USE); /* procname - not really needed */ + for (v = now->lft; v; v = v->rgt) + def_use(v->lft, USE); /* params */ + break; + + case 's': + def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + def_use(v->lft, USE|code); + break; + + case 'r': + def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + { if (v->lft->ntyp == EVAL) + def_use(v->lft, code); /* will add USE */ + else if (v->lft->ntyp != CONST) + def_use(v->lft, DEF|code); + } + break; + + case 'R': + def_use(now->lft, DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + { if (v->lft->ntyp == EVAL) + def_use(v->lft, code); /* will add USE */ + } + break; + + case '?': + def_use(now->lft, USE|code); + if (now->rgt) + { def_use(now->rgt->lft, code); + def_use(now->rgt->rgt, code); + } + break; + + case PRINT: + for (v = now->lft; v; v = v->rgt) + def_use(v->lft, USE|code); + break; + + case PRINTM: + def_use(now->lft, USE); + break; + + case CONST: + case ELSE: /* ? */ + case NONPROGRESS: + case PC_VAL: + case 'p': + case 'q': + break; + + case '.': + case GOTO: + case BREAK: + case '@': + case D_STEP: + case ATOMIC: + case NON_ATOMIC: + case IF: + case DO: + case UNLESS: + case TIMEOUT: + case C_CODE: + case C_EXPR: + default: + break; + } +} + +static int +AST_add_alias(Lextok *n, int nr) +{ ALIAS *ca; + int res; + + for (ca = chalcur->alias; ca; ca = ca->nxt) + if (AST_mutual(ca->cnm, n, 1)) + { res = (ca->origin&nr); + ca->origin |= nr; /* 1, 2, or 4 - run, asgn, or rcv */ + return (res == 0); /* 0 if already there with same origin */ + } + + ca = (ALIAS *) emalloc(sizeof(ALIAS)); + ca->cnm = n; + ca->origin = nr; + ca->nxt = chalcur->alias; + chalcur->alias = ca; + return 1; +} + +static void +AST_run_alias(char *pn, char *s, Lextok *t, int parno) +{ Lextok *v; + int cnt; + + if (!t) return; + + if (t->ntyp == RUN) + { if (strcmp(t->sym->name, s) == 0) + for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++) + if (cnt == parno) + { AST_add_alias(v->lft, 1); /* RUN */ + break; + } + } else + { AST_run_alias(pn, s, t->lft, parno); + AST_run_alias(pn, s, t->rgt, parno); + } +} + +static void +AST_findrun(char *s, int parno) +{ FSM_state *f; + FSM_trans *t; + AST *a; + + for (a = ast; a; a = a->nxt) /* automata */ + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* transitions */ + { if (t->step) + AST_run_alias(a->p->n->name, s, t->step->n, parno); + } +} + +static void +AST_par_chans(ProcList *p) /* find local chan's init'd to chan passed as param */ +{ Ordered *walk; + Symbol *sp; + + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp + && sp->context + && strcmp(sp->context->name, p->n->name) == 0 + && sp->Nid >= 0 /* not itself a param */ + && sp->type == CHAN + && sp->ini->ntyp == NAME) /* != CONST and != CHAN */ + { Lextok *x = nn(ZN, 0, ZN, ZN); + x->sym = sp; + AST_setcur(x); + AST_add_alias(sp->ini, 2); /* ASGN */ + } } +} + +static void +AST_para(ProcList *p) +{ Lextok *f, *t, *c; + int cnt = 0; + + AST_par_chans(p); + + for (f = p->p; f; f = f->rgt) /* list of types */ + for (t = f->lft; t; t = t->rgt) + { if (t->ntyp != ',') + c = t; + else + c = t->lft; /* expanded struct */ + + cnt++; + if (Sym_typ(c) == CHAN) + { ALIAS *na = (ALIAS *) emalloc(sizeof(ALIAS)); + + na->cnm = c; + na->nxt = chalias; + chalcur = chalias = na; +#if 0 + printf("%s -- (par) -- ", p->n->name); + AST_var(c, c->sym, 1); + printf(" => <<"); +#endif + AST_findrun(p->n->name, cnt); +#if 0 + printf(">>\n"); +#endif + } + } +} + +static void +AST_haschan(Lextok *c) +{ + if (!c) return; + if (Sym_typ(c) == CHAN) + { AST_add_alias(c, 2); /* ASGN */ +#if 0 + printf("<<"); + AST_var(c, c->sym, 1); + printf(">>\n"); +#endif + } else + { AST_haschan(c->rgt); + AST_haschan(c->lft); + } +} + +static int +AST_nrpar(Lextok *n) /* 's' or 'r' */ +{ Lextok *m; + int j = 0; + + for (m = n->rgt; m; m = m->rgt) + j++; + return j; +} + +static int +AST_ord(Lextok *n, Lextok *s) +{ Lextok *m; + int j = 0; + + for (m = n->rgt; m; m = m->rgt) + { j++; + if (s->sym == m->lft->sym) + return j; + } + return 0; +} + +#if 0 +static void +AST_ownership(Symbol *s) +{ + if (!s) return; + printf("%s:", s->name); + AST_ownership(s->owner); +} +#endif + +static int +AST_mutual(Lextok *a, Lextok *b, int toplevel) +{ Symbol *as, *bs; + + if (!a && !b) return 1; + + if (!a || !b) return 0; + + as = a->sym; + bs = b->sym; + + if (!as || !bs) return 0; + + if (toplevel && as->context != bs->context) + return 0; + + if (as->type != bs->type) + return 0; + + if (strcmp(as->name, bs->name) != 0) + return 0; + + if (as->type == STRUCT && a->rgt && b->rgt) + return AST_mutual(a->rgt->lft, b->rgt->lft, 0); + + return 1; +} + +static void +AST_setcur(Lextok *n) /* set chalcur */ +{ ALIAS *ca; + + for (ca = chalias; ca; ca = ca->nxt) + if (AST_mutual(ca->cnm, n, 1)) /* if same chan */ + { chalcur = ca; + return; + } + + ca = (ALIAS *) emalloc(sizeof(ALIAS)); + ca->cnm = n; + ca->nxt = chalias; + chalcur = chalias = ca; +} + +static void +AST_other(AST *a) /* check chan params in asgns and recvs */ +{ FSM_state *f; + FSM_trans *t; + FSM_use *u; + ChanList *cl; + + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* transitions */ + for (u = t->Val[0]; u; u = u->nxt) /* def/use info */ + if (Sym_typ(u->n) == CHAN + && (u->special&DEF)) /* def of chan-name */ + { AST_setcur(u->n); + switch (t->step->n->ntyp) { + case ASGN: + AST_haschan(t->step->n->rgt); + break; + case 'r': + /* guess sends where name may originate */ + for (cl = chanlist; cl; cl = cl->nxt) /* all sends */ + { int a = AST_nrpar(cl->s); + int b = AST_nrpar(t->step->n); + if (a != b) /* 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 */ + continue; + + AST_add_alias(cl->n, 4); /* RCV assume possible match */ + } + break; + default: + printf("type = %d\n", t->step->n->ntyp); + non_fatal("unexpected chan def type", (char *) 0); + break; + } } +} + +static void +AST_aliases(void) +{ ALIAS *na, *ca; + + for (na = chalias; na; na = na->nxt) + { printf("\npossible aliases of "); + AST_var(na->cnm, na->cnm->sym, 1); + printf("\n\t"); + for (ca = na->alias; ca; ca = ca->nxt) + { if (!ca->cnm->sym) + printf("no valid name "); + else + AST_var(ca->cnm, ca->cnm->sym, 1); + printf("<"); + if (ca->origin & 1) printf("RUN "); + if (ca->origin & 2) printf("ASGN "); + if (ca->origin & 4) printf("RCV "); + printf("[%s]", AST_isini(ca->cnm)?"Initzd":"Name"); + printf(">"); + if (ca->nxt) printf(", "); + } + printf("\n"); + } + printf("\n"); +} + +static void +AST_indirect(FSM_use *uin, FSM_trans *t, char *cause, char *pn) +{ FSM_use *u; + + /* this is a newly discovered relevant statement */ + /* all vars it uses to contribute to its DEF are new criteria */ + + if (!(t->relevant&1)) AST_Changes++; + + t->round = AST_Round; + t->relevant = 1; + + if ((verbose&32) && t->step) + { printf("\tDR %s [[ ", pn); + comment(stdout, t->step->n, 0); + printf("]]\n\t\tfully relevant %s", cause); + if (uin) { printf(" due to "); AST_var(uin->n, uin->n->sym, 1); } + printf("\n"); + } + for (u = t->Val[0]; u; u = u->nxt) + if (u != uin + && (u->special&(USE|DEREF_USE))) + { if (verbose&32) + { printf("\t\t\tuses(%d): ", u->special); + AST_var(u->n, u->n->sym, 1); + printf("\n"); + } + name_AST_track(u->n, u->special); /* add to slice criteria */ + } +} + +static void +def_relevant(char *pn, FSM_trans *t, Lextok *n, int ischan) +{ FSM_use *u; + ALIAS *na, *ca; + int chanref; + + /* look for all DEF's of n + * mark those stmnts relevant + * mark all var USEs in those stmnts as criteria + */ + + if (n->ntyp != ELSE) + for (u = t->Val[0]; u; u = u->nxt) + { chanref = (Sym_typ(u->n) == CHAN); + + if (ischan != chanref /* no possible match */ + || !(u->special&(DEF|DEREF_DEF))) /* not a def */ + continue; + + if (AST_mutual(u->n, n, 1)) + { AST_indirect(u, t, "(exact match)", pn); + continue; + } + + if (chanref) + for (na = chalias; na; na = na->nxt) + { if (!AST_mutual(u->n, na->cnm, 1)) + continue; + for (ca = na->alias; ca; ca = ca->nxt) + if (AST_mutual(ca->cnm, n, 1) + && AST_isini(ca->cnm)) + { AST_indirect(u, t, "(alias match)", pn); + break; + } + if (ca) break; + } } +} + +static void +AST_relevant(Lextok *n) +{ AST *a; + FSM_state *f; + FSM_trans *t; + int ischan; + + /* look for all DEF's of n + * mark those stmnts relevant + * mark all var USEs in those stmnts as criteria + */ + + if (!n) return; + ischan = (Sym_typ(n) == CHAN); + + if (verbose&32) + { printf("<<ast_relevant (ntyp=%d) ", n->ntyp); + AST_var(n, n->sym, 1); + printf(">>\n"); + } + + for (t = expl_par; t; t = t->nxt) /* param assignments */ + { if (!(t->relevant&1)) + def_relevant(":params:", t, n, ischan); + } + + for (t = expl_var; t; t = t->nxt) + { if (!(t->relevant&1)) /* var inits */ + def_relevant(":vars:", t, n, ischan); + } + + for (a = ast; a; a = a->nxt) /* all other stmnts */ + { if (strcmp(a->p->n->name, ":never:") != 0 + && strcmp(a->p->n->name, ":trace:") != 0 + && strcmp(a->p->n->name, ":notrace:") != 0) + for (f = a->fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + { if (!(t->relevant&1)) + def_relevant(a->p->n->name, t, n, ischan); + } } +} + +static int +AST_relpar(char *s) +{ FSM_trans *t, *T; + FSM_use *u; + + for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0) + for (t = T; t; t = t->nxt) + { if (t->relevant&1) + for (u = t->Val[0]; u; u = u->nxt) + { if (u->n->sym->type + && u->n->sym->context + && strcmp(u->n->sym->context->name, s) == 0) + { + if (verbose&32) + { printf("proctype %s relevant, due to symbol ", s); + AST_var(u->n, u->n->sym, 1); + printf("\n"); + } + return 1; + } } } + return 0; +} + +static void +AST_dorelevant(void) +{ AST *a; + RPN *r; + + for (r = rpn; r; r = r->nxt) + { for (a = ast; a; a = a->nxt) + if (strcmp(a->p->n->name, r->rn->name) == 0) + { a->relevant |= 1; + break; + } + if (!a) + fatal("cannot find proctype %s", r->rn->name); + } +} + +static void +AST_procisrelevant(Symbol *s) +{ RPN *r; + for (r = rpn; r; r = r->nxt) + if (strcmp(r->rn->name, s->name) == 0) + return; + r = (RPN *) emalloc(sizeof(RPN)); + r->rn = s; + r->nxt = rpn; + rpn = r; +} + +static int +AST_proc_isrel(char *s) +{ AST *a; + + for (a = ast; a; a = a->nxt) + if (strcmp(a->p->n->name, s) == 0) + return (a->relevant&1); + non_fatal("cannot happen, missing proc in ast", (char *) 0); + return 0; +} + +static int +AST_scoutrun(Lextok *t) +{ + if (!t) return 0; + + if (t->ntyp == RUN) + return AST_proc_isrel(t->sym->name); + return (AST_scoutrun(t->lft) || AST_scoutrun(t->rgt)); +} + +static void +AST_tagruns(void) +{ AST *a; + FSM_state *f; + FSM_trans *t; + + /* if any stmnt inside a proctype is relevant + * or any parameter passed in a run + * then so are all the run statements on that proctype + */ + + for (a = ast; a; a = a->nxt) + { if (strcmp(a->p->n->name, ":never:") == 0 + || strcmp(a->p->n->name, ":trace:") == 0 + || strcmp(a->p->n->name, ":notrace:") == 0 + || strcmp(a->p->n->name, ":init:") == 0) + { a->relevant |= 1; /* the proctype is relevant */ + continue; + } + if (AST_relpar(a->p->n->name)) + a->relevant |= 1; + else + { for (f = a->fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + if (t->relevant) + goto yes; +yes: if (f) + a->relevant |= 1; + } + } + + for (a = ast; a; a = a->nxt) + for (f = a->fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + if (t->step + && AST_scoutrun(t->step->n)) + { AST_indirect((FSM_use *)0, t, ":run:", a->p->n->name); + /* BUT, not all actual params are relevant */ + } +} + +static void +AST_report(AST *a, Element *e) /* ALSO deduce irrelevant vars */ +{ + if (!(a->relevant&2)) + { a->relevant |= 2; + printf("spin: redundant in proctype %s (for given property):\n", + a->p->n->name); + } + printf(" line %3d %s (state %d)", + e->n?e->n->ln:-1, + e->n?e->n->fn->name:"-", + e->seqno); + printf(" ["); + comment(stdout, e->n, 0); + printf("]\n"); +} + +static int +AST_always(Lextok *n) +{ + if (!n) return 0; + + if (n->ntyp == '@' /* -end */ + || n->ntyp == 'p') /* remote reference */ + return 1; + return AST_always(n->lft) || AST_always(n->rgt); +} + +static void +AST_edge_dump(AST *a, FSM_state *f) +{ FSM_trans *t; + FSM_use *u; + + for (t = f->t; t; t = t->nxt) /* edges */ + { + if (t->step && AST_always(t->step->n)) + t->relevant |= 1; /* always relevant */ + + if (verbose&32) + { switch (t->relevant) { + case 0: printf(" "); break; + case 1: printf("*%3d ", t->round); break; + case 2: printf("+%3d ", t->round); break; + case 3: printf("#%3d ", t->round); break; + default: printf("? "); break; + } + + printf("%d\t->\t%d\t", f->from, t->to); + if (t->step) + comment(stdout, t->step->n, 0); + else + printf("Unless"); + + for (u = t->Val[0]; u; u = u->nxt) + { printf(" <"); + AST_var(u->n, u->n->sym, 1); + printf(":%d>", u->special); + } + printf("\n"); + } else + { if (t->relevant) + continue; + + if (t->step) + switch(t->step->n->ntyp) { + case ASGN: + case 's': + case 'r': + case 'c': + if (t->step->n->lft->ntyp != CONST) + AST_report(a, t->step); + break; + + case PRINT: /* don't report */ + case PRINTM: + case ASSERT: + case C_CODE: + case C_EXPR: + default: + break; + } } } +} + +static void +AST_dfs(AST *a, int s, int vis) +{ FSM_state *f; + FSM_trans *t; + + f = fsm_tbl[s]; + if (f->seen) return; + + f->seen = 1; + if (vis) AST_edge_dump(a, f); + + for (t = f->t; t; t = t->nxt) + AST_dfs(a, t->to, vis); +} + +static void +AST_dump(AST *a) +{ FSM_state *f; + + for (f = a->fsm; f; f = f->nxt) + { f->seen = 0; + fsm_tbl[f->from] = f; + } + + if (verbose&32) + printf("AST_START %s from %d\n", a->p->n->name, a->i_st); + + AST_dfs(a, a->i_st, 1); +} + +static void +AST_sends(AST *a) +{ FSM_state *f; + FSM_trans *t; + FSM_use *u; + ChanList *cl; + + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* transitions */ + { if (t->step + && t->step->n + && t->step->n->ntyp == 's') + for (u = t->Val[0]; u; u = u->nxt) + { if (Sym_typ(u->n) == CHAN + && ((u->special&USE) && !(u->special&DEREF_USE))) + { +#if 0 + printf("%s -- (%d->%d) -- ", + a->p->n->name, f->from, t->to); + AST_var(u->n, u->n->sym, 1); + printf(" -> chanlist\n"); +#endif + cl = (ChanList *) emalloc(sizeof(ChanList)); + cl->s = t->step->n; + cl->n = u->n; + cl->nxt = chanlist; + chanlist = cl; +} } } } + +static ALIAS * +AST_alfind(Lextok *n) +{ ALIAS *na; + + for (na = chalias; na; na = na->nxt) + if (AST_mutual(na->cnm, n, 1)) + return na; + return (ALIAS *) 0; +} + +static void +AST_trans(void) +{ ALIAS *na, *ca, *da, *ea; + int nchanges; + + do { + nchanges = 0; + for (na = chalias; na; na = na->nxt) + { chalcur = na; + for (ca = na->alias; ca; ca = ca->nxt) + { da = AST_alfind(ca->cnm); + if (da) + for (ea = da->alias; ea; ea = ea->nxt) + { nchanges += AST_add_alias(ea->cnm, + ea->origin|ca->origin); + } } } + } while (nchanges > 0); + + chalcur = (ALIAS *) 0; +} + +static void +AST_def_use(AST *a) +{ FSM_state *f; + FSM_trans *t; + + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* all edges */ + { cur_t = t; + rel_use(t->Val[0]); /* redo Val; doesn't cover structs */ + rel_use(t->Val[1]); + t->Val[0] = t->Val[1] = (FSM_use *) 0; + + if (!t->step) continue; + + def_use(t->step->n, 0); /* def/use info, including structs */ + } + cur_t = (FSM_trans *) 0; +} + +static void +name_AST_track(Lextok *n, int code) +{ extern int nr_errs; +#if 0 + printf("AST_name: "); + AST_var(n, n->sym, 1); + printf(" -- %d\n", code); +#endif + if (in_recv && (code&DEF) && (code&USE)) + { printf("spin: error: DEF and USE of same var in rcv stmnt: "); + AST_var(n, n->sym, 1); + printf(" -- %d\n", code); + nr_errs++; + } + check_slice(n, code); +} + +void +AST_track(Lextok *now, int code) /* called from main.c */ +{ Lextok *v; extern int export_ast; + + if (!export_ast) return; + + if (now) + switch (now->ntyp) { + case LEN: + case FULL: + case EMPTY: + case NFULL: + case NEMPTY: + AST_track(now->lft, DEREF_USE|USE|code); + break; + + case '/': + case '*': + case '-': + case '+': + case '%': + case '&': + case '^': + case '|': + case LE: + case GE: + case GT: + case LT: + case NE: + case EQ: + case OR: + case AND: + case LSHIFT: + case RSHIFT: + AST_track(now->rgt, USE|code); + /* fall through */ + case '!': + case UMIN: + case '~': + case 'c': + case ENABLED: + case ASSERT: + AST_track(now->lft, USE|code); + break; + + case EVAL: + AST_track(now->lft, USE|(code&(~DEF))); + break; + + case NAME: + name_AST_track(now, code); + if (now->sym->nel != 1) + AST_track(now->lft, USE|code); /* index */ + break; + + case 'R': + AST_track(now->lft, DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + AST_track(v->lft, code); /* a deeper eval can add USE */ + break; + + case '?': + AST_track(now->lft, USE|code); + if (now->rgt) + { AST_track(now->rgt->lft, code); + AST_track(now->rgt->rgt, code); + } + break; + +/* added for control deps: */ + case TYPE: + name_AST_track(now, code); + break; + case ASGN: + AST_track(now->lft, DEF|code); + AST_track(now->rgt, USE|code); + break; + case RUN: + name_AST_track(now, USE); + for (v = now->lft; v; v = v->rgt) + AST_track(v->lft, USE|code); + break; + case 's': + AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + AST_track(v->lft, USE|code); + break; + case 'r': + AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code); + for (v = now->rgt; v; v = v->rgt) + { in_recv++; + AST_track(v->lft, DEF|code); + in_recv--; + } + break; + case PRINT: + for (v = now->lft; v; v = v->rgt) + AST_track(v->lft, USE|code); + break; + case PRINTM: + AST_track(now->lft, USE); + break; +/* end add */ + case 'p': +#if 0 + 'p' -sym-> _p + / + '?' -sym-> a (proctype) + / + b (pid expr) +#endif + AST_track(now->lft->lft, USE|code); + AST_procisrelevant(now->lft->sym); + break; + + case CONST: + case ELSE: + case NONPROGRESS: + case PC_VAL: + case 'q': + break; + + case '.': + case GOTO: + case BREAK: + case '@': + case D_STEP: + case ATOMIC: + case NON_ATOMIC: + case IF: + case DO: + case UNLESS: + case TIMEOUT: + case C_CODE: + case C_EXPR: + break; + + default: + printf("AST_track, NOT EXPECTED ntyp: %d\n", now->ntyp); + break; + } +} + +static int +AST_dump_rel(void) +{ Slicer *rv; + Ordered *walk; + char buf[64]; + int banner=0; + + if (verbose&32) + { printf("Relevant variables:\n"); + for (rv = rel_vars; rv; rv = rv->nxt) + { printf("\t"); + AST_var(rv->n, rv->n->sym, 1); + printf("\n"); + } + return 1; + } + for (rv = rel_vars; rv; rv = rv->nxt) + rv->n->sym->setat = 1; /* mark it */ + + for (walk = all_names; walk; walk = walk->next) + { Symbol *s; + s = walk->entry; + if (!s->setat + && (s->type != MTYPE || s->ini->ntyp != CONST) + && s->type != STRUCT /* report only fields */ + && s->type != PROCTYPE + && !s->owner + && sputtype(buf, s->type)) + { if (!banner) + { banner = 1; + printf("spin: redundant vars (for given property):\n"); + } + printf("\t"); + symvar(s); + } } + return banner; +} + +static void +AST_suggestions(void) +{ Symbol *s; + Ordered *walk; + FSM_state *f; + FSM_trans *t; + AST *a; + int banner=0; + int talked=0; + + for (walk = all_names; walk; walk = walk->next) + { s = walk->entry; + if (s->colnr == 2 /* only used in conditionals */ + && (s->type == BYTE + || s->type == SHORT + || s->type == INT + || s->type == MTYPE)) + { if (!banner) + { banner = 1; + printf("spin: consider using predicate"); + printf(" abstraction to replace:\n"); + } + printf("\t"); + symvar(s); + } } + + /* look for source and sink processes */ + + for (a = ast; a; a = a->nxt) /* automata */ + { banner = 0; + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* transitions */ + { if (t->step) + switch (t->step->n->ntyp) { + case 's': + banner |= 1; + break; + case 'r': + banner |= 2; + break; + case '.': + case D_STEP: + case ATOMIC: + case NON_ATOMIC: + case IF: + case DO: + case UNLESS: + case '@': + case GOTO: + case BREAK: + case PRINT: + case PRINTM: + case ASSERT: + case C_CODE: + case C_EXPR: + break; + default: + banner |= 4; + goto no_good; + } + } +no_good: if (banner == 1 || banner == 2) + { printf("spin: proctype %s defines a %s process\n", + a->p->n->name, + banner==1?"source":"sink"); + talked |= banner; + } else if (banner == 3) + { printf("spin: proctype %s mimics a buffer\n", + a->p->n->name); + talked |= 4; + } + } + if (talked&1) + { printf("\tto reduce complexity, consider merging the code of\n"); + printf("\teach source process into the code of its target\n"); + } + if (talked&2) + { printf("\tto reduce complexity, consider merging the code of\n"); + printf("\teach sink process into the code of its source\n"); + } + if (talked&4) + printf("\tto reduce complexity, avoid buffer processes\n"); +} + +static void +AST_preserve(void) +{ Slicer *sc, *nx, *rv; + + for (sc = slicer; sc; sc = nx) + { if (!sc->used) + break; /* done */ + + nx = sc->nxt; + + for (rv = rel_vars; rv; rv = rv->nxt) + if (AST_mutual(sc->n, rv->n, 1)) + break; + + if (!rv) /* not already there */ + { sc->nxt = rel_vars; + rel_vars = sc; + } } + slicer = sc; +} + +static void +check_slice(Lextok *n, int code) +{ Slicer *sc; + + for (sc = slicer; sc; sc = sc->nxt) + if (AST_mutual(sc->n, n, 1) + && sc->code == code) + return; /* already there */ + + sc = (Slicer *) emalloc(sizeof(Slicer)); + sc->n = n; + + sc->code = code; + sc->used = 0; + sc->nxt = slicer; + slicer = sc; +} + +static void +AST_data_dep(void) +{ Slicer *sc; + + /* mark all def-relevant transitions */ + for (sc = slicer; sc; sc = sc->nxt) + { sc->used = 1; + if (verbose&32) + { printf("spin: slice criterion "); + AST_var(sc->n, sc->n->sym, 1); + printf(" type=%d\n", Sym_typ(sc->n)); + } + AST_relevant(sc->n); + } + AST_tagruns(); /* mark 'run's relevant if target proctype is relevant */ +} + +static int +AST_blockable(AST *a, int s) +{ FSM_state *f; + FSM_trans *t; + + f = fsm_tbl[s]; + + for (t = f->t; t; t = t->nxt) + { if (t->relevant&2) + return 1; + + if (t->step && t->step->n) + switch (t->step->n->ntyp) { + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + if (AST_blockable(a, t->to)) + { t->round = AST_Round; + t->relevant |= 2; + return 1; + } + /* else fall through */ + default: + break; + } + else if (AST_blockable(a, t->to)) /* Unless */ + { t->round = AST_Round; + t->relevant |= 2; + return 1; + } + } + return 0; +} + +static void +AST_spread(AST *a, int s) +{ FSM_state *f; + FSM_trans *t; + + f = fsm_tbl[s]; + + for (t = f->t; t; t = t->nxt) + { if (t->relevant&2) + continue; + + if (t->step && t->step->n) + switch (t->step->n->ntyp) { + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + AST_spread(a, t->to); + /* fall thru */ + default: + t->round = AST_Round; + t->relevant |= 2; + break; + } + else /* Unless */ + { AST_spread(a, t->to); + t->round = AST_Round; + t->relevant |= 2; + } + } +} + +static int +AST_notrelevant(Lextok *n) +{ Slicer *s; + + for (s = rel_vars; s; s = s->nxt) + if (AST_mutual(s->n, n, 1)) + return 0; + for (s = slicer; s; s = s->nxt) + if (AST_mutual(s->n, n, 1)) + return 0; + return 1; +} + +static int +AST_withchan(Lextok *n) +{ + if (!n) return 0; + if (Sym_typ(n) == CHAN) + return 1; + return AST_withchan(n->lft) || AST_withchan(n->rgt); +} + +static int +AST_suspect(FSM_trans *t) +{ FSM_use *u; + /* check for possible overkill */ + if (!t || !t->step || !AST_withchan(t->step->n)) + return 0; + for (u = t->Val[0]; u; u = u->nxt) + if (AST_notrelevant(u->n)) + return 1; + return 0; +} + +static void +AST_shouldconsider(AST *a, int s) +{ FSM_state *f; + FSM_trans *t; + + f = fsm_tbl[s]; + for (t = f->t; t; t = t->nxt) + { if (t->step && t->step->n) + switch (t->step->n->ntyp) { + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + AST_shouldconsider(a, t->to); + break; + default: + AST_track(t->step->n, 0); +/* + AST_track is called here for a blockable stmnt from which + a relevant stmnmt was shown to be reachable + for a condition this makes all USEs relevant + but for a channel operation it only makes the executability + relevant -- in those cases, parameters that aren't already + relevant may be replaceable with arbitrary tokens + */ + if (AST_suspect(t)) + { printf("spin: possibly redundant parameters in: "); + comment(stdout, t->step->n, 0); + printf("\n"); + } + break; + } + else /* an Unless */ + AST_shouldconsider(a, t->to); + } +} + +static int +FSM_critical(AST *a, int s) +{ FSM_state *f; + FSM_trans *t; + + /* is a 1-relevant stmnt reachable from this state? */ + + f = fsm_tbl[s]; + if (f->seen) + goto done; + f->seen = 1; + f->cr = 0; + for (t = f->t; t; t = t->nxt) + if ((t->relevant&1) + || FSM_critical(a, t->to)) + { f->cr = 1; + + if (verbose&32) + { printf("\t\t\t\tcritical(%d) ", t->relevant); + comment(stdout, t->step->n, 0); + printf("\n"); + } + break; + } +#if 0 + else { + if (verbose&32) + { printf("\t\t\t\tnot-crit "); + comment(stdout, t->step->n, 0); + printf("\n"); + } + } +#endif +done: + return f->cr; +} + +static void +AST_ctrl(AST *a) +{ FSM_state *f; + FSM_trans *t; + int hit; + + /* add all blockable transitions + * from which relevant transitions can be reached + */ + if (verbose&32) + printf("CTL -- %s\n", a->p->n->name); + + /* 1 : mark all blockable edges */ + for (f = a->fsm; f; f = f->nxt) + { if (!(f->scratch&2)) /* not part of irrelevant subgraph */ + for (t = f->t; t; t = t->nxt) + { if (t->step && t->step->n) + switch (t->step->n->ntyp) { + case 'r': + case 's': + case 'c': + case ELSE: + t->round = AST_Round; + t->relevant |= 2; /* mark for next phases */ + if (verbose&32) + { printf("\tpremark "); + comment(stdout, t->step->n, 0); + printf("\n"); + } + break; + default: + break; + } } } + + /* 2: keep only 2-marked stmnts from which 1-marked stmnts can be reached */ + for (f = a->fsm; f; f = f->nxt) + { fsm_tbl[f->from] = f; + f->seen = 0; /* used in dfs from FSM_critical */ + } + for (f = a->fsm; f; f = f->nxt) + { if (!FSM_critical(a, f->from)) + for (t = f->t; t; t = t->nxt) + if (t->relevant&2) + { t->relevant &= ~2; /* clear mark */ + if (verbose&32) + { printf("\t\tnomark "); + comment(stdout, t->step->n, 0); + printf("\n"); + } } } + + /* 3 : lift marks across IF/DO etc. */ + for (f = a->fsm; f; f = f->nxt) + { hit = 0; + for (t = f->t; t; t = t->nxt) + { if (t->step && t->step->n) + switch (t->step->n->ntyp) { + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + if (AST_blockable(a, t->to)) + hit = 1; + break; + default: + break; + } + else if (AST_blockable(a, t->to)) /* Unless */ + hit = 1; + + if (hit) break; + } + if (hit) /* at least one outgoing trans can block */ + for (t = f->t; t; t = t->nxt) + { t->round = AST_Round; + t->relevant |= 2; /* lift */ + if (verbose&32) + { printf("\t\t\tliftmark "); + comment(stdout, t->step->n, 0); + printf("\n"); + } + AST_spread(a, t->to); /* and spread to all guards */ + } } + + /* 4: nodes with 2-marked out-edges contribute new slice criteria */ + for (f = a->fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + if (t->relevant&2) + { AST_shouldconsider(a, f->from); + break; /* inner loop */ + } +} + +static void +AST_control_dep(void) +{ AST *a; + + for (a = ast; a; a = a->nxt) + if (strcmp(a->p->n->name, ":never:") != 0 + && strcmp(a->p->n->name, ":trace:") != 0 + && strcmp(a->p->n->name, ":notrace:") != 0) + AST_ctrl(a); +} + +static void +AST_prelabel(void) +{ AST *a; + FSM_state *f; + FSM_trans *t; + + for (a = ast; a; a = a->nxt) + { if (strcmp(a->p->n->name, ":never:") != 0 + && strcmp(a->p->n->name, ":trace:") != 0 + && strcmp(a->p->n->name, ":notrace:") != 0) + for (f = a->fsm; f; f = f->nxt) + for (t = f->t; t; t = t->nxt) + { if (t->step + && t->step->n + && t->step->n->ntyp == ASSERT + ) + { t->relevant |= 1; + } } } +} + +static void +AST_criteria(void) +{ /* + * remote labels are handled separately -- by making + * sure they are not pruned away during optimization + */ + AST_Changes = 1; /* to get started */ + for (AST_Round = 1; slicer && AST_Changes; AST_Round++) + { AST_Changes = 0; + AST_data_dep(); + AST_preserve(); /* moves processed vars from slicer to rel_vars */ + AST_dominant(); /* mark data-irrelevant subgraphs */ + AST_control_dep(); /* can add data deps, which add control deps */ + + if (verbose&32) + printf("\n\nROUND %d -- changes %d\n", + AST_Round, AST_Changes); + } +} + +static void +AST_alias_analysis(void) /* aliasing of promela channels */ +{ AST *a; + + for (a = ast; a; a = a->nxt) + AST_sends(a); /* collect chan-names that are send across chans */ + + for (a = ast; a; a = a->nxt) + AST_para(a->p); /* aliasing of chans thru proctype parameters */ + + for (a = ast; a; a = a->nxt) + AST_other(a); /* chan params in asgns and recvs */ + + AST_trans(); /* transitive closure of alias table */ + + if (verbose&32) + AST_aliases(); /* show channel aliasing info */ +} + +void +AST_slice(void) +{ AST *a; + int spurious = 0; + + if (!slicer) + { non_fatal("no slice criteria (or no claim) specified", + (char *) 0); + spurious = 1; + } + AST_dorelevant(); /* mark procs refered to in remote refs */ + + for (a = ast; a; a = a->nxt) + AST_def_use(a); /* compute standard def/use information */ + + AST_hidden(); /* parameter passing and local var inits */ + + AST_alias_analysis(); /* channel alias analysis */ + + AST_prelabel(); /* mark all 'assert(...)' stmnts as relevant */ + AST_criteria(); /* process the slice criteria from + * asserts and from the never claim + */ + if (!spurious || (verbose&32)) + { spurious = 1; + for (a = ast; a; a = a->nxt) + { AST_dump(a); /* marked up result */ + if (a->relevant&2) /* it printed something */ + spurious = 0; + } + if (!AST_dump_rel() /* relevant variables */ + && spurious) + printf("spin: no redundancies found (for given property)\n"); + } + AST_suggestions(); + + if (verbose&32) + show_expl(); +} + +void +AST_store(ProcList *p, int start_state) +{ AST *n_ast; + + if (strcmp(p->n->name, ":never:") != 0 + && strcmp(p->n->name, ":trace:") != 0 + && strcmp(p->n->name, ":notrace:") != 0) + { n_ast = (AST *) emalloc(sizeof(AST)); + n_ast->p = p; + n_ast->i_st = start_state; + n_ast->relevant = 0; + n_ast->fsm = fsm; + n_ast->nxt = ast; + ast = n_ast; + } + fsm = (FSM_state *) 0; /* hide it from FSM_DEL */ +} + +static void +AST_add_explicit(Lextok *d, Lextok *u) +{ FSM_trans *e = (FSM_trans *) emalloc(sizeof(FSM_trans)); + + e->to = 0; /* or start_state ? */ + e->relevant = 0; /* to be determined */ + e->step = (Element *) 0; /* left blank */ + e->Val[0] = e->Val[1] = (FSM_use *) 0; + + cur_t = e; + + def_use(u, USE); + def_use(d, DEF); + + cur_t = (FSM_trans *) 0; + + e->nxt = explicit; + explicit = e; +} + +static void +AST_fp1(char *s, Lextok *t, Lextok *f, int parno) +{ Lextok *v; + int cnt; + + if (!t) return; + + if (t->ntyp == RUN) + { if (strcmp(t->sym->name, s) == 0) + for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++) + if (cnt == parno) + { AST_add_explicit(f, v->lft); + break; + } + } else + { AST_fp1(s, t->lft, f, parno); + AST_fp1(s, t->rgt, f, parno); + } +} + +static void +AST_mk1(char *s, Lextok *c, int parno) +{ AST *a; + FSM_state *f; + FSM_trans *t; + + /* concoct an extra FSM_trans *t with the asgn of + * formal par c to matching actual pars made explicit + */ + + for (a = ast; a; a = a->nxt) /* automata */ + for (f = a->fsm; f; f = f->nxt) /* control states */ + for (t = f->t; t; t = t->nxt) /* transitions */ + { if (t->step) + AST_fp1(s, t->step->n, c, parno); + } +} + +static void +AST_par_init(void) /* parameter passing -- hidden assignments */ +{ AST *a; + Lextok *f, *t, *c; + int cnt; + + for (a = ast; a; a = a->nxt) + { if (strcmp(a->p->n->name, ":never:") == 0 + || strcmp(a->p->n->name, ":trace:") == 0 + || strcmp(a->p->n->name, ":notrace:") == 0 + || strcmp(a->p->n->name, ":init:") == 0) + continue; /* have no params */ + + cnt = 0; + for (f = a->p->p; f; f = f->rgt) /* types */ + for (t = f->lft; t; t = t->rgt) /* formals */ + { cnt++; /* formal par count */ + c = (t->ntyp != ',')? t : t->lft; /* the formal parameter */ + AST_mk1(a->p->n->name, c, cnt); /* all matching run statements */ + } } +} + +static void +AST_var_init(void) /* initialized vars (not chans) - hidden assignments */ +{ Ordered *walk; + Lextok *x; + Symbol *sp; + AST *a; + + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp + && !sp->context /* globals */ + && sp->type != PROCTYPE + && sp->ini + && (sp->type != MTYPE || sp->ini->ntyp != CONST) /* not mtype defs */ + && sp->ini->ntyp != CHAN) + { x = nn(ZN, TYPE, ZN, ZN); + x->sym = sp; + AST_add_explicit(x, sp->ini); + } } + + for (a = ast; a; a = a->nxt) + { if (strcmp(a->p->n->name, ":never:") != 0 + && strcmp(a->p->n->name, ":trace:") != 0 + && strcmp(a->p->n->name, ":notrace:") != 0) /* claim has no locals */ + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp + && sp->context + && strcmp(sp->context->name, a->p->n->name) == 0 + && sp->Nid >= 0 /* not a param */ + && sp->type != LABEL + && sp->ini + && sp->ini->ntyp != CHAN) + { x = nn(ZN, TYPE, ZN, ZN); + x->sym = sp; + AST_add_explicit(x, sp->ini); + } } } +} + +static void +show_expl(void) +{ FSM_trans *t, *T; + FSM_use *u; + + printf("\nExplicit List:\n"); + for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0) + { for (t = T; t; t = t->nxt) + { if (!t->Val[0]) continue; + printf("%s", t->relevant?"*":" "); + printf("%3d", t->round); + for (u = t->Val[0]; u; u = u->nxt) + { printf("\t<"); + AST_var(u->n, u->n->sym, 1); + printf(":%d>, ", u->special); + } + printf("\n"); + } + printf("==\n"); + } + printf("End\n"); +} + +static void +AST_hidden(void) /* reveal all hidden assignments */ +{ + AST_par_init(); + expl_par = explicit; + explicit = (FSM_trans *) 0; + + AST_var_init(); + expl_var = explicit; + explicit = (FSM_trans *) 0; +} + +#define BPW (8*sizeof(ulong)) /* bits per word */ + +static int +bad_scratch(FSM_state *f, int upto) +{ FSM_trans *t; +#if 0 + 1. all internal branch-points have else-s + 2. all non-branchpoints have non-blocking out-edge + 3. all internal edges are non-relevant + subgraphs like this need NOT contribute control-dependencies +#endif + + if (!f->seen + || (f->scratch&4)) + return 0; + + if (f->scratch&8) + return 1; + + f->scratch |= 4; + + if (verbose&32) printf("X[%d:%d:%d] ", f->from, upto, f->scratch); + + if (f->scratch&1) + { if (verbose&32) + printf("\tbad scratch: %d\n", f->from); +bad: f->scratch &= ~4; + /* f->scratch |= 8; wrong */ + return 1; + } + + if (f->from != upto) + for (t = f->t; t; t = t->nxt) + if (bad_scratch(fsm_tbl[t->to], upto)) + goto bad; + + return 0; +} + +static void +mark_subgraph(FSM_state *f, int upto) +{ FSM_trans *t; + + if (f->from == upto + || !f->seen + || (f->scratch&2)) + return; + + f->scratch |= 2; + + for (t = f->t; t; t = t->nxt) + mark_subgraph(fsm_tbl[t->to], upto); +} + +static void +AST_pair(AST *a, FSM_state *h, int y) +{ Pair *p; + + for (p = a->pairs; p; p = p->nxt) + if (p->h == h + && p->b == y) + return; + + p = (Pair *) emalloc(sizeof(Pair)); + p->h = h; + p->b = y; + p->nxt = a->pairs; + a->pairs = p; +} + +static void +AST_checkpairs(AST *a) +{ Pair *p; + + for (p = a->pairs; p; p = p->nxt) + { if (verbose&32) + printf(" inspect pair %d %d\n", p->b, p->h->from); + if (!bad_scratch(p->h, p->b)) /* subgraph is clean */ + { if (verbose&32) + printf("subgraph: %d .. %d\n", p->b, p->h->from); + mark_subgraph(p->h, p->b); + } + } +} + +static void +subgraph(AST *a, FSM_state *f, int out) +{ FSM_state *h; + int i, j; + ulong *g; +#if 0 + reverse dominance suggests that this is a possible + entry and exit node for a proper subgraph +#endif + h = fsm_tbl[out]; + + i = f->from / BPW; + j = f->from % BPW; + g = h->mod; + + if (verbose&32) + printf("possible pair %d %d -- %d\n", + f->from, h->from, (g[i]&(1<<j))?1:0); + + if (g[i]&(1<<j)) /* also a forward dominance pair */ + AST_pair(a, h, f->from); /* record this pair */ +} + +static void +act_dom(AST *a) +{ FSM_state *f; + FSM_trans *t; + int i, j, cnt; + + for (f = a->fsm; f; f = f->nxt) + { if (!f->seen) continue; +#if 0 + f->from is the exit-node of a proper subgraph, with + the dominator its entry-node, if: + a. this node has more than 1 reachable predecessor + b. the dominator has more than 1 reachable successor + (need reachability - in case of reverse dominance) + d. the dominator is reachable, and not equal to this node +#endif + for (t = f->p, i = 0; t; t = t->nxt) + i += fsm_tbl[t->to]->seen; + if (i <= 1) continue; /* a. */ + + for (cnt = 1; cnt < a->nstates; cnt++) /* 0 is endstate */ + { if (cnt == f->from + || !fsm_tbl[cnt]->seen) + continue; /* c. */ + + i = cnt / BPW; + j = cnt % BPW; + if (!(f->dom[i]&(1<<j))) + continue; + + for (t = fsm_tbl[cnt]->t, i = 0; t; t = t->nxt) + i += fsm_tbl[t->to]->seen; + if (i <= 1) + continue; /* b. */ + + if (f->mod) /* final check in 2nd phase */ + subgraph(a, f, cnt); /* possible entry-exit pair */ + } + } +} + +static void +reachability(AST *a) +{ FSM_state *f; + + for (f = a->fsm; f; f = f->nxt) + f->seen = 0; /* clear */ + AST_dfs(a, a->i_st, 0); /* mark 'seen' */ +} + +static int +see_else(FSM_state *f) +{ FSM_trans *t; + + for (t = f->t; t; t = t->nxt) + { if (t->step + && t->step->n) + switch (t->step->n->ntyp) { + case ELSE: + return 1; + case IF: + case DO: + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + if (see_else(fsm_tbl[t->to])) + return 1; + default: + break; + } + } + return 0; +} + +static int +is_guard(FSM_state *f) +{ FSM_state *g; + FSM_trans *t; + + for (t = f->p; t; t = t->nxt) + { g = fsm_tbl[t->to]; + if (!g->seen) + continue; + + if (t->step + && t->step->n) + switch(t->step->n->ntyp) { + case IF: + case DO: + return 1; + case ATOMIC: + case NON_ATOMIC: + case D_STEP: + if (is_guard(g)) + return 1; + default: + break; + } + } + return 0; +} + +static void +curtail(AST *a) +{ FSM_state *f, *g; + FSM_trans *t; + int i, haselse, isrel, blocking; +#if 0 + mark nodes that do not satisfy these requirements: + 1. all internal branch-points have else-s + 2. all non-branchpoints have non-blocking out-edge + 3. all internal edges are non-data-relevant +#endif + if (verbose&32) + printf("Curtail %s:\n", a->p->n->name); + + for (f = a->fsm; f; f = f->nxt) + { if (!f->seen + || (f->scratch&(1|2))) + continue; + + isrel = haselse = i = blocking = 0; + + for (t = f->t; t; t = t->nxt) + { g = fsm_tbl[t->to]; + + isrel |= (t->relevant&1); /* data relevant */ + i += g->seen; + + if (t->step + && t->step->n) + { switch (t->step->n->ntyp) { + case IF: + case DO: + haselse |= see_else(g); + break; + case 'c': + case 's': + case 'r': + blocking = 1; + break; + } } } +#if 0 + if (verbose&32) + printf("prescratch %d -- %d %d %d %d -- %d\n", + f->from, i, isrel, blocking, haselse, is_guard(f)); +#endif + if (isrel /* 3. */ + || (i == 1 && blocking) /* 2. */ + || (i > 1 && !haselse)) /* 1. */ + { if (!is_guard(f)) + { f->scratch |= 1; + if (verbose&32) + printf("scratch %d -- %d %d %d %d\n", + f->from, i, isrel, blocking, haselse); + } + } + } +} + +static void +init_dom(AST *a) +{ FSM_state *f; + int i, j, cnt; +#if 0 + (1) D(s0) = {s0} + (2) for s in S - {s0} do D(s) = S +#endif + + for (f = a->fsm; f; f = f->nxt) + { if (!f->seen) continue; + + f->dom = (ulong *) + emalloc(a->nwords * sizeof(ulong)); + + if (f->from == a->i_st) + { i = a->i_st / BPW; + j = a->i_st % BPW; + f->dom[i] = (1<<j); /* (1) */ + } else /* (2) */ + { for (i = 0; i < a->nwords; i++) + f->dom[i] = (ulong) ~0; /* all 1's */ + + if (a->nstates % BPW) + for (i = (a->nstates % BPW); i < (int) BPW; i++) + f->dom[a->nwords-1] &= ~(1<<i); /* clear tail */ + + for (cnt = 0; cnt < a->nstates; cnt++) + if (!fsm_tbl[cnt]->seen) + { i = cnt / BPW; + j = cnt % BPW; + f->dom[i] &= ~(1<<j); + } } } +} + +static int +dom_perculate(AST *a, FSM_state *f) +{ static ulong *ndom = (ulong *) 0; + static int on = 0; + int i, j, cnt = 0; + FSM_state *g; + FSM_trans *t; + + if (on < a->nwords) + { on = a->nwords; + ndom = (ulong *) + emalloc(on * sizeof(ulong)); + } + + for (i = 0; i < a->nwords; i++) + ndom[i] = (ulong) ~0; + + for (t = f->p; t; t = t->nxt) /* all reachable predecessors */ + { g = fsm_tbl[t->to]; + if (g->seen) + for (i = 0; i < a->nwords; i++) + ndom[i] &= g->dom[i]; /* (5b) */ + } + + i = f->from / BPW; + j = f->from % BPW; + ndom[i] |= (1<<j); /* (5a) */ + + for (i = 0; i < a->nwords; i++) + if (f->dom[i] != ndom[i]) + { cnt++; + f->dom[i] = ndom[i]; + } + + return cnt; +} + +static void +dom_forward(AST *a) +{ FSM_state *f; + int cnt; + + init_dom(a); /* (1,2) */ + do { + cnt = 0; + for (f = a->fsm; f; f = f->nxt) + { if (f->seen + && f->from != a->i_st) /* (4) */ + cnt += dom_perculate(a, f); /* (5) */ + } + } while (cnt); /* (3) */ + dom_perculate(a, fsm_tbl[a->i_st]); +} + +static void +AST_dominant(void) +{ FSM_state *f; + FSM_trans *t; + AST *a; + int oi; +#if 0 + find dominators + Aho, Sethi, & Ullman, Compilers - principles, techniques, and tools + Addison-Wesley, 1986, p.671. + + (1) D(s0) = {s0} + (2) for s in S - {s0} do D(s) = S + + (3) while any D(s) changes do + (4) for s in S - {s0} do + (5) D(s) = {s} union with intersection of all D(p) + where p are the immediate predecessors of s + + the purpose is to find proper subgraphs + (one entry node, one exit node) +#endif + if (AST_Round == 1) /* computed once, reused in every round */ + for (a = ast; a; a = a->nxt) + { a->nstates = 0; + for (f = a->fsm; f; f = f->nxt) + { a->nstates++; /* count */ + fsm_tbl[f->from] = f; /* fast lookup */ + f->scratch = 0; /* clear scratch marks */ + } + for (oi = 0; oi < a->nstates; oi++) + if (!fsm_tbl[oi]) + fsm_tbl[oi] = &no_state; + + a->nwords = (a->nstates + BPW - 1) / BPW; /* round up */ + + if (verbose&32) + { printf("%s (%d): ", a->p->n->name, a->i_st); + printf("states=%d (max %d), words = %d, bpw %d, overflow %d\n", + a->nstates, o_max, a->nwords, + (int) BPW, (int) (a->nstates % BPW)); + } + + reachability(a); + dom_forward(a); /* forward dominance relation */ + + curtail(a); /* mark ineligible edges */ + for (f = a->fsm; f; f = f->nxt) + { t = f->p; + f->p = f->t; + f->t = t; /* invert edges */ + + f->mod = f->dom; + f->dom = (ulong *) 0; + } + oi = a->i_st; + if (fsm_tbl[0]->seen) /* end-state reachable - else leave it */ + a->i_st = 0; /* becomes initial state */ + + dom_forward(a); /* reverse dominance -- don't redo reachability! */ + act_dom(a); /* mark proper subgraphs, if any */ + AST_checkpairs(a); /* selectively place 2 scratch-marks */ + + for (f = a->fsm; f; f = f->nxt) + { t = f->p; + f->p = f->t; + f->t = t; /* restore */ + } + a->i_st = oi; /* restore */ + } else + for (a = ast; a; a = a->nxt) + { for (f = a->fsm; f; f = f->nxt) + { fsm_tbl[f->from] = f; + f->scratch &= 1; /* preserve 1-marks */ + } + for (oi = 0; oi < a->nstates; oi++) + if (!fsm_tbl[oi]) + fsm_tbl[oi] = &no_state; + + curtail(a); /* mark ineligible edges */ + + for (f = a->fsm; f; f = f->nxt) + { t = f->p; + f->p = f->t; + f->t = t; /* invert edges */ + } + + AST_checkpairs(a); /* recompute 2-marks */ + + for (f = a->fsm; f; f = f->nxt) + { t = f->p; + f->p = f->t; + f->t = t; /* restore */ + } } +} diff --git a/sys/src/cmd/spin/pc_zpp.c b/sys/src/cmd/spin/pc_zpp.c new file mode 100755 index 000000000..5d2a69b15 --- /dev/null +++ b/sys/src/cmd/spin/pc_zpp.c @@ -0,0 +1,408 @@ +/***** spin: pc_zpp.c *****/ + +/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* pc_zpp.c is only used in the PC version of Spin */ +/* it is included to avoid too great a reliance on an external cpp */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifdef PC +enum cstate { PLAIN, IN_STRING, IN_QUOTE, S_COMM, COMMENT, E_COMM }; + +#define MAXNEST 32 +#define MAXDEF 128 +#define MAXLINE 512 +#define GENEROUS 4096 + +#define debug(x,y) if (verbose) printf(x,y) + +static FILE *outpp /* = stdout */; + +static int if_truth[MAXNEST]; +static int printing[MAXNEST]; +static int if_depth, nr_defs, verbose = 0; +static enum cstate state = PLAIN; +static char Out1[GENEROUS], Out2[GENEROUS]; + +static struct Defines { + int exists; + char *src, *trg; +} d[MAXDEF]; + +static int process(char *, int, char *); +static int zpp_do(char *); + +extern char *emalloc(int); /* main.c */ + +static int +do_define(char *p) +{ char *q, *r, *s; + + for (q = p+strlen(p)-1; q > p; q--) + if (*q == '\n' || *q == '\t' || *q == ' ') + *q = '\0'; + else + break; + + q = p + strspn(p, " \t"); + if (!(r = strchr(q, '\t'))) + r = strchr(q, ' '); + if (!r) { s = ""; goto adddef; } + s = r + strspn(r, " \t"); + *r = '\0'; + if (strchr(q, '(')) + { debug("zpp: #define with arguments %s\n", q); + return 0; + } + for (r = q+strlen(q)-1; r > q; r--) + if (*r == ' ' || *r == '\t') + *r = '\0'; + else + break; + if (nr_defs >= MAXDEF) + { debug("zpp: too many #defines (max %d)\n", nr_defs); + return 0; + } + if (strcmp(q, s) != 0) + { int j; +adddef: for (j = 0; j < nr_defs; j++) + if (!strcmp(d[j].src, q)) + d[j].exists = 0; + d[nr_defs].src = emalloc(strlen(q)+1); + d[nr_defs].trg = emalloc(strlen(s)+1); + strcpy(d[nr_defs].src, q); + strcpy(d[nr_defs].trg, s); + d[nr_defs++].exists = 1; + } + return 1; +} + +static int +isvalid(int c) +{ + return (isalnum(c) || c == '_'); +} + +static char * +apply(char *p0) +{ char *out, *in1, *in2, *startat; + int i, j; + + startat = in1 = Out2; strcpy(Out2, p0); + out = Out1; *out = '\0'; + + for (i = nr_defs-1; i >= 0; i--) + { if (!d[i].exists) continue; + j = (int) strlen(d[i].src); +more: in2 = strstr(startat, d[i].src); + if (!in2) /* no more matches */ + { startat = in1; + continue; + } + if ((in2 == in1 || !isvalid(*(in2-1))) + && (in2+j == '\0' || !isvalid(*(in2+j)))) + { *in2 = '\0'; + + if (strlen(in1)+strlen(d[i].trg)+strlen(in2+j) >= GENEROUS) + { + printf("spin: circular macro expansion %s -> %s ?\n", + d[i].src, d[i].trg); + return in1; + } + strcat(out, in1); + strcat(out, d[i].trg); + strcat(out, in2+j); + if (in1 == Out2) + { startat = in1 = Out1; + out = Out2; + } else + { startat = in1 = Out2; + out = Out1; + } + *out = '\0'; + } else + { startat = in2+1; /* +1 not +j.. */ + } + goto more; /* recursive defines */ + } + return in1; +} + +static char * +do_common(char *p) +{ char *q, *s; + + q = p + strspn(p, " \t"); + for (s = (q + strlen(q) - 1); s > q; s--) + if (*s == ' ' || *s == '\t' || *s == '\n') + *s = '\0'; + else + break; + return q; +} + +static int +do_undefine(char *p) +{ int i; char *q = do_common(p); + + for (i = 0; i < nr_defs; i++) + if (!strcmp(d[i].src, q)) + d[i].exists = 0; + return 1; +} + +static char * +check_ifdef(char *p) +{ int i; char *q = do_common(p); + + for (i = 0; i < nr_defs; i++) + if (d[i].exists + && !strcmp(d[i].src, q)) + return d[i].trg; + return (char *) 0; +} + +static int +do_ifdef(char *p) +{ + if (++if_depth >= MAXNEST) + { debug("zpp: too deeply nested (max %d)\n", MAXNEST); + return 0; + } + if_truth[if_depth] = (check_ifdef(p) != (char *)0); + printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; + + return 1; +} + +static int +do_ifndef(char *p) +{ + if (++if_depth >= MAXNEST) + { debug("zpp: too deeply nested (max %d)\n", MAXNEST); + return 0; + } + if_truth[if_depth] = (check_ifdef(p) == (char *)0); + printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; + + return 1; +} + +static int +is_simple(char *q) +{ + if (!q) return 0; + if (strcmp(q, "0") == 0) + if_truth[if_depth] = 0; + else if (strcmp(q, "1") == 0) + if_truth[if_depth] = 1; + else + return 0; + return 1; +} + +static int +do_if(char *p) +{ char *q = do_common(p); + if (++if_depth >= MAXNEST) + { debug("zpp: too deeply nested (max %d)\n", MAXNEST); + return 0; + } + if (!is_simple(q) + && !is_simple(check_ifdef(q))) + { debug("zpp: cannot handle #if %s\n", q); + return 0; + } + printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; + + return 1; +} + +static int +do_else(char *unused) +{ + if_truth[if_depth] = 1-if_truth[if_depth]; + printing[if_depth] = printing[if_depth-1]&&if_truth[if_depth]; + + return 1; +} + +static int +do_endif(char *p) +{ + if (--if_depth < 0) + { debug("zpp: unbalanced #endif %s\n", p); + return 0; + } + return 1; +} + +static int +do_include(char *p) +{ char *r, *q; + + q = strchr(p, '<'); + r = strrchr(p, '>'); + if (!q || !r) + { q = strchr (p, '\"'); + r = strrchr(p, '\"'); + if (!q || !r || q == r) + { debug("zpp: malformed #include %s", p); + return 0; + } } + *r = '\0'; + return zpp_do(++q); +} + +static int +in_comment(char *p) +{ char *q = p; + + for (q = p; *q != '\n' && *q != '\0'; q++) + switch (state) { + case PLAIN: + switch (*q) { + case '"': state = IN_STRING; break; + case '\'': state = IN_QUOTE; break; + case '/': state = S_COMM; break; + case '\\': q++; break; + } + break; + case IN_STRING: + if (*q == '"') state = PLAIN; + else if (*q == '\\') q++; + break; + case IN_QUOTE: + if (*q == '\'') state = PLAIN; + else if (*q == '\\') q++; + break; + case S_COMM: + if (*q == '*') + { *(q-1) = *q = ' '; + state = COMMENT; + } else if (*q != '/') + state = PLAIN; + break; + case COMMENT: + state = (*q == '*') ? E_COMM: COMMENT; + *q = ' '; + break; + case E_COMM: + if (*q == '/') + state = PLAIN; + else if (*q != '*') + state = COMMENT; + *q = ' '; + break; + } + if (state == S_COMM) state = PLAIN; + else if (state == E_COMM) state = COMMENT; + return (state == COMMENT); +} + +static int +zpp_do(char *fnm) +{ char buf[2048], buf2[MAXLINE], *p; int n, on; + FILE *inp; int lno = 0, nw_lno = 0; + + if ((inp = fopen(fnm, "r")) == NULL) + { fprintf(stdout, "spin: error, '%s': No such file\n", fnm); + return 0; /* 4.1.2 was stderr */ + } + printing[0] = if_truth[0] = 1; + fprintf(outpp, "#line %d \"%s\"\n", lno+1, fnm); + while (fgets(buf, MAXLINE, inp)) + { lno++; n = (int) strlen(buf); + on = 0; nw_lno = 0; + while (n > 2 && buf[n-2] == '\\') + { buf[n-2] = '\0'; +feedme: if (!fgets(buf2, MAXLINE, inp)) + { debug("zpp: unexpected EOF ln %d\n", lno); + return 0; /* switch to cpp */ + } + lno++; + if (n + (int) strlen(buf2) >= 2048) + { debug("zpp: line %d too long\n", lno); + return 0; + } + strcat(buf, buf2); + n = (int) strlen(buf); + } + if (in_comment(&buf[on])) + { buf[n-1] = '\0'; /* eat newline */ + on = n-1; nw_lno = 1; + goto feedme; + } + p = buf + strspn(buf, " \t"); + if (nw_lno && *p != '#') + fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); + if (*p == '#') + { if (!process(p+1, lno+1, fnm)) + return 0; + } else if (printing[if_depth]) + fprintf(outpp, "%s", apply(buf)); + } + fclose(inp); + return 1; +} + +int +try_zpp(char *fnm, char *onm) +{ int r; + if ((outpp = fopen(onm, "w")) == NULL) + return 0; + r = zpp_do(fnm); + fclose(outpp); + return r; /* 1 = ok; 0 = use cpp */ +} + +static struct Directives { + int len; + char *directive; + int (*handler)(char *); + int interp; +} s[] = { + { 6, "define", do_define, 1 }, + { 4, "else", do_else, 0 }, + { 5, "endif", do_endif, 0 }, + { 5, "ifdef", do_ifdef, 0 }, + { 6, "ifndef", do_ifndef, 0 }, + { 2, "if", do_if, 0 }, + { 7, "include", do_include, 1 }, + { 8, "undefine", do_undefine, 1 }, +}; + +static int +process(char *q, int lno, char *fnm) +{ char *p; int i, r; + + for (p = q; *p; p++) + if (*p != ' ' && *p != '\t') + break; + for (i = 0; i < (int) (sizeof(s)/sizeof(struct Directives)); i++) + if (!strncmp(s[i].directive, p, s[i].len)) + { if (s[i].interp + && !printing[if_depth]) + return 1; + fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); + r = s[i].handler(p + s[i].len); + if (i == 6) /* include */ + fprintf(outpp, "#line %d \"%s\"\n", lno, fnm); + return r; + } + + debug("zpp: unrecognized directive: %s", p); + return 0; +} +#endif diff --git a/sys/src/cmd/spin/ps_msc.c b/sys/src/cmd/spin/ps_msc.c new file mode 100755 index 000000000..0aa764be9 --- /dev/null +++ b/sys/src/cmd/spin/ps_msc.c @@ -0,0 +1,448 @@ +/***** spin: ps_msc.c *****/ + +/* Copyright (c) 1997-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* The Postscript generation code below was written by Gerard J. Holzmann */ +/* in June 1997. Parts of the prolog template are based on similar boiler */ +/* plate in the Tcl/Tk distribution. This code is used to support Spin's */ +/* option M for generating a Postscript file from a simulation run. */ + +#include "spin.h" +#include "version.h" + +#ifdef PC +extern void free(void *); +#endif + +static char *PsPre[] = { + "%%%%Pages: (atend)", + "%%%%PageOrder: Ascend", + "%%%%DocumentData: Clean7Bit", + "%%%%Orientation: Portrait", + "%%%%DocumentNeededResources: font Courier-Bold", + "%%%%EndComments", + "", + "%%%%BeginProlog", + "50 dict begin", + "", + "/baseline 0 def", + "/height 0 def", + "/justify 0 def", + "/lineLength 0 def", + "/spacing 0 def", + "/stipple 0 def", + "/strings 0 def", + "/xoffset 0 def", + "/yoffset 0 def", + "", + "/ISOEncode {", + " dup length dict begin", + " {1 index /FID ne {def} {pop pop} ifelse} forall", + " /Encoding ISOLatin1Encoding def", + " currentdict", + " end", + " /Temporary exch definefont", + "} bind def", + "", + "/AdjustColor {", + " CL 2 lt {", + " currentgray", + " CL 0 eq {", + " .5 lt {0} {1} ifelse", + " } if", + " setgray", + " } if", + "} bind def", + "", + "/DrawText {", + " /stipple exch def", + " /justify exch def", + " /yoffset exch def", + " /xoffset exch def", + " /spacing exch def", + " /strings exch def", + " /lineLength 0 def", + " strings {", + " stringwidth pop", + " dup lineLength gt {/lineLength exch def} {pop} ifelse", + " newpath", + " } forall", + " 0 0 moveto (TXygqPZ) false charpath", + " pathbbox dup /baseline exch def", + " exch pop exch sub /height exch def pop", + " newpath", + " translate", + " lineLength xoffset mul", + " strings length 1 sub spacing mul height add yoffset mul translate", + " justify lineLength mul baseline neg translate", + " strings {", + " dup stringwidth pop", + " justify neg mul 0 moveto", + " stipple {", + " gsave", + " /char (X) def", + " {", + " char 0 3 -1 roll put", + " currentpoint", + " gsave", + " char true charpath clip StippleText", + " grestore", + " char stringwidth translate", + " moveto", + " } forall", + " grestore", + " } {show} ifelse", + " 0 spacing neg translate", + " } forall", + "} bind def", + "%%%%EndProlog", + "%%%%BeginSetup", + "/CL 2 def", + "%%%%IncludeResource: font Courier-Bold", + "%%%%EndSetup", + 0, +}; + +int MH = 600; /* page height - can be scaled */ +int oMH = 600; /* page height - not scaled */ +#define MW 500 /* page width */ +#define LH 100 /* bottom margin */ +#define RH 100 /* right margin */ +#define WW 50 /* distance between process lines */ +#define HH 8 /* vertical distance between steps */ +#define PH 14 /* height of process-tag headers */ + +static FILE *pfd; +static char **I; /* initial procs */ +static int *D,*R; /* maps between depth and ldepth */ +static short *M; /* x location of each box at index y */ +static short *T; /* y index of match for each box at index y */ +static char **L; /* text labels */ +static char *ProcLine; /* active processes */ +static int pspno = 0; /* postscript page */ +static int ldepth = 1; +static int maxx, TotSteps = 2*4096; /* max nr of steps, about 40 pages */ +static float Scaler = (float) 1.0; + +extern int ntrail, s_trail, pno, depth; +extern Symbol *oFname; +extern void exit(int); +void putpages(void); +void spitbox(int, int, int, char *); + +void +putlegend(void) +{ + fprintf(pfd, "gsave\n"); + fprintf(pfd, "/Courier-Bold findfont 8 scalefont "); + fprintf(pfd, "ISOEncode setfont\n"); + fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n"); + fprintf(pfd, "%d %d [\n", MW/2, LH+oMH+ 5*HH); + fprintf(pfd, " (%s -- %s -- MSC -- %d)\n] 10 -0.5 0.5 0 ", + Version, oFname?oFname->name:"", pspno); + fprintf(pfd, "false DrawText\ngrestore\n"); +} + +void +startpage(void) +{ int i; + + pspno++; + fprintf(pfd, "%%%%Page: %d %d\n", pspno, pspno); + putlegend(); + + for (i = TotSteps-1; i >= 0; i--) + { if (!I[i]) continue; + spitbox(i, RH, -PH, I[i]); + } + + fprintf(pfd, "save\n"); + fprintf(pfd, "10 %d moveto\n", LH+oMH+5); + fprintf(pfd, "%d %d lineto\n", RH+MW, LH+oMH+5); + fprintf(pfd, "%d %d lineto\n", RH+MW, LH); + fprintf(pfd, "10 %d lineto\n", LH); + fprintf(pfd, "closepath clip newpath\n"); + fprintf(pfd, "%f %f translate\n", + (float) RH, (float) LH); + memset(ProcLine, 0, 256*sizeof(char)); + if (Scaler != 1.0) + fprintf(pfd, "%f %f scale\n", Scaler, Scaler); +} + +void +putprelude(void) +{ char snap[256]; FILE *fd; + + sprintf(snap, "%s.ps", oFname?oFname->name:"msc"); + if (!(pfd = fopen(snap, "w"))) + fatal("cannot create file '%s'", snap); + + fprintf(pfd, "%%!PS-Adobe-2.0\n"); + fprintf(pfd, "%%%%Creator: %s\n", Version); + fprintf(pfd, "%%%%Title: MSC %s\n", oFname?oFname->name:"--"); + fprintf(pfd, "%%%%BoundingBox: 119 154 494 638\n"); + ntimes(pfd, 0, 1, PsPre); + + if (s_trail) + { if (ntrail) + sprintf(snap, "%s%d.trail", oFname?oFname->name:"msc", ntrail); + else + sprintf(snap, "%s.trail", oFname?oFname->name:"msc"); + if (!(fd = fopen(snap, "r"))) + { snap[strlen(snap)-2] = '\0'; + if (!(fd = fopen(snap, "r"))) + fatal("cannot open trail file", (char *) 0); + } + TotSteps = 1; + while (fgets(snap, 256, fd)) TotSteps++; + fclose(fd); + } + R = (int *) emalloc(TotSteps * sizeof(int)); + D = (int *) emalloc(TotSteps * sizeof(int)); + M = (short *) emalloc(TotSteps * sizeof(short)); + T = (short *) emalloc(TotSteps * sizeof(short)); + L = (char **) emalloc(TotSteps * sizeof(char *)); + I = (char **) emalloc(TotSteps * sizeof(char *)); + ProcLine = (char *) emalloc(1024 * sizeof(char)); + startpage(); +} + +void +putpostlude(void) +{ putpages(); + fprintf(pfd, "%%%%Trailer\n"); + fprintf(pfd, "end\n"); + fprintf(pfd, "%%%%Pages: %d\n", pspno); + fprintf(pfd, "%%%%EOF\n"); + fclose(pfd); + /* stderr, in case user redirected output */ + fprintf(stderr, "spin: wrote %d pages into '%s.ps'\n", + pspno, oFname?oFname->name:"msc"); + exit(0); +} +void +psline(int x0, int iy0, int x1, int iy1, float r, float g, float b, int w) +{ int y0 = MH-iy0; + int y1 = MH-iy1; + + if (y1 > y0) y1 -= MH; + + fprintf(pfd, "gsave\n"); + fprintf(pfd, "%d %d moveto\n", x0*WW, y0); + fprintf(pfd, "%d %d lineto\n", x1*WW, y1); + fprintf(pfd, "%d setlinewidth\n", w); + fprintf(pfd, "0 setlinecap\n"); + fprintf(pfd, "1 setlinejoin\n"); + fprintf(pfd, "%f %f %f setrgbcolor AdjustColor\n", r,g,b); + fprintf(pfd, "stroke\ngrestore\n"); +} + +void +colbox(int x, int y, int w, int h, float r, float g, float b) +{ fprintf(pfd, "%d %d moveto\n", x - w, y-h); + fprintf(pfd, "%d %d lineto\n", x + w, y-h); + fprintf(pfd, "%d %d lineto\n", x + w, y+h); + fprintf(pfd, "%d %d lineto\n", x - w, y+h); + fprintf(pfd, "%d %d lineto\n", x - w, y-h); + fprintf(pfd, "%f %f %f setrgbcolor AdjustColor\n", r,g,b); + fprintf(pfd, "closepath fill\n"); +} + +void +putgrid(int p) +{ int i; + + for (i = p ; i >= 0; i--) + if (!ProcLine[i]) + { psline(i,0, i,MH-1, + (float) 0.4, (float) 0.4, (float) 1.0, 1); + ProcLine[i] = 1; + } +} + +void +putarrow(int from, int to) +{ + T[D[from]] = D[to]; +} + +void +stepnumber(int i) +{ int y = MH-(i*HH)%MH; + + fprintf(pfd, "gsave\n"); + fprintf(pfd, "/Courier-Bold findfont 6 scalefont "); + fprintf(pfd, "ISOEncode setfont\n"); + fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n"); + fprintf(pfd, "%d %d [\n", -40, y); + fprintf(pfd, " (%d)\n] 10 -0.5 0.5 0 ", R[i]); + fprintf(pfd, "false DrawText\ngrestore\n"); + fprintf(pfd, "%d %d moveto\n", -20, y); + fprintf(pfd, "%d %d lineto\n", M[i]*WW, y); + fprintf(pfd, "1 setlinewidth\n0 setlinecap\n1 setlinejoin\n"); + fprintf(pfd, "0.92 0.92 0.92 setrgbcolor AdjustColor\n"); + fprintf(pfd, "stroke\n"); +} + +void +spitbox(int x, int dx, int y, char *s) +{ float r,g,b, bw; int a; char d[256]; + + if (!dx) + { stepnumber(y); + putgrid(x); + } + bw = (float)2.7*(float)strlen(s); + colbox(x*WW+dx, MH-(y*HH)%MH, (int) (bw+1.0), + 5, (float) 0.,(float) 0.,(float) 0.); + if (s[0] == '~') + { switch (s[1]) { + case 'B': r = (float) 0.2; g = (float) 0.2; b = (float) 1.; + break; + case 'G': r = (float) 0.2; g = (float) 1.; b = (float) 0.2; + break; + case 'R': + default : r = (float) 1.; g = (float) 0.2; b = (float) 0.2; + break; + } + s += 2; + } else if (strchr(s, '!')) + { r = (float) 1.; g = (float) 1.; b = (float) 1.; + } else if (strchr(s, '?')) + { r = (float) 0.; g = (float) 1.; b = (float) 1.; + } else + { r = (float) 1.; g = (float) 1.; b = (float) 0.; + if (!dx + && sscanf(s, "%d:%s", &a, d) == 2 /* was &d */ + && a >= 0 && a < TotSteps) + { if (!I[a] + || strlen(I[a]) <= strlen(s)) + I[a] = emalloc((int) strlen(s)+1); + strcpy(I[a], s); + } } + colbox(x*WW+dx, MH-(y*HH)%MH, (int) bw, 4, r,g,b); + fprintf(pfd, "gsave\n"); + fprintf(pfd, "/Courier-Bold findfont 8 scalefont "); + fprintf(pfd, "ISOEncode setfont\n"); + fprintf(pfd, "0.000 0.000 0.000 setrgbcolor AdjustColor\n"); + fprintf(pfd, "%d %d [\n", x*WW+dx, MH-(y*HH)%MH); + fprintf(pfd, " (%s)\n] 10 -0.5 0.5 0 ", s); + fprintf(pfd, "false DrawText\ngrestore\n"); +} + +void +putpages(void) +{ int i, lasti=0; float nmh; + + if (maxx*WW > MW-RH/2) + { Scaler = (float) (MW-RH/2) / (float) (maxx*WW); + fprintf(pfd, "%f %f scale\n", Scaler, Scaler); + nmh = (float) MH; nmh /= Scaler; MH = (int) nmh; + } + + for (i = TotSteps-1; i >= 0; i--) + { if (!I[i]) continue; + spitbox(i, 0, 0, I[i]); + } + if (ldepth >= TotSteps) ldepth = TotSteps-1; + for (i = 0; i <= ldepth; i++) + { if (!M[i] && !L[i]) continue; /* no box here */ + if (6+i*HH >= MH*pspno) + { fprintf(pfd, "showpage\nrestore\n"); startpage(); } + if (T[i] > 0) /* red arrow */ + { int reali = i*HH; + int realt = T[i]*HH; + int topop = (reali)/MH; topop *= MH; + reali -= topop; realt -= topop; + + if (M[i] == M[T[i]] && reali == realt) + /* an rv handshake */ + psline( M[lasti], reali+2-3*HH/2, + M[i], reali, + (float) 1.,(float) 0.,(float) 0., 2); + else + psline( M[i], reali, + M[T[i]], realt, + (float) 1.,(float) 0.,(float) 0., 2); + + if (realt >= MH) T[T[i]] = -i; + + } else if (T[i] < 0) /* arrow from prev page */ + { int reali = (-T[i])*HH; + int realt = i*HH; + int topop = (realt)/MH; topop *= MH; + reali -= topop; realt -= topop; + + psline( M[-T[i]], reali, + M[i], realt, + (float) 1., (float) 0., (float) 0., 2); + } + if (L[i]) + { spitbox(M[i], 0, i, L[i]); + free(L[i]); + lasti = i; + } + } + fprintf(pfd, "showpage\nrestore\n"); +} + +void +putbox(int x) +{ + if (ldepth >= TotSteps) + { putpostlude(); + fprintf(stderr, "max length of %d steps exceeded\n", + TotSteps); + fatal("postscript file truncated", (char *) 0); + } + M[ldepth] = x; + if (x > maxx) maxx = x; +} + +void +pstext(int x, char *s) +{ char *tmp = emalloc((int) strlen(s)+1); + + strcpy(tmp, s); + if (depth == 0) + I[x] = tmp; + else + { putbox(x); + if (depth >= TotSteps || ldepth >= TotSteps) + { fprintf(stderr, "max nr of %d steps exceeded\n", + TotSteps); + fatal("aborting", (char *) 0); + } + + D[depth] = ldepth; + R[ldepth] = depth; + L[ldepth] = tmp; + ldepth += 2; + } +} + +void +dotag(FILE *fd, char *s) +{ extern int columns, notabs; extern RunList *X; + int i = (!strncmp(s, "MSC: ", 5))?5:0; + int pid = s_trail ? pno : (X?X->pid:0); + + if (columns == 2) + pstext(pid, &s[i]); + else + { if (!notabs) + { printf(" "); + for (i = 0; i <= pid; i++) + printf(" "); + } + fprintf(fd, "%s", s); + fflush(fd); + } +} diff --git a/sys/src/cmd/spin/reprosrc.c b/sys/src/cmd/spin/reprosrc.c new file mode 100755 index 000000000..0d4ba6be7 --- /dev/null +++ b/sys/src/cmd/spin/reprosrc.c @@ -0,0 +1,136 @@ +/***** spin: reprosrc.c *****/ + +/* Copyright (c) 2002-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include <stdio.h> +#include "spin.h" +#include "y.tab.h" + +static int indent = 1; + +extern ProcList *rdy; +void repro_seq(Sequence *); + +void +doindent(void) +{ int i; + for (i = 0; i < indent; i++) + printf(" "); +} + +void +repro_sub(Element *e) +{ + doindent(); + switch (e->n->ntyp) { + case D_STEP: + printf("d_step {\n"); + break; + case ATOMIC: + printf("atomic {\n"); + break; + case NON_ATOMIC: + printf(" {\n"); + break; + } + indent++; + repro_seq(e->n->sl->this); + indent--; + + doindent(); + printf(" };\n"); +} + +void +repro_seq(Sequence *s) +{ Element *e; + Symbol *v; + SeqList *h; + + for (e = s->frst; e; e = e->nxt) + { + v = has_lab(e, 0); + if (v) printf("%s:\n", v->name); + + if (e->n->ntyp == UNLESS) + { printf("/* normal */ {\n"); + repro_seq(e->n->sl->this); + doindent(); + printf("} unless {\n"); + repro_seq(e->n->sl->nxt->this); + doindent(); + printf("}; /* end unless */\n"); + } else if (e->sub) + { + switch (e->n->ntyp) { + case DO: doindent(); printf("do\n"); indent++; break; + case IF: doindent(); printf("if\n"); indent++; break; + } + + for (h = e->sub; h; h = h->nxt) + { indent--; doindent(); indent++; printf("::\n"); + repro_seq(h->this); + printf("\n"); + } + + switch (e->n->ntyp) { + case DO: indent--; doindent(); printf("od;\n"); break; + case IF: indent--; doindent(); printf("fi;\n"); break; + } + } else + { if (e->n->ntyp == ATOMIC + || e->n->ntyp == D_STEP + || e->n->ntyp == NON_ATOMIC) + repro_sub(e); + else if (e->n->ntyp != '.' + && e->n->ntyp != '@' + && e->n->ntyp != BREAK) + { + doindent(); + if (e->n->ntyp == C_CODE) + { printf("c_code "); + plunk_inline(stdout, e->n->sym->name, 1); + } else if (e->n->ntyp == 'c' + && e->n->lft->ntyp == C_EXPR) + { printf("c_expr { "); + plunk_expr(stdout, e->n->lft->sym->name); + printf("} ->\n"); + } else + { comment(stdout, e->n, 0); + printf(";\n"); + } } + } + if (e == s->last) + break; + } +} + +void +repro_proc(ProcList *p) +{ + if (!p) return; + if (p->nxt) repro_proc(p->nxt); + + if (p->det) printf("D"); /* deterministic */ + printf("proctype %s()", p->n->name); + if (p->prov) + { printf(" provided "); + comment(stdout, p->prov, 0); + } + printf("\n{\n"); + repro_seq(p->s); + printf("}\n"); +} + +void +repro_src(void) +{ + repro_proc(rdy); +} diff --git a/sys/src/cmd/spin/run.c b/sys/src/cmd/spin/run.c new file mode 100755 index 000000000..c336feef2 --- /dev/null +++ b/sys/src/cmd/spin/run.c @@ -0,0 +1,602 @@ +/***** spin: run.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include <stdlib.h> +#include "spin.h" +#include "y.tab.h" + +extern RunList *X, *run; +extern Symbol *Fname; +extern Element *LastStep; +extern int Rvous, lineno, Tval, interactive, MadeChoice; +extern int TstOnly, verbose, s_trail, xspin, jumpsteps, depth; +extern int nproc, nstop, no_print, like_java; + +static long Seed = 1; +static int E_Check = 0, Escape_Check = 0; + +static int eval_sync(Element *); +static int pc_enabled(Lextok *n); +extern void sr_buf(int, int); + +void +Srand(unsigned int s) +{ Seed = s; +} + +long +Rand(void) +{ /* CACM 31(10), Oct 1988 */ + Seed = 16807*(Seed%127773) - 2836*(Seed/127773); + if (Seed <= 0) Seed += 2147483647; + return Seed; +} + +Element * +rev_escape(SeqList *e) +{ Element *r; + + if (!e) + return (Element *) 0; + + if ((r = rev_escape(e->nxt)) != ZE) /* reversed order */ + return r; + + return eval_sub(e->this->frst); +} + +Element * +eval_sub(Element *e) +{ Element *f, *g; + SeqList *z; + int i, j, k; + + if (!e->n) + return ZE; +#ifdef DEBUG + printf("\n\teval_sub(%d %s: line %d) ", + e->Seqno, e->esc?"+esc":"", e->n?e->n->ln:0); + comment(stdout, e->n, 0); + printf("\n"); +#endif + if (e->n->ntyp == GOTO) + { if (Rvous) return ZE; + LastStep = e; f = get_lab(e->n, 1); + cross_dsteps(e->n, f->n); + return f; + } + if (e->n->ntyp == UNLESS) + { /* escapes were distributed into sequence */ + return eval_sub(e->sub->this->frst); + } else if (e->sub) /* true for IF, DO, and UNLESS */ + { Element *has_else = ZE; + Element *bas_else = ZE; + int nr_else = 0, nr_choices = 0; + + if (interactive + && !MadeChoice && !E_Check + && !Escape_Check + && !(e->status&(D_ATOM)) + && depth >= jumpsteps) + { printf("Select stmnt ("); + whoruns(0); printf(")\n"); + if (nproc-nstop > 1) + printf("\tchoice 0: other process\n"); + } + for (z = e->sub, j=0; z; z = z->nxt) + { j++; + if (interactive + && !MadeChoice && !E_Check + && !Escape_Check + && !(e->status&(D_ATOM)) + && depth >= jumpsteps + && z->this->frst + && (xspin || (verbose&32) || Enabled0(z->this->frst))) + { if (z->this->frst->n->ntyp == ELSE) + { has_else = (Rvous)?ZE:z->this->frst->nxt; + nr_else = j; + continue; + } + printf("\tchoice %d: ", j); +#if 0 + if (z->this->frst->n) + printf("line %d, ", z->this->frst->n->ln); +#endif + if (!Enabled0(z->this->frst)) + printf("unexecutable, "); + else + nr_choices++; + comment(stdout, z->this->frst->n, 0); + printf("\n"); + } } + + if (nr_choices == 0 && has_else) + printf("\tchoice %d: (else)\n", nr_else); + + if (interactive && depth >= jumpsteps + && !Escape_Check + && !(e->status&(D_ATOM)) + && !E_Check) + { if (!MadeChoice) + { char buf[256]; + if (xspin) + printf("Make Selection %d\n\n", j); + else + printf("Select [0-%d]: ", j); + fflush(stdout); + scanf("%s", buf); + if (isdigit(buf[0])) + k = atoi(buf); + else + { if (buf[0] == 'q') + alldone(0); + k = -1; + } + } else + { k = MadeChoice; + MadeChoice = 0; + } + if (k < 1 || k > j) + { if (k != 0) printf("\tchoice outside range\n"); + return ZE; + } + k--; + } else + { if (e->n && e->n->indstep >= 0) + k = 0; /* select 1st executable guard */ + else + k = Rand()%j; /* nondeterminism */ + } + has_else = ZE; + bas_else = ZE; + for (i = 0, z = e->sub; i < j+k; i++) + { if (z->this->frst + && z->this->frst->n->ntyp == ELSE) + { bas_else = z->this->frst; + has_else = (Rvous)?ZE:bas_else->nxt; + if (!interactive || depth < jumpsteps + || Escape_Check + || (e->status&(D_ATOM))) + { z = (z->nxt)?z->nxt:e->sub; + continue; + } + } + if (z->this->frst + && ((z->this->frst->n->ntyp == ATOMIC + || z->this->frst->n->ntyp == D_STEP) + && z->this->frst->n->sl->this->frst->n->ntyp == ELSE)) + { bas_else = z->this->frst->n->sl->this->frst; + has_else = (Rvous)?ZE:bas_else->nxt; + if (!interactive || depth < jumpsteps + || Escape_Check + || (e->status&(D_ATOM))) + { z = (z->nxt)?z->nxt:e->sub; + continue; + } + } + if (i >= k) + { if ((f = eval_sub(z->this->frst)) != ZE) + return f; + else if (interactive && depth >= jumpsteps + && !(e->status&(D_ATOM))) + { if (!E_Check && !Escape_Check) + printf("\tunexecutable\n"); + return ZE; + } } + z = (z->nxt)?z->nxt:e->sub; + } + LastStep = bas_else; + return has_else; + } else + { if (e->n->ntyp == ATOMIC + || e->n->ntyp == D_STEP) + { f = e->n->sl->this->frst; + g = e->n->sl->this->last; + g->nxt = e->nxt; + if (!(g = eval_sub(f))) /* atomic guard */ + return ZE; + return g; + } else if (e->n->ntyp == NON_ATOMIC) + { f = e->n->sl->this->frst; + g = e->n->sl->this->last; + g->nxt = e->nxt; /* close it */ + return eval_sub(f); + } else if (e->n->ntyp == '.') + { if (!Rvous) return e->nxt; + return eval_sub(e->nxt); + } else + { SeqList *x; + if (!(e->status & (D_ATOM)) + && e->esc && verbose&32) + { printf("Stmnt ["); + comment(stdout, e->n, 0); + printf("] has escape(s): "); + for (x = e->esc; x; x = x->nxt) + { printf("["); + g = x->this->frst; + if (g->n->ntyp == ATOMIC + || g->n->ntyp == NON_ATOMIC) + g = g->n->sl->this->frst; + comment(stdout, g->n, 0); + printf("] "); + } + printf("\n"); + } +#if 0 + if (!(e->status & D_ATOM)) /* escapes don't reach inside d_steps */ + /* 4.2.4: only the guard of a d_step can have an escape */ +#endif + { Escape_Check++; + if (like_java) + { if ((g = rev_escape(e->esc)) != ZE) + { if (verbose&4) + printf("\tEscape taken\n"); + Escape_Check--; + return g; + } + } else + { for (x = e->esc; x; x = x->nxt) + { if ((g = eval_sub(x->this->frst)) != ZE) + { if (verbose&4) + printf("\tEscape taken\n"); + Escape_Check--; + return g; + } } } + Escape_Check--; + } + + switch (e->n->ntyp) { + case TIMEOUT: case RUN: + case PRINT: case PRINTM: + case C_CODE: case C_EXPR: + case ASGN: case ASSERT: + case 's': case 'r': case 'c': + /* toplevel statements only */ + LastStep = e; + default: + break; + } + if (Rvous) + { + return (eval_sync(e))?e->nxt:ZE; + } + return (eval(e->n))?e->nxt:ZE; + } + } + return ZE; /* not reached */ +} + +static int +eval_sync(Element *e) +{ /* allow only synchronous receives + and related node types */ + Lextok *now = (e)?e->n:ZN; + + if (!now + || now->ntyp != 'r' + || now->val >= 2 /* no rv with a poll */ + || !q_is_sync(now)) + { + return 0; + } + + LastStep = e; + return eval(now); +} + +static int +assign(Lextok *now) +{ int t; + + if (TstOnly) return 1; + + switch (now->rgt->ntyp) { + case FULL: case NFULL: + case EMPTY: case NEMPTY: + case RUN: case LEN: + t = BYTE; + break; + default: + t = Sym_typ(now->rgt); + break; + } + typ_ck(Sym_typ(now->lft), t, "assignment"); + return setval(now->lft, eval(now->rgt)); +} + +static int +nonprogress(void) /* np_ */ +{ RunList *r; + + for (r = run; r; r = r->nxt) + { if (has_lab(r->pc, 4)) /* 4=progress */ + return 0; + } + return 1; +} + +int +eval(Lextok *now) +{ + if (now) { + lineno = now->ln; + Fname = now->fn; +#ifdef DEBUG + printf("eval "); + comment(stdout, now, 0); + printf("\n"); +#endif + switch (now->ntyp) { + case CONST: return now->val; + case '!': return !eval(now->lft); + case UMIN: return -eval(now->lft); + case '~': return ~eval(now->lft); + + case '/': return (eval(now->lft) / eval(now->rgt)); + case '*': return (eval(now->lft) * eval(now->rgt)); + case '-': return (eval(now->lft) - eval(now->rgt)); + case '+': return (eval(now->lft) + eval(now->rgt)); + case '%': return (eval(now->lft) % eval(now->rgt)); + case LT: return (eval(now->lft) < eval(now->rgt)); + case GT: return (eval(now->lft) > eval(now->rgt)); + case '&': return (eval(now->lft) & eval(now->rgt)); + case '^': return (eval(now->lft) ^ eval(now->rgt)); + case '|': return (eval(now->lft) | eval(now->rgt)); + case LE: return (eval(now->lft) <= eval(now->rgt)); + case GE: return (eval(now->lft) >= eval(now->rgt)); + case NE: return (eval(now->lft) != eval(now->rgt)); + case EQ: return (eval(now->lft) == eval(now->rgt)); + case OR: return (eval(now->lft) || eval(now->rgt)); + case AND: return (eval(now->lft) && eval(now->rgt)); + case LSHIFT: return (eval(now->lft) << eval(now->rgt)); + case RSHIFT: return (eval(now->lft) >> eval(now->rgt)); + case '?': return (eval(now->lft) ? eval(now->rgt->lft) + : eval(now->rgt->rgt)); + + case 'p': return remotevar(now); /* _p for remote reference */ + case 'q': return remotelab(now); + case 'R': return qrecv(now, 0); /* test only */ + case LEN: return qlen(now); + case FULL: return (qfull(now)); + case EMPTY: return (qlen(now)==0); + case NFULL: return (!qfull(now)); + case NEMPTY: return (qlen(now)>0); + case ENABLED: if (s_trail) return 1; + return pc_enabled(now->lft); + case EVAL: return eval(now->lft); + case PC_VAL: return pc_value(now->lft); + case NONPROGRESS: return nonprogress(); + case NAME: return getval(now); + + case TIMEOUT: return Tval; + case RUN: return TstOnly?1:enable(now); + + case 's': return qsend(now); /* send */ + case 'r': return qrecv(now, 1); /* receive or poll */ + case 'c': return eval(now->lft); /* condition */ + case PRINT: return TstOnly?1:interprint(stdout, now); + case PRINTM: return TstOnly?1:printm(stdout, now); + case ASGN: return assign(now); + + case C_CODE: printf("%s:\t", now->sym->name); + plunk_inline(stdout, now->sym->name, 0); + return 1; /* uninterpreted */ + + case C_EXPR: printf("%s:\t", now->sym->name); + plunk_expr(stdout, now->sym->name); + printf("\n"); + return 1; /* uninterpreted */ + + case ASSERT: if (TstOnly || eval(now->lft)) return 1; + non_fatal("assertion violated", (char *) 0); + printf("spin: text of failed assertion: assert("); + comment(stdout, now->lft, 0); + printf(")\n"); + if (s_trail && !xspin) return 1; + wrapup(1); /* doesn't return */ + + case IF: case DO: case BREAK: case UNLESS: /* compound */ + case '.': return 1; /* return label for compound */ + case '@': return 0; /* stop state */ + case ELSE: return 1; /* only hit here in guided trails */ + default : printf("spin: bad node type %d (run)\n", now->ntyp); + if (s_trail) printf("spin: trail file doesn't match spec?\n"); + fatal("aborting", 0); + }} + return 0; +} + +int +printm(FILE *fd, Lextok *n) +{ extern char Buf[]; + int j; + + Buf[0] = '\0'; + if (!no_print) + if (!s_trail || depth >= jumpsteps) { + if (n->lft->ismtyp) + j = n->lft->val; + else + j = eval(n->lft); + Buf[0] = '\0'; + sr_buf(j, 1); + dotag(fd, Buf); + } + return 1; +} + +int +interprint(FILE *fd, Lextok *n) +{ Lextok *tmp = n->lft; + char c, *s = n->sym->name; + int i, j; char lbuf[512]; + extern char Buf[]; + char tBuf[4096]; + + Buf[0] = '\0'; + if (!no_print) + if (!s_trail || depth >= jumpsteps) { + for (i = 0; i < (int) strlen(s); i++) + switch (s[i]) { + case '\"': break; /* ignore */ + case '\\': + switch(s[++i]) { + case 't': strcat(Buf, "\t"); break; + case 'n': strcat(Buf, "\n"); break; + default: goto onechar; + } + break; + case '%': + if ((c = s[++i]) == '%') + { strcat(Buf, "%"); /* literal */ + break; + } + if (!tmp) + { non_fatal("too few print args %s", s); + break; + } + j = eval(tmp->lft); + tmp = tmp->rgt; + switch(c) { + case 'c': sprintf(lbuf, "%c", j); break; + case 'd': sprintf(lbuf, "%d", j); break; + + case 'e': strcpy(tBuf, Buf); /* event name */ + Buf[0] = '\0'; + sr_buf(j, 1); + strcpy(lbuf, Buf); + strcpy(Buf, tBuf); + break; + + case 'o': sprintf(lbuf, "%o", j); break; + case 'u': sprintf(lbuf, "%u", (unsigned) j); break; + case 'x': sprintf(lbuf, "%x", j); break; + default: non_fatal("bad print cmd: '%s'", &s[i-1]); + lbuf[0] = '\0'; break; + } + goto append; + default: +onechar: lbuf[0] = s[i]; lbuf[1] = '\0'; +append: strcat(Buf, lbuf); + break; + } + dotag(fd, Buf); + } + if (strlen(Buf) > 4096) fatal("printf string too long", 0); + return 1; +} + +static int +Enabled1(Lextok *n) +{ int i; int v = verbose; + + if (n) + switch (n->ntyp) { + case 'c': + if (has_typ(n->lft, RUN)) + return 1; /* conservative */ + /* else fall through */ + default: /* side-effect free */ + verbose = 0; + E_Check++; + i = eval(n); + E_Check--; + verbose = v; + return i; + + case C_CODE: case C_EXPR: + case PRINT: case PRINTM: + case ASGN: case ASSERT: + return 1; + + case 's': + if (q_is_sync(n)) + { if (Rvous) return 0; + TstOnly = 1; verbose = 0; + E_Check++; + i = eval(n); + E_Check--; + TstOnly = 0; verbose = v; + return i; + } + return (!qfull(n)); + case 'r': + if (q_is_sync(n)) + return 0; /* it's never a user-choice */ + n->ntyp = 'R'; verbose = 0; + E_Check++; + i = eval(n); + E_Check--; + n->ntyp = 'r'; verbose = v; + return i; + } + return 0; +} + +int +Enabled0(Element *e) +{ SeqList *z; + + if (!e || !e->n) + return 0; + + switch (e->n->ntyp) { + case '@': + return X->pid == (nproc-nstop-1); + case '.': + return 1; + case GOTO: + if (Rvous) return 0; + return 1; + case UNLESS: + return Enabled0(e->sub->this->frst); + case ATOMIC: + case D_STEP: + case NON_ATOMIC: + return Enabled0(e->n->sl->this->frst); + } + if (e->sub) /* true for IF, DO, and UNLESS */ + { for (z = e->sub; z; z = z->nxt) + if (Enabled0(z->this->frst)) + return 1; + return 0; + } + for (z = e->esc; z; z = z->nxt) + { if (Enabled0(z->this->frst)) + return 1; + } +#if 0 + printf("enabled1 "); + comment(stdout, e->n, 0); + printf(" ==> %s\n", Enabled1(e->n)?"yes":"nope"); +#endif + return Enabled1(e->n); +} + +int +pc_enabled(Lextok *n) +{ int i = nproc - nstop; + int pid = eval(n); + int result = 0; + RunList *Y, *oX; + + if (pid == X->pid) + fatal("used: enabled(pid=thisproc) [%s]", X->n->name); + + for (Y = run; Y; Y = Y->nxt) + if (--i == pid) + { oX = X; X = Y; + result = Enabled0(Y->pc); + X = oX; + break; + } + return result; +} diff --git a/sys/src/cmd/spin/sched.c b/sys/src/cmd/spin/sched.c new file mode 100755 index 000000000..ee5b87403 --- /dev/null +++ b/sys/src/cmd/spin/sched.c @@ -0,0 +1,1025 @@ +/***** spin: sched.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include <stdlib.h> +#include "spin.h" +#include "y.tab.h" + +extern int verbose, s_trail, analyze, no_wrapup; +extern char *claimproc, *eventmap, Buf[]; +extern Ordered *all_names; +extern Symbol *Fname, *context; +extern int lineno, nr_errs, dumptab, xspin, jumpsteps, columns; +extern int u_sync, Elcnt, interactive, TstOnly, cutoff; +extern short has_enabled; +extern int limited_vis; + +RunList *X = (RunList *) 0; +RunList *run = (RunList *) 0; +RunList *LastX = (RunList *) 0; /* previous executing proc */ +ProcList *rdy = (ProcList *) 0; +Element *LastStep = ZE; +int nproc=0, nstop=0, Tval=0; +int Rvous=0, depth=0, nrRdy=0, MadeChoice; +short Have_claim=0, Skip_claim=0; + +static int Priority_Sum = 0; +static void setlocals(RunList *); +static void setparams(RunList *, ProcList *, Lextok *); +static void talk(RunList *); + +void +runnable(ProcList *p, int weight, int noparams) +{ RunList *r = (RunList *) emalloc(sizeof(RunList)); + + r->n = p->n; + r->tn = p->tn; + r->pid = nproc++ - nstop + Skip_claim; + + if ((verbose&4) || (verbose&32)) + printf("Starting %s with pid %d\n", p->n->name, r->pid); + + if (!p->s) + fatal("parsing error, no sequence %s", p->n?p->n->name:"--"); + + r->pc = huntele(p->s->frst, p->s->frst->status, -1); + r->ps = p->s; + + if (p->s->last) + p->s->last->status |= ENDSTATE; /* normal end state */ + + r->nxt = run; + r->prov = p->prov; + r->priority = weight; + if (noparams) setlocals(r); + Priority_Sum += weight; + run = r; +} + +ProcList * +ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov) + /* n=name, p=formals, s=body det=deterministic prov=provided */ +{ ProcList *r = (ProcList *) emalloc(sizeof(ProcList)); + Lextok *fp, *fpt; int j; extern int Npars; + + r->n = n; + r->p = p; + r->s = s; + r->prov = prov; + r->tn = nrRdy++; + r->det = (short) det; + r->nxt = rdy; + rdy = r; + + for (fp = p, j = 0; fp; fp = fp->rgt) + for (fpt = fp->lft; fpt; fpt = fpt->rgt) + j++; /* count # of parameters */ + Npars = max(Npars, j); + + return rdy; +} + +int +find_maxel(Symbol *s) +{ ProcList *p; + + for (p = rdy; p; p = p->nxt) + if (p->n == s) + return p->s->maxel++; + return Elcnt++; +} + +static void +formdump(void) +{ ProcList *p; + Lextok *f, *t; + int cnt; + + for (p = rdy; p; p = p->nxt) + { if (!p->p) continue; + cnt = -1; + for (f = p->p; f; f = f->rgt) /* types */ + for (t = f->lft; t; t = t->rgt) /* formals */ + { if (t->ntyp != ',') + t->sym->Nid = cnt--; /* overload Nid */ + else + t->lft->sym->Nid = cnt--; + } + } +} + +void +announce(char *w) +{ + if (columns) + { extern char Buf[]; + extern int firstrow; + firstrow = 1; + if (columns == 2) + { sprintf(Buf, "%d:%s", + run->pid - Have_claim, run->n->name); + pstext(run->pid - Have_claim, Buf); + } else + printf("proc %d = %s\n", + run->pid - Have_claim, run->n->name); + return; + } + + if (dumptab + || analyze + || s_trail + || !(verbose&4)) + return; + + if (w) + printf(" 0: proc - (%s) ", w); + else + whoruns(1); + printf("creates proc %2d (%s)", + run->pid - Have_claim, + run->n->name); + if (run->priority > 1) + printf(" priority %d", run->priority); + printf("\n"); +} + +#ifndef MAXP +#define MAXP 255 /* matches max nr of processes in verifier */ +#endif + +int +enable(Lextok *m) +{ ProcList *p; + Symbol *s = m->sym; /* proctype name */ + Lextok *n = m->lft; /* actual parameters */ + + if (m->val < 1) m->val = 1; /* minimum priority */ + for (p = rdy; p; p = p->nxt) + if (strcmp(s->name, p->n->name) == 0) + { if (nproc-nstop >= MAXP) + { printf("spin: too many processes (%d max)\n", MAXP); + break; + } + runnable(p, m->val, 0); + announce((char *) 0); + setparams(run, p, n); + setlocals(run); /* after setparams */ + return run->pid - Have_claim + Skip_claim; /* effective simu pid */ + } + return 0; /* process not found */ +} + +void +check_param_count(int i, Lextok *m) +{ ProcList *p; + Symbol *s = m->sym; /* proctype name */ + Lextok *f, *t; /* formal pars */ + int cnt = 0; + + for (p = rdy; p; p = p->nxt) + { if (strcmp(s->name, p->n->name) == 0) + { if (m->lft) /* actual param list */ + { lineno = m->lft->ln; + Fname = m->lft->fn; + } + for (f = p->p; f; f = f->rgt) /* one type at a time */ + for (t = f->lft; t; t = t->rgt) /* count formal params */ + { cnt++; + } + if (i != cnt) + { printf("spin: saw %d parameters, expected %d\n", i, cnt); + non_fatal("wrong number of parameters", ""); + } + break; + } } +} + +void +start_claim(int n) +{ ProcList *p; + RunList *r, *q = (RunList *) 0; + + for (p = rdy; p; p = p->nxt) + if (p->tn == n + && strcmp(p->n->name, ":never:") == 0) + { runnable(p, 1, 1); + goto found; + } + printf("spin: couldn't find claim (ignored)\n"); + Skip_claim = 1; + goto done; +found: + /* move claim to far end of runlist, and reassign it pid 0 */ + if (columns == 2) + { depth = 0; + pstext(0, "0::never:"); + for (r = run; r; r = r->nxt) + { if (!strcmp(r->n->name, ":never:")) + continue; + sprintf(Buf, "%d:%s", + r->pid+1, r->n->name); + pstext(r->pid+1, Buf); + } } + + if (run->pid == 0) return; /* it is the first process started */ + + q = run; run = run->nxt; + q->pid = 0; q->nxt = (RunList *) 0; /* remove */ +done: + Have_claim = 1; + for (r = run; r; r = r->nxt) + { r->pid = r->pid+Have_claim; /* adjust */ + if (!r->nxt) + { r->nxt = q; + break; + } } +} + +int +f_pid(char *n) +{ RunList *r; + int rval = -1; + + for (r = run; r; r = r->nxt) + if (strcmp(n, r->n->name) == 0) + { if (rval >= 0) + { printf("spin: remote ref to proctype %s, ", n); + printf("has more than one match: %d and %d\n", + rval, r->pid); + } else + rval = r->pid; + } + return rval; +} + +void +wrapup(int fini) +{ + limited_vis = 0; + if (columns) + { extern void putpostlude(void); + if (columns == 2) putpostlude(); + if (!no_wrapup) + printf("-------------\nfinal state:\n-------------\n"); + } + if (no_wrapup) + goto short_cut; + if (nproc != nstop) + { int ov = verbose; + printf("#processes: %d\n", nproc-nstop - Have_claim + Skip_claim); + verbose &= ~4; + dumpglobals(); + verbose = ov; + verbose &= ~1; /* no more globals */ + verbose |= 32; /* add process states */ + for (X = run; X; X = X->nxt) + talk(X); + verbose = ov; /* restore */ + } + printf("%d process%s created\n", + nproc - Have_claim + Skip_claim, + (xspin || nproc!=1)?"es":""); +short_cut: + if (xspin) alldone(0); /* avoid an abort from xspin */ + if (fini) alldone(1); +} + +static char is_blocked[256]; + +static int +p_blocked(int p) +{ int i, j; + + is_blocked[p%256] = 1; + for (i = j = 0; i < nproc - nstop; i++) + j += is_blocked[i]; + if (j >= nproc - nstop) + { memset(is_blocked, 0, 256); + return 1; + } + return 0; +} + +static Element * +silent_moves(Element *e) +{ Element *f; + + if (e->n) + switch (e->n->ntyp) { + case GOTO: + if (Rvous) break; + f = get_lab(e->n, 1); + cross_dsteps(e->n, f->n); + return f; /* guard against goto cycles */ + case UNLESS: + return silent_moves(e->sub->this->frst); + case NON_ATOMIC: + case ATOMIC: + case D_STEP: + e->n->sl->this->last->nxt = e->nxt; + return silent_moves(e->n->sl->this->frst); + case '.': + return silent_moves(e->nxt); + } + return e; +} + +static RunList * +pickproc(RunList *Y) +{ SeqList *z; Element *has_else; + short Choices[256]; + int j, k, nr_else = 0; + + if (nproc <= nstop+1) + { X = run; + return NULL; + } + if (!interactive || depth < jumpsteps) + { /* was: j = (int) Rand()%(nproc-nstop); */ + if (Priority_Sum < nproc-nstop) + fatal("cannot happen - weights", (char *)0); + j = (int) Rand()%Priority_Sum; + + while (j - X->priority >= 0) + { j -= X->priority; + Y = X; + X = X->nxt; + if (!X) { Y = NULL; X = run; } + } + } else + { int only_choice = -1; + int no_choice = 0, proc_no_ch, proc_k; + + Tval = 0; /* new 4.2.6 */ +try_again: printf("Select a statement\n"); +try_more: for (X = run, k = 1; X; X = X->nxt) + { if (X->pid > 255) break; + + Choices[X->pid] = (short) k; + + if (!X->pc + || (X->prov && !eval(X->prov))) + { if (X == run) + Choices[X->pid] = 0; + continue; + } + X->pc = silent_moves(X->pc); + if (!X->pc->sub && X->pc->n) + { int unex; + unex = !Enabled0(X->pc); + if (unex) + no_choice++; + else + only_choice = k; + if (!xspin && unex && !(verbose&32)) + { k++; + continue; + } + printf("\tchoice %d: ", k++); + p_talk(X->pc, 0); + if (unex) + printf(" unexecutable,"); + printf(" ["); + comment(stdout, X->pc->n, 0); + if (X->pc->esc) printf(" + Escape"); + printf("]\n"); + } else { + has_else = ZE; + proc_no_ch = no_choice; + proc_k = k; + for (z = X->pc->sub, j=0; z; z = z->nxt) + { Element *y = silent_moves(z->this->frst); + int unex; + if (!y) continue; + + if (y->n->ntyp == ELSE) + { has_else = (Rvous)?ZE:y; + nr_else = k++; + continue; + } + + unex = !Enabled0(y); + if (unex) + no_choice++; + else + only_choice = k; + if (!xspin && unex && !(verbose&32)) + { k++; + continue; + } + printf("\tchoice %d: ", k++); + p_talk(X->pc, 0); + if (unex) + printf(" unexecutable,"); + printf(" ["); + comment(stdout, y->n, 0); + printf("]\n"); + } + if (has_else) + { if (no_choice-proc_no_ch >= (k-proc_k)-1) + { only_choice = nr_else; + printf("\tchoice %d: ", nr_else); + p_talk(X->pc, 0); + printf(" [else]\n"); + } else + { no_choice++; + printf("\tchoice %d: ", nr_else); + p_talk(X->pc, 0); + printf(" unexecutable, [else]\n"); + } } + } } + X = run; + if (k - no_choice < 2 && Tval == 0) + { Tval = 1; + no_choice = 0; only_choice = -1; + goto try_more; + } + if (xspin) + printf("Make Selection %d\n\n", k-1); + else + { if (k - no_choice < 2) + { printf("no executable choices\n"); + alldone(0); + } + printf("Select [1-%d]: ", k-1); + } + if (!xspin && k - no_choice == 2) + { printf("%d\n", only_choice); + j = only_choice; + } else + { char buf[256]; + fflush(stdout); + scanf("%s", buf); + j = -1; + if (isdigit(buf[0])) + j = atoi(buf); + else + { if (buf[0] == 'q') + alldone(0); + } + if (j < 1 || j >= k) + { printf("\tchoice is outside range\n"); + goto try_again; + } } + MadeChoice = 0; + Y = NULL; + for (X = run; X; Y = X, X = X->nxt) + { if (!X->nxt + || X->nxt->pid > 255 + || j < Choices[X->nxt->pid]) + { + MadeChoice = 1+j-Choices[X->pid]; + break; + } } + } + return Y; +} + +void +sched(void) +{ Element *e; + RunList *Y = NULL; /* previous process in run queue */ + RunList *oX; + int go, notbeyond = 0; +#ifdef PC + int bufmax = 100; +#endif + if (dumptab) + { formdump(); + symdump(); + dumplabels(); + return; + } + + if (has_enabled && u_sync > 0) + { printf("spin: error, cannot use 'enabled()' in "); + printf("models with synchronous channels.\n"); + nr_errs++; + } + if (analyze) + { gensrc(); + return; + } else if (s_trail) + { match_trail(); + return; + } + if (claimproc) + printf("warning: never claim not used in random simulation\n"); + if (eventmap) + printf("warning: trace assertion not used in random simulation\n"); + + X = run; + Y = pickproc(Y); + + while (X) + { context = X->n; + if (X->pc && X->pc->n) + { lineno = X->pc->n->ln; + Fname = X->pc->n->fn; + } + if (cutoff > 0 && depth >= cutoff) + { printf("-------------\n"); + printf("depth-limit (-u%d steps) reached\n", cutoff); + break; + } +#ifdef PC + if (xspin && !interactive && --bufmax <= 0) + { int c; /* avoid buffer overflow on pc's */ + printf("spin: type return to proceed\n"); + fflush(stdout); + c = getc(stdin); + if (c == 'q') wrapup(0); + bufmax = 100; + } +#endif + depth++; LastStep = ZE; + oX = X; /* a rendezvous could change it */ + go = 1; + if (X && X->prov && X->pc + && !(X->pc->status & D_ATOM) + && !eval(X->prov)) + { if (!xspin && ((verbose&32) || (verbose&4))) + { p_talk(X->pc, 1); + printf("\t<<Not Enabled>>\n"); + } + go = 0; + } + if (go && (e = eval_sub(X->pc))) + { if (depth >= jumpsteps + && ((verbose&32) || (verbose&4))) + { if (X == oX) + if (!(e->status & D_ATOM) || (verbose&32)) /* no talking in d_steps */ + { p_talk(X->pc, 1); + printf(" ["); + if (!LastStep) LastStep = X->pc; + comment(stdout, LastStep->n, 0); + printf("]\n"); + } + if (verbose&1) dumpglobals(); + if (verbose&2) dumplocal(X); + + if (!(e->status & D_ATOM)) + if (xspin) + printf("\n"); + } + if (oX != X) + { e = silent_moves(e); + notbeyond = 0; + } + oX->pc = e; LastX = X; + + if (!interactive) Tval = 0; + memset(is_blocked, 0, 256); + + if (X->pc && (X->pc->status & (ATOM|L_ATOM)) + && (notbeyond == 0 || oX != X)) + { if ((X->pc->status & L_ATOM)) + notbeyond = 1; + continue; /* no process switch */ + } + } else + { depth--; + if (oX->pc->status & D_ATOM) + non_fatal("stmnt in d_step blocks", (char *)0); + + if (X->pc->n->ntyp == '@' + && X->pid == (nproc-nstop-1)) + { if (X != run && Y != NULL) + Y->nxt = X->nxt; + else + run = X->nxt; + nstop++; + Priority_Sum -= X->priority; + if (verbose&4) + { whoruns(1); + dotag(stdout, "terminates\n"); + } + LastX = X; + if (!interactive) Tval = 0; + if (nproc == nstop) break; + memset(is_blocked, 0, 256); + /* proc X is no longer in runlist */ + X = (X->nxt) ? X->nxt : run; + } else + { if (p_blocked(X->pid)) + { if (Tval) break; + Tval = 1; + if (depth >= jumpsteps) + { oX = X; + X = (RunList *) 0; /* to suppress indent */ + dotag(stdout, "timeout\n"); + X = oX; + } } } } + Y = pickproc(X); + notbeyond = 0; + } + context = ZS; + wrapup(0); +} + +int +complete_rendez(void) +{ RunList *orun = X, *tmp; + Element *s_was = LastStep; + Element *e; + int j, ointer = interactive; + + if (s_trail) + return 1; + if (orun->pc->status & D_ATOM) + fatal("rv-attempt in d_step sequence", (char *)0); + Rvous = 1; + interactive = 0; + + j = (int) Rand()%Priority_Sum; /* randomize start point */ + X = run; + while (j - X->priority >= 0) + { j -= X->priority; + X = X->nxt; + if (!X) X = run; + } + for (j = nproc - nstop; j > 0; j--) + { if (X != orun + && (!X->prov || eval(X->prov)) + && (e = eval_sub(X->pc))) + { if (TstOnly) + { X = orun; + Rvous = 0; + goto out; + } + if ((verbose&32) || (verbose&4)) + { tmp = orun; orun = X; X = tmp; + if (!s_was) s_was = X->pc; + p_talk(s_was, 1); + printf(" ["); + comment(stdout, s_was->n, 0); + printf("]\n"); + tmp = orun; orun = X; X = tmp; + if (!LastStep) LastStep = X->pc; + p_talk(LastStep, 1); + printf(" ["); + comment(stdout, LastStep->n, 0); + printf("]\n"); + } + Rvous = 0; /* before silent_moves */ + X->pc = silent_moves(e); +out: interactive = ointer; + return 1; + } + + X = X->nxt; + if (!X) X = run; + } + Rvous = 0; + X = orun; + interactive = ointer; + return 0; +} + +/***** Runtime - Local Variables *****/ + +static void +addsymbol(RunList *r, Symbol *s) +{ Symbol *t; + int i; + + for (t = r->symtab; t; t = t->next) + if (strcmp(t->name, s->name) == 0) + return; /* it's already there */ + + t = (Symbol *) emalloc(sizeof(Symbol)); + t->name = s->name; + t->type = s->type; + t->hidden = s->hidden; + t->nbits = s->nbits; + t->nel = s->nel; + t->ini = s->ini; + t->setat = depth; + t->context = r->n; + if (s->type != STRUCT) + { if (s->val) /* if already initialized, copy info */ + { t->val = (int *) emalloc(s->nel*sizeof(int)); + for (i = 0; i < s->nel; i++) + t->val[i] = s->val[i]; + } else + (void) checkvar(t, 0); /* initialize it */ + } else + { if (s->Sval) + fatal("saw preinitialized struct %s", s->name); + t->Slst = s->Slst; + t->Snm = s->Snm; + t->owner = s->owner; + /* t->context = r->n; */ + } + t->next = r->symtab; /* add it */ + r->symtab = t; +} + +static void +setlocals(RunList *r) +{ Ordered *walk; + Symbol *sp; + RunList *oX = X; + + X = r; + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (sp + && sp->context + && strcmp(sp->context->name, r->n->name) == 0 + && sp->Nid >= 0 + && (sp->type == UNSIGNED + || sp->type == BIT + || sp->type == MTYPE + || sp->type == BYTE + || sp->type == CHAN + || sp->type == SHORT + || sp->type == INT + || sp->type == STRUCT)) + { if (!findloc(sp)) + non_fatal("setlocals: cannot happen '%s'", + sp->name); + } + } + X = oX; +} + +static void +oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p) +{ int k; int at, ft; + RunList *oX = X; + + if (!a) + fatal("missing actual parameters: '%s'", p->n->name); + if (t->sym->nel != 1) + fatal("array in parameter list, %s", t->sym->name); + k = eval(a->lft); + + at = Sym_typ(a->lft); + X = r; /* switch context */ + ft = Sym_typ(t); + + if (at != ft && (at == CHAN || ft == CHAN)) + { char buf[128], tag1[64], tag2[64]; + (void) sputtype(tag1, ft); + (void) sputtype(tag2, at); + sprintf(buf, "type-clash in params of %s(..), (%s<-> %s)", + p->n->name, tag1, tag2); + non_fatal("%s", buf); + } + t->ntyp = NAME; + addsymbol(r, t->sym); + (void) setval(t, k); + + X = oX; +} + +static void +setparams(RunList *r, ProcList *p, Lextok *q) +{ Lextok *f, *a; /* formal and actual pars */ + Lextok *t; /* list of pars of 1 type */ + + if (q) + { lineno = q->ln; + Fname = q->fn; + } + for (f = p->p, a = q; f; f = f->rgt) /* one type at a time */ + for (t = f->lft; t; t = t->rgt, a = (a)?a->rgt:a) + { if (t->ntyp != ',') + oneparam(r, t, a, p); /* plain var */ + else + oneparam(r, t->lft, a, p); /* expanded struct */ + } +} + +Symbol * +findloc(Symbol *s) +{ Symbol *r; + + if (!X) + { /* fatal("error, cannot eval '%s' (no proc)", s->name); */ + return ZS; + } + for (r = X->symtab; r; r = r->next) + if (strcmp(r->name, s->name) == 0) + break; + if (!r) + { addsymbol(X, s); + r = X->symtab; + } + return r; +} + +int +in_bound(Symbol *r, int n) +{ + if (!r) return 0; + + if (n >= r->nel || n < 0) + { printf("spin: indexing %s[%d] - size is %d\n", + r->name, n, r->nel); + non_fatal("indexing array \'%s\'", r->name); + return 0; + } + return 1; +} + +int +getlocal(Lextok *sn) +{ Symbol *r, *s = sn->sym; + int n = eval(sn->lft); + + r = findloc(s); + if (r && r->type == STRUCT) + return Rval_struct(sn, r, 1); /* 1 = check init */ + if (in_bound(r, n)) + return cast_val(r->type, r->val[n], r->nbits); + return 0; +} + +int +setlocal(Lextok *p, int m) +{ Symbol *r = findloc(p->sym); + int n = eval(p->lft); + + if (in_bound(r, n)) + { if (r->type == STRUCT) + (void) Lval_struct(p, r, 1, m); /* 1 = check init */ + else + { +#if 0 + if (r->nbits > 0) + m = (m & ((1<<r->nbits)-1)); + r->val[n] = m; +#else + r->val[n] = cast_val(r->type, m, r->nbits); +#endif + r->setat = depth; + } } + + return 1; +} + +void +whoruns(int lnr) +{ if (!X) return; + + if (lnr) printf("%3d: ", depth); + printf("proc "); + if (Have_claim && X->pid == 0) + printf(" -"); + else + printf("%2d", X->pid - Have_claim); + printf(" (%s) ", X->n->name); +} + +static void +talk(RunList *r) +{ + if ((verbose&32) || (verbose&4)) + { p_talk(r->pc, 1); + printf("\n"); + if (verbose&1) dumpglobals(); + if (verbose&2) dumplocal(r); + } +} + +void +p_talk(Element *e, int lnr) +{ static int lastnever = -1; + int newnever = -1; + + if (e && e->n) + newnever = e->n->ln; + + if (Have_claim && X && X->pid == 0 + && lastnever != newnever && e) + { if (xspin) + { printf("MSC: ~G line %d\n", newnever); +#if 0 + printf("%3d: proc - (NEVER) line %d \"never\" ", + depth, newnever); + printf("(state 0)\t[printf('MSC: never\\\\n')]\n"); + } else + { printf("%3d: proc - (NEVER) line %d \"never\"\n", + depth, newnever); +#endif + } + lastnever = newnever; + } + + whoruns(lnr); + if (e) + { printf("line %3d %s (state %d)", + e->n?e->n->ln:-1, + e->n?e->n->fn->name:"-", + e->seqno); + if (!xspin + && ((e->status&ENDSTATE) || has_lab(e, 2))) /* 2=end */ + { printf(" <valid end state>"); + } + } +} + +int +remotelab(Lextok *n) +{ int i; + + lineno = n->ln; + Fname = n->fn; + if (n->sym->type != 0 && n->sym->type != LABEL) + { printf("spin: error, type: %d\n", n->sym->type); + fatal("not a labelname: '%s'", n->sym->name); + } + if (n->indstep >= 0) + { fatal("remote ref to label '%s' inside d_step", + n->sym->name); + } + if ((i = find_lab(n->sym, n->lft->sym, 1)) == 0) + fatal("unknown labelname: %s", n->sym->name); + return i; +} + +int +remotevar(Lextok *n) +{ int prno, i, added=0; + RunList *Y, *oX; + Lextok *onl; + Symbol *os; + + lineno = n->ln; + Fname = n->fn; + + if (!n->lft->lft) + prno = f_pid(n->lft->sym->name); + else + { prno = eval(n->lft->lft); /* pid - can cause recursive call */ +#if 0 + if (n->lft->lft->ntyp == CONST) /* user-guessed pid */ +#endif + { prno += Have_claim; + added = Have_claim; + } } + + if (prno < 0) + return 0; /* non-existing process */ +#if 0 + i = nproc - nstop; + for (Y = run; Y; Y = Y->nxt) + { --i; + printf(" %s: i=%d, prno=%d, ->pid=%d\n", Y->n->name, i, prno, Y->pid); + } +#endif + i = nproc - nstop; + for (Y = run; Y; Y = Y->nxt) + if (--i == prno) + { if (strcmp(Y->n->name, n->lft->sym->name) != 0) + { printf("spin: remote reference error on '%s[%d]'\n", + n->lft->sym->name, prno-added); + non_fatal("refers to wrong proctype '%s'", Y->n->name); + } + if (strcmp(n->sym->name, "_p") == 0) + { if (Y->pc) + return Y->pc->seqno; + /* harmless, can only happen with -t */ + return 0; + } +#if 1 + /* new 4.0 allow remote variables */ + oX = X; + X = Y; + + onl = n->lft; + n->lft = n->rgt; + + os = n->sym; + n->sym = findloc(n->sym); + + i = getval(n); + + n->sym = os; + n->lft = onl; + X = oX; + return i; +#else + break; +#endif + } + printf("remote ref: %s[%d] ", n->lft->sym->name, prno-added); + non_fatal("%s not found", n->sym->name); + printf("have only:\n"); + i = nproc - nstop - 1; + for (Y = run; Y; Y = Y->nxt, i--) + if (!strcmp(Y->n->name, n->lft->sym->name)) + printf("\t%d\t%s\n", i, Y->n->name); + + return 0; +} diff --git a/sys/src/cmd/spin/spin.h b/sys/src/cmd/spin/spin.h new file mode 100755 index 000000000..327ec6871 --- /dev/null +++ b/sys/src/cmd/spin/spin.h @@ -0,0 +1,399 @@ +/***** spin: spin.h *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#ifndef SEEN_SPIN_H +#define SEEN_SPIN_H + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +typedef struct Lextok { + unsigned short ntyp; /* node type */ + short ismtyp; /* CONST derived from MTYP */ + int val; /* value attribute */ + int ln; /* line number */ + int indstep; /* part of d_step sequence */ + struct Symbol *fn; /* file name */ + struct Symbol *sym; /* symbol reference */ + struct Sequence *sq; /* sequence */ + struct SeqList *sl; /* sequence list */ + struct Lextok *lft, *rgt; /* children in parse tree */ +} Lextok; + +typedef struct Slicer { + Lextok *n; /* global var, usable as slice criterion */ + short code; /* type of use: DEREF_USE or normal USE */ + short used; /* set when handled */ + struct Slicer *nxt; /* linked list */ +} Slicer; + +typedef struct Access { + struct Symbol *who; /* proctype name of accessor */ + struct Symbol *what; /* proctype name of accessed */ + int cnt, typ; /* parameter nr and, e.g., 's' or 'r' */ + struct Access *lnk; /* linked list */ +} Access; + +typedef struct Symbol { + char *name; + int Nid; /* unique number for the name */ + unsigned short type; /* bit,short,.., chan,struct */ + unsigned char hidden; /* bit-flags: + 1=hide, 2=show, + 4=bit-equiv, 8=byte-equiv, + 16=formal par, 32=inline par, + 64=treat as if local; 128=read at least once + */ + unsigned char colnr; /* for use with xspin during simulation */ + int nbits; /* optional width specifier */ + int nel; /* 1 if scalar, >1 if array */ + int setat; /* last depth value changed */ + int *val; /* runtime value(s), initl 0 */ + Lextok **Sval; /* values for structures */ + + int xu; /* exclusive r or w by 1 pid */ + struct Symbol *xup[2]; /* xr or xs proctype */ + struct Access *access;/* e.g., senders and receives of chan */ + Lextok *ini; /* initial value, or chan-def */ + Lextok *Slst; /* template for structure if struct */ + struct Symbol *Snm; /* name of the defining struct */ + struct Symbol *owner; /* set for names of subfields in typedefs */ + struct Symbol *context; /* 0 if global, or procname */ + struct Symbol *next; /* linked list */ +} Symbol; + +typedef struct Ordered { /* links all names in Symbol table */ + struct Symbol *entry; + struct Ordered *next; +} Ordered; + +typedef struct Queue { + short qid; /* runtime q index */ + int qlen; /* nr messages stored */ + int nslots, nflds; /* capacity, flds/slot */ + int setat; /* last depth value changed */ + int *fld_width; /* type of each field */ + int *contents; /* the values stored */ + int *stepnr; /* depth when each msg was sent */ + struct Queue *nxt; /* linked list */ +} Queue; + +typedef struct FSM_state { /* used in pangen5.c - dataflow */ + int from; /* state number */ + int seen; /* used for dfs */ + int in; /* nr of incoming edges */ + int cr; /* has reachable 1-relevant successor */ + int scratch; + unsigned long *dom, *mod; /* to mark dominant nodes */ + struct FSM_trans *t; /* outgoing edges */ + struct FSM_trans *p; /* incoming edges, predecessors */ + struct FSM_state *nxt; /* linked list of all states */ +} FSM_state; + +typedef struct FSM_trans { /* used in pangen5.c - dataflow */ + int to; + short relevant; /* when sliced */ + short round; /* ditto: iteration when marked */ + struct FSM_use *Val[2]; /* 0=reads, 1=writes */ + struct Element *step; + struct FSM_trans *nxt; +} FSM_trans; + +typedef struct FSM_use { /* used in pangen5.c - dataflow */ + Lextok *n; + Symbol *var; + int special; + struct FSM_use *nxt; +} FSM_use; + +typedef struct Element { + Lextok *n; /* defines the type & contents */ + int Seqno; /* identifies this el within system */ + int seqno; /* identifies this el within a proc */ + int merge; /* set by -O if step can be merged */ + int merge_start; + int merge_single; + short merge_in; /* nr of incoming edges */ + short merge_mark; /* state was generated in merge sequence */ + unsigned char status; /* used by analyzer generator */ + struct FSM_use *dead; /* optional dead variable list */ + struct SeqList *sub; /* subsequences, for compounds */ + struct SeqList *esc; /* zero or more escape sequences */ + struct Element *Nxt; /* linked list - for global lookup */ + struct Element *nxt; /* linked list - program structure */ +} Element; + +typedef struct Sequence { + Element *frst; + Element *last; /* links onto continuations */ + Element *extent; /* last element in original */ + int maxel; /* 1+largest id in sequence */ +} Sequence; + +typedef struct SeqList { + Sequence *this; /* one sequence */ + struct SeqList *nxt; /* linked list */ +} SeqList; + +typedef struct Label { + Symbol *s; + Symbol *c; + Element *e; + int visible; /* label referenced in claim (slice relevant) */ + struct Label *nxt; +} Label; + +typedef struct Lbreak { + Symbol *l; + struct Lbreak *nxt; +} Lbreak; + +typedef struct RunList { + Symbol *n; /* name */ + int tn; /* ordinal of type */ + int pid; /* process id */ + int priority; /* for simulations only */ + Element *pc; /* current stmnt */ + Sequence *ps; /* used by analyzer generator */ + Lextok *prov; /* provided clause */ + Symbol *symtab; /* local variables */ + struct RunList *nxt; /* linked list */ +} RunList; + +typedef struct ProcList { + Symbol *n; /* name */ + Lextok *p; /* parameters */ + Sequence *s; /* body */ + Lextok *prov; /* provided clause */ + short tn; /* ordinal number */ + short det; /* deterministic */ + struct ProcList *nxt; /* linked list */ +} ProcList; + +typedef Lextok *Lexptr; + +#define YYSTYPE Lexptr + +#define ZN (Lextok *)0 +#define ZS (Symbol *)0 +#define ZE (Element *)0 + +#define DONE 1 /* status bits of elements */ +#define ATOM 2 /* part of an atomic chain */ +#define L_ATOM 4 /* last element in a chain */ +#define I_GLOB 8 /* inherited global ref */ +#define DONE2 16 /* used in putcode and main*/ +#define D_ATOM 32 /* deterministic atomic */ +#define ENDSTATE 64 /* normal endstate */ +#define CHECK2 128 + +#define Nhash 255 /* slots in symbol hash-table */ + +#define XR 1 /* non-shared receive-only */ +#define XS 2 /* non-shared send-only */ +#define XX 4 /* overrides XR or XS tag */ + +#define CODE_FRAG 2 /* auto-numbered code-fragment */ +#define CODE_DECL 4 /* auto-numbered c_decl */ +#define PREDEF 3 /* predefined name: _p, _last */ + +#define UNSIGNED 5 /* val defines width in bits */ +#define BIT 1 /* also equal to width in bits */ +#define BYTE 8 /* ditto */ +#define SHORT 16 /* ditto */ +#define INT 32 /* ditto */ +#define CHAN 64 /* not */ +#define STRUCT 128 /* user defined structure name */ + +#define SOMETHINGBIG 65536 +#define RATHERSMALL 512 + +#ifndef max +#define max(a,b) (((a)<(b)) ? (b) : (a)) +#endif + +enum { INIV, PUTV, LOGV }; /* for pangen[14].c */ + +/***** prototype definitions *****/ +Element *eval_sub(Element *); +Element *get_lab(Lextok *, int); +Element *huntele(Element *, int, int); +Element *huntstart(Element *); +Element *target(Element *); + +Lextok *do_unless(Lextok *, Lextok *); +Lextok *expand(Lextok *, int); +Lextok *getuname(Symbol *); +Lextok *mk_explicit(Lextok *, int, int); +Lextok *nn(Lextok *, int, Lextok *, Lextok *); +Lextok *rem_lab(Symbol *, Lextok *, Symbol *); +Lextok *rem_var(Symbol *, Lextok *, Symbol *, Lextok *); +Lextok *tail_add(Lextok *, Lextok *); + +ProcList *ready(Symbol *, Lextok *, Sequence *, int, Lextok *); + +SeqList *seqlist(Sequence *, SeqList *); +Sequence *close_seq(int); + +Symbol *break_dest(void); +Symbol *findloc(Symbol *); +Symbol *has_lab(Element *, int); +Symbol *lookup(char *); +Symbol *prep_inline(Symbol *, Lextok *); + +char *emalloc(int); +long Rand(void); + +int any_oper(Lextok *, int); +int any_undo(Lextok *); +int c_add_sv(FILE *); +int cast_val(int, int, int); +int checkvar(Symbol *, int); +int Cnt_flds(Lextok *); +int cnt_mpars(Lextok *); +int complete_rendez(void); +int enable(Lextok *); +int Enabled0(Element *); +int eval(Lextok *); +int find_lab(Symbol *, Symbol *, int); +int find_maxel(Symbol *); +int full_name(FILE *, Lextok *, Symbol *, int); +int getlocal(Lextok *); +int getval(Lextok *); +int glob_inline(char *); +int has_typ(Lextok *, int); +int in_bound(Symbol *, int); +int interprint(FILE *, Lextok *); +int printm(FILE *, Lextok *); +int ismtype(char *); +int isproctype(char *); +int isutype(char *); +int Lval_struct(Lextok *, Symbol *, int, int); +int main(int, char **); +int pc_value(Lextok *); +int proper_enabler(Lextok *); +int putcode(FILE *, Sequence *, Element *, int, int, int); +int q_is_sync(Lextok *); +int qlen(Lextok *); +int qfull(Lextok *); +int qmake(Symbol *); +int qrecv(Lextok *, int); +int qsend(Lextok *); +int remotelab(Lextok *); +int remotevar(Lextok *); +int Rval_struct(Lextok *, Symbol *, int); +int setlocal(Lextok *, int); +int setval(Lextok *, int); +int sputtype(char *, int); +int Sym_typ(Lextok *); +int tl_main(int, char *[]); +int Width_set(int *, int, Lextok *); +int yyparse(void); +int yywrap(void); +int yylex(void); + +void AST_track(Lextok *, int); +void add_seq(Lextok *); +void alldone(int); +void announce(char *); +void c_state(Symbol *, Symbol *, Symbol *); +void c_add_def(FILE *); +void c_add_loc(FILE *, char *); +void c_add_locinit(FILE *, int, char *); +void c_add_use(FILE *); +void c_chandump(FILE *); +void c_preview(void); +void c_struct(FILE *, char *, Symbol *); +void c_track(Symbol *, Symbol *, Symbol *); +void c_var(FILE *, char *, Symbol *); +void c_wrapper(FILE *); +void chanaccess(void); +void check_param_count(int, Lextok *); +void checkrun(Symbol *, int); +void comment(FILE *, Lextok *, int); +void cross_dsteps(Lextok *, Lextok *); +void doq(Symbol *, int, RunList *); +void dotag(FILE *, char *); +void do_locinits(FILE *); +void do_var(FILE *, int, char *, Symbol *, char *, char *, char *); +void dump_struct(Symbol *, char *, RunList *); +void dumpclaims(FILE *, int, char *); +void dumpglobals(void); +void dumplabels(void); +void dumplocal(RunList *); +void dumpsrc(int, int); +void fatal(char *, char *); +void fix_dest(Symbol *, Symbol *); +void genaddproc(void); +void genaddqueue(void); +void gencodetable(FILE *); +void genheader(void); +void genother(void); +void gensrc(void); +void gensvmap(void); +void genunio(void); +void ini_struct(Symbol *); +void loose_ends(void); +void make_atomic(Sequence *, int); +void match_trail(void); +void no_side_effects(char *); +void nochan_manip(Lextok *, Lextok *, int); +void non_fatal(char *, char *); +void ntimes(FILE *, int, int, char *c[]); +void open_seq(int); +void p_talk(Element *, int); +void pickup_inline(Symbol *, Lextok *); +void plunk_c_decls(FILE *); +void plunk_c_fcts(FILE *); +void plunk_expr(FILE *, char *); +void plunk_inline(FILE *, char *, int); +void prehint(Symbol *); +void preruse(FILE *, Lextok *); +void prune_opts(Lextok *); +void pstext(int, char *); +void pushbreak(void); +void putname(FILE *, char *, Lextok *, int, char *); +void putremote(FILE *, Lextok *, int); +void putskip(int); +void putsrc(Element *); +void putstmnt(FILE *, Lextok *, int); +void putunames(FILE *); +void rem_Seq(void); +void runnable(ProcList *, int, int); +void sched(void); +void setaccess(Symbol *, Symbol *, int, int); +void set_lab(Symbol *, Element *); +void setmtype(Lextok *); +void setpname(Lextok *); +void setptype(Lextok *, int, Lextok *); +void setuname(Lextok *); +void setutype(Lextok *, Symbol *, Lextok *); +void setxus(Lextok *, int); +void Srand(unsigned); +void start_claim(int); +void struct_name(Lextok *, Symbol *, int, char *); +void symdump(void); +void symvar(Symbol *); +void trackchanuse(Lextok *, Lextok *, int); +void trackvar(Lextok *, Lextok *); +void trackrun(Lextok *); +void trapwonly(Lextok *, char *); /* spin.y and main.c */ +void typ2c(Symbol *); +void typ_ck(int, int, char *); +void undostmnt(Lextok *, int); +void unrem_Seq(void); +void unskip(int); +void varcheck(Element *, Element *); +void whoruns(int); +void wrapup(int); +void yyerror(char *, ...); +#endif diff --git a/sys/src/cmd/spin/spin.y b/sys/src/cmd/spin/spin.y new file mode 100755 index 000000000..20c391a89 --- /dev/null +++ b/sys/src/cmd/spin/spin.y @@ -0,0 +1,722 @@ +/***** spin: spin.y *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +%{ +#include "spin.h" +#include <stdarg.h> + +#define YYDEBUG 0 +#define Stop nn(ZN,'@',ZN,ZN) + +extern Symbol *context, *owner; +extern int u_sync, u_async, dumptab; +extern short has_sorted, has_random, has_enabled, has_pcvalue, has_np; +extern short has_code, has_state, has_io; +extern void count_runs(Lextok *); +extern void no_internals(Lextok *); +extern void any_runs(Lextok *); +extern void validref(Lextok *, Lextok *); +extern char yytext[]; + +int Mpars = 0; /* max nr of message parameters */ +int runsafe = 1; /* 1 if all run stmnts are in init */ +int Expand_Ok = 0, realread = 1, IArgs = 0, NamesNotAdded = 0; +char *claimproc = (char *) 0; +char *eventmap = (char *) 0; + +static int Embedded = 0, inEventMap = 0, has_ini = 0; + +%} + +%token ASSERT PRINT PRINTM +%token C_CODE C_DECL C_EXPR C_STATE C_TRACK +%token RUN LEN ENABLED EVAL PC_VAL +%token TYPEDEF MTYPE INLINE LABEL OF +%token GOTO BREAK ELSE SEMI +%token IF FI DO OD SEP +%token ATOMIC NON_ATOMIC D_STEP UNLESS +%token TIMEOUT NONPROGRESS +%token ACTIVE PROCTYPE D_PROCTYPE +%token HIDDEN SHOW ISLOCAL +%token PRIORITY PROVIDED +%token FULL EMPTY NFULL NEMPTY +%token CONST TYPE XU /* val */ +%token NAME UNAME PNAME INAME /* sym */ +%token STRING CLAIM TRACE INIT /* sym */ + +%right ASGN +%left SND O_SND RCV R_RCV /* SND doubles as boolean negation */ +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left GT LT GE LE +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%left INCR DECR +%right '~' UMIN NEG +%left DOT +%% + +/** PROMELA Grammar Rules **/ + +program : units { yytext[0] = '\0'; } + ; + +units : unit + | units unit + ; + +unit : proc /* proctype { } */ + | init /* init { } */ + | claim /* never claim */ + | events /* event assertions */ + | one_decl /* variables, chans */ + | utype /* user defined types */ + | c_fcts /* c functions etc. */ + | ns /* named sequence */ + | SEMI /* optional separator */ + | error + ; + +proc : inst /* optional instantiator */ + proctype NAME { + setptype($3, PROCTYPE, ZN); + setpname($3); + context = $3->sym; + context->ini = $2; /* linenr and file */ + Expand_Ok++; /* expand struct names in decl */ + has_ini = 0; + } + '(' decl ')' { Expand_Ok--; + if (has_ini) + fatal("initializer in parameter list", (char *) 0); + } + Opt_priority + Opt_enabler + body { ProcList *rl; + rl = ready($3->sym, $6, $11->sq, $2->val, $10); + if ($1 != ZN && $1->val > 0) + { int j; + for (j = 0; j < $1->val; j++) + runnable(rl, $9?$9->val:1, 1); + announce(":root:"); + if (dumptab) $3->sym->ini = $1; + } + context = ZS; + } + ; + +proctype: PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 0; } + | D_PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; } + ; + +inst : /* empty */ { $$ = ZN; } + | ACTIVE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; } + | ACTIVE '[' CONST ']' { + $$ = nn(ZN,CONST,ZN,ZN); $$->val = $3->val; + if ($3->val > 255) + non_fatal("max nr of processes is 255\n", ""); + } + | ACTIVE '[' NAME ']' { + $$ = nn(ZN,CONST,ZN,ZN); + $$->val = 0; + if (!$3->sym->type) + non_fatal("undeclared variable %s", + $3->sym->name); + else if ($3->sym->ini->ntyp != CONST) + non_fatal("need constant initializer for %s\n", + $3->sym->name); + else + $$->val = $3->sym->ini->val; + } + ; + +init : INIT { context = $1->sym; } + Opt_priority + body { ProcList *rl; + rl = ready(context, ZN, $4->sq, 0, ZN); + runnable(rl, $3?$3->val:1, 1); + announce(":root:"); + context = ZS; + } + ; + +claim : CLAIM { context = $1->sym; + if (claimproc) + non_fatal("claim %s redefined", claimproc); + claimproc = $1->sym->name; + } + body { (void) ready($1->sym, ZN, $3->sq, 0, ZN); + context = ZS; + } + ; + +events : TRACE { context = $1->sym; + if (eventmap) + non_fatal("trace %s redefined", eventmap); + eventmap = $1->sym->name; + inEventMap++; + } + body { (void) ready($1->sym, ZN, $3->sq, 0, ZN); + context = ZS; + inEventMap--; + } + ; + +utype : TYPEDEF NAME { if (context) + fatal("typedef %s must be global", + $2->sym->name); + owner = $2->sym; + } + '{' decl_lst '}' { setuname($5); owner = ZS; } + ; + +nm : NAME { $$ = $1; } + | INAME { $$ = $1; + if (IArgs) + fatal("invalid use of '%s'", $1->sym->name); + } + ; + +ns : INLINE nm '(' { NamesNotAdded++; } + args ')' { prep_inline($2->sym, $5); + NamesNotAdded--; + } + ; + +c_fcts : ccode { /* leaves pseudo-inlines with sym of + * type CODE_FRAG or CODE_DECL in global context + */ + } + | cstate + ; + +cstate : C_STATE STRING STRING { + c_state($2->sym, $3->sym, ZS); + has_code = has_state = 1; + } + | C_TRACK STRING STRING { + c_track($2->sym, $3->sym, ZS); + has_code = has_state = 1; + } + | C_STATE STRING STRING STRING { + c_state($2->sym, $3->sym, $4->sym); + has_code = has_state = 1; + } + | C_TRACK STRING STRING STRING { + c_track($2->sym, $3->sym, $4->sym); + has_code = has_state = 1; + } + ; + +ccode : C_CODE { Symbol *s; + NamesNotAdded++; + s = prep_inline(ZS, ZN); + NamesNotAdded--; + $$ = nn(ZN, C_CODE, ZN, ZN); + $$->sym = s; + has_code = 1; + } + | C_DECL { Symbol *s; + NamesNotAdded++; + s = prep_inline(ZS, ZN); + NamesNotAdded--; + s->type = CODE_DECL; + $$ = nn(ZN, C_CODE, ZN, ZN); + $$->sym = s; + has_code = 1; + } + ; +cexpr : C_EXPR { Symbol *s; + NamesNotAdded++; + s = prep_inline(ZS, ZN); + NamesNotAdded--; + $$ = nn(ZN, C_EXPR, ZN, ZN); + $$->sym = s; + no_side_effects(s->name); + has_code = 1; + } + ; + +body : '{' { open_seq(1); } + sequence OS { add_seq(Stop); } + '}' { $$->sq = close_seq(0); } + ; + +sequence: step { if ($1) add_seq($1); } + | sequence MS step { if ($3) add_seq($3); } + ; + +step : one_decl { $$ = ZN; } + | XU vref_lst { setxus($2, $1->val); $$ = ZN; } + | NAME ':' one_decl { fatal("label preceding declaration,", (char *)0); } + | NAME ':' XU { fatal("label predecing xr/xs claim,", 0); } + | stmnt { $$ = $1; } + | stmnt UNLESS stmnt { $$ = do_unless($1, $3); } + ; + +vis : /* empty */ { $$ = ZN; } + | HIDDEN { $$ = $1; } + | SHOW { $$ = $1; } + | ISLOCAL { $$ = $1; } + ; + +asgn: /* empty */ + | ASGN + ; + +one_decl: vis TYPE var_list { setptype($3, $2->val, $1); $$ = $3; } + | vis UNAME var_list { setutype($3, $2->sym, $1); + $$ = expand($3, Expand_Ok); + } + | vis TYPE asgn '{' nlst '}' { + if ($2->val != MTYPE) + fatal("malformed declaration", 0); + setmtype($5); + if ($1) + non_fatal("cannot %s mtype (ignored)", + $1->sym->name); + if (context != ZS) + fatal("mtype declaration must be global", 0); + } + ; + +decl_lst: one_decl { $$ = nn(ZN, ',', $1, ZN); } + | one_decl SEMI + decl_lst { $$ = nn(ZN, ',', $1, $3); } + ; + +decl : /* empty */ { $$ = ZN; } + | decl_lst { $$ = $1; } + ; + +vref_lst: varref { $$ = nn($1, XU, $1, ZN); } + | varref ',' vref_lst { $$ = nn($1, XU, $1, $3); } + ; + +var_list: ivar { $$ = nn($1, TYPE, ZN, ZN); } + | ivar ',' var_list { $$ = nn($1, TYPE, ZN, $3); } + ; + +ivar : vardcl { $$ = $1; + $1->sym->ini = nn(ZN,CONST,ZN,ZN); + $1->sym->ini->val = 0; + } + | vardcl ASGN expr { $1->sym->ini = $3; $$ = $1; + trackvar($1,$3); has_ini = 1; + } + | vardcl ASGN ch_init { $1->sym->ini = $3; + $$ = $1; has_ini = 1; + } + ; + +ch_init : '[' CONST ']' OF + '{' typ_list '}' { if ($2->val) u_async++; + else u_sync++; + { int i = cnt_mpars($6); + Mpars = max(Mpars, i); + } + $$ = nn(ZN, CHAN, ZN, $6); + $$->val = $2->val; + } + ; + +vardcl : NAME { $1->sym->nel = 1; $$ = $1; } + | NAME ':' CONST { $1->sym->nbits = $3->val; + if ($3->val >= 8*sizeof(long)) + { non_fatal("width-field %s too large", + $1->sym->name); + $3->val = 8*sizeof(long)-1; + } + $1->sym->nel = 1; $$ = $1; + } + | NAME '[' CONST ']' { $1->sym->nel = $3->val; $$ = $1; } + ; + +varref : cmpnd { $$ = mk_explicit($1, Expand_Ok, NAME); } + ; + +pfld : NAME { $$ = nn($1, NAME, ZN, ZN); } + | NAME { owner = ZS; } + '[' expr ']' { $$ = nn($1, NAME, $4, ZN); } + ; + +cmpnd : pfld { Embedded++; + if ($1->sym->type == STRUCT) + owner = $1->sym->Snm; + } + sfld { $$ = $1; $$->rgt = $3; + if ($3 && $1->sym->type != STRUCT) + $1->sym->type = STRUCT; + Embedded--; + if (!Embedded && !NamesNotAdded + && !$1->sym->type) + non_fatal("undeclared variable: %s", + $1->sym->name); + if ($3) validref($1, $3->lft); + owner = ZS; + } + ; + +sfld : /* empty */ { $$ = ZN; } + | '.' cmpnd %prec DOT { $$ = nn(ZN, '.', $2, ZN); } + ; + +stmnt : Special { $$ = $1; } + | Stmnt { $$ = $1; + if (inEventMap) + non_fatal("not an event", (char *)0); + } + ; + +Special : varref RCV { Expand_Ok++; } + rargs { Expand_Ok--; has_io++; + $$ = nn($1, 'r', $1, $4); + trackchanuse($4, ZN, 'R'); + } + | varref SND { Expand_Ok++; } + margs { Expand_Ok--; has_io++; + $$ = nn($1, 's', $1, $4); + $$->val=0; trackchanuse($4, ZN, 'S'); + any_runs($4); + } + | IF options FI { $$ = nn($1, IF, ZN, ZN); + $$->sl = $2->sl; + prune_opts($$); + } + | DO { pushbreak(); } + options OD { $$ = nn($1, DO, ZN, ZN); + $$->sl = $3->sl; + prune_opts($$); + } + | BREAK { $$ = nn(ZN, GOTO, ZN, ZN); + $$->sym = break_dest(); + } + | GOTO NAME { $$ = nn($2, GOTO, ZN, ZN); + if ($2->sym->type != 0 + && $2->sym->type != LABEL) { + non_fatal("bad label-name %s", + $2->sym->name); + } + $2->sym->type = LABEL; + } + | NAME ':' stmnt { $$ = nn($1, ':',$3, ZN); + if ($1->sym->type != 0 + && $1->sym->type != LABEL) { + non_fatal("bad label-name %s", + $1->sym->name); + } + $1->sym->type = LABEL; + } + ; + +Stmnt : varref ASGN expr { $$ = nn($1, ASGN, $1, $3); + trackvar($1, $3); + nochan_manip($1, $3, 0); + no_internals($1); + } + | varref INCR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1; + $$ = nn(ZN, '+', $1, $$); + $$ = nn($1, ASGN, $1, $$); + trackvar($1, $1); + no_internals($1); + if ($1->sym->type == CHAN) + fatal("arithmetic on chan", (char *)0); + } + | varref DECR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1; + $$ = nn(ZN, '-', $1, $$); + $$ = nn($1, ASGN, $1, $$); + trackvar($1, $1); + no_internals($1); + if ($1->sym->type == CHAN) + fatal("arithmetic on chan id's", (char *)0); + } + | PRINT '(' STRING { realread = 0; } + prargs ')' { $$ = nn($3, PRINT, $5, ZN); realread = 1; } + | PRINTM '(' varref ')' { $$ = nn(ZN, PRINTM, $3, ZN); } + | PRINTM '(' CONST ')' { $$ = nn(ZN, PRINTM, $3, ZN); } + | ASSERT full_expr { $$ = nn(ZN, ASSERT, $2, ZN); AST_track($2, 0); } + | ccode { $$ = $1; } + | varref R_RCV { Expand_Ok++; } + rargs { Expand_Ok--; has_io++; + $$ = nn($1, 'r', $1, $4); + $$->val = has_random = 1; + trackchanuse($4, ZN, 'R'); + } + | varref RCV { Expand_Ok++; } + LT rargs GT { Expand_Ok--; has_io++; + $$ = nn($1, 'r', $1, $5); + $$->val = 2; /* fifo poll */ + trackchanuse($5, ZN, 'R'); + } + | varref R_RCV { Expand_Ok++; } + LT rargs GT { Expand_Ok--; has_io++; /* rrcv poll */ + $$ = nn($1, 'r', $1, $5); + $$->val = 3; has_random = 1; + trackchanuse($5, ZN, 'R'); + } + | varref O_SND { Expand_Ok++; } + margs { Expand_Ok--; has_io++; + $$ = nn($1, 's', $1, $4); + $$->val = has_sorted = 1; + trackchanuse($4, ZN, 'S'); + any_runs($4); + } + | full_expr { $$ = nn(ZN, 'c', $1, ZN); count_runs($$); } + | ELSE { $$ = nn(ZN,ELSE,ZN,ZN); + } + | ATOMIC '{' { open_seq(0); } + sequence OS '}' { $$ = nn($1, ATOMIC, ZN, ZN); + $$->sl = seqlist(close_seq(3), 0); + make_atomic($$->sl->this, 0); + } + | D_STEP '{' { open_seq(0); rem_Seq(); } + sequence OS '}' { $$ = nn($1, D_STEP, ZN, ZN); + $$->sl = seqlist(close_seq(4), 0); + make_atomic($$->sl->this, D_ATOM); + unrem_Seq(); + } + | '{' { open_seq(0); } + sequence OS '}' { $$ = nn(ZN, NON_ATOMIC, ZN, ZN); + $$->sl = seqlist(close_seq(5), 0); + } + | INAME { IArgs++; } + '(' args ')' { pickup_inline($1->sym, $4); IArgs--; } + Stmnt { $$ = $7; } + ; + +options : option { $$->sl = seqlist($1->sq, 0); } + | option options { $$->sl = seqlist($1->sq, $2->sl); } + ; + +option : SEP { open_seq(0); } + sequence OS { $$ = nn(ZN,0,ZN,ZN); + $$->sq = close_seq(6); } + ; + +OS : /* empty */ + | SEMI { /* redundant semi at end of sequence */ } + ; + +MS : SEMI { /* at least one semi-colon */ } + | MS SEMI { /* but more are okay too */ } + ; + +aname : NAME { $$ = $1; } + | PNAME { $$ = $1; } + ; + +expr : '(' expr ')' { $$ = $2; } + | expr '+' expr { $$ = nn(ZN, '+', $1, $3); } + | expr '-' expr { $$ = nn(ZN, '-', $1, $3); } + | expr '*' expr { $$ = nn(ZN, '*', $1, $3); } + | expr '/' expr { $$ = nn(ZN, '/', $1, $3); } + | expr '%' expr { $$ = nn(ZN, '%', $1, $3); } + | expr '&' expr { $$ = nn(ZN, '&', $1, $3); } + | expr '^' expr { $$ = nn(ZN, '^', $1, $3); } + | expr '|' expr { $$ = nn(ZN, '|', $1, $3); } + | expr GT expr { $$ = nn(ZN, GT, $1, $3); } + | expr LT expr { $$ = nn(ZN, LT, $1, $3); } + | expr GE expr { $$ = nn(ZN, GE, $1, $3); } + | expr LE expr { $$ = nn(ZN, LE, $1, $3); } + | expr EQ expr { $$ = nn(ZN, EQ, $1, $3); } + | expr NE expr { $$ = nn(ZN, NE, $1, $3); } + | expr AND expr { $$ = nn(ZN, AND, $1, $3); } + | expr OR expr { $$ = nn(ZN, OR, $1, $3); } + | expr LSHIFT expr { $$ = nn(ZN, LSHIFT,$1, $3); } + | expr RSHIFT expr { $$ = nn(ZN, RSHIFT,$1, $3); } + | '~' expr { $$ = nn(ZN, '~', $2, ZN); } + | '-' expr %prec UMIN { $$ = nn(ZN, UMIN, $2, ZN); } + | SND expr %prec NEG { $$ = nn(ZN, '!', $2, ZN); } + + | '(' expr SEMI expr ':' expr ')' { + $$ = nn(ZN, OR, $4, $6); + $$ = nn(ZN, '?', $2, $$); + } + + | RUN aname { Expand_Ok++; + if (!context) + fatal("used 'run' outside proctype", + (char *) 0); + if (strcmp(context->name, ":init:") != 0) + runsafe = 0; + } + '(' args ')' + Opt_priority { Expand_Ok--; + $$ = nn($2, RUN, $5, ZN); + $$->val = ($7) ? $7->val : 1; + trackchanuse($5, $2, 'A'); trackrun($$); + } + | LEN '(' varref ')' { $$ = nn($3, LEN, $3, ZN); } + | ENABLED '(' expr ')' { $$ = nn(ZN, ENABLED, $3, ZN); + has_enabled++; + } + | varref RCV { Expand_Ok++; } + '[' rargs ']' { Expand_Ok--; has_io++; + $$ = nn($1, 'R', $1, $5); + } + | varref R_RCV { Expand_Ok++; } + '[' rargs ']' { Expand_Ok--; has_io++; + $$ = nn($1, 'R', $1, $5); + $$->val = has_random = 1; + } + | varref { $$ = $1; trapwonly($1, "varref"); } + | cexpr { $$ = $1; } + | CONST { $$ = nn(ZN,CONST,ZN,ZN); + $$->ismtyp = $1->ismtyp; + $$->val = $1->val; + } + | TIMEOUT { $$ = nn(ZN,TIMEOUT, ZN, ZN); } + | NONPROGRESS { $$ = nn(ZN,NONPROGRESS, ZN, ZN); + has_np++; + } + | PC_VAL '(' expr ')' { $$ = nn(ZN, PC_VAL, $3, ZN); + has_pcvalue++; + } + | PNAME '[' expr ']' '@' NAME + { $$ = rem_lab($1->sym, $3, $6->sym); } + | PNAME '[' expr ']' ':' pfld + { $$ = rem_var($1->sym, $3, $6->sym, $6->lft); } + | PNAME '@' NAME { $$ = rem_lab($1->sym, ZN, $3->sym); } + | PNAME ':' pfld { $$ = rem_var($1->sym, ZN, $3->sym, $3->lft); } + ; + +Opt_priority: /* none */ { $$ = ZN; } + | PRIORITY CONST { $$ = $2; } + ; + +full_expr: expr { $$ = $1; } + | Expr { $$ = $1; } + ; + +Opt_enabler: /* none */ { $$ = ZN; } + | PROVIDED '(' full_expr ')' { if (!proper_enabler($3)) + { non_fatal("invalid PROVIDED clause", + (char *)0); + $$ = ZN; + } else + $$ = $3; + } + | PROVIDED error { $$ = ZN; + non_fatal("usage: provided ( ..expr.. )", + (char *)0); + } + ; + + /* an Expr cannot be negated - to protect Probe expressions */ +Expr : Probe { $$ = $1; } + | '(' Expr ')' { $$ = $2; } + | Expr AND Expr { $$ = nn(ZN, AND, $1, $3); } + | Expr AND expr { $$ = nn(ZN, AND, $1, $3); } + | Expr OR Expr { $$ = nn(ZN, OR, $1, $3); } + | Expr OR expr { $$ = nn(ZN, OR, $1, $3); } + | expr AND Expr { $$ = nn(ZN, AND, $1, $3); } + | expr OR Expr { $$ = nn(ZN, OR, $1, $3); } + ; + +Probe : FULL '(' varref ')' { $$ = nn($3, FULL, $3, ZN); } + | NFULL '(' varref ')' { $$ = nn($3, NFULL, $3, ZN); } + | EMPTY '(' varref ')' { $$ = nn($3, EMPTY, $3, ZN); } + | NEMPTY '(' varref ')' { $$ = nn($3,NEMPTY, $3, ZN); } + ; + +basetype: TYPE { $$->sym = ZS; + $$->val = $1->val; + if ($$->val == UNSIGNED) + fatal("unsigned cannot be used as mesg type", 0); + } + | UNAME { $$->sym = $1->sym; + $$->val = STRUCT; + } + | error /* e.g., unsigned ':' const */ + ; + +typ_list: basetype { $$ = nn($1, $1->val, ZN, ZN); } + | basetype ',' typ_list { $$ = nn($1, $1->val, ZN, $3); } + ; + +args : /* empty */ { $$ = ZN; } + | arg { $$ = $1; } + ; + +prargs : /* empty */ { $$ = ZN; } + | ',' arg { $$ = $2; } + ; + +margs : arg { $$ = $1; } + | expr '(' arg ')' { if ($1->ntyp == ',') + $$ = tail_add($1, $3); + else + $$ = nn(ZN, ',', $1, $3); + } + ; + +arg : expr { if ($1->ntyp == ',') + $$ = $1; + else + $$ = nn(ZN, ',', $1, ZN); + } + | expr ',' arg { if ($1->ntyp == ',') + $$ = tail_add($1, $3); + else + $$ = nn(ZN, ',', $1, $3); + } + ; + +rarg : varref { $$ = $1; trackvar($1, $1); + trapwonly($1, "rarg"); } + | EVAL '(' expr ')' { $$ = nn(ZN,EVAL,$3,ZN); + trapwonly($1, "eval rarg"); } + | CONST { $$ = nn(ZN,CONST,ZN,ZN); + $$->ismtyp = $1->ismtyp; + $$->val = $1->val; + } + | '-' CONST %prec UMIN { $$ = nn(ZN,CONST,ZN,ZN); + $$->val = - ($2->val); + } + ; + +rargs : rarg { if ($1->ntyp == ',') + $$ = $1; + else + $$ = nn(ZN, ',', $1, ZN); + } + | rarg ',' rargs { if ($1->ntyp == ',') + $$ = tail_add($1, $3); + else + $$ = nn(ZN, ',', $1, $3); + } + | rarg '(' rargs ')' { if ($1->ntyp == ',') + $$ = tail_add($1, $3); + else + $$ = nn(ZN, ',', $1, $3); + } + | '(' rargs ')' { $$ = $2; } + ; + +nlst : NAME { $$ = nn($1, NAME, ZN, ZN); + $$ = nn(ZN, ',', $$, ZN); } + | nlst NAME { $$ = nn($2, NAME, ZN, ZN); + $$ = nn(ZN, ',', $$, $1); + } + | nlst ',' { $$ = $1; /* commas optional */ } + ; +%% + +void +yyerror(char *fmt, ...) +{ + non_fatal(fmt, (char *) 0); +} diff --git a/sys/src/cmd/spin/spinlex.c b/sys/src/cmd/spin/spinlex.c new file mode 100755 index 000000000..48c431f72 --- /dev/null +++ b/sys/src/cmd/spin/spinlex.c @@ -0,0 +1,1384 @@ +/***** spin: spinlex.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include <stdlib.h> +#include "spin.h" +#include "y.tab.h" + +#define MAXINL 16 /* max recursion depth inline fcts */ +#define MAXPAR 32 /* max params to an inline call */ +#define MAXLEN 512 /* max len of an actual parameter text */ + +typedef struct IType { + Symbol *nm; /* name of the type */ + Lextok *cn; /* contents */ + Lextok *params; /* formal pars if any */ + char **anms; /* literal text for actual pars */ + char *prec; /* precondition for c_code or c_expr */ + int dln, cln; /* def and call linenr */ + Symbol *dfn, *cfn; /* def and call filename */ + struct IType *nxt; /* linked list */ +} IType; + +typedef struct C_Added { + Symbol *s; + Symbol *t; + Symbol *ival; + struct C_Added *nxt; +} C_Added; + +extern RunList *X; +extern ProcList *rdy; +extern Symbol *Fname; +extern Symbol *context, *owner; +extern YYSTYPE yylval; +extern short has_last, has_code; +extern int verbose, IArgs, hastrack, separate; + +short has_stack = 0; +int lineno = 1; +char yytext[2048]; +FILE *yyin, *yyout; + +static C_Added *c_added, *c_tracked; +static IType *Inline_stub[MAXINL]; +static char *ReDiRect; +static char *Inliner[MAXINL], IArg_cont[MAXPAR][MAXLEN]; +static unsigned char in_comment=0; +static int IArgno = 0, Inlining = -1; +static int check_name(char *); + +#if 1 +#define Token(y) { if (in_comment) goto again; \ + yylval = nn(ZN,0,ZN,ZN); return y; } + +#define ValToken(x, y) { if (in_comment) goto again; \ + yylval = nn(ZN,0,ZN,ZN); yylval->val = x; return y; } + +#define SymToken(x, y) { if (in_comment) goto again; \ + yylval = nn(ZN,0,ZN,ZN); yylval->sym = x; return y; } +#else +#define Token(y) { yylval = nn(ZN,0,ZN,ZN); \ + if (!in_comment) return y; else goto again; } + +#define ValToken(x, y) { yylval = nn(ZN,0,ZN,ZN); yylval->val = x; \ + if (!in_comment) return y; else goto again; } + +#define SymToken(x, y) { yylval = nn(ZN,0,ZN,ZN); yylval->sym = x; \ + if (!in_comment) return y; else goto again; } +#endif + +static int getinline(void); +static void uninline(void); + +#if 1 +#define Getchar() ((Inlining<0)?getc(yyin):getinline()) +#define Ungetch(c) {if (Inlining<0) ungetc(c,yyin); else uninline(); } + +#else + +static int +Getchar(void) +{ int c; + if (Inlining<0) + c = getc(yyin); + else + c = getinline(); +#if 1 + printf("<%c>", c); +#endif + return c; +} + +static void +Ungetch(int c) +{ + if (Inlining<0) + ungetc(c,yyin); + else + uninline(); +#if 1 + printf("<bs>"); +#endif +} +#endif + +static int +notquote(int c) +{ return (c != '\"' && c != '\n'); +} + +int +isalnum_(int c) +{ return (isalnum(c) || c == '_'); +} + +static int +isalpha_(int c) +{ return isalpha(c); /* could be macro */ +} + +static int +isdigit_(int c) +{ return isdigit(c); /* could be macro */ +} + +static void +getword(int first, int (*tst)(int)) +{ int i=0; char c; + + yytext[i++]= (char) first; + while (tst(c = Getchar())) + { yytext[i++] = c; + if (c == '\\') + yytext[i++] = Getchar(); /* no tst */ + } + yytext[i] = '\0'; + Ungetch(c); +} + +static int +follow(int tok, int ifyes, int ifno) +{ int c; + + if ((c = Getchar()) == tok) + return ifyes; + Ungetch(c); + + return ifno; +} + +static IType *seqnames; + +static void +def_inline(Symbol *s, int ln, char *ptr, char *prc, Lextok *nms) +{ IType *tmp; + char *nw = (char *) emalloc((int) strlen(ptr)+1); + strcpy(nw, ptr); + + for (tmp = seqnames; tmp; tmp = tmp->nxt) + if (!strcmp(s->name, tmp->nm->name)) + { non_fatal("procedure name %s redefined", + tmp->nm->name); + tmp->cn = (Lextok *) nw; + tmp->params = nms; + tmp->dln = ln; + tmp->dfn = Fname; + return; + } + tmp = (IType *) emalloc(sizeof(IType)); + tmp->nm = s; + tmp->cn = (Lextok *) nw; + tmp->params = nms; + if (strlen(prc) > 0) + { tmp->prec = (char *) emalloc((int) strlen(prc)+1); + strcpy(tmp->prec, prc); + } + tmp->dln = ln; + tmp->dfn = Fname; + tmp->nxt = seqnames; + seqnames = tmp; +} + +void +gencodetable(FILE *fd) +{ IType *tmp; + char *q; + int cnt; + + if (separate == 2) return; + + fprintf(fd, "struct {\n"); + fprintf(fd, " char *c; char *t;\n"); + fprintf(fd, "} code_lookup[] = {\n"); + + if (has_code) + for (tmp = seqnames; tmp; tmp = tmp->nxt) + if (tmp->nm->type == CODE_FRAG + || tmp->nm->type == CODE_DECL) + { fprintf(fd, "\t{ \"%s\", ", + tmp->nm->name); + q = (char *) tmp->cn; + + while (*q == '\n' || *q == '\r' || *q == '\\') + q++; + + fprintf(fd, "\""); + cnt = 0; + while (*q && cnt < 1024) /* pangen1.h allows 2048 */ + { switch (*q) { + case '"': + fprintf(fd, "\\\""); + break; + case '%': + fprintf(fd, "%%"); + break; + case '\n': + fprintf(fd, "\\n"); + break; + default: + putc(*q, fd); + break; + } + q++; cnt++; + } + if (*q) fprintf(fd, "..."); + fprintf(fd, "\""); + fprintf(fd, " },\n"); + } + + fprintf(fd, " { (char *) 0, \"\" }\n"); + fprintf(fd, "};\n"); +} + +static int +iseqname(char *t) +{ IType *tmp; + + for (tmp = seqnames; tmp; tmp = tmp->nxt) + { if (!strcmp(t, tmp->nm->name)) + return 1; + } + return 0; +} + +static int +getinline(void) +{ int c; + + if (ReDiRect) + { c = *ReDiRect++; + if (c == '\0') + { ReDiRect = (char *) 0; + c = *Inliner[Inlining]++; + } + } else + c = *Inliner[Inlining]++; + + if (c == '\0') + { lineno = Inline_stub[Inlining]->cln; + Fname = Inline_stub[Inlining]->cfn; + Inlining--; +#if 0 + if (verbose&32) + printf("spin: line %d, done inlining %s\n", + lineno, Inline_stub[Inlining+1]->nm->name); +#endif + return Getchar(); + } + return c; +} + +static void +uninline(void) +{ + if (ReDiRect) + ReDiRect--; + else + Inliner[Inlining]--; +} + +IType * +find_inline(char *s) +{ IType *tmp; + + for (tmp = seqnames; tmp; tmp = tmp->nxt) + if (!strcmp(s, tmp->nm->name)) + break; + if (!tmp) + fatal("cannot happen, missing inline def %s", s); + return tmp; +} + +void +c_state(Symbol *s, Symbol *t, Symbol *ival) /* name, scope, ival */ +{ C_Added *r; + + r = (C_Added *) emalloc(sizeof(C_Added)); + r->s = s; /* pointer to a data object */ + r->t = t; /* size of object, or "global", or "local proctype_name" */ + r->ival = ival; + r->nxt = c_added; + c_added = r; +} + +void +c_track(Symbol *s, Symbol *t, Symbol *stackonly) /* name, size */ +{ C_Added *r; + + r = (C_Added *) emalloc(sizeof(C_Added)); + r->s = s; + r->t = t; + r->ival = stackonly; /* abuse of name */ + r->nxt = c_tracked; + c_tracked = r; + + if (stackonly != ZS) + { if (strcmp(stackonly->name, "\"Matched\"") == 0) + r->ival = ZS; /* the default */ + else if (strcmp(stackonly->name, "\"UnMatched\"") != 0 + && strcmp(stackonly->name, "\"unMatched\"") != 0 + && strcmp(stackonly->name, "\"StackOnly\"") != 0) + non_fatal("expecting '[Un]Matched', saw %s", stackonly->name); + else + has_stack = 1; + } +} + +char * +jump_etc(char *op) +{ char *p = op; + + /* kludgy - try to get the type separated from the name */ + + while (*p == ' ' || *p == '\t') + p++; /* initial white space */ + while (*p != ' ' && *p != '\t') + p++; /* type name */ + while (*p == ' ' || *p == '\t') + p++; /* white space */ + while (*p == '*') + p++; /* decorations */ + while (*p == ' ' || *p == '\t') + p++; /* white space */ + + if (*p == '\0') + fatal("c_state format (%s)", op); + + if (strchr(p, '[') + && !strchr(p, '{')) + { non_fatal("array initialization error, c_state (%s)", p); + return (char *) 0; + } + + return p; +} + +void +c_add_globinit(FILE *fd) +{ C_Added *r; + char *p, *q; + + fprintf(fd, "void\nglobinit(void)\n{\n"); + for (r = c_added; r; r = r->nxt) + { if (r->ival == ZS) + continue; + + if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0) + { for (q = r->ival->name; *q; q++) + { if (*q == '\"') + *q = ' '; + if (*q == '\\') + *q++ = ' '; /* skip over the next */ + } + p = jump_etc(r->s->name); /* e.g., "int **q" */ + if (p) + fprintf(fd, " now.%s = %s;\n", p, r->ival->name); + + } else + if (strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0) + { for (q = r->ival->name; *q; q++) + { if (*q == '\"') + *q = ' '; + if (*q == '\\') + *q++ = ' '; /* skip over the next */ + } + p = jump_etc(r->s->name); /* e.g., "int **q" */ + if (p) + fprintf(fd, " %s = %s;\n", p, r->ival->name); /* no now. prefix */ + + } } + fprintf(fd, "}\n"); +} + +void +c_add_locinit(FILE *fd, int tpnr, char *pnm) +{ C_Added *r; + char *p, *q, *s; + int frst = 1; + + fprintf(fd, "void\nlocinit%d(int h)\n{\n", tpnr); + for (r = c_added; r; r = r->nxt) + if (r->ival != ZS + && strncmp(r->t->name, " Local", strlen(" Local")) == 0) + { for (q = r->ival->name; *q; q++) + if (*q == '\"') + *q = ' '; + + p = jump_etc(r->s->name); /* e.g., "int **q" */ + + q = r->t->name + strlen(" Local"); + while (*q == ' ' || *q == '\t') + q++; /* process name */ + + s = (char *) emalloc(strlen(q)+1); + strcpy(s, q); + + q = &s[strlen(s)-1]; + while (*q == ' ' || *q == '\t') + *q-- = '\0'; + + if (strcmp(pnm, s) != 0) + continue; + + if (frst) + { fprintf(fd, "\tuchar *this = pptr(h);\n"); + frst = 0; + } + + if (p) + fprintf(fd, " ((P%d *)this)->%s = %s;\n", + tpnr, p, r->ival->name); + + } + fprintf(fd, "}\n"); +} + +/* tracking: + 1. for non-global and non-local c_state decls: add up all the sizes in c_added + 2. add a global char array of that size into now + 3. generate a routine that memcpy's the required values into that array + 4. generate a call to that routine + */ + +void +c_preview(void) +{ C_Added *r; + + hastrack = 0; + if (c_tracked) + hastrack = 1; + else + for (r = c_added; r; r = r->nxt) + if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0 + && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0 + && strncmp(r->t->name, " Local", strlen(" Local")) != 0) + { hastrack = 1; /* c_state variant now obsolete */ + break; + } +} + +int +c_add_sv(FILE *fd) /* 1+2 -- called in pangen1.c */ +{ C_Added *r; + int cnt = 0; + + if (!c_added && !c_tracked) return 0; + + for (r = c_added; r; r = r->nxt) /* pickup global decls */ + if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0) + fprintf(fd, " %s;\n", r->s->name); + + for (r = c_added; r; r = r->nxt) + if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0 + && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0 + && strncmp(r->t->name, " Local", strlen(" Local")) != 0) + { cnt++; /* obsolete use */ + } + + for (r = c_tracked; r; r = r->nxt) + cnt++; /* preferred use */ + + if (cnt == 0) return 0; + + cnt = 0; + fprintf(fd, " uchar c_state["); + for (r = c_added; r; r = r->nxt) + if (strncmp(r->t->name, " Global ", strlen(" Global ")) != 0 + && strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) != 0 + && strncmp(r->t->name, " Local", strlen(" Local")) != 0) + { fprintf(fd, "%ssizeof(%s)", + (cnt==0)?"":"+", r->t->name); + cnt++; + } + + for (r = c_tracked; r; r = r->nxt) + { if (r->ival != ZS) continue; + + fprintf(fd, "%s%s", + (cnt==0)?"":"+", r->t->name); + cnt++; + } + + if (cnt == 0) fprintf(fd, "4"); /* now redundant */ + fprintf(fd, "];\n"); + return 1; +} + +void +c_add_stack(FILE *fd) +{ C_Added *r; + int cnt = 0; + + if ((!c_added && !c_tracked) || !has_stack) return; + + + for (r = c_tracked; r; r = r->nxt) + if (r->ival != ZS) + cnt++; + + if (cnt == 0) return; + + fprintf(fd, " uchar c_stack["); + + cnt = 0; + for (r = c_tracked; r; r = r->nxt) + { if (r->ival == ZS) continue; + + fprintf(fd, "%s%s", + (cnt==0)?"":"+", r->t->name); + cnt++; + } + + if (cnt == 0) fprintf(fd, "WS"); /* can't happen */ + fprintf(fd, "];\n"); +} + +void +c_add_hidden(FILE *fd) +{ C_Added *r; + + for (r = c_added; r; r = r->nxt) /* pickup hidden decls */ + if (strncmp(r->t->name, "\"Hidden\"", strlen("\"Hidden\"")) == 0) + { r->s->name[strlen(r->s->name)-1] = ' '; + fprintf(fd, "%s; /* Hidden */\n", &r->s->name[1]); + r->s->name[strlen(r->s->name)-1] = '"'; + } + /* called before c_add_def - quotes are still there */ +} + +void +c_add_loc(FILE *fd, char *s) /* state vector entries for proctype s */ +{ C_Added *r; + static char buf[1024]; + char *p; + + if (!c_added) return; + + strcpy(buf, s); + strcat(buf, " "); + for (r = c_added; r; r = r->nxt) /* pickup local decls */ + if (strncmp(r->t->name, " Local", strlen(" Local")) == 0) + { p = r->t->name + strlen(" Local"); + while (*p == ' ' || *p == '\t') + p++; + if (strcmp(p, buf) == 0) + fprintf(fd, " %s;\n", r->s->name); + } +} +void +c_add_def(FILE *fd) /* 3 - called in plunk_c_fcts() */ +{ C_Added *r; + + fprintf(fd, "#if defined(C_States) && defined(HAS_TRACK)\n"); + for (r = c_added; r; r = r->nxt) + { r->s->name[strlen(r->s->name)-1] = ' '; + r->s->name[0] = ' '; /* remove the "s */ + + r->t->name[strlen(r->t->name)-1] = ' '; + r->t->name[0] = ' '; + + if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0 + || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0 + || strncmp(r->t->name, " Local", strlen(" Local")) == 0) + continue; + + if (strchr(r->s->name, '&')) + fatal("dereferencing state object: %s", r->s->name); + + fprintf(fd, "extern %s %s;\n", r->t->name, r->s->name); + } + for (r = c_tracked; r; r = r->nxt) + { r->s->name[strlen(r->s->name)-1] = ' '; + r->s->name[0] = ' '; /* remove " */ + + r->t->name[strlen(r->t->name)-1] = ' '; + r->t->name[0] = ' '; + } + + if (separate == 2) + { fprintf(fd, "#endif\n"); + return; + } + + if (has_stack) + { fprintf(fd, "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, "#endif\n"); + for (r = c_tracked; r; r = r->nxt) + { if (r->ival == ZS) continue; + + fprintf(fd, "\tif(%s)\n", r->s->name); + fprintf(fd, "\t\tmemcpy(p_t_r, %s, %s);\n", + r->s->name, r->t->name); + fprintf(fd, "\telse\n"); + fprintf(fd, "\t\tmemset(p_t_r, 0, %s);\n", + r->t->name); + fprintf(fd, "\tp_t_r += %s;\n", r->t->name); + } + fprintf(fd, "}\n\n"); + } + + fprintf(fd, "void\nc_update(uchar *p_t_r)\n{\n"); + fprintf(fd, "#ifdef VERBOSE\n"); + fprintf(fd, " printf(\"c_update %%u\\n\", p_t_r);\n"); + fprintf(fd, "#endif\n"); + for (r = c_added; r; r = r->nxt) + { if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0 + || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0 + || strncmp(r->t->name, " Local", strlen(" Local")) == 0) + continue; + + fprintf(fd, "\tmemcpy(p_t_r, &%s, sizeof(%s));\n", + r->s->name, r->t->name); + fprintf(fd, "\tp_t_r += sizeof(%s);\n", r->t->name); + } + + for (r = c_tracked; r; r = r->nxt) + { if (r->ival) continue; + + fprintf(fd, "\tif(%s)\n", r->s->name); + fprintf(fd, "\t\tmemcpy(p_t_r, %s, %s);\n", + r->s->name, r->t->name); + fprintf(fd, "\telse\n"); + fprintf(fd, "\t\tmemset(p_t_r, 0, %s);\n", + r->t->name); + fprintf(fd, "\tp_t_r += %s;\n", r->t->name); + } + + fprintf(fd, "}\n"); + + if (has_stack) + { fprintf(fd, "void\nc_unstack(uchar *p_t_r)\n{\n"); + fprintf(fd, "#ifdef VERBOSE\n"); + fprintf(fd, " printf(\"c_unstack %%u\\n\", p_t_r);\n"); + fprintf(fd, "#endif\n"); + for (r = c_tracked; r; r = r->nxt) + { if (r->ival == ZS) continue; + + fprintf(fd, "\tif(%s)\n", r->s->name); + fprintf(fd, "\t\tmemcpy(%s, p_t_r, %s);\n", + r->s->name, r->t->name); + fprintf(fd, "\tp_t_r += %s;\n", r->t->name); + } + fprintf(fd, "}\n"); + } + + fprintf(fd, "void\nc_revert(uchar *p_t_r)\n{\n"); + fprintf(fd, "#ifdef VERBOSE\n"); + fprintf(fd, " printf(\"c_revert %%u\\n\", p_t_r);\n"); + fprintf(fd, "#endif\n"); + for (r = c_added; r; r = r->nxt) + { if (strncmp(r->t->name, " Global ", strlen(" Global ")) == 0 + || strncmp(r->t->name, " Hidden ", strlen(" Hidden ")) == 0 + || strncmp(r->t->name, " Local", strlen(" Local")) == 0) + continue; + + fprintf(fd, "\tmemcpy(&%s, p_t_r, sizeof(%s));\n", + r->s->name, r->t->name); + fprintf(fd, "\tp_t_r += sizeof(%s);\n", r->t->name); + } + for (r = c_tracked; r; r = r->nxt) + { if (r->ival != ZS) continue; + + fprintf(fd, "\tif(%s)\n", r->s->name); + fprintf(fd, "\t\tmemcpy(%s, p_t_r, %s);\n", + r->s->name, r->t->name); + fprintf(fd, "\tp_t_r += %s;\n", r->t->name); + } + + fprintf(fd, "}\n"); + fprintf(fd, "#endif\n"); +} + +void +plunk_reverse(FILE *fd, IType *p, int matchthis) +{ char *y, *z; + + if (!p) return; + plunk_reverse(fd, p->nxt, matchthis); + + if (!p->nm->context + && p->nm->type == matchthis) + { fprintf(fd, "\n/* start of %s */\n", p->nm->name); + z = (char *) p->cn; + while (*z == '\n' || *z == '\r' || *z == '\\') + z++; + /* e.g.: \#include "..." */ + + y = z; + while ((y = strstr(y, "\\#")) != NULL) + { *y = '\n'; y++; + } + + fprintf(fd, "%s\n", z); + fprintf(fd, "\n/* end of %s */\n", p->nm->name); + } +} + +void +plunk_c_decls(FILE *fd) +{ + plunk_reverse(fd, seqnames, CODE_DECL); +} + +void +plunk_c_fcts(FILE *fd) +{ + if (separate == 2 && hastrack) + { c_add_def(fd); + return; + } + + c_add_hidden(fd); + plunk_reverse(fd, seqnames, CODE_FRAG); + + if (c_added || c_tracked) /* enables calls to c_revert and c_update */ + fprintf(fd, "#define C_States 1\n"); + else + fprintf(fd, "#undef C_States\n"); + + if (hastrack) + c_add_def(fd); + + c_add_globinit(fd); + do_locinits(fd); +} + +static void +check_inline(IType *tmp) +{ char buf[128]; + ProcList *p; + + if (!X) return; + + for (p = rdy; p; p = p->nxt) + { if (strcmp(p->n->name, X->n->name) == 0) + continue; + sprintf(buf, "P%s->", p->n->name); + if (strstr((char *)tmp->cn, buf)) + { printf("spin: in proctype %s, ref to object in proctype %s\n", + X->n->name, p->n->name); + fatal("invalid variable ref in '%s'", tmp->nm->name); + } } +} + +void +plunk_expr(FILE *fd, char *s) +{ IType *tmp; + + tmp = find_inline(s); + check_inline(tmp); + + fprintf(fd, "%s", (char *) tmp->cn); +} + +void +preruse(FILE *fd, Lextok *n) /* check a condition for c_expr with preconditions */ +{ IType *tmp; + + if (!n) return; + if (n->ntyp == C_EXPR) + { tmp = find_inline(n->sym->name); + if (tmp->prec) + { fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec); + fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;"); + fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec); + fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec); + fprintf(fd, "_m = 3; goto P999; } } \n\t\t"); + } + } else + { preruse(fd, n->rgt); + preruse(fd, n->lft); + } +} + +int +glob_inline(char *s) +{ IType *tmp; + char *bdy; + + tmp = find_inline(s); + bdy = (char *) tmp->cn; + return (strstr(bdy, "now.") /* global ref or */ + || strchr(bdy, '(') > bdy); /* possible C-function call */ +} + +void +plunk_inline(FILE *fd, char *s, int how) /* c_code with precondition */ +{ IType *tmp; + + tmp = find_inline(s); + check_inline(tmp); + + fprintf(fd, "{ "); + if (how && tmp->prec) + { fprintf(fd, "if (!(%s)) { if (!readtrail) { depth++; ", tmp->prec); + fprintf(fd, "trpt++; trpt->pr = II; trpt->o_t = t;"); + fprintf(fd, "trpt->st = tt; Uerror(\"%s\"); } ", tmp->prec); + fprintf(fd, "else { printf(\"pan: precondition false: %s\\n\"); ", tmp->prec); + fprintf(fd, "_m = 3; goto P999; } } "); + } + fprintf(fd, "%s", (char *) tmp->cn); + fprintf(fd, " }\n"); +} + +void +no_side_effects(char *s) +{ IType *tmp; + char *t; + + /* could still defeat this check via hidden + * side effects in function calls, + * but this will catch at least some cases + */ + + tmp = find_inline(s); + t = (char *) tmp->cn; + + if (strchr(t, ';') + || strstr(t, "++") + || strstr(t, "--")) + { +bad: lineno = tmp->dln; + Fname = tmp->dfn; + non_fatal("c_expr %s has side-effects", s); + return; + } + while ((t = strchr(t, '=')) != NULL) + { if (*(t-1) == '!' + || *(t-1) == '>' + || *(t-1) == '<') + { t++; + continue; + } + t++; + if (*t != '=') + goto bad; + t++; + } +} + +void +pickup_inline(Symbol *t, Lextok *apars) +{ IType *tmp; Lextok *p, *q; int j; + + tmp = find_inline(t->name); + + if (++Inlining >= MAXINL) + fatal("inline fcts too deeply nested", 0); + + tmp->cln = lineno; /* remember calling point */ + tmp->cfn = Fname; /* and filename */ + + for (p = apars, q = tmp->params, j = 0; p && q; p = p->rgt, q = q->rgt) + j++; /* count them */ + if (p || q) + fatal("wrong nr of params on call of '%s'", t->name); + + tmp->anms = (char **) emalloc(j * sizeof(char *)); + for (p = apars, j = 0; p; p = p->rgt, j++) + { tmp->anms[j] = (char *) emalloc((int) strlen(IArg_cont[j])+1); + strcpy(tmp->anms[j], IArg_cont[j]); + } + + lineno = tmp->dln; /* linenr of def */ + Fname = tmp->dfn; /* filename of same */ + Inliner[Inlining] = (char *)tmp->cn; + Inline_stub[Inlining] = tmp; +#if 0 + if (verbose&32) + printf("spin: line %d, file %s, inlining '%s' (from line %d, file %s)\n", + tmp->cln, tmp->cfn->name, t->name, tmp->dln, tmp->dfn->name); +#endif + for (j = 0; j < Inlining; j++) + if (Inline_stub[j] == Inline_stub[Inlining]) + fatal("cyclic inline attempt on: %s", t->name); +} + +static void +do_directive(int first) +{ int c = first; /* handles lines starting with pound */ + + getword(c, isalpha_); + + if (strcmp(yytext, "#ident") == 0) + goto done; + + if ((c = Getchar()) != ' ') + fatal("malformed preprocessor directive - # .", 0); + + if (!isdigit_(c = Getchar())) + fatal("malformed preprocessor directive - # .lineno", 0); + + getword(c, isdigit_); + lineno = atoi(yytext); /* pickup the line number */ + + if ((c = Getchar()) == '\n') + return; /* no filename */ + + if (c != ' ') + fatal("malformed preprocessor directive - .fname", 0); + + if ((c = Getchar()) != '\"') + fatal("malformed preprocessor directive - .fname", 0); + + getword(c, notquote); + if (Getchar() != '\"') + fatal("malformed preprocessor directive - fname.", 0); + + strcat(yytext, "\""); + Fname = lookup(yytext); +done: + while (Getchar() != '\n') + ; +} + +void +precondition(char *q) +{ int c, nest = 1; + + for (;;) + { c = Getchar(); + *q++ = c; + switch (c) { + case '\n': + lineno++; + break; + case '[': + nest++; + break; + case ']': + if (--nest <= 0) + { *--q = '\0'; + return; + } + break; + } + } + fatal("cannot happen", (char *) 0); +} + +Symbol * +prep_inline(Symbol *s, Lextok *nms) +{ int c, nest = 1, dln, firstchar, cnr; + char *p, buf[SOMETHINGBIG], buf2[RATHERSMALL]; + Lextok *t; + 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); + t->lft->sym->hidden |= 32; + } + + if (!s) /* C_Code fragment */ + { s = (Symbol *) emalloc(sizeof(Symbol)); + s->name = (char *) emalloc((int) 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'; + for (;;) + { c = Getchar(); + switch (c) { + case '[': + if (s->type != CODE_FRAG) + goto bad; + precondition(&buf2[0]); /* e.g., c_code [p] { r = p-r; } */ + continue; + case '{': + break; + case '\n': + lineno++; + /* fall through */ + case ' ': case '\t': case '\f': case '\r': + continue; + default : + printf("spin: saw char '%c'\n", c); +bad: fatal("bad inline: %s", s->name); + } + break; + } + dln = lineno; + if (s->type == CODE_FRAG) + { if (verbose&32) + sprintf(buf, "\t/* line %d %s */\n\t\t", + lineno, Fname->name); + else + strcpy(buf, ""); + } else + sprintf(buf, "\n#line %d %s\n{", lineno, Fname->name); + p += strlen(buf); + firstchar = 1; + + cnr = 1; /* not zero */ +more: + *p++ = c = Getchar(); + if (p - buf >= SOMETHINGBIG) + fatal("inline text too long", 0); + switch (c) { + case '\n': + lineno++; + cnr = 0; + break; + case '{': + cnr++; + nest++; + break; + case '}': + cnr++; + if (--nest <= 0) + { *p = '\0'; + if (s->type == CODE_FRAG) + *--p = '\0'; /* remove trailing '}' */ + def_inline(s, dln, &buf[0], &buf2[0], nms); + if (firstchar && s) + printf("%3d: %s, warning: empty inline definition (%s)\n", + dln, Fname->name, s->name); + return s; /* normal return */ + } + break; + case '#': + if (cnr == 0) + { p--; + do_directive(c); /* reads to newline */ + break; + } /* else fall through */ + default: + firstchar = 0; + case '\t': + case ' ': + case '\f': + cnr++; + break; + } + goto more; +} + +static int +lex(void) +{ int c; + +again: + c = Getchar(); + yytext[0] = (char) c; + yytext[1] = '\0'; + switch (c) { + case '\n': /* newline */ + lineno++; + case '\r': /* carriage return */ + goto again; + + case ' ': case '\t': case '\f': /* white space */ + goto again; + + case '#': /* preprocessor directive */ + if (in_comment) goto again; + do_directive(c); + goto again; + + case '\"': + getword(c, notquote); + if (Getchar() != '\"') + fatal("string not terminated", yytext); + strcat(yytext, "\""); + SymToken(lookup(yytext), STRING) + + case '\'': /* new 3.0.9 */ + c = Getchar(); + if (c == '\\') + { c = Getchar(); + if (c == 'n') c = '\n'; + else if (c == 'r') c = '\r'; + else if (c == 't') c = '\t'; + else if (c == 'f') c = '\f'; + } + if (Getchar() != '\'' && !in_comment) + fatal("character quote missing: %s", yytext); + ValToken(c, CONST) + + default: + break; + } + + if (isdigit_(c)) + { getword(c, isdigit_); + ValToken(atoi(yytext), CONST) + } + + if (isalpha_(c) || c == '_') + { getword(c, isalnum_); + if (!in_comment) + { c = check_name(yytext); + if (c) return c; + /* else fall through */ + } + goto again; + } + + switch (c) { + case '/': c = follow('*', 0, '/'); + if (!c) { in_comment = 1; goto again; } + break; + case '*': c = follow('/', 0, '*'); + if (!c) { in_comment = 0; goto again; } + break; + case ':': c = follow(':', SEP, ':'); break; + case '-': c = follow('>', SEMI, follow('-', DECR, '-')); break; + case '+': c = follow('+', INCR, '+'); break; + case '<': c = follow('<', LSHIFT, follow('=', LE, LT)); break; + case '>': c = follow('>', RSHIFT, follow('=', GE, GT)); break; + case '=': c = follow('=', EQ, ASGN); break; + case '!': c = follow('=', NE, follow('!', O_SND, SND)); break; + case '?': c = follow('?', R_RCV, RCV); break; + case '&': c = follow('&', AND, '&'); break; + case '|': c = follow('|', OR, '|'); break; + case ';': c = SEMI; break; + default : break; + } + Token(c) +} + +static struct { + char *s; int tok; int val; char *sym; +} Names[] = { + {"active", ACTIVE, 0, 0}, + {"assert", ASSERT, 0, 0}, + {"atomic", ATOMIC, 0, 0}, + {"bit", TYPE, BIT, 0}, + {"bool", TYPE, BIT, 0}, + {"break", BREAK, 0, 0}, + {"byte", TYPE, BYTE, 0}, + {"c_code", C_CODE, 0, 0}, + {"c_decl", C_DECL, 0, 0}, + {"c_expr", C_EXPR, 0, 0}, + {"c_state", C_STATE, 0, 0}, + {"c_track", C_TRACK, 0, 0}, + {"D_proctype", D_PROCTYPE, 0, 0}, + {"do", DO, 0, 0}, + {"chan", TYPE, CHAN, 0}, + {"else", ELSE, 0, 0}, + {"empty", EMPTY, 0, 0}, + {"enabled", ENABLED, 0, 0}, + {"eval", EVAL, 0, 0}, + {"false", CONST, 0, 0}, + {"fi", FI, 0, 0}, + {"full", FULL, 0, 0}, + {"goto", GOTO, 0, 0}, + {"hidden", HIDDEN, 0, ":hide:"}, + {"if", IF, 0, 0}, + {"init", INIT, 0, ":init:"}, + {"int", TYPE, INT, 0}, + {"len", LEN, 0, 0}, + {"local", ISLOCAL, 0, ":local:"}, + {"mtype", TYPE, MTYPE, 0}, + {"nempty", NEMPTY, 0, 0}, + {"never", CLAIM, 0, ":never:"}, + {"nfull", NFULL, 0, 0}, + {"notrace", TRACE, 0, ":notrace:"}, + {"np_", NONPROGRESS, 0, 0}, + {"od", OD, 0, 0}, + {"of", OF, 0, 0}, + {"pc_value", PC_VAL, 0, 0}, + {"pid", TYPE, BYTE, 0}, + {"printf", PRINT, 0, 0}, + {"printm", PRINTM, 0, 0}, + {"priority", PRIORITY, 0, 0}, + {"proctype", PROCTYPE, 0, 0}, + {"provided", PROVIDED, 0, 0}, + {"run", RUN, 0, 0}, + {"d_step", D_STEP, 0, 0}, + {"inline", INLINE, 0, 0}, + {"short", TYPE, SHORT, 0}, + {"skip", CONST, 1, 0}, + {"timeout", TIMEOUT, 0, 0}, + {"trace", TRACE, 0, ":trace:"}, + {"true", CONST, 1, 0}, + {"show", SHOW, 0, ":show:"}, + {"typedef", TYPEDEF, 0, 0}, + {"unless", UNLESS, 0, 0}, + {"unsigned", TYPE, UNSIGNED, 0}, + {"xr", XU, XR, 0}, + {"xs", XU, XS, 0}, + {0, 0, 0, 0}, +}; + +static int +check_name(char *s) +{ int i; + + yylval = nn(ZN, 0, ZN, ZN); + for (i = 0; Names[i].s; i++) + if (strcmp(s, Names[i].s) == 0) + { yylval->val = Names[i].val; + if (Names[i].sym) + yylval->sym = lookup(Names[i].sym); + return Names[i].tok; + } + + if ((yylval->val = ismtype(s)) != 0) + { yylval->ismtyp = 1; + return CONST; + } + + if (strcmp(s, "_last") == 0) + has_last++; + + if (Inlining >= 0 && !ReDiRect) + { Lextok *tt, *t = Inline_stub[Inlining]->params; + + for (i = 0; t; t = t->rgt, i++) /* formal pars */ + if (!strcmp(s, t->lft->sym->name) /* varname matches formal */ + && strcmp(s, Inline_stub[Inlining]->anms[i]) != 0) /* actual pars */ + { +#if 0 + if (verbose&32) + printf("\tline %d, replace %s in call of '%s' with %s\n", + lineno, s, + Inline_stub[Inlining]->nm->name, + Inline_stub[Inlining]->anms[i]); +#endif + + for (tt = Inline_stub[Inlining]->params; tt; tt = tt->rgt) + if (!strcmp(Inline_stub[Inlining]->anms[i], + tt->lft->sym->name)) + { /* would be cyclic if not caught */ + printf("spin: line %d replacement value: %s\n", + lineno, tt->lft->sym->name); + fatal("formal par of %s matches replacement value", + Inline_stub[Inlining]->nm->name); + yylval->ntyp = tt->lft->ntyp; + yylval->sym = lookup(tt->lft->sym->name); + return NAME; + } + ReDiRect = Inline_stub[Inlining]->anms[i]; + return 0; + } } + + yylval->sym = lookup(s); /* symbol table */ + if (isutype(s)) + return UNAME; + if (isproctype(s)) + return PNAME; + if (iseqname(s)) + return INAME; + + return NAME; +} + +int +yylex(void) +{ static int last = 0; + static int hold = 0; + int c; + /* + * repair two common syntax mistakes with + * semi-colons before or after a '}' + */ + if (hold) + { c = hold; + hold = 0; + } else + { c = lex(); + if (last == ELSE + && c != SEMI + && c != FI) + { hold = c; + last = 0; + return SEMI; + } + if (last == '}' + && c != PROCTYPE + && c != INIT + && c != CLAIM + && c != SEP + && c != FI + && c != OD + && c != '}' + && c != UNLESS + && c != SEMI + && c != EOF) + { hold = c; + last = 0; + return SEMI; /* insert ';' */ + } + if (c == SEMI) + { /* if context, we're not in a typedef + * because they're global. + * if owner, we're at the end of a ref + * to a struct field -- prevent that the + * lookahead is interpreted as a field of + * the same struct... + */ + if (context) owner = ZS; + hold = lex(); /* look ahead */ + if (hold == '}' + || hold == SEMI) + { c = hold; /* omit ';' */ + hold = 0; + } + } + } + last = c; + + if (IArgs) + { static int IArg_nst = 0; + + if (strcmp(yytext, ",") == 0) + { IArg_cont[++IArgno][0] = '\0'; + } else if (strcmp(yytext, "(") == 0) + { if (IArg_nst++ == 0) + { IArgno = 0; + IArg_cont[0][0] = '\0'; + } else + strcat(IArg_cont[IArgno], yytext); + } else if (strcmp(yytext, ")") == 0) + { if (--IArg_nst > 0) + strcat(IArg_cont[IArgno], yytext); + } else if (c == CONST && yytext[0] == '\'') + { sprintf(yytext, "'%c'", yylval->val); + strcat(IArg_cont[IArgno], yytext); + } else + { + switch (c) { + case SEP: strcpy(yytext, "::"); break; + case SEMI: strcpy(yytext, ";"); break; + case DECR: strcpy(yytext, "--"); break; + case INCR: strcpy(yytext, "++"); break; + case LSHIFT: strcpy(yytext, "<<"); break; + case RSHIFT: strcpy(yytext, ">>"); break; + case LE: strcpy(yytext, "<="); break; + case LT: strcpy(yytext, "<"); break; + case GE: strcpy(yytext, ">="); break; + case GT: strcpy(yytext, ">"); break; + case EQ: strcpy(yytext, "=="); break; + case ASGN: strcpy(yytext, "="); break; + case NE: strcpy(yytext, "!="); break; + case R_RCV: strcpy(yytext, "??"); break; + case RCV: strcpy(yytext, "?"); break; + case O_SND: strcpy(yytext, "!!"); break; + case SND: strcpy(yytext, "!"); break; + case AND: strcpy(yytext, "&&"); break; + case OR: strcpy(yytext, "||"); break; + } + strcat(IArg_cont[IArgno], yytext); + } + } + + return c; +} diff --git a/sys/src/cmd/spin/structs.c b/sys/src/cmd/spin/structs.c new file mode 100755 index 000000000..5ade6054e --- /dev/null +++ b/sys/src/cmd/spin/structs.c @@ -0,0 +1,659 @@ +/***** spin: structs.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +typedef struct UType { + Symbol *nm; /* name of the type */ + Lextok *cn; /* contents */ + struct UType *nxt; /* linked list */ +} UType; + +extern Symbol *Fname; +extern int lineno, depth, Expand_Ok; + +Symbol *owner; + +static UType *Unames = 0; +static UType *Pnames = 0; + +static Lextok *cpnn(Lextok *, int, int, int); +extern void sr_mesg(FILE *, int, int); + +void +setuname(Lextok *n) +{ UType *tmp; + + 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; + tmp->nxt = Unames; + Unames = tmp; +} + +static void +putUname(FILE *fd, UType *tmp) +{ Lextok *fp, *tl; + + if (!tmp) return; + putUname(fd, tmp->nxt); /* postorder */ + fprintf(fd, "struct %s { /* user defined type */\n", + tmp->nm->name); + for (fp = tmp->cn; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + typ2c(tl->sym); + fprintf(fd, "};\n"); +} + +void +putunames(FILE *fd) +{ + putUname(fd, Unames); +} + +int +isutype(char *t) +{ UType *tmp; + + for (tmp = Unames; tmp; tmp = tmp->nxt) + { if (!strcmp(t, tmp->nm->name)) + return 1; + } + return 0; +} + +Lextok * +getuname(Symbol *t) +{ UType *tmp; + + for (tmp = Unames; tmp; tmp = tmp->nxt) + { if (!strcmp(t->name, tmp->nm->name)) + return tmp->cn; + } + fatal("%s is not a typename", t->name); + return (Lextok *)0; +} + +void +setutype(Lextok *p, Symbol *t, Lextok *vis) /* user-defined types */ +{ int oln = lineno; + Symbol *ofn = Fname; + Lextok *m, *n; + + m = getuname(t); + for (n = p; n; n = n->rgt) + { lineno = n->ln; + Fname = n->fn; + if (n->sym->type) + non_fatal("redeclaration of '%s'", n->sym->name); + + if (n->sym->nbits > 0) + non_fatal("(%s) only an unsigned can have width-field", + n->sym->name); + + if (Expand_Ok) + n->sym->hidden |= (4|8|16); /* formal par */ + + if (vis) + { if (strncmp(vis->sym->name, ":hide:", 6) == 0) + n->sym->hidden |= 1; + else if (strncmp(vis->sym->name, ":show:", 6) == 0) + n->sym->hidden |= 2; + else if (strncmp(vis->sym->name, ":local:", 7) == 0) + n->sym->hidden |= 64; + } + n->sym->type = STRUCT; /* classification */ + n->sym->Slst = m; /* structure itself */ + n->sym->Snm = t; /* name of typedef */ + n->sym->Nid = 0; /* this is no chan */ + n->sym->hidden |= 4; + if (n->sym->nel <= 0) + non_fatal("bad array size for '%s'", n->sym->name); + } + lineno = oln; + Fname = ofn; +} + +static Symbol * +do_same(Lextok *n, Symbol *v, int xinit) +{ Lextok *tmp, *fp, *tl; + int ix = eval(n->lft); + int oln = lineno; + Symbol *ofn = Fname; + + lineno = n->ln; + Fname = n->fn; + + /* n->sym->type == STRUCT + * index: n->lft + * subfields: n->rgt + * structure template: n->sym->Slst + * runtime values: n->sym->Sval + */ + if (xinit) ini_struct(v); /* once, at top level */ + + if (ix >= v->nel || ix < 0) + { printf("spin: indexing %s[%d] - size is %d\n", + v->name, ix, v->nel); + fatal("indexing error \'%s\'", v->name); + } + if (!n->rgt || !n->rgt->lft) + { non_fatal("no subfields %s", v->name); /* i.e., wants all */ + lineno = oln; Fname = ofn; + return ZS; + } + + if (n->rgt->ntyp != '.') + { printf("bad subfield type %d\n", n->rgt->ntyp); + alldone(1); + } + + tmp = n->rgt->lft; + if (tmp->ntyp != NAME && tmp->ntyp != TYPE) + { printf("bad subfield entry %d\n", tmp->ntyp); + alldone(1); + } + for (fp = v->Sval[ix]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + if (!strcmp(tl->sym->name, tmp->sym->name)) + { lineno = oln; Fname = ofn; + return tl->sym; + } + fatal("cannot locate subfield %s", tmp->sym->name); + return ZS; +} + +int +Rval_struct(Lextok *n, Symbol *v, int xinit) /* n varref, v valref */ +{ Symbol *tl; + Lextok *tmp; + int ix; + + if (!n || !(tl = do_same(n, v, xinit))) + return 0; + + tmp = n->rgt->lft; + if (tmp->sym->type == STRUCT) + { return Rval_struct(tmp, tl, 0); + } else if (tmp->rgt) + fatal("non-zero 'rgt' on non-structure", 0); + + ix = eval(tmp->lft); + if (ix >= tl->nel || ix < 0) + fatal("indexing error \'%s\'", tl->name); + + return cast_val(tl->type, tl->val[ix], tl->nbits); +} + +int +Lval_struct(Lextok *n, Symbol *v, int xinit, int a) /* a = assigned value */ +{ Symbol *tl; + Lextok *tmp; + int ix; + + if (!(tl = do_same(n, v, xinit))) + return 1; + + tmp = n->rgt->lft; + if (tmp->sym->type == STRUCT) + return Lval_struct(tmp, tl, 0, a); + else if (tmp->rgt) + fatal("non-zero 'rgt' on non-structure", 0); + + ix = eval(tmp->lft); + if (ix >= tl->nel || ix < 0) + fatal("indexing error \'%s\'", tl->name); + + if (tl->nbits > 0) + a = (a & ((1<<tl->nbits)-1)); + tl->val[ix] = a; + tl->setat = depth; + + return 1; +} + +int +Cnt_flds(Lextok *m) +{ Lextok *fp, *tl, *n; + int cnt = 0; + + if (m->ntyp == ',') + { n = m; + goto is_lst; + } + if (!m->sym || m->ntyp != STRUCT) + return 1; + + n = getuname(m->sym); +is_lst: + for (fp = n; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + { if (tl->sym->nel != 1) + fatal("array of structures in param list, %s", + tl->sym->name); + cnt += Cnt_flds(tl->sym->Slst); + } else + cnt += tl->sym->nel; + } + return cnt; +} + +int +Sym_typ(Lextok *t) +{ Symbol *s = t->sym; + + if (!s) return 0; + + if (s->type != STRUCT) + return s->type; + + if (!t->rgt + || !t->rgt->ntyp == '.' + || !t->rgt->lft) + return STRUCT; /* not a field reference */ + + return Sym_typ(t->rgt->lft); +} + +int +Width_set(int *wdth, int i, Lextok *n) +{ Lextok *fp, *tl; + int j = i, k; + + for (fp = n; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + j = Width_set(wdth, j, tl->sym->Slst); + else + { for (k = 0; k < tl->sym->nel; k++, j++) + wdth[j] = tl->sym->type; + } } + return j; +} + +void +ini_struct(Symbol *s) +{ int i; Lextok *fp, *tl; + + if (s->type != STRUCT) /* last step */ + { (void) checkvar(s, 0); + return; + } + if (s->Sval == (Lextok **) 0) + { s->Sval = (Lextok **) emalloc(s->nel * sizeof(Lextok *)); + for (i = 0; i < s->nel; i++) + { s->Sval[i] = cpnn(s->Slst, 1, 1, 1); + + for (fp = s->Sval[i]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + ini_struct(tl->sym); + } } +} + +static Lextok * +cpnn(Lextok *s, int L, int R, int S) +{ Lextok *d; extern int Nid; + + if (!s) return ZN; + + d = (Lextok *) emalloc(sizeof(Lextok)); + d->ntyp = s->ntyp; + d->val = s->val; + d->ln = s->ln; + d->fn = s->fn; + d->sym = s->sym; + if (L) d->lft = cpnn(s->lft, 1, 1, S); + if (R) d->rgt = cpnn(s->rgt, 1, 1, S); + + if (S && s->sym) + { d->sym = (Symbol *) emalloc(sizeof(Symbol)); + memcpy(d->sym, s->sym, sizeof(Symbol)); + if (d->sym->type == CHAN) + d->sym->Nid = ++Nid; + } + if (s->sq || s->sl) + fatal("cannot happen cpnn", (char *) 0); + + return d; +} + +int +full_name(FILE *fd, Lextok *n, Symbol *v, int xinit) +{ Symbol *tl; + Lextok *tmp; + int hiddenarrays = 0; + + fprintf(fd, "%s", v->name); + + if (!n || !(tl = do_same(n, v, xinit))) + return 0; + tmp = n->rgt->lft; + + if (tmp->sym->type == STRUCT) + { fprintf(fd, "."); + hiddenarrays = full_name(fd, tmp, tl, 0); + goto out; + } + fprintf(fd, ".%s", tl->name); +out: if (tmp->sym->nel > 1) + { fprintf(fd, "[%d]", eval(tmp->lft)); + hiddenarrays = 1; + } + return hiddenarrays; +} + +void +validref(Lextok *p, Lextok *c) +{ Lextok *fp, *tl; + char lbuf[512]; + + for (fp = p->sym->Slst; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + if (strcmp(tl->sym->name, c->sym->name) == 0) + return; + + sprintf(lbuf, "no field '%s' defined in structure '%s'\n", + c->sym->name, p->sym->name); + non_fatal(lbuf, (char *) 0); +} + +void +struct_name(Lextok *n, Symbol *v, int xinit, char *buf) +{ Symbol *tl; + Lextok *tmp; + char lbuf[512]; + + if (!n || !(tl = do_same(n, v, xinit))) + return; + tmp = n->rgt->lft; + if (tmp->sym->type == STRUCT) + { strcat(buf, "."); + struct_name(tmp, tl, 0, buf); + return; + } + sprintf(lbuf, ".%s", tl->name); + strcat(buf, lbuf); + if (tmp->sym->nel > 1) + { sprintf(lbuf, "[%d]", eval(tmp->lft)); + strcat(buf, lbuf); + } +} + +void +walk2_struct(char *s, Symbol *z) +{ Lextok *fp, *tl; + char eprefix[128]; + int ix; + extern void Done_case(char *, Symbol *); + + ini_struct(z); + if (z->nel == 1) + sprintf(eprefix, "%s%s.", s, z->name); + for (ix = 0; ix < z->nel; ix++) + { if (z->nel > 1) + sprintf(eprefix, "%s%s[%d].", s, z->name, ix); + for (fp = z->Sval[ix]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + walk2_struct(eprefix, tl->sym); + else if (tl->sym->type == CHAN) + Done_case(eprefix, tl->sym); + } } +} + +void +walk_struct(FILE *ofd, int dowhat, char *s, Symbol *z, char *a, char *b, char *c) +{ Lextok *fp, *tl; + char eprefix[128]; + int ix; + + ini_struct(z); + if (z->nel == 1) + sprintf(eprefix, "%s%s.", s, z->name); + for (ix = 0; ix < z->nel; ix++) + { if (z->nel > 1) + sprintf(eprefix, "%s%s[%d].", s, z->name, ix); + for (fp = z->Sval[ix]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + walk_struct(ofd, dowhat, eprefix, tl->sym, a,b,c); + else + do_var(ofd, dowhat, eprefix, tl->sym, a,b,c); + } } +} + +void +c_struct(FILE *fd, char *ipref, Symbol *z) +{ Lextok *fp, *tl; + char pref[256], eprefix[256]; + int ix; + + ini_struct(z); + + for (ix = 0; ix < z->nel; ix++) + for (fp = z->Sval[ix]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { strcpy(eprefix, ipref); + if (z->nel > 1) + { /* insert index before last '.' */ + eprefix[strlen(eprefix)-1] = '\0'; + sprintf(pref, "[ %d ].", ix); + strcat(eprefix, pref); + } + if (tl->sym->type == STRUCT) + { strcat(eprefix, tl->sym->name); + strcat(eprefix, "."); + c_struct(fd, eprefix, tl->sym); + } else + c_var(fd, eprefix, tl->sym); + } +} + +void +dump_struct(Symbol *z, char *prefix, RunList *r) +{ Lextok *fp, *tl; + char eprefix[256]; + int ix, jx; + + ini_struct(z); + + for (ix = 0; ix < z->nel; ix++) + { if (z->nel > 1) + sprintf(eprefix, "%s[%d]", prefix, ix); + else + strcpy(eprefix, prefix); + + for (fp = z->Sval[ix]; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + { char pref[256]; + strcpy(pref, eprefix); + strcat(pref, "."); + strcat(pref, tl->sym->name); + dump_struct(tl->sym, pref, r); + } else + for (jx = 0; jx < tl->sym->nel; jx++) + { if (tl->sym->type == CHAN) + doq(tl->sym, jx, r); + else + { printf("\t\t"); + if (r) + printf("%s(%d):", r->n->name, r->pid); + printf("%s.%s", eprefix, tl->sym->name); + if (tl->sym->nel > 1) + printf("[%d]", jx); + printf(" = "); + sr_mesg(stdout, tl->sym->val[jx], + tl->sym->type == MTYPE); + printf("\n"); + } } } + } +} + +static int +retrieve(Lextok **targ, int i, int want, Lextok *n, int Ntyp) +{ Lextok *fp, *tl; + int j = i, k; + + for (fp = n; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + { j = retrieve(targ, j, want, tl->sym->Slst, Ntyp); + if (j < 0) + { Lextok *x = cpnn(tl, 1, 0, 0); + x->rgt = nn(ZN, '.', (*targ), ZN); + (*targ) = x; + return -1; + } + } else + { for (k = 0; k < tl->sym->nel; k++, j++) + { if (j == want) + { *targ = cpnn(tl, 1, 0, 0); + (*targ)->lft = nn(ZN, CONST, ZN, ZN); + (*targ)->lft->val = k; + if (Ntyp) + (*targ)->ntyp = (short) Ntyp; + return -1; + } + } } } + return j; +} + +static int +is_explicit(Lextok *n) +{ + if (!n) return 0; + if (!n->sym) fatal("unexpected - no symbol", 0); + if (n->sym->type != STRUCT) return 1; + if (!n->rgt) return 0; + if (n->rgt->ntyp != '.') + { lineno = n->ln; + Fname = n->fn; + printf("ntyp %d\n", n->rgt->ntyp); + fatal("unexpected %s, no '.'", n->sym->name); + } + return is_explicit(n->rgt->lft); +} + +Lextok * +expand(Lextok *n, int Ok) + /* turn rgt-lnked list of struct nms, into ',' list of flds */ +{ Lextok *x = ZN, *y; + + if (!Ok) return n; + + while (n) + { y = mk_explicit(n, 1, 0); + if (x) + (void) tail_add(x, y); + else + x = y; + + n = n->rgt; + } + return x; +} + +Lextok * +mk_explicit(Lextok *n, int Ok, int Ntyp) + /* produce a single ',' list of fields */ +{ Lextok *bld = ZN, *x; + int i, cnt; extern int IArgs; + + if (n->sym->type != STRUCT + || is_explicit(n)) + return n; + + if (n->rgt + && n->rgt->ntyp == '.' + && n->rgt->lft + && n->rgt->lft->sym + && n->rgt->lft->sym->type == STRUCT) + { Lextok *y; + bld = mk_explicit(n->rgt->lft, Ok, Ntyp); + for (x = bld; x; x = x->rgt) + { y = cpnn(n, 1, 0, 0); + y->rgt = nn(ZN, '.', x->lft, ZN); + x->lft = y; + } + + return bld; + } + + if (!Ok || !n->sym->Slst) + { if (IArgs) return n; + printf("spin: saw '"); + comment(stdout, n, 0); + printf("'\n"); + fatal("incomplete structure ref '%s'", n->sym->name); + } + + cnt = Cnt_flds(n->sym->Slst); + for (i = cnt-1; i >= 0; i--) + { bld = nn(ZN, ',', ZN, bld); + if (retrieve(&(bld->lft), 0, i, n->sym->Slst, Ntyp) >= 0) + { printf("cannot retrieve field %d\n", i); + fatal("bad structure %s", n->sym->name); + } + x = cpnn(n, 1, 0, 0); + x->rgt = nn(ZN, '.', bld->lft, ZN); + bld->lft = x; + } + return bld; +} + +Lextok * +tail_add(Lextok *a, Lextok *b) +{ Lextok *t; + + for (t = a; t->rgt; t = t->rgt) + if (t->ntyp != ',') + fatal("unexpected type - tail_add", 0); + t->rgt = b; + return a; +} + +void +setpname(Lextok *n) +{ UType *tmp; + + for (tmp = Pnames; tmp; tmp = tmp->nxt) + if (!strcmp(n->sym->name, tmp->nm->name)) + { non_fatal("proctype %s redefined", + n->sym->name); + return; + } + tmp = (UType *) emalloc(sizeof(UType)); + tmp->nm = n->sym; + tmp->nxt = Pnames; + Pnames = tmp; +} + +int +isproctype(char *t) +{ UType *tmp; + + for (tmp = Pnames; tmp; tmp = tmp->nxt) + { if (!strcmp(t, tmp->nm->name)) + return 1; + } + return 0; +} diff --git a/sys/src/cmd/spin/sym.c b/sys/src/cmd/spin/sym.c new file mode 100755 index 000000000..0001d2f5f --- /dev/null +++ b/sys/src/cmd/spin/sym.c @@ -0,0 +1,534 @@ +/***** spin: sym.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +extern Symbol *Fname, *owner; +extern int lineno, depth, verbose, NamesNotAdded, deadvar; +extern short has_xu; + +Symbol *context = ZS; +Ordered *all_names = (Ordered *)0; +int Nid = 0; + +static Ordered *last_name = (Ordered *)0; +static Symbol *symtab[Nhash+1]; + +static int +samename(Symbol *a, Symbol *b) +{ + if (!a && !b) return 1; + if (!a || !b) return 0; + return !strcmp(a->name, b->name); +} + +int +hash(char *s) +{ int h=0; + + while (*s) + { h += *s++; + h <<= 1; + if (h&(Nhash+1)) + h |= 1; + } + return h&Nhash; +} + +Symbol * +lookup(char *s) +{ Symbol *sp; Ordered *no; + int h = hash(s); + + for (sp = symtab[h]; sp; sp = sp->next) + if (strcmp(sp->name, s) == 0 + && samename(sp->context, context) + && samename(sp->owner, owner)) + return sp; /* found */ + + if (context) /* in proctype */ + for (sp = symtab[h]; sp; sp = sp->next) + if (strcmp(sp->name, s) == 0 + && !sp->context + && samename(sp->owner, owner)) + return sp; /* global */ + + sp = (Symbol *) emalloc(sizeof(Symbol)); + sp->name = (char *) emalloc((int) strlen(s) + 1); + strcpy(sp->name, s); + sp->nel = 1; + sp->setat = depth; + sp->context = context; + sp->owner = owner; /* if fld in struct */ + + if (NamesNotAdded == 0) + { sp->next = symtab[h]; + symtab[h] = sp; + no = (Ordered *) emalloc(sizeof(Ordered)); + no->entry = sp; + if (!last_name) + last_name = all_names = no; + else + { last_name->next = no; + last_name = no; + } } + + return sp; +} + +void +trackvar(Lextok *n, Lextok *m) +{ Symbol *sp = n->sym; + + if (!sp) return; /* a structure list */ + switch (m->ntyp) { + case NAME: + if (m->sym->type != BIT) + { sp->hidden |= 4; + if (m->sym->type != BYTE) + sp->hidden |= 8; + } + break; + case CONST: + if (m->val != 0 && m->val != 1) + sp->hidden |= 4; + if (m->val < 0 || m->val > 256) + sp->hidden |= 8; /* ditto byte-equiv */ + break; + default: /* unknown */ + sp->hidden |= (4|8); /* not known bit-equiv */ + } +} + +Lextok *runstmnts = ZN; + +void +trackrun(Lextok *n) +{ + runstmnts = nn(ZN, 0, n, runstmnts); +} + +void +checkrun(Symbol *parnm, int posno) +{ Lextok *n, *now, *v; int i, m; + int res = 0; char buf[16], buf2[16]; + + for (n = runstmnts; n; n = n->rgt) + { now = n->lft; + if (now->sym != parnm->context) + continue; + for (v = now->lft, i = 0; v; v = v->rgt, i++) + if (i == posno) + { m = v->lft->ntyp; + if (m == CONST) + { m = v->lft->val; + if (m != 0 && m != 1) + res |= 4; + if (m < 0 || m > 256) + res |= 8; + } else if (m == NAME) + { m = v->lft->sym->type; + if (m != BIT) + { res |= 4; + if (m != BYTE) + res |= 8; + } + } else + res |= (4|8); /* unknown */ + break; + } } + if (!(res&4) || !(res&8)) + { if (!(verbose&32)) return; + strcpy(buf2, (!(res&4))?"bit":"byte"); + sputtype(buf, parnm->type); + i = (int) strlen(buf); + while (buf[--i] == ' ') buf[i] = '\0'; + if (strcmp(buf, buf2) == 0) return; + prehint(parnm); + printf("proctype %s, '%s %s' could be declared", + parnm->context->name, buf, parnm->name); + printf(" '%s %s'\n", buf2, parnm->name); + } +} + +void +trackchanuse(Lextok *m, Lextok *w, int t) +{ Lextok *n = m; int cnt = 1; + while (n) + { if (n->lft + && n->lft->sym + && n->lft->sym->type == CHAN) + setaccess(n->lft->sym, w?w->sym:ZS, cnt, t); + n = n->rgt; cnt++; + } +} + +void +setptype(Lextok *n, int t, Lextok *vis) /* predefined types */ +{ int oln = lineno, cnt = 1; extern int Expand_Ok; + + while (n) + { if (n->sym->type && !(n->sym->hidden&32)) + { lineno = n->ln; Fname = n->fn; + non_fatal("redeclaration of '%s'", n->sym->name); + lineno = oln; + } + n->sym->type = (short) t; + + if (Expand_Ok) + { n->sym->hidden |= (4|8|16); /* formal par */ + if (t == CHAN) + setaccess(n->sym, ZS, cnt, 'F'); + } + if (t == UNSIGNED) + { if (n->sym->nbits < 0 || n->sym->nbits >= 32) + fatal("(%s) has invalid width-field", n->sym->name); + if (n->sym->nbits == 0) + { n->sym->nbits = 16; + non_fatal("unsigned without width-field", 0); + } + } else if (n->sym->nbits > 0) + { non_fatal("(%s) only an unsigned can have width-field", + n->sym->name); + } + if (vis) + { if (strncmp(vis->sym->name, ":hide:", 6) == 0) + { n->sym->hidden |= 1; + if (t == BIT) + fatal("bit variable (%s) cannot be hidden", + n->sym->name); + } else if (strncmp(vis->sym->name, ":show:", 6) == 0) + { n->sym->hidden |= 2; + } else if (strncmp(vis->sym->name, ":local:", 7) == 0) + { n->sym->hidden |= 64; + } + } + if (t == CHAN) + n->sym->Nid = ++Nid; + else + { n->sym->Nid = 0; + if (n->sym->ini + && n->sym->ini->ntyp == CHAN) + { Fname = n->fn; + lineno = n->ln; + fatal("chan initializer for non-channel %s", + n->sym->name); + } + } + if (n->sym->nel <= 0) + { lineno = n->ln; Fname = n->fn; + non_fatal("bad array size for '%s'", n->sym->name); + lineno = oln; + } + n = n->rgt; cnt++; + } +} + +static void +setonexu(Symbol *sp, int t) +{ + sp->xu |= t; + if (t == XR || t == XS) + { if (sp->xup[t-1] + && strcmp(sp->xup[t-1]->name, context->name)) + { printf("error: x[rs] claims from %s and %s\n", + sp->xup[t-1]->name, context->name); + non_fatal("conflicting claims on chan '%s'", + sp->name); + } + sp->xup[t-1] = context; + } +} + +static void +setallxu(Lextok *n, int t) +{ Lextok *fp, *tl; + + for (fp = n; fp; fp = fp->rgt) + for (tl = fp->lft; tl; tl = tl->rgt) + { if (tl->sym->type == STRUCT) + setallxu(tl->sym->Slst, t); + else if (tl->sym->type == CHAN) + setonexu(tl->sym, t); + } +} + +Lextok *Xu_List = (Lextok *) 0; + +void +setxus(Lextok *p, int t) +{ Lextok *m, *n; + + has_xu = 1; + if (!context) + { lineno = p->ln; + Fname = p->fn; + fatal("non-local x[rs] assertion", (char *)0); + } + for (m = p; m; m = m->rgt) + { Lextok *Xu_new = (Lextok *) emalloc(sizeof(Lextok)); + Xu_new->val = t; + Xu_new->lft = m->lft; + Xu_new->sym = context; + Xu_new->rgt = Xu_List; + Xu_List = Xu_new; + + n = m->lft; + if (n->sym->type == STRUCT) + setallxu(n->sym->Slst, t); + else if (n->sym->type == CHAN) + setonexu(n->sym, t); + else + { int oln = lineno; + lineno = n->ln; Fname = n->fn; + non_fatal("xr or xs of non-chan '%s'", + n->sym->name); + lineno = oln; + } + } +} + +Lextok *Mtype = (Lextok *) 0; + +void +setmtype(Lextok *m) +{ Lextok *n; + int cnt, oln = lineno; + + if (m) { lineno = m->ln; Fname = m->fn; } + + if (!Mtype) + Mtype = m; + else + { for (n = Mtype; n->rgt; n = n->rgt) + ; + n->rgt = m; /* concatenate */ + } + + for (n = Mtype, cnt = 1; n; n = n->rgt, cnt++) /* syntax check */ + { if (!n->lft || !n->lft->sym + || n->lft->ntyp != NAME + || n->lft->lft) /* indexed variable */ + fatal("bad mtype definition", (char *)0); + + /* label the name */ + if (n->lft->sym->type != MTYPE) + { n->lft->sym->hidden |= 128; /* is used */ + n->lft->sym->type = MTYPE; + n->lft->sym->ini = nn(ZN,CONST,ZN,ZN); + n->lft->sym->ini->val = cnt; + } else if (n->lft->sym->ini->val != cnt) + non_fatal("name %s appears twice in mtype declaration", + n->lft->sym->name); + } + lineno = oln; + if (cnt > 256) + fatal("too many mtype elements (>255)", (char *)0); +} + +int +ismtype(char *str) /* name to number */ +{ Lextok *n; + int cnt = 1; + + for (n = Mtype; n; n = n->rgt) + { if (strcmp(str, n->lft->sym->name) == 0) + return cnt; + cnt++; + } + return 0; +} + +int +sputtype(char *foo, int m) +{ + switch (m) { + case UNSIGNED: strcpy(foo, "unsigned "); break; + case BIT: strcpy(foo, "bit "); break; + case BYTE: strcpy(foo, "byte "); break; + case CHAN: strcpy(foo, "chan "); break; + case SHORT: strcpy(foo, "short "); break; + case INT: strcpy(foo, "int "); break; + case MTYPE: strcpy(foo, "mtype "); break; + case STRUCT: strcpy(foo, "struct"); break; + case PROCTYPE: strcpy(foo, "proctype"); break; + case LABEL: strcpy(foo, "label "); return 0; + default: strcpy(foo, "value "); return 0; + } + return 1; +} + + +static int +puttype(int m) +{ char buf[128]; + + if (sputtype(buf, m)) + { printf("%s", buf); + return 1; + } + return 0; +} + +void +symvar(Symbol *sp) +{ Lextok *m; + + if (!puttype(sp->type)) + return; + + printf("\t"); + if (sp->owner) printf("%s.", sp->owner->name); + printf("%s", sp->name); + if (sp->nel > 1) printf("[%d]", sp->nel); + + if (sp->type == CHAN) + printf("\t%d", (sp->ini)?sp->ini->val:0); + else if (sp->type == STRUCT) /* Frank Weil, 2.9.8 */ + printf("\t%s", sp->Snm->name); + else + printf("\t%d", eval(sp->ini)); + + if (sp->owner) + printf("\t<:struct-field:>"); + else + if (!sp->context) + printf("\t<:global:>"); + else + printf("\t<%s>", sp->context->name); + + if (sp->Nid < 0) /* formal parameter */ + printf("\t<parameter %d>", -(sp->Nid)); + else + printf("\t<variable>"); + if (sp->type == CHAN && sp->ini) + { int i; + for (m = sp->ini->rgt, i = 0; m; m = m->rgt) + i++; + printf("\t%d\t", i); + for (m = sp->ini->rgt; m; m = m->rgt) + { if (m->ntyp == STRUCT) + printf("struct %s", m->sym->name); + else + (void) puttype(m->ntyp); + if (m->rgt) printf("\t"); + } + } + printf("\n"); +} + +void +symdump(void) +{ Ordered *walk; + + for (walk = all_names; walk; walk = walk->next) + symvar(walk->entry); +} + +void +chname(Symbol *sp) +{ printf("chan "); + if (sp->context) printf("%s-", sp->context->name); + if (sp->owner) printf("%s.", sp->owner->name); + printf("%s", sp->name); + if (sp->nel > 1) printf("[%d]", sp->nel); + printf("\t"); +} + + +static struct X { + int typ; char *nm; +} xx[] = { + { 'A', "exported as run parameter" }, + { 'F', "imported as proctype parameter" }, + { 'L', "used as l-value in asgnmnt" }, + { 'V', "used as r-value in asgnmnt" }, + { 'P', "polled in receive stmnt" }, + { 'R', "used as parameter in receive stmnt" }, + { 'S', "used as parameter in send stmnt" }, + { 'r', "received from" }, + { 's', "sent to" }, +}; + +static void +chan_check(Symbol *sp) +{ Access *a; int i, b=0, d; + + if (verbose&1) goto report; /* -C -g */ + + for (a = sp->access; a; a = a->lnk) + if (a->typ == 'r') + b |= 1; + else if (a->typ == 's') + b |= 2; + if (b == 3 || (sp->hidden&16)) /* balanced or formal par */ + return; +report: + chname(sp); + for (i = d = 0; i < (int) (sizeof(xx)/sizeof(struct X)); i++) + { b = 0; + for (a = sp->access; a; a = a->lnk) + if (a->typ == xx[i].typ) b++; + if (b == 0) continue; d++; + printf("\n\t%s by: ", xx[i].nm); + for (a = sp->access; a; a = a->lnk) + if (a->typ == xx[i].typ) + { printf("%s", a->who->name); + if (a->what) printf(" to %s", a->what->name); + if (a->cnt) printf(" par %d", a->cnt); + if (--b > 0) printf(", "); + } + } + printf("%s\n", (!d)?"\n\tnever used under this name":""); +} + +void +chanaccess(void) +{ Ordered *walk; + char buf[128]; + extern int Caccess, separate; + extern short has_code; + + for (walk = all_names; walk; walk = walk->next) + { if (!walk->entry->owner) + switch (walk->entry->type) { + case CHAN: + if (Caccess) chan_check(walk->entry); + break; + case MTYPE: + case BIT: + case BYTE: + case SHORT: + case INT: + case UNSIGNED: + if ((walk->entry->hidden&128)) /* was: 32 */ + continue; + + if (!separate + && !walk->entry->context + && !has_code + && deadvar) + walk->entry->hidden |= 1; /* auto-hide */ + + if (!(verbose&32) || has_code) continue; + + printf("spin: warning, %s, ", Fname->name); + sputtype(buf, walk->entry->type); + if (walk->entry->context) + printf("proctype %s", + walk->entry->context->name); + else + printf("global"); + printf(", '%s%s' variable is never used\n", + buf, walk->entry->name); + } } +} diff --git a/sys/src/cmd/spin/tl.h b/sys/src/cmd/spin/tl.h new file mode 100755 index 000000000..eaa2056c8 --- /dev/null +++ b/sys/src/cmd/spin/tl.h @@ -0,0 +1,128 @@ +/***** tl_spin: tl.h *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include <stdio.h> +#include <string.h> + +typedef struct Symbol { + char *name; + struct Symbol *next; /* linked list, symbol table */ +} Symbol; + +typedef struct Node { + short ntyp; /* node type */ + struct Symbol *sym; + struct Node *lft; /* tree */ + struct Node *rgt; /* tree */ + struct Node *nxt; /* if linked list */ +} Node; + +typedef struct Graph { + Symbol *name; + Symbol *incoming; + Symbol *outgoing; + Symbol *oldstring; + Symbol *nxtstring; + Node *New; + Node *Old; + Node *Other; + Node *Next; + unsigned char isred[64], isgrn[64]; + unsigned char redcnt, grncnt; + unsigned char reachable; + struct Graph *nxt; +} Graph; + +typedef struct Mapping { + char *from; + Graph *to; + struct Mapping *nxt; +} Mapping; + +enum { + ALWAYS=257, + AND, /* 258 */ + EQUIV, /* 259 */ + EVENTUALLY, /* 260 */ + FALSE, /* 261 */ + IMPLIES, /* 262 */ + NOT, /* 263 */ + OR, /* 264 */ + PREDICATE, /* 265 */ + TRUE, /* 266 */ + U_OPER, /* 267 */ + V_OPER /* 268 */ +#ifdef NXT + , NEXT /* 269 */ +#endif +}; + +Node *Canonical(Node *); +Node *canonical(Node *); +Node *cached(Node *); +Node *dupnode(Node *); +Node *getnode(Node *); +Node *in_cache(Node *); +Node *push_negation(Node *); +Node *right_linked(Node *); +Node *tl_nn(int, Node *, Node *); + +Symbol *tl_lookup(char *); +Symbol *getsym(Symbol *); +Symbol *DoDump(Node *); + +char *emalloc(int); /* in main.c */ + +int anywhere(int, Node *, Node *); +int dump_cond(Node *, Node *, int); +int hash(char *); /* in sym.c */ +int isalnum_(int); /* in spinlex.c */ +int isequal(Node *, Node *); +int tl_Getchar(void); + +void *tl_emalloc(int); +void a_stats(void); +void addtrans(Graph *, char *, Node *, char *); +void cache_stats(void); +void dump(Node *); +void exit(int); +void Fatal(char *, char *); +void fatal(char *, char *); +void fsm_print(void); +void releasenode(int, Node *); +void tfree(void *); +void tl_explain(int); +void tl_UnGetchar(void); +void tl_parse(void); +void tl_yyerror(char *); +void trans(Node *); + +#define ZN (Node *)0 +#define ZS (Symbol *)0 +#define Nhash 255 /* must match size in spin.h */ +#define True tl_nn(TRUE, ZN, ZN) +#define False tl_nn(FALSE, ZN, ZN) +#define Not(a) push_negation(tl_nn(NOT, a, ZN)) +#define rewrite(n) canonical(right_linked(n)) + +typedef Node *Nodeptr; +#define YYSTYPE Nodeptr + +#define Debug(x) { if (tl_verbose) printf(x); } +#define Debug2(x,y) { if (tl_verbose) printf(x,y); } +#define Dump(x) { if (tl_verbose) dump(x); } +#define Explain(x) { if (tl_verbose) tl_explain(x); } + +#define Assert(x, y) { if (!(x)) { tl_explain(y); \ + Fatal(": assertion failed\n",(char *)0); } } diff --git a/sys/src/cmd/spin/tl_buchi.c b/sys/src/cmd/spin/tl_buchi.c new file mode 100755 index 000000000..161661950 --- /dev/null +++ b/sys/src/cmd/spin/tl_buchi.c @@ -0,0 +1,666 @@ +/***** tl_spin: tl_buchi.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +extern int tl_verbose, tl_clutter, Total, Max_Red; + +FILE *tl_out; /* if standalone: = stdout; */ + +typedef struct Transition { + Symbol *name; + Node *cond; + int redundant, merged, marked; + struct Transition *nxt; +} Transition; + +typedef struct State { + Symbol *name; + Transition *trans; + Graph *colors; + unsigned char redundant; + unsigned char accepting; + unsigned char reachable; + struct State *nxt; +} State; + +static State *never = (State *) 0; +static int hitsall; + +static int +sametrans(Transition *s, Transition *t) +{ + if (strcmp(s->name->name, t->name->name) != 0) + return 0; + return isequal(s->cond, t->cond); +} + +static Node * +Prune(Node *p) +{ + if (p) + switch (p->ntyp) { + case PREDICATE: + case NOT: + case FALSE: + case TRUE: +#ifdef NXT + case NEXT: +#endif + return p; + case OR: + p->lft = Prune(p->lft); + if (!p->lft) + { releasenode(1, p->rgt); + return ZN; + } + p->rgt = Prune(p->rgt); + if (!p->rgt) + { releasenode(1, p->lft); + return ZN; + } + return p; + case AND: + p->lft = Prune(p->lft); + if (!p->lft) + return Prune(p->rgt); + p->rgt = Prune(p->rgt); + if (!p->rgt) + return p->lft; + return p; + } + releasenode(1, p); + return ZN; +} + +static State * +findstate(char *nm) +{ State *b; + for (b = never; b; b = b->nxt) + if (!strcmp(b->name->name, nm)) + return b; + if (strcmp(nm, "accept_all")) + { if (strncmp(nm, "accept", 6)) + { int i; char altnm[64]; + for (i = 0; i < 64; i++) + if (nm[i] == '_') + break; + if (i >= 64) + Fatal("name too long %s", nm); + sprintf(altnm, "accept%s", &nm[i]); + return findstate(altnm); + } + /* Fatal("buchi: no state %s", nm); */ + } + return (State *) 0; +} + +static void +Dfs(State *b) +{ Transition *t; + + if (!b || b->reachable) return; + b->reachable = 1; + + if (b->redundant) + printf("/* redundant state %s */\n", + b->name->name); + for (t = b->trans; t; t = t->nxt) + { if (!t->redundant) + { Dfs(findstate(t->name->name)); + if (!hitsall + && strcmp(t->name->name, "accept_all") == 0) + hitsall = 1; + } + } +} + +void +retarget(char *from, char *to) +{ State *b; + Transition *t; + Symbol *To = tl_lookup(to); + + if (tl_verbose) printf("replace %s with %s\n", from, to); + + for (b = never; b; b = b->nxt) + { if (!strcmp(b->name->name, from)) + b->redundant = 1; + else + for (t = b->trans; t; t = t->nxt) + { if (!strcmp(t->name->name, from)) + t->name = To; + } } +} + +#ifdef NXT +static Node * +nonxt(Node *n) +{ + switch (n->ntyp) { + case U_OPER: + case V_OPER: + case NEXT: + return ZN; + case OR: + n->lft = nonxt(n->lft); + n->rgt = nonxt(n->rgt); + if (!n->lft || !n->rgt) + return True; + return n; + case AND: + n->lft = nonxt(n->lft); + n->rgt = nonxt(n->rgt); + if (!n->lft) + { if (!n->rgt) + n = ZN; + else + n = n->rgt; + } else if (!n->rgt) + n = n->lft; + return n; + } + return n; +} +#endif + +static Node * +combination(Node *s, Node *t) +{ Node *nc; +#ifdef NXT + Node *a = nonxt(s); + Node *b = nonxt(t); + + if (tl_verbose) + { printf("\tnonxtA: "); dump(a); + printf("\n\tnonxtB: "); dump(b); + printf("\n"); + } + /* if there's only a X(f), its equivalent to true */ + if (!a || !b) + nc = True; + else + nc = tl_nn(OR, a, b); +#else + nc = tl_nn(OR, s, t); +#endif + if (tl_verbose) + { printf("\tcombo: "); dump(nc); + printf("\n"); + } + return nc; +} + +Node * +unclutter(Node *n, char *snm) +{ Node *t, *s, *v, *u; + Symbol *w; + + /* check only simple cases like !q && q */ + for (t = n; t; t = t->rgt) + { if (t->rgt) + { if (t->ntyp != AND || !t->lft) + return n; + if (t->lft->ntyp != PREDICATE +#ifdef NXT + && t->lft->ntyp != NEXT +#endif + && t->lft->ntyp != NOT) + return n; + } else + { if (t->ntyp != PREDICATE +#ifdef NXT + && t->ntyp != NEXT +#endif + && t->ntyp != NOT) + return n; + } + } + + for (t = n; t; t = t->rgt) + { if (t->rgt) + v = t->lft; + else + v = t; + if (v->ntyp == NOT + && v->lft->ntyp == PREDICATE) + { w = v->lft->sym; + for (s = n; s; s = s->rgt) + { if (s == t) continue; + if (s->rgt) + u = s->lft; + else + u = s; + if (u->ntyp == PREDICATE + && strcmp(u->sym->name, w->name) == 0) + { if (tl_verbose) + { printf("BINGO %s:\t", snm); + dump(n); + printf("\n"); + } + return False; + } + } + } } + return n; +} + +static void +clutter(void) +{ State *p; + Transition *s; + + for (p = never; p; p = p->nxt) + for (s = p->trans; s; s = s->nxt) + { s->cond = unclutter(s->cond, p->name->name); + if (s->cond + && s->cond->ntyp == FALSE) + { if (s != p->trans + || s->nxt) + s->redundant = 1; + } + } +} + +static void +showtrans(State *a) +{ Transition *s; + + for (s = a->trans; s; s = s->nxt) + { printf("%s ", s->name?s->name->name:"-"); + dump(s->cond); + printf(" %d %d %d\n", s->redundant, s->merged, s->marked); + } +} + +static int +mergetrans(void) +{ State *b; + Transition *s, *t; + Node *nc; int cnt = 0; + + for (b = never; b; b = b->nxt) + { if (!b->reachable) continue; + + for (s = b->trans; s; s = s->nxt) + { if (s->redundant) continue; + + for (t = s->nxt; t; t = t->nxt) + if (!t->redundant + && !strcmp(s->name->name, t->name->name)) + { if (tl_verbose) + { printf("===\nstate %s, trans to %s redundant\n", + b->name->name, s->name->name); + showtrans(b); + printf(" conditions "); + dump(s->cond); printf(" <-> "); + dump(t->cond); printf("\n"); + } + + if (!s->cond) /* same as T */ + { releasenode(1, t->cond); /* T or t */ + nc = True; + } else if (!t->cond) + { releasenode(1, s->cond); + nc = True; + } else + { nc = combination(s->cond, t->cond); + } + t->cond = rewrite(nc); + t->merged = 1; + s->redundant = 1; + cnt++; + break; + } } } + return cnt; +} + +static int +all_trans_match(State *a, State *b) +{ Transition *s, *t; + int found, result = 0; + + if (a->accepting != b->accepting) + goto done; + + for (s = a->trans; s; s = s->nxt) + { if (s->redundant) continue; + found = 0; + for (t = b->trans; t; t = t->nxt) + { if (t->redundant) continue; + if (sametrans(s, t)) + { found = 1; + t->marked = 1; + break; + } } + if (!found) + goto done; + } + for (s = b->trans; s; s = s->nxt) + { if (s->redundant || s->marked) continue; + found = 0; + for (t = a->trans; t; t = t->nxt) + { if (t->redundant) continue; + if (sametrans(s, t)) + { found = 1; + break; + } } + if (!found) + goto done; + } + result = 1; +done: + for (s = b->trans; s; s = s->nxt) + s->marked = 0; + return result; +} + +#ifndef NO_OPT +#define BUCKY +#endif + +#ifdef BUCKY +static int +all_bucky(State *a, State *b) +{ Transition *s, *t; + int found, result = 0; + + for (s = a->trans; s; s = s->nxt) + { if (s->redundant) continue; + found = 0; + for (t = b->trans; t; t = t->nxt) + { if (t->redundant) continue; + + if (isequal(s->cond, t->cond)) + { if (strcmp(s->name->name, b->name->name) == 0 + && strcmp(t->name->name, a->name->name) == 0) + { found = 1; /* they point to each other */ + t->marked = 1; + break; + } + if (strcmp(s->name->name, t->name->name) == 0 + && strcmp(s->name->name, "accept_all") == 0) + { found = 1; + t->marked = 1; + break; + /* same exit from which there is no return */ + } + } + } + if (!found) + goto done; + } + for (s = b->trans; s; s = s->nxt) + { if (s->redundant || s->marked) continue; + found = 0; + for (t = a->trans; t; t = t->nxt) + { if (t->redundant) continue; + + if (isequal(s->cond, t->cond)) + { if (strcmp(s->name->name, a->name->name) == 0 + && strcmp(t->name->name, b->name->name) == 0) + { found = 1; + t->marked = 1; + break; + } + if (strcmp(s->name->name, t->name->name) == 0 + && strcmp(s->name->name, "accept_all") == 0) + { found = 1; + t->marked = 1; + break; + } + } + } + if (!found) + goto done; + } + result = 1; +done: + for (s = b->trans; s; s = s->nxt) + s->marked = 0; + return result; +} + +static int +buckyballs(void) +{ State *a, *b, *c, *d; + int m, cnt=0; + + do { + m = 0; cnt++; + for (a = never; a; a = a->nxt) + { if (!a->reachable) continue; + + if (a->redundant) continue; + + for (b = a->nxt; b; b = b->nxt) + { if (!b->reachable) continue; + + if (b->redundant) continue; + + if (all_bucky(a, b)) + { m++; + if (tl_verbose) + { printf("%s bucky match %s\n", + a->name->name, b->name->name); + } + + if (a->accepting && !b->accepting) + { if (strcmp(b->name->name, "T0_init") == 0) + { c = a; d = b; + b->accepting = 1; + } else + { c = b; d = a; + } + } else + { c = a; d = b; + } + + retarget(c->name->name, d->name->name); + if (!strncmp(c->name->name, "accept", 6) + && Max_Red == 0) + { char buf[64]; + sprintf(buf, "T0%s", &(c->name->name[6])); + retarget(buf, d->name->name); + } + break; + } + } } + } while (m && cnt < 10); + return cnt-1; +} +#endif + +static int +mergestates(int v) +{ State *a, *b; + int m, cnt=0; + + if (tl_verbose) + return 0; + + do { + m = 0; cnt++; + for (a = never; a; a = a->nxt) + { if (v && !a->reachable) continue; + + if (a->redundant) continue; /* 3.3.10 */ + + for (b = a->nxt; b; b = b->nxt) + { if (v && !b->reachable) continue; + + if (b->redundant) continue; /* 3.3.10 */ + + if (all_trans_match(a, b)) + { m++; + if (tl_verbose) + { printf("%d: state %s equals state %s\n", + cnt, a->name->name, b->name->name); + showtrans(a); + printf("==\n"); + showtrans(b); + } + retarget(a->name->name, b->name->name); + if (!strncmp(a->name->name, "accept", 6) + && Max_Red == 0) + { char buf[64]; + sprintf(buf, "T0%s", &(a->name->name[6])); + retarget(buf, b->name->name); + } + break; + } +#if 0 + else if (tl_verbose) + { printf("\n%d: state %s differs from state %s [%d,%d]\n", + cnt, a->name->name, b->name->name, + a->accepting, b->accepting); + showtrans(a); + printf("==\n"); + showtrans(b); + printf("\n"); + } +#endif + } } + } while (m && cnt < 10); + return cnt-1; +} + +static int tcnt; + +static void +rev_trans(Transition *t) /* print transitions in reverse order... */ +{ + if (!t) return; + rev_trans(t->nxt); + + if (t->redundant && !tl_verbose) return; + fprintf(tl_out, "\t:: ("); + if (dump_cond(t->cond, t->cond, 1)) + fprintf(tl_out, "1"); + fprintf(tl_out, ") -> goto %s\n", t->name->name); + tcnt++; +} + +static void +printstate(State *b) +{ + if (!b || (!tl_verbose && !b->reachable)) return; + + b->reachable = 0; /* print only once */ + fprintf(tl_out, "%s:\n", b->name->name); + + if (tl_verbose) + { fprintf(tl_out, " /* "); + dump(b->colors->Other); + fprintf(tl_out, " */\n"); + } + + if (strncmp(b->name->name, "accept", 6) == 0 + && Max_Red == 0) + fprintf(tl_out, "T0%s:\n", &(b->name->name[6])); + + fprintf(tl_out, "\tif\n"); + tcnt = 0; + rev_trans(b->trans); + if (!tcnt) fprintf(tl_out, "\t:: false\n"); + fprintf(tl_out, "\tfi;\n"); + Total++; +} + +void +addtrans(Graph *col, char *from, Node *op, char *to) +{ State *b; + Transition *t; + + t = (Transition *) tl_emalloc(sizeof(Transition)); + t->name = tl_lookup(to); + t->cond = Prune(dupnode(op)); + + if (tl_verbose) + { printf("\n%s <<\t", from); dump(op); + printf("\n\t"); dump(t->cond); + printf(">> %s\n", t->name->name); + } + if (t->cond) t->cond = rewrite(t->cond); + + for (b = never; b; b = b->nxt) + if (!strcmp(b->name->name, from)) + { t->nxt = b->trans; + b->trans = t; + return; + } + b = (State *) tl_emalloc(sizeof(State)); + b->name = tl_lookup(from); + b->colors = col; + b->trans = t; + if (!strncmp(from, "accept", 6)) + b->accepting = 1; + b->nxt = never; + never = b; +} + +static void +clr_reach(void) +{ State *p; + for (p = never; p; p = p->nxt) + p->reachable = 0; + hitsall = 0; +} + +void +fsm_print(void) +{ State *b; int cnt1, cnt2=0; + extern void put_uform(void); + + if (tl_clutter) clutter(); + + b = findstate("T0_init"); + if (Max_Red == 0) + b->accepting = 1; + + mergestates(0); + b = findstate("T0_init"); + + fprintf(tl_out, "never { /* "); + put_uform(); + fprintf(tl_out, " */\n"); + + do { + clr_reach(); + Dfs(b); + cnt1 = mergetrans(); + cnt2 = mergestates(1); + if (tl_verbose) + printf("/* >>%d,%d<< */\n", cnt1, cnt2); + } while (cnt2 > 0); + +#ifdef BUCKY + buckyballs(); + clr_reach(); + Dfs(b); +#endif + if (b && b->accepting) + fprintf(tl_out, "accept_init:\n"); + + if (!b && !never) + { fprintf(tl_out, " 0 /* false */;\n"); + } else + { printstate(b); /* init state must be first */ + for (b = never; b; b = b->nxt) + printstate(b); + } + if (hitsall) + fprintf(tl_out, "accept_all:\n skip\n"); + fprintf(tl_out, "}\n"); +} diff --git a/sys/src/cmd/spin/tl_cache.c b/sys/src/cmd/spin/tl_cache.c new file mode 100755 index 000000000..fc902dcb0 --- /dev/null +++ b/sys/src/cmd/spin/tl_cache.c @@ -0,0 +1,328 @@ +/***** tl_spin: tl_cache.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +typedef struct Cache { + Node *before; + Node *after; + int same; + struct Cache *nxt; +} Cache; + +static Cache *stored = (Cache *) 0; +static unsigned long Caches, CacheHits; + +static int ismatch(Node *, Node *); +extern void fatal(char *, char *); +int sameform(Node *, Node *); + +#if 0 +void +cache_dump(void) +{ Cache *d; int nr=0; + + printf("\nCACHE DUMP:\n"); + for (d = stored; d; d = d->nxt, nr++) + { if (d->same) continue; + printf("B%3d: ", nr); dump(d->before); printf("\n"); + printf("A%3d: ", nr); dump(d->after); printf("\n"); + } + printf("============\n"); +} +#endif + +Node * +in_cache(Node *n) +{ Cache *d; int nr=0; + + for (d = stored; d; d = d->nxt, nr++) + if (isequal(d->before, n)) + { CacheHits++; + if (d->same && ismatch(n, d->before)) return n; + return dupnode(d->after); + } + return ZN; +} + +Node * +cached(Node *n) +{ Cache *d; + Node *m; + + if (!n) return n; + if ((m = in_cache(n)) != ZN) + return m; + + Caches++; + d = (Cache *) tl_emalloc(sizeof(Cache)); + d->before = dupnode(n); + d->after = Canonical(n); /* n is released */ + + if (ismatch(d->before, d->after)) + { d->same = 1; + releasenode(1, d->after); + d->after = d->before; + } + d->nxt = stored; + stored = d; + return dupnode(d->after); +} + +void +cache_stats(void) +{ + printf("cache stores : %9ld\n", Caches); + printf("cache hits : %9ld\n", CacheHits); +} + +void +releasenode(int all_levels, Node *n) +{ + if (!n) return; + + if (all_levels) + { releasenode(1, n->lft); + n->lft = ZN; + releasenode(1, n->rgt); + n->rgt = ZN; + } + tfree((void *) n); +} + +Node * +tl_nn(int t, Node *ll, Node *rl) +{ Node *n = (Node *) tl_emalloc(sizeof(Node)); + + n->ntyp = (short) t; + n->lft = ll; + n->rgt = rl; + + return n; +} + +Node * +getnode(Node *p) +{ Node *n; + + if (!p) return p; + + n = (Node *) tl_emalloc(sizeof(Node)); + n->ntyp = p->ntyp; + n->sym = p->sym; /* same name */ + n->lft = p->lft; + n->rgt = p->rgt; + + return n; +} + +Node * +dupnode(Node *n) +{ Node *d; + + if (!n) return n; + d = getnode(n); + d->lft = dupnode(n->lft); + d->rgt = dupnode(n->rgt); + return d; +} + +int +one_lft(int ntyp, Node *x, Node *in) +{ + if (!x) return 1; + if (!in) return 0; + + if (sameform(x, in)) + return 1; + + if (in->ntyp != ntyp) + return 0; + + if (one_lft(ntyp, x, in->lft)) + return 1; + + return one_lft(ntyp, x, in->rgt); +} + +int +all_lfts(int ntyp, Node *from, Node *in) +{ + if (!from) return 1; + + if (from->ntyp != ntyp) + return one_lft(ntyp, from, in); + + if (!one_lft(ntyp, from->lft, in)) + return 0; + + return all_lfts(ntyp, from->rgt, in); +} + +int +sametrees(int ntyp, Node *a, Node *b) +{ /* toplevel is an AND or OR */ + /* both trees are right-linked, but the leafs */ + /* can be in different places in the two trees */ + + if (!all_lfts(ntyp, a, b)) + return 0; + + return all_lfts(ntyp, b, a); +} + +int /* a better isequal() */ +sameform(Node *a, Node *b) +{ + if (!a && !b) return 1; + if (!a || !b) return 0; + if (a->ntyp != b->ntyp) return 0; + + if (a->sym + && b->sym + && strcmp(a->sym->name, b->sym->name) != 0) + return 0; + + switch (a->ntyp) { + case TRUE: + case FALSE: + return 1; + case PREDICATE: + if (!a->sym || !b->sym) fatal("sameform...", (char *) 0); + return !strcmp(a->sym->name, b->sym->name); + + case NOT: +#ifdef NXT + case NEXT: +#endif + return sameform(a->lft, b->lft); + case U_OPER: + case V_OPER: + if (!sameform(a->lft, b->lft)) + return 0; + if (!sameform(a->rgt, b->rgt)) + return 0; + return 1; + + case AND: + case OR: /* the hard case */ + return sametrees(a->ntyp, a, b); + + default: + printf("type: %d\n", a->ntyp); + fatal("cannot happen, sameform", (char *) 0); + } + + return 0; +} + +int +isequal(Node *a, Node *b) +{ + if (!a && !b) + return 1; + + if (!a || !b) + { if (!a) + { if (b->ntyp == TRUE) + return 1; + } else + { if (a->ntyp == TRUE) + return 1; + } + return 0; + } + if (a->ntyp != b->ntyp) + return 0; + + if (a->sym + && b->sym + && strcmp(a->sym->name, b->sym->name) != 0) + return 0; + + if (isequal(a->lft, b->lft) + && isequal(a->rgt, b->rgt)) + return 1; + + return sameform(a, b); +} + +static int +ismatch(Node *a, Node *b) +{ + if (!a && !b) return 1; + if (!a || !b) return 0; + if (a->ntyp != b->ntyp) return 0; + + if (a->sym + && b->sym + && strcmp(a->sym->name, b->sym->name) != 0) + return 0; + + if (ismatch(a->lft, b->lft) + && ismatch(a->rgt, b->rgt)) + return 1; + + return 0; +} + +int +any_term(Node *srch, Node *in) +{ + if (!in) return 0; + + if (in->ntyp == AND) + return any_term(srch, in->lft) || + any_term(srch, in->rgt); + + return isequal(in, srch); +} + +int +any_and(Node *srch, Node *in) +{ + if (!in) return 0; + + if (srch->ntyp == AND) + return any_and(srch->lft, in) && + any_and(srch->rgt, in); + + return any_term(srch, in); +} + +int +any_lor(Node *srch, Node *in) +{ + if (!in) return 0; + + if (in->ntyp == OR) + return any_lor(srch, in->lft) || + any_lor(srch, in->rgt); + + return isequal(in, srch); +} + +int +anywhere(int tok, Node *srch, Node *in) +{ + if (!in) return 0; + + switch (tok) { + case AND: return any_and(srch, in); + case OR: return any_lor(srch, in); + case 0: return any_term(srch, in); + } + fatal("cannot happen, anywhere", (char *) 0); + return 0; +} diff --git a/sys/src/cmd/spin/tl_lex.c b/sys/src/cmd/spin/tl_lex.c new file mode 100755 index 000000000..110e06eae --- /dev/null +++ b/sys/src/cmd/spin/tl_lex.c @@ -0,0 +1,148 @@ +/***** tl_spin: tl_lex.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include <stdlib.h> +#include <ctype.h> +#include "tl.h" + +static Symbol *symtab[Nhash+1]; +static int tl_lex(void); + +extern YYSTYPE tl_yylval; +extern char yytext[]; + +#define Token(y) tl_yylval = tl_nn(y,ZN,ZN); return y + +static void +tl_getword(int first, int (*tst)(int)) +{ int i=0; char c; + + yytext[i++] = (char ) first; + while (tst(c = tl_Getchar())) + yytext[i++] = c; + yytext[i] = '\0'; + tl_UnGetchar(); +} + +static int +tl_follow(int tok, int ifyes, int ifno) +{ int c; + char buf[32]; + extern int tl_yychar; + + if ((c = tl_Getchar()) == tok) + return ifyes; + tl_UnGetchar(); + tl_yychar = c; + sprintf(buf, "expected '%c'", tok); + tl_yyerror(buf); /* no return from here */ + return ifno; +} + +int +tl_yylex(void) +{ int c = tl_lex(); +#if 0 + printf("c = %d\n", c); +#endif + return c; +} + +static int +tl_lex(void) +{ int c; + + do { + c = tl_Getchar(); + yytext[0] = (char ) c; + yytext[1] = '\0'; + + if (c <= 0) + { Token(';'); + } + + } while (c == ' '); /* '\t' is removed in tl_main.c */ + + if (islower(c)) + { tl_getword(c, isalnum_); + if (strcmp("true", yytext) == 0) + { Token(TRUE); + } + if (strcmp("false", yytext) == 0) + { Token(FALSE); + } + tl_yylval = tl_nn(PREDICATE,ZN,ZN); + tl_yylval->sym = tl_lookup(yytext); + return PREDICATE; + } + if (c == '<') + { c = tl_Getchar(); + if (c == '>') + { Token(EVENTUALLY); + } + if (c != '-') + { tl_UnGetchar(); + tl_yyerror("expected '<>' or '<->'"); + } + c = tl_Getchar(); + if (c == '>') + { Token(EQUIV); + } + tl_UnGetchar(); + tl_yyerror("expected '<->'"); + } + + switch (c) { + case '/' : c = tl_follow('\\', AND, '/'); break; + case '\\': c = tl_follow('/', OR, '\\'); break; + case '&' : c = tl_follow('&', AND, '&'); break; + case '|' : c = tl_follow('|', OR, '|'); break; + case '[' : c = tl_follow(']', ALWAYS, '['); break; + case '-' : c = tl_follow('>', IMPLIES, '-'); break; + case '!' : c = NOT; break; + case 'U' : c = U_OPER; break; + case 'V' : c = V_OPER; break; +#ifdef NXT + case 'X' : c = NEXT; break; +#endif + default : break; + } + Token(c); +} + +Symbol * +tl_lookup(char *s) +{ Symbol *sp; + int h = hash(s); + + for (sp = symtab[h]; sp; sp = sp->next) + if (strcmp(sp->name, s) == 0) + return sp; + + sp = (Symbol *) tl_emalloc(sizeof(Symbol)); + sp->name = (char *) tl_emalloc((int) strlen(s) + 1); + strcpy(sp->name, s); + sp->next = symtab[h]; + symtab[h] = sp; + + return sp; +} + +Symbol * +getsym(Symbol *s) +{ Symbol *n = (Symbol *) tl_emalloc(sizeof(Symbol)); + + n->name = s->name; + return n; +} diff --git a/sys/src/cmd/spin/tl_main.c b/sys/src/cmd/spin/tl_main.c new file mode 100755 index 000000000..10ab0e9bd --- /dev/null +++ b/sys/src/cmd/spin/tl_main.c @@ -0,0 +1,234 @@ +/***** tl_spin: tl_main.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +extern FILE *tl_out; + +int newstates = 0; /* debugging only */ +int tl_errs = 0; +int tl_verbose = 0; +int tl_terse = 0; +int tl_clutter = 0; +unsigned long All_Mem = 0; + +static char uform[4096]; +static int hasuform=0, cnt=0; + +extern void cache_stats(void); +extern void a_stats(void); + +int +tl_Getchar(void) +{ + if (cnt < hasuform) + return uform[cnt++]; + cnt++; + return -1; +} + +void +tl_balanced(void) +{ int i; + int k = 0; + + for (i = 0; i < hasuform; i++) + { if (uform[i] == '(') + { k++; + } else if (uform[i] == ')') + { k--; + } } + if (k != 0) + { tl_errs++; + tl_yyerror("parentheses not balanced"); + } +} + +void +put_uform(void) +{ + fprintf(tl_out, "%s", uform); +} + +void +tl_UnGetchar(void) +{ + if (cnt > 0) cnt--; +} + +static void +tl_stats(void) +{ extern int Stack_mx; + printf("total memory used: %9ld\n", All_Mem); + printf("largest stack sze: %9d\n", Stack_mx); + cache_stats(); + a_stats(); +} + +int +tl_main(int argc, char *argv[]) +{ int i; + extern int verbose, xspin; + tl_verbose = verbose; + tl_clutter = 1-xspin; /* use -X -f to turn off uncluttering */ + + while (argc > 1 && argv[1][0] == '-') + { switch (argv[1][1]) { + case 'd': newstates = 1; /* debugging mode */ + break; + case 'f': argc--; argv++; + for (i = 0; i < argv[1][i]; i++) + { if (argv[1][i] == '\t' + || argv[1][i] == '\"' + || argv[1][i] == '\n') + argv[1][i] = ' '; + } + strcpy(uform, argv[1]); + hasuform = (int) strlen(uform); + break; + case 'v': tl_verbose++; + break; + case 'n': tl_terse = 1; + break; + default : printf("spin -f: saw '-%c'\n", argv[1][1]); + goto nogood; + } + argc--; argv++; + } + if (hasuform == 0) + { +nogood: printf("usage:\tspin [-v] [-n] -f formula\n"); + printf(" -v verbose translation\n"); + printf(" -n normalize tl formula and exit\n"); + exit(1); + } + tl_balanced(); + + if (tl_errs == 0) + tl_parse(); + + if (tl_verbose) tl_stats(); + return tl_errs; +} + +#define Binop(a) \ + fprintf(tl_out, "("); \ + dump(n->lft); \ + fprintf(tl_out, a); \ + dump(n->rgt); \ + fprintf(tl_out, ")") + +void +dump(Node *n) +{ + if (!n) return; + + switch(n->ntyp) { + case OR: Binop(" || "); break; + case AND: Binop(" && "); break; + case U_OPER: Binop(" U "); break; + case V_OPER: Binop(" V "); break; +#ifdef NXT + case NEXT: + fprintf(tl_out, "X"); + fprintf(tl_out, " ("); + dump(n->lft); + fprintf(tl_out, ")"); + break; +#endif + case NOT: + fprintf(tl_out, "!"); + fprintf(tl_out, " ("); + dump(n->lft); + fprintf(tl_out, ")"); + break; + case FALSE: + fprintf(tl_out, "false"); + break; + case TRUE: + fprintf(tl_out, "true"); + break; + case PREDICATE: + fprintf(tl_out, "(%s)", n->sym->name); + break; + case -1: + fprintf(tl_out, " D "); + break; + default: + printf("Unknown token: "); + tl_explain(n->ntyp); + break; + } +} + +void +tl_explain(int n) +{ + switch (n) { + case ALWAYS: printf("[]"); break; + case EVENTUALLY: printf("<>"); break; + case IMPLIES: printf("->"); break; + case EQUIV: printf("<->"); break; + case PREDICATE: printf("predicate"); break; + case OR: printf("||"); break; + case AND: printf("&&"); break; + case NOT: printf("!"); break; + case U_OPER: printf("U"); break; + case V_OPER: printf("V"); break; +#ifdef NXT + case NEXT: printf("X"); break; +#endif + case TRUE: printf("true"); break; + case FALSE: printf("false"); break; + case ';': printf("end of formula"); break; + default: printf("%c", n); break; + } +} + +static void +tl_non_fatal(char *s1, char *s2) +{ extern int tl_yychar; + int i; + + printf("tl_spin: "); + if (s2) + printf(s1, s2); + else + printf(s1); + if (tl_yychar != -1 && tl_yychar != 0) + { printf(", saw '"); + tl_explain(tl_yychar); + printf("'"); + } + printf("\ntl_spin: %s\n---------", uform); + for (i = 0; i < cnt; i++) + printf("-"); + printf("^\n"); + fflush(stdout); + tl_errs++; +} + +void +tl_yyerror(char *s1) +{ + Fatal(s1, (char *) 0); +} + +void +Fatal(char *s1, char *s2) +{ + tl_non_fatal(s1, s2); + /* tl_stats(); */ + exit(1); +} diff --git a/sys/src/cmd/spin/tl_mem.c b/sys/src/cmd/spin/tl_mem.c new file mode 100755 index 000000000..52021e466 --- /dev/null +++ b/sys/src/cmd/spin/tl_mem.c @@ -0,0 +1,120 @@ +/***** tl_spin: tl_mem.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +#if 1 +#define log(e, u, d) event[e][(int) u] += (long) d; +#else +#define log(e, u, d) +#endif + +#define A_LARGE 80 +#define A_USER 0x55000000 +#define NOTOOBIG 32768 + +#define POOL 0 +#define ALLOC 1 +#define FREE 2 +#define NREVENT 3 + +extern unsigned long All_Mem; +extern int tl_verbose; + +union M { + long size; + union M *link; +}; + +static union M *freelist[A_LARGE]; +static long req[A_LARGE]; +static long event[NREVENT][A_LARGE]; + +void * +tl_emalloc(int U) +{ union M *m; + long r, u; + void *rp; + + u = (long) ((U-1)/sizeof(union M) + 2); + + if (u >= A_LARGE) + { log(ALLOC, 0, 1); + if (tl_verbose) + printf("tl_spin: memalloc %ld bytes\n", u); + m = (union M *) emalloc((int) u*sizeof(union M)); + All_Mem += (unsigned long) u*sizeof(union M); + } else + { if (!freelist[u]) + { r = req[u] += req[u] ? req[u] : 1; + if (r >= NOTOOBIG) + r = req[u] = NOTOOBIG; + log(POOL, u, r); + freelist[u] = (union M *) + emalloc((int) r*u*sizeof(union M)); + All_Mem += (unsigned long) r*u*sizeof(union M); + m = freelist[u] + (r-2)*u; + for ( ; m >= freelist[u]; m -= u) + m->link = m+u; + } + log(ALLOC, u, 1); + m = freelist[u]; + freelist[u] = m->link; + } + m->size = (u|A_USER); + + for (r = 1; r < u; ) + (&m->size)[r++] = 0; + + rp = (void *) (m+1); + memset(rp, 0, U); + return rp; +} + +void +tfree(void *v) +{ union M *m = (union M *) v; + long u; + + --m; + if ((m->size&0xFF000000) != A_USER) + Fatal("releasing a free block", (char *)0); + + u = (m->size &= 0xFFFFFF); + if (u >= A_LARGE) + { log(FREE, 0, 1); + /* free(m); */ + } else + { log(FREE, u, 1); + m->link = freelist[u]; + freelist[u] = m; + } +} + +void +a_stats(void) +{ long p, a, f; + int i; + + printf(" size\t pool\tallocs\t frees\n"); + for (i = 0; i < A_LARGE; i++) + { p = event[POOL][i]; + a = event[ALLOC][i]; + f = event[FREE][i]; + + if(p|a|f) + printf("%5d\t%6ld\t%6ld\t%6ld\n", + i, p, a, f); + } +} diff --git a/sys/src/cmd/spin/tl_parse.c b/sys/src/cmd/spin/tl_parse.c new file mode 100755 index 000000000..6206a0d99 --- /dev/null +++ b/sys/src/cmd/spin/tl_parse.c @@ -0,0 +1,400 @@ +/***** tl_spin: tl_parse.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +extern int tl_yylex(void); +extern int tl_verbose, tl_errs; + +int tl_yychar = 0; +YYSTYPE tl_yylval; + +static Node *tl_formula(void); +static Node *tl_factor(void); +static Node *tl_level(int); + +static int prec[2][4] = { + { U_OPER, V_OPER, 0, 0 }, /* left associative */ + { OR, AND, IMPLIES, EQUIV, }, /* left associative */ +}; + +static Node * +tl_factor(void) +{ Node *ptr = ZN; + + switch (tl_yychar) { + case '(': + ptr = tl_formula(); + if (tl_yychar != ')') + tl_yyerror("expected ')'"); + tl_yychar = tl_yylex(); + break; + case NOT: + ptr = tl_yylval; + tl_yychar = tl_yylex(); + ptr->lft = tl_factor(); + ptr = push_negation(ptr); + break; + case ALWAYS: + tl_yychar = tl_yylex(); + + ptr = tl_factor(); +#ifndef NO_OPT + if (ptr->ntyp == FALSE + || ptr->ntyp == TRUE) + break; /* [] false == false */ + + if (ptr->ntyp == V_OPER) + { if (ptr->lft->ntyp == FALSE) + break; /* [][]p = []p */ + + ptr = ptr->rgt; /* [] (p V q) = [] q */ + } +#endif + ptr = tl_nn(V_OPER, False, ptr); + break; +#ifdef NXT + case NEXT: + tl_yychar = tl_yylex(); + ptr = tl_factor(); + if (ptr->ntyp == TRUE) + break; /* X true = true */ + ptr = tl_nn(NEXT, ptr, ZN); + break; +#endif + case EVENTUALLY: + tl_yychar = tl_yylex(); + + ptr = tl_factor(); +#ifndef NO_OPT + if (ptr->ntyp == TRUE + || ptr->ntyp == FALSE) + break; /* <> true == true */ + + if (ptr->ntyp == U_OPER + && ptr->lft->ntyp == TRUE) + break; /* <><>p = <>p */ + + if (ptr->ntyp == U_OPER) + { /* <> (p U q) = <> q */ + ptr = ptr->rgt; + /* fall thru */ + } +#endif + ptr = tl_nn(U_OPER, True, ptr); + + break; + case PREDICATE: + ptr = tl_yylval; + tl_yychar = tl_yylex(); + break; + case TRUE: + case FALSE: + ptr = tl_yylval; + tl_yychar = tl_yylex(); + break; + } + if (!ptr) tl_yyerror("expected predicate"); +#if 0 + printf("factor: "); + tl_explain(ptr->ntyp); + printf("\n"); +#endif + return ptr; +} + +static Node * +bin_simpler(Node *ptr) +{ Node *a, *b; + + if (ptr) + switch (ptr->ntyp) { + case U_OPER: +#ifndef NO_OPT + if (ptr->rgt->ntyp == TRUE + || ptr->rgt->ntyp == FALSE + || ptr->lft->ntyp == FALSE) + { ptr = ptr->rgt; + break; + } + if (isequal(ptr->lft, ptr->rgt)) + { /* p U p = p */ + ptr = ptr->rgt; + break; + } + if (ptr->lft->ntyp == U_OPER + && isequal(ptr->lft->lft, ptr->rgt)) + { /* (p U q) U p = (q U p) */ + ptr->lft = ptr->lft->rgt; + break; + } + if (ptr->rgt->ntyp == U_OPER + && ptr->rgt->lft->ntyp == TRUE) + { /* p U (T U q) = (T U q) */ + ptr = ptr->rgt; + break; + } +#ifdef NXT + /* X p U X q == X (p U q) */ + if (ptr->rgt->ntyp == NEXT + && ptr->lft->ntyp == NEXT) + { ptr = tl_nn(NEXT, + tl_nn(U_OPER, + ptr->lft->lft, + ptr->rgt->lft), ZN); + } +#endif +#endif + break; + case V_OPER: +#ifndef NO_OPT + if (ptr->rgt->ntyp == FALSE + || ptr->rgt->ntyp == TRUE + || ptr->lft->ntyp == TRUE) + { ptr = ptr->rgt; + break; + } + if (isequal(ptr->lft, ptr->rgt)) + { /* p V p = p */ + ptr = ptr->rgt; + break; + } + /* F V (p V q) == F V q */ + if (ptr->lft->ntyp == FALSE + && ptr->rgt->ntyp == V_OPER) + { ptr->rgt = ptr->rgt->rgt; + break; + } + /* p V (F V q) == F V q */ + if (ptr->rgt->ntyp == V_OPER + && ptr->rgt->lft->ntyp == FALSE) + { ptr->lft = False; + ptr->rgt = ptr->rgt->rgt; + break; + } +#endif + break; + case IMPLIES: +#ifndef NO_OPT + if (isequal(ptr->lft, ptr->rgt)) + { ptr = True; + break; + } +#endif + ptr = tl_nn(OR, Not(ptr->lft), ptr->rgt); + ptr = rewrite(ptr); + break; + case EQUIV: +#ifndef NO_OPT + if (isequal(ptr->lft, ptr->rgt)) + { ptr = True; + break; + } +#endif + a = rewrite(tl_nn(AND, + dupnode(ptr->lft), + dupnode(ptr->rgt))); + b = rewrite(tl_nn(AND, + Not(ptr->lft), + Not(ptr->rgt))); + ptr = tl_nn(OR, a, b); + ptr = rewrite(ptr); + break; + case AND: +#ifndef NO_OPT + /* p && (q U p) = p */ + if (ptr->rgt->ntyp == U_OPER + && isequal(ptr->rgt->rgt, ptr->lft)) + { ptr = ptr->lft; + break; + } + if (ptr->lft->ntyp == U_OPER + && isequal(ptr->lft->rgt, ptr->rgt)) + { ptr = ptr->rgt; + break; + } + + /* p && (q V p) == q V p */ + if (ptr->rgt->ntyp == V_OPER + && isequal(ptr->rgt->rgt, ptr->lft)) + { ptr = ptr->rgt; + break; + } + if (ptr->lft->ntyp == V_OPER + && isequal(ptr->lft->rgt, ptr->rgt)) + { ptr = ptr->lft; + break; + } + + /* (p U q) && (r U q) = (p && r) U q*/ + if (ptr->rgt->ntyp == U_OPER + && ptr->lft->ntyp == U_OPER + && isequal(ptr->rgt->rgt, ptr->lft->rgt)) + { ptr = tl_nn(U_OPER, + tl_nn(AND, ptr->lft->lft, ptr->rgt->lft), + ptr->lft->rgt); + break; + } + + /* (p V q) && (p V r) = p V (q && r) */ + if (ptr->rgt->ntyp == V_OPER + && ptr->lft->ntyp == V_OPER + && isequal(ptr->rgt->lft, ptr->lft->lft)) + { ptr = tl_nn(V_OPER, + ptr->rgt->lft, + tl_nn(AND, ptr->lft->rgt, ptr->rgt->rgt)); + break; + } +#ifdef NXT + /* X p && X q == X (p && q) */ + if (ptr->rgt->ntyp == NEXT + && ptr->lft->ntyp == NEXT) + { ptr = tl_nn(NEXT, + tl_nn(AND, + ptr->rgt->lft, + ptr->lft->lft), ZN); + break; + } +#endif + + if (isequal(ptr->lft, ptr->rgt) /* (p && p) == p */ + || ptr->rgt->ntyp == FALSE /* (p && F) == F */ + || ptr->lft->ntyp == TRUE) /* (T && p) == p */ + { ptr = ptr->rgt; + break; + } + if (ptr->rgt->ntyp == TRUE /* (p && T) == p */ + || ptr->lft->ntyp == FALSE) /* (F && p) == F */ + { ptr = ptr->lft; + break; + } + + /* (p V q) && (r U q) == p V q */ + if (ptr->rgt->ntyp == U_OPER + && ptr->lft->ntyp == V_OPER + && isequal(ptr->lft->rgt, ptr->rgt->rgt)) + { ptr = ptr->lft; + break; + } +#endif + break; + + case OR: +#ifndef NO_OPT + /* p || (q U p) == q U p */ + if (ptr->rgt->ntyp == U_OPER + && isequal(ptr->rgt->rgt, ptr->lft)) + { ptr = ptr->rgt; + break; + } + + /* p || (q V p) == p */ + if (ptr->rgt->ntyp == V_OPER + && isequal(ptr->rgt->rgt, ptr->lft)) + { ptr = ptr->lft; + break; + } + + /* (p U q) || (p U r) = p U (q || r) */ + if (ptr->rgt->ntyp == U_OPER + && ptr->lft->ntyp == U_OPER + && isequal(ptr->rgt->lft, ptr->lft->lft)) + { ptr = tl_nn(U_OPER, + ptr->rgt->lft, + tl_nn(OR, ptr->lft->rgt, ptr->rgt->rgt)); + break; + } + + if (isequal(ptr->lft, ptr->rgt) /* (p || p) == p */ + || ptr->rgt->ntyp == FALSE /* (p || F) == p */ + || ptr->lft->ntyp == TRUE) /* (T || p) == T */ + { ptr = ptr->lft; + break; + } + if (ptr->rgt->ntyp == TRUE /* (p || T) == T */ + || ptr->lft->ntyp == FALSE) /* (F || p) == p */ + { ptr = ptr->rgt; + break; + } + + /* (p V q) || (r V q) = (p || r) V q */ + if (ptr->rgt->ntyp == V_OPER + && ptr->lft->ntyp == V_OPER + && isequal(ptr->lft->rgt, ptr->rgt->rgt)) + { ptr = tl_nn(V_OPER, + tl_nn(OR, ptr->lft->lft, ptr->rgt->lft), + ptr->rgt->rgt); + break; + } + + /* (p V q) || (r U q) == r U q */ + if (ptr->rgt->ntyp == U_OPER + && ptr->lft->ntyp == V_OPER + && isequal(ptr->lft->rgt, ptr->rgt->rgt)) + { ptr = ptr->rgt; + break; + } +#endif + break; + } + return ptr; +} + +static Node * +tl_level(int nr) +{ int i; Node *ptr = ZN; + + if (nr < 0) + return tl_factor(); + + ptr = tl_level(nr-1); +again: + for (i = 0; i < 4; i++) + if (tl_yychar == prec[nr][i]) + { tl_yychar = tl_yylex(); + ptr = tl_nn(prec[nr][i], + ptr, tl_level(nr-1)); + ptr = bin_simpler(ptr); + goto again; + } + if (!ptr) tl_yyerror("syntax error"); +#if 0 + printf("level %d: ", nr); + tl_explain(ptr->ntyp); + printf("\n"); +#endif + return ptr; +} + +static Node * +tl_formula(void) +{ tl_yychar = tl_yylex(); + return tl_level(1); /* 2 precedence levels, 1 and 0 */ +} + +void +tl_parse(void) +{ Node *n = tl_formula(); + if (tl_verbose) + { printf("formula: "); + dump(n); + printf("\n"); + } + if (tl_Getchar() != -1) + { tl_yyerror("syntax error"); + tl_errs++; + return; + } + trans(n); +} diff --git a/sys/src/cmd/spin/tl_rewrt.c b/sys/src/cmd/spin/tl_rewrt.c new file mode 100755 index 000000000..6e00d9ac0 --- /dev/null +++ b/sys/src/cmd/spin/tl_rewrt.c @@ -0,0 +1,301 @@ +/***** tl_spin: tl_rewrt.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +extern int tl_verbose; + +static Node *can = ZN; + +Node * +right_linked(Node *n) +{ + if (!n) return n; + + if (n->ntyp == AND || n->ntyp == OR) + while (n->lft && n->lft->ntyp == n->ntyp) + { Node *tmp = n->lft; + n->lft = tmp->rgt; + tmp->rgt = n; + n = tmp; + } + + n->lft = right_linked(n->lft); + n->rgt = right_linked(n->rgt); + + return n; +} + +Node * +canonical(Node *n) +{ Node *m; /* assumes input is right_linked */ + + if (!n) return n; + if ((m = in_cache(n)) != ZN) + return m; + + n->rgt = canonical(n->rgt); + n->lft = canonical(n->lft); + + return cached(n); +} + +Node * +push_negation(Node *n) +{ Node *m; + + Assert(n->ntyp == NOT, n->ntyp); + + switch (n->lft->ntyp) { + case TRUE: + Debug("!true => false\n"); + releasenode(0, n->lft); + n->lft = ZN; + n->ntyp = FALSE; + break; + case FALSE: + Debug("!false => true\n"); + releasenode(0, n->lft); + n->lft = ZN; + n->ntyp = TRUE; + break; + case NOT: + Debug("!!p => p\n"); + m = n->lft->lft; + releasenode(0, n->lft); + n->lft = ZN; + releasenode(0, n); + n = m; + break; + case V_OPER: + Debug("!(p V q) => (!p U !q)\n"); + n->ntyp = U_OPER; + goto same; + case U_OPER: + Debug("!(p U q) => (!p V !q)\n"); + n->ntyp = V_OPER; + goto same; +#ifdef NXT + case NEXT: + Debug("!X -> X!\n"); + n->ntyp = NEXT; + n->lft->ntyp = NOT; + n->lft = push_negation(n->lft); + break; +#endif + case AND: + Debug("!(p && q) => !p || !q\n"); + n->ntyp = OR; + goto same; + case OR: + Debug("!(p || q) => !p && !q\n"); + n->ntyp = AND; + +same: m = n->lft->rgt; + n->lft->rgt = ZN; + + n->rgt = Not(m); + n->lft->ntyp = NOT; + m = n->lft; + n->lft = push_negation(m); + break; + } + + return rewrite(n); +} + +static void +addcan(int tok, Node *n) +{ Node *m, *prev = ZN; + Node **ptr; + Node *N; + Symbol *s, *t; int cmp; + + if (!n) return; + + if (n->ntyp == tok) + { addcan(tok, n->rgt); + addcan(tok, n->lft); + return; + } + + N = dupnode(n); + if (!can) + { can = N; + return; + } + + s = DoDump(N); + if (can->ntyp != tok) /* only one element in list so far */ + { ptr = &can; + goto insert; + } + + /* there are at least 2 elements in list */ + prev = ZN; + for (m = can; m->ntyp == tok && m->rgt; prev = m, m = m->rgt) + { t = DoDump(m->lft); + cmp = strcmp(s->name, t->name); + if (cmp == 0) /* duplicate */ + return; + if (cmp < 0) + { if (!prev) + { can = tl_nn(tok, N, can); + return; + } else + { ptr = &(prev->rgt); + goto insert; + } } } + + /* new entry goes at the end of the list */ + ptr = &(prev->rgt); +insert: + t = DoDump(*ptr); + cmp = strcmp(s->name, t->name); + if (cmp == 0) /* duplicate */ + return; + if (cmp < 0) + *ptr = tl_nn(tok, N, *ptr); + else + *ptr = tl_nn(tok, *ptr, N); +} + +static void +marknode(int tok, Node *m) +{ + if (m->ntyp != tok) + { releasenode(0, m->rgt); + m->rgt = ZN; + } + m->ntyp = -1; +} + +Node * +Canonical(Node *n) +{ Node *m, *p, *k1, *k2, *prev, *dflt = ZN; + int tok; + + if (!n) return n; + + tok = n->ntyp; + if (tok != AND && tok != OR) + return n; + + can = ZN; + addcan(tok, n); +#if 0 + Debug("\nA0: "); Dump(can); + Debug("\nA1: "); Dump(n); Debug("\n"); +#endif + releasenode(1, n); + + /* mark redundant nodes */ + if (tok == AND) + { for (m = can; m; m = (m->ntyp == AND) ? m->rgt : ZN) + { k1 = (m->ntyp == AND) ? m->lft : m; + if (k1->ntyp == TRUE) + { marknode(AND, m); + dflt = True; + continue; + } + if (k1->ntyp == FALSE) + { releasenode(1, can); + can = False; + goto out; + } } + for (m = can; m; m = (m->ntyp == AND) ? m->rgt : ZN) + for (p = can; p; p = (p->ntyp == AND) ? p->rgt : ZN) + { if (p == m + || p->ntyp == -1 + || m->ntyp == -1) + continue; + k1 = (m->ntyp == AND) ? m->lft : m; + k2 = (p->ntyp == AND) ? p->lft : p; + + if (isequal(k1, k2)) + { marknode(AND, p); + continue; + } + if (anywhere(OR, k1, k2)) + { marknode(AND, p); + continue; + } + } } + if (tok == OR) + { for (m = can; m; m = (m->ntyp == OR) ? m->rgt : ZN) + { k1 = (m->ntyp == OR) ? m->lft : m; + if (k1->ntyp == FALSE) + { marknode(OR, m); + dflt = False; + continue; + } + if (k1->ntyp == TRUE) + { releasenode(1, can); + can = True; + goto out; + } } + for (m = can; m; m = (m->ntyp == OR) ? m->rgt : ZN) + for (p = can; p; p = (p->ntyp == OR) ? p->rgt : ZN) + { if (p == m + || p->ntyp == -1 + || m->ntyp == -1) + continue; + k1 = (m->ntyp == OR) ? m->lft : m; + k2 = (p->ntyp == OR) ? p->lft : p; + + if (isequal(k1, k2)) + { marknode(OR, p); + continue; + } + if (anywhere(AND, k1, k2)) + { marknode(OR, p); + continue; + } + } } + for (m = can, prev = ZN; m; ) /* remove marked nodes */ + { if (m->ntyp == -1) + { k2 = m->rgt; + releasenode(0, m); + if (!prev) + { m = can = can->rgt; + } else + { m = prev->rgt = k2; + /* if deleted the last node in a chain */ + if (!prev->rgt && prev->lft + && (prev->ntyp == AND || prev->ntyp == OR)) + { k1 = prev->lft; + prev->ntyp = prev->lft->ntyp; + prev->sym = prev->lft->sym; + prev->rgt = prev->lft->rgt; + prev->lft = prev->lft->lft; + releasenode(0, k1); + } + } + continue; + } + prev = m; + m = m->rgt; + } +out: +#if 0 + Debug("A2: "); Dump(can); Debug("\n"); +#endif + if (!can) + { if (!dflt) + fatal("cannot happen, Canonical", (char *) 0); + return dflt; + } + + return can; +} diff --git a/sys/src/cmd/spin/tl_trans.c b/sys/src/cmd/spin/tl_trans.c new file mode 100755 index 000000000..72964ca6b --- /dev/null +++ b/sys/src/cmd/spin/tl_trans.c @@ -0,0 +1,875 @@ +/***** tl_spin: tl_trans.c *****/ + +/* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +/* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ +/* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ + +#include "tl.h" + +extern FILE *tl_out; +extern int tl_errs, tl_verbose, tl_terse, newstates; + +int Stack_mx=0, Max_Red=0, Total=0; + +static Mapping *Mapped = (Mapping *) 0; +static Graph *Nodes_Set = (Graph *) 0; +static Graph *Nodes_Stack = (Graph *) 0; + +static char dumpbuf[2048]; +static int Red_cnt = 0; +static int Lab_cnt = 0; +static int Base = 0; +static int Stack_sz = 0; + +static Graph *findgraph(char *); +static Graph *pop_stack(void); +static Node *Duplicate(Node *); +static Node *flatten(Node *); +static Symbol *catSlist(Symbol *, Symbol *); +static Symbol *dupSlist(Symbol *); +static char *newname(void); +static int choueka(Graph *, int); +static int not_new(Graph *); +static int set_prefix(char *, int, Graph *); +static void Addout(char *, char *); +static void fsm_trans(Graph *, int, char *); +static void mkbuchi(void); +static void expand_g(Graph *); +static void fixinit(Node *); +static void liveness(Node *); +static void mk_grn(Node *); +static void mk_red(Node *); +static void ng(Symbol *, Symbol *, Node *, Node *, Node *); +static void push_stack(Graph *); +static void sdump(Node *); + +static void +dump_graph(Graph *g) +{ Node *n1; + + printf("\n\tnew:\t"); + for (n1 = g->New; n1; n1 = n1->nxt) + { dump(n1); printf(", "); } + printf("\n\told:\t"); + for (n1 = g->Old; n1; n1 = n1->nxt) + { dump(n1); printf(", "); } + printf("\n\tnxt:\t"); + for (n1 = g->Next; n1; n1 = n1->nxt) + { dump(n1); printf(", "); } + printf("\n\tother:\t"); + for (n1 = g->Other; n1; n1 = n1->nxt) + { dump(n1); printf(", "); } + printf("\n"); +} + +static void +push_stack(Graph *g) +{ + if (!g) return; + + g->nxt = Nodes_Stack; + Nodes_Stack = g; + if (tl_verbose) + { Symbol *z; + printf("\nPush %s, from ", g->name->name); + for (z = g->incoming; z; z = z->next) + printf("%s, ", z->name); + dump_graph(g); + } + Stack_sz++; + if (Stack_sz > Stack_mx) Stack_mx = Stack_sz; +} + +static Graph * +pop_stack(void) +{ Graph *g = Nodes_Stack; + + if (g) Nodes_Stack = g->nxt; + + Stack_sz--; + + return g; +} + +static char * +newname(void) +{ static int cnt = 0; + static char buf[32]; + sprintf(buf, "S%d", cnt++); + return buf; +} + +static int +has_clause(int tok, Graph *p, Node *n) +{ Node *q, *qq; + + switch (n->ntyp) { + case AND: + return has_clause(tok, p, n->lft) && + has_clause(tok, p, n->rgt); + case OR: + return has_clause(tok, p, n->lft) || + has_clause(tok, p, n->rgt); + } + + for (q = p->Other; q; q = q->nxt) + { qq = right_linked(q); + if (anywhere(tok, n, qq)) + return 1; + } + return 0; +} + +static void +mk_grn(Node *n) +{ Graph *p; + + n = right_linked(n); +more: + for (p = Nodes_Set; p; p = p->nxt) + if (p->outgoing + && has_clause(AND, p, n)) + { p->isgrn[p->grncnt++] = + (unsigned char) Red_cnt; + Lab_cnt++; + } + + if (n->ntyp == U_OPER) /* 3.4.0 */ + { n = n->rgt; + goto more; + } +} + +static void +mk_red(Node *n) +{ Graph *p; + + n = right_linked(n); + for (p = Nodes_Set; p; p = p->nxt) + { if (p->outgoing + && has_clause(0, p, n)) + { if (p->redcnt >= 63) + Fatal("too many Untils", (char *)0); + p->isred[p->redcnt++] = + (unsigned char) Red_cnt; + Lab_cnt++; Max_Red = Red_cnt; + } } +} + +static void +liveness(Node *n) +{ + if (n) + switch (n->ntyp) { +#ifdef NXT + case NEXT: + liveness(n->lft); + break; +#endif + case U_OPER: + Red_cnt++; + mk_red(n); + mk_grn(n->rgt); + /* fall through */ + case V_OPER: + case OR: case AND: + liveness(n->lft); + liveness(n->rgt); + break; + } +} + +static Graph * +findgraph(char *nm) +{ Graph *p; + Mapping *m; + + for (p = Nodes_Set; p; p = p->nxt) + if (!strcmp(p->name->name, nm)) + return p; + for (m = Mapped; m; m = m->nxt) + if (strcmp(m->from, nm) == 0) + return m->to; + + printf("warning: node %s not found\n", nm); + return (Graph *) 0; +} + +static void +Addout(char *to, char *from) +{ Graph *p = findgraph(from); + Symbol *s; + + if (!p) return; + s = getsym(tl_lookup(to)); + s->next = p->outgoing; + p->outgoing = s; +} + +#ifdef NXT +int +only_nxt(Node *n) +{ + switch (n->ntyp) { + case NEXT: + return 1; + case OR: + case AND: + return only_nxt(n->rgt) && only_nxt(n->lft); + default: + return 0; + } +} +#endif + +int +dump_cond(Node *pp, Node *r, int first) +{ Node *q; + int frst = first; + + if (!pp) return frst; + + q = dupnode(pp); + q = rewrite(q); + + if (q->ntyp == PREDICATE + || q->ntyp == NOT +#ifndef NXT + || q->ntyp == OR +#endif + || q->ntyp == FALSE) + { if (!frst) fprintf(tl_out, " && "); + dump(q); + frst = 0; +#ifdef NXT + } else if (q->ntyp == OR) + { if (!frst) fprintf(tl_out, " && "); + fprintf(tl_out, "(("); + frst = dump_cond(q->lft, r, 1); + + if (!frst) + fprintf(tl_out, ") || ("); + else + { if (only_nxt(q->lft)) + { fprintf(tl_out, "1))"); + return 0; + } + } + + frst = dump_cond(q->rgt, r, 1); + + if (frst) + { if (only_nxt(q->rgt)) + fprintf(tl_out, "1"); + else + fprintf(tl_out, "0"); + frst = 0; + } + + fprintf(tl_out, "))"); +#endif + } else if (q->ntyp == V_OPER + && !anywhere(AND, q->rgt, r)) + { frst = dump_cond(q->rgt, r, frst); + } else if (q->ntyp == AND) + { + frst = dump_cond(q->lft, r, frst); + frst = dump_cond(q->rgt, r, frst); + } + + return frst; +} + +static int +choueka(Graph *p, int count) +{ int j, k, incr_cnt = 0; + + for (j = count; j <= Max_Red; j++) /* for each acceptance class */ + { int delta = 0; + + /* is state p labeled Grn-j OR not Red-j ? */ + + for (k = 0; k < (int) p->grncnt; k++) + if (p->isgrn[k] == j) + { delta = 1; + break; + } + if (delta) + { incr_cnt++; + continue; + } + for (k = 0; k < (int) p->redcnt; k++) + if (p->isred[k] == j) + { delta = 1; + break; + } + + if (delta) break; + + incr_cnt++; + } + return incr_cnt; +} + +static int +set_prefix(char *pref, int count, Graph *r2) +{ int incr_cnt = 0; /* acceptance class 'count' */ + + if (Lab_cnt == 0 + || Max_Red == 0) + sprintf(pref, "accept"); /* new */ + else if (count >= Max_Red) + sprintf(pref, "T0"); /* cycle */ + else + { incr_cnt = choueka(r2, count+1); + if (incr_cnt + count >= Max_Red) + sprintf(pref, "accept"); /* last hop */ + else + sprintf(pref, "T%d", count+incr_cnt); + } + return incr_cnt; +} + +static void +fsm_trans(Graph *p, int count, char *curnm) +{ Graph *r; + Symbol *s; + char prefix[128], nwnm[128]; + + if (!p->outgoing) + addtrans(p, curnm, False, "accept_all"); + + for (s = p->outgoing; s; s = s->next) + { r = findgraph(s->name); + if (!r) continue; + if (r->outgoing) + { (void) set_prefix(prefix, count, r); + sprintf(nwnm, "%s_%s", prefix, s->name); + } else + strcpy(nwnm, "accept_all"); + + if (tl_verbose) + { printf("maxred=%d, count=%d, curnm=%s, nwnm=%s ", + Max_Red, count, curnm, nwnm); + printf("(greencnt=%d,%d, redcnt=%d,%d)\n", + r->grncnt, r->isgrn[0], + r->redcnt, r->isred[0]); + } + addtrans(p, curnm, r->Old, nwnm); + } +} + +static void +mkbuchi(void) +{ Graph *p; + int k; + char curnm[64]; + + for (k = 0; k <= Max_Red; k++) + for (p = Nodes_Set; p; p = p->nxt) + { if (!p->outgoing) + continue; + if (k != 0 + && !strcmp(p->name->name, "init") + && Max_Red != 0) + continue; + + if (k == Max_Red + && strcmp(p->name->name, "init") != 0) + strcpy(curnm, "accept_"); + else + sprintf(curnm, "T%d_", k); + + strcat(curnm, p->name->name); + + fsm_trans(p, k, curnm); + } + fsm_print(); +} + +static Symbol * +dupSlist(Symbol *s) +{ Symbol *p1, *p2, *p3, *d = ZS; + + for (p1 = s; p1; p1 = p1->next) + { for (p3 = d; p3; p3 = p3->next) + { if (!strcmp(p3->name, p1->name)) + break; + } + if (p3) continue; /* a duplicate */ + + p2 = getsym(p1); + p2->next = d; + d = p2; + } + return d; +} + +static Symbol * +catSlist(Symbol *a, Symbol *b) +{ Symbol *p1, *p2, *p3, *tmp; + + /* remove duplicates from b */ + for (p1 = a; p1; p1 = p1->next) + { p3 = ZS; + for (p2 = b; p2; p2 = p2->next) + { if (strcmp(p1->name, p2->name)) + { p3 = p2; + continue; + } + tmp = p2->next; + tfree((void *) p2); + if (p3) + p3->next = tmp; + else + b = tmp; + } } + if (!a) return b; + if (!b) return a; + if (!b->next) + { b->next = a; + return b; + } + /* find end of list */ + for (p1 = a; p1->next; p1 = p1->next) + ; + p1->next = b; + return a; +} + +static void +fixinit(Node *orig) +{ Graph *p1, *g; + Symbol *q1, *q2 = ZS; + + ng(tl_lookup("init"), ZS, ZN, ZN, ZN); + p1 = pop_stack(); + p1->nxt = Nodes_Set; + p1->Other = p1->Old = orig; + Nodes_Set = p1; + + for (g = Nodes_Set; g; g = g->nxt) + { for (q1 = g->incoming; q1; q1 = q2) + { q2 = q1->next; + Addout(g->name->name, q1->name); + tfree((void *) q1); + } + g->incoming = ZS; + } +} + +static Node * +flatten(Node *p) +{ Node *q, *r, *z = ZN; + + for (q = p; q; q = q->nxt) + { r = dupnode(q); + if (z) + z = tl_nn(AND, r, z); + else + z = r; + } + if (!z) return z; + z = rewrite(z); + return z; +} + +static Node * +Duplicate(Node *n) +{ Node *n1, *n2, *lst = ZN, *d = ZN; + + for (n1 = n; n1; n1 = n1->nxt) + { n2 = dupnode(n1); + if (lst) + { lst->nxt = n2; + lst = n2; + } else + d = lst = n2; + } + return d; +} + +static void +ng(Symbol *s, Symbol *in, Node *isnew, Node *isold, Node *next) +{ Graph *g = (Graph *) tl_emalloc(sizeof(Graph)); + + if (s) g->name = s; + else g->name = tl_lookup(newname()); + + if (in) g->incoming = dupSlist(in); + if (isnew) g->New = flatten(isnew); + if (isold) g->Old = Duplicate(isold); + if (next) g->Next = flatten(next); + + push_stack(g); +} + +static void +sdump(Node *n) +{ + switch (n->ntyp) { + case PREDICATE: strcat(dumpbuf, n->sym->name); + break; + case U_OPER: strcat(dumpbuf, "U"); + goto common2; + case V_OPER: strcat(dumpbuf, "V"); + goto common2; + case OR: strcat(dumpbuf, "|"); + goto common2; + case AND: strcat(dumpbuf, "&"); +common2: sdump(n->rgt); +common1: sdump(n->lft); + break; +#ifdef NXT + case NEXT: strcat(dumpbuf, "X"); + goto common1; +#endif + case NOT: strcat(dumpbuf, "!"); + goto common1; + case TRUE: strcat(dumpbuf, "T"); + break; + case FALSE: strcat(dumpbuf, "F"); + break; + default: strcat(dumpbuf, "?"); + break; + } +} + +Symbol * +DoDump(Node *n) +{ + if (!n) return ZS; + + if (n->ntyp == PREDICATE) + return n->sym; + + dumpbuf[0] = '\0'; + sdump(n); + return tl_lookup(dumpbuf); +} + +static int +not_new(Graph *g) +{ Graph *q1; Node *tmp, *n1, *n2; + Mapping *map; + + tmp = flatten(g->Old); /* duplicate, collapse, normalize */ + g->Other = g->Old; /* non normalized full version */ + g->Old = tmp; + + g->oldstring = DoDump(g->Old); + + tmp = flatten(g->Next); + g->nxtstring = DoDump(tmp); + + if (tl_verbose) dump_graph(g); + + Debug2("\tformula-old: [%s]\n", g->oldstring?g->oldstring->name:"true"); + Debug2("\tformula-nxt: [%s]\n", g->nxtstring?g->nxtstring->name:"true"); + for (q1 = Nodes_Set; q1; q1 = q1->nxt) + { Debug2(" compare old to: %s", q1->name->name); + Debug2(" [%s]", q1->oldstring?q1->oldstring->name:"true"); + + Debug2(" compare nxt to: %s", q1->name->name); + Debug2(" [%s]", q1->nxtstring?q1->nxtstring->name:"true"); + + if (q1->oldstring != g->oldstring + || q1->nxtstring != g->nxtstring) + { Debug(" => different\n"); + continue; + } + Debug(" => match\n"); + + if (g->incoming) + q1->incoming = catSlist(g->incoming, q1->incoming); + + /* check if there's anything in g->Other that needs + adding to q1->Other + */ + for (n2 = g->Other; n2; n2 = n2->nxt) + { for (n1 = q1->Other; n1; n1 = n1->nxt) + if (isequal(n1, n2)) + break; + if (!n1) + { Node *n3 = dupnode(n2); + /* don't mess up n2->nxt */ + n3->nxt = q1->Other; + q1->Other = n3; + } } + + map = (Mapping *) tl_emalloc(sizeof(Mapping)); + map->from = g->name->name; + map->to = q1; + map->nxt = Mapped; + Mapped = map; + + for (n1 = g->Other; n1; n1 = n2) + { n2 = n1->nxt; + releasenode(1, n1); + } + for (n1 = g->Old; n1; n1 = n2) + { n2 = n1->nxt; + releasenode(1, n1); + } + for (n1 = g->Next; n1; n1 = n2) + { n2 = n1->nxt; + releasenode(1, n1); + } + return 1; + } + + if (newstates) tl_verbose=1; + Debug2(" New Node %s [", g->name->name); + for (n1 = g->Old; n1; n1 = n1->nxt) + { Dump(n1); Debug(", "); } + Debug2("] nr %d\n", Base); + if (newstates) tl_verbose=0; + + Base++; + g->nxt = Nodes_Set; + Nodes_Set = g; + + return 0; +} + +static void +expand_g(Graph *g) +{ Node *now, *n1, *n2, *nx; + int can_release; + + if (!g->New) + { Debug2("\nDone with %s", g->name->name); + if (tl_verbose) dump_graph(g); + if (not_new(g)) + { if (tl_verbose) printf("\tIs Not New\n"); + return; + } + if (g->Next) + { Debug(" Has Next ["); + for (n1 = g->Next; n1; n1 = n1->nxt) + { Dump(n1); Debug(", "); } + Debug("]\n"); + + ng(ZS, getsym(g->name), g->Next, ZN, ZN); + } + return; + } + + if (tl_verbose) + { Symbol *z; + printf("\nExpand %s, from ", g->name->name); + for (z = g->incoming; z; z = z->next) + printf("%s, ", z->name); + printf("\n\thandle:\t"); Explain(g->New->ntyp); + dump_graph(g); + } + + if (g->New->ntyp == AND) + { if (g->New->nxt) + { n2 = g->New->rgt; + while (n2->nxt) + n2 = n2->nxt; + n2->nxt = g->New->nxt; + } + n1 = n2 = g->New->lft; + while (n2->nxt) + n2 = n2->nxt; + n2->nxt = g->New->rgt; + + releasenode(0, g->New); + + g->New = n1; + push_stack(g); + return; + } + + can_release = 0; /* unless it need not go into Old */ + now = g->New; + g->New = g->New->nxt; + now->nxt = ZN; + + if (now->ntyp != TRUE) + { if (g->Old) + { for (n1 = g->Old; n1->nxt; n1 = n1->nxt) + if (isequal(now, n1)) + { can_release = 1; + goto out; + } + n1->nxt = now; + } else + g->Old = now; + } +out: + switch (now->ntyp) { + case FALSE: + push_stack(g); + break; + case TRUE: + releasenode(1, now); + push_stack(g); + break; + case PREDICATE: + case NOT: + if (can_release) releasenode(1, now); + push_stack(g); + break; + case V_OPER: + Assert(now->rgt->nxt == ZN, now->ntyp); + Assert(now->lft->nxt == ZN, now->ntyp); + n1 = now->rgt; + n1->nxt = g->New; + + if (can_release) + nx = now; + else + nx = getnode(now); /* now also appears in Old */ + nx->nxt = g->Next; + + n2 = now->lft; + n2->nxt = getnode(now->rgt); + n2->nxt->nxt = g->New; + g->New = flatten(n2); + push_stack(g); + ng(ZS, g->incoming, n1, g->Old, nx); + break; + + case U_OPER: + Assert(now->rgt->nxt == ZN, now->ntyp); + Assert(now->lft->nxt == ZN, now->ntyp); + n1 = now->lft; + + if (can_release) + nx = now; + else + nx = getnode(now); /* now also appears in Old */ + nx->nxt = g->Next; + + n2 = now->rgt; + n2->nxt = g->New; + + goto common; + +#ifdef NXT + case NEXT: + nx = dupnode(now->lft); + nx->nxt = g->Next; + g->Next = nx; + if (can_release) releasenode(0, now); + push_stack(g); + break; +#endif + + case OR: + Assert(now->rgt->nxt == ZN, now->ntyp); + Assert(now->lft->nxt == ZN, now->ntyp); + n1 = now->lft; + nx = g->Next; + + n2 = now->rgt; + n2->nxt = g->New; +common: + n1->nxt = g->New; + + ng(ZS, g->incoming, n1, g->Old, nx); + g->New = flatten(n2); + + if (can_release) releasenode(1, now); + + push_stack(g); + break; + } +} + +Node * +twocases(Node *p) +{ Node *q; + /* 1: ([]p1 && []p2) == [](p1 && p2) */ + /* 2: (<>p1 || <>p2) == <>(p1 || p2) */ + + if (!p) return p; + + switch(p->ntyp) { + case AND: + case OR: + case U_OPER: + case V_OPER: + p->lft = twocases(p->lft); + p->rgt = twocases(p->rgt); + break; +#ifdef NXT + case NEXT: +#endif + case NOT: + p->lft = twocases(p->lft); + break; + + default: + break; + } + if (p->ntyp == AND /* 1 */ + && p->lft->ntyp == V_OPER + && p->lft->lft->ntyp == FALSE + && p->rgt->ntyp == V_OPER + && p->rgt->lft->ntyp == FALSE) + { q = tl_nn(V_OPER, False, + tl_nn(AND, p->lft->rgt, p->rgt->rgt)); + } else + if (p->ntyp == OR /* 2 */ + && p->lft->ntyp == U_OPER + && p->lft->lft->ntyp == TRUE + && p->rgt->ntyp == U_OPER + && p->rgt->lft->ntyp == TRUE) + { q = tl_nn(U_OPER, True, + tl_nn(OR, p->lft->rgt, p->rgt->rgt)); + } else + q = p; + return q; +} + +void +trans(Node *p) +{ Node *op; + Graph *g; + + if (!p || tl_errs) return; + + p = twocases(p); + + if (tl_verbose || tl_terse) + { fprintf(tl_out, "\t/* Normlzd: "); + dump(p); + fprintf(tl_out, " */\n"); + } + if (tl_terse) + return; + + op = dupnode(p); + + ng(ZS, getsym(tl_lookup("init")), p, ZN, ZN); + while ((g = Nodes_Stack) != (Graph *) 0) + { Nodes_Stack = g->nxt; + expand_g(g); + } + if (newstates) + return; + + fixinit(p); + liveness(flatten(op)); /* was: liveness(op); */ + + mkbuchi(); + if (tl_verbose) + { printf("/*\n"); + printf(" * %d states in Streett automaton\n", Base); + printf(" * %d Streett acceptance conditions\n", Max_Red); + printf(" * %d Buchi states\n", Total); + printf(" */\n"); + } +} diff --git a/sys/src/cmd/spin/vars.c b/sys/src/cmd/spin/vars.c new file mode 100755 index 000000000..e83d74f57 --- /dev/null +++ b/sys/src/cmd/spin/vars.c @@ -0,0 +1,347 @@ +/***** spin: vars.c *****/ + +/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */ +/* All Rights Reserved. This software is for educational purposes only. */ +/* No guarantee whatsoever is expressed or implied by the distribution of */ +/* this code. Permission is given to distribute this code provided that */ +/* this introductory message is not removed and no monies are exchanged. */ +/* Software written by Gerard J. Holzmann. For tool documentation see: */ +/* http://spinroot.com/ */ +/* Send all bug-reports and/or questions to: bugs@spinroot.com */ + +#include "spin.h" +#include "y.tab.h" + +extern Ordered *all_names; +extern RunList *X, *LastX; +extern Symbol *Fname; +extern char Buf[]; +extern int lineno, depth, verbose, xspin, limited_vis; +extern int analyze, jumpsteps, nproc, nstop, columns; +extern short no_arrays, Have_claim; +extern void sr_mesg(FILE *, int, int); +extern void sr_buf(int, int); + +static int getglobal(Lextok *); +static int setglobal(Lextok *, int); +static int maxcolnr = 1; + +int +getval(Lextok *sn) +{ Symbol *s = sn->sym; + + if (strcmp(s->name, "_") == 0) + { non_fatal("attempt to read value of '_'", 0); + return 0; + } + if (strcmp(s->name, "_last") == 0) + return (LastX)?LastX->pid:0; + if (strcmp(s->name, "_p") == 0) + return (X && X->pc)?X->pc->seqno:0; + if (strcmp(s->name, "_pid") == 0) + { if (!X) return 0; + return X->pid - Have_claim; + } + if (strcmp(s->name, "_nr_pr") == 0) + return nproc-nstop; /* new 3.3.10 */ + + if (s->context && s->type) + return getlocal(sn); + + if (!s->type) /* not declared locally */ + { s = lookup(s->name); /* try global */ + sn->sym = s; /* fix it */ + } + return getglobal(sn); +} + +int +setval(Lextok *v, int n) +{ + if (v->sym->context && v->sym->type) + return setlocal(v, n); + if (!v->sym->type) + v->sym = lookup(v->sym->name); + return setglobal(v, n); +} + +void +rm_selfrefs(Symbol *s, Lextok *i) +{ + if (!i) return; + + if (i->ntyp == NAME + && strcmp(i->sym->name, s->name) == 0 + && ( (!i->sym->context && !s->context) + || ( i->sym->context && s->context + && strcmp(i->sym->context->name, s->context->name) == 0))) + { lineno = i->ln; + Fname = i->fn; + non_fatal("self-reference initializing '%s'", s->name); + i->ntyp = CONST; + i->val = 0; + } else + { rm_selfrefs(s, i->lft); + rm_selfrefs(s, i->rgt); + } +} + +int +checkvar(Symbol *s, int n) +{ int i, oln = lineno; /* calls on eval() change it */ + Symbol *ofnm = Fname; + + if (!in_bound(s, n)) + return 0; + + if (s->type == 0) + { non_fatal("undecl var %s (assuming int)", s->name); + s->type = INT; + } + /* not a STRUCT */ + if (s->val == (int *) 0) /* uninitialized */ + { s->val = (int *) emalloc(s->nel*sizeof(int)); + for (i = 0; i < s->nel; i++) + { if (s->type != CHAN) + { rm_selfrefs(s, s->ini); + s->val[i] = eval(s->ini); + } else if (!analyze) + s->val[i] = qmake(s); + } } + lineno = oln; + Fname = ofnm; + return 1; +} + +static int +getglobal(Lextok *sn) +{ Symbol *s = sn->sym; + int i, n = eval(sn->lft); + + if (s->type == 0 && X && (i = find_lab(s, X->n, 0))) + { printf("findlab through getglobal on %s\n", s->name); + return i; /* can this happen? */ + } + if (s->type == STRUCT) + return Rval_struct(sn, s, 1); /* 1 = check init */ + if (checkvar(s, n)) + return cast_val(s->type, s->val[n], s->nbits); + return 0; +} + +int +cast_val(int t, int v, int w) +{ int i=0; short s=0; unsigned int u=0; + + if (t == PREDEF || t == INT || t == CHAN) i = v; /* predef means _ */ + else if (t == SHORT) s = (short) v; + else if (t == BYTE || t == MTYPE) u = (unsigned char)v; + else if (t == BIT) u = (unsigned char)(v&1); + else if (t == UNSIGNED) + { if (w == 0) + fatal("cannot happen, cast_val", (char *)0); + /* u = (unsigned)(v& ((1<<w)-1)); problem when w=32 */ + u = (unsigned)(v& (~0u>>(8*sizeof(unsigned)-w))); /* doug */ + } + + if (v != i+s+ (int) u) + { char buf[32]; sprintf(buf, "%d->%d (%d)", v, i+s+u, t); + non_fatal("value (%s) truncated in assignment", buf); + } + return (int)(i+s+u); +} + +static int +setglobal(Lextok *v, int m) +{ + if (v->sym->type == STRUCT) + (void) Lval_struct(v, v->sym, 1, m); + else + { int n = eval(v->lft); + if (checkvar(v->sym, n)) + { v->sym->val[n] = cast_val(v->sym->type, m, v->sym->nbits); + v->sym->setat = depth; + } } + return 1; +} + +void +dumpclaims(FILE *fd, int pid, char *s) +{ extern Lextok *Xu_List; extern int Pid; + extern short terse; + Lextok *m; int cnt = 0; int oPid = Pid; + + for (m = Xu_List; m; m = m->rgt) + if (strcmp(m->sym->name, s) == 0) + { cnt=1; + break; + } + if (cnt == 0) return; + + Pid = pid; + fprintf(fd, "#ifndef XUSAFE\n"); + for (m = Xu_List; m; m = m->rgt) + { if (strcmp(m->sym->name, s) != 0) + continue; + no_arrays = 1; + putname(fd, "\t\tsetq_claim(", m->lft, 0, ""); + no_arrays = 0; + fprintf(fd, ", %d, ", m->val); + terse = 1; + putname(fd, "\"", m->lft, 0, "\", h, "); + terse = 0; + fprintf(fd, "\"%s\");\n", s); + } + fprintf(fd, "#endif\n"); + Pid = oPid; +} + +void +dumpglobals(void) +{ Ordered *walk; + static Lextok *dummy = ZN; + Symbol *sp; + int j; + + if (!dummy) + dummy = nn(ZN, NAME, nn(ZN,CONST,ZN,ZN), ZN); + + for (walk = all_names; walk; walk = walk->next) + { sp = walk->entry; + if (!sp->type || sp->context || sp->owner + || sp->type == PROCTYPE || sp->type == PREDEF + || sp->type == CODE_FRAG || sp->type == CODE_DECL + || (sp->type == MTYPE && ismtype(sp->name))) + continue; + + if (sp->type == STRUCT) + { dump_struct(sp, sp->name, 0); + continue; + } + for (j = 0; j < sp->nel; j++) + { int prefetch; + if (sp->type == CHAN) + { doq(sp, j, 0); + continue; + } + if ((verbose&4) && !(verbose&64) + && (sp->setat < depth + && jumpsteps != depth)) + continue; + dummy->sym = sp; + dummy->lft->val = j; + /* in case of cast_val warnings, do this first: */ + prefetch = getglobal(dummy); + printf("\t\t%s", sp->name); + if (sp->nel > 1) printf("[%d]", j); + printf(" = "); + sr_mesg(stdout, prefetch, + sp->type == MTYPE); + printf("\n"); + if (limited_vis && (sp->hidden&2)) + { int colpos; + Buf[0] = '\0'; + if (!xspin) + { if (columns == 2) + sprintf(Buf, "~G%s = ", sp->name); + else + sprintf(Buf, "%s = ", sp->name); + } + sr_buf(prefetch, sp->type == MTYPE); + if (sp->colnr == 0) + { sp->colnr = maxcolnr; + maxcolnr = 1+(maxcolnr%10); + } + colpos = nproc+sp->colnr-1; + if (columns == 2) + { pstext(colpos, Buf); + continue; + } + if (!xspin) + { printf("\t\t%s\n", Buf); + continue; + } + printf("MSC: ~G %s %s\n", sp->name, Buf); + printf("%3d:\tproc %3d (TRACK) line 1 \"var\" ", + depth, colpos); + printf("(state 0)\t[printf('MSC: globvar\\\\n')]\n"); + printf("\t\t%s", sp->name); + if (sp->nel > 1) printf("[%d]", j); + printf(" = %s\n", Buf); + } } } +} + +void +dumplocal(RunList *r) +{ static Lextok *dummy = ZN; + Symbol *z, *s; + int i; + + if (!r) return; + + s = r->symtab; + + if (!dummy) + dummy = nn(ZN, NAME, nn(ZN,CONST,ZN,ZN), ZN); + + for (z = s; z; z = z->next) + { if (z->type == STRUCT) + { dump_struct(z, z->name, r); + continue; + } + for (i = 0; i < z->nel; i++) + { if (z->type == CHAN) + { doq(z, i, r); + continue; + } + if ((verbose&4) && !(verbose&64) + && (z->setat < depth + && jumpsteps != depth)) + continue; + + dummy->sym = z; + dummy->lft->val = i; + + printf("\t\t%s(%d):%s", + r->n->name, r->pid, z->name); + if (z->nel > 1) printf("[%d]", i); + printf(" = "); + sr_mesg(stdout, getval(dummy), z->type == MTYPE); + printf("\n"); + if (limited_vis && (z->hidden&2)) + { int colpos; + Buf[0] = '\0'; + if (!xspin) + { if (columns == 2) + sprintf(Buf, "~G%s(%d):%s = ", + r->n->name, r->pid, z->name); + else + sprintf(Buf, "%s(%d):%s = ", + r->n->name, r->pid, z->name); + } + sr_buf(getval(dummy), z->type==MTYPE); + if (z->colnr == 0) + { z->colnr = maxcolnr; + maxcolnr = 1+(maxcolnr%10); + } + colpos = nproc+z->colnr-1; + if (columns == 2) + { pstext(colpos, Buf); + continue; + } + if (!xspin) + { printf("\t\t%s\n", Buf); + continue; + } + printf("MSC: ~G %s(%d):%s %s\n", + r->n->name, r->pid, z->name, Buf); + + printf("%3d:\tproc %3d (TRACK) line 1 \"var\" ", + depth, colpos); + printf("(state 0)\t[printf('MSC: locvar\\\\n')]\n"); + printf("\t\t%s(%d):%s", + r->n->name, r->pid, z->name); + if (z->nel > 1) printf("[%d]", i); + printf(" = %s\n", Buf); + } } } +} diff --git a/sys/src/cmd/spin/version.h b/sys/src/cmd/spin/version.h new file mode 100755 index 000000000..4ad23fa4a --- /dev/null +++ b/sys/src/cmd/spin/version.h @@ -0,0 +1 @@ +#define Version "Spin Version 4.3.0 -- 22 June 2007" |