1 /* Sparse Arrays for Objective C dispatch tables 2 Copyright (C) 1993-2022 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 6 GCC is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 GCC is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 Under Section 7 of GPL version 3, you are granted additional 17 permissions described in the GCC Runtime Library Exception, version 18 3.1, as published by the Free Software Foundation. 19 20 You should have received a copy of the GNU General Public License and 21 a copy of the GCC Runtime Library Exception along with this program; 22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 <http://www.gnu.org/licenses/>. */ 24 25 #include "objc-private/common.h" 26 #include "objc-private/sarray.h" 27 #include "objc/runtime.h" /* For objc_malloc */ 28 #include "objc/thr.h" /* For objc_mutex_lock */ 29 #include "objc-private/module-abi-8.h" 30 #include "objc-private/runtime.h" 31 #include <stdio.h> 32 #include <string.h> /* For memset */ 33 #include <assert.h> /* For assert */ 34 35 int nbuckets = 0; /* !T:MUTEX */ 36 int nindices = 0; /* !T:MUTEX */ 37 int narrays = 0; /* !T:MUTEX */ 38 int idxsize = 0; /* !T:MUTEX */ 39 40 static void *first_free_data = NULL; /* !T:MUTEX */ 41 42 #ifdef OBJC_SPARSE2 43 const char *__objc_sparse2_id = "2 level sparse indices"; 44 #endif 45 46 #ifdef OBJC_SPARSE3 47 const char *__objc_sparse3_id = "3 level sparse indices"; 48 #endif 49 50 /* This function removes any structures left over from free operations 51 that were not safe in a multi-threaded environment. */ 52 void 53 sarray_remove_garbage (void) 54 { 55 void **vp; 56 void *np; 57 58 objc_mutex_lock (__objc_runtime_mutex); 59 60 vp = first_free_data; 61 first_free_data = NULL; 62 63 while (vp) 64 { 65 np = *vp; 66 objc_free (vp); 67 vp = np; 68 } 69 70 objc_mutex_unlock (__objc_runtime_mutex); 71 } 72 73 /* Free a block of dynamically allocated memory. If we are in 74 multi-threaded mode, it is ok to free it. If not, we add it to the 75 garbage heap to be freed later. */ 76 static void 77 sarray_free_garbage (void *vp) 78 { 79 objc_mutex_lock (__objc_runtime_mutex); 80 81 if (__objc_runtime_threads_alive == 1) 82 { 83 objc_free (vp); 84 if (first_free_data) 85 sarray_remove_garbage (); 86 } 87 else 88 { 89 *(void **)vp = first_free_data; 90 first_free_data = vp; 91 } 92 93 objc_mutex_unlock (__objc_runtime_mutex); 94 } 95 96 /* sarray_at_put copies data in such a way as to be thread reader 97 safe. */ 98 void 99 sarray_at_put (struct sarray *array, sidx index, void *element) 100 { 101 #ifdef OBJC_SPARSE3 102 struct sindex **the_index; 103 struct sindex *new_index; 104 #endif 105 struct sbucket **the_bucket; 106 struct sbucket *new_bucket; 107 #ifdef OBJC_SPARSE3 108 size_t ioffset; 109 #endif 110 size_t boffset; 111 size_t eoffset; 112 #ifdef PRECOMPUTE_SELECTORS 113 union sofftype xx; 114 xx.idx = index; 115 #ifdef OBJC_SPARSE3 116 ioffset = xx.off.ioffset; 117 #endif 118 boffset = xx.off.boffset; 119 eoffset = xx.off.eoffset; 120 #else /* not PRECOMPUTE_SELECTORS */ 121 #ifdef OBJC_SPARSE3 122 ioffset = index/INDEX_CAPACITY; 123 boffset = (index/BUCKET_SIZE)%INDEX_SIZE; 124 eoffset = index%BUCKET_SIZE; 125 #else 126 boffset = index/BUCKET_SIZE; 127 eoffset = index%BUCKET_SIZE; 128 #endif 129 #endif /* not PRECOMPUTE_SELECTORS */ 130 131 assert (soffset_decode (index) < array->capacity); /* Range check */ 132 133 #ifdef OBJC_SPARSE3 134 the_index = &(array->indices[ioffset]); 135 the_bucket = &((*the_index)->buckets[boffset]); 136 #else 137 the_bucket = &(array->buckets[boffset]); 138 #endif 139 140 if ((*the_bucket)->elems[eoffset] == element) 141 return; /* Great! we just avoided a lazy copy. */ 142 143 #ifdef OBJC_SPARSE3 144 145 /* First, perform lazy copy/allocation of index if needed. */ 146 147 if ((*the_index) == array->empty_index) 148 { 149 /* The index was previously empty, allocate a new. */ 150 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); 151 memcpy (new_index, array->empty_index, sizeof (struct sindex)); 152 new_index->version.version = array->version.version; 153 *the_index = new_index; /* Prepared for install. */ 154 the_bucket = &((*the_index)->buckets[boffset]); 155 156 nindices += 1; 157 } 158 else if ((*the_index)->version.version != array->version.version) 159 { 160 /* This index must be lazy copied. */ 161 struct sindex *old_index = *the_index; 162 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); 163 memcpy (new_index, old_index, sizeof (struct sindex)); 164 new_index->version.version = array->version.version; 165 *the_index = new_index; /* Prepared for install. */ 166 the_bucket = &((*the_index)->buckets[boffset]); 167 168 nindices += 1; 169 } 170 171 #endif /* OBJC_SPARSE3 */ 172 173 /* Next, perform lazy allocation/copy of the bucket if needed. */ 174 if ((*the_bucket) == array->empty_bucket) 175 { 176 /* The bucket was previously empty (or something like that), 177 allocate a new. This is the effect of `lazy' allocation. */ 178 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); 179 memcpy ((void *) new_bucket, (const void *) array->empty_bucket, 180 sizeof (struct sbucket)); 181 new_bucket->version.version = array->version.version; 182 *the_bucket = new_bucket; /* Prepared for install. */ 183 184 nbuckets += 1; 185 186 } 187 else if ((*the_bucket)->version.version != array->version.version) 188 { 189 /* Perform lazy copy. */ 190 struct sbucket *old_bucket = *the_bucket; 191 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); 192 memcpy (new_bucket, old_bucket, sizeof (struct sbucket)); 193 new_bucket->version.version = array->version.version; 194 *the_bucket = new_bucket; /* Prepared for install. */ 195 196 nbuckets += 1; 197 } 198 (*the_bucket)->elems[eoffset] = element; 199 } 200 201 void 202 sarray_at_put_safe (struct sarray *array, sidx index, void *element) 203 { 204 if (soffset_decode (index) >= array->capacity) 205 sarray_realloc (array, soffset_decode (index) + 1); 206 sarray_at_put (array, index, element); 207 } 208 209 struct sarray * 210 sarray_new (int size, void *default_element) 211 { 212 struct sarray *arr; 213 #ifdef OBJC_SPARSE3 214 size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1; 215 struct sindex **new_indices; 216 #else /* OBJC_SPARSE2 */ 217 size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1; 218 struct sbucket **new_buckets; 219 #endif 220 size_t counter; 221 222 assert (size > 0); 223 224 /* Allocate core array. */ 225 arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); 226 arr->version.version = 0; 227 228 /* Initialize members. */ 229 #ifdef OBJC_SPARSE3 230 arr->capacity = num_indices*INDEX_CAPACITY; 231 new_indices = (struct sindex **) 232 objc_malloc (sizeof (struct sindex *) * num_indices); 233 234 arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); 235 arr->empty_index->version.version = 0; 236 237 narrays += 1; 238 idxsize += num_indices; 239 nindices += 1; 240 241 #else /* OBJC_SPARSE2 */ 242 arr->capacity = num_indices*BUCKET_SIZE; 243 new_buckets = (struct sbucket **) 244 objc_malloc (sizeof (struct sbucket *) * num_indices); 245 246 narrays += 1; 247 idxsize += num_indices; 248 249 #endif 250 251 arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); 252 arr->empty_bucket->version.version = 0; 253 254 nbuckets += 1; 255 256 arr->ref_count = 1; 257 arr->is_copy_of = (struct sarray *) 0; 258 259 for (counter = 0; counter < BUCKET_SIZE; counter++) 260 arr->empty_bucket->elems[counter] = default_element; 261 262 #ifdef OBJC_SPARSE3 263 for (counter = 0; counter < INDEX_SIZE; counter++) 264 arr->empty_index->buckets[counter] = arr->empty_bucket; 265 266 for (counter = 0; counter < num_indices; counter++) 267 new_indices[counter] = arr->empty_index; 268 269 #else /* OBJC_SPARSE2 */ 270 271 for (counter = 0; counter < num_indices; counter++) 272 new_buckets[counter] = arr->empty_bucket; 273 274 #endif 275 276 #ifdef OBJC_SPARSE3 277 arr->indices = new_indices; 278 #else /* OBJC_SPARSE2 */ 279 arr->buckets = new_buckets; 280 #endif 281 282 return arr; 283 } 284 285 287 /* Reallocate the sparse array to hold `newsize' entries Note: We 288 really allocate and then free. We have to do this to ensure that 289 any concurrent readers notice the update. */ 290 void 291 sarray_realloc (struct sarray *array, int newsize) 292 { 293 #ifdef OBJC_SPARSE3 294 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY; 295 size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY); 296 size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY; 297 298 struct sindex **new_indices; 299 struct sindex **old_indices; 300 301 #else /* OBJC_SPARSE2 */ 302 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE; 303 size_t new_max_index = ((newsize - 1)/BUCKET_SIZE); 304 size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE; 305 306 struct sbucket **new_buckets; 307 struct sbucket **old_buckets; 308 309 #endif 310 311 size_t counter; 312 313 assert (newsize > 0); 314 315 /* The size is the same, just ignore the request. */ 316 if (rounded_size <= array->capacity) 317 return; 318 319 assert (array->ref_count == 1); /* stop if lazy copied... */ 320 321 /* We are asked to extend the array -- allocate new bucket table, 322 and insert empty_bucket in newly allocated places. */ 323 if (rounded_size > array->capacity) 324 { 325 #ifdef OBJC_SPARSE3 326 new_max_index += 4; 327 rounded_size = (new_max_index + 1) * INDEX_CAPACITY; 328 #else /* OBJC_SPARSE2 */ 329 new_max_index += 4; 330 rounded_size = (new_max_index + 1) * BUCKET_SIZE; 331 #endif 332 333 /* Update capacity. */ 334 array->capacity = rounded_size; 335 336 #ifdef OBJC_SPARSE3 337 /* Alloc to force re-read by any concurrent readers. */ 338 old_indices = array->indices; 339 new_indices = (struct sindex **) 340 objc_malloc ((new_max_index + 1) * sizeof (struct sindex *)); 341 #else /* OBJC_SPARSE2 */ 342 old_buckets = array->buckets; 343 new_buckets = (struct sbucket **) 344 objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *)); 345 #endif 346 347 /* Copy buckets below old_max_index (they are still valid). */ 348 for (counter = 0; counter <= old_max_index; counter++ ) 349 { 350 #ifdef OBJC_SPARSE3 351 new_indices[counter] = old_indices[counter]; 352 #else /* OBJC_SPARSE2 */ 353 new_buckets[counter] = old_buckets[counter]; 354 #endif 355 } 356 357 #ifdef OBJC_SPARSE3 358 /* Reset entries above old_max_index to empty_bucket. */ 359 for (counter = old_max_index + 1; counter <= new_max_index; counter++) 360 new_indices[counter] = array->empty_index; 361 #else /* OBJC_SPARSE2 */ 362 /* Reset entries above old_max_index to empty_bucket. */ 363 for (counter = old_max_index + 1; counter <= new_max_index; counter++) 364 new_buckets[counter] = array->empty_bucket; 365 #endif 366 367 #ifdef OBJC_SPARSE3 368 /* Install the new indices. */ 369 array->indices = new_indices; 370 #else /* OBJC_SPARSE2 */ 371 array->buckets = new_buckets; 372 #endif 373 374 #ifdef OBJC_SPARSE3 375 /* Free the old indices. */ 376 sarray_free_garbage (old_indices); 377 #else /* OBJC_SPARSE2 */ 378 sarray_free_garbage (old_buckets); 379 #endif 380 381 idxsize += (new_max_index-old_max_index); 382 return; 383 } 384 } 385 386 388 /* Free a sparse array allocated with sarray_new */ 389 void 390 sarray_free (struct sarray *array) { 391 #ifdef OBJC_SPARSE3 392 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY; 393 struct sindex **old_indices; 394 #else 395 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE; 396 struct sbucket **old_buckets; 397 #endif 398 size_t counter = 0; 399 400 assert (array->ref_count != 0); /* Freed multiple times!!! */ 401 402 if (--(array->ref_count) != 0) /* There exists copies of me */ 403 return; 404 405 #ifdef OBJC_SPARSE3 406 old_indices = array->indices; 407 #else 408 old_buckets = array->buckets; 409 #endif 410 411 /* Free all entries that do not point to empty_bucket. */ 412 for (counter = 0; counter <= old_max_index; counter++ ) 413 { 414 #ifdef OBJC_SPARSE3 415 struct sindex *idx = old_indices[counter]; 416 if ((idx != array->empty_index) 417 && (idx->version.version == array->version.version)) 418 { 419 int c2; 420 for (c2 = 0; c2 < INDEX_SIZE; c2++) 421 { 422 struct sbucket *bkt = idx->buckets[c2]; 423 if ((bkt != array->empty_bucket) 424 && (bkt->version.version == array->version.version)) 425 { 426 sarray_free_garbage (bkt); 427 nbuckets -= 1; 428 } 429 } 430 sarray_free_garbage (idx); 431 nindices -= 1; 432 } 433 #else /* OBJC_SPARSE2 */ 434 struct sbucket *bkt = old_buckets[counter]; 435 if ((bkt != array->empty_bucket) 436 && (bkt->version.version == array->version.version)) 437 { 438 sarray_free_garbage (bkt); 439 nbuckets -= 1; 440 } 441 #endif 442 } 443 444 #ifdef OBJC_SPARSE3 445 /* Free empty_index. */ 446 if (array->empty_index->version.version == array->version.version) 447 { 448 sarray_free_garbage (array->empty_index); 449 nindices -= 1; 450 } 451 #endif 452 453 /* Free empty_bucket. */ 454 if (array->empty_bucket->version.version == array->version.version) 455 { 456 sarray_free_garbage (array->empty_bucket); 457 nbuckets -= 1; 458 } 459 idxsize -= (old_max_index + 1); 460 narrays -= 1; 461 462 #ifdef OBJC_SPARSE3 463 /* Free bucket table. */ 464 sarray_free_garbage (array->indices); 465 #else 466 /* Free bucket table. */ 467 sarray_free_garbage (array->buckets); 468 #endif 469 470 /* If this is a copy of another array, we free it (which might just 471 decrement its reference count so it will be freed when no longer 472 in use). */ 473 if (array->is_copy_of) 474 sarray_free (array->is_copy_of); 475 476 /* Free array. */ 477 sarray_free_garbage (array); 478 } 479 480 /* This is a lazy copy. Only the core of the structure is actually 481 copied. */ 482 struct sarray * 483 sarray_lazy_copy (struct sarray *oarr) 484 { 485 struct sarray *arr; 486 487 #ifdef OBJC_SPARSE3 488 size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1; 489 struct sindex **new_indices; 490 #else /* OBJC_SPARSE2 */ 491 size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1; 492 struct sbucket **new_buckets; 493 #endif 494 495 /* Allocate core array. */ 496 arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */ 497 arr->version.version = oarr->version.version + 1; 498 #ifdef OBJC_SPARSE3 499 arr->empty_index = oarr->empty_index; 500 #endif 501 arr->empty_bucket = oarr->empty_bucket; 502 arr->ref_count = 1; 503 oarr->ref_count += 1; 504 arr->is_copy_of = oarr; 505 arr->capacity = oarr->capacity; 506 507 #ifdef OBJC_SPARSE3 508 /* Copy bucket table. */ 509 new_indices = (struct sindex **) 510 objc_malloc (sizeof (struct sindex *) * num_indices); 511 memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices); 512 arr->indices = new_indices; 513 #else 514 /* Copy bucket table. */ 515 new_buckets = (struct sbucket **) 516 objc_malloc (sizeof (struct sbucket *) * num_indices); 517 memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices); 518 arr->buckets = new_buckets; 519 #endif 520 521 idxsize += num_indices; 522 narrays += 1; 523 524 return arr; 525 } 526