emit1.c revision 1.91 1 /* $NetBSD: emit1.c,v 1.91 2024/03/09 13:20:55 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.91 2024/03/09 13:20:55 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->u.dimension);
117 } else if (ts == ENUM) {
118 outtt(tp->u.enumer->en_tag,
119 tp->u.enumer->en_first_typedef);
120 } else if (is_struct_or_union(ts)) {
121 outtt(tp->u.sou->sou_tag,
122 tp->u.sou->sou_first_typedef);
123 } else if (ts == FUNC && tp->t_proto) {
124 na = 0;
125 for (const sym_t *param = tp->u.params;
126 param != NULL; param = param->s_next)
127 na++;
128 if (tp->t_vararg)
129 na++;
130 outint(na);
131 for (const sym_t *param = tp->u.params;
132 param != NULL; param = param->s_next)
133 outtype(param->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 and
184 * extern functions) must be checked in lint2. Lint1 can't do this,
185 * especially not if functions are declared at block level before their
186 * first declaration at level 0.
187 */
188 if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
189 return;
190 if (isdigit((unsigned char)sym->s_name[0])) /* 00000000_tmp */
191 return;
192
193 outint(csrc_pos.p_line);
194 outchar('d'); /* declaration */
195 outint(get_filename_id(sym->s_def_pos.p_file));
196 outchar('.');
197 outint(sym->s_def_pos.p_line);
198
199 /* flags */
200
201 if (def == DEF)
202 outchar('d'); /* defined */
203 else if (def == TDEF)
204 outchar('t'); /* tentative defined */
205 else {
206 lint_assert(def == DECL);
207 outchar('e'); /* declared */
208 }
209
210 if (llibflg && def != DECL) {
211 /*
212 * mark it as used so lint2 does not complain about unused
213 * symbols in libraries
214 */
215 outchar('u');
216 }
217
218 if (sc == STATIC)
219 outchar('s');
220
221 outname(sym->s_name);
222
223 if (sym->s_rename != NULL) {
224 outchar('r');
225 outname(sym->s_rename);
226 }
227
228 outtype(sym->s_type);
229 outchar('\n');
230 }
231
232 /*
233 * Write information about a function definition. This is also done for static
234 * functions, to later check if they are called with proper argument types.
235 */
236 void
237 outfdef(const sym_t *fsym, const pos_t *posp, bool rval, bool osdef,
238 const sym_t *args)
239 {
240 int narg;
241
242 if (posp->p_file == csrc_pos.p_file) {
243 outint(posp->p_line);
244 } else {
245 outint(csrc_pos.p_line);
246 }
247 outchar('d'); /* declaration */
248 outint(get_filename_id(posp->p_file));
249 outchar('.');
250 outint(posp->p_line);
251
252 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
253 if (printflike_argnum != -1) {
254 nvararg = printflike_argnum;
255 } else if (scanflike_argnum != -1) {
256 nvararg = scanflike_argnum;
257 }
258
259 if (nvararg != -1) {
260 outchar('v');
261 outint(nvararg);
262 }
263 if (scanflike_argnum != -1) {
264 outchar('S');
265 outint(scanflike_argnum);
266 }
267 if (printflike_argnum != -1) {
268 outchar('P');
269 outint(printflike_argnum);
270 }
271 nvararg = printflike_argnum = scanflike_argnum = -1;
272
273 outchar('d');
274
275 if (rval)
276 outchar('r'); /* has return value */
277
278 if (llibflg)
279 /*
280 * mark it as used so lint2 does not complain about unused
281 * symbols in libraries
282 */
283 outchar('u');
284
285 if (osdef)
286 outchar('o'); /* old-style function definition */
287
288 if (fsym->s_inline)
289 outchar('i');
290
291 if (fsym->s_scl == STATIC)
292 outchar('s');
293
294 outname(fsym->s_name);
295
296 if (fsym->s_rename != NULL) {
297 outchar('r');
298 outname(fsym->s_rename);
299 }
300
301 /* parameter types and return value */
302 if (osdef) {
303 narg = 0;
304 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next)
305 narg++;
306 outchar('f');
307 outint(narg);
308 for (const sym_t *arg = args; arg != NULL; arg = arg->s_next)
309 outtype(arg->s_type);
310 outtype(fsym->s_type->t_subt);
311 } else {
312 outtype(fsym->s_type);
313 }
314 outchar('\n');
315 }
316
317 /*
318 * write out all information necessary for lint2 to check function
319 * calls
320 *
321 * retval_used is set if the return value is used (assigned to a variable)
322 * retval_discarded is set if the return value is neither used nor ignored
323 * (that is, cast to void)
324 */
325 void
326 outcall(const tnode_t *tn, bool retval_used, bool retval_discarded)
327 {
328 outint(csrc_pos.p_line);
329 outchar('c'); /* function call */
330 outint(get_filename_id(curr_pos.p_file));
331 outchar('.');
332 outint(curr_pos.p_line);
333
334 /*
335 * flags; 'u' and 'i' must be last to make sure a letter is between the
336 * numeric argument of a flag and the name of the function
337 */
338 const function_call *call = tn->tn_call;
339
340 /* information about arguments */
341 for (size_t n = 1; call->args != NULL && n <= call->args_len; n++) {
342 const tnode_t *arg = call->args[n - 1];
343 if (arg->tn_op == CON) {
344 tspec_t t = arg->tn_type->t_tspec;
345 if (is_integer(t)) {
346 /*
347 * XXX it would probably be better to
348 * explicitly test the sign
349 */
350 int64_t si = arg->tn_val.u.integer;
351 if (si == 0)
352 /* zero constant */
353 outchar('z');
354 else if (!msb(si, t))
355 /* positive if cast to signed */
356 outchar('p');
357 else
358 /* negative if cast to signed */
359 outchar('n');
360 outint((int)n);
361 }
362 } else if (arg->tn_op == ADDR &&
363 arg->tn_left->tn_op == STRING &&
364 arg->tn_left->tn_string->data != NULL) {
365 buffer buf;
366 buf_init(&buf);
367 quoted_iterator it = { .end = 0 };
368 while (quoted_next(arg->tn_left->tn_string, &it))
369 buf_add_char(&buf, (char)it.value);
370
371 /* string literal, write all format specifiers */
372 outchar('s');
373 outint((int)n);
374 outfstrg(buf.data);
375 free(buf.data);
376 }
377 }
378 outchar((char)(retval_discarded ? 'd' : retval_used ? 'u' : 'i'));
379
380 outname(call->func->tn_left->tn_sym->s_name);
381
382 /* types of arguments */
383 outchar('f');
384 outint((int)call->args_len);
385 for (size_t i = 0; call->args != NULL && i < call->args_len; i++)
386 outtype(call->args[i]->tn_type);
387 /* expected type of return value */
388 outtype(tn->tn_type);
389 outchar('\n');
390 }
391
392 /* write a character to the output file, quoted if necessary */
393 static void
394 outqchar(char c)
395 {
396
397 if (isprint((unsigned char)c) && c != '\\' && c != '"' && c != '\'') {
398 outchar(c);
399 return;
400 }
401
402 outchar('\\');
403 switch (c) {
404 case '\\':
405 outchar('\\');
406 break;
407 case '"':
408 outchar('"');
409 break;
410 case '\'':
411 outchar('\'');
412 break;
413 case '\b':
414 outchar('b');
415 break;
416 case '\t':
417 outchar('t');
418 break;
419 case '\n':
420 outchar('n');
421 break;
422 case '\f':
423 outchar('f');
424 break;
425 case '\r':
426 outchar('r');
427 break;
428 case '\v':
429 outchar('v');
430 break;
431 case '\a':
432 outchar('a');
433 break;
434 default:
435 outchar((char)((((unsigned char)c >> 6) & 07) + '0'));
436 outchar((char)((((unsigned char)c >> 3) & 07) + '0'));
437 outchar((char)((c & 07) + '0'));
438 break;
439 }
440 }
441
442 /*
443 * extracts potential format specifiers for printf() and scanf() and
444 * writes them, enclosed in "" and quoted if necessary, to the output file
445 */
446 static void
447 outfstrg(const char *cp)
448 {
449
450 outchar('"');
451
452 char c = *cp++;
453 while (c != '\0') {
454
455 if (c != '%') {
456 c = *cp++;
457 continue;
458 }
459
460 outchar('%');
461 c = *cp++;
462
463 /* flags for printf and scanf and *-fieldwidth for printf */
464 while (c == '-' || c == '+' || c == ' ' ||
465 c == '#' || c == '0' || c == '*') {
466 outchar(c);
467 c = *cp++;
468 }
469
470 /* numeric field width */
471 while (isdigit((unsigned char)c)) {
472 outchar(c);
473 c = *cp++;
474 }
475
476 /* precision for printf */
477 if (c == '.') {
478 outchar(c);
479 c = *cp++;
480 if (c == '*') {
481 outchar(c);
482 c = *cp++;
483 } else {
484 while (isdigit((unsigned char)c)) {
485 outchar(c);
486 c = *cp++;
487 }
488 }
489 }
490
491 /* h, l, L and q flags for printf and scanf */
492 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
493 outchar(c);
494 c = *cp++;
495 }
496
497 /*
498 * The last character. It is always written, so we can detect
499 * invalid format specifiers.
500 */
501 if (c != '\0') {
502 outqchar(c);
503 char oc = c;
504 c = *cp++;
505 /*
506 * handle [ for scanf. [-] means that a minus sign was
507 * found at an undefined position.
508 */
509 if (oc == '[') {
510 if (c == '^')
511 c = *cp++;
512 if (c == ']')
513 c = *cp++;
514 bool first = true;
515 while (c != '\0' && c != ']') {
516 if (c == '-') {
517 if (!first && *cp != ']')
518 outchar(c);
519 }
520 first = false;
521 c = *cp++;
522 }
523 if (c == ']') {
524 outchar(c);
525 c = *cp++;
526 }
527 }
528 }
529 }
530
531 outchar('"');
532 }
533
534 /* writes a record if sym was used */
535 void
536 outusg(const sym_t *sym)
537 {
538 if (isdigit((unsigned char)sym->s_name[0])) /* see mktempsym */
539 return;
540
541 outint(csrc_pos.p_line);
542 outchar('u'); /* used */
543 outint(get_filename_id(curr_pos.p_file));
544 outchar('.');
545 outint(curr_pos.p_line);
546 outchar('x'); /* separate the two numbers */
547 outname(sym->s_name);
548 outchar('\n');
549 }
550