1 /***** spin: pangen5.c *****/
2
3 /* Copyright (c) 1999-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 */
11
12 #include "spin.h"
13 #include "y.tab.h"
14
15 typedef struct BuildStack {
16 FSM_trans *t;
17 struct BuildStack *nxt;
18 } BuildStack;
19
20 extern ProcList *rdy;
21 extern int verbose, eventmapnr, claimnr, rvopt, export_ast, u_sync;
22 extern Element *Al_El;
23
24 static FSM_state *fsm_free;
25 static FSM_trans *trans_free;
26 static BuildStack *bs, *bf;
27 static int max_st_id;
28 static int cur_st_id;
29 int o_max;
30 FSM_state *fsm;
31 FSM_state **fsm_tbl;
32 FSM_use *use_free;
33
34 static void ana_seq(Sequence *);
35 static void ana_stmnt(FSM_trans *, Lextok *, int);
36
37 extern void AST_slice(void);
38 extern void AST_store(ProcList *, int);
39 extern int has_global(Lextok *);
40 extern void exit(int);
41
42 static void
fsm_table(void)43 fsm_table(void)
44 { FSM_state *f;
45 max_st_id += 2;
46 /* fprintf(stderr, "omax %d, max=%d\n", o_max, max_st_id); */
47 if (o_max < max_st_id)
48 { o_max = max_st_id;
49 fsm_tbl = (FSM_state **) emalloc(max_st_id * sizeof(FSM_state *));
50 } else
51 memset((char *)fsm_tbl, 0, max_st_id * sizeof(FSM_state *));
52 cur_st_id = max_st_id;
53 max_st_id = 0;
54
55 for (f = fsm; f; f = f->nxt)
56 fsm_tbl[f->from] = f;
57 }
58
59 static int
FSM_DFS(int from,FSM_use * u)60 FSM_DFS(int from, FSM_use *u)
61 { FSM_state *f;
62 FSM_trans *t;
63 FSM_use *v;
64 int n;
65
66 if (from == 0)
67 return 1;
68
69 f = fsm_tbl[from];
70
71 if (!f)
72 { printf("cannot find state %d\n", from);
73 fatal("fsm_dfs: cannot happen\n", (char *) 0);
74 }
75
76 if (f->seen)
77 return 1;
78 f->seen = 1;
79
80 for (t = f->t; t; t = t->nxt)
81 {
82 for (n = 0; n < 2; n++)
83 for (v = t->Val[n]; v; v = v->nxt)
84 if (u->var == v->var)
85 return n; /* a read or write */
86
87 if (!FSM_DFS(t->to, u))
88 return 0;
89 }
90 return 1;
91 }
92
93 static void
new_dfs(void)94 new_dfs(void)
95 { int i;
96
97 for (i = 0; i < cur_st_id; i++)
98 if (fsm_tbl[i])
99 fsm_tbl[i]->seen = 0;
100 }
101
102 static int
good_dead(Element * e,FSM_use * u)103 good_dead(Element *e, FSM_use *u)
104 {
105 switch (u->special) {
106 case 2: /* ok if it's a receive */
107 if (e->n->ntyp == ASGN
108 && e->n->rgt->ntyp == CONST
109 && e->n->rgt->val == 0)
110 return 0;
111 break;
112 case 1: /* must be able to use oval */
113 if (e->n->ntyp != 'c'
114 && e->n->ntyp != 'r')
115 return 0; /* can't really happen */
116 break;
117 }
118 return 1;
119 }
120
121 #if 0
122 static int howdeep = 0;
123 #endif
124
125 static int
eligible(FSM_trans * v)126 eligible(FSM_trans *v)
127 { Element *el = ZE;
128 Lextok *lt = ZN;
129
130 if (v) el = v->step;
131 if (el) lt = v->step->n;
132
133 if (!lt /* dead end */
134 || v->nxt /* has alternatives */
135 || el->esc /* has an escape */
136 || (el->status&CHECK2) /* remotely referenced */
137 || lt->ntyp == ATOMIC
138 || lt->ntyp == NON_ATOMIC /* used for inlines -- should be able to handle this */
139 || lt->ntyp == IF
140 || lt->ntyp == C_CODE
141 || lt->ntyp == C_EXPR
142 || has_lab(el, 0) /* any label at all */
143
144 || lt->ntyp == DO
145 || lt->ntyp == UNLESS
146 || lt->ntyp == D_STEP
147 || lt->ntyp == ELSE
148 || lt->ntyp == '@'
149 || lt->ntyp == 'c'
150 || lt->ntyp == 'r'
151 || lt->ntyp == 's')
152 return 0;
153
154 if (!(el->status&(2|4))) /* not atomic */
155 { int unsafe = (el->status&I_GLOB)?1:has_global(el->n);
156 if (unsafe)
157 return 0;
158 }
159
160 return 1;
161 }
162
163 static int
canfill_in(FSM_trans * v)164 canfill_in(FSM_trans *v)
165 { Element *el = v->step;
166 Lextok *lt = v->step->n;
167
168 if (!lt /* dead end */
169 || v->nxt /* has alternatives */
170 || el->esc /* has an escape */
171 || (el->status&CHECK2)) /* remotely referenced */
172 return 0;
173
174 if (!(el->status&(2|4)) /* not atomic */
175 && ((el->status&I_GLOB)
176 || has_global(el->n))) /* and not safe */
177 return 0;
178
179 return 1;
180 }
181
182 static int
pushbuild(FSM_trans * v)183 pushbuild(FSM_trans *v)
184 { BuildStack *b;
185
186 for (b = bs; b; b = b->nxt)
187 if (b->t == v)
188 return 0;
189 if (bf)
190 { b = bf;
191 bf = bf->nxt;
192 } else
193 b = (BuildStack *) emalloc(sizeof(BuildStack));
194 b->t = v;
195 b->nxt = bs;
196 bs = b;
197 return 1;
198 }
199
200 static void
popbuild(void)201 popbuild(void)
202 { BuildStack *f;
203 if (!bs)
204 fatal("cannot happen, popbuild", (char *) 0);
205 f = bs;
206 bs = bs->nxt;
207 f->nxt = bf;
208 bf = f; /* freelist */
209 }
210
211 static int
build_step(FSM_trans * v)212 build_step(FSM_trans *v)
213 { FSM_state *f;
214 Element *el;
215 #if 0
216 Lextok *lt = ZN;
217 #endif
218 int st;
219 int r;
220
221 if (!v) return -1;
222
223 el = v->step;
224 st = v->to;
225
226 if (!el) return -1;
227
228 if (v->step->merge)
229 return v->step->merge; /* already done */
230
231 if (!eligible(v)) /* non-blocking */
232 return -1;
233
234 if (!pushbuild(v)) /* cycle detected */
235 return -1; /* break cycle */
236
237 f = fsm_tbl[st];
238 #if 0
239 lt = v->step->n;
240 if (verbose&32)
241 { if (++howdeep == 1)
242 printf("spin: %s:%d, merge:\n", lt->fn->name, lt->ln);
243 printf("\t[%d] <seqno %d>\t", howdeep, el->seqno);
244 comment(stdout, lt, 0);
245 printf(";\n");
246 }
247 #endif
248 r = build_step(f->t);
249 v->step->merge = (r == -1) ? st : r;
250 #if 0
251 if (verbose&32)
252 { printf(" merge value: %d (st=%d,r=%d, line %d)\n",
253 v->step->merge, st, r, el->n->ln);
254 howdeep--;
255 }
256 #endif
257 popbuild();
258
259 return v->step->merge;
260 }
261
262 static void
FSM_MERGER(void)263 FSM_MERGER(/* char *pname */ void) /* find candidates for safely merging steps */
264 { FSM_state *f, *g;
265 FSM_trans *t;
266 Lextok *lt;
267
268 for (f = fsm; f; f = f->nxt) /* all states */
269 for (t = f->t; t; t = t->nxt) /* all edges */
270 { if (!t->step) continue; /* happens with 'unless' */
271
272 t->step->merge_in = f->in; /* ?? */
273
274 if (t->step->merge)
275 continue;
276 lt = t->step->n;
277
278 if (lt->ntyp == 'c'
279 || lt->ntyp == 'r'
280 || lt->ntyp == 's') /* blocking stmnts */
281 continue; /* handled in 2nd scan */
282
283 if (!eligible(t))
284 continue;
285
286 g = fsm_tbl[t->to];
287 if (!g || !eligible(g->t))
288 {
289 #define SINGLES
290 #ifdef SINGLES
291 t->step->merge_single = t->to;
292 #if 0
293 if ((verbose&32))
294 { printf("spin: %s:%d, merge_single:\n\t<seqno %d>\t",
295 t->step->n->fn->name,
296 t->step->n->ln,
297 t->step->seqno);
298 comment(stdout, t->step->n, 0);
299 printf(";\n");
300 }
301 #endif
302 #endif
303 /* t is an isolated eligible step:
304 *
305 * a merge_start can connect to a proper
306 * merge chain or to a merge_single
307 * a merge chain can be preceded by
308 * a merge_start, but not by a merge_single
309 */
310
311 continue;
312 }
313
314 (void) build_step(t);
315 }
316
317 /* 2nd scan -- find possible merge_starts */
318
319 for (f = fsm; f; f = f->nxt) /* all states */
320 for (t = f->t; t; t = t->nxt) /* all edges */
321 { if (!t->step || t->step->merge)
322 continue;
323
324 lt = t->step->n;
325 #if 0
326 4.1.3:
327 an rv send operation inside an atomic, *loses* atomicity
328 when executed
329 and should therefore never be merged with a subsequent
330 statement within the atomic sequence
331 the same is not true for non-rv send operations
332 #endif
333
334 if (lt->ntyp == 'c' /* potentially blocking stmnts */
335 || lt->ntyp == 'r'
336 || (lt->ntyp == 's' && u_sync == 0)) /* added !u_sync in 4.1.3 */
337 { if (!canfill_in(t)) /* atomic, non-global, etc. */
338 continue;
339
340 g = fsm_tbl[t->to];
341 if (!g || !g->t || !g->t->step)
342 continue;
343 if (g->t->step->merge)
344 t->step->merge_start = g->t->step->merge;
345 #ifdef SINGLES
346 else if (g->t->step->merge_single)
347 t->step->merge_start = g->t->step->merge_single;
348 #endif
349 #if 0
350 if ((verbose&32)
351 && t->step->merge_start)
352 { printf("spin: %s:%d, merge_START:\n\t<seqno %d>\t",
353 lt->fn->name, lt->ln,
354 t->step->seqno);
355 comment(stdout, lt, 0);
356 printf(";\n");
357 }
358 #endif
359 }
360 }
361 }
362
363 static void
FSM_ANA(void)364 FSM_ANA(void)
365 { FSM_state *f;
366 FSM_trans *t;
367 FSM_use *u, *v, *w;
368 int n;
369
370 for (f = fsm; f; f = f->nxt) /* all states */
371 for (t = f->t; t; t = t->nxt) /* all edges */
372 for (n = 0; n < 2; n++) /* reads and writes */
373 for (u = t->Val[n]; u; u = u->nxt)
374 { if (!u->var->context /* global */
375 || u->var->type == CHAN
376 || u->var->type == STRUCT)
377 continue;
378 new_dfs();
379 if (FSM_DFS(t->to, u)) /* cannot hit read before hitting write */
380 u->special = n+1; /* means, reset to 0 after use */
381 }
382
383 if (!export_ast)
384 for (f = fsm; f; f = f->nxt)
385 for (t = f->t; t; t = t->nxt)
386 for (n = 0; n < 2; n++)
387 for (u = t->Val[n], w = (FSM_use *) 0; u; )
388 { if (u->special)
389 { v = u->nxt;
390 if (!w) /* remove from list */
391 t->Val[n] = v;
392 else
393 w->nxt = v;
394 #if q
395 if (verbose&32)
396 { printf("%s : %3d: %d -> %d \t",
397 t->step->n->fn->name,
398 t->step->n->ln,
399 f->from,
400 t->to);
401 comment(stdout, t->step->n, 0);
402 printf("\t%c%d: %s\n", n==0?'R':'L',
403 u->special, u->var->name);
404 }
405 #endif
406 if (good_dead(t->step, u))
407 { u->nxt = t->step->dead; /* insert into dead */
408 t->step->dead = u;
409 }
410 u = v;
411 } else
412 { w = u;
413 u = u->nxt;
414 } }
415 }
416
417 void
rel_use(FSM_use * u)418 rel_use(FSM_use *u)
419 {
420 if (!u) return;
421 rel_use(u->nxt);
422 u->var = (Symbol *) 0;
423 u->special = 0;
424 u->nxt = use_free;
425 use_free = u;
426 }
427
428 static void
rel_trans(FSM_trans * t)429 rel_trans(FSM_trans *t)
430 {
431 if (!t) return;
432 rel_trans(t->nxt);
433 rel_use(t->Val[0]);
434 rel_use(t->Val[1]);
435 t->Val[0] = t->Val[1] = (FSM_use *) 0;
436 t->nxt = trans_free;
437 trans_free = t;
438 }
439
440 static void
rel_state(FSM_state * f)441 rel_state(FSM_state *f)
442 {
443 if (!f) return;
444 rel_state(f->nxt);
445 rel_trans(f->t);
446 f->t = (FSM_trans *) 0;
447 f->nxt = fsm_free;
448 fsm_free = f;
449 }
450
451 static void
FSM_DEL(void)452 FSM_DEL(void)
453 {
454 rel_state(fsm);
455 fsm = (FSM_state *) 0;
456 }
457
458 static FSM_state *
mkstate(int s)459 mkstate(int s)
460 { FSM_state *f;
461
462 /* fsm_tbl isn't allocated yet */
463 for (f = fsm; f; f = f->nxt)
464 if (f->from == s)
465 break;
466 if (!f)
467 { if (fsm_free)
468 { f = fsm_free;
469 memset(f, 0, sizeof(FSM_state));
470 fsm_free = fsm_free->nxt;
471 } else
472 f = (FSM_state *) emalloc(sizeof(FSM_state));
473 f->from = s;
474 f->t = (FSM_trans *) 0;
475 f->nxt = fsm;
476 fsm = f;
477 if (s > max_st_id)
478 max_st_id = s;
479 }
480 return f;
481 }
482
483 static FSM_trans *
get_trans(int to)484 get_trans(int to)
485 { FSM_trans *t;
486
487 if (trans_free)
488 { t = trans_free;
489 memset(t, 0, sizeof(FSM_trans));
490 trans_free = trans_free->nxt;
491 } else
492 t = (FSM_trans *) emalloc(sizeof(FSM_trans));
493
494 t->to = to;
495 return t;
496 }
497
498 static void
FSM_EDGE(int from,int to,Element * e)499 FSM_EDGE(int from, int to, Element *e)
500 { FSM_state *f;
501 FSM_trans *t;
502
503 f = mkstate(from); /* find it or else make it */
504 t = get_trans(to);
505
506 t->step = e;
507 t->nxt = f->t;
508 f->t = t;
509
510 f = mkstate(to);
511 f->in++;
512
513 if (export_ast)
514 { t = get_trans(from);
515 t->step = e;
516 t->nxt = f->p; /* from is a predecessor of to */
517 f->p = t;
518 }
519
520 if (t->step)
521 ana_stmnt(t, t->step->n, 0);
522 }
523
524 #define LVAL 1
525 #define RVAL 0
526
527 static void
ana_var(FSM_trans * t,Lextok * now,int usage)528 ana_var(FSM_trans *t, Lextok *now, int usage)
529 { FSM_use *u, *v;
530
531 if (!t || !now || !now->sym)
532 return;
533
534 if (now->sym->name[0] == '_'
535 && (strcmp(now->sym->name, "_") == 0
536 || strcmp(now->sym->name, "_pid") == 0
537 || strcmp(now->sym->name, "_last") == 0))
538 return;
539
540 v = t->Val[usage];
541 for (u = v; u; u = u->nxt)
542 if (u->var == now->sym)
543 return; /* it's already there */
544
545 if (!now->lft)
546 { /* not for array vars -- it's hard to tell statically
547 if the index would, at runtime, evaluate to the
548 same values at lval and rval references
549 */
550 if (use_free)
551 { u = use_free;
552 use_free = use_free->nxt;
553 } else
554 u = (FSM_use *) emalloc(sizeof(FSM_use));
555
556 u->var = now->sym;
557 u->nxt = t->Val[usage];
558 t->Val[usage] = u;
559 } else
560 ana_stmnt(t, now->lft, RVAL); /* index */
561
562 if (now->sym->type == STRUCT
563 && now->rgt
564 && now->rgt->lft)
565 ana_var(t, now->rgt->lft, usage);
566 }
567
568 static void
ana_stmnt(FSM_trans * t,Lextok * now,int usage)569 ana_stmnt(FSM_trans *t, Lextok *now, int usage)
570 { Lextok *v;
571
572 if (!t || !now) return;
573
574 switch (now->ntyp) {
575 case '.':
576 case BREAK:
577 case GOTO:
578 case CONST:
579 case TIMEOUT:
580 case NONPROGRESS:
581 case ELSE:
582 case '@':
583 case 'q':
584 case IF:
585 case DO:
586 case ATOMIC:
587 case NON_ATOMIC:
588 case D_STEP:
589 case C_CODE:
590 case C_EXPR:
591 break;
592
593 case '!':
594 case UMIN:
595 case '~':
596 case ENABLED:
597 case PC_VAL:
598 case LEN:
599 case FULL:
600 case EMPTY:
601 case NFULL:
602 case NEMPTY:
603 case ASSERT:
604 case 'c':
605 ana_stmnt(t, now->lft, RVAL);
606 break;
607
608 case '/':
609 case '*':
610 case '-':
611 case '+':
612 case '%':
613 case '&':
614 case '^':
615 case '|':
616 case LT:
617 case GT:
618 case LE:
619 case GE:
620 case NE:
621 case EQ:
622 case OR:
623 case AND:
624 case LSHIFT:
625 case RSHIFT:
626 ana_stmnt(t, now->lft, RVAL);
627 ana_stmnt(t, now->rgt, RVAL);
628 break;
629
630 case ASGN:
631 ana_stmnt(t, now->lft, LVAL);
632 ana_stmnt(t, now->rgt, RVAL);
633 break;
634
635 case PRINT:
636 case RUN:
637 for (v = now->lft; v; v = v->rgt)
638 ana_stmnt(t, v->lft, RVAL);
639 break;
640
641 case PRINTM:
642 if (now->lft && !now->lft->ismtyp)
643 ana_stmnt(t, now->lft, RVAL);
644 break;
645
646 case 's':
647 ana_stmnt(t, now->lft, RVAL);
648 for (v = now->rgt; v; v = v->rgt)
649 ana_stmnt(t, v->lft, RVAL);
650 break;
651
652 case 'R':
653 case 'r':
654 ana_stmnt(t, now->lft, RVAL);
655 for (v = now->rgt; v; v = v->rgt)
656 { if (v->lft->ntyp == EVAL)
657 ana_stmnt(t, v->lft->lft, RVAL);
658 else
659 if (v->lft->ntyp != CONST
660 && now->ntyp != 'R') /* was v->lft->ntyp */
661 ana_stmnt(t, v->lft, LVAL);
662 }
663 break;
664
665 case '?':
666 ana_stmnt(t, now->lft, RVAL);
667 if (now->rgt)
668 { ana_stmnt(t, now->rgt->lft, RVAL);
669 ana_stmnt(t, now->rgt->rgt, RVAL);
670 }
671 break;
672
673 case NAME:
674 ana_var(t, now, usage);
675 break;
676
677 case 'p': /* remote ref */
678 ana_stmnt(t, now->lft->lft, RVAL); /* process id */
679 ana_var(t, now, RVAL);
680 ana_var(t, now->rgt, RVAL);
681 break;
682
683 default:
684 printf("spin: %s:%d, bad node type %d (ana_stmnt)\n",
685 now->fn->name, now->ln, now->ntyp);
686 fatal("aborting", (char *) 0);
687 }
688 }
689
690 void
ana_src(int dataflow,int merger)691 ana_src(int dataflow, int merger) /* called from main.c and guided.c */
692 { ProcList *p;
693 Element *e;
694 #if 0
695 int counter = 1;
696 #endif
697 for (p = rdy; p; p = p->nxt)
698 {
699 ana_seq(p->s);
700 fsm_table();
701
702 e = p->s->frst;
703 #if 0
704 if (dataflow || merger)
705 { printf("spin: %d, optimizing '%s'",
706 counter++, p->n->name);
707 fflush(stdout);
708 }
709 #endif
710 if (dataflow)
711 { FSM_ANA();
712 }
713 if (merger)
714 { FSM_MERGER(/* p->n->name */);
715 huntele(e, e->status, -1)->merge_in = 1; /* start-state */
716 #if 0
717 printf("\n");
718 #endif
719 }
720 if (export_ast)
721 AST_store(p, huntele(e, e->status, -1)->seqno);
722
723 FSM_DEL();
724 }
725 for (e = Al_El; e; e = e->Nxt)
726 {
727 if (!(e->status&DONE) && (verbose&32))
728 { printf("unreachable code: ");
729 printf("%s:%3d ", e->n->fn->name, e->n->ln);
730 comment(stdout, e->n, 0);
731 printf("\n");
732 }
733 e->status &= ~DONE;
734 }
735 if (export_ast)
736 { AST_slice();
737 alldone(0); /* changed in 5.3.0: was exit(0) */
738 }
739 }
740
741 void
spit_recvs(FILE * f1,FILE * f2)742 spit_recvs(FILE *f1, FILE *f2) /* called from pangen2.c */
743 { Element *e;
744 Sequence *s;
745 extern int Unique;
746
747 fprintf(f1, "unsigned char Is_Recv[%d];\n", Unique);
748
749 fprintf(f2, "void\nset_recvs(void)\n{\n");
750 for (e = Al_El; e; e = e->Nxt)
751 { if (!e->n) continue;
752
753 switch (e->n->ntyp) {
754 case 'r':
755 markit: fprintf(f2, "\tIs_Recv[%d] = 1;\n", e->Seqno);
756 break;
757 case D_STEP:
758 s = e->n->sl->this;
759 switch (s->frst->n->ntyp) {
760 case DO:
761 fatal("unexpected: do at start of d_step", (char *) 0);
762 case IF: /* conservative: fall through */
763 case 'r': goto markit;
764 }
765 break;
766 }
767 }
768 fprintf(f2, "}\n");
769
770 if (rvopt)
771 {
772 fprintf(f2, "int\nno_recvs(int me)\n{\n");
773 fprintf(f2, " int h; uchar ot; short tt;\n");
774 fprintf(f2, " Trans *t;\n");
775 fprintf(f2, " for (h = BASE; h < (int) now._nr_pr; h++)\n");
776 fprintf(f2, " { if (h == me) continue;\n");
777 fprintf(f2, " tt = (short) ((P0 *)pptr(h))->_p;\n");
778 fprintf(f2, " ot = (uchar) ((P0 *)pptr(h))->_t;\n");
779 fprintf(f2, " for (t = trans[ot][tt]; t; t = t->nxt)\n");
780 fprintf(f2, " if (Is_Recv[t->t_id]) return 0;\n");
781 fprintf(f2, " }\n");
782 fprintf(f2, " return 1;\n");
783 fprintf(f2, "}\n");
784 }
785 }
786
787 static void
ana_seq(Sequence * s)788 ana_seq(Sequence *s)
789 { SeqList *h;
790 Sequence *t;
791 Element *e, *g;
792 int From, To;
793
794 for (e = s->frst; e; e = e->nxt)
795 { if (e->status & DONE)
796 goto checklast;
797
798 e->status |= DONE;
799
800 From = e->seqno;
801
802 if (e->n->ntyp == UNLESS)
803 ana_seq(e->sub->this);
804 else if (e->sub)
805 { for (h = e->sub; h; h = h->nxt)
806 { g = huntstart(h->this->frst);
807 To = g->seqno;
808
809 if (g->n->ntyp != 'c'
810 || g->n->lft->ntyp != CONST
811 || g->n->lft->val != 0
812 || g->esc)
813 FSM_EDGE(From, To, e);
814 /* else it's a dead link */
815 }
816 for (h = e->sub; h; h = h->nxt)
817 ana_seq(h->this);
818 } else if (e->n->ntyp == ATOMIC
819 || e->n->ntyp == D_STEP
820 || e->n->ntyp == NON_ATOMIC)
821 {
822 t = e->n->sl->this;
823 g = huntstart(t->frst);
824 t->last->nxt = e->nxt;
825 To = g->seqno;
826 FSM_EDGE(From, To, e);
827
828 ana_seq(t);
829 } else
830 { if (e->n->ntyp == GOTO)
831 { g = get_lab(e->n, 1);
832 g = huntele(g, e->status, -1);
833 To = g->seqno;
834 } else if (e->nxt)
835 { g = huntele(e->nxt, e->status, -1);
836 To = g->seqno;
837 } else
838 To = 0;
839
840 FSM_EDGE(From, To, e);
841
842 if (e->esc
843 && e->n->ntyp != GOTO
844 && e->n->ntyp != '.')
845 for (h = e->esc; h; h = h->nxt)
846 { g = huntstart(h->this->frst);
847 To = g->seqno;
848 FSM_EDGE(From, To, ZE);
849 ana_seq(h->this);
850 }
851 }
852
853 checklast: if (e == s->last)
854 break;
855 }
856 }
857