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