• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bdwgc / bdwgc / 2053

22 Feb 2026 05:48AM UTC coverage: 77.233% (+3.3%) from 73.894%
2053

push

travis-ci

ivmai
Fix missing GC_ATTR_NONNULL for API functions
(fix of commit d012f92c)

* include/gc/gc.h (GC_exclude_static_roots, GC_add_roots): Add
`GC_ATTR_NONNULL` attribute for the appropriate arguments.
* include/gc/gc.h [GC_WIN32_THREADS && (!GC_PTHREADS || GC_BUILD
|| GC_WINDOWS_H_INCLUDED) && (!GC_NO_THREAD_DECLS || GC_BUILD)
&& !GC_DONT_INCL_WINDOWS_H] (GC_CreateThread, GC_beginthreadex):
Likewise.
* include/gc/gc_inline.h (GC_generic_malloc_many): Likewise.
* include/gc/gc_mark.h (GC_mark_and_push, GC_new_proc,
GC_new_proc_inner): Likewise.
* include/gc/gc_pthread_redirects.h [GC_PTHREADS
&& !GC_PTHREAD_REDIRECTS_ONLY] (GC_pthread_create): Likewise.
* include/private/gc_priv.h (NONNULL_PROC_NOT_ZERO): New macro.
* mallocx.c (GC_generic_malloc_many): Add assertion that `result` is
non-null.
* mark_rts.c (GC_add_roots_inner): Add assertion that `b` is non-null.
* mark_rts.c (GC_exclude_static_roots_inner): Add assertion that
`start` is non-null.
* misc.c (GC_new_proc_inner): Add assertion that `proc` is non-zero.
* pthread_support.c (GC_wrap_pthread_create): Add assertion that
`start_routine` is non-zero.

3 of 3 new or added lines in 2 files covered. (100.0%)

128 existing lines in 9 files now uncovered.

6873 of 8899 relevant lines covered (77.23%)

17354920.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.93
/typd_mlc.c
1
/*
2
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
3
 * Copyright (c) 1999-2000 by Hewlett-Packard Company.  All rights reserved.
4
 * Copyright (c) 2008-2025 Ivan Maidanski
5
 *
6
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
8
 *
9
 * Permission is hereby granted to use or copy this program
10
 * for any purpose, provided the above notices are retained on all copies.
11
 * Permission to modify the code and to distribute modified code is granted,
12
 * provided the above notices are retained, and a notice that the code was
13
 * modified is included with the above copyright notice.
14
 */
15

16
#include "private/gc_pmark.h"
17

18
/*
19
 * Some simple primitives for allocation with explicit type information.
20
 * Simple objects are allocated such that they contain a `GC_descr` at the
21
 * end (in the last allocated word).  This descriptor may be a procedure
22
 * which then examines an extended descriptor passed as its environment.
23
 *
24
 * Arrays are treated as simple objects if they have sufficiently simple
25
 * structure.  Otherwise they are allocated from an array kind that supplies
26
 * a special mark procedure.  These arrays contain a pointer to a
27
 * `complex_descriptor` as their last "pointer-sized" word.
28
 * This is done because the environment field is too small, and the collector
29
 * must trace the `complex_descriptor`.
30
 *
31
 * Note that descriptors inside objects may appear cleared, if we encounter
32
 * a false reference to an object on a free list.  In the case of a simple
33
 * object, this is OK, since a zero descriptor corresponds to examining no
34
 * fields.  In the `complex_descriptor` case, we explicitly check for that
35
 * case.
36
 *
37
 * Note: major parts of this code have not been tested at all and are not
38
 * testable, since they are not accessible through the current interface.
39
 */
40

41
#include "gc/gc_typed.h"
42

43
/* Object kind for objects with indirect (possibly extended) descriptors. */
44
STATIC int GC_explicit_kind = 0;
45

46
/*
47
 * Object kind for objects with complex descriptors and
48
 * `GC_array_mark_proc`.
49
 */
50
STATIC int GC_array_kind = 0;
51

52
#define ED_INITIAL_SIZE 100
53

54
/* Indices of the typed mark procedures. */
55
STATIC unsigned GC_typed_mark_proc_index = 0;
56
STATIC unsigned GC_array_mark_proc_index = 0;
57

58
STATIC void
59
GC_push_typed_structures_proc(void)
×
60
{
61
  GC_PUSH_ALL_SYM(GC_ext_descriptors);
×
62
}
×
63

64
/*
65
 * Add a multi-word bitmap to `GC_ext_descriptors` arrays.
66
 * Returns starting index on success, -1 otherwise.
67
 */
