1 /***** spin: pangen6.c *****/
3 /* Copyright (c) 2000-2003 by Lucent Technologies, Bell Laboratories. */
4 /* All Rights Reserved. This software is for educational purposes only. */
5 /* No guarantee whatsoever is expressed or implied by the distribution of */
6 /* this code. Permission is given to distribute this code provided that */
7 /* this introductory message is not removed and no monies are exchanged. */
8 /* Software written by Gerard J. Holzmann. For tool documentation see: */
9 /* http://spinroot.com/ */
10 /* Send all bug-reports and/or questions to: bugs@spinroot.com */
12 /* Abstract syntax tree analysis / slicing (spin option -A) */
13 /* AST_store stores the fsms's for each proctype */
14 /* AST_track keeps track of variables used in properties */
15 /* AST_slice starts the slicing algorithm */
16 /* it first collects more info and then calls */
17 /* AST_criteria to process the slice criteria */
22 extern Ordered *all_names;
23 extern FSM_use *use_free;
24 extern FSM_state **fsm_tbl;
25 extern FSM_state *fsm;
26 extern int verbose, o_max;
28 static FSM_trans *cur_t;
29 static FSM_trans *expl_par;
30 static FSM_trans *expl_var;
31 static FSM_trans *explicit;
33 extern void rel_use(FSM_use *);
35 #define ulong unsigned long
44 ProcList *p; /* proctype decl */
45 int i_st; /* start state */
48 Pair *pairs; /* entry and exit nodes of proper subgraphs */
49 FSM_state *fsm; /* proctype body */
50 struct AST *nxt; /* linked list */
53 typedef struct RPN { /* relevant proctype names */
58 typedef struct ALIAS { /* channel aliasing info */
59 Lextok *cnm; /* this chan */
60 int origin; /* debugging - origin of the alias */
61 struct ALIAS *alias; /* can be an alias for these other chans */
62 struct ALIAS *nxt; /* linked list */
65 typedef struct ChanList {
66 Lextok *s; /* containing stmnt */
67 Lextok *n; /* point of reference - could be struct */
68 struct ChanList *nxt; /* linked list */
71 /* a chan alias can be created in one of three ways:
72 assignement to chan name
73 a = b -- a is now an alias for b
74 passing chan name as parameter in run
75 run x(b) -- proctype x(chan a)
76 passing chan name through channel
86 static ALIAS *chalcur;
87 static ALIAS *chalias;
88 static ChanList *chanlist;
89 static Slicer *slicer;
90 static Slicer *rel_vars; /* all relevant variables */
91 static int AST_Changes;
93 static FSM_state no_state;
95 static int in_recv = 0;
97 static int AST_mutual(Lextok *, Lextok *, int);
98 static void AST_dominant(void);
99 static void AST_hidden(void);
100 static void AST_setcur(Lextok *);
101 static void check_slice(Lextok *, int);
102 static void curtail(AST *);
103 static void def_use(Lextok *, int);
104 static void name_AST_track(Lextok *, int);
105 static void show_expl(void);
108 AST_isini(Lextok *n) /* is this an initialized channel */
111 if (!n || !n->sym) return 0;
116 return (s->ini->ntyp == CHAN); /* freshly instantiated */
118 if (s->type == STRUCT && n->rgt)
119 return AST_isini(n->rgt->lft);
125 AST_var(Lextok *n, Symbol *s, int toplevel)
130 { if (s->context && s->type)
131 printf(":%s:L:", s->context->name);
135 printf("%s", s->name); /* array indices ignored */
137 if (s->type == STRUCT && n && n->rgt && n->rgt->lft)
139 AST_var(n->rgt->lft, n->rgt->lft->sym, 0);
144 name_def_indices(Lextok *n, int code)
146 if (!n || !n->sym) return;
148 if (n->sym->nel != 1)
149 def_use(n->lft, code); /* process the index */
151 if (n->sym->type == STRUCT /* and possible deeper ones */
153 name_def_indices(n->rgt->lft, code);
157 name_def_use(Lextok *n, int code)
165 { switch (cur_t->step->n->ntyp) {
166 case 'c': /* possible predicate abstraction? */
167 n->sym->colnr |= 2; /* yes */
170 n->sym->colnr |= 1; /* no */
175 for (u = cur_t->Val[0]; u; u = u->nxt)
176 if (AST_mutual(n, u->n, 1)
177 && u->special == code)
182 use_free = use_free->nxt;
184 u = (FSM_use *) emalloc(sizeof(FSM_use));
188 u->nxt = cur_t->Val[0];
191 name_def_indices(n, USE|(code&(~DEF))); /* not def, but perhaps deref */
195 def_use(Lextok *now, int code)
207 def_use(now->lft, USE|code);
215 def_use(now->lft, DEREF_USE|USE|code);
236 def_use(now->lft, USE|code);
237 def_use(now->rgt, USE|code);
241 def_use(now->lft, DEF|code);
242 def_use(now->rgt, USE|code);
245 case TYPE: /* name in parameter list */
246 name_def_use(now, code);
250 name_def_use(now, code);
254 name_def_use(now, USE); /* procname - not really needed */
255 for (v = now->lft; v; v = v->rgt)
256 def_use(v->lft, USE); /* params */
260 def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
261 for (v = now->rgt; v; v = v->rgt)
262 def_use(v->lft, USE|code);
266 def_use(now->lft, DEREF_DEF|DEREF_USE|USE|code);
267 for (v = now->rgt; v; v = v->rgt)
268 { if (v->lft->ntyp == EVAL)
269 def_use(v->lft, code); /* will add USE */
270 else if (v->lft->ntyp != CONST)
271 def_use(v->lft, DEF|code);
276 def_use(now->lft, DEREF_USE|USE|code);
277 for (v = now->rgt; v; v = v->rgt)
278 { if (v->lft->ntyp == EVAL)
279 def_use(v->lft, code); /* will add USE */
284 def_use(now->lft, USE|code);
286 { def_use(now->rgt->lft, code);
287 def_use(now->rgt->rgt, code);
292 for (v = now->lft; v; v = v->rgt)
293 def_use(v->lft, USE|code);
297 def_use(now->lft, USE);
327 AST_add_alias(Lextok *n, int nr)
331 for (ca = chalcur->alias; ca; ca = ca->nxt)
332 if (AST_mutual(ca->cnm, n, 1))
333 { res = (ca->origin&nr);
334 ca->origin |= nr; /* 1, 2, or 4 - run, asgn, or rcv */
335 return (res == 0); /* 0 if already there with same origin */
338 ca = (ALIAS *) emalloc(sizeof(ALIAS));
341 ca->nxt = chalcur->alias;
347 AST_run_alias(char *pn, char *s, Lextok *t, int parno)
354 { if (strcmp(t->sym->name, s) == 0)
355 for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
357 { AST_add_alias(v->lft, 1); /* RUN */
361 { AST_run_alias(pn, s, t->lft, parno);
362 AST_run_alias(pn, s, t->rgt, parno);
367 AST_findrun(char *s, int parno)
372 for (a = ast; a; a = a->nxt) /* automata */
373 for (f = a->fsm; f; f = f->nxt) /* control states */
374 for (t = f->t; t; t = t->nxt) /* transitions */
376 AST_run_alias(a->p->n->name, s, t->step->n, parno);
381 AST_par_chans(ProcList *p) /* find local chan's init'd to chan passed as param */
385 for (walk = all_names; walk; walk = walk->next)
389 && strcmp(sp->context->name, p->n->name) == 0
390 && sp->Nid >= 0 /* not itself a param */
392 && sp->ini->ntyp == NAME) /* != CONST and != CHAN */
393 { Lextok *x = nn(ZN, 0, ZN, ZN);
396 AST_add_alias(sp->ini, 2); /* ASGN */
401 AST_para(ProcList *p)
407 for (f = p->p; f; f = f->rgt) /* list of types */
408 for (t = f->lft; t; t = t->rgt)
409 { if (t->ntyp != ',')
412 c = t->lft; /* expanded struct */
415 if (Sym_typ(c) == CHAN)
416 { ALIAS *na = (ALIAS *) emalloc(sizeof(ALIAS));
420 chalcur = chalias = na;
422 printf("%s -- (par) -- ", p->n->name);
423 AST_var(c, c->sym, 1);
426 AST_findrun(p->n->name, cnt);
435 AST_haschan(Lextok *c)
438 if (Sym_typ(c) == CHAN)
439 { AST_add_alias(c, 2); /* ASGN */
442 AST_var(c, c->sym, 1);
446 { AST_haschan(c->rgt);
452 AST_nrpar(Lextok *n) /* 's' or 'r' */
456 for (m = n->rgt; m; m = m->rgt)
462 AST_ord(Lextok *n, Lextok *s)
466 for (m = n->rgt; m; m = m->rgt)
468 if (s->sym == m->lft->sym)
476 AST_ownership(Symbol *s)
479 printf("%s:", s->name);
480 AST_ownership(s->owner);
485 AST_mutual(Lextok *a, Lextok *b, int toplevel)
488 if (!a && !b) return 1;
490 if (!a || !b) return 0;
495 if (!as || !bs) return 0;
497 if (toplevel && as->context != bs->context)
500 if (as->type != bs->type)
503 if (strcmp(as->name, bs->name) != 0)
506 if (as->type == STRUCT && a->rgt && b->rgt)
507 return AST_mutual(a->rgt->lft, b->rgt->lft, 0);
513 AST_setcur(Lextok *n) /* set chalcur */
516 for (ca = chalias; ca; ca = ca->nxt)
517 if (AST_mutual(ca->cnm, n, 1)) /* if same chan */
522 ca = (ALIAS *) emalloc(sizeof(ALIAS));
525 chalcur = chalias = ca;
529 AST_other(AST *a) /* check chan params in asgns and recvs */
535 for (f = a->fsm; f; f = f->nxt) /* control states */
536 for (t = f->t; t; t = t->nxt) /* transitions */
537 for (u = t->Val[0]; u; u = u->nxt) /* def/use info */
538 if (Sym_typ(u->n) == CHAN
539 && (u->special&DEF)) /* def of chan-name */
541 switch (t->step->n->ntyp) {
543 AST_haschan(t->step->n->rgt);
546 /* guess sends where name may originate */
547 for (cl = chanlist; cl; cl = cl->nxt) /* all sends */
548 { int a = AST_nrpar(cl->s);
549 int b = AST_nrpar(t->step->n);
550 if (a != b) /* matching nrs of params */
553 a = AST_ord(cl->s, cl->n);
554 b = AST_ord(t->step->n, u->n);
555 if (a != b) /* same position in parlist */
558 AST_add_alias(cl->n, 4); /* RCV assume possible match */
562 printf("type = %d\n", t->step->n->ntyp);
563 non_fatal("unexpected chan def type", (char *) 0);
572 for (na = chalias; na; na = na->nxt)
573 { printf("\npossible aliases of ");
574 AST_var(na->cnm, na->cnm->sym, 1);
576 for (ca = na->alias; ca; ca = ca->nxt)
578 printf("no valid name ");
580 AST_var(ca->cnm, ca->cnm->sym, 1);
582 if (ca->origin & 1) printf("RUN ");
583 if (ca->origin & 2) printf("ASGN ");
584 if (ca->origin & 4) printf("RCV ");
585 printf("[%s]", AST_isini(ca->cnm)?"Initzd":"Name");
587 if (ca->nxt) printf(", ");
595 AST_indirect(FSM_use *uin, FSM_trans *t, char *cause, char *pn)
598 /* this is a newly discovered relevant statement */
599 /* all vars it uses to contribute to its DEF are new criteria */
601 if (!(t->relevant&1)) AST_Changes++;
603 t->round = AST_Round;
606 if ((verbose&32) && t->step)
607 { printf("\tDR %s [[ ", pn);
608 comment(stdout, t->step->n, 0);
609 printf("]]\n\t\tfully relevant %s", cause);
610 if (uin) { printf(" due to "); AST_var(uin->n, uin->n->sym, 1); }
613 for (u = t->Val[0]; u; u = u->nxt)
615 && (u->special&(USE|DEREF_USE)))
617 { printf("\t\t\tuses(%d): ", u->special);
618 AST_var(u->n, u->n->sym, 1);
621 name_AST_track(u->n, u->special); /* add to slice criteria */
626 def_relevant(char *pn, FSM_trans *t, Lextok *n, int ischan)
631 /* look for all DEF's of n
632 * mark those stmnts relevant
633 * mark all var USEs in those stmnts as criteria
637 for (u = t->Val[0]; u; u = u->nxt)
638 { chanref = (Sym_typ(u->n) == CHAN);
640 if (ischan != chanref /* no possible match */
641 || !(u->special&(DEF|DEREF_DEF))) /* not a def */
644 if (AST_mutual(u->n, n, 1))
645 { AST_indirect(u, t, "(exact match)", pn);
650 for (na = chalias; na; na = na->nxt)
651 { if (!AST_mutual(u->n, na->cnm, 1))
653 for (ca = na->alias; ca; ca = ca->nxt)
654 if (AST_mutual(ca->cnm, n, 1)
655 && AST_isini(ca->cnm))
656 { AST_indirect(u, t, "(alias match)", pn);
664 AST_relevant(Lextok *n)
670 /* look for all DEF's of n
671 * mark those stmnts relevant
672 * mark all var USEs in those stmnts as criteria
676 ischan = (Sym_typ(n) == CHAN);
679 { printf("<<ast_relevant (ntyp=%d) ", n->ntyp);
680 AST_var(n, n->sym, 1);
684 for (t = expl_par; t; t = t->nxt) /* param assignments */
685 { if (!(t->relevant&1))
686 def_relevant(":params:", t, n, ischan);
689 for (t = expl_var; t; t = t->nxt)
690 { if (!(t->relevant&1)) /* var inits */
691 def_relevant(":vars:", t, n, ischan);
694 for (a = ast; a; a = a->nxt) /* all other stmnts */
695 { if (strcmp(a->p->n->name, ":never:") != 0
696 && strcmp(a->p->n->name, ":trace:") != 0
697 && strcmp(a->p->n->name, ":notrace:") != 0)
698 for (f = a->fsm; f; f = f->nxt)
699 for (t = f->t; t; t = t->nxt)
700 { if (!(t->relevant&1))
701 def_relevant(a->p->n->name, t, n, ischan);
710 for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
711 for (t = T; t; t = t->nxt)
713 for (u = t->Val[0]; u; u = u->nxt)
714 { if (u->n->sym->type
715 && u->n->sym->context
716 && strcmp(u->n->sym->context->name, s) == 0)
719 { printf("proctype %s relevant, due to symbol ", s);
720 AST_var(u->n, u->n->sym, 1);
733 for (r = rpn; r; r = r->nxt)
734 { for (a = ast; a; a = a->nxt)
735 if (strcmp(a->p->n->name, r->rn->name) == 0)
740 fatal("cannot find proctype %s", r->rn->name);
745 AST_procisrelevant(Symbol *s)
747 for (r = rpn; r; r = r->nxt)
748 if (strcmp(r->rn->name, s->name) == 0)
750 r = (RPN *) emalloc(sizeof(RPN));
757 AST_proc_isrel(char *s)
760 for (a = ast; a; a = a->nxt)
761 if (strcmp(a->p->n->name, s) == 0)
762 return (a->relevant&1);
763 non_fatal("cannot happen, missing proc in ast", (char *) 0);
768 AST_scoutrun(Lextok *t)
773 return AST_proc_isrel(t->sym->name);
774 return (AST_scoutrun(t->lft) || AST_scoutrun(t->rgt));
783 /* if any stmnt inside a proctype is relevant
784 * or any parameter passed in a run
785 * then so are all the run statements on that proctype
788 for (a = ast; a; a = a->nxt)
789 { if (strcmp(a->p->n->name, ":never:") == 0
790 || strcmp(a->p->n->name, ":trace:") == 0
791 || strcmp(a->p->n->name, ":notrace:") == 0
792 || strcmp(a->p->n->name, ":init:") == 0)
793 { a->relevant |= 1; /* the proctype is relevant */
796 if (AST_relpar(a->p->n->name))
799 { for (f = a->fsm; f; f = f->nxt)
800 for (t = f->t; t; t = t->nxt)
808 for (a = ast; a; a = a->nxt)
809 for (f = a->fsm; f; f = f->nxt)
810 for (t = f->t; t; t = t->nxt)
812 && AST_scoutrun(t->step->n))
813 { AST_indirect((FSM_use *)0, t, ":run:", a->p->n->name);
814 /* BUT, not all actual params are relevant */
819 AST_report(AST *a, Element *e) /* ALSO deduce irrelevant vars */
821 if (!(a->relevant&2))
823 printf("spin: redundant in proctype %s (for given property):\n",
826 printf(" line %3d %s (state %d)",
828 e->n?e->n->fn->name:"-",
831 comment(stdout, e->n, 0);
836 AST_always(Lextok *n)
840 if (n->ntyp == '@' /* -end */
841 || n->ntyp == 'p') /* remote reference */
843 return AST_always(n->lft) || AST_always(n->rgt);
847 AST_edge_dump(AST *a, FSM_state *f)
851 for (t = f->t; t; t = t->nxt) /* edges */
853 if (t->step && AST_always(t->step->n))
854 t->relevant |= 1; /* always relevant */
857 { switch (t->relevant) {
858 case 0: printf(" "); break;
859 case 1: printf("*%3d ", t->round); break;
860 case 2: printf("+%3d ", t->round); break;
861 case 3: printf("#%3d ", t->round); break;
862 default: printf("? "); break;
865 printf("%d\t->\t%d\t", f->from, t->to);
867 comment(stdout, t->step->n, 0);
871 for (u = t->Val[0]; u; u = u->nxt)
873 AST_var(u->n, u->n->sym, 1);
874 printf(":%d>", u->special);
882 switch(t->step->n->ntyp) {
887 if (t->step->n->lft->ntyp != CONST)
888 AST_report(a, t->step);
891 case PRINT: /* don't report */
902 AST_dfs(AST *a, int s, int vis)
910 if (vis) AST_edge_dump(a, f);
912 for (t = f->t; t; t = t->nxt)
913 AST_dfs(a, t->to, vis);
920 for (f = a->fsm; f; f = f->nxt)
922 fsm_tbl[f->from] = f;
926 printf("AST_START %s from %d\n", a->p->n->name, a->i_st);
928 AST_dfs(a, a->i_st, 1);
938 for (f = a->fsm; f; f = f->nxt) /* control states */
939 for (t = f->t; t; t = t->nxt) /* transitions */
942 && t->step->n->ntyp == 's')
943 for (u = t->Val[0]; u; u = u->nxt)
944 { if (Sym_typ(u->n) == CHAN
945 && ((u->special&USE) && !(u->special&DEREF_USE)))
948 printf("%s -- (%d->%d) -- ",
949 a->p->n->name, f->from, t->to);
950 AST_var(u->n, u->n->sym, 1);
951 printf(" -> chanlist\n");
953 cl = (ChanList *) emalloc(sizeof(ChanList));
961 AST_alfind(Lextok *n)
964 for (na = chalias; na; na = na->nxt)
965 if (AST_mutual(na->cnm, n, 1))
972 { ALIAS *na, *ca, *da, *ea;
977 for (na = chalias; na; na = na->nxt)
979 for (ca = na->alias; ca; ca = ca->nxt)
980 { da = AST_alfind(ca->cnm);
982 for (ea = da->alias; ea; ea = ea->nxt)
983 { nchanges += AST_add_alias(ea->cnm,
984 ea->origin|ca->origin);
986 } while (nchanges > 0);
988 chalcur = (ALIAS *) 0;
996 for (f = a->fsm; f; f = f->nxt) /* control states */
997 for (t = f->t; t; t = t->nxt) /* all edges */
999 rel_use(t->Val[0]); /* redo Val; doesn't cover structs */
1001 t->Val[0] = t->Val[1] = (FSM_use *) 0;
1003 if (!t->step) continue;
1005 def_use(t->step->n, 0); /* def/use info, including structs */
1007 cur_t = (FSM_trans *) 0;
1011 name_AST_track(Lextok *n, int code)
1012 { extern int nr_errs;
1014 printf("AST_name: ");
1015 AST_var(n, n->sym, 1);
1016 printf(" -- %d\n", code);
1018 if (in_recv && (code&DEF) && (code&USE))
1019 { printf("spin: error: DEF and USE of same var in rcv stmnt: ");
1020 AST_var(n, n->sym, 1);
1021 printf(" -- %d\n", code);
1024 check_slice(n, code);
1028 AST_track(Lextok *now, int code) /* called from main.c */
1029 { Lextok *v; extern int export_ast;
1031 if (!export_ast) return;
1034 switch (now->ntyp) {
1040 AST_track(now->lft, DEREF_USE|USE|code);
1061 AST_track(now->rgt, USE|code);
1069 AST_track(now->lft, USE|code);
1073 AST_track(now->lft, USE|(code&(~DEF)));
1077 name_AST_track(now, code);
1078 if (now->sym->nel != 1)
1079 AST_track(now->lft, USE|code); /* index */
1083 AST_track(now->lft, DEREF_USE|USE|code);
1084 for (v = now->rgt; v; v = v->rgt)
1085 AST_track(v->lft, code); /* a deeper eval can add USE */
1089 AST_track(now->lft, USE|code);
1091 { AST_track(now->rgt->lft, code);
1092 AST_track(now->rgt->rgt, code);
1096 /* added for control deps: */
1098 name_AST_track(now, code);
1101 AST_track(now->lft, DEF|code);
1102 AST_track(now->rgt, USE|code);
1105 name_AST_track(now, USE);
1106 for (v = now->lft; v; v = v->rgt)
1107 AST_track(v->lft, USE|code);
1110 AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
1111 for (v = now->rgt; v; v = v->rgt)
1112 AST_track(v->lft, USE|code);
1115 AST_track(now->lft, DEREF_DEF|DEREF_USE|USE|code);
1116 for (v = now->rgt; v; v = v->rgt)
1118 AST_track(v->lft, DEF|code);
1123 for (v = now->lft; v; v = v->rgt)
1124 AST_track(v->lft, USE|code);
1127 AST_track(now->lft, USE);
1134 '?' -sym-> a (proctype)
1138 AST_track(now->lft->lft, USE|code);
1139 AST_procisrelevant(now->lft->sym);
1165 printf("AST_track, NOT EXPECTED ntyp: %d\n", now->ntyp);
1178 { printf("Relevant variables:\n");
1179 for (rv = rel_vars; rv; rv = rv->nxt)
1181 AST_var(rv->n, rv->n->sym, 1);
1186 for (rv = rel_vars; rv; rv = rv->nxt)
1187 rv->n->sym->setat = 1; /* mark it */
1189 for (walk = all_names; walk; walk = walk->next)
1193 && (s->type != MTYPE || s->ini->ntyp != CONST)
1194 && s->type != STRUCT /* report only fields */
1195 && s->type != PROCTYPE
1197 && sputtype(buf, s->type))
1200 printf("spin: redundant vars (for given property):\n");
1209 AST_suggestions(void)
1218 for (walk = all_names; walk; walk = walk->next)
1220 if (s->colnr == 2 /* only used in conditionals */
1224 || s->type == MTYPE))
1227 printf("spin: consider using predicate");
1228 printf(" abstraction to replace:\n");
1234 /* look for source and sink processes */
1236 for (a = ast; a; a = a->nxt) /* automata */
1238 for (f = a->fsm; f; f = f->nxt) /* control states */
1239 for (t = f->t; t; t = t->nxt) /* transitions */
1241 switch (t->step->n->ntyp) {
1269 no_good: if (banner == 1 || banner == 2)
1270 { printf("spin: proctype %s defines a %s process\n",
1272 banner==1?"source":"sink");
1274 } else if (banner == 3)
1275 { printf("spin: proctype %s mimics a buffer\n",
1281 { printf("\tto reduce complexity, consider merging the code of\n");
1282 printf("\teach source process into the code of its target\n");
1285 { printf("\tto reduce complexity, consider merging the code of\n");
1286 printf("\teach sink process into the code of its source\n");
1289 printf("\tto reduce complexity, avoid buffer processes\n");
1294 { Slicer *sc, *nx, *rv;
1296 for (sc = slicer; sc; sc = nx)
1302 for (rv = rel_vars; rv; rv = rv->nxt)
1303 if (AST_mutual(sc->n, rv->n, 1))
1306 if (!rv) /* not already there */
1307 { sc->nxt = rel_vars;
1314 check_slice(Lextok *n, int code)
1317 for (sc = slicer; sc; sc = sc->nxt)
1318 if (AST_mutual(sc->n, n, 1)
1319 && sc->code == code)
1320 return; /* already there */
1322 sc = (Slicer *) emalloc(sizeof(Slicer));
1335 /* mark all def-relevant transitions */
1336 for (sc = slicer; sc; sc = sc->nxt)
1339 { printf("spin: slice criterion ");
1340 AST_var(sc->n, sc->n->sym, 1);
1341 printf(" type=%d\n", Sym_typ(sc->n));
1343 AST_relevant(sc->n);
1345 AST_tagruns(); /* mark 'run's relevant if target proctype is relevant */
1349 AST_blockable(AST *a, int s)
1355 for (t = f->t; t; t = t->nxt)
1356 { if (t->relevant&2)
1359 if (t->step && t->step->n)
1360 switch (t->step->n->ntyp) {
1366 if (AST_blockable(a, t->to))
1367 { t->round = AST_Round;
1371 /* else fall through */
1375 else if (AST_blockable(a, t->to)) /* Unless */
1376 { t->round = AST_Round;
1385 AST_spread(AST *a, int s)
1391 for (t = f->t; t; t = t->nxt)
1392 { if (t->relevant&2)
1395 if (t->step && t->step->n)
1396 switch (t->step->n->ntyp) {
1402 AST_spread(a, t->to);
1405 t->round = AST_Round;
1410 { AST_spread(a, t->to);
1411 t->round = AST_Round;
1418 AST_notrelevant(Lextok *n)
1421 for (s = rel_vars; s; s = s->nxt)
1422 if (AST_mutual(s->n, n, 1))
1424 for (s = slicer; s; s = s->nxt)
1425 if (AST_mutual(s->n, n, 1))
1431 AST_withchan(Lextok *n)
1434 if (Sym_typ(n) == CHAN)
1436 return AST_withchan(n->lft) || AST_withchan(n->rgt);
1440 AST_suspect(FSM_trans *t)
1442 /* check for possible overkill */
1443 if (!t || !t->step || !AST_withchan(t->step->n))
1445 for (u = t->Val[0]; u; u = u->nxt)
1446 if (AST_notrelevant(u->n))
1452 AST_shouldconsider(AST *a, int s)
1457 for (t = f->t; t; t = t->nxt)
1458 { if (t->step && t->step->n)
1459 switch (t->step->n->ntyp) {
1465 AST_shouldconsider(a, t->to);
1468 AST_track(t->step->n, 0);
1470 AST_track is called here for a blockable stmnt from which
1471 a relevant stmnmt was shown to be reachable
1472 for a condition this makes all USEs relevant
1473 but for a channel operation it only makes the executability
1474 relevant -- in those cases, parameters that aren't already
1475 relevant may be replaceable with arbitrary tokens
1478 { printf("spin: possibly redundant parameters in: ");
1479 comment(stdout, t->step->n, 0);
1484 else /* an Unless */
1485 AST_shouldconsider(a, t->to);
1490 FSM_critical(AST *a, int s)
1494 /* is a 1-relevant stmnt reachable from this state? */
1501 for (t = f->t; t; t = t->nxt)
1503 || FSM_critical(a, t->to))
1507 { printf("\t\t\t\tcritical(%d) ", t->relevant);
1508 comment(stdout, t->step->n, 0);
1516 { printf("\t\t\t\tnot-crit ");
1517 comment(stdout, t->step->n, 0);
1532 /* add all blockable transitions
1533 * from which relevant transitions can be reached
1536 printf("CTL -- %s\n", a->p->n->name);
1538 /* 1 : mark all blockable edges */
1539 for (f = a->fsm; f; f = f->nxt)
1540 { if (!(f->scratch&2)) /* not part of irrelevant subgraph */
1541 for (t = f->t; t; t = t->nxt)
1542 { if (t->step && t->step->n)
1543 switch (t->step->n->ntyp) {
1548 t->round = AST_Round;
1549 t->relevant |= 2; /* mark for next phases */
1551 { printf("\tpremark ");
1552 comment(stdout, t->step->n, 0);
1560 /* 2: keep only 2-marked stmnts from which 1-marked stmnts can be reached */
1561 for (f = a->fsm; f; f = f->nxt)
1562 { fsm_tbl[f->from] = f;
1563 f->seen = 0; /* used in dfs from FSM_critical */
1565 for (f = a->fsm; f; f = f->nxt)
1566 { if (!FSM_critical(a, f->from))
1567 for (t = f->t; t; t = t->nxt)
1569 { t->relevant &= ~2; /* clear mark */
1571 { printf("\t\tnomark ");
1572 comment(stdout, t->step->n, 0);
1576 /* 3 : lift marks across IF/DO etc. */
1577 for (f = a->fsm; f; f = f->nxt)
1579 for (t = f->t; t; t = t->nxt)
1580 { if (t->step && t->step->n)
1581 switch (t->step->n->ntyp) {
1587 if (AST_blockable(a, t->to))
1593 else if (AST_blockable(a, t->to)) /* Unless */
1598 if (hit) /* at least one outgoing trans can block */
1599 for (t = f->t; t; t = t->nxt)
1600 { t->round = AST_Round;
1601 t->relevant |= 2; /* lift */
1603 { printf("\t\t\tliftmark ");
1604 comment(stdout, t->step->n, 0);
1607 AST_spread(a, t->to); /* and spread to all guards */
1610 /* 4: nodes with 2-marked out-edges contribute new slice criteria */
1611 for (f = a->fsm; f; f = f->nxt)
1612 for (t = f->t; t; t = t->nxt)
1614 { AST_shouldconsider(a, f->from);
1615 break; /* inner loop */
1620 AST_control_dep(void)
1623 for (a = ast; a; a = a->nxt)
1624 if (strcmp(a->p->n->name, ":never:") != 0
1625 && strcmp(a->p->n->name, ":trace:") != 0
1626 && strcmp(a->p->n->name, ":notrace:") != 0)
1636 for (a = ast; a; a = a->nxt)
1637 { if (strcmp(a->p->n->name, ":never:") != 0
1638 && strcmp(a->p->n->name, ":trace:") != 0
1639 && strcmp(a->p->n->name, ":notrace:") != 0)
1640 for (f = a->fsm; f; f = f->nxt)
1641 for (t = f->t; t; t = t->nxt)
1644 && t->step->n->ntyp == ASSERT
1653 * remote labels are handled separately -- by making
1654 * sure they are not pruned away during optimization
1656 AST_Changes = 1; /* to get started */
1657 for (AST_Round = 1; slicer && AST_Changes; AST_Round++)
1660 AST_preserve(); /* moves processed vars from slicer to rel_vars */
1661 AST_dominant(); /* mark data-irrelevant subgraphs */
1662 AST_control_dep(); /* can add data deps, which add control deps */
1665 printf("\n\nROUND %d -- changes %d\n",
1666 AST_Round, AST_Changes);
1671 AST_alias_analysis(void) /* aliasing of promela channels */
1674 for (a = ast; a; a = a->nxt)
1675 AST_sends(a); /* collect chan-names that are send across chans */
1677 for (a = ast; a; a = a->nxt)
1678 AST_para(a->p); /* aliasing of chans thru proctype parameters */
1680 for (a = ast; a; a = a->nxt)
1681 AST_other(a); /* chan params in asgns and recvs */
1683 AST_trans(); /* transitive closure of alias table */
1686 AST_aliases(); /* show channel aliasing info */
1695 { non_fatal("no slice criteria (or no claim) specified",
1699 AST_dorelevant(); /* mark procs refered to in remote refs */
1701 for (a = ast; a; a = a->nxt)
1702 AST_def_use(a); /* compute standard def/use information */
1704 AST_hidden(); /* parameter passing and local var inits */
1706 AST_alias_analysis(); /* channel alias analysis */
1708 AST_prelabel(); /* mark all 'assert(...)' stmnts as relevant */
1709 AST_criteria(); /* process the slice criteria from
1710 * asserts and from the never claim
1712 if (!spurious || (verbose&32))
1714 for (a = ast; a; a = a->nxt)
1715 { AST_dump(a); /* marked up result */
1716 if (a->relevant&2) /* it printed something */
1719 if (!AST_dump_rel() /* relevant variables */
1721 printf("spin: no redundancies found (for given property)\n");
1730 AST_store(ProcList *p, int start_state)
1733 if (strcmp(p->n->name, ":never:") != 0
1734 && strcmp(p->n->name, ":trace:") != 0
1735 && strcmp(p->n->name, ":notrace:") != 0)
1736 { n_ast = (AST *) emalloc(sizeof(AST));
1738 n_ast->i_st = start_state;
1739 n_ast->relevant = 0;
1744 fsm = (FSM_state *) 0; /* hide it from FSM_DEL */
1748 AST_add_explicit(Lextok *d, Lextok *u)
1749 { FSM_trans *e = (FSM_trans *) emalloc(sizeof(FSM_trans));
1751 e->to = 0; /* or start_state ? */
1752 e->relevant = 0; /* to be determined */
1753 e->step = (Element *) 0; /* left blank */
1754 e->Val[0] = e->Val[1] = (FSM_use *) 0;
1761 cur_t = (FSM_trans *) 0;
1768 AST_fp1(char *s, Lextok *t, Lextok *f, int parno)
1775 { if (strcmp(t->sym->name, s) == 0)
1776 for (v = t->lft, cnt = 1; v; v = v->rgt, cnt++)
1778 { AST_add_explicit(f, v->lft);
1782 { AST_fp1(s, t->lft, f, parno);
1783 AST_fp1(s, t->rgt, f, parno);
1788 AST_mk1(char *s, Lextok *c, int parno)
1793 /* concoct an extra FSM_trans *t with the asgn of
1794 * formal par c to matching actual pars made explicit
1797 for (a = ast; a; a = a->nxt) /* automata */
1798 for (f = a->fsm; f; f = f->nxt) /* control states */
1799 for (t = f->t; t; t = t->nxt) /* transitions */
1801 AST_fp1(s, t->step->n, c, parno);
1806 AST_par_init(void) /* parameter passing -- hidden assignments */
1811 for (a = ast; a; a = a->nxt)
1812 { if (strcmp(a->p->n->name, ":never:") == 0
1813 || strcmp(a->p->n->name, ":trace:") == 0
1814 || strcmp(a->p->n->name, ":notrace:") == 0
1815 || strcmp(a->p->n->name, ":init:") == 0)
1816 continue; /* have no params */
1819 for (f = a->p->p; f; f = f->rgt) /* types */
1820 for (t = f->lft; t; t = t->rgt) /* formals */
1821 { cnt++; /* formal par count */
1822 c = (t->ntyp != ',')? t : t->lft; /* the formal parameter */
1823 AST_mk1(a->p->n->name, c, cnt); /* all matching run statements */
1828 AST_var_init(void) /* initialized vars (not chans) - hidden assignments */
1834 for (walk = all_names; walk; walk = walk->next)
1837 && !sp->context /* globals */
1838 && sp->type != PROCTYPE
1840 && (sp->type != MTYPE || sp->ini->ntyp != CONST) /* not mtype defs */
1841 && sp->ini->ntyp != CHAN)
1842 { x = nn(ZN, TYPE, ZN, ZN);
1844 AST_add_explicit(x, sp->ini);
1847 for (a = ast; a; a = a->nxt)
1848 { if (strcmp(a->p->n->name, ":never:") != 0
1849 && strcmp(a->p->n->name, ":trace:") != 0
1850 && strcmp(a->p->n->name, ":notrace:") != 0) /* claim has no locals */
1851 for (walk = all_names; walk; walk = walk->next)
1855 && strcmp(sp->context->name, a->p->n->name) == 0
1856 && sp->Nid >= 0 /* not a param */
1857 && sp->type != LABEL
1859 && sp->ini->ntyp != CHAN)
1860 { x = nn(ZN, TYPE, ZN, ZN);
1862 AST_add_explicit(x, sp->ini);
1871 printf("\nExplicit List:\n");
1872 for (T = expl_par; T; T = (T == expl_par)?expl_var: (FSM_trans *) 0)
1873 { for (t = T; t; t = t->nxt)
1874 { if (!t->Val[0]) continue;
1875 printf("%s", t->relevant?"*":" ");
1876 printf("%3d", t->round);
1877 for (u = t->Val[0]; u; u = u->nxt)
1879 AST_var(u->n, u->n->sym, 1);
1880 printf(":%d>, ", u->special);
1890 AST_hidden(void) /* reveal all hidden assignments */
1893 expl_par = explicit;
1894 explicit = (FSM_trans *) 0;
1897 expl_var = explicit;
1898 explicit = (FSM_trans *) 0;
1901 #define BPW (8*sizeof(ulong)) /* bits per word */
1904 bad_scratch(FSM_state *f, int upto)
1907 1. all internal branch-points have else-s
1908 2. all non-branchpoints have non-blocking out-edge
1909 3. all internal edges are non-relevant
1910 subgraphs like this need NOT contribute control-dependencies
1922 if (verbose&32) printf("X[%d:%d:%d] ", f->from, upto, f->scratch);
1926 printf("\tbad scratch: %d\n", f->from);
1927 bad: f->scratch &= ~4;
1928 /* f->scratch |= 8; wrong */
1932 if (f->from != upto)
1933 for (t = f->t; t; t = t->nxt)
1934 if (bad_scratch(fsm_tbl[t->to], upto))
1941 mark_subgraph(FSM_state *f, int upto)
1951 for (t = f->t; t; t = t->nxt)
1952 mark_subgraph(fsm_tbl[t->to], upto);
1956 AST_pair(AST *a, FSM_state *h, int y)
1959 for (p = a->pairs; p; p = p->nxt)
1964 p = (Pair *) emalloc(sizeof(Pair));
1972 AST_checkpairs(AST *a)
1975 for (p = a->pairs; p; p = p->nxt)
1977 printf(" inspect pair %d %d\n", p->b, p->h->from);
1978 if (!bad_scratch(p->h, p->b)) /* subgraph is clean */
1980 printf("subgraph: %d .. %d\n", p->b, p->h->from);
1981 mark_subgraph(p->h, p->b);
1987 subgraph(AST *a, FSM_state *f, int out)
1992 reverse dominance suggests that this is a possible
1993 entry and exit node for a proper subgraph
2002 printf("possible pair %d %d -- %d\n",
2003 f->from, h->from, (g[i]&(1<<j))?1:0);
2005 if (g[i]&(1<<j)) /* also a forward dominance pair */
2006 AST_pair(a, h, f->from); /* record this pair */
2015 for (f = a->fsm; f; f = f->nxt)
2016 { if (!f->seen) continue;
2018 f->from is the exit-node of a proper subgraph, with
2019 the dominator its entry-node, if:
2020 a. this node has more than 1 reachable predecessor
2021 b. the dominator has more than 1 reachable successor
2022 (need reachability - in case of reverse dominance)
2023 d. the dominator is reachable, and not equal to this node
2025 for (t = f->p, i = 0; t; t = t->nxt)
2026 i += fsm_tbl[t->to]->seen;
2027 if (i <= 1) continue; /* a. */
2029 for (cnt = 1; cnt < a->nstates; cnt++) /* 0 is endstate */
2030 { if (cnt == f->from
2031 || !fsm_tbl[cnt]->seen)
2036 if (!(f->dom[i]&(1<<j)))
2039 for (t = fsm_tbl[cnt]->t, i = 0; t; t = t->nxt)
2040 i += fsm_tbl[t->to]->seen;
2044 if (f->mod) /* final check in 2nd phase */
2045 subgraph(a, f, cnt); /* possible entry-exit pair */
2051 reachability(AST *a)
2054 for (f = a->fsm; f; f = f->nxt)
2055 f->seen = 0; /* clear */
2056 AST_dfs(a, a->i_st, 0); /* mark 'seen' */
2060 see_else(FSM_state *f)
2063 for (t = f->t; t; t = t->nxt)
2066 switch (t->step->n->ntyp) {
2074 if (see_else(fsm_tbl[t->to]))
2084 is_guard(FSM_state *f)
2088 for (t = f->p; t; t = t->nxt)
2089 { g = fsm_tbl[t->to];
2095 switch(t->step->n->ntyp) {
2115 int i, haselse, isrel, blocking;
2117 mark nodes that do not satisfy these requirements:
2118 1. all internal branch-points have else-s
2119 2. all non-branchpoints have non-blocking out-edge
2120 3. all internal edges are non-data-relevant
2123 printf("Curtail %s:\n", a->p->n->name);
2125 for (f = a->fsm; f; f = f->nxt)
2127 || (f->scratch&(1|2)))
2130 isrel = haselse = i = blocking = 0;
2132 for (t = f->t; t; t = t->nxt)
2133 { g = fsm_tbl[t->to];
2135 isrel |= (t->relevant&1); /* data relevant */
2140 { switch (t->step->n->ntyp) {
2143 haselse |= see_else(g);
2153 printf("prescratch %d -- %d %d %d %d -- %d\n",
2154 f->from, i, isrel, blocking, haselse, is_guard(f));
2157 || (i == 1 && blocking) /* 2. */
2158 || (i > 1 && !haselse)) /* 1. */
2162 printf("scratch %d -- %d %d %d %d\n",
2163 f->from, i, isrel, blocking, haselse);
2175 (2) for s in S - {s0} do D(s) = S
2178 for (f = a->fsm; f; f = f->nxt)
2179 { if (!f->seen) continue;
2182 emalloc(a->nwords * sizeof(ulong));
2184 if (f->from == a->i_st)
2185 { i = a->i_st / BPW;
2187 f->dom[i] = (1<<j); /* (1) */
2189 { for (i = 0; i < a->nwords; i++)
2190 f->dom[i] = (ulong) ~0; /* all 1's */
2192 if (a->nstates % BPW)
2193 for (i = (a->nstates % BPW); i < (int) BPW; i++)
2194 f->dom[a->nwords-1] &= ~(1<<i); /* clear tail */
2196 for (cnt = 0; cnt < a->nstates; cnt++)
2197 if (!fsm_tbl[cnt]->seen)
2200 f->dom[i] &= ~(1<<j);
2205 dom_perculate(AST *a, FSM_state *f)
2206 { static ulong *ndom = (ulong *) 0;
2215 emalloc(on * sizeof(ulong));
2218 for (i = 0; i < a->nwords; i++)
2219 ndom[i] = (ulong) ~0;
2221 for (t = f->p; t; t = t->nxt) /* all reachable predecessors */
2222 { g = fsm_tbl[t->to];
2224 for (i = 0; i < a->nwords; i++)
2225 ndom[i] &= g->dom[i]; /* (5b) */
2230 ndom[i] |= (1<<j); /* (5a) */
2232 for (i = 0; i < a->nwords; i++)
2233 if (f->dom[i] != ndom[i])
2235 f->dom[i] = ndom[i];
2246 init_dom(a); /* (1,2) */
2249 for (f = a->fsm; f; f = f->nxt)
2251 && f->from != a->i_st) /* (4) */
2252 cnt += dom_perculate(a, f); /* (5) */
2254 } while (cnt); /* (3) */
2255 dom_perculate(a, fsm_tbl[a->i_st]);
2266 Aho, Sethi, & Ullman, Compilers - principles, techniques, and tools
2267 Addison-Wesley, 1986, p.671.
2270 (2) for s in S - {s0} do D(s) = S
2272 (3) while any D(s) changes do
2273 (4) for s in S - {s0} do
2274 (5) D(s) = {s} union with intersection of all D(p)
2275 where p are the immediate predecessors of s
2277 the purpose is to find proper subgraphs
2278 (one entry node, one exit node)
2280 if (AST_Round == 1) /* computed once, reused in every round */
2281 for (a = ast; a; a = a->nxt)
2283 for (f = a->fsm; f; f = f->nxt)
2284 { a->nstates++; /* count */
2285 fsm_tbl[f->from] = f; /* fast lookup */
2286 f->scratch = 0; /* clear scratch marks */
2288 for (oi = 0; oi < a->nstates; oi++)
2290 fsm_tbl[oi] = &no_state;
2292 a->nwords = (a->nstates + BPW - 1) / BPW; /* round up */
2295 { printf("%s (%d): ", a->p->n->name, a->i_st);
2296 printf("states=%d (max %d), words = %d, bpw %d, overflow %d\n",
2297 a->nstates, o_max, a->nwords,
2298 (int) BPW, (int) (a->nstates % BPW));
2302 dom_forward(a); /* forward dominance relation */
2304 curtail(a); /* mark ineligible edges */
2305 for (f = a->fsm; f; f = f->nxt)
2308 f->t = t; /* invert edges */
2311 f->dom = (ulong *) 0;
2314 if (fsm_tbl[0]->seen) /* end-state reachable - else leave it */
2315 a->i_st = 0; /* becomes initial state */
2317 dom_forward(a); /* reverse dominance -- don't redo reachability! */
2318 act_dom(a); /* mark proper subgraphs, if any */
2319 AST_checkpairs(a); /* selectively place 2 scratch-marks */
2321 for (f = a->fsm; f; f = f->nxt)
2324 f->t = t; /* restore */
2326 a->i_st = oi; /* restore */
2328 for (a = ast; a; a = a->nxt)
2329 { for (f = a->fsm; f; f = f->nxt)
2330 { fsm_tbl[f->from] = f;
2331 f->scratch &= 1; /* preserve 1-marks */
2333 for (oi = 0; oi < a->nstates; oi++)
2335 fsm_tbl[oi] = &no_state;
2337 curtail(a); /* mark ineligible edges */
2339 for (f = a->fsm; f; f = f->nxt)
2342 f->t = t; /* invert edges */
2345 AST_checkpairs(a); /* recompute 2-marks */
2347 for (f = a->fsm; f; f = f->nxt)
2350 f->t = t; /* restore */