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