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