68
STATIC GC_signed_word
69
GC_add_ext_descriptor(const word *bm, size_t nbits)
×
70
{
71
  GC_signed_word result;
72
  size_t i;
73
  size_t nwords = divWORDSZ(nbits + CPP_WORDSZ - 1);
×
74

75
  LOCK();
×
76
  while (UNLIKELY(GC_avail_descr + nwords >= GC_ed_size)) {
×
77
    typed_ext_descr_t *newExtD;
78
    size_t new_size;
79
    size_t ed_size = GC_ed_size;
×
80

81
    if (0 == ed_size) {
×
82
      GC_ASSERT(ADDR(&GC_ext_descriptors) % ALIGNMENT == 0);
83
      GC_push_typed_structures = GC_push_typed_structures_proc;
×
84
      UNLOCK();
×
85
      new_size = ED_INITIAL_SIZE;
×
86
    } else {
87
      UNLOCK();
×
88
      new_size = 2 * ed_size;
×
89
      if (new_size > MAX_ENV)
×
90
        return -1;
×
91
    }
92
    newExtD = (typed_ext_descr_t *)GC_malloc_atomic(
×
93
        new_size * sizeof(typed_ext_descr_t));
94
    if (NULL == newExtD)
×
95
      return -1;
×
96
    LOCK();
×
97
    if (ed_size == GC_ed_size) {
×
98
      if (GC_avail_descr != 0) {
×
99
        BCOPY(GC_ext_descriptors, newExtD,
×
100
              GC_avail_descr * sizeof(typed_ext_descr_t));
101
      }
102
      GC_ed_size = new_size;
×
103
      GC_ext_descriptors = newExtD;
×
104
    } else {
105
      /* Another thread is already resized it in the meantime. */
106
    }
107
  }
108
  result = (GC_signed_word)GC_avail_descr;
×
109
  for (i = 0; i < nwords - 1; i++) {
×
110
    GC_ext_descriptors[(size_t)result + i].ed_bitmap = bm[i];
×
111
    GC_ext_descriptors[(size_t)result + i].ed_continued = TRUE;
×
112
  }
113
  /* Clear irrelevant (highest) bits for the last element. */
114
  GC_ext_descriptors[(size_t)result + i].ed_bitmap
×
115
      = bm[i] & (GC_WORD_MAX >> (nwords * CPP_WORDSZ - nbits));
×
116
  GC_ext_descriptors[(size_t)result + i].ed_continued = FALSE;
×
117
  GC_avail_descr += nwords;
×
118
  GC_ASSERT(result >= 0);
×
119
  UNLOCK();
×
120
  return result;
×
121
}
122

123
/*
124
 * Return a descriptor for the concatenation of 2 objects, each one is
125
 * `lpw` pointers long and described by descriptor `d`.  The result is
126
 * known to be short enough to fit into a bitmap descriptor.
127
 * `d` is a `GC_DS_LENGTH` or `GC_DS_BITMAP` descriptor.
128
 */
129
STATIC GC_descr
130
GC_double_descr(GC_descr d, size_t lpw)
126✔
131
{
132
  if ((d & GC_DS_TAGS) == GC_DS_LENGTH) {
126✔
133
    /* Convert to bitmap descriptor for all-pointer objects of `d` size. */
134
    GC_ASSERT(CPP_WORDSZ >= BYTES_TO_PTRS(d));
×
135
    d = d < sizeof(ptr_t)
×
136
            ? GC_DS_BITMAP
137
            : (GC_WORD_MAX << (CPP_WORDSZ - BYTES_TO_PTRS(d))) | GC_DS_BITMAP;
×
138
  }
139
  d |= (d & ~(GC_descr)GC_DS_TAGS) >> lpw;
126✔
140
  return d;
126✔
141
}
142

143
STATIC mse *GC_CALLBACK GC_typed_mark_proc(word *addr, mse *mark_stack_top,
144
                                           mse *mark_stack_limit, word env);
145

146
STATIC mse *GC_CALLBACK GC_array_mark_proc(word *addr, mse *mark_stack_top,
147
                                           mse *mark_stack_limit, word env);
148

149
STATIC void
150
GC_init_explicit_typing(void)
45✔
151
{
152
  /*
153
   * Set up object kind with simple indirect descriptor.
154
   * Descriptor is in the last `word` of the object.
155
   */
156
  GC_typed_mark_proc_index = GC_new_proc_inner(GC_typed_mark_proc);
45✔
157
  GC_explicit_kind = (int)GC_new_kind_inner(
45✔
158
      GC_new_free_list_inner(),
159
      (PTRS_TO_BYTES(GC_WORD_MAX) | GC_DS_PER_OBJECT), TRUE, TRUE);
160

161
  /* Set up object kind with array descriptor. */
162
  GC_array_mark_proc_index = GC_new_proc_inner(GC_array_mark_proc);
45✔
163
  GC_array_kind = (int)GC_new_kind_inner(
45✔
164
      GC_new_free_list_inner(), GC_MAKE_PROC(GC_array_mark_proc_index, 0),
45✔
165
      FALSE, TRUE);
166
}
45✔
167

168
STATIC mse *GC_CALLBACK
169
GC_typed_mark_proc(word *addr, mse *mark_stack_top, mse *mark_stack_limit,
×
170
                   word env)
