/***** tl_spin: tl_buchi.c *****/ /* Copyright (c) 1995-2003 by Lucent Technologies, Bell Laboratories. */ /* All Rights Reserved. This software is for educational purposes only. */ /* No guarantee whatsoever is expressed or implied by the distribution of */ /* this code. Permission is given to distribute this code provided that */ /* this introductory message is not removed and no monies are exchanged. */ /* Software written by Gerard J. Holzmann. For tool documentation see: */ /* http://spinroot.com/ */ /* Send all bug-reports and/or questions to: bugs@spinroot.com */ /* Based on the translation algorithm by Gerth, Peled, Vardi, and Wolper, */ /* presented at the PSTV Conference, held in 1995, Warsaw, Poland 1995. */ #include "tl.h" extern int tl_verbose, tl_clutter, Total, Max_Red; extern char *claim_name; FILE *tl_out; /* if standalone: = stdout; */ typedef struct Transition { Symbol *name; Node *cond; int redundant, merged, marked; struct Transition *nxt; } Transition; typedef struct State { Symbol *name; Transition *trans; Graph *colors; unsigned char redundant; unsigned char accepting; unsigned char reachable; struct State *nxt; } State; static State *never = (State *) 0; static int hitsall; void ini_buchi(void) { never = (State *) 0; hitsall = 0; } static int sametrans(Transition *s, Transition *t) { if (strcmp(s->name->name, t->name->name) != 0) return 0; return isequal(s->cond, t->cond); } static Node * Prune(Node *p) { if (p) switch (p->ntyp) { case PREDICATE: case NOT: case FALSE: case TRUE: #ifdef NXT case NEXT: #endif return p; case OR: p->lft = Prune(p->lft); if (!p->lft) { releasenode(1, p->rgt); return ZN; } p->rgt = Prune(p->rgt); if (!p->rgt) { releasenode(1, p->lft); return ZN; } return p; case AND: p->lft = Prune(p->lft); if (!p->lft) return Prune(p->rgt); p->rgt = Prune(p->rgt); if (!p->rgt) return p->lft; return p; } releasenode(1, p); return ZN; } static State * findstate(char *nm) { State *b; for (b = never; b; b = b->nxt) if (!strcmp(b->name->name, nm)) return b; if (strcmp(nm, "accept_all")) { if (strncmp(nm, "accept", 6)) { int i; char altnm[64]; for (i = 0; i < 64; i++) if (nm[i] == '_') break; if (i >= 64) Fatal("name too long %s", nm); sprintf(altnm, "accept%s", &nm[i]); return findstate(altnm); } /* Fatal("buchi: no state %s", nm); */ } return (State *) 0; } static void Dfs(State *b) { Transition *t; if (!b || b->reachable) return; b->reachable = 1; if (b->redundant) printf("/* redundant state %s */\n", b->name->name); for (t = b->trans; t; t = t->nxt) { if (!t->redundant) { Dfs(findstate(t->name->name)); if (!hitsall && strcmp(t->name->name, "accept_all") == 0) hitsall = 1; } } } void retarget(char *from, char *to) { State *b; Transition *t; Symbol *To = tl_lookup(to); if (tl_verbose) printf("replace %s with %s\n", from, to); for (b = never; b; b = b->nxt) { if (!strcmp(b->name->name, from)) b->redundant = 1; else for (t = b->trans; t; t = t->nxt) { if (!strcmp(t->name->name, from)) t->name = To; } } } #ifdef NXT static Node * nonxt(Node *n) { switch (n->ntyp) { case U_OPER: case V_OPER: case NEXT: return ZN; case OR: n->lft = nonxt(n->lft); n->rgt = nonxt(n->rgt); if (!n->lft || !n->rgt) return True; return n; case AND: n->lft = nonxt(n->lft); n->rgt = nonxt(n->rgt); if (!n->lft) { if (!n->rgt) n = ZN; else n = n->rgt; } else if (!n->rgt) n = n->lft; return n; } return n; } #endif static Node * combination(Node *s, Node *t) { Node *nc; #ifdef NXT Node *a = nonxt(s); Node *b = nonxt(t); if (tl_verbose) { printf("\tnonxtA: "); dump(a); printf("\n\tnonxtB: "); dump(b); printf("\n"); } /* if there's only a X(f), its equivalent to true */ if (!a || !b) nc = True; else nc = tl_nn(OR, a, b); #else nc = tl_nn(OR, s, t); #endif if (tl_verbose) { printf("\tcombo: "); dump(nc); printf("\n"); } return nc; } Node * unclutter(Node *n, char *snm) { Node *t, *s, *v, *u; Symbol *w; /* check only simple cases like !q && q */ for (t = n; t; t = t->rgt) { if (t->rgt) { if (t->ntyp != AND || !t->lft) return n; if (t->lft->ntyp != PREDICATE #ifdef NXT && t->lft->ntyp != NEXT #endif && t->lft->ntyp != NOT) return n; } else { if (t->ntyp != PREDICATE #ifdef NXT && t->ntyp != NEXT #endif && t->ntyp != NOT) return n; } } for (t = n; t; t = t->rgt) { if (t->rgt) v = t->lft; else v = t; if (v->ntyp == NOT && v->lft->ntyp == PREDICATE) { w = v->lft->sym; for (s = n; s; s = s->rgt) { if (s == t) continue; if (s->rgt) u = s->lft; else u = s; if (u->ntyp == PREDICATE && strcmp(u->sym->name, w->name) == 0) { if (tl_verbose) { printf("BINGO %s:\t", snm); dump(n); printf("\n"); } return False; } } } } return n; } static void clutter(void) { State *p; Transition *s; for (p = never; p; p = p->nxt) for (s = p->trans; s; s = s->nxt) { s->cond = unclutter(s->cond, p->name->name); if (s->cond && s->cond->ntyp == FALSE) { if (s != p->trans || s->nxt) s->redundant = 1; } } } static void showtrans(State *a) { Transition *s; for (s = a->trans; s; s = s->nxt) { printf("%s ", s->name?s->name->name:"-"); dump(s->cond); printf(" %d %d %d\n", s->redundant, s->merged, s->marked); } } static int mergetrans(void) { State *b; Transition *s, *t; Node *nc; int cnt = 0; for (b = never; b; b = b->nxt) { if (!b->reachable) continue; for (s = b->trans; s; s = s->nxt) { if (s->redundant) continue; for (t = s->nxt; t; t = t->nxt) if (!t->redundant && !strcmp(s->name->name, t->name->name)) { if (tl_verbose) { printf("===\nstate %s, trans to %s redundant\n", b->name->name, s->name->name); showtrans(b); printf(" conditions "); dump(s->cond); printf(" <-> "); dump(t->cond); printf("\n"); } if (!s->cond) /* same as T */ { releasenode(1, t->cond); /* T or t */ nc = True; } else if (!t->cond) { releasenode(1, s->cond); nc = True; } else { nc = combination(s->cond, t->cond); } t->cond = rewrite(nc); t->merged = 1; s->redundant = 1; cnt++; break; } } } return cnt; } static int all_trans_match(State *a, State *b) { Transition *s, *t; int found, result = 0; if (a->accepting != b->accepting) goto done; for (s = a->trans; s; s = s->nxt) { if (s->redundant) continue; found = 0; for (t = b->trans; t; t = t->nxt) { if (t->redundant) continue; if (sametrans(s, t)) { found = 1; t->marked = 1; break; } } if (!found) goto done; } for (s = b->trans; s; s = s->nxt) { if (s->redundant || s->marked) continue; found = 0; for (t = a->trans; t; t = t->nxt) { if (t->redundant) continue; if (sametrans(s, t)) { found = 1; break; } } if (!found) goto done; } result = 1; done: for (s = b->trans; s; s = s->nxt) s->marked = 0; return result; } #ifndef NO_OPT #define BUCKY #endif #ifdef BUCKY static int all_bucky(State *a, State *b) { Transition *s, *t; int found, result = 0; for (s = a->trans; s; s = s->nxt) { if (s->redundant) continue; found = 0; for (t = b->trans; t; t = t->nxt) { if (t->redundant) continue; if (isequal(s->cond, t->cond)) { if (strcmp(s->name->name, b->name->name) == 0 && strcmp(t->name->name, a->name->name) == 0) { found = 1; /* they point to each other */ t->marked = 1; break; } if (strcmp(s->name->name, t->name->name) == 0 && strcmp(s->name->name, "accept_all") == 0) { found = 1; t->marked = 1; break; /* same exit from which there is no return */ } } } if (!found) goto done; } for (s = b->trans; s; s = s->nxt) { if (s->redundant || s->marked) continue; found = 0; for (t = a->trans; t; t = t->nxt) { if (t->redundant) continue; if (isequal(s->cond, t->cond)) { if (strcmp(s->name->name, a->name->name) == 0 && strcmp(t->name->name, b->name->name) == 0) { found = 1; t->marked = 1; break; } if (strcmp(s->name->name, t->name->name) == 0 && strcmp(s->name->name, "accept_all") == 0) { found = 1; t->marked = 1; break; } } } if (!found) goto done; } result = 1; done: for (s = b->trans; s; s = s->nxt) s->marked = 0; return result; } static int buckyballs(void) { State *a, *b, *c, *d; int m, cnt=0; do { m = 0; cnt++; for (a = never; a; a = a->nxt) { if (!a->reachable) continue; if (a->redundant) continue; for (b = a->nxt; b; b = b->nxt) { if (!b->reachable) continue; if (b->redundant) continue; if (all_bucky(a, b)) { m++; if (tl_verbose) { printf("%s bucky match %s\n", a->name->name, b->name->name); } if (a->accepting && !b->accepting) { if (strcmp(b->name->name, "T0_init") == 0) { c = a; d = b; b->accepting = 1; } else { c = b; d = a; } } else { c = a; d = b; } retarget(c->name->name, d->name->name); if (!strncmp(c->name->name, "accept", 6) && Max_Red == 0) { char buf[64]; sprintf(buf, "T0%s", &(c->name->name[6])); retarget(buf, d->name->name); } break; } } } } while (m && cnt < 10); return cnt-1; } #endif static int mergestates(int v) { State *a, *b; int m, cnt=0; if (tl_verbose) return 0; do { m = 0; cnt++; for (a = never; a; a = a->nxt) { if (v && !a->reachable) continue; if (a->redundant) continue; /* 3.3.10 */ for (b = a->nxt; b; b = b->nxt) { if (v && !b->reachable) continue; if (b->redundant) continue; /* 3.3.10 */ if (all_trans_match(a, b)) { m++; if (tl_verbose) { printf("%d: state %s equals state %s\n", cnt, a->name->name, b->name->name); showtrans(a); printf("==\n"); showtrans(b); } retarget(a->name->name, b->name->name); if (!strncmp(a->name->name, "accept", 6) && Max_Red == 0) { char buf[64]; sprintf(buf, "T0%s", &(a->name->name[6])); retarget(buf, b->name->name); } break; } #if 0 else if (tl_verbose) { printf("\n%d: state %s differs from state %s [%d,%d]\n", cnt, a->name->name, b->name->name, a->accepting, b->accepting); showtrans(a); printf("==\n"); showtrans(b); printf("\n"); } #endif } } } while (m && cnt < 10); return cnt-1; } static int tcnt; static void rev_trans(Transition *t) /* print transitions in reverse order... */ { if (!t) return; rev_trans(t->nxt); if (t->redundant && !tl_verbose) return; fprintf(tl_out, "\t:: ("); if (dump_cond(t->cond, t->cond, 1)) fprintf(tl_out, "1"); fprintf(tl_out, ") -> goto %s\n", t->name->name); tcnt++; } static void printstate(State *b) { if (!b || (!tl_verbose && !b->reachable)) return; b->reachable = 0; /* print only once */ fprintf(tl_out, "%s:\n", b->name->name); if (tl_verbose) { fprintf(tl_out, " /* "); dump(b->colors->Other); fprintf(tl_out, " */\n"); } if (strncmp(b->name->name, "accept", 6) == 0 && Max_Red == 0) fprintf(tl_out, "T0%s:\n", &(b->name->name[6])); fprintf(tl_out, "\tif\n"); tcnt = 0; rev_trans(b->trans); if (!tcnt) fprintf(tl_out, "\t:: false\n"); fprintf(tl_out, "\tfi;\n"); Total++; } void addtrans(Graph *col, char *from, Node *op, char *to) { State *b; Transition *t; t = (Transition *) tl_emalloc(sizeof(Transition)); t->name = tl_lookup(to); t->cond = Prune(dupnode(op)); if (tl_verbose) { printf("\n%s <<\t", from); dump(op); printf("\n\t"); dump(t->cond); printf(">> %s\n", t->name->name); } if (t->cond) t->cond = rewrite(t->cond); for (b = never; b; b = b->nxt) if (!strcmp(b->name->name, from)) { t->nxt = b->trans; b->trans = t; return; } b = (State *) tl_emalloc(sizeof(State)); b->name = tl_lookup(from); b->colors = col; b->trans = t; if (!strncmp(from, "accept", 6)) b->accepting = 1; b->nxt = never; never = b; } static void clr_reach(void) { State *p; for (p = never; p; p = p->nxt) p->reachable = 0; hitsall = 0; } void fsm_print(void) { State *b; int cnt1, cnt2=0; extern void put_uform(void); if (tl_clutter) clutter(); b = findstate("T0_init"); if (b && (Max_Red == 0)) b->accepting = 1; mergestates(0); b = findstate("T0_init"); fprintf(tl_out, "never %s { /* ", claim_name?claim_name:""); put_uform(); fprintf(tl_out, " */\n"); do { clr_reach(); Dfs(b); cnt1 = mergetrans(); cnt2 = mergestates(1); if (tl_verbose) printf("/* >>%d,%d<< */\n", cnt1, cnt2); } while (cnt2 > 0); #ifdef BUCKY buckyballs(); clr_reach(); Dfs(b); #endif if (b && b->accepting) fprintf(tl_out, "accept_init:\n"); if (!b && !never) { fprintf(tl_out, " 0 /* false */;\n"); } else { printstate(b); /* init state must be first */ for (b = never; b; b = b->nxt) printstate(b); } if (hitsall) fprintf(tl_out, "accept_all:\n skip\n"); fprintf(tl_out, "}\n"); }