1 /***** spin: pangen6.c *****/
4 * This file is part of the public release of Spin. It is subject to the
5 * terms in the LICENSE file that is included in this source directory.
6 * Tool documentation is available at http://spinroot.com
12 extern Ordered *all_names;
13 extern FSM_use *use_free;
14 extern FSM_state **fsm_tbl;
15 extern FSM_state *fsm;
16 extern int verbose, o_max;
18 static FSM_trans *cur_t;
19 static FSM_trans *expl_par;
20 static FSM_trans *expl_var;
21 static FSM_trans *explicit;
23 extern void rel_use(FSM_use *);
25 #define ulong unsigned long
34 ProcList *p; /* proctype decl */
35 int i_st; /* start state */
38 Pair *pairs; /* entry and exit nodes of proper subgraphs */
39 FSM_state *fsm; /* proctype body */
40 struct AST *nxt; /* linked list */
43 typedef struct RPN { /* relevant proctype names */
48 typedef struct ALIAS { /* channel aliasing info */
49 Lextok *cnm; /* this chan */
50 int origin; /* debugging - origin of the alias */
51 struct ALIAS *alias; /* can be an alias for these other chans */
52 struct ALIAS *nxt; /* linked list */
55 typedef struct ChanList {
56 Lextok *s; /* containing stmnt */
57 Lextok *n; /* point of reference - could be struct */
58 struct ChanList *nxt; /* linked list */
61 /* a chan alias can be created in one of three ways:
62 assignement to chan name
63 a = b -- a is now an alias for b
64 passing chan name as parameter in run
65 run x(b) -- proctype x(chan a)
66 passing chan name through channel
76 static ALIAS *chalcur;
77 static ALIAS *chalias;
78 static ChanList *chanlist;
79 static Slicer *slicer;
80 static Slicer *rel_vars; /* all relevant variables */
81 static int AST_Changes;
84 static int in_recv = 0;
86 static int AST_mutual(Lextok *, Lextok *, int);
87 static void AST_dominant(void);
88 static void AST_hidden(void);
89 static void AST_setcur(Lextok *);
90 static void check_slice(Lextok *, int);
91 static void curtail(AST *);
92 static void def_use(Lextok *, int);
93 static void name_AST_track(Lextok *, int);
94 static void show_expl(void);
97 AST_isini(Lextok *n) /* is this an initialized channel */
100 if (!n || !n->sym) return 0;
105 return (s->ini->ntyp == CHAN); /* freshly instantiated */
107 if (s->type == STRUCT && n->rgt)
108 return AST_isini(n->rgt->lft);
114 AST_var(Lextok *n, Symbol *s, int toplevel)
119 { if (s->context && s->type)
120 printf(":%s:L:", s->context->name);
124 printf("%s", s->name); /* array indices ignored */
126 if (s->type == STRUCT && n && n->rgt && n->rgt->lft)
128 AST_var(n->rgt->lft, n->rgt->lft->sym, 0);
133 name_def_indices(Lextok *n, int code)
135 if (!n || !n->sym) return;
137 if (n->sym->nel > 1 || n->sym->isarray)
138 def_use(n->lft, code); /* process the index */
140 if (n->sym->type == STRUCT /* and possible deeper ones */
142 name_def_indices(n->rgt->lft, code);
146 name_def_use(Lextok *n, int code)
154 { switch (cur_t->step->n->ntyp) {
155 case 'c': /* possible predicate abstraction? */
156 n->sym->colnr |= 2; /* yes */
159 n->sym->colnr |= 1; /* no */
164 for (u = cur_t->Val[0]; u; u = u->nxt)
165 if (AST_mutual(n, u->n, 1)
166 && u->special == code)
171 use_free = use_free->nxt;
173 u = (FSM_use *) emalloc(sizeof(FSM_use));
177 u->nxt = cur_t->Val[0];
180 name_def_indices(n, USE|(code&(~DEF))); /* not def, but perhaps deref */
184 def_use(Lextok *now, int code)
198 def_use(now->lft, USE|code);
206 def_use(now->lft, DEREF_USE|USE|code);
227 def_use(now->lft, USE|code);
228 def_use(now->rgt, USE|code);
232 def_use(now->lft, DEF|code);
233 def_use(now->rgt, USE|code);
236 case TYPE: /* name in parameter list */
237 name_def_use(now, code);
241 name_def_use(now, code);
245 name_def_use(now, USE); /* procname - not really needed */
246 for (v = now->lft; v; v = v->rgt)
247 def_use(v->lft, USE); /* params */
251 def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
252 for (v = now->rgt; v; v = v->rgt)
253 def_use(v->lft, USE|code);
257 def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
258 for (v = now->rgt; v; v = v->rgt)
259 { if (v->lft->ntyp == EVAL)
260 def_use(v->lft, code); /* will add USE */
261 else if (v->lft->ntyp != CONST)
262 def_use(v->lft, DEF|code);
267 def_use(now->lft, DEREF_USE|USE|code);
268 for (v = now->rgt; v; v = v->rgt)
269 { if (v->lft->ntyp == EVAL)
270 def_use(v->lft, code); /* will add USE */
275 def_use(now->lft, USE|code);
277 { def_use(now->rgt->lft, code);
278 def_use(now->rgt->rgt, code);
283 for (v = now->lft; v; v = v->rgt)
284 def_use(v->lft, USE|code);
288 def_use(now->lft, USE);
318 AST_add_alias(Lextok *n, int nr)
322 for (ca = chalcur->alias; ca; ca = ca->nxt)
323 if (AST_mutual(ca->cnm, n, 1))
324 { res = (ca->origin&nr);
325 ca->origin |= nr; /* 1, 2, or 4 - run, asgn, or rcv */
326 return (res == 0); /* 0 if already there with same origin */
329 ca = (ALIAS *) emalloc(sizeof(ALIAS));
332 ca->nxt = chalcur->alias;
338 AST_run_alias(char *pn, char *s, Lextok *t, int parno)
345 { if (strcmp(t->sym->name, s) == 0)
346 for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
348 { AST_add_alias(v->lft, 1); /* RUN */
352 { AST_run_alias(pn, s, t->lft, parno);
353 AST_run_alias(pn, s, t->rgt, parno);
358 AST_findrun(char *s, int parno)
363 for (a = ast; a; a = a->nxt) /* automata */
364 for (f = a->fsm; f; f = f->nxt) /* control states */
365 for (t = f->t; t; t = t->nxt) /* transitions */
367 AST_run_alias(a->p->n->name, s, t->step->n, parno);
372 AST_par_chans(ProcList *p) /* find local chan's init'd to chan passed as param */
376 for (walk = all_names; walk; walk = walk->next)
380 && strcmp(sp->context->name, p->n->name) == 0
381 && sp->Nid >= 0 /* not itself a param */
383 && sp->ini->ntyp == NAME) /* != CONST and != CHAN */
384 { Lextok *x = nn(ZN, 0, ZN, ZN);
387 AST_add_alias(sp->ini, 2); /* ASGN */
392 AST_para(ProcList *p)
398 for (f = p->p; f; f = f->rgt) /* list of types */
399 for (t = f->lft; t; t = t->rgt)
400 { if (t->ntyp != ',')
403 c = t->lft; /* expanded struct */
406 if (Sym_typ(c) == CHAN)
407 { ALIAS *na = (ALIAS *) emalloc(sizeof(ALIAS));
411 chalcur = chalias = na;
413 printf("%s -- (par) -- ", p->n->name);
414 AST_var(c, c->sym, 1);
417 AST_findrun(p->n->name, cnt);
426 AST_haschan(Lextok *c)
429 if (Sym_typ(c) == CHAN)
430 { AST_add_alias(c, 2); /* ASGN */
433 AST_var(c, c->sym, 1);
437 { AST_haschan(c->rgt);
443 AST_nrpar(Lextok *n) /* 's' or 'r' */
447 for (m = n->rgt; m; m = m->rgt)
453 AST_ord(Lextok *n, Lextok *s)
457 for (m = n->rgt; m; m = m->rgt)
459 if (s->sym == m->lft->sym)
467 AST_ownership(Symbol *s)
470 printf("%s:", s->name);
471 AST_ownership(s->owner);
476 AST_mutual(Lextok *a, Lextok *b, int toplevel)
479 if (!a && !b) return 1;
481 if (!a || !b) return 0;
486 if (!as || !bs) return 0;
488 if (toplevel && as->context != bs->context)
491 if (as->type != bs->type)
494 if (strcmp(as->name, bs->name) != 0)
497 if (as->type == STRUCT && a->rgt && b->rgt) /* we know that a and b are not null */
498 return AST_mutual(a->rgt->lft, b->rgt->lft, 0);
504 AST_setcur(Lextok *n) /* set chalcur */
507 for (ca = chalias; ca; ca = ca->nxt)
508 if (AST_mutual(ca->cnm, n, 1)) /* if same chan */
513 ca = (ALIAS *) emalloc(sizeof(ALIAS));
516 chalcur = chalias = ca;
520 AST_other(AST *a) /* check chan params in asgns and recvs */
526 for (f = a->fsm; f; f = f->nxt) /* control states */
527 for (t = f->t; t; t = t->nxt) /* transitions */
528 for (u = t->Val[0]; u; u = u->nxt) /* def/use info */
529 if (Sym_typ(u->n) == CHAN
530 && (u->special&DEF)) /* def of chan-name */
532 switch (t->step->n->ntyp) {
534 AST_haschan(t->step->n->rgt);
537 /* guess sends where name may originate */
538 for (cl = chanlist; cl; cl = cl->nxt) /* all sends */
539 { int aa = AST_nrpar(cl->s);
540 int bb = AST_nrpar(t->step->n);
541 if (aa != bb) /* matching nrs of params */
544 aa = AST_ord(cl->s, cl->n);
545 bb = AST_ord(t->step->n, u->n);
546 if (aa != bb) /* same position in parlist */
549 AST_add_alias(cl->n, 4); /* RCV assume possible match */
553 printf("type = %d\n", t->step->n->ntyp);
554 non_fatal("unexpected chan def type", (char *) 0);
563 for (na = chalias; na; na = na->nxt)
564 { printf("\npossible aliases of ");
565 AST_var(na->cnm, na->cnm->sym, 1);
567 for (ca = na->alias; ca; ca = ca->nxt)
569 printf("no valid name ");
571 AST_var(ca->cnm, ca->cnm->sym, 1);
573 if (ca->origin & 1) printf("RUN ");
574 if (ca->origin & 2) printf("ASGN ");
575 if (ca->origin & 4) printf("RCV ");
576 printf("[%s]", AST_isini(ca->cnm)?"Initzd":"Name");
578 if (ca->nxt) printf(", ");
586 AST_indirect(FSM_use *uin, FSM_trans *t, char *cause, char *pn)
589 /* this is a newly discovered relevant statement */
590 /* all vars it uses to contribute to its DEF are new criteria */
592 if (!(t->relevant&1)) AST_Changes++;
594 t->round = AST_Round;
597 if ((verbose&32) && t->step)
598 { printf("\tDR %s [[ ", pn);
599 comment(stdout, t->step->n, 0);
600 printf("]]\n\t\tfully relevant %s", cause);
601 if (uin) { printf(" due to "); AST_var(uin->n, uin->n->sym, 1); }
604 for (u = t->Val[0]; u; u = u->nxt)
606 && (u->special&(USE|DEREF_USE)))
608 { printf("\t\t\tuses(%d): ", u->special);
609 AST_var(u->n, u->n->sym, 1);
612 name_AST_track(u->n, u->special); /* add to slice criteria */
617 def_relevant(char *pn, FSM_trans *t, Lextok *n, int ischan)
622 /* look for all DEF's of n
623 * mark those stmnts relevant
624 * mark all var USEs in those stmnts as criteria
628 for (u = t->Val[0]; u; u = u->nxt)
629 { chanref = (Sym_typ(u->n) == CHAN);
631 if (ischan != chanref /* no possible match */
632 || !(u->special&(DEF|DEREF_DEF))) /* not a def */
635 if (AST_mutual(u->n, n, 1))
636 { AST_indirect(u, t, "(exact match)", pn);
641 for (na = chalias; na; na = na->nxt)
642 { if (!AST_mutual(u->n, na->cnm, 1))
644 for (ca = na->alias; ca; ca = ca->nxt)
645 if (AST_mutual(ca->cnm, n, 1)
646 && AST_isini(ca->cnm))
647 { AST_indirect(u, t, "(alias match)", pn);
655 AST_relevant(Lextok *n)
661 /* look for all DEF's of n
662 * mark those stmnts relevant
663 * mark all var USEs in those stmnts as criteria
667 ischan = (Sym_typ(n) == CHAN);
670 { printf("<<ast_relevant (ntyp=%d) ", n->ntyp);
671 AST_var(n, n->sym, 1);
675 for (t = expl_par; t; t = t->nxt) /* param assignments */
676 { if (!(t->relevant&1))
677 def_relevant(":params:", t, n, ischan);
680 for (t = expl_var; t; t = t->nxt)
681 { if (!(t->relevant&1)) /* var inits */
682 def_relevant(":vars:", t, n, ischan);
685 for (a = ast; a; a = a->nxt) /* all other stmnts */
686 { if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
687 for (f = a->fsm; f; f = f->nxt)
688 for (t = f->t; t; t = t->nxt)
689 { if (!(t->relevant&1))
690 def_relevant(a->p->n->name, t, n, ischan);
699 for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
700 for (t = T; t; t = t->nxt)
702 for (u = t->Val[0]; u; u = u->nxt)
703 { if (u->n->sym->type
704 && u->n->sym->context
705 && strcmp(u->n->sym->context->name, s) == 0)
708 { printf("proctype %s relevant, due to symbol ", s);
709 AST_var(u->n, u->n->sym, 1);
722 for (r = rpn; r; r = r->nxt)
723 { for (a = ast; a; a = a->nxt)
724 if (strcmp(a->p->n->name, r->rn->name) == 0)
729 fatal("cannot find proctype %s", r->rn->name);
734 AST_procisrelevant(Symbol *s)
736 for (r = rpn; r; r = r->nxt)
737 if (strcmp(r->rn->name, s->name) == 0)
739 r = (RPN *) emalloc(sizeof(RPN));
746 AST_proc_isrel(char *s)
749 for (a = ast; a; a = a->nxt)
750 if (strcmp(a->p->n->name, s) == 0)
751 return (a->relevant&1);
752 non_fatal("cannot happen, missing proc in ast", (char *) 0);
757 AST_scoutrun(Lextok *t)
762 return AST_proc_isrel(t->sym->name);
763 return (AST_scoutrun(t->lft) || AST_scoutrun(t->rgt));
772 /* if any stmnt inside a proctype is relevant
773 * or any parameter passed in a run
774 * then so are all the run statements on that proctype
777 for (a = ast; a; a = a->nxt)
778 { if (a->p->b == N_CLAIM || a->p->b == I_PROC
779 || a->p->b == E_TRACE || a->p->b == N_TRACE)
780 { a->relevant |= 1; /* the proctype is relevant */
783 if (AST_relpar(a->p->n->name))
786 { for (f = a->fsm; f; f = f->nxt)
787 for (t = f->t; t; t = t->nxt)
795 for (a = ast; a; a = a->nxt)
796 for (f = a->fsm; f; f = f->nxt)
797 for (t = f->t; t; t = t->nxt)
799 && AST_scoutrun(t->step->n))
800 { AST_indirect((FSM_use *)0, t, ":run:", a->p->n->name);
801 /* BUT, not all actual params are relevant */
806 AST_report(AST *a, Element *e) /* ALSO deduce irrelevant vars */
808 if (!(a->relevant&2))
810 printf("spin: redundant in proctype %s (for given property):\n",
813 printf(" %s:%d (state %d)",
814 e->n?e->n->fn->name:"-",
818 comment(stdout, e->n, 0);
823 AST_always(Lextok *n)
827 if (n->ntyp == '@' /* -end */
828 || n->ntyp == 'p') /* remote reference */
830 return AST_always(n->lft) || AST_always(n->rgt);
834 AST_edge_dump(AST *a, FSM_state *f)
838 for (t = f->t; t; t = t->nxt) /* edges */
840 if (t->step && AST_always(t->step->n))
841 t->relevant |= 1; /* always relevant */
844 { switch (t->relevant) {
845 case 0: printf(" "); break;
846 case 1: printf("*%3d ", t->round); break;
847 case 2: printf("+%3d ", t->round); break;
848 case 3: printf("#%3d ", t->round); break;
849 default: printf("? "); break;
852 printf("%d\t->\t%d\t", f->from, t->to);
854 comment(stdout, t->step->n, 0);
858 for (u = t->Val[0]; u; u = u->nxt)
860 AST_var(u->n, u->n->sym, 1);
861 printf(":%d>", u->special);
869 switch(t->step->n->ntyp) {
874 if (t->step->n->lft->ntyp != CONST)
875 AST_report(a, t->step);
878 case PRINT: /* don't report */
889 AST_dfs(AST *a, int s, int vis)
897 if (vis) AST_edge_dump(a, f);
899 for (t = f->t; t; t = t->nxt)
900 AST_dfs(a, t->to, vis);
907 for (f = a->fsm; f; f = f->nxt)
909 fsm_tbl[f->from] = f;
913 printf("AST_START %s from %d\n", a->p->n->name, a->i_st);
915 AST_dfs(a, a->i_st, 1);
925 for (f = a->fsm; f; f = f->nxt) /* control states */
926 for (t = f->t; t; t = t->nxt) /* transitions */
929 && t->step->n->ntyp == 's')
930 for (u = t->Val[0]; u; u = u->nxt)
931 { if (Sym_typ(u->n) == CHAN
932 && ((u->special&USE) && !(u->special&DEREF_USE)))
935 printf("%s -- (%d->%d) -- ",
936 a->p->n->name, f->from, t->to);
937 AST_var(u->n, u->n->sym, 1);
938 printf(" -> chanlist\n");
940 cl = (ChanList *) emalloc(sizeof(ChanList));
948 AST_alfind(Lextok *n)
951 for (na = chalias; na; na = na->nxt)
952 if (AST_mutual(na->cnm, n, 1))
959 { ALIAS *na, *ca, *da, *ea;
964 for (na = chalias; na; na = na->nxt)
966 for (ca = na->alias; ca; ca = ca->nxt)
967 { da = AST_alfind(ca->cnm);
969 for (ea = da->alias; ea; ea = ea->nxt)
970 { nchanges += AST_add_alias(ea->cnm,
971 ea->origin|ca->origin);
973 } while (nchanges > 0);
975 chalcur = (ALIAS *) 0;
983 for (f = a->fsm; f; f = f->nxt) /* control states */
984 for (t = f->t; t; t = t->nxt) /* all edges */
986 rel_use(t->Val[0]); /* redo Val; doesn't cover structs */
988 t->Val[0] = t->Val[1] = (FSM_use *) 0;
990 if (!t->step) continue;
992 def_use(t->step->n, 0); /* def/use info, including structs */
994 cur_t = (FSM_trans *) 0;
998 name_AST_track(Lextok *n, int code)
999 { extern int nr_errs;
1001 printf("AST_name: ");
1002 AST_var(n, n->sym, 1);
1003 printf(" -- %d\n", code);
1005 if (in_recv && (code&DEF) && (code&USE))
1006 { printf("spin: %s:%d, error: DEF and USE of same var in rcv stmnt: ",
1007 n->fn->name, n->ln);
1008 AST_var(n, n->sym, 1);
1009 printf(" -- %d\n", code);
1012 check_slice(n, code);
1016 AST_track(Lextok *now, int code) /* called from main.c */
1017 { Lextok *v; extern int export_ast;
1019 if (!export_ast) return;
1022 switch (now->ntyp) {
1028 AST_track(now->lft, DEREF_USE|USE|code);
1049 AST_track(now->rgt, USE|code);
1059 AST_track(now->lft, USE|code);
1063 AST_track(now->lft, USE|(code&(~DEF)));
1067 name_AST_track(now, code);
1068 if (now->sym->nel > 1 || now->sym->isarray)
1069 AST_track(now->lft, USE); /* index, was USE|code */
1073 AST_track(now->lft, DEREF_USE|USE|code);
1074 for (v = now->rgt; v; v = v->rgt)
1075 AST_track(v->lft, code); /* a deeper eval can add USE */
1079 AST_track(now->lft, USE|code);
1081 { AST_track(now->rgt->lft, code);
1082 AST_track(now->rgt->rgt, code);
1086 /* added for control deps: */
1088 name_AST_track(now, code);
1091 AST_track(now->lft, DEF|code);
1092 AST_track(now->rgt, USE|code);
1095 name_AST_track(now, USE);
1096 for (v = now->lft; v; v = v->rgt)
1097 AST_track(v->lft, USE|code);
1100 AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
1101 for (v = now->rgt; v; v = v->rgt)
1102 AST_track(v->lft, USE|code);
1105 AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
1106 for (v = now->rgt; v; v = v->rgt)
1108 AST_track(v->lft, DEF|code);
1113 for (v = now->lft; v; v = v->rgt)
1114 AST_track(v->lft, USE|code);
1117 AST_track(now->lft, USE);
1124 '?' -sym-> a (proctype)
1128 AST_track(now->lft->lft, USE|code);
1129 AST_procisrelevant(now->lft->sym);
1155 printf("AST_track, NOT EXPECTED ntyp: %d\n", now->ntyp);
1168 { printf("Relevant variables:\n");
1169 for (rv = rel_vars; rv; rv = rv->nxt)
1171 AST_var(rv->n, rv->n->sym, 1);
1176 for (rv = rel_vars; rv; rv = rv->nxt)
1177 rv->n->sym->setat = 1; /* mark it */
1179 for (walk = all_names; walk; walk = walk->next)
1183 && (s->type != MTYPE || s->ini->ntyp != CONST)
1184 && s->type != STRUCT /* report only fields */
1185 && s->type != PROCTYPE
1187 && sputtype(buf, s->type))
1190 printf("spin: redundant vars (for given property):\n");
1199 AST_suggestions(void)
1208 for (walk = all_names; walk; walk = walk->next)
1210 if (s->colnr == 2 /* only used in conditionals */
1214 || s->type == MTYPE))
1217 printf("spin: consider using predicate");
1218 printf(" abstraction to replace:\n");
1224 /* look for source and sink processes */
1226 for (a = ast; a; a = a->nxt) /* automata */
1228 for (f = a->fsm; f; f = f->nxt) /* control states */
1229 for (t = f->t; t; t = t->nxt) /* transitions */
1231 switch (t->step->n->ntyp) {
1259 no_good: if (banner == 1 || banner == 2)
1260 { printf("spin: proctype %s defines a %s process\n",
1262 banner==1?"source":"sink");
1264 } else if (banner == 3)
1265 { printf("spin: proctype %s mimics a buffer\n",
1271 { printf("\tto reduce complexity, consider merging the code of\n");
1272 printf("\teach source process into the code of its target\n");
1275 { printf("\tto reduce complexity, consider merging the code of\n");
1276 printf("\teach sink process into the code of its source\n");
1279 printf("\tto reduce complexity, avoid buffer processes\n");
1284 { Slicer *sc, *nx, *rv;
1286 for (sc = slicer; sc; sc = nx)
1292 for (rv = rel_vars; rv; rv = rv->nxt)
1293 if (AST_mutual(sc->n, rv->n, 1))
1296 if (!rv) /* not already there */
1297 { sc->nxt = rel_vars;
1304 check_slice(Lextok *n, int code)
1307 for (sc = slicer; sc; sc = sc->nxt)
1308 if (AST_mutual(sc->n, n, 1)
1309 && sc->code == code)
1310 return; /* already there */
1312 sc = (Slicer *) emalloc(sizeof(Slicer));
1325 /* mark all def-relevant transitions */
1326 for (sc = slicer; sc; sc = sc->nxt)
1329 { printf("spin: slice criterion ");
1330 AST_var(sc->n, sc->n->sym, 1);
1331 printf(" type=%d\n", Sym_typ(sc->n));
1333 AST_relevant(sc->n);
1335 AST_tagruns(); /* mark 'run's relevant if target proctype is relevant */
1339 AST_blockable(AST *a, int s)
1345 for (t = f->t; t; t = t->nxt)
1346 { if (t->relevant&2)
1349 if (t->step && t->step->n)
1350 switch (t->step->n->ntyp) {
1356 if (AST_blockable(a, t->to))
1357 { t->round = AST_Round;
1361 /* else fall through */
1365 else if (AST_blockable(a, t->to)) /* Unless */
1366 { t->round = AST_Round;
1375 AST_spread(AST *a, int s)
1381 for (t = f->t; t; t = t->nxt)
1382 { if (t->relevant&2)
1385 if (t->step && t->step->n)
1386 switch (t->step->n->ntyp) {
1392 AST_spread(a, t->to);
1395 t->round = AST_Round;
1400 { AST_spread(a, t->to);
1401 t->round = AST_Round;
1408 AST_notrelevant(Lextok *n)
1411 for (s = rel_vars; s; s = s->nxt)
1412 if (AST_mutual(s->n, n, 1))
1414 for (s = slicer; s; s = s->nxt)
1415 if (AST_mutual(s->n, n, 1))
1421 AST_withchan(Lextok *n)
1424 if (Sym_typ(n) == CHAN)
1426 return AST_withchan(n->lft) || AST_withchan(n->rgt);
1430 AST_suspect(FSM_trans *t)
1432 /* check for possible overkill */
1433 if (!t || !t->step || !AST_withchan(t->step->n))
1435 for (u = t->Val[0]; u; u = u->nxt)
1436 if (AST_notrelevant(u->n))
1442 AST_shouldconsider(AST *a, int s)
1447 for (t = f->t; t; t = t->nxt)
1448 { if (t->step && t->step->n)
1449 switch (t->step->n->ntyp) {
1455 AST_shouldconsider(a, t->to);
1458 AST_track(t->step->n, 0);
1460 AST_track is called here for a blockable stmnt from which
1461 a relevant stmnmt was shown to be reachable
1462 for a condition this makes all USEs relevant
1463 but for a channel operation it only makes the executability
1464 relevant -- in those cases, parameters that aren't already
1465 relevant may be replaceable with arbitrary tokens
1468 { printf("spin: possibly redundant parameters in: ");
1469 comment(stdout, t->step->n, 0);
1474 else /* an Unless */
1475 AST_shouldconsider(a, t->to);
1480 FSM_critical(AST *a, int s)
1484 /* is a 1-relevant stmnt reachable from this state? */
1491 for (t = f->t; t; t = t->nxt)
1493 || FSM_critical(a, t->to))
1497 { printf("\t\t\t\tcritical(%d) ", t->relevant);
1498 comment(stdout, t->step->n, 0);
1506 { printf("\t\t\t\tnot-crit ");
1507 comment(stdout, t->step->n, 0);
1522 /* add all blockable transitions
1523 * from which relevant transitions can be reached
1526 printf("CTL -- %s\n", a->p->n->name);
1528 /* 1 : mark all blockable edges */
1529 for (f = a->fsm; f; f = f->nxt)
1530 { if (!(f->scratch&2)) /* not part of irrelevant subgraph */
1531 for (t = f->t; t; t = t->nxt)
1532 { if (t->step && t->step->n)
1533 switch (t->step->n->ntyp) {
1538 t->round = AST_Round;
1539 t->relevant |= 2; /* mark for next phases */
1541 { printf("\tpremark ");
1542 comment(stdout, t->step->n, 0);
1550 /* 2: keep only 2-marked stmnts from which 1-marked stmnts can be reached */
1551 for (f = a->fsm; f; f = f->nxt)
1552 { fsm_tbl[f->from] = f;
1553 f->seen = 0; /* used in dfs from FSM_critical */
1555 for (f = a->fsm; f; f = f->nxt)
1556 { if (!FSM_critical(a, f->from))
1557 for (t = f->t; t; t = t->nxt)
1559 { t->relevant &= ~2; /* clear mark */
1561 { printf("\t\tnomark ");
1562 if (t->step && t->step->n)
1563 comment(stdout, t->step->n, 0);
1567 /* 3 : lift marks across IF/DO etc. */
1568 for (f = a->fsm; f; f = f->nxt)
1570 for (t = f->t; t; t = t->nxt)
1571 { if (t->step && t->step->n)
1572 switch (t->step->n->ntyp) {
1578 if (AST_blockable(a, t->to))
1584 else if (AST_blockable(a, t->to)) /* Unless */
1589 if (hit) /* at least one outgoing trans can block */
1590 for (t = f->t; t; t = t->nxt)
1591 { t->round = AST_Round;
1592 t->relevant |= 2; /* lift */
1594 { printf("\t\t\tliftmark ");
1595 if (t->step && t->step->n)
1596 comment(stdout, t->step->n, 0);
1599 AST_spread(a, t->to); /* and spread to all guards */
1602 /* 4: nodes with 2-marked out-edges contribute new slice criteria */
1603 for (f = a->fsm; f; f = f->nxt)
1604 for (t = f->t; t; t = t->nxt)
1606 { AST_shouldconsider(a, f->from);
1607 break; /* inner loop */
1612 AST_control_dep(void)
1615 for (a = ast; a; a = a->nxt)
1616 { if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
1627 for (a = ast; a; a = a->nxt)
1628 { if (a->p->b != N_CLAIM && a->p->b != E_TRACE && a->p->b != N_TRACE)
1629 for (f = a->fsm; f; f = f->nxt)
1630 for (t = f->t; t; t = t->nxt)
1633 && t->step->n->ntyp == ASSERT
1642 * remote labels are handled separately -- by making
1643 * sure they are not pruned away during optimization
1645 AST_Changes = 1; /* to get started */
1646 for (AST_Round = 1; slicer && AST_Changes; AST_Round++)
1649 AST_preserve(); /* moves processed vars from slicer to rel_vars */
1650 AST_dominant(); /* mark data-irrelevant subgraphs */
1651 AST_control_dep(); /* can add data deps, which add control deps */
1654 printf("\n\nROUND %d -- changes %d\n",
1655 AST_Round, AST_Changes);
1660 AST_alias_analysis(void) /* aliasing of promela channels */
1663 for (a = ast; a; a = a->nxt)
1664 AST_sends(a); /* collect chan-names that are send across chans */
1666 for (a = ast; a; a = a->nxt)
1667 AST_para(a->p); /* aliasing of chans thru proctype parameters */
1669 for (a = ast; a; a = a->nxt)
1670 AST_other(a); /* chan params in asgns and recvs */
1672 AST_trans(); /* transitive closure of alias table */
1675 AST_aliases(); /* show channel aliasing info */
1684 { printf("spin: warning: no slice criteria found (no assertions and no claim)\n");
1687 AST_dorelevant(); /* mark procs refered to in remote refs */
1689 for (a = ast; a; a = a->nxt)
1690 AST_def_use(a); /* compute standard def/use information */
1692 AST_hidden(); /* parameter passing and local var inits */
1694 AST_alias_analysis(); /* channel alias analysis */
1696 AST_prelabel(); /* mark all 'assert(...)' stmnts as relevant */
1697 AST_criteria(); /* process the slice criteria from
1698 * asserts and from the never claim
1700 if (!spurious || (verbose&32))
1702 for (a = ast; a; a = a->nxt)
1703 { AST_dump(a); /* marked up result */
1704 if (a->relevant&2) /* it printed something */
1707 if (!AST_dump_rel() /* relevant variables */
1709 printf("spin: no redundancies found (for given property)\n");
1718 AST_store(ProcList *p, int start_state)
1721 if (p->b != N_CLAIM && p->b != E_TRACE && p->b != N_TRACE)
1722 { n_ast = (AST *) emalloc(sizeof(AST));
1724 n_ast->i_st = start_state;
1725 n_ast->relevant = 0;
1730 fsm = (FSM_state *) 0; /* hide it from FSM_DEL */
1734 AST_add_explicit(Lextok *d, Lextok *u)
1735 { FSM_trans *e = (FSM_trans *) emalloc(sizeof(FSM_trans));
1737 e->to = 0; /* or start_state ? */
1738 e->relevant = 0; /* to be determined */
1739 e->step = (Element *) 0; /* left blank */
1740 e->Val[0] = e->Val[1] = (FSM_use *) 0;
1747 cur_t = (FSM_trans *) 0;
1754 AST_fp1(char *s, Lextok *t, Lextok *f, int parno)
1761 { if (strcmp(t->sym->name, s) == 0)
1762 for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
1764 { AST_add_explicit(f, v->lft);
1768 { AST_fp1(s, t->lft, f, parno);
1769 AST_fp1(s, t->rgt, f, parno);
1774 AST_mk1(char *s, Lextok *c, int parno)
1779 /* concoct an extra FSM_trans *t with the asgn of
1780 * formal par c to matching actual pars made explicit
1783 for (a = ast; a; a = a->nxt) /* automata */
1784 for (f = a->fsm; f; f = f->nxt) /* control states */
1785 for (t = f->t; t; t = t->nxt) /* transitions */
1787 AST_fp1(s, t->step->n, c, parno);
1792 AST_par_init(void) /* parameter passing -- hidden assignments */
1797 for (a = ast; a; a = a->nxt)
1798 { if (a->p->b == N_CLAIM || a->p->b == I_PROC
1799 || a->p->b == E_TRACE || a->p->b == N_TRACE)
1800 { continue; /* has no params */
1803 for (f = a->p->p; f; f = f->rgt) /* types */
1804 for (t = f->lft; t; t = t->rgt) /* formals */
1805 { cnt++; /* formal par count */
1806 c = (t->ntyp != ',')? t : t->lft; /* the formal parameter */
1807 AST_mk1(a->p->n->name, c, cnt); /* all matching run statements */
1812 AST_var_init(void) /* initialized vars (not chans) - hidden assignments */
1818 for (walk = all_names; walk; walk = walk->next)
1821 && !sp->context /* globals */
1822 && sp->type != PROCTYPE
1824 && (sp->type != MTYPE || sp->ini->ntyp != CONST) /* not mtype defs */
1825 && sp->ini->ntyp != CHAN)
1826 { x = nn(ZN, TYPE, ZN, ZN);
1828 AST_add_explicit(x, sp->ini);
1831 for (a = ast; a; a = a->nxt)
1832 { if (a->p->b != N_CLAIM
1833 && a->p->b != E_TRACE && a->p->b != N_TRACE) /* has no locals */
1834 for (walk = all_names; walk; walk = walk->next)
1838 && strcmp(sp->context->name, a->p->n->name) == 0
1839 && sp->Nid >= 0 /* not a param */
1840 && sp->type != LABEL
1842 && sp->ini->ntyp != CHAN)
1843 { x = nn(ZN, TYPE, ZN, ZN);
1845 AST_add_explicit(x, sp->ini);
1854 printf("\nExplicit List:\n");
1855 for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
1856 { for (t = T; t; t = t->nxt)
1857 { if (!t->Val[0]) continue;
1858 printf("%s", t->relevant?"*":" ");
1859 printf("%3d", t->round);
1860 for (u = t->Val[0]; u; u = u->nxt)
1862 AST_var(u->n, u->n->sym, 1);
1863 printf(":%d>, ", u->special);
1873 AST_hidden(void) /* reveal all hidden assignments */
1876 expl_par = explicit;
1877 explicit = (FSM_trans *) 0;
1880 expl_var = explicit;
1881 explicit = (FSM_trans *) 0;
1884 #define BPW (8*sizeof(ulong)) /* bits per word */
1887 bad_scratch(FSM_state *f, int upto)
1890 1. all internal branch-points have else-s
1891 2. all non-branchpoints have non-blocking out-edge
1892 3. all internal edges are non-relevant
1893 subgraphs like this need NOT contribute control-dependencies
1905 if (verbose&32) printf("X[%d:%d:%d] ", f->from, upto, f->scratch);
1909 printf("\tbad scratch: %d\n", f->from);
1910 bad: f->scratch &= ~4;
1911 /* f->scratch |= 8; wrong */
1915 if (f->from != upto)
1916 for (t = f->t; t; t = t->nxt)
1917 if (bad_scratch(fsm_tbl[t->to], upto))
1924 mark_subgraph(FSM_state *f, int upto)
1934 for (t = f->t; t; t = t->nxt)
1935 mark_subgraph(fsm_tbl[t->to], upto);
1939 AST_pair(AST *a, FSM_state *h, int y)
1942 for (p = a->pairs; p; p = p->nxt)
1947 p = (Pair *) emalloc(sizeof(Pair));
1955 AST_checkpairs(AST *a)
1958 for (p = a->pairs; p; p = p->nxt)
1960 printf(" inspect pair %d %d\n", p->b, p->h->from);
1961 if (!bad_scratch(p->h, p->b)) /* subgraph is clean */
1963 printf("subgraph: %d .. %d\n", p->b, p->h->from);
1964 mark_subgraph(p->h, p->b);
1970 subgraph(AST *a, FSM_state *f, int out)
1975 reverse dominance suggests that this is a possible
1976 entry and exit node for a proper subgraph
1981 j = f->from % BPW; /* assert(j <= 32); else lshift undefined? */
1985 printf("possible pair %d %d -- %d\n",
1986 f->from, h->from, (g[i]&(1<<j))?1:0);
1988 if (g[i]&(1<<j)) /* also a forward dominance pair */
1989 AST_pair(a, h, f->from); /* record this pair */
1998 for (f = a->fsm; f; f = f->nxt)
1999 { if (!f->seen) continue;
2001 f->from is the exit-node of a proper subgraph, with
2002 the dominator its entry-node, if:
2003 a. this node has more than 1 reachable predecessor
2004 b. the dominator has more than 1 reachable successor
2005 (need reachability - in case of reverse dominance)
2006 d. the dominator is reachable, and not equal to this node
2008 for (t = f->p, i = 0; t; t = t->nxt)
2009 { i += fsm_tbl[t->to]->seen;
2012 { continue; /* a. */
2014 for (cnt = 1; cnt < a->nstates; cnt++) /* 0 is endstate */
2015 { if (cnt == f->from
2016 || !fsm_tbl[cnt]->seen)
2017 { continue; /* c. */
2020 j = cnt % BPW; /* assert(j <= 32); */
2021 if (!(f->dom[i]&(1<<j)))
2024 for (t = fsm_tbl[cnt]->t, i = 0; t; t = t->nxt)
2025 { i += fsm_tbl[t->to]->seen;
2028 { continue; /* b. */
2030 if (f->mod) /* final check in 2nd phase */
2031 { subgraph(a, f, cnt); /* possible entry-exit pair */
2036 reachability(AST *a)
2039 for (f = a->fsm; f; f = f->nxt)
2040 f->seen = 0; /* clear */
2041 AST_dfs(a, a->i_st, 0); /* mark 'seen' */
2045 see_else(FSM_state *f)
2048 for (t = f->t; t; t = t->nxt)
2051 switch (t->step->n->ntyp) {
2059 if (see_else(fsm_tbl[t->to]))
2069 is_guard(FSM_state *f)
2073 for (t = f->p; t; t = t->nxt)
2074 { g = fsm_tbl[t->to];
2080 switch(t->step->n->ntyp) {
2100 int i, haselse, isrel, blocking;
2102 mark nodes that do not satisfy these requirements:
2103 1. all internal branch-points have else-s
2104 2. all non-branchpoints have non-blocking out-edge
2105 3. all internal edges are non-data-relevant
2108 printf("Curtail %s:\n", a->p->n->name);
2110 for (f = a->fsm; f; f = f->nxt)
2112 || (f->scratch&(1|2)))
2115 isrel = haselse = i = blocking = 0;
2117 for (t = f->t; t; t = t->nxt)
2118 { g = fsm_tbl[t->to];
2120 isrel |= (t->relevant&1); /* data relevant */
2125 { switch (t->step->n->ntyp) {
2128 haselse |= see_else(g);
2138 printf("prescratch %d -- %d %d %d %d -- %d\n",
2139 f->from, i, isrel, blocking, haselse, is_guard(f));
2142 || (i == 1 && blocking) /* 2. */
2143 || (i > 1 && !haselse)) /* 1. */
2147 printf("scratch %d -- %d %d %d %d\n",
2148 f->from, i, isrel, blocking, haselse);
2160 (2) for s in S - {s0} do D(s) = S
2163 for (f = a->fsm; f; f = f->nxt)
2164 { if (!f->seen) continue;
2166 f->dom = (ulong *) emalloc(a->nwords * sizeof(ulong));
2168 if (f->from == a->i_st)
2169 { i = a->i_st / BPW;
2170 j = a->i_st % BPW; /* assert(j <= 32); */
2171 f->dom[i] = (1<<j); /* (1) */
2173 { for (i = 0; i < a->nwords; i++)
2174 { f->dom[i] = (ulong) ~0; /* all 1's */
2176 if (a->nstates % BPW)
2177 for (i = (a->nstates % BPW); i < (int) BPW; i++)
2178 { f->dom[a->nwords-1] &= ~(1<< ((ulong) i)); /* clear tail */
2180 for (cnt = 0; cnt < a->nstates; cnt++)
2181 { if (!fsm_tbl[cnt]->seen)
2183 j = cnt % BPW; /* assert(j <= 32); */
2184 f->dom[i] &= ~(1<< ((ulong) j));
2189 dom_perculate(AST *a, FSM_state *f)
2190 { static ulong *ndom = (ulong *) 0;
2199 emalloc(on * sizeof(ulong));
2202 for (i = 0; i < a->nwords; i++)
2203 ndom[i] = (ulong) ~0;
2205 for (t = f->p; t; t = t->nxt) /* all reachable predecessors */
2206 { g = fsm_tbl[t->to];
2208 for (i = 0; i < a->nwords; i++)
2209 ndom[i] &= g->dom[i]; /* (5b) */
2213 j = f->from % BPW; /* assert(j <= 32); */
2214 ndom[i] |= (1<<j); /* (5a) */
2216 for (i = 0; i < a->nwords; i++)
2217 if (f->dom[i] != ndom[i])
2219 f->dom[i] = ndom[i];
2230 init_dom(a); /* (1,2) */
2233 for (f = a->fsm; f; f = f->nxt)
2235 && f->from != a->i_st) /* (4) */
2236 cnt += dom_perculate(a, f); /* (5) */
2238 } while (cnt); /* (3) */
2239 dom_perculate(a, fsm_tbl[a->i_st]);
2248 static FSM_state no_state;
2251 Aho, Sethi, & Ullman, Compilers - principles, techniques, and tools
2252 Addison-Wesley, 1986, p.671.
2255 (2) for s in S - {s0} do D(s) = S
2257 (3) while any D(s) changes do
2258 (4) for s in S - {s0} do
2259 (5) D(s) = {s} union with intersection of all D(p)
2260 where p are the immediate predecessors of s
2262 the purpose is to find proper subgraphs
2263 (one entry node, one exit node)
2265 if (AST_Round == 1) /* computed once, reused in every round */
2266 for (a = ast; a; a = a->nxt)
2268 for (f = a->fsm; f; f = f->nxt)
2269 { a->nstates++; /* count */
2270 fsm_tbl[f->from] = f; /* fast lookup */
2271 f->scratch = 0; /* clear scratch marks */
2273 for (oi = 0; oi < a->nstates; oi++)
2275 fsm_tbl[oi] = &no_state;
2277 a->nwords = (a->nstates + BPW - 1) / BPW; /* round up */
2280 { printf("%s (%d): ", a->p->n->name, a->i_st);
2281 printf("states=%d (max %d), words = %d, bpw %d, overflow %d\n",
2282 a->nstates, o_max, a->nwords,
2283 (int) BPW, (int) (a->nstates % BPW));
2287 dom_forward(a); /* forward dominance relation */
2289 curtail(a); /* mark ineligible edges */
2290 for (f = a->fsm; f; f = f->nxt)
2293 f->t = t; /* invert edges */
2296 f->dom = (ulong *) 0;
2299 if (fsm_tbl[0]->seen) /* end-state reachable - else leave it */
2300 a->i_st = 0; /* becomes initial state */
2302 dom_forward(a); /* reverse dominance -- don't redo reachability! */
2303 act_dom(a); /* mark proper subgraphs, if any */
2304 AST_checkpairs(a); /* selectively place 2 scratch-marks */
2306 for (f = a->fsm; f; f = f->nxt)
2309 f->t = t; /* restore */
2311 a->i_st = oi; /* restore */
2313 for (a = ast; a; a = a->nxt)
2314 { for (f = a->fsm; f; f = f->nxt)
2315 { fsm_tbl[f->from] = f;
2316 f->scratch &= 1; /* preserve 1-marks */
2318 for (oi = 0; oi < a->nstates; oi++)
2320 fsm_tbl[oi] = &no_state;
2322 curtail(a); /* mark ineligible edges */
2324 for (f = a->fsm; f; f = f->nxt)
2327 f->t = t; /* invert edges */
2330 AST_checkpairs(a); /* recompute 2-marks */
2332 for (f = a->fsm; f; f = f->nxt)
2335 f->t = t; /* restore */