171
{
172
  word bm;
173
  ptr_t current_p = (ptr_t)addr;
×
174
  ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr;
×
175
  ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr;
×
176
  DECLARE_HDR_CACHE;
177

178
  /* The allocator lock is held by the collection initiating thread. */
179
  GC_ASSERT(GC_get_parallel() || I_HOLD_LOCK());
×
180
  bm = GC_ext_descriptors[env].ed_bitmap;
×
181

182
  INIT_HDR_CACHE;
×
183
  for (; bm != 0; bm >>= 1, current_p += sizeof(ptr_t)) {
×
184
    if (bm & 1) {
×
185
      ptr_t q;
186

187
      LOAD_PTR_OR_CONTINUE(q, current_p);
×
188
      FIXUP_POINTER(q);
189
      if (ADDR_LT(least_ha, q) && ADDR_LT(q, greatest_ha)) {
×
190
        PUSH_CONTENTS(q, mark_stack_top, mark_stack_limit, current_p);
×
191
      }
192
    }
193
  }
194
  if (GC_ext_descriptors[env].ed_continued) {
×
195
    /*
196
     * Push an entry with the rest of the descriptor back onto the stack.
197
     * Thus we never do too much work at once.  Note that we also cannot
198
     * overflow the mark stack unless we actually mark something.
199
     */
200
    mark_stack_top
201
        = GC_ms_push_obj_descr((ptr_t *)addr + CPP_WORDSZ,
×
UNCOV
202
                               GC_MAKE_PROC(GC_typed_mark_proc_index, env + 1),
×
203
                               mark_stack_top, mark_stack_limit);
204
  }
UNCOV
205
  return mark_stack_top;
×
206
}
207

208
GC_API GC_descr GC_CALL
209
GC_make_descriptor(const GC_word *bm, size_t len)
630✔
210
{
211
  GC_signed_word last_set_bit = (GC_signed_word)len - 1;
630✔
212
  GC_descr d;
213

214
#if defined(AO_HAVE_load_acquire) && defined(AO_HAVE_store_release)
215
  if (UNLIKELY(!AO_load_acquire(&GC_explicit_typing_initialized))) {
630✔
216
    LOCK();
45✔
217
    if (!GC_explicit_typing_initialized) {
45✔
218
      GC_init_explicit_typing();
45✔
219
      AO_store_release(&GC_explicit_typing_initialized, TRUE);
45✔
220
    }
221
    UNLOCK();
45✔
222
  }
223
#else
224
  LOCK();
225
  if (UNLIKELY(!GC_explicit_typing_initialized)) {
226
    GC_init_explicit_typing();
227
    GC_explicit_typing_initialized = TRUE;
228
  }
229
  UNLOCK();
230
#endif
231

232
  while (last_set_bit >= 0 && !GC_get_bit(bm, (word)last_set_bit))
1,638✔
233
    last_set_bit--;
1,008✔
234
  if (last_set_bit < 0) {
630✔
235
    /* No pointers. */
UNCOV
236
    return 0;
×
237
  }
238

239
#if ALIGNMENT == CPP_PTRSZ / 8
240
  {
241
    GC_signed_word i;
242

243
    for (i = 0; i < last_set_bit; i++) {
43,722✔
244
      if (!GC_get_bit(bm, (word)i))
43,470✔
245
        break;
378✔
246
    }
247
    if (i == last_set_bit) {
630✔
248
      /*
249
       * The initial section contains all pointers; use the length
250
       * descriptor.
251
       */
252
      return PTRS_TO_BYTES((word)last_set_bit + 1) | GC_DS_LENGTH;
252✔
253
    }
254
  }
255
#endif
256
  if (last_set_bit < BITMAP_BITS) {
378✔
257
    GC_signed_word i;
258

259
    /*
260
     * Hopefully the common case.  Build the bitmap descriptor (with the
261
     * bits reversed).
262
     */
263
    d = SIGNB;
378✔
264
    for (i = last_set_bit - 1; i >= 0; i--) {
8,316✔
265
      d >>= 1;
7,938✔
266
      if (GC_get_bit(bm, (word)i))
7,938✔
267
        d |= SIGNB;
7,308✔
268
    }
269
    d |= GC_DS_BITMAP;
378✔
270
  } else {
UNCOV
271
    GC_signed_word index = GC_add_ext_descriptor(bm, (size_t)last_set_bit + 1);
×
272

UNCOV
273
    if (UNLIKELY(index < 0)) {
×
274
      /* Out of memory: use a conservative approximation. */
UNCOV
275
      return PTRS_TO_BYTES((word)last_set_bit + 1) | GC_DS_LENGTH;
×
276
    }
277
#ifdef LINT2
278
    if ((word)index > MAX_ENV)
279
      ABORT("GC_add_ext_descriptor() result cannot exceed MAX_ENV");
280
#endif
UNCOV
281
    d = GC_MAKE_PROC(GC_typed_mark_proc_index, index);
×
282
  }
283
  return d;
378✔
284
}
285

