1 /***** spin: flow.c *****/ 2 3 /* Copyright (c) 1989-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 #ifdef PC 14 #include "y_tab.h" 15 #else 16 #include "y.tab.h" 17 #endif 18 19 extern Symbol *Fname; 20 extern int nr_errs, lineno, verbose; 21 extern short has_unless, has_badelse; 22 23 Element *Al_El = ZE; 24 Label *labtab = (Label *) 0; 25 int Unique=0, Elcnt=0, DstepStart = -1; 26 27 static Lbreak *breakstack = (Lbreak *) 0; 28 static Lextok *innermost; 29 static SeqList *cur_s = (SeqList *) 0; 30 static int break_id=0; 31 32 static Element *if_seq(Lextok *); 33 static Element *new_el(Lextok *); 34 static Element *unless_seq(Lextok *); 35 static void add_el(Element *, Sequence *); 36 static void attach_escape(Sequence *, Sequence *); 37 static void mov_lab(Symbol *, Element *, Element *); 38 static void walk_atomic(Element *, Element *, int); 39 40 void 41 open_seq(int top) 42 { SeqList *t; 43 Sequence *s = (Sequence *) emalloc(sizeof(Sequence)); 44 45 t = seqlist(s, cur_s); 46 cur_s = t; 47 if (top) Elcnt = 1; 48 } 49 50 void 51 rem_Seq(void) 52 { 53 DstepStart = Unique; 54 } 55 56 void 57 unrem_Seq(void) 58 { 59 DstepStart = -1; 60 } 61 62 static int 63 Rjumpslocal(Element *q, Element *stop) 64 { Element *lb, *f; 65 SeqList *h; 66 67 /* allow no jumps out of a d_step sequence */ 68 for (f = q; f && f != stop; f = f->nxt) 69 { if (f && f->n && f->n->ntyp == GOTO) 70 { lb = get_lab(f->n, 0); 71 if (!lb || lb->Seqno < DstepStart) 72 { lineno = f->n->ln; 73 Fname = f->n->fn; 74 return 0; 75 } } 76 for (h = f->sub; h; h = h->nxt) 77 { if (!Rjumpslocal(h->this->frst, h->this->last)) 78 return 0; 79 80 } } 81 return 1; 82 } 83 84 void 85 cross_dsteps(Lextok *a, Lextok *b) 86 { 87 if (a && b 88 && a->indstep != b->indstep) 89 { lineno = a->ln; 90 Fname = a->fn; 91 fatal("jump into d_step sequence", (char *) 0); 92 } 93 } 94 95 int 96 is_skip(Lextok *n) 97 { 98 return (n->ntyp == PRINT 99 || n->ntyp == PRINTM 100 || (n->ntyp == 'c' 101 && n->lft 102 && n->lft->ntyp == CONST 103 && n->lft->val == 1)); 104 } 105 106 void 107 check_sequence(Sequence *s) 108 { Element *e, *le = ZE; 109 Lextok *n; 110 int cnt = 0; 111 112 for (e = s->frst; e; le = e, e = e->nxt) 113 { n = e->n; 114 if (is_skip(n) && !has_lab(e, 0)) 115 { cnt++; 116 if (cnt > 1 117 && n->ntyp != PRINT 118 && n->ntyp != PRINTM) 119 { if (verbose&32) 120 printf("spin: line %d %s, redundant skip\n", 121 n->ln, n->fn->name); 122 if (e != s->frst 123 && e != s->last 124 && e != s->extent) 125 { e->status |= DONE; /* not unreachable */ 126 le->nxt = e->nxt; /* remove it */ 127 e = le; 128 } 129 } 130 } else 131 cnt = 0; 132 } 133 } 134 135 void 136 prune_opts(Lextok *n) 137 { SeqList *l; 138 extern Symbol *context; 139 extern char *claimproc; 140 141 if (!n 142 || (context && claimproc && strcmp(context->name, claimproc) == 0)) 143 return; 144 145 for (l = n->sl; l; l = l->nxt) /* find sequences of unlabeled skips */ 146 check_sequence(l->this); 147 } 148 149 Sequence * 150 close_seq(int nottop) 151 { Sequence *s = cur_s->this; 152 Symbol *z; 153 154 if (nottop > 0 && (z = has_lab(s->frst, 0))) 155 { printf("error: (%s:%d) label %s placed incorrectly\n", 156 (s->frst->n)?s->frst->n->fn->name:"-", 157 (s->frst->n)?s->frst->n->ln:0, 158 z->name); 159 switch (nottop) { 160 case 1: 161 printf("=====> stmnt unless Label: stmnt\n"); 162 printf("sorry, cannot jump to the guard of an\n"); 163 printf("escape (it is not a unique state)\n"); 164 break; 165 case 2: 166 printf("=====> instead of "); 167 printf("\"Label: stmnt unless stmnt\"\n"); 168 printf("=====> always use "); 169 printf("\"Label: { stmnt unless stmnt }\"\n"); 170 break; 171 case 3: 172 printf("=====> instead of "); 173 printf("\"atomic { Label: statement ... }\"\n"); 174 printf("=====> always use "); 175 printf("\"Label: atomic { statement ... }\"\n"); 176 break; 177 case 4: 178 printf("=====> instead of "); 179 printf("\"d_step { Label: statement ... }\"\n"); 180 printf("=====> always use "); 181 printf("\"Label: d_step { statement ... }\"\n"); 182 break; 183 case 5: 184 printf("=====> instead of "); 185 printf("\"{ Label: statement ... }\"\n"); 186 printf("=====> always use "); 187 printf("\"Label: { statement ... }\"\n"); 188 break; 189 case 6: 190 printf("=====>instead of\n"); 191 printf(" do (or if)\n"); 192 printf(" :: ...\n"); 193 printf(" :: Label: statement\n"); 194 printf(" od (of fi)\n"); 195 printf("=====>always use\n"); 196 printf("Label: do (or if)\n"); 197 printf(" :: ...\n"); 198 printf(" :: statement\n"); 199 printf(" od (or fi)\n"); 200 break; 201 case 7: 202 printf("cannot happen - labels\n"); 203 break; 204 } 205 alldone(1); 206 } 207 208 if (nottop == 4 209 && !Rjumpslocal(s->frst, s->last)) 210 fatal("non_local jump in d_step sequence", (char *) 0); 211 212 cur_s = cur_s->nxt; 213 s->maxel = Elcnt; 214 s->extent = s->last; 215 if (!s->last) 216 fatal("sequence must have at least one statement", (char *) 0); 217 return s; 218 } 219 220 Lextok * 221 do_unless(Lextok *No, Lextok *Es) 222 { SeqList *Sl; 223 Lextok *Re = nn(ZN, UNLESS, ZN, ZN); 224 Re->ln = No->ln; 225 Re->fn = No->fn; 226 227 has_unless++; 228 if (Es->ntyp == NON_ATOMIC) 229 Sl = Es->sl; 230 else 231 { open_seq(0); add_seq(Es); 232 Sl = seqlist(close_seq(1), 0); 233 } 234 235 if (No->ntyp == NON_ATOMIC) 236 { No->sl->nxt = Sl; 237 Sl = No->sl; 238 } else if (No->ntyp == ':' 239 && (No->lft->ntyp == NON_ATOMIC 240 || No->lft->ntyp == ATOMIC 241 || No->lft->ntyp == D_STEP)) 242 { 243 int tok = No->lft->ntyp; 244 245 No->lft->sl->nxt = Sl; 246 Re->sl = No->lft->sl; 247 248 open_seq(0); add_seq(Re); 249 Re = nn(ZN, tok, ZN, ZN); 250 Re->sl = seqlist(close_seq(7), 0); 251 Re->ln = No->ln; 252 Re->fn = No->fn; 253 254 Re = nn(No, ':', Re, ZN); /* lift label */ 255 Re->ln = No->ln; 256 Re->fn = No->fn; 257 return Re; 258 } else 259 { open_seq(0); add_seq(No); 260 Sl = seqlist(close_seq(2), Sl); 261 } 262 263 Re->sl = Sl; 264 return Re; 265 } 266 267 SeqList * 268 seqlist(Sequence *s, SeqList *r) 269 { SeqList *t = (SeqList *) emalloc(sizeof(SeqList)); 270 271 t->this = s; 272 t->nxt = r; 273 return t; 274 } 275 276 static Element * 277 new_el(Lextok *n) 278 { Element *m; 279 280 if (n) 281 { if (n->ntyp == IF || n->ntyp == DO) 282 return if_seq(n); 283 if (n->ntyp == UNLESS) 284 return unless_seq(n); 285 } 286 m = (Element *) emalloc(sizeof(Element)); 287 m->n = n; 288 m->seqno = Elcnt++; 289 m->Seqno = Unique++; 290 m->Nxt = Al_El; Al_El = m; 291 return m; 292 } 293 294 static int 295 has_chanref(Lextok *n) 296 { 297 if (!n) return 0; 298 299 switch (n->ntyp) { 300 case 's': case 'r': 301 #if 0 302 case 'R': case LEN: 303 #endif 304 case FULL: case NFULL: 305 case EMPTY: case NEMPTY: 306 return 1; 307 default: 308 break; 309 } 310 if (has_chanref(n->lft)) 311 return 1; 312 313 return has_chanref(n->rgt); 314 } 315 316 void 317 loose_ends(void) /* properly tie-up ends of sub-sequences */ 318 { Element *e, *f; 319 320 for (e = Al_El; e; e = e->Nxt) 321 { if (!e->n 322 || !e->nxt) 323 continue; 324 switch (e->n->ntyp) { 325 case ATOMIC: 326 case NON_ATOMIC: 327 case D_STEP: 328 f = e->nxt; 329 while (f && f->n->ntyp == '.') 330 f = f->nxt; 331 if (0) printf("link %d, {%d .. %d} -> %d (ntyp=%d) was %d\n", 332 e->seqno, 333 e->n->sl->this->frst->seqno, 334 e->n->sl->this->last->seqno, 335 f?f->seqno:-1, f?f->n->ntyp:-1, 336 e->n->sl->this->last->nxt?e->n->sl->this->last->nxt->seqno:-1); 337 if (!e->n->sl->this->last->nxt) 338 e->n->sl->this->last->nxt = f; 339 else 340 { if (e->n->sl->this->last->nxt->n->ntyp != GOTO) 341 { if (!f || e->n->sl->this->last->nxt->seqno != f->seqno) 342 non_fatal("unexpected: loose ends", (char *)0); 343 } else 344 e->n->sl->this->last = e->n->sl->this->last->nxt; 345 /* 346 * fix_dest can push a goto into the nxt position 347 * in that case the goto wins and f is not needed 348 * but the last fields needs adjusting 349 */ 350 } 351 break; 352 } } 353 } 354 355 static Element * 356 if_seq(Lextok *n) 357 { int tok = n->ntyp; 358 SeqList *s = n->sl; 359 Element *e = new_el(ZN); 360 Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */ 361 SeqList *z, *prev_z = (SeqList *) 0; 362 SeqList *move_else = (SeqList *) 0; /* to end of optionlist */ 363 int ref_chans = 0; 364 365 for (z = s; z; z = z->nxt) 366 { if (!z->this->frst) 367 continue; 368 if (z->this->frst->n->ntyp == ELSE) 369 { if (move_else) 370 fatal("duplicate `else'", (char *) 0); 371 if (z->nxt) /* is not already at the end */ 372 { move_else = z; 373 if (prev_z) 374 prev_z->nxt = z->nxt; 375 else 376 s = n->sl = z->nxt; 377 continue; 378 } 379 } else 380 ref_chans |= has_chanref(z->this->frst->n); 381 prev_z = z; 382 } 383 if (move_else) 384 { move_else->nxt = (SeqList *) 0; 385 /* if there is no prev, then else was at the end */ 386 if (!prev_z) fatal("cannot happen - if_seq", (char *) 0); 387 prev_z->nxt = move_else; 388 prev_z = move_else; 389 } 390 if (prev_z 391 && ref_chans 392 && prev_z->this->frst->n->ntyp == ELSE) 393 { prev_z->this->frst->n->val = 1; 394 has_badelse++; 395 non_fatal("dubious use of 'else' combined with i/o,", 396 (char *)0); 397 nr_errs--; 398 } 399 400 e->n = nn(n, tok, ZN, ZN); 401 e->n->sl = s; /* preserve as info only */ 402 e->sub = s; 403 for (z = s; z; prev_z = z, z = z->nxt) 404 add_el(t, z->this); /* append target */ 405 if (tok == DO) 406 { add_el(t, cur_s->this); /* target upfront */ 407 t = new_el(nn(n, BREAK, ZN, ZN)); /* break target */ 408 set_lab(break_dest(), t); /* new exit */ 409 breakstack = breakstack->nxt; /* pop stack */ 410 } 411 add_el(e, cur_s->this); 412 add_el(t, cur_s->this); 413 return e; /* destination node for label */ 414 } 415 416 static void 417 escape_el(Element *f, Sequence *e) 418 { SeqList *z; 419 420 for (z = f->esc; z; z = z->nxt) 421 if (z->this == e) 422 return; /* already there */ 423 424 /* cover the lower-level escapes of this state */ 425 for (z = f->esc; z; z = z->nxt) 426 attach_escape(z->this, e); 427 428 /* now attach escape to the state itself */ 429 430 f->esc = seqlist(e, f->esc); /* in lifo order... */ 431 #ifdef DEBUG 432 printf("attach %d (", e->frst->Seqno); 433 comment(stdout, e->frst->n, 0); 434 printf(") to %d (", f->Seqno); 435 comment(stdout, f->n, 0); 436 printf(")\n"); 437 #endif 438 switch (f->n->ntyp) { 439 case UNLESS: 440 attach_escape(f->sub->this, e); 441 break; 442 case IF: 443 case DO: 444 for (z = f->sub; z; z = z->nxt) 445 attach_escape(z->this, e); 446 break; 447 case D_STEP: 448 /* attach only to the guard stmnt */ 449 escape_el(f->n->sl->this->frst, e); 450 break; 451 case ATOMIC: 452 case NON_ATOMIC: 453 /* attach to all stmnts */ 454 attach_escape(f->n->sl->this, e); 455 break; 456 } 457 } 458 459 static void 460 attach_escape(Sequence *n, Sequence *e) 461 { Element *f; 462 463 for (f = n->frst; f; f = f->nxt) 464 { escape_el(f, e); 465 if (f == n->extent) 466 break; 467 } 468 } 469 470 static Element * 471 unless_seq(Lextok *n) 472 { SeqList *s = n->sl; 473 Element *e = new_el(ZN); 474 Element *t = new_el(nn(ZN,'.',ZN,ZN)); /* target */ 475 SeqList *z; 476 477 e->n = nn(n, UNLESS, ZN, ZN); 478 e->n->sl = s; /* info only */ 479 e->sub = s; 480 481 /* need 2 sequences: normal execution and escape */ 482 if (!s || !s->nxt || s->nxt->nxt) 483 fatal("unexpected unless structure", (char *)0); 484 485 /* append the target state to both */ 486 for (z = s; z; z = z->nxt) 487 add_el(t, z->this); 488 489 /* attach escapes to all states in normal sequence */ 490 attach_escape(s->this, s->nxt->this); 491 492 add_el(e, cur_s->this); 493 add_el(t, cur_s->this); 494 #ifdef DEBUG 495 printf("unless element (%d,%d):\n", e->Seqno, t->Seqno); 496 for (z = s; z; z = z->nxt) 497 { Element *x; printf("\t%d,%d,%d :: ", 498 z->this->frst->Seqno, 499 z->this->extent->Seqno, 500 z->this->last->Seqno); 501 for (x = z->this->frst; x; x = x->nxt) 502 printf("(%d)", x->Seqno); 503 printf("\n"); 504 } 505 #endif 506 return e; 507 } 508 509 Element * 510 mk_skip(void) 511 { Lextok *t = nn(ZN, CONST, ZN, ZN); 512 t->val = 1; 513 return new_el(nn(ZN, 'c', t, ZN)); 514 } 515 516 static void 517 add_el(Element *e, Sequence *s) 518 { 519 if (e->n->ntyp == GOTO) 520 { Symbol *z = has_lab(e, (1|2|4)); 521 if (z) 522 { Element *y; /* insert a skip */ 523 y = mk_skip(); 524 mov_lab(z, e, y); /* inherit label */ 525 add_el(y, s); 526 } } 527 #ifdef DEBUG 528 printf("add_el %d after %d -- ", 529 e->Seqno, (s->last)?s->last->Seqno:-1); 530 comment(stdout, e->n, 0); 531 printf("\n"); 532 #endif 533 if (!s->frst) 534 s->frst = e; 535 else 536 s->last->nxt = e; 537 s->last = e; 538 } 539 540 static Element * 541 colons(Lextok *n) 542 { 543 if (!n) 544 return ZE; 545 if (n->ntyp == ':') 546 { Element *e = colons(n->lft); 547 set_lab(n->sym, e); 548 return e; 549 } 550 innermost = n; 551 return new_el(n); 552 } 553 554 void 555 add_seq(Lextok *n) 556 { Element *e; 557 558 if (!n) return; 559 innermost = n; 560 e = colons(n); 561 if (innermost->ntyp != IF 562 && innermost->ntyp != DO 563 && innermost->ntyp != UNLESS) 564 add_el(e, cur_s->this); 565 } 566 567 void 568 set_lab(Symbol *s, Element *e) 569 { Label *l; extern Symbol *context; 570 571 if (!s) return; 572 for (l = labtab; l; l = l->nxt) 573 if (l->s == s && l->c == context) 574 { non_fatal("label %s redeclared", s->name); 575 break; 576 } 577 l = (Label *) emalloc(sizeof(Label)); 578 l->s = s; 579 l->c = context; 580 l->e = e; 581 l->nxt = labtab; 582 labtab = l; 583 } 584 585 Element * 586 get_lab(Lextok *n, int md) 587 { Label *l; 588 Symbol *s = n->sym; 589 590 for (l = labtab; l; l = l->nxt) 591 if (s == l->s) 592 return (l->e); 593 594 lineno = n->ln; 595 Fname = n->fn; 596 if (md) fatal("undefined label %s", s->name); 597 return ZE; 598 } 599 600 Symbol * 601 has_lab(Element *e, int special) 602 { Label *l; 603 604 for (l = labtab; l; l = l->nxt) 605 { if (e != l->e) 606 continue; 607 if (special == 0 608 || ((special&1) && !strncmp(l->s->name, "accept", 6)) 609 || ((special&2) && !strncmp(l->s->name, "end", 3)) 610 || ((special&4) && !strncmp(l->s->name, "progress", 8))) 611 return (l->s); 612 } 613 return ZS; 614 } 615 616 static void 617 mov_lab(Symbol *z, Element *e, Element *y) 618 { Label *l; 619 620 for (l = labtab; l; l = l->nxt) 621 if (e == l->e) 622 { l->e = y; 623 return; 624 } 625 if (e->n) 626 { lineno = e->n->ln; 627 Fname = e->n->fn; 628 } 629 fatal("cannot happen - mov_lab %s", z->name); 630 } 631 632 void 633 fix_dest(Symbol *c, Symbol *a) /* c:label name, a:proctype name */ 634 { Label *l; extern Symbol *context; 635 636 #if 0 637 printf("ref to label '%s' in proctype '%s', search:\n", 638 c->name, a->name); 639 for (l = labtab; l; l = l->nxt) 640 printf(" %s in %s\n", l->s->name, l->c->name); 641 #endif 642 643 for (l = labtab; l; l = l->nxt) 644 { if (strcmp(c->name, l->s->name) == 0 645 && strcmp(a->name, l->c->name) == 0) /* ? */ 646 break; 647 } 648 if (!l) 649 { printf("spin: label '%s' (proctype %s)\n", c->name, a->name); 650 non_fatal("unknown label '%s'", c->name); 651 if (context == a) 652 printf("spin: cannot remote ref a label inside the same proctype\n"); 653 return; 654 } 655 if (!l->e || !l->e->n) 656 fatal("fix_dest error (%s)", c->name); 657 if (l->e->n->ntyp == GOTO) 658 { Element *y = (Element *) emalloc(sizeof(Element)); 659 int keep_ln = l->e->n->ln; 660 Symbol *keep_fn = l->e->n->fn; 661 662 /* insert skip - or target is optimized away */ 663 y->n = l->e->n; /* copy of the goto */ 664 y->seqno = find_maxel(a); /* unique seqno within proc */ 665 y->nxt = l->e->nxt; 666 y->Seqno = Unique++; y->Nxt = Al_El; Al_El = y; 667 668 /* turn the original element+seqno into a skip */ 669 l->e->n = nn(ZN, 'c', nn(ZN, CONST, ZN, ZN), ZN); 670 l->e->n->ln = l->e->n->lft->ln = keep_ln; 671 l->e->n->fn = l->e->n->lft->fn = keep_fn; 672 l->e->n->lft->val = 1; 673 l->e->nxt = y; /* append the goto */ 674 } 675 l->e->status |= CHECK2; /* treat as if global */ 676 if (l->e->status & (ATOM | L_ATOM | D_ATOM)) 677 { non_fatal("cannot reference label inside atomic or d_step (%s)", 678 c->name); 679 } 680 } 681 682 int 683 find_lab(Symbol *s, Symbol *c, int markit) 684 { Label *l; 685 686 for (l = labtab; l; l = l->nxt) 687 { if (strcmp(s->name, l->s->name) == 0 688 && strcmp(c->name, l->c->name) == 0) 689 { l->visible |= markit; 690 return (l->e->seqno); 691 } } 692 return 0; 693 } 694 695 void 696 pushbreak(void) 697 { Lbreak *r = (Lbreak *) emalloc(sizeof(Lbreak)); 698 Symbol *l; 699 char buf[64]; 700 701 sprintf(buf, ":b%d", break_id++); 702 l = lookup(buf); 703 r->l = l; 704 r->nxt = breakstack; 705 breakstack = r; 706 } 707 708 Symbol * 709 break_dest(void) 710 { 711 if (!breakstack) 712 fatal("misplaced break statement", (char *)0); 713 return breakstack->l; 714 } 715 716 void 717 make_atomic(Sequence *s, int added) 718 { Element *f; 719 720 walk_atomic(s->frst, s->last, added); 721 722 f = s->last; 723 switch (f->n->ntyp) { /* is last step basic stmnt or sequence ? */ 724 case NON_ATOMIC: 725 case ATOMIC: 726 /* redo and search for the last step of that sequence */ 727 make_atomic(f->n->sl->this, added); 728 break; 729 730 case UNLESS: 731 /* escapes are folded into main sequence */ 732 make_atomic(f->sub->this, added); 733 break; 734 735 default: 736 f->status &= ~ATOM; 737 f->status |= L_ATOM; 738 break; 739 } 740 } 741 742 static void 743 walk_atomic(Element *a, Element *b, int added) 744 { Element *f; Symbol *ofn; int oln; 745 SeqList *h; 746 747 ofn = Fname; 748 oln = lineno; 749 for (f = a; ; f = f->nxt) 750 { f->status |= (ATOM|added); 751 switch (f->n->ntyp) { 752 case ATOMIC: 753 if (verbose&32) 754 printf("spin: warning, line %3d %s, atomic inside %s (ignored)\n", 755 f->n->ln, f->n->fn->name, (added)?"d_step":"atomic"); 756 goto mknonat; 757 case D_STEP: 758 if (!(verbose&32)) 759 { if (added) goto mknonat; 760 break; 761 } 762 printf("spin: warning, line %3d %s, d_step inside ", 763 f->n->ln, f->n->fn->name); 764 if (added) 765 { printf("d_step (ignored)\n"); 766 goto mknonat; 767 } 768 printf("atomic\n"); 769 break; 770 case NON_ATOMIC: 771 mknonat: f->n->ntyp = NON_ATOMIC; /* can jump here */ 772 h = f->n->sl; 773 walk_atomic(h->this->frst, h->this->last, added); 774 break; 775 case UNLESS: 776 if (added) 777 { printf("spin: error, line %3d %s, unless in d_step (ignored)\n", 778 f->n->ln, f->n->fn->name); 779 } 780 } 781 for (h = f->sub; h; h = h->nxt) 782 walk_atomic(h->this->frst, h->this->last, added); 783 if (f == b) 784 break; 785 } 786 Fname = ofn; 787 lineno = oln; 788 } 789 790 void 791 dumplabels(void) 792 { Label *l; 793 794 for (l = labtab; l; l = l->nxt) 795 if (l->c != 0 && l->s->name[0] != ':') 796 printf("label %s %d <%s>\n", 797 l->s->name, l->e->seqno, l->c->name); 798 } 799