/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /***** tl_spin: tl_trans.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 FILE *tl_out; extern int tl_errs, tl_verbose, tl_terse, newstates, state_cnt; int Stack_mx=0, Max_Red=0, Total=0; static Mapping *Mapped = (Mapping *) 0; static Graph *Nodes_Set = (Graph *) 0; static Graph *Nodes_Stack = (Graph *) 0; static char dumpbuf[2048]; static int Red_cnt = 0; static int Lab_cnt = 0; static int Base = 0; static int Stack_sz = 0; static Graph *findgraph(char *); static Graph *pop_stack(void); static Node *Duplicate(Node *); static Node *flatten(Node *); static Symbol *catSlist(Symbol *, Symbol *); static Symbol *dupSlist(Symbol *); static char *newname(void); static int choueka(Graph *, int); static int not_new(Graph *); static int set_prefix(char *, int, Graph *); static void Addout(char *, char *); static void fsm_trans(Graph *, int, char *); static void mkbuchi(void); static void expand_g(Graph *); static void fixinit(Node *); static void liveness(Node *); static void mk_grn(Node *); static void mk_red(Node *); static void ng(Symbol *, Symbol *, Node *, Node *, Node *); static void push_stack(Graph *); static void sdump(Node *); void ini_trans(void) { Stack_mx = 0; Max_Red = 0; Total = 0; Mapped = (Mapping *) 0; Nodes_Set = (Graph *) 0; Nodes_Stack = (Graph *) 0; memset(dumpbuf, 0, sizeof(dumpbuf)); Red_cnt = 0; Lab_cnt = 0; Base = 0; Stack_sz = 0; } static void dump_graph(Graph *g) { Node *n1; printf("\n\tnew:\t"); for (n1 = g->New; n1; n1 = n1->nxt) { dump(n1); printf(", "); } printf("\n\told:\t"); for (n1 = g->Old; n1; n1 = n1->nxt) { dump(n1); printf(", "); } printf("\n\tnxt:\t"); for (n1 = g->Next; n1; n1 = n1->nxt) { dump(n1); printf(", "); } printf("\n\tother:\t"); for (n1 = g->Other; n1; n1 = n1->nxt) { dump(n1); printf(", "); } printf("\n"); } static void push_stack(Graph *g) { if (!g) return; g->nxt = Nodes_Stack; Nodes_Stack = g; if (tl_verbose) { Symbol *z; printf("\nPush %s, from ", g->name->name); for (z = g->incoming; z; z = z->next) printf("%s, ", z->name); dump_graph(g); } Stack_sz++; if (Stack_sz > Stack_mx) Stack_mx = Stack_sz; } static Graph * pop_stack(void) { Graph *g = Nodes_Stack; if (g) Nodes_Stack = g->nxt; Stack_sz--; return g; } static char * newname(void) { static char buf[32]; sprintf(buf, "S%d", state_cnt++); return buf; } static int has_clause(int tok, Graph *p, Node *n) { Node *q, *qq; switch (n->ntyp) { case AND: return has_clause(tok, p, n->lft) && has_clause(tok, p, n->rgt); case OR: return has_clause(tok, p, n->lft) || has_clause(tok, p, n->rgt); } for (q = p->Other; q; q = q->nxt) { qq = right_linked(q); if (anywhere(tok, n, qq)) return 1; } return 0; } static void mk_grn(Node *n) { Graph *p; n = right_linked(n); more: for (p = Nodes_Set; p; p = p->nxt) if (p->outgoing && has_clause(AND, p, n)) { p->isgrn[p->grncnt++] = (unsigned char) Red_cnt; Lab_cnt++; } if (n->ntyp == U_OPER) /* 3.4.0 */ { n = n->rgt; goto more; } } static void mk_red(Node *n) { Graph *p; n = right_linked(n); for (p = Nodes_Set; p; p = p->nxt) { if (p->outgoing && has_clause(0, p, n)) { if (p->redcnt >= 63) Fatal("too many Untils", (char *)0); p->isred[p->redcnt++] = (unsigned char) Red_cnt; Lab_cnt++; Max_Red = Red_cnt; } } } static void liveness(Node *n) { if (n) switch (n->ntyp) { #ifdef NXT case NEXT: liveness(n->lft); break; #endif case U_OPER: Red_cnt++; mk_red(n); mk_grn(n->rgt); /* fall through */ case V_OPER: case OR: case AND: liveness(n->lft); liveness(n->rgt); break; } } static Graph * findgraph(char *nm) { Graph *p; Mapping *m; for (p = Nodes_Set; p; p = p->nxt) if (!strcmp(p->name->name, nm)) return p; for (m = Mapped; m; m = m->nxt) if (strcmp(m->from, nm) == 0) return m->to; printf("warning: node %s not found\n", nm); return (Graph *) 0; } static void Addout(char *to, char *from) { Graph *p = findgraph(from); Symbol *s; if (!p) return; s = getsym(tl_lookup(to)); s->next = p->outgoing; p->outgoing = s; } #ifdef NXT int only_nxt(Node *n) { switch (n->ntyp) { case NEXT: return 1; case OR: case AND: return only_nxt(n->rgt) && only_nxt(n->lft); default: return 0; } } #endif int dump_cond(Node *pp, Node *r, int first) { Node *q; int frst = first; if (!pp) return frst; q = dupnode(pp); q = rewrite(q); if (q->ntyp == PREDICATE || q->ntyp == NOT #ifndef NXT || q->ntyp == OR #endif || q->ntyp == FALSE) { if (!frst) fprintf(tl_out, " && "); dump(q); frst = 0; #ifdef NXT } else if (q->ntyp == OR) { if (!frst) fprintf(tl_out, " && "); fprintf(tl_out, "(("); frst = dump_cond(q->lft, r, 1); if (!frst) fprintf(tl_out, ") || ("); else { if (only_nxt(q->lft)) { fprintf(tl_out, "1))"); return 0; } } frst = dump_cond(q->rgt, r, 1); if (frst) { if (only_nxt(q->rgt)) fprintf(tl_out, "1"); else fprintf(tl_out, "0"); frst = 0; } fprintf(tl_out, "))"); #endif } else if (q->ntyp == V_OPER && !anywhere(AND, q->rgt, r)) { frst = dump_cond(q->rgt, r, frst); } else if (q->ntyp == AND) { frst = dump_cond(q->lft, r, frst); frst = dump_cond(q->rgt, r, frst); } return frst; } static int choueka(Graph *p, int count) { int j, k, incr_cnt = 0; for (j = count; j <= Max_Red; j++) /* for each acceptance class */ { int delta = 0; /* is state p labeled Grn-j OR not Red-j ? */ for (k = 0; k < (int) p->grncnt; k++) if (p->isgrn[k] == j) { delta = 1; break; } if (delta) { incr_cnt++; continue; } for (k = 0; k < (int) p->redcnt; k++) if (p->isred[k] == j) { delta = 1; break; } if (delta) break; incr_cnt++; } return incr_cnt; } static int set_prefix(char *pref, int count, Graph *r2) { int incr_cnt = 0; /* acceptance class 'count' */ if (Lab_cnt == 0 || Max_Red == 0) sprintf(pref, "accept"); /* new */ else if (count >= Max_Red) sprintf(pref, "T0"); /* cycle */ else { incr_cnt = choueka(r2, count+1); if (incr_cnt + count >= Max_Red) sprintf(pref, "accept"); /* last hop */ else sprintf(pref, "T%d", count+incr_cnt); } return incr_cnt; } static void fsm_trans(Graph *p, int count, char *curnm) { Graph *r; Symbol *s; char prefix[128], nwnm[256]; if (!p->outgoing) addtrans(p, curnm, False, "accept_all"); for (s = p->outgoing; s; s = s->next) { r = findgraph(s->name); if (!r) continue; if (r->outgoing) { (void) set_prefix(prefix, count, r); sprintf(nwnm, "%s_%s", prefix, s->name); } else strcpy(nwnm, "accept_all"); if (tl_verbose) { printf("maxred=%d, count=%d, curnm=%s, nwnm=%s ", Max_Red, count, curnm, nwnm); printf("(greencnt=%d,%d, redcnt=%d,%d)\n", r->grncnt, r->isgrn[0], r->redcnt, r->isred[0]); } addtrans(p, curnm, r->Old, nwnm); } } static void mkbuchi(void) { Graph *p; int k; char curnm[64]; for (k = 0; k <= Max_Red; k++) for (p = Nodes_Set; p; p = p->nxt) { if (!p->outgoing) continue; if (k != 0 && !strcmp(p->name->name, "init") && Max_Red != 0) continue; if (k == Max_Red && strcmp(p->name->name, "init") != 0) strcpy(curnm, "accept_"); else sprintf(curnm, "T%d_", k); strcat(curnm, p->name->name); fsm_trans(p, k, curnm); } fsm_print(); } static Symbol * dupSlist(Symbol *s) { Symbol *p1, *p2, *p3, *d = ZS; for (p1 = s; p1; p1 = p1->next) { for (p3 = d; p3; p3 = p3->next) { if (!strcmp(p3->name, p1->name)) break; } if (p3) continue; /* a duplicate */ p2 = getsym(p1); p2->next = d; d = p2; } return d; } static Symbol * catSlist(Symbol *a, Symbol *b) { Symbol *p1, *p2, *p3, *tmp; /* remove duplicates from b */ for (p1 = a; p1; p1 = p1->next) { p3 = ZS; for (p2 = b; p2; p2 = p2->next) { if (strcmp(p1->name, p2->name)) { p3 = p2; continue; } tmp = p2->next; tfree((void *) p2); if (p3) p3->next = tmp; else b = tmp; } } if (!a) return b; if (!b) return a; if (!b->next) { b->next = a; return b; } /* find end of list */ for (p1 = a; p1->next; p1 = p1->next) ; p1->next = b; return a; } static void fixinit(Node *orig) { Graph *p1, *g; Symbol *q1, *q2 = ZS; ng(tl_lookup("init"), ZS, ZN, ZN, ZN); p1 = pop_stack(); p1->nxt = Nodes_Set; p1->Other = p1->Old = orig; Nodes_Set = p1; for (g = Nodes_Set; g; g = g->nxt) { for (q1 = g->incoming; q1; q1 = q2) { q2 = q1->next; Addout(g->name->name, q1->name); tfree((void *) q1); } g->incoming = ZS; } } static Node * flatten(Node *p) { Node *q, *r, *z = ZN; for (q = p; q; q = q->nxt) { r = dupnode(q); if (z) z = tl_nn(AND, r, z); else z = r; } if (!z) return z; z = rewrite(z); return z; } static Node * Duplicate(Node *n) { Node *n1, *n2, *lst = ZN, *d = ZN; for (n1 = n; n1; n1 = n1->nxt) { n2 = dupnode(n1); if (lst) { lst->nxt = n2; lst = n2; } else d = lst = n2; } return d; } static void ng(Symbol *s, Symbol *in, Node *isnew, Node *isold, Node *next) { Graph *g = (Graph *) tl_emalloc(sizeof(Graph)); if (s) g->name = s; else g->name = tl_lookup(newname()); if (in) g->incoming = dupSlist(in); if (isnew) g->New = flatten(isnew); if (isold) g->Old = Duplicate(isold); if (next) g->Next = flatten(next); push_stack(g); } static void sdump(Node *n) { switch (n->ntyp) { case PREDICATE: strcat(dumpbuf, n->sym->name); break; case U_OPER: strcat(dumpbuf, "U"); goto common2; case V_OPER: strcat(dumpbuf, "V"); goto common2; case OR: strcat(dumpbuf, "|"); goto common2; case AND: strcat(dumpbuf, "&"); common2: sdump(n->rgt); common1: sdump(n->lft); break; #ifdef NXT case NEXT: strcat(dumpbuf, "X"); goto common1; #endif case NOT: strcat(dumpbuf, "!"); goto common1; case TRUE: strcat(dumpbuf, "T"); break; case FALSE: strcat(dumpbuf, "F"); break; default: strcat(dumpbuf, "?"); break; } } Symbol * DoDump(Node *n) { if (!n) return ZS; if (n->ntyp == PREDICATE) return n->sym; dumpbuf[0] = '\0'; sdump(n); return tl_lookup(dumpbuf); } static int not_new(Graph *g) { Graph *q1; Node *tmp, *n1, *n2; Mapping *map; tmp = flatten(g->Old); /* duplicate, collapse, normalize */ g->Other = g->Old; /* non normalized full version */ g->Old = tmp; g->oldstring = DoDump(g->Old); tmp = flatten(g->Next); g->nxtstring = DoDump(tmp); if (tl_verbose) dump_graph(g); Debug2("\tformula-old: [%s]\n", g->oldstring?g->oldstring->name:"true"); Debug2("\tformula-nxt: [%s]\n", g->nxtstring?g->nxtstring->name:"true"); for (q1 = Nodes_Set; q1; q1 = q1->nxt) { Debug2(" compare old to: %s", q1->name->name); Debug2(" [%s]", q1->oldstring?q1->oldstring->name:"true"); Debug2(" compare nxt to: %s", q1->name->name); Debug2(" [%s]", q1->nxtstring?q1->nxtstring->name:"true"); if (q1->oldstring != g->oldstring || q1->nxtstring != g->nxtstring) { Debug(" => different\n"); continue; } Debug(" => match\n"); if (g->incoming) q1->incoming = catSlist(g->incoming, q1->incoming); /* check if there's anything in g->Other that needs adding to q1->Other */ for (n2 = g->Other; n2; n2 = n2->nxt) { for (n1 = q1->Other; n1; n1 = n1->nxt) if (isequal(n1, n2)) break; if (!n1) { Node *n3 = dupnode(n2); /* don't mess up n2->nxt */ n3->nxt = q1->Other; q1->Other = n3; } } map = (Mapping *) tl_emalloc(sizeof(Mapping)); map->from = g->name->name; map->to = q1; map->nxt = Mapped; Mapped = map; for (n1 = g->Other; n1; n1 = n2) { n2 = n1->nxt; releasenode(1, n1); } for (n1 = g->Old; n1; n1 = n2) { n2 = n1->nxt; releasenode(1, n1); } for (n1 = g->Next; n1; n1 = n2) { n2 = n1->nxt; releasenode(1, n1); } return 1; } if (newstates) tl_verbose=1; Debug2(" New Node %s [", g->name->name); for (n1 = g->Old; n1; n1 = n1->nxt) { Dump(n1); Debug(", "); } Debug2("] nr %d\n", Base); if (newstates) tl_verbose=0; Base++; g->nxt = Nodes_Set; Nodes_Set = g; return 0; } static void expand_g(Graph *g) { Node *now, *n1, *n2, *nx; int can_release; if (!g->New) { Debug2("\nDone with %s", g->name->name); if (tl_verbose) dump_graph(g); if (not_new(g)) { if (tl_verbose) printf("\tIs Not New\n"); return; } if (g->Next) { Debug(" Has Next ["); for (n1 = g->Next; n1; n1 = n1->nxt) { Dump(n1); Debug(", "); } Debug("]\n"); ng(ZS, getsym(g->name), g->Next, ZN, ZN); } return; } if (tl_verbose) { Symbol *z; printf("\nExpand %s, from ", g->name->name); for (z = g->incoming; z; z = z->next) printf("%s, ", z->name); printf("\n\thandle:\t"); Explain(g->New->ntyp); dump_graph(g); } if (g->New->ntyp == AND) { if (g->New->nxt) { n2 = g->New->rgt; while (n2->nxt) n2 = n2->nxt; n2->nxt = g->New->nxt; } n1 = n2 = g->New->lft; while (n2->nxt) n2 = n2->nxt; n2->nxt = g->New->rgt; releasenode(0, g->New); g->New = n1; push_stack(g); return; } can_release = 0; /* unless it need not go into Old */ now = g->New; g->New = g->New->nxt; now->nxt = ZN; if (now->ntyp != TRUE) { if (g->Old) { for (n1 = g->Old; n1->nxt; n1 = n1->nxt) if (isequal(now, n1)) { can_release = 1; goto out; } n1->nxt = now; } else g->Old = now; } out: switch (now->ntyp) { case FALSE: push_stack(g); break; case TRUE: releasenode(1, now); push_stack(g); break; case PREDICATE: case NOT: if (can_release) releasenode(1, now); push_stack(g); break; case V_OPER: Assert(now->rgt != ZN, now->ntyp); Assert(now->lft != ZN, now->ntyp); Assert(now->rgt->nxt == ZN, now->ntyp); Assert(now->lft->nxt == ZN, now->ntyp); n1 = now->rgt; n1->nxt = g->New; if (can_release) nx = now; else nx = getnode(now); /* now also appears in Old */ nx->nxt = g->Next; n2 = now->lft; n2->nxt = getnode(now->rgt); n2->nxt->nxt = g->New; g->New = flatten(n2); push_stack(g); ng(ZS, g->incoming, n1, g->Old, nx); break; case U_OPER: Assert(now->rgt->nxt == ZN, now->ntyp); Assert(now->lft->nxt == ZN, now->ntyp); n1 = now->lft; if (can_release) nx = now; else nx = getnode(now); /* now also appears in Old */ nx->nxt = g->Next; n2 = now->rgt; n2->nxt = g->New; goto common; #ifdef NXT case NEXT: Assert(now->lft != ZN, now->ntyp); nx = dupnode(now->lft); nx->nxt = g->Next; g->Next = nx; if (can_release) releasenode(0, now); push_stack(g); break; #endif case OR: Assert(now->rgt->nxt == ZN, now->ntyp); Assert(now->lft->nxt == ZN, now->ntyp); n1 = now->lft; nx = g->Next; n2 = now->rgt; n2->nxt = g->New; common: n1->nxt = g->New; ng(ZS, g->incoming, n1, g->Old, nx); g->New = flatten(n2); if (can_release) releasenode(1, now); push_stack(g); break; } } Node * twocases(Node *p) { Node *q; /* 1: ([]p1 && []p2) == [](p1 && p2) */ /* 2: (<>p1 || <>p2) == <>(p1 || p2) */ if (!p) return p; switch(p->ntyp) { case AND: case OR: case U_OPER: case V_OPER: p->lft = twocases(p->lft); p->rgt = twocases(p->rgt); break; #ifdef NXT case NEXT: #endif case NOT: p->lft = twocases(p->lft); break; default: break; } if (p->ntyp == AND /* 1 */ && p->lft->ntyp == V_OPER && p->lft->lft->ntyp == FALSE && p->rgt->ntyp == V_OPER && p->rgt->lft->ntyp == FALSE) { q = tl_nn(V_OPER, False, tl_nn(AND, p->lft->rgt, p->rgt->rgt)); } else if (p->ntyp == OR /* 2 */ && p->lft->ntyp == U_OPER && p->lft->lft->ntyp == TRUE && p->rgt->ntyp == U_OPER && p->rgt->lft->ntyp == TRUE) { q = tl_nn(U_OPER, True, tl_nn(OR, p->lft->rgt, p->rgt->rgt)); } else q = p; return q; } void trans(Node *p) { Node *op; Graph *g; if (!p || tl_errs) return; p = twocases(p); if (tl_verbose || tl_terse) { fprintf(tl_out, "\t/* Normlzd: "); dump(p); fprintf(tl_out, " */\n"); } if (tl_terse) return; op = dupnode(p); ng(ZS, getsym(tl_lookup("init")), p, ZN, ZN); while ((g = Nodes_Stack) != (Graph *) 0) { Nodes_Stack = g->nxt; expand_g(g); } if (newstates) return; fixinit(p); liveness(flatten(op)); /* was: liveness(op); */ mkbuchi(); if (tl_verbose) { printf("/*\n"); printf(" * %d states in Streett automaton\n", Base); printf(" * %d Streett acceptance conditions\n", Max_Red); printf(" * %d Buchi states\n", Total); printf(" */\n"); } }