286
static void
287
set_obj_descr(ptr_t op, GC_descr d)
2,016,126✔
288
{
289
  size_t sz;
290

291
  if (UNLIKELY(NULL == op))
2,016,126✔
UNCOV
292
    return;
×
293
  /*
294
   * It is not safe to use `GC_size_map[]` here as the table might be
295
   * updated asynchronously.
296
   */
297
  sz = GC_size(op);
2,016,126✔
298

299
  GC_ASSERT((sz & (GC_GRANULE_BYTES - 1)) == 0 && sz > sizeof(GC_descr));
2,016,126✔
300
#ifdef AO_HAVE_store_release
301
  AO_store_release((volatile AO_t *)&op[sz - sizeof(GC_descr)], d);
2,016,126✔
302
#else
303
  *(GC_descr *)&op[sz - sizeof(GC_descr)] = d;
304
#endif
305
}
306

307
GC_API GC_ATTR_MALLOC void *GC_CALL
308
GC_malloc_explicitly_typed(size_t lb, GC_descr d)
2,016,126✔
309
{
310
  ptr_t op;
311

312
  GC_ASSERT(GC_explicit_typing_initialized);
2,016,126✔
313
  if (UNLIKELY(lb < sizeof(ptr_t) - sizeof(GC_descr) + 1)) {
2,016,126✔
314
    /* Ensure the descriptor does not occupy the first pointer place. */
UNCOV
315
    lb = sizeof(ptr_t) - sizeof(GC_descr) + 1;
×
316
  }
317
  op = (ptr_t)GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(GC_descr) - EXTRA_BYTES),
2,016,126✔
318
                             GC_explicit_kind);
319
  set_obj_descr(op, d);
2,016,126✔
320
  return op;
2,016,126✔
321
}
322

323
GC_API GC_ATTR_MALLOC void *GC_CALL
324
GC_malloc_explicitly_typed_ignore_off_page(size_t lb, GC_descr d)
2,016✔
325
{
326
  ptr_t op;
327

328
  if (lb < HBLKSIZE - sizeof(GC_descr))
2,016✔
329
    return GC_malloc_explicitly_typed(lb, d);
2,016✔
330

UNCOV
331
  GC_ASSERT(GC_explicit_typing_initialized);
×
332
  /*
333
   * Note that ignore-off-page objects with the requested size of
334
   * at least `HBLKSIZE` do not have `EXTRA_BYTES` added by
335
   * `GC_generic_malloc_aligned()`.
336
   */
337
  op = (ptr_t)GC_clear_stack(
×
UNCOV
338
      GC_generic_malloc_aligned(SIZET_SAT_ADD(lb, sizeof(GC_descr)),
×
339
                                GC_explicit_kind, IGNORE_OFF_PAGE, 0));
340
  set_obj_descr(op, d);
×
UNCOV
341
  return op;
×
342
}
343

344
/*
345
 * Array descriptors.  `GC_array_mark_proc` understands these.
346
 * We may eventually need to add provisions for headers and trailers.
347
 * Hence we provide for tree structured descriptors, though we do not
348
 * really use them currently.
349
 */
350

351
/* This type describes simple array. */
352
struct LeafDescriptor {
353
  word ld_tag;
354
#define LEAF_TAG 1
355
  /* Bytes per element; nonzero, multiple of `ALIGNMENT`. */
356
  size_t ld_size;
357
  /* Number of elements. */
358
  size_t ld_nelements;
359
  /* A simple length, bitmap, or procedure descriptor. */
360
  GC_descr ld_descriptor;
361
};
362

363
struct ComplexArrayDescriptor {
364
  word ad_tag;
365
#define ARRAY_TAG 2
366
  size_t ad_nelements;
367
  union ComplexDescriptor *ad_element_descr;
368
};
369

370
struct SequenceDescriptor {
371
  word sd_tag;
372
#define SEQUENCE_TAG 3
373
  union ComplexDescriptor *sd_first;
374
  union ComplexDescriptor *sd_second;
375
};
376

377
typedef union ComplexDescriptor {
378
  struct LeafDescriptor ld;
379
  struct ComplexArrayDescriptor ad;
380
  struct SequenceDescriptor sd;
381
} complex_descriptor;
382

383
STATIC complex_descriptor *
384
GC_make_leaf_descriptor(size_t size, size_t nelements, GC_descr d)
252✔
385
{
386
  complex_descriptor *result
387
      = (complex_descriptor *)GC_malloc_atomic(sizeof(struct LeafDescriptor));
252✔
388

389
  GC_ASSERT(size != 0);
252✔
390
  if (UNLIKELY(NULL == result))
252✔
UNCOV
391
    return NULL;
×
392

393
  result->ld.ld_tag = LEAF_TAG;
252✔
394
  result->ld.ld_size = size;
252✔
395
  result->ld.ld_nelements = nelements;
252✔
396
  result->ld.ld_descriptor = d;
252✔
397
  return result;
252✔
398
}
399

400
STATIC complex_descriptor *
401
GC_make_sequence_descriptor(complex_descriptor *first,
126✔
402
                            complex_descriptor *second)
