/***** spin: pangen2.c *****/ /* Copyright (c) 1991,1995 by AT&T Corporation. All Rights Reserved. */ /* This software is for educational purposes only. */ /* Permission is given to distribute this code provided that this intro- */ /* ductory message is not removed and no monies are exchanged. */ /* No guarantee is expressed or implied by the distribution of this code. */ /* Software written by Gerard J. Holzmann as part of the book: */ /* `Design and Validation of Computer Protocols,' ISBN 0-13-539925-4, */ /* Prentice Hall, Englewood Cliffs, NJ, 07632. */ /* Send bug-reports and/or questions to: gerard@research.att.com */ #include "spin.h" #include "y.tab.h" #include "pangen2.h" #define DELTA 500 /* sets an upperbound on nr of chan names */ #define blurb(fd, e) fprintf(fd, "\n\t\t/* %s:%d */\n", \ e->n->fn->name, e->n->ln) #define tr_map(m, e) 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, *owner, *context; extern char *claimproc; extern int lineno, Npars, Mpars; extern int m_loss, Nid, has_remote; extern int Ntimeouts, Etimeouts; extern int u_sync, u_async, nrRdy; extern int GenCode, IsGuard, Level, TestOnly; extern char *NextLab[]; FILE *tc, *th, *tt, *tm, *tb; int uniq=1; int nocast=0; /* to turn off casts in lvalues */ int terse=0; /* terse printing of varnames */ int no_arrays=0; int has_last=0; /* spec refers to _last */ int has_enabled=0; /* spec contains enabled() */ int has_sorted=0; /* spec contains `!!' (sorted-send) operator */ int has_random=0; /* spec contains `??' (random-recv) operator */ int has_xu=0; /* spec contains xr or xs assertions */ int has_unless=0; /* spec contains unless statements */ int mst=0; /* max nr of state/process */ int claimnr = -1; /* claim process, if any */ int Pid; /* proc currently processed */ int T_sum, T_mus, t_cyc; int TPE[2], EPT[2]; Lextok *Nn[2]; void Tpe(Lextok *); 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; } void reverse_procs(RunList *q) { if (!q) return; reverse_procs(q->nxt); fprintf(tc, " Addproc(%d);\n", q->tn); } void gensrc(void) { ProcList *p; if (!(tc = fopen("pan.c", "w")) /* main routines */ || !(th = fopen("pan.h", "w")) /* header file */ || !(tt = fopen("pan.t", "w")) /* transition matrix */ || !(tm = fopen("pan.m", "w")) /* forward moves */ || !(tb = fopen("pan.b", "w"))) /* backward moves */ { printf("spin: cannot create pan.[chtmfb]\n"); exit(1); } fprintf(th, "#define Version \"%s\"\n", Version); fprintf(th, "#define Source \"%s\"\n\n", oFname->name); fprintf(th, "#define uchar unsigned char\n"); fprintf(th, "#define DELTA %d\n", DELTA); fprintf(th, "#if !defined(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_remote) fprintf(th, "#define REM_REFS %d\n", has_remote); 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 (has_random) fprintf(th, "#define HAS_RANDOM %d\n", has_random); if (has_enabled == 0) fprintf(th, "#define INLINE 1\n"); if (has_unless) fprintf(th, "#define HAS_UNLESS %d\n", has_unless); if (claimproc) { claimnr = fproc(claimproc); fprintf(th, "#if !defined(NOCLAIM)\n"); fprintf(th, "#define VERI %d\n", claimnr); fprintf(th, "#endif\n"); fprintf(th, "#define claimline"); fprintf(th, " src_ln%d[((P0 *)pptr(1))->_p]\n", claimnr); } fprintf(th, "#define endclaim endstate%d\n", claimnr); fprintf(tc, "/*** Generated by %s ***/\n", Version); fprintf(tc, "/*** From source: %s ***/\n\n", oFname->name); ntimes(tc, 0, 1, Preamble); fprintf(tc, "#ifndef NOBOUNDCHECK\n"); fprintf(tc, "#define Index(x, y) Boundcheck(x, y, II, tt, t)\n"); fprintf(tc, "#else\n"); fprintf(tc, "#define Index(x, y) x\n"); fprintf(tc, "#endif\n"); for (p = rdy; p; p = p->nxt) mst = max(p->s->maxel, mst); 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 file %%s line %%3d\\n\",\n"); fprintf(tt, " m, n, T_SRC[n].fl, T_SRC[n].ln);\n"); fprintf(tt, "}\n"); 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,char *,int,int,int);\n\n"); fprintf(tt, "\ttrans = (Trans ***) "); fprintf(tt, "emalloc(%d*sizeof(Trans **));\n", nrRdy); fprintf(tm, " switch (t->forw) {\n"); fprintf(tm, " default: Uerror(\"bad forward move\");\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->n, p->s, p->tn, p->s->maxel, p->s->last->seqno); fprintf(tt, "}\n\n"); /* end of settable() */ fprintf(tm, " }\n\n"); fprintf(tb, " }\n\n"); ntimes(tt, 0, 1, Tail); genheader(); genaddproc(); genother(); genaddqueue(); genunio(); genconditionals(); 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"); fprintf(th, "#define NTRANS %d\n", uniq); fprintf(th, "#ifdef PEG\n"); fprintf(th, " long peg[NTRANS];\n"); fprintf(th, "#endif\n"); } 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; } 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->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]; } BB[4] = { { "Q_FULL_F", " q_full" }, { "Q_FULL_T", "!q_full" }, { "Q_EMPT_F", " !q_len" }, { "Q_EMPT_T", " q_len" } }; 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] == %s && (", BB[j].TT); for (k = 0; k < qln; k++) { if (k > 0) fprintf(tc, "\n\t\t\t || "); fprintf(tc, "%s(%s%s", BB[j].CC, 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"); } 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] == %s && (", BB[j].TT); 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"); } void genconditionals(void) { Symbol *s; int last=0, j, i; extern Queue *qtab; extern Symbol *symtab[Nhash+1]; 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(tc, "int\n"); fprintf(tc, "q_cond(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"); /* we switch on the chan name from the spec (as identifief 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 (i = 0; i <= Nhash; i++) for (s = symtab[i]; s; s = s->next) { 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 gather[128]; char pregat[128]; Symbol *z = (Symbol *)0; int nc, ov=0; if (s->context) sprintf(pregat, "((P%d *)this)->%s", j, s->name); else sprintf(pregat, "now.%s", s->name); do { strcpy(gather, pregat); nc = fill_struct(s, 0, ov, gather, &z); if (z && last != z->Nid) { last = z->Nid; if (nc < 0) Done_case(gather, z); } ov = -nc; } while (nc < 0); } } fprintf(tc, " default: Uerror(\"unknown qid - q_cond\");\n"); fprintf(tc, " return 0;\n"); fprintf(tc, " }\n"); fprintf(tc, " }\n"); fprintf(tc, " return 1;\n"); fprintf(tc, "}\n"); } void putproc(Symbol *n, Sequence *s, int i, int j, int k) { Pid = i; fprintf(th, "\nshort nstates%d=%d;\t/* %s */\n", i, j, n->name); if (i == claimnr) fprintf(th, "#define nstates_claim nstates%d\n", i); fprintf(th, "#define endstate%d %d\n", i, k); fprintf(tm, "\n /* PROC %s */\n", n->name); fprintf(tb, "\n /* PROC %s */\n", n->name); fprintf(tt, "\n /* proctype %d: %s */\n", i, n->name); fprintf(tt, "\n trans[%d] = (Trans **)", i); fprintf(tt, " emalloc(%d*sizeof(Trans *));\n\n", j); put_seq(s, 2, 0); dumpsrc(j, i); } 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; } void cnt_seq(Sequence *s) { Element *f; SeqList *h; if (s) for (f = s->frst; ; 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; } } 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; } } 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 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; } 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) */ switch (n->ntyp) { case FULL: res += DELTA; /* adds 3*DELTA + chan nr */ case EMPTY: res += DELTA; /* adds 2*DELTA + chan nr */ case 'r': case NEMPTY: res += DELTA; /* adds 1*DELTA + chan nr */ case 's': case NFULL: res += n->sym->Nid; /* add channel nr */ break; case TIMEOUT: res = 6*DELTA; default: break; } return res; } 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]); } void put_sub(Element *e, int Tt0, int Tt1) { Sequence *s = e->n->sl->this; int a; patch_atomic(s); putskip(s->frst->seqno); a = huntstart(s->frst)->seqno; 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) { 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 (e->n && e->n->ntyp != 'r' && Pid != claimnr) fprintf(tm, "IfNotBlocked\n\t\t"); if (!putcode(tm, s, e->nxt, 0)) fprintf(tm, "\t\tm = %d; goto P999;\n\n", getweight(s->frst->n)); fprintf(tb, "\tcase %d: ", uniq-1); fprintf(tb, "/* STATE %d */\n", e->seqno); fprintf(tb, "#if defined(FULLSTACK) && defined(NOCOMP)\n"); fprintf(tb, "\t\tsv_restor(!(t->atom&2));\n"); fprintf(tb, "#else\n"); fprintf(tb, "\t\tsv_restor(0);\n"); fprintf(tb, "#endif\n"); fprintf(tb, "\t\tgoto R999;\n"); if (e->nxt) a = huntele(e->nxt, e->status)->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, 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);", TPE[0], TPE[1]); } 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); 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); else fprintf(tt, "%d, %d);", TPE[0], TPE[1]); blurb(tt, e); put_seq(s, TPE[0], TPE[1]); } } void put_el(Element *e, int Tt0, int Tt1) { int n, a, Global_ref; SeqList *x; #ifdef DEBUG printf("put_el %d (%d) (->%d (%d)) (", e->Seqno, e->seqno, (e->nxt)?e->nxt->Seqno:-1, (e->nxt)?e->nxt->seqno:-1); comment(stdout, e->n, 0); printf(")"); if (e->esc) printf(" - escaped by "); for (x = e->esc; x; x = x->nxt) { printf("%d (", x->this->frst->Seqno); comment(stdout, x->this->frst->n, 0); printf(") "); } printf("\n"); #endif if (e->n->ntyp == GOTO) { Element *g = get_lab(e->n, 1); cross_dsteps(e->n, g->n); a = huntele(g, e->status)->seqno; } else if (e->nxt) a = huntele(e->nxt, e->status)->seqno; else a = 0; tr_map(uniq, e); fprintf(tt, "\ttrans[%d][%d]\t= ", Pid, e->seqno); fprintf(tm, "\tcase %d: /* STATE %d - line %d %s - [", uniq++, e->seqno, e->n->ln, e->n->fn->name); comment(tm, e->n, 0); fprintf(tm, "] */\n\t\t"); if (e->n->ntyp != 'r' && Pid != claimnr) fprintf(tm, "IfNotBlocked\n\t\t"); Global_ref = (e->status&I_GLOB)?1:has_global(e->n); putstmnt(tm, e->n, e->seqno); n = getweight(e->n); fprintf(tm, ";\n\t\tm = %d; goto P999;\n", n); if (any_undo(e->n)) { fprintf(tb, "\tcase %d: ", uniq-1); fprintf(tb, "/* STATE %d */\n\t\t", e->seqno); if (any_undo(e->n)) undostmnt(e->n, e->seqno); fprintf(tb, ";\n\t\tgoto R999;\n"); fprintf(tt, "settr(%d,%d,%d,%d,%d,\"", e->Seqno, e->status&2, a, uniq-1, uniq-1); } else fprintf(tt, "settr(%d,%d,%d,%d,0,\"", e->Seqno, e->status&2, a, uniq-1); comment(tt, e->n, e->seqno); fprintf(tt, "\", %d, ", Global_ref); if (Tt0 != 2) fprintf(tt, "%d, %d);\n", Tt0, Tt1); else { Tpe(e->n); /* sets EPT */ fprintf(tt, "%d, %d);\n", EPT[0], EPT[1]); } 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)->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)->seqno); put_seq(x->this, 2, 0); /* args?? */ } fprintf(tt, " /* end-escapes */\n"); } } void put_seq(Sequence *s, int Tt0, int Tt1) { SeqList *h; Element *e; int a; #ifdef DEBUG printf("put_seq %d - %d [%d] (%d,%d)\n", s->frst->Seqno, s->last->Seqno, s->extent->Seqno, Tt0, Tt1); #endif for (e = s->frst; e; e = e->nxt) { #ifdef DEBUG printf("put_seq %d (%d) (; %d (%d)) ", e->Seqno, e->seqno, e->nxt?e->nxt->Seqno:-1, e->nxt?e->nxt->seqno:-1); comment(stdout, e->n, 0); printf(" (%s)\n", (e->status & DONE)?"done":"new"); #endif if (e->status & DONE) continue; e->status |= DONE; if (e->n->ln) putsrc(e->n->ln, e->seqno); if (e->n->ntyp == UNLESS) { #ifdef DEBUG printf("\t(unless) %d\n", e->sub->this->frst->seqno); #endif put_seq(e->sub->this, Tt0, Tt1); } else if (e->sub) { fprintf(tt, "\tT = trans[%d][%d] = ", Pid, e->seqno); fprintf(tt, "settr(%d,%d,0,0,0,\"", e->Seqno, e->status&2); comment(tt, e->n, e->seqno); 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); a = huntstart(h->this->frst)->seqno; 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&2, a); comment(tt, e->n, e->seqno); fprintf(tt, "\", %d, %d, %d);", (h->this->frst->status&I_GLOB)?1:0, Tt0, Tt1); blurb(tt, e); } for (h = e->sub; h; h = h->nxt) put_seq(h->this, Tt0, Tt1); } else { if (e->n->ntyp == ATOMIC || e->n->ntyp == D_STEP || e->n->ntyp == NON_ATOMIC) put_sub(e, Tt0, Tt1); else put_el(e, Tt0, Tt1); } if (e == s->last) break; } } void patch_atomic(Sequence *s) /* catch goto's that break the chain */ { Element *f, *g; SeqList *h; for (f = s->frst; ; 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; } } else for (h = f->sub; h; h = h->nxt) patch_atomic(h->this); if (f == s->extent) break; } } void mark_seq(Sequence *s) { Element *f; SeqList *h; for (f = s->frst; ; f = f->nxt) { f->status |= I_GLOB; for (h = f->sub; h; h = h->nxt) mark_seq(h->this); if (f == s->last) return; } } 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)); /* 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); } int scan_seq(Sequence *s) { Element *f, *g; SeqList *h; for (f = s->frst; ; f = f->nxt) { if (has_global(f->n)) return 1; if (f->n->ntyp == GOTO) /* may reach other atomic */ { g = target(f); if (g && !(f->status & L_ATOM) && !(g->status & (ATOM|L_ATOM))) { fprintf(tt, " /* goto mark-down, "); fprintf(tt, "line %d - %d */\n", f->n->ln, (g->n)?g->n->ln:0); 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; } int has_global(Lextok *n) { Lextok *v; if (!n) return 0; switch (n->ntyp) { case '.': case BREAK: case GOTO: case CONST: case RUN: case '@': case TIMEOUT: return 0; case ELSE: return n->val; /* true if Probe's appear in other guards */ case NAME: return (n->sym->context)?0:1; case 's': return ((n->sym->xu&(XS|XX)) != XS); case 'r': return ((n->sym->xu&(XR|XX)) != XR); 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: case 'R': return (((n->sym->xu)&(XR|XS|XX)) != (XR|XS)); case ENABLED: case PC_VAL: case 'p': case 'q': return 1; case '!': case UMIN: case '~': case ASSERT: return has_global(n->lft); case '/': case '*': case '-': case '+': case '%': case LT: case GT: 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; } return 0; } 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 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: fprintf(fd, "((trpt->tau)&1)"); break; case RUN: if (claimproc && strcmp(now->sym->name, claimproc) == 0) fatal("%s is claim, not runnable", claimproc); fprintf(fd,"addproc(%d", fproc(now->sym->name)); for (v = now->lft, i = 0; v; v = v->rgt, i++) { cat2(", ", v->lft); } for ( ; i < Npars; i++) fprintf(fd, ", 0"); fprintf(fd, ")"); break; case ENABLED: cat3("enabled(II, ", now->lft, ")"); 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, ", 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, ", 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, ", 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 (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) { if (!GenCode) fprintf(fd, "\t\t{ nlost++; m=3; goto P999; }\n"); else fprintf(fd, "\t\t\tnlost++;\n\t\telse {"); } else { fprintf(fd, "\t\t\t"); Bailout(fd, ";"); } if (has_enabled) fprintf(fd, "\n\t\tif (TstOnly) return 1;"); 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) 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 == 0) putname(fd, "boq = ", now->lft, m, ""); else { putname(fd, "if (q_zero(", now->lft, m, ")) "); putname(fd, "boq = ", now->lft, m, ""); } } if (m_loss && GenCode) fprintf(fd, ";\n\t\t}\n\t\t"); break; case 'r': if (TestOnly) { fprintf(fd, "(("); if (u_sync) fprintf(fd, "(boq == -1 && "); putname(fd, "q_len(", now->lft, m, ")"); if (u_sync) { putname(fd, ") || (boq == ", now->lft,m," && "); putname(fd, "q_zero(", now->lft,m,"))"); } fprintf(fd, ")"); if (now->val == 0) { for (v = now->rgt, i=j=0; v; v = v->rgt, i++) { if (v->lft->ntyp != CONST) { j++; continue; } cat3("\n\t\t&& (", v->lft, " == "); putname(fd, "qrecv(", now->lft, m, ", "); fprintf(fd, "0, %d, 0))", i); } fprintf(fd, ")"); } else { putname(fd, "\n\t\t&& Q_has(", now->lft, m, ""); for (v = now->rgt, i=0; v; v = v->rgt, i++) { if (v->lft->ntyp != CONST) fprintf(fd, ", 0, 0"); else { fprintf(fd, ", 1, "); putstmnt(fd, v->lft, m); } } for ( ; i < Mpars; i++) fprintf(fd, ", 0, 0"); 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) { 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"); putname(fd, "{ if (boq != ", now->lft,m,") "); Bailout(fd, ";\n\t\t} else\n\t\t"); fprintf(fd, "{ if (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) j++; /* count settables */ } fprintf(fd, ";\n\t{\n\t\tint XX=1"); /* test */ if (now->val == 0) { for (v = now->rgt, i=0; v; v = v->rgt, i++) { if (v->lft->ntyp != CONST) continue; fprintf(fd, ";\n\t\t"); cat3("if (", v->lft, " != "); putname(fd, "qrecv(", now->lft, m, ", "); fprintf(fd, "0, %d, 0)) ", i); Bailout(fd, ""); } } else { putname(fd, ";\n\t\tif (!(XX = Q_has(", now->lft, m, ""); for (v = now->rgt, i=0; v; v = v->rgt, i++) { if (v->lft->ntyp != CONST) fprintf(fd, ", 0, 0"); else { fprintf(fd, ", 1, "); putstmnt(fd, v->lft, m); } } for ( ; i < Mpars; i++) fprintf(fd, ", 0, 0"); fprintf(fd, "))) "); Bailout(fd, ""); } if (has_enabled) fprintf(fd, ";\n\t\tif (TstOnly) return 1"); if (!GenCode) { fprintf(fd, ";\n"); if (j > 0) { fprintf(fd, "#if defined(FULLSTACK) && defined(NOCOMP)\n"); fprintf(fd, "\t\tif (t->atom&2)\n"); fprintf(fd, "#endif\n"); } else { fprintf(fd, "\t\tif (q_flds"); putname(fd, "[((Q0 *)qptr(", now->lft, m, "-1))->_t]"); fprintf(fd, " != %d)\n", i); fprintf(fd, "#if defined(FULLSTACK) && defined(NOCOMP)\n"); fprintf(fd, "\t\tif (t->atom&2)\n"); fprintf(fd, "#endif\n"); } fprintf(fd, "\t\t\tsv_save((char *)&now)"); } /* set */ for (v = now->rgt, i = 0; v; v = v->rgt, i++) { if (v->lft->ntyp == CONST && v->rgt) continue; fprintf(fd, ";\n\t\t"); if (v->lft->ntyp != CONST) { nocast=1; putstmnt(fd, v->lft, m); nocast=0; fprintf(fd, " = "); } putname(fd, "qrecv(", now->lft, m, ", "); fprintf(fd, "XX-1, %d, ", i); fprintf(fd, "%d)", (v->rgt)?0:1); } fprintf(fd, ";\n\t}\n\t\t"); if (u_sync) { putname(fd, "\t\tif (q_zero(", now->lft, m, ""); fprintf(fd, ")) boq = -1"); } break; case 'R': 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)) &&\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) { 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) continue; fprintf(fd, " \\\n\t\t&& qrecv("); putname(fd, "", now->lft, m, ", "); fprintf(fd, "0, %d, 0) == ", i); putstmnt(fd, v->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, ", 0, 0"); else { fprintf(fd, ", 1, "); putstmnt(fd, v->lft, m); } } for ( ; i < Mpars; i++) fprintf(fd, ", 0, 0"); fprintf(fd, ")"); } break; case 'c': cat3("if (!(", now->lft, "))\n\t\t\t"); Bailout(fd, ""); break; case ELSE: if (!GenCode) fprintf(fd, "if (trpt->o_pm&1)\n\t\t\t"); else fprintf(fd, "/* else */ "); Bailout(fd, ""); break; case '?': cat3("( (", now->lft, ") ? "); cat3("(", now->rgt->lft, ") : "); cat3("(", now->rgt->rgt, ") )"); break; case ASGN: if (has_enabled) fprintf(fd, "if (TstOnly) return 1;\n\t\t"); if (!GenCode) { cat3("(trpt+1)->oval = ", now->lft, ";\n\t\t"); } nocast = 1; putstmnt(fd,now->lft,m); nocast = 0; fprintf(fd," = "); putstmnt(fd,now->rgt,m); break; case PRINT: if (has_enabled) fprintf(fd, "if (TstOnly) return 1;\n\t\t"); fprintf(fd, "printf(%s", now->sym->name); for (v = now->lft; v; v = v->rgt) { cat2(", ", v->lft); } 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 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: putskip(m); break; case '@': if (has_enabled) fprintf(fd, "if (TstOnly) 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)\n", now->ntyp); exit(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", ""); 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) { if (s->context || strcmp(s->name, "_p") == 0 || strcmp(s->name, "_pid") == 0) { fprintf(fd, "((P%d *)this)->", Pid); } else { if (!s->hidden) fprintf(fd, "now."); } } fprintf(fd, "%s", s->name); if (s->nel != 1) { if (no_arrays) { non_fatal("ref to array element is invalid in this context", (char *)0); printf("\thint: instead of, for instance, x[rs] qu[3], use\n"); printf("\tchan nm_3 = qu[3]; x[rs] nm_3;\n"); printf("\tand use nm_3 in all 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 (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; if (terse) { fprintf(fd, "%s[", n->lft->sym->name); putstmnt(fd, n->lft->lft, m); if (strcmp(n->sym->name, "_p") == 0) fprintf(fd, "]:"); else fprintf(fd, "].%s", n->sym->name); } else { if (Sym_typ(n) < SHORT) { promoted = 1; fprintf(fd, "((int)"); } fprintf(fd, "((P%d *)Pptr(", fproc(n->lft->sym->name)); if (claimproc) fprintf(fd, "1+"); putstmnt(fd, n->lft->lft, m); fprintf(fd, "))->%s", n->sym->name); } if (n->rgt) { fprintf(fd, "["); putstmnt(fd, n->rgt, m); fprintf(fd, "]"); } if (promoted) fprintf(fd, ")"); } int getweight(Lextok *n) { switch (n->ntyp) { case 'r': return 4; case 's': return 2; case TIMEOUT: return 1; /* lowest priority */ 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)); }