1/* $NetBSD: emit1.c,v 1.95 2024/05/12 18:49:36 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.95 2024/05/12 18:49:36 rillig Exp $"); 42#endif 43 44#include <stdlib.h> 45 46#include "lint1.h" 47 48static void outtt(sym_t *, sym_t *); 49static 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 */ 89void 90outtype(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 */ 148static void 149outtt(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 */ 177void 178outsym(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 (ch_isdigit(sym->s_name[0])) /* see mktempsym */ 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 */ 236void 237outfdef(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 */ 325void 326outcall(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->u.call; 339 340 for (size_t i = 0, n = call->args_len; i < n; i++) { 341 const tnode_t *arg = call->args[i]; 342 if (arg->tn_op == CON) { 343 tspec_t t = arg->tn_type->t_tspec; 344 if (is_integer(t)) { 345 /* 346 * XXX it would probably be better to 347 * explicitly test the sign 348 */ 349 int64_t si = arg->u.value.u.integer; 350 if (si == 0) 351 /* zero constant */ 352 outchar('z'); 353 else if (!msb(si, t)) 354 /* positive if cast to signed */ 355 outchar('p'); 356 else 357 /* negative if cast to signed */ 358 outchar('n'); 359 outint((int)i + 1); 360 } 361 } else if (arg->tn_op == ADDR && 362 arg->u.ops.left->tn_op == STRING && 363 arg->u.ops.left->u.str_literals->data != NULL) { 364 buffer buf; 365 buf_init(&buf); 366 quoted_iterator it = { .end = 0 }; 367 while (quoted_next(arg->u.ops.left->u.str_literals, &it)) 368 buf_add_char(&buf, (char)it.value); 369 370 /* string literal, write all format specifiers */ 371 outchar('s'); 372 outint((int)i + 1); 373 outfstrg(buf.data); 374 free(buf.data); 375 } 376 } 377 outchar((char)(retval_discarded ? 'd' : retval_used ? 'u' : 'i')); 378 379 outname(call->func->u.ops.left->u.sym->s_name); 380 381 /* types of arguments */ 382 outchar('f'); 383 outint((int)call->args_len); 384 for (size_t i = 0, n = call->args_len; i < n; i++) 385 outtype(call->args[i]->tn_type); 386 /* expected type of return value */ 387 outtype(tn->tn_type); 388 outchar('\n'); 389} 390 391/* write a character to the output file, quoted if necessary */ 392static void 393outqchar(char c) 394{ 395 396 if (ch_isprint(c) && c != '\\' && c != '"' && c != '\'') { 397 outchar(c); 398 return; 399 } 400 401 outchar('\\'); 402 switch (c) { 403 case '\\': 404 outchar('\\'); 405 break; 406 case '"': 407 outchar('"'); 408 break; 409 case '\'': 410 outchar('\''); 411 break; 412 case '\b': 413 outchar('b'); 414 break; 415 case '\t': 416 outchar('t'); 417 break; 418 case '\n': 419 outchar('n'); 420 break; 421 case '\f': 422 outchar('f'); 423 break; 424 case '\r': 425 outchar('r'); 426 break; 427 case '\v': 428 outchar('v'); 429 break; 430 case '\a': 431 outchar('a'); 432 break; 433 default: 434 outchar((char)((((unsigned char)c >> 6) & 07) + '0')); 435 outchar((char)((((unsigned char)c >> 3) & 07) + '0')); 436 outchar((char)((c & 07) + '0')); 437 break; 438 } 439} 440 441/* 442 * extracts potential format specifiers for printf() and scanf() and 443 * writes them, enclosed in "" and quoted if necessary, to the output file 444 */ 445static void 446outfstrg(const char *cp) 447{ 448 449 outchar('"'); 450 451 char c = *cp++; 452 while (c != '\0') { 453 454 if (c != '%') { 455 c = *cp++; 456 continue; 457 } 458 459 outchar('%'); 460 c = *cp++; 461 462 /* flags for printf and scanf and *-fieldwidth for printf */ 463 while (c == '-' || c == '+' || c == ' ' || 464 c == '#' || c == '0' || c == '*') { 465 outchar(c); 466 c = *cp++; 467 } 468 469 /* numeric field width */ 470 while (ch_isdigit(c)) { 471 outchar(c); 472 c = *cp++; 473 } 474 475 /* precision for printf */ 476 if (c == '.') { 477 outchar(c); 478 c = *cp++; 479 if (c == '*') { 480 outchar(c); 481 c = *cp++; 482 } else { 483 while (ch_isdigit(c)) { 484 outchar(c); 485 c = *cp++; 486 } 487 } 488 } 489 490 /* h, l, L and q flags for printf and scanf */ 491 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') { 492 outchar(c); 493 c = *cp++; 494 } 495 496 /* 497 * The last character. It is always written, so we can detect 498 * invalid format specifiers. 499 */ 500 if (c != '\0') { 501 outqchar(c); 502 char oc = c; 503 c = *cp++; 504 /* 505 * handle [ for scanf. [-] means that a minus sign was 506 * found at an undefined position. 507 */ 508 if (oc == '[') { 509 if (c == '^') 510 c = *cp++; 511 if (c == ']') 512 c = *cp++; 513 bool first = true; 514 while (c != '\0' && c != ']') { 515 if (c == '-') { 516 if (!first && *cp != ']') 517 outchar(c); 518 } 519 first = false; 520 c = *cp++; 521 } 522 if (c == ']') { 523 outchar(c); 524 c = *cp++; 525 } 526 } 527 } 528 } 529 530 outchar('"'); 531} 532 533/* writes a record if sym was used */ 534void 535outusg(const sym_t *sym) 536{ 537 if (ch_isdigit(sym->s_name[0])) /* see mktempsym */ 538 return; 539 540 outint(csrc_pos.p_line); 541 outchar('u'); /* used */ 542 outint(get_filename_id(curr_pos.p_file)); 543 outchar('.'); 544 outint(curr_pos.p_line); 545 outchar('x'); /* separate the two numbers */ 546 outname(sym->s_name); 547 outchar('\n'); 548} 549