emit1.c revision 1.40 1 /* $NetBSD: emit1.c,v 1.40 2021/02/19 22:16:12 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
5 * Copyright (c) 1994, 1995 Jochen Pohl
6 * All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Jochen Pohl for
19 * The NetBSD Project.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38
39 #include <sys/cdefs.h>
40 #if defined(__RCSID) && !defined(lint)
41 __RCSID("$NetBSD: emit1.c,v 1.40 2021/02/19 22:16:12 rillig Exp $");
42 #endif
43
44 #include <ctype.h>
45
46 #include "lint1.h"
47
48 static void outtt(sym_t *, sym_t *);
49 static void outfstrg(strg_t *);
50
51 /*
52 * Write type into the output buffer.
53 * The type is written as a sequence of substrings, each of which describes a
54 * node of type type_t
55 * a node is encoded as follows:
56 * _Bool B
57 * _Complex float s X
58 * _Complex double X
59 * _Complex long double l X
60 * char C
61 * signed char s C
62 * unsigned char u C
63 * short S
64 * unsigned short u S
65 * int I
66 * unsigned int u I
67 * long L
68 * unsigned long u L
69 * long long Q
70 * unsigned long long u Q
71 * float s D
72 * double D
73 * long double l D
74 * void V
75 * * P
76 * [n] A n
77 * () F
78 * (void) F 0
79 * (n arguments) F n arg1 arg2 ... argn
80 * (n arguments, ...) F n arg1 arg2 ... argn-1 E
81 * (a, b, c, ...) f n arg1 arg2 ...
82 * enum tag e T tag_or_typename
83 * struct tag s T tag_or_typename
84 * union tag u T tag_or_typename
85 *
86 * tag_or_typename 0 no tag or type name
87 * 1 n tag Tag
88 * 2 n typename only type name
89 *
90 * spaces are only for better readability
91 * additionally it is possible to prepend the characters 'c' (for const)
92 * and 'v' (for volatile)
93 */
94 void
95 outtype(const type_t *tp)
96 {
97 int t, s, na;
98 sym_t *arg;
99 tspec_t ts;
100
101 while (tp != NULL) {
102 if ((ts = tp->t_tspec) == INT && tp->t_isenum)
103 ts = ENUM;
104 switch (ts) {
105 case BOOL: t = 'B'; s = '\0'; break;
106 case CHAR: t = 'C'; s = '\0'; break;
107 case SCHAR: t = 'C'; s = 's'; break;
108 case UCHAR: t = 'C'; s = 'u'; break;
109 case SHORT: t = 'S'; s = '\0'; break;
110 case USHORT: t = 'S'; s = 'u'; break;
111 case INT: t = 'I'; s = '\0'; break;
112 case UINT: t = 'I'; s = 'u'; break;
113 case LONG: t = 'L'; s = '\0'; break;
114 case ULONG: t = 'L'; s = 'u'; break;
115 case QUAD: t = 'Q'; s = '\0'; break;
116 case UQUAD: t = 'Q'; s = 'u'; break;
117 case FLOAT: t = 'D'; s = 's'; break;
118 case DOUBLE: t = 'D'; s = '\0'; break;
119 case LDOUBLE: t = 'D'; s = 'l'; break;
120 case VOID: t = 'V'; s = '\0'; break;
121 case PTR: t = 'P'; s = '\0'; break;
122 case ARRAY: t = 'A'; s = '\0'; break;
123 case FUNC: t = 'F'; s = '\0'; break;
124 case ENUM: t = 'T'; s = 'e'; break;
125 case STRUCT: t = 'T'; s = 's'; break;
126 case UNION: t = 'T'; s = 'u'; break;
127 case FCOMPLEX: t = 'X'; s = 's'; break;
128 case DCOMPLEX: t = 'X'; s = '\0'; break;
129 case LCOMPLEX: t = 'X'; s = 'l'; break;
130 default:
131 lint_assert(/*CONSTCOND*/false);
132 }
133 if (tp->t_const)
134 outchar('c');
135 if (tp->t_volatile)
136 outchar('v');
137 if (s != '\0')
138 outchar(s);
139 outchar(t);
140 if (ts == ARRAY) {
141 outint(tp->t_dim);
142 } else if (ts == ENUM) {
143 outtt(tp->t_enum->etag, tp->t_enum->etdef);
144 } else if (ts == STRUCT || ts == UNION) {
145 outtt(tp->t_str->sou_tag, tp->t_str->sou_first_typedef);
146 } else if (ts == FUNC && tp->t_proto) {
147 na = 0;
148 for (arg = tp->t_args; arg != NULL; arg = arg->s_next)
149 na++;
150 if (tp->t_vararg)
151 na++;
152 outint(na);
153 for (arg = tp->t_args; arg != NULL; arg = arg->s_next)
154 outtype(arg->s_type);
155 if (tp->t_vararg)
156 outchar('E');
157 }
158 tp = tp->t_subt;
159 }
160 }
161
162 /*
163 * type to string
164 * used for debugging output
165 *
166 * it uses its own output buffer for conversion
167 */
168 const char *
169 ttos(const type_t *tp)
170 {
171 static ob_t tob;
172 ob_t tmp;
173
174 if (tob.o_buf == NULL) {
175 tob.o_len = 64;
176 tob.o_buf = tob.o_next = xmalloc(tob.o_len);
177 tob.o_end = tob.o_buf + tob.o_len;
178 }
179
180 tmp = ob;
181 ob = tob;
182 ob.o_next = ob.o_buf;
183 outtype(tp);
184 outchar('\0');
185 tob = ob;
186 ob = tmp;
187
188 return tob.o_buf;
189 }
190
191 /*
192 * write the name of a tag or typename
193 *
194 * if the tag is named, the name of the
195 * tag is written, otherwise, if a typename exists which
196 * refers to this tag, this typename is written
197 */
198 static void
199 outtt(sym_t *tag, sym_t *tdef)
200 {
201
202 /*
203 * 0 is no longer used.
204 */
205 if (tag->s_name != unnamed) {
206 outint(1);
207 outname(tag->s_name);
208 } else if (tdef != NULL) {
209 outint(2);
210 outname(tdef->s_name);
211 } else {
212 outint(3);
213 outint(tag->s_def_pos.p_line);
214 outchar('.');
215 outint(getfnid(tag->s_def_pos.p_file));
216 outchar('.');
217 outint(tag->s_def_pos.p_uniq);
218 }
219 }
220
221 /*
222 * write information about a globally declared/defined symbol
223 * with storage class extern
224 *
225 * information about function definitions are written in outfdef(),
226 * not here
227 */
228 void
229 outsym(const sym_t *sym, scl_t sc, def_t def)
230 {
231
232 /*
233 * Static function declarations must also be written to the output
234 * file. Compatibility of function declarations (for both static
235 * and extern functions) must be checked in lint2. Lint1 can't do
236 * this, especially not if functions are declared at block level
237 * before their first declaration at level 0.
238 */
239 if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
240 return;
241
242 /* reset buffer */
243 outclr();
244
245 /*
246 * line number of .c source, 'd' for declaration, Id of current
247 * source (.c or .h), and line in current source.
248 */
249 outint(csrc_pos.p_line);
250 outchar('d');
251 outint(getfnid(sym->s_def_pos.p_file));
252 outchar('.');
253 outint(sym->s_def_pos.p_line);
254
255 /* flags */
256
257 switch (def) {
258 case DEF:
259 /* defined */
260 outchar('d');
261 break;
262 case TDEF:
263 /* tentative defined */
264 outchar('t');
265 break;
266 case DECL:
267 /* declared */
268 outchar('e');
269 break;
270 default:
271 lint_assert(/*CONSTCOND*/false);
272 }
273 if (llibflg && def != DECL) {
274 /*
275 * mark it as used so we get no warnings from lint2 about
276 * unused symbols in libraries.
277 */
278 outchar('u');
279 }
280
281 if (sc == STATIC)
282 outchar('s');
283
284 /* name of the symbol */
285 outname(sym->s_name);
286
287 /* renamed name of symbol, if necessary */
288 if (sym->s_rename != NULL) {
289 outchar('r');
290 outname(sym->s_rename);
291 }
292
293 /* type of the symbol */
294 outtype(sym->s_type);
295 }
296
297 /*
298 * write information about function definition
299 *
300 * this is also done for static functions so we are able to check if
301 * they are called with proper argument types
302 */
303 void
304 outfdef(const sym_t *fsym, const pos_t *posp, bool rval, bool osdef,
305 const sym_t *args)
306 {
307 int narg;
308 const sym_t *arg;
309
310 /* reset the buffer */
311 outclr();
312
313 /*
314 * line number of .c source, 'd' for declaration, Id of current
315 * source (.c or .h), and line in current source
316 *
317 * we are already at the end of the function. If we are in the
318 * .c source, posp->p_line is correct, otherwise csrc_pos.p_line
319 * (for functions defined in header files).
320 */
321 if (posp->p_file == csrc_pos.p_file) {
322 outint(posp->p_line);
323 } else {
324 outint(csrc_pos.p_line);
325 }
326 outchar('d');
327 outint(getfnid(posp->p_file));
328 outchar('.');
329 outint(posp->p_line);
330
331 /* flags */
332
333 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
334 if (printflike_argnum != -1) {
335 nvararg = printflike_argnum;
336 } else if (scanflike_argnum != -1) {
337 nvararg = scanflike_argnum;
338 }
339
340 if (nvararg != -1) {
341 outchar('v');
342 outint(nvararg);
343 }
344 if (scanflike_argnum != -1) {
345 outchar('S');
346 outint(scanflike_argnum);
347 }
348 if (printflike_argnum != -1) {
349 outchar('P');
350 outint(printflike_argnum);
351 }
352 nvararg = printflike_argnum = scanflike_argnum = -1;
353
354 outchar('d');
355
356 if (rval)
357 /* has return value */
358 outchar('r');
359
360 if (llibflg)
361 /*
362 * mark it as used so lint2 does not complain about
363 * unused symbols in libraries
364 */
365 outchar('u');
366
367 if (osdef)
368 /* old style function definition */
369 outchar('o');
370
371 if (fsym->s_inline)
372 outchar('i');
373
374 if (fsym->s_scl == STATIC)
375 outchar('s');
376
377 /* name of function */
378 outname(fsym->s_name);
379
380 /* renamed name of function, if necessary */
381 if (fsym->s_rename != NULL) {
382 outchar('r');
383 outname(fsym->s_rename);
384 }
385
386 /* argument types and return value */
387 if (osdef) {
388 narg = 0;
389 for (arg = args; arg != NULL; arg = arg->s_next)
390 narg++;
391 outchar('f');
392 outint(narg);
393 for (arg = args; arg != NULL; arg = arg->s_next)
394 outtype(arg->s_type);
395 outtype(fsym->s_type->t_subt);
396 } else {
397 outtype(fsym->s_type);
398 }
399 }
400
401 /*
402 * write out all information necessary for lint2 to check function
403 * calls
404 *
405 * rvused is set if the return value is used (assigned to a variable)
406 * rvdisc is set if the return value is not used and not ignored
407 * (casted to void)
408 */
409 void
410 outcall(const tnode_t *tn, bool rvused, bool rvdisc)
411 {
412 tnode_t *args, *arg;
413 int narg, n, i;
414 int64_t q;
415 tspec_t t;
416
417 /* reset buffer */
418 outclr();
419
420 /*
421 * line number of .c source, 'c' for function call, Id of current
422 * source (.c or .h), and line in current source
423 */
424 outint(csrc_pos.p_line);
425 outchar('c');
426 outint(getfnid(curr_pos.p_file));
427 outchar('.');
428 outint(curr_pos.p_line);
429
430 /*
431 * flags; 'u' and 'i' must be last to make sure a letter
432 * is between the numeric argument of a flag and the name of
433 * the function
434 */
435 narg = 0;
436 args = tn->tn_right;
437 for (arg = args; arg != NULL; arg = arg->tn_right)
438 narg++;
439 /* information about arguments */
440 for (n = 1; n <= narg; n++) {
441 /* the last argument is the top one in the tree */
442 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
443 continue;
444 arg = arg->tn_left;
445 if (arg->tn_op == CON) {
446 if (is_integer(t = arg->tn_type->t_tspec)) {
447 /*
448 * XXX it would probably be better to
449 * explicitly test the sign
450 */
451 if ((q = arg->tn_val->v_quad) == 0) {
452 /* zero constant */
453 outchar('z');
454 } else if (msb(q, t, 0) == 0) {
455 /* positive if casted to signed */
456 outchar('p');
457 } else {
458 /* negative if casted to signed */
459 outchar('n');
460 }
461 outint(n);
462 }
463 } else if (arg->tn_op == ADDR &&
464 arg->tn_left->tn_op == STRING &&
465 arg->tn_left->tn_string->st_tspec == CHAR) {
466 /* constant string, write all format specifiers */
467 outchar('s');
468 outint(n);
469 outfstrg(arg->tn_left->tn_string);
470 }
471
472 }
473 /* return value discarded/used/ignored */
474 outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i'));
475
476 /* name of the called function */
477 outname(tn->tn_left->tn_left->tn_sym->s_name);
478
479 /* types of arguments */
480 outchar('f');
481 outint(narg);
482 for (n = 1; n <= narg; n++) {
483 /* the last argument is the top one in the tree */
484 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
485 continue;
486 outtype(arg->tn_left->tn_type);
487 }
488 /* expected type of return value */
489 outtype(tn->tn_type);
490 }
491
492 /*
493 * extracts potential format specifiers for printf() and scanf() and
494 * writes them, enclosed in "" and quoted if necessary, to the output buffer
495 */
496 static void
497 outfstrg(strg_t *strg)
498 {
499 int c, oc;
500 bool first;
501 u_char *cp;
502
503 lint_assert(strg->st_tspec == CHAR);
504
505 cp = strg->st_cp;
506
507 outchar('"');
508
509 c = *cp++;
510
511 while (c != '\0') {
512
513 if (c != '%') {
514 c = *cp++;
515 continue;
516 }
517
518 outqchar('%');
519 c = *cp++;
520
521 /* flags for printf and scanf and *-fieldwidth for printf */
522 while (c != '\0' && (c == '-' || c == '+' || c == ' ' ||
523 c == '#' || c == '0' || c == '*')) {
524 outqchar(c);
525 c = *cp++;
526 }
527
528 /* numeric field width */
529 while (c != '\0' && ch_isdigit(c)) {
530 outqchar(c);
531 c = *cp++;
532 }
533
534 /* precision for printf */
535 if (c == '.') {
536 outqchar(c);
537 if ((c = *cp++) == '*') {
538 outqchar(c);
539 c = *cp++;
540 } else {
541 while (c != '\0' && ch_isdigit(c)) {
542 outqchar(c);
543 c = *cp++;
544 }
545 }
546 }
547
548 /* h, l, L and q flags fpr printf and scanf */
549 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
550 outqchar(c);
551 c = *cp++;
552 }
553
554 /*
555 * The last character. It is always written so we can detect
556 * invalid format specifiers.
557 */
558 if (c != '\0') {
559 outqchar(c);
560 oc = c;
561 c = *cp++;
562 /*
563 * handle [ for scanf. [-] means that a minus sign
564 * was found at an undefined position.
565 */
566 if (oc == '[') {
567 if (c == '^')
568 c = *cp++;
569 if (c == ']')
570 c = *cp++;
571 first = true;
572 while (c != '\0' && c != ']') {
573 if (c == '-') {
574 if (!first && *cp != ']')
575 outqchar(c);
576 }
577 first = false;
578 c = *cp++;
579 }
580 if (c == ']') {
581 outqchar(c);
582 c = *cp++;
583 }
584 }
585 }
586
587 }
588
589 outchar('"');
590 }
591
592 /*
593 * writes a record if sym was used
594 */
595 void
596 outusg(const sym_t *sym)
597 {
598 /* reset buffer */
599 outclr();
600
601 /*
602 * line number of .c source, 'u' for used, Id of current
603 * source (.c or .h), and line in current source
604 */
605 outint(csrc_pos.p_line);
606 outchar('u');
607 outint(getfnid(curr_pos.p_file));
608 outchar('.');
609 outint(curr_pos.p_line);
610
611 /* necessary to delimit both numbers */
612 outchar('x');
613
614 outname(sym->s_name);
615 }
616