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