plan9fox/sys/src/cmd/spin/flow.c

795 lines
17 KiB
C
Raw Normal View History

/***** 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);
}