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