emit1.c revision 1.84 1 /* $NetBSD: emit1.c,v 1.84 2024/02/03 12:57:12 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.84 2024/02/03 12:57:12 rillig Exp $");
42 #endif
43
44 #include "lint1.h"
45
46 static void outtt(sym_t *, sym_t *);
47 static void outfstrg(const char *);
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 and
180 * extern functions) must be checked in lint2. Lint1 can't do this,
181 * especially not if functions are declared at block level before their
182 * 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 unused
209 * 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
238 if (posp->p_file == csrc_pos.p_file) {
239 outint(posp->p_line);
240 } else {
241 outint(csrc_pos.p_line);
242 }
243 outchar('d'); /* declaration */
244 outint(get_filename_id(posp->p_file));
245 outchar('.');
246 outint(posp->p_line);
247
248 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
249 if (printflike_argnum != -1) {
250 nvararg = printflike_argnum;
251 } else if (scanflike_argnum != -1) {
252 nvararg = scanflike_argnum;
253 }
254
255 if (nvararg != -1) {
256 outchar('v');
257 outint(nvararg);
258 }
259 if (scanflike_argnum != -1) {
260 outchar('S');
261 outint(scanflike_argnum);
262 }
263 if (printflike_argnum != -1) {
264 outchar('P');
265 outint(printflike_argnum);
266 }
267 nvararg = printflike_argnum = scanflike_argnum = -1;
268
269 outchar('d');
270
271 if (rval)
272 outchar('r'); /* has return value */
273
274 if (llibflg)
275 /*
276 * mark it as used so lint2 does not complain about unused
277 * symbols in libraries
278 */
279 outchar('u');
280
281 if (osdef)
282 outchar('o'); /* old-style function definition */
283
284 if (fsym->s_inline)
285 outchar('i');
286
287 if (fsym->s_scl == STATIC)
288 outchar('s');
289
290 outname(fsym->s_name);
291
292 if (fsym->s_rename != NULL) {
293 outchar('r');
294 outname(fsym->s_rename);
295 }
296
297 /* parameter types and return value */
298 if (osdef) {
299 narg = 0;
300 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next)
301 narg++;
302 outchar('f');
303 outint(narg);
304 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next)
305 outtype(arg->s_type);
306 outtype(fsym->s_type->t_subt);
307 } else {
308 outtype(fsym->s_type);
309 }
310 outchar('\n');
311 }
312
313 /*
314 * write out all information necessary for lint2 to check function
315 * calls
316 *
317 * retval_used is set if the return value is used (assigned to a variable)
318 * retval_discarded is set if the return value is neither used nor ignored
319 * (that is, cast to void)
320 */
321 void
322 outcall(const tnode_t *tn, bool retval_used, bool retval_discarded)
323 {
324 tnode_t *args, *arg;
325 int narg, i;
326
327 outint(csrc_pos.p_line);
328 outchar('c'); /* function call */
329 outint(get_filename_id(curr_pos.p_file));
330 outchar('.');
331 outint(curr_pos.p_line);
332
333 /*
334 * flags; 'u' and 'i' must be last to make sure a letter is between the
335 * numeric argument of a flag and the name of the function
336 */
337 narg = 0;
338 args = tn_ck_right(tn);
339 for (arg = args; arg != NULL; arg = tn_ck_right(arg))
340 narg++;
341 /* information about arguments */
342 for (int n = 1; n <= narg; n++) {
343 /* the last argument is the top one in the tree */
344 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
345 continue;
346 arg = arg->tn_left;
347 if (arg->tn_op == CON) {
348 tspec_t t = arg->tn_type->t_tspec;
349 if (is_integer(t)) {
350 /*
351 * XXX it would probably be better to
352 * explicitly test the sign
353 */
354 int64_t si = arg->tn_val.u.integer;
355 if (si == 0) {
356 /* zero constant */
357 outchar('z');
358 } else if (!msb(si, t)) {
359 /* positive if cast to signed */
360 outchar('p');
361 } else {
362 /* negative if cast to signed */
363 outchar('n');
364 }
365 outint(n);
366 }
367 } else if (arg->tn_op == ADDR &&
368 arg->tn_left->tn_op == STRING &&
369 arg->tn_left->tn_string->data != NULL) {
370 /* constant string, write all format specifiers */
371 outchar('s');
372 outint(n);
373 outfstrg(arg->tn_left->tn_string->data);
374 }
375 }
376 outchar((char)(retval_discarded ? 'd' : retval_used ? 'u' : 'i'));
377
378 /* name of the called function */
379 outname(tn_ck_left(tn->tn_left)->tn_sym->s_name);
380
381 /* types of arguments */
382 outchar('f');
383 outint(narg);
384 for (int n = 1; n <= narg; n++) {
385 /* the last argument is the top one in the tree */
386 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
387 continue;
388 outtype(arg->tn_left->tn_type);
389 }
390 /* expected type of return value */
391 outtype(tn->tn_type);
392 outchar('\n');
393 }
394
395 /* write a character to the output file, quoted if necessary */
396 static void
397 outqchar(char c)
398 {
399
400 if (ch_isprint(c) && c != '\\' && c != '"' && c != '\'') {
401 outchar(c);
402 return;
403 }
404
405 outchar('\\');
406 switch (c) {
407 case '\\':
408 outchar('\\');
409 break;
410 case '"':
411 outchar('"');
412 break;
413 case '\'':
414 outchar('\'');
415 break;
416 case '\b':
417 outchar('b');
418 break;
419 case '\t':
420 outchar('t');
421 break;
422 case '\n':
423 outchar('n');
424 break;
425 case '\f':
426 outchar('f');
427 break;
428 case '\r':
429 outchar('r');
430 break;
431 case '\v':
432 outchar('v');
433 break;
434 case '\a':
435 outchar('a');
436 break;
437 default:
438 outchar((char)((((unsigned char)c >> 6) & 07) + '0'));
439 outchar((char)((((unsigned char)c >> 3) & 07) + '0'));
440 outchar((char)((c & 07) + '0'));
441 break;
442 }
443 }
444
445 /*
446 * extracts potential format specifiers for printf() and scanf() and
447 * writes them, enclosed in "" and quoted if necessary, to the output file
448 */
449 static void
450 outfstrg(const char *cp)
451 {
452
453 outchar('"');
454
455 char c = *cp++;
456 while (c != '\0') {
457
458 if (c != '%') {
459 c = *cp++;
460 continue;
461 }
462
463 outchar('%');
464 c = *cp++;
465
466 /* flags for printf and scanf and *-fieldwidth for printf */
467 while (c == '-' || c == '+' || c == ' ' ||
468 c == '#' || c == '0' || c == '*') {
469 outchar(c);
470 c = *cp++;
471 }
472
473 /* numeric field width */
474 while (ch_isdigit(c)) {
475 outchar(c);
476 c = *cp++;
477 }
478
479 /* precision for printf */
480 if (c == '.') {
481 outchar(c);
482 c = *cp++;
483 if (c == '*') {
484 outchar(c);
485 c = *cp++;
486 } else {
487 while (ch_isdigit(c)) {
488 outchar(c);
489 c = *cp++;
490 }
491 }
492 }
493
494 /* h, l, L and q flags for printf and scanf */
495 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
496 outchar(c);
497 c = *cp++;
498 }
499
500 /*
501 * The last character. It is always written, so we can detect
502 * invalid format specifiers.
503 */
504 if (c != '\0') {
505 outqchar(c);
506 char oc = c;
507 c = *cp++;
508 /*
509 * handle [ for scanf. [-] means that a minus sign was
510 * found at an undefined position.
511 */
512 if (oc == '[') {
513 if (c == '^')
514 c = *cp++;
515 if (c == ']')
516 c = *cp++;
517 bool first = true;
518 while (c != '\0' && c != ']') {
519 if (c == '-') {
520 if (!first && *cp != ']')
521 outchar(c);
522 }
523 first = false;
524 c = *cp++;
525 }
526 if (c == ']') {
527 outchar(c);
528 c = *cp++;
529 }
530 }
531 }
532 }
533
534 outchar('"');
535 }
536
537 /* writes a record if sym was used */
538 void
539 outusg(const sym_t *sym)
540 {
541 if (ch_isdigit(sym->s_name[0])) /* 00000000_tmp, from mktempsym */
542 return;
543
544 outint(csrc_pos.p_line);
545 outchar('u'); /* used */
546 outint(get_filename_id(curr_pos.p_file));
547 outchar('.');
548 outint(curr_pos.p_line);
549 outchar('x'); /* separate the two numbers */
550 outname(sym->s_name);
551 outchar('\n');
552 }
553