403
{
404
  /*
405
   * Note: for a reason, the sanitizer runtime complains of insufficient
406
   * space for `complex_descriptor` if the pointer type of `result` variable
407
   * is changed to.
408
   */
409
  struct SequenceDescriptor *result = (struct SequenceDescriptor *)GC_malloc(
126✔
410
      sizeof(struct SequenceDescriptor));
411

412
  if (UNLIKELY(NULL == result))
126✔
UNCOV
413
    return NULL;
×
414

415
  /*
416
   * Cannot result in overly conservative marking, since tags are very
417
   * small integers.  Probably faster than maintaining type information.
418
   */
419
  result->sd_tag = SEQUENCE_TAG;
126✔
420
  result->sd_first = first;
126✔
421
  result->sd_second = second;
126✔
422
  GC_dirty(result);
126✔
423
  REACHABLE_AFTER_DIRTY(first);
126✔
424
  REACHABLE_AFTER_DIRTY(second);
126✔
425
  return (complex_descriptor *)result;
126✔
426
}
427

428
#define NO_MEM (-1)
429
#define SIMPLE 0
430
#define LEAF 1
431
#define COMPLEX 2
432

433
/*
434
 * Build a descriptor for an array with `nelements` elements, each of
435
 * which can be described by a simple descriptor `d`.
436
 * We try to optimize some common cases.  If the result is `COMPLEX`,
437
 * a `complex_descriptor *` value is returned in `*p_complex_d`.
438
 * If the result is `LEAF`, then a `LeafDescriptor` value is built in the
439
 * structure pointed to by `pleaf`.  The tag in the `*pleaf` structure
440
 * is not set.  If the result is `SIMPLE`, then a `GC_descr` value is
441
 * returned in `*psimple_d`.  If the result is `NO_MEM`, then we failed
442
 * to allocate the descriptor.  The implementation assumes `GC_DS_LENGTH`
443
 * is zero.  `*pleaf`, `*p_complex_d` and `*psimple_d` may be used as
444
 * temporaries during the construction.
445
 */
446
STATIC int
447
GC_make_array_descriptor(size_t nelements, size_t size, GC_descr d,
1,006,236✔
448
                         GC_descr *psimple_d, complex_descriptor **p_complex_d,
449
                         struct LeafDescriptor *pleaf)
450
{
451
  /*
452
   * For larger arrays, we try to combine descriptors of adjacent
453
   * descriptors to speed up marking, and to reduce the amount of space
454
   * needed on the mark stack.
455
   */
456
#define OPT_THRESHOLD 50
457

458
  GC_ASSERT(size != 0);
1,006,236✔
459
  if ((d & GC_DS_TAGS) == GC_DS_LENGTH) {
1,006,236✔
460
    if (d == (GC_descr)size) {
504,000✔
461
      /* Note: no overflow is guaranteed by caller. */
462
      *psimple_d = nelements * d;
504,000✔
463
      return SIMPLE;
504,000✔
464
    } else if (0 == d) {
×
465
      *psimple_d = 0;
×
UNCOV
466
      return SIMPLE;
×
467
    }
468
  }
469

470
  if (nelements <= OPT_THRESHOLD) {
502,236✔
471
    if (nelements <= 1) {
501,984✔
472
      *psimple_d = nelements == 1 ? d : 0;
×
UNCOV
473
      return SIMPLE;
×
474
    }
475
  } else if (size <= BITMAP_BITS / 2 && (d & GC_DS_TAGS) != GC_DS_PROC
252✔
476
             && (size & (sizeof(ptr_t) - 1)) == 0) {
126✔
477
    complex_descriptor *one_element, *beginning;
478
    int result = GC_make_array_descriptor(
126✔
479
        nelements / 2, 2 * size, GC_double_descr(d, BYTES_TO_PTRS(size)),
480
        psimple_d, p_complex_d, pleaf);
481

482
    if ((nelements & 1) == 0 || UNLIKELY(NO_MEM == result))
126✔
UNCOV
483
      return result;
×
484

485
    one_element = GC_make_leaf_descriptor(size, 1, d);
126✔
486
    if (UNLIKELY(NULL == one_element))
126✔
UNCOV
487
      return NO_MEM;
×
488

489
    if (COMPLEX == result) {
126✔
UNCOV
490
      beginning = *p_complex_d;
×
491
    } else {
492
      beginning
493
          = SIMPLE == result
126✔
UNCOV
494
                ? GC_make_leaf_descriptor(size, 1, *psimple_d)
×
495
                : GC_make_leaf_descriptor(pleaf->ld_size, pleaf->ld_nelements,
126✔
496
                                          pleaf->ld_descriptor);
497
      if (UNLIKELY(NULL == beginning))
126✔
UNCOV
498
        return NO_MEM;
×
499
    }
500
    *p_complex_d = GC_make_sequence_descriptor(beginning, one_element);
126✔
501
    if (UNLIKELY(NULL == *p_complex_d))
126✔
UNCOV
502
      return NO_MEM;
×
503

504
    return COMPLEX;
126✔
505
  }
506

507
  pleaf->ld_size = size;
502,110✔
508
  pleaf->ld_nelements = nelements;
502,110✔
509
  pleaf->ld_descriptor = d;
502,110✔
510
  return LEAF;
502,110✔
511
}
512

