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