0b55f123 |
1 | /***** spin: sym.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, *owner; |
16 | extern int lineno, depth, verbose, NamesNotAdded, deadvar, has_hidden; |
17 | extern short has_xu; |
18 | |
19 | Symbol *context = ZS; |
20 | Ordered *all_names = (Ordered *)0; |
21 | int Nid = 0; |
22 | |
23 | Lextok *Mtype = (Lextok *) 0; |
24 | |
25 | static Ordered *last_name = (Ordered *)0; |
26 | static Symbol *symtab[Nhash+1]; |
27 | static Lextok *runstmnts = ZN; |
28 | |
29 | static int |
30 | samename(Symbol *a, Symbol *b) |
31 | { |
32 | if (!a && !b) return 1; |
33 | if (!a || !b) return 0; |
34 | return !strcmp(a->name, b->name); |
35 | } |
36 | |
37 | int |
38 | hash(char *s) |
39 | { int h=0; |
40 | |
41 | while (*s) |
42 | { h += *s++; |
43 | h <<= 1; |
44 | if (h&(Nhash+1)) |
45 | h |= 1; |
46 | } |
47 | return h&Nhash; |
48 | } |
49 | |
50 | Symbol * |
51 | lookup(char *s) |
52 | { Symbol *sp; Ordered *no; |
53 | int h = hash(s); |
54 | |
55 | for (sp = symtab[h]; sp; sp = sp->next) |
56 | if (strcmp(sp->name, s) == 0 |
57 | && samename(sp->context, context) |
58 | && samename(sp->owner, owner)) |
59 | return sp; /* found */ |
60 | |
61 | if (context) /* in proctype */ |
62 | for (sp = symtab[h]; sp; sp = sp->next) |
63 | if (strcmp(sp->name, s) == 0 |
64 | && !sp->context |
65 | && samename(sp->owner, owner)) |
66 | return sp; /* global */ |
67 | |
68 | sp = (Symbol *) emalloc(sizeof(Symbol)); |
69 | sp->name = (char *) emalloc(strlen(s) + 1); |
70 | strcpy(sp->name, s); |
71 | sp->nel = 1; |
72 | sp->setat = depth; |
73 | sp->context = context; |
74 | sp->owner = owner; /* if fld in struct */ |
75 | |
76 | if (NamesNotAdded == 0) |
77 | { sp->next = symtab[h]; |
78 | symtab[h] = sp; |
79 | no = (Ordered *) emalloc(sizeof(Ordered)); |
80 | no->entry = sp; |
81 | if (!last_name) |
82 | last_name = all_names = no; |
83 | else |
84 | { last_name->next = no; |
85 | last_name = no; |
86 | } } |
87 | |
88 | return sp; |
89 | } |
90 | |
91 | void |
92 | trackvar(Lextok *n, Lextok *m) |
93 | { Symbol *sp = n->sym; |
94 | |
95 | if (!sp) return; /* a structure list */ |
96 | switch (m->ntyp) { |
97 | case NAME: |
98 | if (m->sym->type != BIT) |
99 | { sp->hidden |= 4; |
100 | if (m->sym->type != BYTE) |
101 | sp->hidden |= 8; |
102 | } |
103 | break; |
104 | case CONST: |
105 | if (m->val != 0 && m->val != 1) |
106 | sp->hidden |= 4; |
107 | if (m->val < 0 || m->val > 256) |
108 | sp->hidden |= 8; /* ditto byte-equiv */ |
109 | break; |
110 | default: /* unknown */ |
111 | sp->hidden |= (4|8); /* not known bit-equiv */ |
112 | } |
113 | } |
114 | |
115 | void |
116 | trackrun(Lextok *n) |
117 | { |
118 | runstmnts = nn(ZN, 0, n, runstmnts); |
119 | } |
120 | |
121 | void |
122 | checkrun(Symbol *parnm, int posno) |
123 | { Lextok *n, *now, *v; int i, m; |
124 | int res = 0; char buf[16], buf2[16]; |
125 | |
126 | for (n = runstmnts; n; n = n->rgt) |
127 | { now = n->lft; |
128 | if (now->sym != parnm->context) |
129 | continue; |
130 | for (v = now->lft, i = 0; v; v = v->rgt, i++) |
131 | if (i == posno) |
132 | { m = v->lft->ntyp; |
133 | if (m == CONST) |
134 | { m = v->lft->val; |
135 | if (m != 0 && m != 1) |
136 | res |= 4; |
137 | if (m < 0 || m > 256) |
138 | res |= 8; |
139 | } else if (m == NAME) |
140 | { m = v->lft->sym->type; |
141 | if (m != BIT) |
142 | { res |= 4; |
143 | if (m != BYTE) |
144 | res |= 8; |
145 | } |
146 | } else |
147 | res |= (4|8); /* unknown */ |
148 | break; |
149 | } } |
150 | if (!(res&4) || !(res&8)) |
151 | { if (!(verbose&32)) return; |
152 | strcpy(buf2, (!(res&4))?"bit":"byte"); |
153 | sputtype(buf, parnm->type); |
154 | i = (int) strlen(buf); |
155 | while (i > 0 && buf[--i] == ' ') buf[i] = '\0'; |
156 | if (i == 0 || strcmp(buf, buf2) == 0) return; |
157 | prehint(parnm); |
158 | printf("proctype %s, '%s %s' could be declared", |
159 | parnm->context?parnm->context->name:"", buf, parnm->name); |
160 | printf(" '%s %s'\n", buf2, parnm->name); |
161 | } |
162 | } |
163 | |
164 | void |
165 | trackchanuse(Lextok *m, Lextok *w, int t) |
166 | { Lextok *n = m; int cnt = 1; |
167 | while (n) |
168 | { if (n->lft |
169 | && n->lft->sym |
170 | && n->lft->sym->type == CHAN) |
171 | setaccess(n->lft->sym, w?w->sym:ZS, cnt, t); |
172 | n = n->rgt; cnt++; |
173 | } |
174 | } |
175 | |
176 | void |
177 | setptype(Lextok *n, int t, Lextok *vis) /* predefined types */ |
178 | { int oln = lineno, cnt = 1; extern int Expand_Ok; |
179 | |
180 | while (n) |
181 | { if (n->sym->type && !(n->sym->hidden&32)) |
182 | { lineno = n->ln; Fname = n->fn; |
183 | non_fatal("redeclaration of '%s'", n->sym->name); |
184 | lineno = oln; |
185 | } |
186 | n->sym->type = (short) t; |
187 | |
188 | if (Expand_Ok) |
189 | { n->sym->hidden |= (4|8|16); /* formal par */ |
190 | if (t == CHAN) |
191 | setaccess(n->sym, ZS, cnt, 'F'); |
192 | } |
193 | if (t == UNSIGNED) |
194 | { if (n->sym->nbits < 0 || n->sym->nbits >= 32) |
195 | fatal("(%s) has invalid width-field", n->sym->name); |
196 | if (n->sym->nbits == 0) |
197 | { n->sym->nbits = 16; |
198 | non_fatal("unsigned without width-field", 0); |
199 | } |
200 | } else if (n->sym->nbits > 0) |
201 | { non_fatal("(%s) only an unsigned can have width-field", |
202 | n->sym->name); |
203 | } |
204 | if (vis) |
205 | { if (strncmp(vis->sym->name, ":hide:", (size_t) 6) == 0) |
206 | { n->sym->hidden |= 1; |
207 | has_hidden++; |
208 | if (t == BIT) |
209 | fatal("bit variable (%s) cannot be hidden", |
210 | n->sym->name); |
211 | } else if (strncmp(vis->sym->name, ":show:", (size_t) 6) == 0) |
212 | { n->sym->hidden |= 2; |
213 | } else if (strncmp(vis->sym->name, ":local:", (size_t) 7) == 0) |
214 | { n->sym->hidden |= 64; |
215 | } |
216 | } |
217 | if (t == CHAN) |
218 | n->sym->Nid = ++Nid; |
219 | else |
220 | { n->sym->Nid = 0; |
221 | if (n->sym->ini |
222 | && n->sym->ini->ntyp == CHAN) |
223 | { Fname = n->fn; |
224 | lineno = n->ln; |
225 | fatal("chan initializer for non-channel %s", |
226 | n->sym->name); |
227 | } |
228 | } |
229 | if (n->sym->nel <= 0) |
230 | { lineno = n->ln; Fname = n->fn; |
231 | non_fatal("bad array size for '%s'", n->sym->name); |
232 | lineno = oln; |
233 | } |
234 | n = n->rgt; cnt++; |
235 | } |
236 | } |
237 | |
238 | static void |
239 | setonexu(Symbol *sp, int t) |
240 | { |
241 | sp->xu |= t; |
242 | if (t == XR || t == XS) |
243 | { if (sp->xup[t-1] |
244 | && strcmp(sp->xup[t-1]->name, context->name)) |
245 | { printf("error: x[rs] claims from %s and %s\n", |
246 | sp->xup[t-1]->name, context->name); |
247 | non_fatal("conflicting claims on chan '%s'", |
248 | sp->name); |
249 | } |
250 | sp->xup[t-1] = context; |
251 | } |
252 | } |
253 | |
254 | static void |
255 | setallxu(Lextok *n, int t) |
256 | { Lextok *fp, *tl; |
257 | |
258 | for (fp = n; fp; fp = fp->rgt) |
259 | for (tl = fp->lft; tl; tl = tl->rgt) |
260 | { if (tl->sym->type == STRUCT) |
261 | setallxu(tl->sym->Slst, t); |
262 | else if (tl->sym->type == CHAN) |
263 | setonexu(tl->sym, t); |
264 | } |
265 | } |
266 | |
267 | Lextok *Xu_List = (Lextok *) 0; |
268 | |
269 | void |
270 | setxus(Lextok *p, int t) |
271 | { Lextok *m, *n; |
272 | |
273 | has_xu = 1; |
274 | if (!context) |
275 | { lineno = p->ln; |
276 | Fname = p->fn; |
277 | fatal("non-local x[rs] assertion", (char *)0); |
278 | } |
279 | for (m = p; m; m = m->rgt) |
280 | { Lextok *Xu_new = (Lextok *) emalloc(sizeof(Lextok)); |
281 | Xu_new->val = t; |
282 | Xu_new->lft = m->lft; |
283 | Xu_new->sym = context; |
284 | Xu_new->rgt = Xu_List; |
285 | Xu_List = Xu_new; |
286 | |
287 | n = m->lft; |
288 | if (n->sym->type == STRUCT) |
289 | setallxu(n->sym->Slst, t); |
290 | else if (n->sym->type == CHAN) |
291 | setonexu(n->sym, t); |
292 | else |
293 | { int oln = lineno; |
294 | lineno = n->ln; Fname = n->fn; |
295 | non_fatal("xr or xs of non-chan '%s'", |
296 | n->sym->name); |
297 | lineno = oln; |
298 | } |
299 | } |
300 | } |
301 | |
302 | void |
303 | setmtype(Lextok *m) |
304 | { Lextok *n; |
305 | int cnt, oln = lineno; |
306 | |
307 | if (m) { lineno = m->ln; Fname = m->fn; } |
308 | |
309 | if (!Mtype) |
310 | Mtype = m; |
311 | else |
312 | { for (n = Mtype; n->rgt; n = n->rgt) |
313 | ; |
314 | n->rgt = m; /* concatenate */ |
315 | } |
316 | |
317 | for (n = Mtype, cnt = 1; n; n = n->rgt, cnt++) /* syntax check */ |
318 | { if (!n->lft || !n->lft->sym |
319 | || n->lft->ntyp != NAME |
320 | || n->lft->lft) /* indexed variable */ |
321 | fatal("bad mtype definition", (char *)0); |
322 | |
323 | /* label the name */ |
324 | if (n->lft->sym->type != MTYPE) |
325 | { n->lft->sym->hidden |= 128; /* is used */ |
326 | n->lft->sym->type = MTYPE; |
327 | n->lft->sym->ini = nn(ZN,CONST,ZN,ZN); |
328 | n->lft->sym->ini->val = cnt; |
329 | } else if (n->lft->sym->ini->val != cnt) |
330 | non_fatal("name %s appears twice in mtype declaration", |
331 | n->lft->sym->name); |
332 | } |
333 | lineno = oln; |
334 | if (cnt > 256) |
335 | fatal("too many mtype elements (>255)", (char *)0); |
336 | } |
337 | |
338 | int |
339 | ismtype(char *str) /* name to number */ |
340 | { Lextok *n; |
341 | int cnt = 1; |
342 | |
343 | for (n = Mtype; n; n = n->rgt) |
344 | { if (strcmp(str, n->lft->sym->name) == 0) |
345 | return cnt; |
346 | cnt++; |
347 | } |
348 | return 0; |
349 | } |
350 | |
351 | int |
352 | sputtype(char *foo, int m) |
353 | { |
354 | switch (m) { |
355 | case UNSIGNED: strcpy(foo, "unsigned "); break; |
356 | case BIT: strcpy(foo, "bit "); break; |
357 | case BYTE: strcpy(foo, "byte "); break; |
358 | case CHAN: strcpy(foo, "chan "); break; |
359 | case SHORT: strcpy(foo, "short "); break; |
360 | case INT: strcpy(foo, "int "); break; |
361 | case MTYPE: strcpy(foo, "mtype "); break; |
362 | case STRUCT: strcpy(foo, "struct"); break; |
363 | case PROCTYPE: strcpy(foo, "proctype"); break; |
364 | case LABEL: strcpy(foo, "label "); return 0; |
365 | default: strcpy(foo, "value "); return 0; |
366 | } |
367 | return 1; |
368 | } |
369 | |
370 | |
371 | static int |
372 | puttype(int m) |
373 | { char buf[128]; |
374 | |
375 | if (sputtype(buf, m)) |
376 | { printf("%s", buf); |
377 | return 1; |
378 | } |
379 | return 0; |
380 | } |
381 | |
382 | void |
383 | symvar(Symbol *sp) |
384 | { Lextok *m; |
385 | |
386 | if (!puttype(sp->type)) |
387 | return; |
388 | |
389 | printf("\t"); |
390 | if (sp->owner) printf("%s.", sp->owner->name); |
391 | printf("%s", sp->name); |
392 | if (sp->nel > 1) printf("[%d]", sp->nel); |
393 | |
394 | if (sp->type == CHAN) |
395 | printf("\t%d", (sp->ini)?sp->ini->val:0); |
396 | else if (sp->type == STRUCT) /* Frank Weil, 2.9.8 */ |
397 | printf("\t%s", sp->Snm->name); |
398 | else |
399 | printf("\t%d", eval(sp->ini)); |
400 | |
401 | if (sp->owner) |
402 | printf("\t<:struct-field:>"); |
403 | else |
404 | if (!sp->context) |
405 | printf("\t<:global:>"); |
406 | else |
407 | printf("\t<%s>", sp->context->name); |
408 | |
409 | if (sp->Nid < 0) /* formal parameter */ |
410 | printf("\t<parameter %d>", -(sp->Nid)); |
411 | else |
412 | printf("\t<variable>"); |
413 | if (sp->type == CHAN && sp->ini) |
414 | { int i; |
415 | for (m = sp->ini->rgt, i = 0; m; m = m->rgt) |
416 | i++; |
417 | printf("\t%d\t", i); |
418 | for (m = sp->ini->rgt; m; m = m->rgt) |
419 | { if (m->ntyp == STRUCT) |
420 | printf("struct %s", m->sym->name); |
421 | else |
422 | (void) puttype(m->ntyp); |
423 | if (m->rgt) printf("\t"); |
424 | } |
425 | } |
426 | printf("\n"); |
427 | } |
428 | |
429 | void |
430 | symdump(void) |
431 | { Ordered *walk; |
432 | |
433 | for (walk = all_names; walk; walk = walk->next) |
434 | symvar(walk->entry); |
435 | } |
436 | |
437 | void |
438 | chname(Symbol *sp) |
439 | { printf("chan "); |
440 | if (sp->context) printf("%s-", sp->context->name); |
441 | if (sp->owner) printf("%s.", sp->owner->name); |
442 | printf("%s", sp->name); |
443 | if (sp->nel > 1) printf("[%d]", sp->nel); |
444 | printf("\t"); |
445 | } |
446 | |
447 | static struct X { |
448 | int typ; char *nm; |
449 | } xx[] = { |
450 | { 'A', "exported as run parameter" }, |
451 | { 'F', "imported as proctype parameter" }, |
452 | { 'L', "used as l-value in asgnmnt" }, |
453 | { 'V', "used as r-value in asgnmnt" }, |
454 | { 'P', "polled in receive stmnt" }, |
455 | { 'R', "used as parameter in receive stmnt" }, |
456 | { 'S', "used as parameter in send stmnt" }, |
457 | { 'r', "received from" }, |
458 | { 's', "sent to" }, |
459 | }; |
460 | |
461 | static void |
462 | chan_check(Symbol *sp) |
463 | { Access *a; int i, b=0, d; |
464 | |
465 | if (verbose&1) goto report; /* -C -g */ |
466 | |
467 | for (a = sp->access; a; a = a->lnk) |
468 | if (a->typ == 'r') |
469 | b |= 1; |
470 | else if (a->typ == 's') |
471 | b |= 2; |
472 | if (b == 3 || (sp->hidden&16)) /* balanced or formal par */ |
473 | return; |
474 | report: |
475 | chname(sp); |
476 | for (i = d = 0; i < (int) (sizeof(xx)/sizeof(struct X)); i++) |
477 | { b = 0; |
478 | for (a = sp->access; a; a = a->lnk) |
479 | if (a->typ == xx[i].typ) b++; |
480 | if (b == 0) continue; d++; |
481 | printf("\n\t%s by: ", xx[i].nm); |
482 | for (a = sp->access; a; a = a->lnk) |
483 | if (a->typ == xx[i].typ) |
484 | { printf("%s", a->who->name); |
485 | if (a->what) printf(" to %s", a->what->name); |
486 | if (a->cnt) printf(" par %d", a->cnt); |
487 | if (--b > 0) printf(", "); |
488 | } |
489 | } |
490 | printf("%s\n", (!d)?"\n\tnever used under this name":""); |
491 | } |
492 | |
493 | void |
494 | chanaccess(void) |
495 | { Ordered *walk; |
496 | char buf[128]; |
497 | extern int Caccess, separate; |
498 | extern short has_code; |
499 | |
500 | for (walk = all_names; walk; walk = walk->next) |
501 | { if (!walk->entry->owner) |
502 | switch (walk->entry->type) { |
503 | case CHAN: |
504 | if (Caccess) chan_check(walk->entry); |
505 | break; |
506 | case MTYPE: |
507 | case BIT: |
508 | case BYTE: |
509 | case SHORT: |
510 | case INT: |
511 | case UNSIGNED: |
512 | if ((walk->entry->hidden&128)) /* was: 32 */ |
513 | continue; |
514 | |
515 | if (!separate |
516 | && !walk->entry->context |
517 | && !has_code |
518 | && deadvar) |
519 | walk->entry->hidden |= 1; /* auto-hide */ |
520 | |
521 | if (!(verbose&32) || has_code) continue; |
522 | |
523 | printf("spin: warning, %s, ", Fname->name); |
524 | sputtype(buf, walk->entry->type); |
525 | if (walk->entry->context) |
526 | printf("proctype %s", |
527 | walk->entry->context->name); |
528 | else |
529 | printf("global"); |
530 | printf(", '%s%s' variable is never used\n", |
531 | buf, walk->entry->name); |
532 | } } |
533 | } |