513
struct GC_calloc_typed_descr_s {
514
  complex_descriptor *complex_d; /*< the first field, the only pointer */
515
  struct LeafDescriptor leaf;
516
  GC_descr simple_d;
517
  word alloc_lb;             /*< of `size_t` type actually */
518
  GC_signed_word descr_type; /*< of `int` type actually */
519
};
520

521
GC_API int GC_CALL
522
GC_calloc_prepare_explicitly_typed(struct GC_calloc_typed_descr_s *p_ctd,
1,006,110✔
523
                                   size_t ctd_sz, size_t n, size_t lb,
524
                                   GC_descr d)
525
{
526
  GC_STATIC_ASSERT(sizeof(struct GC_calloc_typed_descr_opaque_s)
527
                   == sizeof(struct GC_calloc_typed_descr_s));
528
  GC_ASSERT(GC_explicit_typing_initialized);
1,006,110✔
529
  GC_ASSERT(sizeof(struct GC_calloc_typed_descr_s) == ctd_sz);
1,006,110✔
530
  (void)ctd_sz; /*< unused currently */
531
  if (UNLIKELY(0 == lb || 0 == n))
1,006,110✔
UNCOV
532
    lb = n = 1;
×
533
  if (UNLIKELY((lb | n) > GC_SQRT_SIZE_MAX) /*< fast initial check */
1,006,110✔
UNCOV
534
      && n > GC_SIZE_MAX / lb) {
×
535
    /* `n * lb` overflows. */
536
    p_ctd->alloc_lb = GC_SIZE_MAX;
×
UNCOV
537
    p_ctd->descr_type = NO_MEM;
×
538
    /* The rest of the fields are unset. */
UNCOV
539
    return 0; /*< failure */
×
540
  }
541

542
  p_ctd->descr_type = GC_make_array_descriptor(
1,006,110✔
543
      n, lb, d, &p_ctd->simple_d, &p_ctd->complex_d, &p_ctd->leaf);
544
  switch (p_ctd->descr_type) {
1,006,110✔
545
  case NO_MEM:
504,000✔
546
  case SIMPLE:
547
    p_ctd->alloc_lb = (word)lb * n;
504,000✔
548
    break;
504,000✔
549
  case LEAF:
501,984✔
550
    p_ctd->alloc_lb = SIZET_SAT_ADD(
501,984✔
551
        lb * n, (BYTES_TO_PTRS_ROUNDUP(sizeof(struct LeafDescriptor)) + 1)
552
                        * sizeof(ptr_t)
553
                    - EXTRA_BYTES);
554
    break;
501,984✔
555
  case COMPLEX:
126✔
556
    p_ctd->alloc_lb = SIZET_SAT_ADD(lb * n, sizeof(ptr_t) - EXTRA_BYTES);
126✔
557
    break;
126✔
558
  }
559
  return 1; /*< success */
1,006,110✔
560
}
561

562
GC_API GC_ATTR_MALLOC void *GC_CALL
563
GC_calloc_do_explicitly_typed(const struct GC_calloc_typed_descr_s *p_ctd,
1,008,000✔
564
                              size_t ctd_sz)
