1/* $NetBSD: mem1.c,v 1.77 2023/12/03 18:17:41 rillig Exp $ */ 2 3/* 4 * Copyright (c) 1994, 1995 Jochen Pohl 5 * All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Jochen Pohl for 18 * The NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#if HAVE_NBTOOL_CONFIG_H 35#include "nbtool_config.h" 36#endif 37 38#include <sys/cdefs.h> 39#if defined(__RCSID) 40__RCSID("$NetBSD: mem1.c,v 1.77 2023/12/03 18:17:41 rillig Exp $"); 41#endif 42 43#include <sys/param.h> 44#include <stdlib.h> 45#include <string.h> 46 47#include "lint1.h" 48 49/* 50 * Filenames allocated by record_filename are shared and have unlimited 51 * lifetime. 52 */ 53struct filename { 54 const char *fn_name; 55 size_t fn_len; 56 int fn_id; 57 struct filename *fn_next; 58}; 59 60static struct filename *filenames; /* null-terminated array */ 61static int next_filename_id; 62 63/* Find the given filename, or return NULL. */ 64static const struct filename * 65search_filename(const char *s, size_t len) 66{ 67 const struct filename *fn; 68 69 for (fn = filenames; fn != NULL; fn = fn->fn_next) { 70 if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0) 71 break; 72 } 73 return fn; 74} 75 76struct filename_replacement { 77 const char *orig; 78 size_t orig_len; 79 const char *repl; 80 const struct filename_replacement *next; 81}; 82 83static struct filename_replacement *filename_replacements; 84 85void 86add_directory_replacement(char *arg) 87{ 88 struct filename_replacement *r = xmalloc(sizeof(*r)); 89 90 char *sep = strchr(arg, '='); 91 if (sep == NULL) 92 err(1, "Bad replacement directory spec `%s'", arg); 93 *sep = '\0'; 94 95 r->orig = arg; 96 r->orig_len = (size_t)(sep - arg); 97 r->repl = sep + 1; 98 r->next = filename_replacements; 99 filename_replacements = r; 100} 101 102const char * 103transform_filename(const char *name, size_t len) 104{ 105 static char buf[MAXPATHLEN]; 106 const struct filename_replacement *r; 107 108 for (r = filename_replacements; r != NULL; r = r->next) 109 if (r->orig_len < len && 110 memcmp(name, r->orig, r->orig_len) == 0) 111 break; 112 if (r == NULL) 113 return name; 114 (void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len); 115 return buf; 116} 117 118/* 119 * Return a copy of the filename s with unlimited lifetime. 120 * If the filename is new, write it to the output file. 121 */ 122const char * 123record_filename(const char *s, size_t slen) 124{ 125 126 const struct filename *existing_fn = search_filename(s, slen); 127 if (existing_fn != NULL) 128 return existing_fn->fn_name; 129 130 char *name = xmalloc(slen + 1); 131 (void)memcpy(name, s, slen); 132 name[slen] = '\0'; 133 134 struct filename *fn = xmalloc(sizeof(*fn)); 135 fn->fn_name = name; 136 fn->fn_len = slen; 137 fn->fn_id = next_filename_id++; 138 fn->fn_next = filenames; 139 filenames = fn; 140 141 outint(fn->fn_id); 142 outchar('s'); 143 outstrg(transform_filename(fn->fn_name, fn->fn_len)); 144 outchar('\n'); 145 146 return fn->fn_name; 147} 148 149/* Get the ID of a filename. */ 150int 151get_filename_id(const char *s) 152{ 153 const struct filename *fn; 154 155 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL) 156 return -1; 157 return fn->fn_id; 158} 159 160typedef struct memory_pools { 161 struct memory_pool *pools; 162 size_t cap; 163} memory_pools; 164 165/* Array of memory pools, indexed by mem_block_level. */ 166static memory_pools mpools; 167 168/* The pool for the current expression is independent of any block level. */ 169static memory_pool expr_pool; 170 171static void 172mpool_add(memory_pool *pool, struct memory_pool_item item) 173{ 174 175 if (pool->len >= pool->cap) { 176 pool->cap = 2 * pool->len + 16; 177 pool->items = xrealloc(pool->items, 178 sizeof(*pool->items) * pool->cap); 179 } 180 pool->items[pool->len++] = item; 181} 182 183#ifdef DEBUG_MEM 184static void 185debug_memory_pool_item(const struct memory_pool_item *item) 186{ 187 void *p = item->p; 188 size_t size = item->size; 189 const char *descr = item->descr; 190 191 if (strcmp(descr, "string") == 0) { 192 const char *str = p; 193 debug_step("%s: freeing string '%s'", __func__, str); 194 } else if (strcmp(descr, "sym") == 0) { 195 const sym_t *sym = p; 196 debug_step("%s: freeing symbol '%s'", __func__, sym->s_name); 197 } else if (strcmp(descr, "type") == 0) { 198 const type_t *tp = p; 199 debug_step("%s: freeing type '%s'", __func__, type_name(tp)); 200 } else if (strcmp(descr, "tnode") == 0) { 201 const tnode_t *tn = p; 202 debug_step("%s: freeing node '%s' with type '%s'", 203 __func__, op_name(tn->tn_op), type_name(tn->tn_type)); 204 } else 205 debug_step("%s: freeing '%s' with %zu bytes", 206 __func__, descr, size); 207} 208#endif 209 210static void 211mpool_free(memory_pool *pool) 212{ 213 214#ifdef DEBUG_MEM 215 for (size_t i = pool->len; i-- > 0; ) 216 debug_memory_pool_item(pool->items + i); 217#endif 218 219 for (size_t i = pool->len; i-- > 0;) { 220#ifdef DEBUG_MEM 221 static void *(*volatile set)(void *, int, size_t) = memset; 222 set(pool->items[i].p, 'Z', pool->items[i].size); 223#endif 224 free(pool->items[i].p); 225 } 226 pool->len = 0; 227} 228 229static void * 230#ifdef DEBUG_MEM 231mpool_zero_alloc(memory_pool *pool, size_t size, const char *descr) 232#else 233mpool_zero_alloc(memory_pool *pool, size_t size) 234#endif 235{ 236 237 void *mem = xmalloc(size); 238 memset(mem, 0, size); 239#if DEBUG_MEM 240 mpool_add(pool, (struct memory_pool_item){ mem, size, descr }); 241#else 242 mpool_add(pool, (struct memory_pool_item){ mem }); 243#endif 244 return mem; 245} 246 247static memory_pool * 248mpool_at(size_t level) 249{ 250 251 if (level >= mpools.cap) { 252 size_t prev_cap = mpools.cap; 253 mpools.cap = level + 16; 254 mpools.pools = xrealloc(mpools.pools, 255 sizeof(*mpools.pools) * mpools.cap); 256 for (size_t i = prev_cap; i < mpools.cap; i++) 257 mpools.pools[i] = (memory_pool){ NULL, 0, 0 }; 258 } 259 return mpools.pools + level; 260} 261 262 263/* Allocate memory associated with the level, initialized with zero. */ 264#ifdef DEBUG_MEM 265void * 266level_zero_alloc(size_t level, size_t size, const char *descr) 267{ 268 269 debug_step("%s: %s at level %zu", __func__, descr, level); 270 return mpool_zero_alloc(mpool_at(level), size, descr); 271} 272#else 273void * 274(level_zero_alloc)(size_t level, size_t size) 275{ 276 277 return mpool_zero_alloc(mpool_at(level), size); 278} 279#endif 280 281/* Allocate memory that is freed at the end of the current block. */ 282#ifdef DEBUG_MEM 283void * 284block_zero_alloc(size_t size, const char *descr) 285{ 286 287 return level_zero_alloc(mem_block_level, size, descr); 288} 289#else 290void * 291(block_zero_alloc)(size_t size) 292{ 293 294 return (level_zero_alloc)(mem_block_level, size); 295} 296#endif 297 298void 299level_free_all(size_t level) 300{ 301 302 debug_step("+ %s %zu", __func__, level); 303 debug_indent_inc(); 304 mpool_free(mpool_at(level)); 305 debug_leave(); 306} 307 308/* Allocate memory that is freed at the end of the current expression. */ 309#if DEBUG_MEM 310void * 311expr_zero_alloc(size_t s, const char *descr) 312{ 313 314 return mpool_zero_alloc(&expr_pool, s, descr); 315} 316#else 317void * 318(expr_zero_alloc)(size_t size) 319{ 320 321 return mpool_zero_alloc(&expr_pool, size); 322} 323#endif 324 325static bool 326str_ends_with(const char *haystack, const char *needle) 327{ 328 size_t hlen = strlen(haystack); 329 size_t nlen = strlen(needle); 330 331 return nlen <= hlen && 332 memcmp(haystack + hlen - nlen, needle, nlen) == 0; 333} 334 335/* 336 * Return a freshly allocated tree node that is freed at the end of the 337 * current expression. 338 * 339 * The node records whether it comes from a system file, which makes strict 340 * bool mode less restrictive. 341 */ 342tnode_t * 343expr_alloc_tnode(void) 344{ 345 tnode_t *tn = expr_zero_alloc(sizeof(*tn), "tnode"); 346 /* 347 * files named *.c that are different from the main translation unit 348 * typically contain generated code that cannot be influenced, such as 349 * a flex lexer or a yacc parser. 350 */ 351 tn->tn_sys = in_system_header || 352 (curr_pos.p_file != csrc_pos.p_file && 353 str_ends_with(curr_pos.p_file, ".c")); 354 return tn; 355} 356 357/* Free all memory which is allocated by the current expression. */ 358void 359expr_free_all(void) 360{ 361 362 debug_step("%s", __func__); 363 mpool_free(&expr_pool); 364} 365 366/* 367 * Save the memory which is used by the current expression. This memory 368 * is not freed by the next expr_free_all() call. The returned value can be 369 * used to restore the memory. 370 */ 371memory_pool 372expr_save_memory(void) 373{ 374 375 memory_pool saved_pool = expr_pool; 376 expr_pool = (memory_pool){ NULL, 0, 0 }; 377 return saved_pool; 378} 379 380/* 381 * Free all memory used for the current expression and restore the memory used 382 * by a previous expression and saved by expr_save_memory(). The next call to 383 * expr_free_all() frees the restored memory. 384 */ 385void 386expr_restore_memory(memory_pool saved_pool) 387{ 388 389 expr_free_all(); 390 free(expr_pool.items); 391 expr_pool = saved_pool; 392} 393