emit1.c revision 1.59 1 /* $NetBSD: emit1.c,v 1.59 2021/09/12 10:06:03 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.59 2021/09/12 10:06:03 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 /* 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 (ts == STRUCT || ts == UNION) {
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
191 /* reset buffer */
192 outclr();
193
194 /*
195 * line number of .c source, 'd' for declaration, Id of current
196 * source (.c or .h), and line in current source.
197 */
198 outint(csrc_pos.p_line);
199 outchar('d');
200 outint(get_filename_id(sym->s_def_pos.p_file));
201 outchar('.');
202 outint(sym->s_def_pos.p_line);
203
204 /* flags */
205
206 if (def == DEF)
207 outchar('d'); /* defined */
208 else if (def == TDEF)
209 outchar('t'); /* tentative defined */
210 else {
211 lint_assert(def == DECL);
212 outchar('e'); /* declared */
213 }
214
215 if (llibflg && def != DECL) {
216 /*
217 * mark it as used so lint2 does not complain about
218 * unused symbols in libraries
219 */
220 outchar('u');
221 }
222
223 if (sc == STATIC)
224 outchar('s');
225
226 /* name of the symbol */
227 outname(sym->s_name);
228
229 /* renamed name of symbol, if necessary */
230 if (sym->s_rename != NULL) {
231 outchar('r');
232 outname(sym->s_rename);
233 }
234
235 /* type of the symbol */
236 outtype(sym->s_type);
237 }
238
239 /*
240 * write information about function definition
241 *
242 * this is also done for static functions so we are able to check if
243 * they are called with proper argument types
244 */
245 void
246 outfdef(const sym_t *fsym, const pos_t *posp, bool rval, bool osdef,
247 const sym_t *args)
248 {
249 int narg;
250 const sym_t *arg;
251
252 /* reset the buffer */
253 outclr();
254
255 /*
256 * line number of .c source, 'd' for declaration, Id of current
257 * source (.c or .h), and line in current source
258 *
259 * we are already at the end of the function. If we are in the
260 * .c source, posp->p_line is correct, otherwise csrc_pos.p_line
261 * (for functions defined in header files).
262 */
263 if (posp->p_file == csrc_pos.p_file) {
264 outint(posp->p_line);
265 } else {
266 outint(csrc_pos.p_line);
267 }
268 outchar('d');
269 outint(get_filename_id(posp->p_file));
270 outchar('.');
271 outint(posp->p_line);
272
273 /* flags */
274
275 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
276 if (printflike_argnum != -1) {
277 nvararg = printflike_argnum;
278 } else if (scanflike_argnum != -1) {
279 nvararg = scanflike_argnum;
280 }
281
282 if (nvararg != -1) {
283 outchar('v');
284 outint(nvararg);
285 }
286 if (scanflike_argnum != -1) {
287 outchar('S');
288 outint(scanflike_argnum);
289 }
290 if (printflike_argnum != -1) {
291 outchar('P');
292 outint(printflike_argnum);
293 }
294 nvararg = printflike_argnum = scanflike_argnum = -1;
295
296 outchar('d');
297
298 if (rval)
299 outchar('r'); /* has return value */
300
301 if (llibflg)
302 /*
303 * mark it as used so lint2 does not complain about
304 * unused symbols in libraries
305 */
306 outchar('u');
307
308 if (osdef)
309 outchar('o'); /* old style function definition */
310
311 if (fsym->s_inline)
312 outchar('i');
313
314 if (fsym->s_scl == STATIC)
315 outchar('s');
316
317 /* name of function */
318 outname(fsym->s_name);
319
320 /* renamed name of function, if necessary */
321 if (fsym->s_rename != NULL) {
322 outchar('r');
323 outname(fsym->s_rename);
324 }
325
326 /* argument types and return value */
327 if (osdef) {
328 narg = 0;
329 for (arg = args; arg != NULL; arg = arg->s_next)
330 narg++;
331 outchar('f');
332 outint(narg);
333 for (arg = args; arg != NULL; arg = arg->s_next)
334 outtype(arg->s_type);
335 outtype(fsym->s_type->t_subt);
336 } else {
337 outtype(fsym->s_type);
338 }
339 }
340
341 /*
342 * write out all information necessary for lint2 to check function
343 * calls
344 *
345 * retval_used is set if the return value is used (assigned to a variable)
346 * retval_discarded is set if the return value is neither used nor ignored
347 * (that is, cast to void)
348 */
349 void
350 outcall(const tnode_t *tn, bool retval_used, bool retval_discarded)
351 {
352 tnode_t *args, *arg;
353 int narg, n, i;
354 int64_t q;
355 tspec_t t;
356
357 /* reset buffer */
358 outclr();
359
360 /*
361 * line number of .c source, 'c' for function call, Id of current
362 * source (.c or .h), and line in current source
363 */
364 outint(csrc_pos.p_line);
365 outchar('c');
366 outint(get_filename_id(curr_pos.p_file));
367 outchar('.');
368 outint(curr_pos.p_line);
369
370 /*
371 * flags; 'u' and 'i' must be last to make sure a letter
372 * is between the numeric argument of a flag and the name of
373 * the function
374 */
375 narg = 0;
376 args = tn->tn_right;
377 for (arg = args; arg != NULL; arg = arg->tn_right)
378 narg++;
379 /* information about arguments */
380 for (n = 1; n <= narg; n++) {
381 /* the last argument is the top one in the tree */
382 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
383 continue;
384 arg = arg->tn_left;
385 if (arg->tn_op == CON) {
386 if (is_integer(t = arg->tn_type->t_tspec)) {
387 /*
388 * XXX it would probably be better to
389 * explicitly test the sign
390 */
391 if ((q = arg->tn_val->v_quad) == 0) {
392 /* zero constant */
393 outchar('z');
394 } else if (!msb(q, t)) {
395 /* positive if cast to signed */
396 outchar('p');
397 } else {
398 /* negative if cast to signed */
399 outchar('n');
400 }
401 outint(n);
402 }
403 } else if (arg->tn_op == ADDR &&
404 arg->tn_left->tn_op == STRING &&
405 arg->tn_left->tn_string->st_tspec == CHAR) {
406 /* constant string, write all format specifiers */
407 outchar('s');
408 outint(n);
409 outfstrg(arg->tn_left->tn_string);
410 }
411
412 }
413 /* return value discarded/used/ignored */
414 outchar((char)(retval_discarded ? 'd' : (retval_used ? 'u' : 'i')));
415
416 /* name of the called function */
417 outname(tn->tn_left->tn_left->tn_sym->s_name);
418
419 /* types of arguments */
420 outchar('f');
421 outint(narg);
422 for (n = 1; n <= narg; n++) {
423 /* the last argument is the top one in the tree */
424 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
425 continue;
426 outtype(arg->tn_left->tn_type);
427 }
428 /* expected type of return value */
429 outtype(tn->tn_type);
430 }
431
432 /* write a character to the output buffer, quoted if necessary */
433 static void
434 outqchar(char c)
435 {
436
437 if (ch_isprint(c) && c != '\\' && c != '"' && c != '\'') {
438 outchar(c);
439 return;
440 }
441
442 outchar('\\');
443 switch (c) {
444 case '\\':
445 outchar('\\');
446 break;
447 case '"':
448 outchar('"');
449 break;
450 case '\'':
451 outchar('\'');
452 break;
453 case '\b':
454 outchar('b');
455 break;
456 case '\t':
457 outchar('t');
458 break;
459 case '\n':
460 outchar('n');
461 break;
462 case '\f':
463 outchar('f');
464 break;
465 case '\r':
466 outchar('r');
467 break;
468 case '\v':
469 outchar('v');
470 break;
471 case '\a':
472 outchar('a');
473 break;
474 default:
475 outchar((char)((((unsigned char)c >> 6) & 07) + '0'));
476 outchar((char)((((unsigned char)c >> 3) & 07) + '0'));
477 outchar((char)((c & 07) + '0'));
478 break;
479 }
480 }
481
482 /*
483 * extracts potential format specifiers for printf() and scanf() and
484 * writes them, enclosed in "" and quoted if necessary, to the output buffer
485 */
486 static void
487 outfstrg(strg_t *strg)
488 {
489 char c, oc;
490 bool first;
491 const char *cp;
492
493 lint_assert(strg->st_tspec == CHAR);
494
495 cp = (const char *)strg->st_cp;
496
497 outchar('"');
498
499 c = *cp++;
500
501 while (c != '\0') {
502
503 if (c != '%') {
504 c = *cp++;
505 continue;
506 }
507
508 outchar('%');
509 c = *cp++;
510
511 /* flags for printf and scanf and *-fieldwidth for printf */
512 while (c == '-' || c == '+' || c == ' ' ||
513 c == '#' || c == '0' || c == '*') {
514 outchar(c);
515 c = *cp++;
516 }
517
518 /* numeric field width */
519 while (ch_isdigit(c)) {
520 outchar(c);
521 c = *cp++;
522 }
523
524 /* precision for printf */
525 if (c == '.') {
526 outchar(c);
527 c = *cp++;
528 if (c == '*') {
529 outchar(c);
530 c = *cp++;
531 } else {
532 while (ch_isdigit(c)) {
533 outchar(c);
534 c = *cp++;
535 }
536 }
537 }
538
539 /* h, l, L and q flags for printf and scanf */
540 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
541 outchar(c);
542 c = *cp++;
543 }
544
545 /*
546 * The last character. It is always written, so we can detect
547 * invalid format specifiers.
548 */
549 if (c != '\0') {
550 outqchar(c);
551 oc = c;
552 c = *cp++;
553 /*
554 * handle [ for scanf. [-] means that a minus sign
555 * was found at an undefined position.
556 */
557 if (oc == '[') {
558 if (c == '^')
559 c = *cp++;
560 if (c == ']')
561 c = *cp++;
562 first = true;
563 while (c != '\0' && c != ']') {
564 if (c == '-') {
565 if (!first && *cp != ']')
566 outchar(c);
567 }
568 first = false;
569 c = *cp++;
570 }
571 if (c == ']') {
572 outchar(c);
573 c = *cp++;
574 }
575 }
576 }
577
578 }
579
580 outchar('"');
581 }
582
583 /*
584 * writes a record if sym was used
585 */
586 void
587 outusg(const sym_t *sym)
588 {
589 /* reset buffer */
590 outclr();
591
592 /*
593 * line number of .c source, 'u' for used, Id of current
594 * source (.c or .h), and line in current source
595 */
596 outint(csrc_pos.p_line);
597 outchar('u');
598 outint(get_filename_id(curr_pos.p_file));
599 outchar('.');
600 outint(curr_pos.p_line);
601
602 /* necessary to delimit both numbers */
603 outchar('x');
604
605 outname(sym->s_name);
606 }
607