565
{
566
  void *op;
567
  size_t lpw_m1;
568

569
  GC_ASSERT(sizeof(struct GC_calloc_typed_descr_s) == ctd_sz);
1,008,000✔
570
  (void)ctd_sz; /*< unused currently */
571
  switch (p_ctd->descr_type) {
1,008,000✔
572
  case NO_MEM:
×
UNCOV
573
    return (*GC_get_oom_fn())((size_t)p_ctd->alloc_lb);
×
574
  case SIMPLE:
504,000✔
575
    return GC_malloc_explicitly_typed((size_t)p_ctd->alloc_lb,
504,000✔
576
                                      p_ctd->simple_d);
504,000✔
577
  case LEAF:
504,000✔
578
  case COMPLEX:
579
    break;
504,000✔
580
  default:
×
581
    ABORT_RET("Bad descriptor type");
×
UNCOV
582
    return NULL;
×
583
  }
584
  op = GC_malloc_kind((size_t)p_ctd->alloc_lb, GC_array_kind);
504,000✔
585
  if (UNLIKELY(NULL == op))
504,000✔
UNCOV
586
    return NULL;
×
587

588
  lpw_m1 = BYTES_TO_PTRS(GC_size(op)) - 1;
504,000✔
589
  if (p_ctd->descr_type == LEAF) {
504,000✔
590
    /* Set up the descriptor inside the object itself. */
591
    struct LeafDescriptor *lp
501,984✔
592
        = (struct LeafDescriptor *)((ptr_t *)op + lpw_m1
501,984✔
593
                                    - BYTES_TO_PTRS_ROUNDUP(
501,984✔
594
                                        sizeof(struct LeafDescriptor)));
595

596
    lp->ld_tag = LEAF_TAG;
501,984✔
597
    lp->ld_size = p_ctd->leaf.ld_size;
501,984✔
598
    lp->ld_nelements = p_ctd->leaf.ld_nelements;
501,984✔
599
    lp->ld_descriptor = p_ctd->leaf.ld_descriptor;
501,984✔
600
    /*
601
     * Hold the allocator lock (in the reader mode which should be enough)
602
     * while writing the descriptor `word` to the object to ensure that
603
     * the descriptor contents are seen by `GC_array_mark_proc` as expected.
604
     */
605

606
    /*
607
     * TODO: It should be possible to replace locking with the atomic
608
     * operations (with the release barrier here) but, in this case,
609
     * avoiding the acquire barrier in `GC_array_mark_proc` seems to
610
     * be tricky as `GC_mark_some` might be invoked with the world running.
611
     */
612
    READER_LOCK();
501,984✔
613
    ((struct LeafDescriptor **)op)[lpw_m1] = lp;
501,984✔
614
    READER_UNLOCK_RELEASE();
501,984✔
615
  } else {
616
#ifndef GC_NO_FINALIZATION
617
    READER_LOCK();
2,016✔
618
    ((complex_descriptor **)op)[lpw_m1] = p_ctd->complex_d;
2,016✔
619
    READER_UNLOCK_RELEASE();
2,016✔
620

621
    GC_dirty((ptr_t *)op + lpw_m1);
2,016✔
622
    REACHABLE_AFTER_DIRTY(p_ctd->complex_d);
2,016✔
623

624
    /*
625
     * Make sure the descriptor is cleared once there is any danger
626
     * it may have been collected.
627
     */
628
    if (UNLIKELY(
2,016✔
629
            GC_general_register_disappearing_link((void **)op + lpw_m1, op)
630
            == GC_NO_MEMORY))
631
#endif
632
    {
633
      /* Could not register it due to lack of memory.  Punt. */
UNCOV
634
      return (*GC_get_oom_fn())((size_t)p_ctd->alloc_lb);
×
635
    }
636
  }
637
  return op;
504,000✔
638
}
639

640
GC_API GC_ATTR_MALLOC void *GC_CALL
641
GC_calloc_explicitly_typed(size_t n, size_t lb, GC_descr d)
1,005,984✔
642
{
643
  struct GC_calloc_typed_descr_s ctd;
644

645
  (void)GC_calloc_prepare_explicitly_typed(&ctd, sizeof(ctd), n, lb, d);
1,005,984✔
646
  return GC_calloc_do_explicitly_typed(&ctd, sizeof(ctd));
1,005,984✔
647
}
648

649
/*
650
 * Return the size of the object described by `complex_d`.
651
 * It would be faster to store this directly, or to compute it as part
652
 * of `GC_push_complex_descriptor`, but hopefully it does not matter.
653
 */
654
STATIC size_t
655
GC_descr_obj_size(complex_descriptor *complex_d)
12,093✔
656
{
657
  switch (complex_d->ad.ad_tag) {
12,093✔
658
  case LEAF_TAG:
12,093✔
659
    return complex_d->ld.ld_nelements * complex_d->ld.ld_size;
12,093✔
660
  case ARRAY_TAG:
×
661
    return complex_d->ad.ad_nelements
×
662
           * GC_descr_obj_size(complex_d->ad.ad_element_descr);
×
663
  case SEQUENCE_TAG:
×
664
    return GC_descr_obj_size(complex_d->sd.sd_first)
×
665
           + GC_descr_obj_size(complex_d->sd.sd_second);
×
666
  default:
×
667
    ABORT_RET("Bad complex descriptor");
×
UNCOV
668
    return 0;
×
669
  }
670
}
671

672
/*
673
 * Push descriptors for the object with the complex descriptor onto
674
 * the mark stack.  Return `NULL` if the mark stack overflowed.
675
 */
676
STATIC mse *
677
GC_push_complex_descriptor(ptr_t current, complex_descriptor *complex_d,
2,697,199✔
678
                           mse *msp, mse *msl)
