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