679
{
680
  size_t i, nelements;
681
  size_t sz;
682
  GC_descr d;
683
  complex_descriptor *element_descr;
684

685
  switch (complex_d->ad.ad_tag) {
2,697,199✔
686
  case LEAF_TAG:
2,685,106✔
687
    d = complex_d->ld.ld_descriptor;
2,685,106✔
688
    nelements = complex_d->ld.ld_nelements;
2,685,106✔
689
    sz = complex_d->ld.ld_size;
2,685,106✔
690

691
    if (UNLIKELY(msl - msp <= (GC_signed_word)nelements))
2,685,106✔
UNCOV
692
      return NULL;
×
693
    GC_ASSERT(sz != 0);
2,685,106✔
694
    for (i = 0; i < nelements; i++) {
27,370,139✔
695
      msp++;
24,685,033✔
696
      msp->mse_start = current;
24,685,033✔
697
      msp->mse_descr = d;
24,685,033✔
698
      current += sz;
24,685,033✔
699
    }
700
    break;
2,685,106✔
701
  case ARRAY_TAG:
×
702
    element_descr = complex_d->ad.ad_element_descr;
×
703
    nelements = complex_d->ad.ad_nelements;
×
704
    sz = GC_descr_obj_size(element_descr);
×
705
    GC_ASSERT(sz != 0 || 0 == nelements);
×
706
    for (i = 0; i < nelements; i++) {
×
707
      msp = GC_push_complex_descriptor(current, element_descr, msp, msl);
×
708
      if (UNLIKELY(NULL == msp))
×
709
        return NULL;
×
UNCOV
710
      current += sz;
×
711
    }
UNCOV
712
    break;
×
713
  case SEQUENCE_TAG:
12,093✔
714
    sz = GC_descr_obj_size(complex_d->sd.sd_first);
12,093✔
715
    msp = GC_push_complex_descriptor(current, complex_d->sd.sd_first, msp,
12,093✔
716
                                     msl);
717
    if (UNLIKELY(NULL == msp))
12,093✔
UNCOV
718
      return NULL;
×
719
    GC_ASSERT(sz != 0);
12,093✔
720
    current += sz;
12,093✔
721
    msp = GC_push_complex_descriptor(current, complex_d->sd.sd_second, msp,
12,093✔
722
                                     msl);
723
    break;
12,093✔
724
  default:
×
UNCOV
725
    ABORT("Bad complex descriptor");
×
726
  }
727
  return msp;
2,697,199✔
728
}
729

730
GC_ATTR_NO_SANITIZE_THREAD
731
static complex_descriptor *
732
get_complex_descr(ptr_t *p, size_t lpw)
2,673,202✔
733
{
734
  return (complex_descriptor *)p[lpw - 1];
2,673,202✔
735
}
736

737
/* Used by `GC_calloc_do_explicitly_typed()` via `GC_array_kind`. */
738
STATIC mse *GC_CALLBACK
739
GC_array_mark_proc(word *addr, mse *mark_stack_top, mse *mark_stack_limit,
2,673,202✔
740
                   word env)
741
{
742
  size_t sz = HDR(addr)->hb_sz;
2,673,202✔
743
  size_t lpw = BYTES_TO_PTRS(sz);
2,673,202✔
744
  complex_descriptor *complex_d = get_complex_descr((ptr_t *)addr, lpw);
2,673,202✔
745
  mse *orig_mark_stack_top = mark_stack_top;
2,673,202✔
746
  mse *new_mark_stack_top;
747

748
  UNUSED_ARG(env);
749
  if (NULL == complex_d) {
2,673,202✔
750
    /* Found a reference to a free-list entry.  Ignore it. */
751
    return orig_mark_stack_top;
189✔
752
  }
753
  /*
754
   * In-use counts were already updated when array descriptor was pushed.
755
   * Here we only replace it by subobject descriptors, so no update is
756
   * necessary.
757
   */
758
  new_mark_stack_top = GC_push_complex_descriptor(
2,673,013✔
759
      (ptr_t)addr, complex_d, mark_stack_top, mark_stack_limit - 1);
760
  if (NULL == new_mark_stack_top) {
2,673,013✔
761
    /* Explicitly instruct Clang Static Analyzer that pointer is non-`NULL`. */
762
    if (NULL == mark_stack_top) {
×
UNCOV
763
      ABORT("Bad mark_stack_top");
×
764
    }
765

766
    /*
767
     * Does not fit.  Conservatively push the whole array as a unit and
768
     * request a mark stack expansion.  This cannot cause a mark stack
769
     * overflow, since it replaces the original array entry.
770
     */
771
#ifdef PARALLEL_MARK
772
    /* We might be using a `local_mark_stack` in the parallel collection. */
UNCOV
773
    if (GC_mark_stack + GC_mark_stack_size == mark_stack_limit)
×
774
#endif
775
    {
UNCOV
776
      GC_mark_stack_too_small = TRUE;
×
777
    }
778
    new_mark_stack_top = orig_mark_stack_top + 1;
×
779
    new_mark_stack_top->mse_start = (ptr_t)addr;
×
UNCOV
780
    new_mark_stack_top->mse_descr = sz | GC_DS_LENGTH;
×
781
  } else {
782
    /* Push descriptor itself. */
783
    new_mark_stack_top++;
2,673,013✔
784
    new_mark_stack_top->mse_start = (ptr_t)((ptr_t *)addr + lpw - 1);
2,673,013✔
785
    new_mark_stack_top->mse_descr = sizeof(ptr_t) | GC_DS_LENGTH;
2,673,013✔
786
  }
787
  return new_mark_stack_top;
2,673,013✔
788
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc