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

nickg / nvc / 21785110820

07 Feb 2026 06:50PM UTC coverage: 92.6% (-0.004%) from 92.604%
21785110820

push

github

nickg
Work around case where cloned layout may not match

4 of 4 new or added lines in 1 file covered. (100.0%)

146 existing lines in 6 files now uncovered.

76629 of 82753 relevant lines covered (92.6%)

441555.36 hits per line

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

92.04
/src/object.c
1
//
2
//  Copyright (C) 2014-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "common.h"
20
#include "diag.h"
21
#include "fbuf.h"
22
#include "hash.h"
23
#include "ident.h"
24
#include "object.h"
25
#include "option.h"
26
#include "thread.h"
27

28
#include <string.h>
29
#include <stdlib.h>
30
#include <inttypes.h>
31
#include <signal.h>
32

33
typedef uint64_t mark_mask_t;
34

35
typedef A(object_arena_t *) arena_array_t;
36
typedef A(object_t **) object_ptr_array_t;
37

38
typedef enum { OBJ_DISK, OBJ_FRESH } obj_src_t;
39

40
typedef struct _object_arena {
41
   void           *base;
42
   void           *alloc;
43
   void           *limit;
44
   uint32_t       *forward;
45
   mark_mask_t    *mark_bits;
46
   size_t          mark_sz;
47
   size_t          mark_low;
48
   size_t          mark_high;
49
   uint32_t        live_bytes;
50
   uint32_t        flags;
51
   generation_t    generation;
52
   arena_key_t     key;
53
   arena_array_t   deps;
54
   object_t       *root;
55
   obj_src_t       source;
56
   vhdl_standard_t std;
57
   uint32_t        checksum;
58
   generation_t    copygen;
59
   bool            frozen;
60
   bool            obsolete;
61
} object_arena_t;
62

63
#if !ASAN_ENABLED
64
#define OBJECT_UNMAP_UNUSED 1
65
#endif
66

67
#define ITEM_IDENT       (I_IDENT | I_IDENT2)
68
#define ITEM_OBJECT      (I_VALUE | I_SEVERITY | I_MESSAGE | I_TARGET   \
69
                          | I_DELAY | I_REJECT | I_REF | I_FILE_MODE    \
70
                          | I_NAME | I_SPEC | I_RESOLUTION              \
71
                          | I_LEFT | I_RIGHT | I_TYPE | I_BASE | I_ELEM \
72
                          | I_DESIGNATED | I_RESULT | I_PRIMARY         \
73
                          | I_GUARD | I_FOREIGN | I_CONSTRAINT)
74
#define ITEM_OBJ_ARRAY   (I_DECLS | I_STMTS | I_PORTS | I_GENERICS      \
75
                          | I_WAVES | I_CONDS | I_TRIGGERS | I_PARAMS   \
76
                          | I_GENMAPS | I_ASSOCS | I_CONTEXT            \
77
                          | I_LITERALS | I_FIELDS | I_UNITS | I_CHARS   \
78
                          | I_DIMS | I_RANGES | I_INDEXES | I_PARTS \
79
                          | I_PRAGMAS | I_CHOICES)
80
#define ITEM_INT64       (I_POS | I_IVAL)
81
#define ITEM_INT32       (I_SUBKIND | I_CLASS | I_FLAGS)
82
#define ITEM_DOUBLE      (I_DVAL)
83
#define ITEM_NUMBER      (I_NUMBER)
84

85
static const char *item_text_map[] = {
86
   "I_IDENT",    "I_VALUE",      "I_PRIMARY",  "I_GENERICS",   "I_PORTS",
87
   "I_DECLS",    "I_STMTS",      "I_TARGET",   "I_IVAL",       "I_IDENT2",
88
   "I_SEVERITY", "I_GENMAPS",    "I_PARAMS",   "I_WAVES",      "I_CONDS",
89
   "I_TYPE",     "I_SUBKIND",    "I_DELAY",    "I_REJECT",     "I_POS",
90
   "I_REF",      "I_FILE_MODE",  "I_ASSOCS",   "I_CONTEXT",    "I_TRIGGERS",
91
   "I_PARTS"  ,  "I_CLASS",      "I_RANGES",   "I_NAME",       "I_PRAGMAS",
92
   "I_DVAL",     "I_SPEC",       "I_FOREIGN",  "I_INDEXES",    "I_BASE",
93
   "I_ELEM",     "I_DESIGNATED", "I_CHOICES",  "I_RESOLUTION", "I_RESULT",
94
   "I_UNITS",    "I_LITERALS",   "I_DIMS",     "I_FIELDS",     "I_CLOCK",
95
   "I_GUARD",    "???",          "I_CHARS",    "I_CONSTRAINT", "I_FLAGS",
96
   "???",        "I_LEFT",       "I_RIGHT",    "I_NUMBER",     "I_MESSAGE",
97
};
98

99
static object_class_t *classes[4];
100
static uint32_t        format_digest;
101
static generation_t    next_generation = 1;
102
static arena_array_t   all_arenas;
103
static object_arena_t *global_arena = NULL;
104
static chash_t        *arena_lookup;
105

106
static inline bool object_in_arena_p(object_arena_t *arena, object_t *object)
11,097,680✔
107
{
108
   return (void *)object >= arena->base && (void *)object < arena->limit;
11,097,680✔
109
}
110

111
static inline object_arena_t *__object_arena(object_t *object)
27,128,465✔
112
{
113
   assert(object->arena < all_arenas.count);
27,128,465✔
114
   assert(object->arena != 0);
27,128,465✔
115
   return all_arenas.items[object->arena];
27,128,465✔
116
}
117

118
static ident_t object_arena_name(object_arena_t *arena)
173,569✔
119
{
120
   if (arena->alloc > arena->base) {
173,569✔
121
      ident_t name = object_ident(arena_root(arena));
165,534✔
122
      if (name != NULL)
165,534✔
123
         return name;
124
   }
125

126
   return ident_new("???");
10,625✔
127
}
128

129
static inline void zero_mark_bits(object_arena_t *arena, unsigned first,
178,116✔
130
                                  size_t count)
131
{
132
   assert(first + count <= arena->mark_sz);
178,116✔
133
   assert(first > arena->mark_high || first + count <= arena->mark_low);
178,116✔
134

135
   if (count == 1)
178,116✔
136
      arena->mark_bits[first] = 0;
155,043✔
137
   else
138
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
23,073✔
139
}
178,116✔
140

141
static bool object_marked_p(object_t *object, generation_t generation)
11,500,941✔
142
{
143
   object_arena_t *arena = __object_arena(object);
11,500,941✔
144

145
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
11,500,941✔
146
   const uintptr_t word = bit / 64;
11,500,941✔
147

148
   if (unlikely(arena->mark_bits == NULL)) {
11,500,941✔
149
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
17,489✔
150
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
17,489✔
151
      arena->mark_bits = xmalloc(arena->mark_sz);
17,489✔
152
      arena->mark_bits[word] = 0;
17,489✔
153
      arena->mark_low = arena->mark_high = word;
17,489✔
154
      arena->generation = generation;
17,489✔
155
   }
156
   else if (arena->generation != generation) {
11,483,452✔
157
      arena->mark_bits[word] = 0;
56,011✔
158
      arena->mark_low = arena->mark_high = word;
56,011✔
159
      arena->generation = generation;
56,011✔
160
   }
161

162
   // Lazy zeroing of mark bits helps performance with large arenas
163
   if (word < arena->mark_low) {
11,500,941✔
164
      zero_mark_bits(arena, word, arena->mark_low - word);
7,122✔
165
      arena->mark_low = word;
7,122✔
166
   }
167
   else if (word > arena->mark_high) {
11,493,819✔
168
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
170,994✔
169
      arena->mark_high = word;
170,994✔
170
   }
171

172
   const uint64_t mask = UINT64_C(1) << (bit & 63);
11,500,941✔
173

174
   const bool marked = !!(arena->mark_bits[word] & mask);
11,500,941✔
175
   arena->mark_bits[word] |= mask;
11,500,941✔
176

177
   return marked;
11,500,941✔
178
}
179

180
void arena_set_checksum(object_arena_t *arena, uint32_t checksum)
30,541✔
181
{
182
   arena->checksum = checksum;
30,541✔
183
}
30,541✔
184

185
object_t *arena_root(object_arena_t *arena)
254,426✔
186
{
187
   return arena->root ?: (object_t *)arena->base;
254,426✔
188
}
189

190
bool arena_frozen(object_arena_t *arena)
110,844✔
191
{
192
   return arena->frozen;
110,844✔
193
}
194

195
uint32_t arena_flags(object_arena_t *arena)
53,717✔
196
{
197
   return arena->flags;
53,717✔
198
}
199

200
void arena_set_flags(object_arena_t *arena, uint32_t flags)
7,224✔
201
{
202
   arena->flags |= flags;
7,224✔
203
}
7,224✔
204

205
void arena_set_obsolete(object_arena_t *arena, bool obsolete)
24✔
206
{
207
   arena->obsolete = true;
24✔
208
}
24✔
209

210
object_arena_t *object_arena(object_t *object)
266,631✔
211
{
212
   return __object_arena(object);
266,631✔
213
}
214

215
ident_t object_ident(object_t *object)
165,540✔
216
{
217
   const object_class_t *class = classes[object->tag];
165,540✔
218
   const imask_t has = class->has_map[object->kind];
165,540✔
219

220
   if (has & I_IDENT) {
165,540✔
221
      const int n = __builtin_popcountll(has & (I_IDENT - 1));
163,147✔
222
      return object->items[n].ident;
163,147✔
223
   }
224

225
   return NULL;
226
}
227

228
void __object_write_barrier(object_t *lhs, object_t *rhs)
5,050,009✔
229
{
230
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
5,050,009✔
231
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
5,050,009✔
232

233
   if (lhs_mask == rhs_mask || rhs == NULL)
5,050,009✔
234
      return;
235
   else if (lhs->arena == rhs->arena)
5,050,009✔
236
      return;
237

238
   object_arena_t *larena = __object_arena(lhs);
4,502,993✔
239
   object_arena_t *rarena = __object_arena(rhs);
4,502,993✔
240

241
   assert(!larena->frozen);
4,502,993✔
242
   assert(rarena->frozen);
4,502,993✔
243

244
   for (unsigned i = 0; i < larena->deps.count; i++) {
7,256,519✔
245
      if (larena->deps.items[i] == rarena)
7,219,536✔
246
         return;
247
   }
248

249
   APUSH(larena->deps, rarena);
36,983✔
250
}
251

252
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
253
{
254
   unsigned int item;
×
255
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
256
      ;
257

258
   assert(item < ARRAY_LEN(item_text_map));
×
259

260
   diag_t *d = diag_new(DIAG_FATAL, &(object->loc));
×
261
   diag_printf(d, "%s kind %s does not have item %s", class->name,
×
262
               class->kind_text_map[object->kind], item_text_map[item]);
×
263
   diag_set_consumer(NULL, NULL);
×
264
   diag_suppress(d, false);
×
265
   diag_emit(d);
×
266
   show_stacktrace();
×
267
   fatal_exit(EXIT_FAILURE);
×
268
}
269

270
void obj_array_add(obj_array_t **a, object_t *o)
1,340,635✔
271
{
272
   if (*a == NULL) {
1,340,635✔
273
      const int defsz = 8;
481,018✔
274
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
481,018✔
275
      (*a)->count = 0;
481,018✔
276
      (*a)->limit = defsz;
481,018✔
277
   }
278
   else if ((*a)->count == (*a)->limit) {
859,617✔
279
      (*a)->limit *= 2;
32,926✔
280
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
32,926✔
281
                         (*a)->limit, sizeof(object_t *));
282
   }
283

284
   (*a)->items[(*a)->count++] = o;
1,340,635✔
285
}
1,340,635✔
286

287
void obj_array_copy(obj_array_t **dst, const obj_array_t *src)
11,471✔
288
{
289
   if (src == NULL)
11,471✔
290
      return;
291

292
   if (*dst == NULL) {
11,471✔
293
      *dst = xmalloc_flex(sizeof(obj_array_t), src->count, sizeof(object_t *));
11,379✔
294
      (*dst)->count = 0;
11,379✔
295
      (*dst)->limit = src->count;
11,379✔
296
   }
297
   else if ((*dst)->count + src->count > (*dst)->limit) {
92✔
298
      (*dst)->limit = (*dst)->count + src->count;
92✔
299
      *dst = xrealloc_flex(*dst, sizeof(obj_array_t),
92✔
300
                           (*dst)->limit, sizeof(object_t *));
301
   }
302

303
   for (int i = 0; i < src->count; i++)
54,150✔
304
      (*dst)->items[(*dst)->count++] = src->items[i];
42,679✔
305
}
306

307
void obj_array_free(obj_array_t **a)
100,418✔
308
{
309
   free(*a);
100,418✔
310
   *a = NULL;
100,418✔
311
}
100,418✔
312

313
void object_change_kind(const object_class_t *class, object_t *object, int kind)
25,278✔
314
{
25,278✔
315
   if (kind == object->kind)
25,278✔
316
      return;
×
317

318
   bool allow = false;
319
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
261,075✔
320
      allow = (class->change_allowed[i][0] == object->kind)
235,797✔
321
         && (class->change_allowed[i][1] == kind);
235,797✔
322
   }
323

324
   if (!allow)
25,278✔
325
      fatal_trace("cannot change %s kind %s to %s", class->name,
326
                  class->kind_text_map[object->kind],
×
327
                  class->kind_text_map[kind]);
×
328

329
   const imask_t old_has = class->has_map[object->kind];
25,278✔
330
   const imask_t new_has = class->has_map[kind];
25,278✔
331

332
   const int old_nitems = __builtin_popcountll(old_has);
25,278✔
333
   const int new_nitems = __builtin_popcountll(new_has);
25,278✔
334

335
   const int max_items = MAX(old_nitems, new_nitems);
25,278✔
336

337
   item_t tmp[max_items];
25,278✔
338
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
25,278✔
339

340
   int op = 0, np = 0;
25,278✔
341
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
1,026,039✔
342
      if ((old_has & mask) && (new_has & mask))
1,000,761✔
343
         object->items[np++] = tmp[op++];
91,981✔
344
      else if (old_has & mask) {
908,780✔
345
         if (ITEM_OBJ_ARRAY & mask)
146✔
346
            obj_array_free(&(tmp[op].obj_array));
116✔
347
         ++op;
146✔
348
      }
349
      else if (new_has & mask)
908,634✔
350
         memset(&(object->items[np++]), '\0', sizeof(item_t));
1,000,761✔
351
   }
352

353
   object->kind = kind;
25,278✔
354
}
355

356
static void object_init(object_class_t *class)
21,532✔
357
{
358
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
21,532✔
359

360
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
21,532✔
361

362
   assert(class->tag < ARRAY_LEN(classes));
21,532✔
363
   classes[class->tag] = class;
21,532✔
364

365
#ifdef DEBUG
366
   imask_t all_items = 0;
21,532✔
367
#endif
368

369
   for (int i = 0; i < class->last_kind; i++) {
1,442,644✔
370
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,421,112✔
371
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,421,112✔
372
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,421,112✔
373

374
      format_digest += knuth_hash(class->has_map[i] >> 32);
1,421,112✔
375
      format_digest += knuth_hash(class->has_map[i]);
1,421,112✔
376
   }
377

378
   bool changed = false;
32,298✔
379
   do {
32,298✔
380
      changed = false;
32,298✔
381
      for (int i = 0; i < class->last_kind; i++) {
2,745,330✔
382
         size_t max_size = class->object_size[i];
2,713,032✔
383
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
33,719,112✔
384
            if (class->change_allowed[j][0] == i)
31,006,080✔
385
               max_size = MAX(max_size,
258,384✔
386
                              class->object_size[class->change_allowed[j][1]]);
387
            else if (class->change_allowed[j][1] == i)
30,747,696✔
388
               max_size = MAX(max_size,
258,384✔
389
                              class->object_size[class->change_allowed[j][0]]);
390
         }
391

392
         if (max_size != class->object_size[i]) {
2,713,032✔
393
            class->object_size[i] = max_size;
64,596✔
394
            changed = true;
64,596✔
395
         }
396
      }
397
   } while (changed);
32,298✔
398

399
#ifdef DEBUG
400
   if (getenv("NVC_TREE_SIZES") != NULL) {
21,532✔
401
      for (int i = 0; i < class->last_kind; i++)
×
402
         printf("%-15s %d\n", class->kind_text_map[i],
×
403
                (int)class->object_size[i]);
×
404
   }
405

406
   const imask_t known_types =
21,532✔
407
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
408
      | ITEM_DOUBLE | ITEM_NUMBER;
409

410
   const imask_t missing = all_items & ~known_types;
21,532✔
411
   if (missing != 0) {
21,532✔
412
      int item;
413
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
414
         ;
415

416
      assert(item < ARRAY_LEN(item_text_map));
×
417
      fatal_trace("item %s does not have a type", item_text_map[item]);
418
   }
419
#endif
420
}
21,532✔
421

422
static void check_frozen_object_fault(int sig, void *addr,
×
423
                                      struct cpu_state *cpu, void *context)
424
{
425
#ifndef __MINGW32__
426
   if (sig != SIGSEGV && sig != SIGBUS)
×
427
      return;
428
#endif
429

430
   for (unsigned i = 1; i < all_arenas.count; i++) {
×
431
      object_arena_t *arena = AGET(all_arenas, i);
×
432
      if (!arena->frozen)
×
433
         continue;
×
434
      else if (addr < arena->base)
×
435
         continue;
×
436
      else if (addr >= arena->limit)
×
437
         continue;
×
438

439
      fatal_trace("Write to object in frozen arena %s [address=%p]",
440
                  istr(object_arena_name(arena)), addr);
441
   }
442
}
443

444
static void object_one_time_init(void)
25,693,429✔
445
{
446
   INIT_ONCE({
25,693,429✔
447
         extern object_class_t tree_object;
448
         object_init(&tree_object);
449

450
         extern object_class_t type_object;
451
         object_init(&type_object);
452

453
         extern object_class_t vlog_object;
454
         object_init(&vlog_object);
455

456
         extern object_class_t psl_object;
457
         object_init(&psl_object);
458

459
         // Increment this each time a incompatible change is made to
460
         // the on-disk format not expressed in the object items table
461
         const uint32_t format_fudge = 45;
462

463
         format_digest += format_fudge * UINT32_C(2654435761);
464

465
         add_fault_handler(check_frozen_object_fault, NULL);
466

467
         arena_lookup = chash_new(64);
468
      });
469
}
25,693,429✔
470

471
static bool is_gc_root(const object_class_t *class, int kind)
2,245,081✔
472
{
473
   for (int j = 0; j < class->gc_num_roots; j++) {
20,844,991✔
474
      if (class->gc_roots[j] == kind)
18,658,636✔
475
         return true;
476
   }
477

478
   return false;
479
}
480

481
object_t *object_new(object_arena_t *arena,
25,648,178✔
482
                     const object_class_t *class, int kind)
483
{
484
   if (unlikely(kind >= class->last_kind))
25,648,178✔
485
      fatal_trace("invalid kind %d for %s object", kind, class->name);
486

487
   object_one_time_init();
25,648,178✔
488

489
   if (arena == NULL)
25,648,178✔
490
      arena = global_arena;
2,013,884✔
491

492
   if (unlikely(arena == NULL))
25,648,178✔
493
      fatal_trace("allocating object without active arena");
494

495
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
25,648,178✔
496

497
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
25,648,178✔
498

499
   if (unlikely(arena->limit - arena->alloc < size)) {
25,648,178✔
500
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
501
      diag_suppress(d, false);
×
502
      diag_printf(d, "memory exhausted while creating unit %s",
×
503
                  istr(object_arena_name(arena)));
504
      diag_hint(d, NULL, "The current limit is %zu bytes which you can "
×
505
                "increase with the $bold$-M$$ option, for example "
506
                "$bold$-M 32m$$", object_arena_default_size());
507
      diag_emit(d);
×
508
      fatal_exit(EXIT_FAILURE);
×
509
   }
510

511
   object_t *object = arena->alloc;
25,648,178✔
512
   arena->alloc = (char *)arena->alloc + size;
25,648,178✔
513

514
   if (arena->root == NULL && is_gc_root(class, kind))
25,648,178✔
515
      arena->root = object;
36,662✔
516

517
   memset(object, '\0', size);
25,648,178✔
518

519
   object->kind  = kind;
25,648,178✔
520
   object->tag   = class->tag;
25,648,178✔
521
   object->arena = arena->key;
25,648,178✔
522
   object->loc   = LOC_INVALID;
25,648,178✔
523

524
   return object;
25,648,178✔
525
}
526

527
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,975,264✔
528
                              generation_t generation)
529
{
530
   if (object == NULL)
4,975,264✔
531
      return;
532
   else if (!object_in_arena_p(arena, object))
4,147,652✔
533
      return;
534
   else if (object_marked_p(object, generation))
2,513,400✔
535
      return;
536

537
   const object_class_t *class = classes[object->tag];
1,887,246✔
538

539
   imask_t has = class->has_map[object->kind];
1,887,246✔
540
   for (int n = 0; has; has &= has - 1, n++) {
10,086,776✔
541
      const uint64_t mask = has & -has;
8,199,530✔
542
      item_t *item = &(object->items[n]);
8,199,530✔
543
      if (ITEM_OBJECT & mask)
8,199,530✔
544
         gc_mark_from_root(item->object, arena, generation);
3,510,307✔
545
      else if (ITEM_OBJ_ARRAY & mask) {
4,689,223✔
546
         if (item->obj_array != NULL) {
833,058✔
547
            for (unsigned j = 0; j < item->obj_array->count; j++)
1,972,197✔
548
               gc_mark_from_root(item->obj_array->items[j], arena,
1,442,893✔
549
                                 generation);
550
         }
551
      }
552
   }
553
}
554

555
static void gc_free_external(object_t *object)
321,153✔
556
{
557
   const object_class_t *class = classes[object->tag];
321,153✔
558

559
   imask_t has = class->has_map[object->kind];
321,153✔
560
   if ((has & ITEM_OBJ_ARRAY) == 0)
321,153✔
561
      return;
562

563
   for (int n = 0; has; has &= has - 1, n++) {
370,901✔
564
      const uint64_t mask = has & -has;
308,999✔
565
      item_t *item = &(object->items[n]);
308,999✔
566
      if (ITEM_OBJ_ARRAY & mask)
308,999✔
567
         obj_array_free(&(item->obj_array));
99,455✔
568
   }
569
}
570

571
static void object_arena_gc(object_arena_t *arena)
17,027✔
572
{
573
   const generation_t generation = object_next_generation();
17,027✔
574
   const uint64_t start_ticks = get_timestamp_us();
17,027✔
575

576
   // Mark
577
   for (void *p = arena->base; p != arena->alloc; ) {
2,225,420✔
578
      assert(p < arena->alloc);
2,208,393✔
579
      object_t *object = p;
2,208,393✔
580

581
      const object_class_t *class = classes[object->tag];
2,208,393✔
582

583
      if (is_gc_root(class, object->kind))
2,208,393✔
584
         gc_mark_from_root(object, arena, generation);
22,064✔
585

586
      const size_t size =
2,208,393✔
587
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,208,393✔
588
      p = (char *)p + size;
2,208,393✔
589
   }
590

591
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
17,027✔
592
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
17,027✔
593

594
   // Must initialise here for the search in object_from_locus
595
   memset(forward, 0xff, fwdsz * sizeof(uint32_t));
17,027✔
596

597
   // Calculate forwarding addresses
598
   unsigned woffset = 0, live = 0, dead = 0;
17,027✔
599
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,225,420✔
600
      assert(rptr < arena->alloc);
2,208,393✔
601
      object_t *object = rptr;
2,208,393✔
602

603
      const object_class_t *class = classes[object->tag];
2,208,393✔
604

605
      const size_t size =
2,208,393✔
606
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,208,393✔
607

608
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,208,393✔
609
      if (!object_marked_p(object, generation)) {
2,208,393✔
610
         forward[index] = UINT32_MAX;
321,147✔
611
         gc_free_external(object);
321,147✔
612
         dead++;
321,147✔
613
      }
614
      else {
615
         forward[index] = woffset;
1,887,246✔
616
         woffset += size;
1,887,246✔
617
         live++;
1,887,246✔
618
      }
619

620
      rptr = (char *)rptr + size;
2,208,393✔
621
   }
622

623
   if (woffset == 0)
17,027✔
624
      fatal_trace("GC removed all objects from arena %s",
625
                  istr(object_arena_name(arena)));
626

627
   arena->forward = forward;
17,027✔
628
   arena->live_bytes = woffset;
17,027✔
629

630
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
17,027✔
631
      const int ticks = get_timestamp_us() - start_ticks;
×
632
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
633
             istr(object_arena_name(arena)), dead, live, ticks);
634
   }
635
}
17,027✔
636

637
void object_visit(object_t *object, object_visit_ctx_t *ctx)
705,883✔
638
{
639
   // If `deep' then will follow links above the tree originally passed
640
   // to tree_visit - e.g. following references back to their declarations
641

642
   if (object == NULL)
705,883✔
643
      return;
644
   else if (object_marked_p(object, ctx->generation))
531,440✔
645
      return;
646

647
   const object_class_t *class = classes[object->tag];
504,878✔
648

649
   const bool visit =
1,009,756✔
650
      (object->tag == ctx->tag && object->kind == ctx->kind)
504,878✔
651
      || ctx->kind == class->last_kind;
1,007,542✔
652

653
   if (visit && ctx->preorder != NULL)
504,878✔
654
      (*ctx->preorder)(object, ctx->context);
×
655

656
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
504,878✔
657

658
   imask_t has = class->has_map[object->kind];
504,878✔
659
   for (int n = 0; has; has &= has - 1, n++) {
2,655,476✔
660
      const uint64_t mask = has & -has;
2,150,598✔
661
      if (mask & deep_mask) {
2,150,598✔
662
         item_t *item = &(object->items[n]);
1,524,984✔
663
         if (ITEM_OBJECT & mask)
1,524,984✔
664
            object_visit(item->object, ctx);
353,601✔
665
         else if (ITEM_OBJ_ARRAY & mask) {
1,171,383✔
666
            if (item->obj_array != NULL) {
155,554✔
667
               for (unsigned j = 0; j < item->obj_array->count; j++)
437,755✔
668
                  object_visit(item->obj_array->items[j], ctx);
342,890✔
669
            }
670
         }
671
      }
672
   }
673

674
   if (visit) {
504,878✔
675
      if (ctx->postorder != NULL)
490,953✔
676
         (*ctx->postorder)(object, ctx->context);
490,953✔
677
      ctx->count++;
490,953✔
678
   }
679
}
680

681
static object_t *object_rewrite_iter(object_t *object,
3,630,945✔
682
                                     object_rewrite_ctx_t *ctx)
683
{
684
   // The callback may return a new object or a pointer to an existing
685
   // object in the same arena that that needs to be rewritten so
686
   // iterate rewriting until we reach a fixed point
687
   object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,630,945✔
688
   if (new == object)
3,630,941✔
689
      return new;
690
   else
691
      return object_rewrite(new, ctx);
57,238✔
692
}
693

694
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
8,331,569✔
695
{
696
   if (object == NULL)
8,331,569✔
697
      return NULL;
698

699
   if (!object_in_arena_p(ctx->arena, object))
6,950,028✔
700
      return object;
701

702
   const ptrdiff_t index =
4,638,341✔
703
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,638,341✔
704

705
   // New objects can be allocated while rewrite is in progress so we
706
   // need to check if the index is greater than the current cache size
707
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
4,638,341✔
708
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
70,097✔
709
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
70,097✔
710
                                  ctx->cache_sz);
711
   }
712

713
   if (object_marked_p(object, ctx->generation)) {
4,638,341✔
714
      if (ctx->cache[index] == (object_t *)-1) {
779,988✔
715
         // Found a circular reference: eagerly rewrite the object now
716
         // and break the cycle
717
         if (ctx->post_fn[object->tag] != NULL) {
1,155✔
718
            if (ctx->pre_fn[object->tag] != NULL)
50✔
719
               (*ctx->pre_fn[object->tag])(object, ctx->context);
×
720
            object_t *new = object_rewrite_iter(object, ctx);
50✔
721
            object_write_barrier(object, new);
50✔
722
            return (ctx->cache[index] = new);
50✔
723
         }
724
         else
725
            return (ctx->cache[index] = object);
1,105✔
726
      }
727
      else {
728
         // Already rewritten this tree so return the cached version
729
         return ctx->cache[index];
730
      }
731
   }
732

733
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,858,353✔
734

735
   if (ctx->pre_fn[object->tag] != NULL)
3,858,353✔
736
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
737

738
   const imask_t skip_mask =
3,858,353✔
739
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
740

741
   const object_class_t *class = classes[object->tag];
3,858,353✔
742

743
   imask_t has = class->has_map[object->kind];
3,858,353✔
744
   for (int n = 0; has; has &= has - 1, n++) {
20,636,694✔
745
      const uint64_t mask = has & -has;
16,778,349✔
746
      if (mask & ~skip_mask) {
16,778,349✔
747
         if (ITEM_OBJECT & mask) {
7,043,648✔
748
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,521,598✔
749
            object->items[n].object = o;
5,521,594✔
750
            object_write_barrier(object, o);
5,521,594✔
751
         }
752
         else if (ITEM_OBJ_ARRAY & mask) {
1,522,050✔
753
            obj_array_t **a = &(object->items[n].obj_array);
1,522,050✔
754
            if (object->items[n].obj_array != NULL) {
1,522,050✔
755
               // The callback may add new items to the array so the
756
               // array pointer cannot be cached between iterations
757
               unsigned wptr = 0;
758
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,713,633✔
759
                  object_t *o = object->items[n].obj_array->items[i];
2,721,571✔
760
                  if ((o = object_rewrite(o, ctx))) {
2,721,571✔
761
                     object_write_barrier(object, o);
2,717,115✔
762
                     object->items[n].obj_array->items[wptr++] = o;
2,717,115✔
763
                  }
764
               }
765

766
               if (wptr == 0)
992,062✔
767
                  obj_array_free(a);
847✔
768
               else
769
                  (*a)->count = wptr;
991,215✔
770
            }
771
         }
772
         else
773
            should_not_reach_here();
774
      }
775
   }
776

777
   if (ctx->cache[index] != (object_t *)-1) {
3,858,345✔
778
      // The cache was already updated due to a circular reference
779
      return ctx->cache[index];
780
   }
781
   else if (ctx->post_fn[object->tag] != NULL) {
3,857,190✔
782
      object_t *new = object_rewrite_iter(object, ctx);
3,630,895✔
783
      object_write_barrier(object, new);
3,630,891✔
784
      return (ctx->cache[index] = new);
3,630,891✔
785
   }
786
   else
787
      return (ctx->cache[index] = object);
226,295✔
788
}
789

790
static void object_write_ref(object_t *object, fbuf_t *f)
4,472,510✔
791
{
792
   if (object == NULL)
4,472,510✔
793
      fbuf_put_uint(f, 0);
765,513✔
794
   else {
795
      object_arena_t *arena = __object_arena(object);
3,706,997✔
796
      assert(arena->key != 0);
3,706,997✔
797
      fbuf_put_uint(f, arena->key);
3,706,997✔
798

799
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,706,997✔
800
      if (arena->forward != NULL)
3,706,997✔
801
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,338,010✔
802
      else
803
         fbuf_put_uint(f, index);
1,368,987✔
804
   }
805
}
4,472,510✔
806

807
void object_write(object_t *root, fbuf_t *f, ident_wr_ctx_t ident_ctx,
11,544✔
808
                  loc_wr_ctx_t *loc_ctx)
809
{
810
   object_arena_t *arena = __object_arena(root);
11,544✔
811
   if (root != arena_root(arena))
11,544✔
812
      fatal_trace("must write root object first");
813
   else if (arena->source == OBJ_DISK)
11,544✔
814
      fatal_trace("writing arena %s originally read from disk",
815
                  istr(object_arena_name(arena)));
816
   else if (!arena->frozen)
11,544✔
817
      fatal_trace("arena %s must be frozen before writing to disk",
818
                  istr(object_arena_name(arena)));
819
   else if (arena->obsolete)
11,544✔
820
      fatal_trace("writing obsolete arena %s", istr(object_arena_name(arena)));
821

822
   write_u32(format_digest, f);
11,544✔
823
   fbuf_put_uint(f, standard());
11,544✔
824
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
11,544✔
825
   fbuf_put_uint(f, arena->flags);
11,544✔
826
   fbuf_put_uint(f, arena->key);
11,544✔
827
   ident_write(object_arena_name(arena), ident_ctx);
11,544✔
828

829
   arena_key_t max_key = arena->key;
11,544✔
830
   for (unsigned i = 0; i < arena->deps.count; i++)
34,435✔
831
      max_key = MAX(max_key, arena->deps.items[i]->key);
22,891✔
832
   fbuf_put_uint(f, max_key);
11,544✔
833

834
   fbuf_put_uint(f, arena->deps.count);
11,544✔
835
   for (unsigned i = 0; i < arena->deps.count; i++) {
34,435✔
836
      fbuf_put_uint(f, arena->deps.items[i]->key);
22,891✔
837
      fbuf_put_uint(f, arena->deps.items[i]->std);
22,891✔
838
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
22,891✔
839
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
22,891✔
840
   }
841

842
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
2,010,178✔
843
      assert(p < arena->alloc);
1,998,634✔
844

845
      object_t *object = p;
1,998,634✔
846
      object_class_t *class = classes[object->tag];
1,998,634✔
847

848
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,998,634✔
849

850
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,998,634✔
851
      if (arena->forward[index] == UINT32_MAX)
1,998,634✔
852
         continue;   // Dead object
272,005✔
853

854
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,726,629✔
855
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,726,629✔
856

857
      if (class->has_loc)
1,726,629✔
858
         loc_write(&object->loc, loc_ctx);
1,620,998✔
859

860
      imask_t has = class->has_map[object->kind];
1,726,629✔
861
      for (int n = 0; has; has &= has - 1, n++) {
9,181,229✔
862
         const uint64_t mask = has & -has;
7,454,600✔
863
         item_t *item = &(object->items[n]);
7,454,600✔
864
         if (ITEM_IDENT & mask)
7,454,600✔
865
            ident_write(item->ident, ident_ctx);
1,389,512✔
866
         else if (ITEM_OBJECT & mask)
6,065,088✔
867
            object_write_ref(item->object, f);
3,269,463✔
868
         else if (ITEM_OBJ_ARRAY & mask) {
2,795,625✔
869
            if (item->obj_array != NULL) {
662,865✔
870
               const unsigned count = item->obj_array->count;
430,794✔
871
               fbuf_put_uint(f, count);
430,794✔
872
               for (unsigned i = 0; i < count; i++)
1,633,841✔
873
                  object_write_ref(item->obj_array->items[i], f);
1,203,047✔
874
            }
875
            else
876
               fbuf_put_uint(f, 0);
232,071✔
877
         }
878
         else if (ITEM_INT64 & mask)
2,132,760✔
879
            fbuf_put_int(f, item->ival);
377,639✔
880
         else if (ITEM_INT32 & mask)
1,755,121✔
881
            fbuf_put_int(f, item->ival);
1,637,701✔
882
         else if (ITEM_DOUBLE & mask)
117,420✔
883
            write_double(item->dval, f);
111,033✔
884
         else if (ITEM_NUMBER & mask)
6,387✔
885
            number_write(item->number, f);
6,387✔
886
         else
887
            should_not_reach_here();
888
      }
889
   }
890

891
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
11,544✔
892
}
11,544✔
893

894
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
60,348,759✔
895
{
896
   arena_key_t key = fbuf_get_uint(f);
60,348,759✔
897
   if (key == 0)
60,348,759✔
898
      return NULL;
899

900
   arena_key_t mapped = key_map[key];
51,336,918✔
901
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
51,336,918✔
902

903
   if (unlikely(mapped == 0))
51,336,918✔
904
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
905

906
   assert(mapped < all_arenas.count);
51,336,918✔
907
   assert(mapped > 0);
51,336,918✔
908

909
   object_arena_t *arena = all_arenas.items[mapped];
51,336,918✔
910
   assert(!arena->frozen || offset < arena->alloc - arena->base);
51,336,918✔
911

912
   return (object_t *)((char *)arena->base + offset);
51,336,918✔
913
}
914

915
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
19,006✔
916
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
917
{
918
   object_one_time_init();
19,006✔
919

920
   const uint32_t ver = read_u32(f);
19,006✔
921
   if (ver != format_digest)
19,006✔
922
      fatal("%s: serialised format digest is %x expected %x. This design "
×
923
            "unit uses a library format from an earlier version of "
924
            PACKAGE_NAME " and should be reanalysed.",
925
            fbuf_file_name(f), ver, format_digest);
926

927
   const vhdl_standard_t std = fbuf_get_uint(f);
19,006✔
928

929
   // If this is the first design unit we've loaded then allow it to set
930
   // the default standard
931
   if (all_arenas.count == 0)
19,006✔
932
      set_default_standard(std);
481✔
933

934
   if (std > standard())
19,006✔
935
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
936
            "is more recent that the currently selected standard %s",
937
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
938

939
   const unsigned size = fbuf_get_uint(f);
19,000✔
940
   if (size & OBJECT_PAGE_MASK)
19,000✔
941
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
942

943
   object_arena_t *arena = object_arena_new(size, std);
19,000✔
944
   arena->source = OBJ_DISK;
19,000✔
945
   arena->flags  = fbuf_get_uint(f);
19,000✔
946

947
   arena_key_t key = fbuf_get_uint(f);
19,000✔
948
   ident_t name = ident_read(ident_ctx);
19,000✔
949

950
   arena_key_t max_key = fbuf_get_uint(f);
19,000✔
951

952
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
19,000✔
953
   key_map[key] = arena->key;
19,000✔
954

955
   const int ndeps = fbuf_get_uint(f);
19,000✔
956
   for (int i = 0; i < ndeps; i++) {
42,266✔
957
      arena_key_t dkey = fbuf_get_uint(f);
23,269✔
958
      vhdl_standard_t dstd = fbuf_get_uint(f);
23,269✔
959
      uint32_t checksum = fbuf_get_uint(f);
23,269✔
960
      ident_t dep = ident_read(ident_ctx);
23,269✔
961

962
      object_arena_t *a = NULL;
23,269✔
963
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
110,050✔
964
         if (dep == object_arena_name(all_arenas.items[j]))
86,781✔
965
            a = all_arenas.items[j];
19,199✔
966
      }
967

968
      if (a == NULL) {
23,269✔
969
         object_t *droot = NULL;
4,070✔
970
         if (loader_fn) droot = (*loader_fn)(dep);
4,070✔
971

972
         if (droot == NULL)
4,070✔
973
            fatal("%s depends on %s which cannot be found",
×
974
                  fbuf_file_name(f), istr(dep));
975

976
         a = __object_arena(droot);
4,070✔
977
      }
978

979
      if (a->std != dstd)
23,269✔
980
         fatal("%s: design unit depends on %s version of %s but conflicting "
×
981
               "%s version has been loaded", fbuf_file_name(f),
982
               standard_text(dstd), istr(dep), standard_text(a->std));
983
      else if (a->checksum != checksum) {
23,269✔
984
         diag_t *d = diag_new(DIAG_FATAL, NULL);
3✔
985
         diag_suppress(d, false);
3✔
986
         diag_printf(d, "%s: design unit depends on %s with checksum %08x "
3✔
987
                     "but the current version in the library has checksum %08x",
988
                     fbuf_file_name(f), istr(dep), checksum, a->checksum);
989
         diag_hint(d, NULL, "this usually means %s is outdated and needs to "
3✔
990
                   "be reanalysed", istr(name));
991
         diag_emit(d);
3✔
992
         fatal_exit(EXIT_FAILURE);
3✔
993
      }
994

995
      APUSH(arena->deps, a);
23,266✔
996

997
      assert(dkey <= max_key);
23,266✔
998
      key_map[dkey] = a->key;
23,266✔
999
   }
1000

1001
   for (;;) {
23,389,874✔
1002
      const uint64_t hdr = fbuf_get_uint(f);
23,389,874✔
1003
      if (hdr == UINT16_MAX) break;
23,389,874✔
1004

1005
      const unsigned tag = hdr & 3;
23,370,877✔
1006
      const unsigned kind = hdr >> 2;
23,370,877✔
1007

1008
      assert(tag < OBJECT_TAG_COUNT);
23,370,877✔
1009

1010
      const object_class_t *class = classes[tag];
23,370,877✔
1011

1012
      object_t *object = object_new(arena, class, kind);
23,370,877✔
1013

1014
      if (class->has_loc)
23,370,877✔
1015
         loc_read(&(object->loc), loc_ctx);
21,103,628✔
1016

1017
      imask_t has = class->has_map[object->kind];
23,370,877✔
1018
      for (int n = 0; has; has &= has - 1, n++) {
127,818,404✔
1019
         const uint64_t mask = has & -has;
104,447,527✔
1020
         item_t *item = &(object->items[n]);
104,447,527✔
1021
         if (ITEM_IDENT & mask)
104,447,527✔
1022
            item->ident = ident_read(ident_ctx);
21,186,202✔
1023
         else if (ITEM_OBJECT & mask)
83,261,325✔
1024
            item->object = object_read_ref(f, key_map);
41,983,877✔
1025
         else if (ITEM_OBJ_ARRAY & mask) {
41,277,448✔
1026
            const unsigned count = fbuf_get_uint(f);
9,734,475✔
1027
            if (count > 0) {
9,734,475✔
1028
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
6,504,230✔
1029
                                              count, sizeof(object_t *));
1030
               item->obj_array->count =
6,504,230✔
1031
                  item->obj_array->limit = count;
6,504,230✔
1032
               for (unsigned i = 0; i < count; i++) {
24,869,112✔
1033
                  object_t *o = object_read_ref(f, key_map);
18,364,882✔
1034
                  item->obj_array->items[i] = o;
18,364,882✔
1035
               }
1036
            }
1037
         }
1038
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
31,542,973✔
1039
            item->ival = fbuf_get_int(f);
30,737,892✔
1040
         else if (ITEM_DOUBLE & mask)
805,081✔
1041
            item->dval = read_double(f);
805,081✔
1042
         else if (ITEM_NUMBER & mask)
×
1043
            item->number = number_read(f);
×
1044
         else
1045
            should_not_reach_here();
1046
      }
1047
   }
1048

1049
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
18,997✔
1050

1051
   object_arena_freeze(arena);
18,997✔
1052
   return (object_t *)arena->base;
18,997✔
1053
}
1054

1055
unsigned object_next_generation(void)
63,901✔
1056
{
1057
   return next_generation++;
63,901✔
1058
}
1059

1060
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,163,190✔
1061
{
1062
   if (object == NULL)
3,163,190✔
1063
      return false;
1064

1065
   object_arena_t *arena = __object_arena(object);
2,592,926✔
1066
   if (arena->copygen != ctx->generation)
2,592,926✔
1067
      return false;
1068

1069
   bool marked = false;
1,595,231✔
1070
   if (object_marked_p(object, ctx->generation)) {
1,595,231✔
1071
      object_t *map = hash_get(ctx->copy_map, object);
382,083✔
1072
      if (map == object)
382,083✔
1073
         marked = true;   // Marked as root
1074
      else
1075
         return map != NULL;
368,270✔
1076
   }
1077

1078
   if (!marked && ctx->should_copy[object->tag] != NULL)
1,213,148✔
1079
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,144,379✔
1080

1081
   const object_class_t *class = classes[object->tag];
1,226,961✔
1082

1083
   object_t *copy = NULL;
1,226,961✔
1084
   if (marked) {
1,226,961✔
1085
      copy = object_new(global_arena, class, object->kind);
33,458✔
1086
      hash_put(ctx->copy_map, object, copy);
33,458✔
1087
   }
1088

1089
   imask_t has = class->has_map[object->kind];
1,226,961✔
1090
   for (int n = 0; has; has &= has - 1, n++) {
6,484,164✔
1091
      const uint64_t mask = has & -has;
5,257,203✔
1092
      item_t *item = &(object->items[n]);
5,257,203✔
1093
      if (ITEM_OBJECT & mask)
5,257,203✔
1094
         marked |= object_copy_mark(item->object, ctx);
2,325,497✔
1095
      else if (ITEM_OBJ_ARRAY & mask) {
2,931,706✔
1096
         if (item->obj_array != NULL) {
441,790✔
1097
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,107,197✔
1098
               object_t *o = item->obj_array->items[i];
825,495✔
1099
               marked |= object_copy_mark(o, ctx);
825,495✔
1100
            }
1101
         }
1102
      }
1103
   }
1104

1105
   if (marked && copy == NULL) {
1,226,961✔
1106
      copy = object_new(global_arena, class, object->kind);
229,959✔
1107
      hash_put(ctx->copy_map, object, copy);
229,959✔
1108
   }
1109

1110
   return marked;
1111
}
1112

1113
void object_copy_mark_root(object_t *object, object_copy_ctx_t *ctx)
14,136✔
1114
{
1115
   object_arena_t *arena = __object_arena(object);
14,136✔
1116

1117
   arena->copygen = ctx->generation;
14,136✔
1118

1119
   if (!object_marked_p(object, ctx->generation))
14,136✔
1120
      hash_put(ctx->copy_map, object, object);
13,813✔
1121
}
14,136✔
1122

1123
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
771,730✔
1124
{
1125
   if (object == NULL)
771,730✔
1126
      return NULL;
1127

1128
   object_t *map = hash_get(ctx->copy_map, object);
656,572✔
1129
   return map ?: object;
656,572✔
1130
}
1131

1132
void object_copy_begin(object_copy_ctx_t *ctx)
6,320✔
1133
{
1134
   ctx->copy_map = hash_new(1024);
6,320✔
1135

1136
   for (int i = 0; i < ctx->nroots; i++)
18,518✔
1137
      __object_arena(ctx->roots[i])->copygen = ctx->generation;
12,198✔
1138
}
6,320✔
1139

1140
void object_copy_finish(object_copy_ctx_t *ctx)
6,320✔
1141
{
1142
   for (int i = 0; i < ctx->nroots; i++)
18,518✔
1143
      (void)object_copy_mark(ctx->roots[i], ctx);
12,198✔
1144

1145
   unsigned ncopied = 0;
6,320✔
1146
   const void *key;
6,320✔
1147
   void *value;
6,320✔
1148
   for (hash_iter_t it = HASH_BEGIN;
6,320✔
1149
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
269,737✔
1150
      const object_t *object = key;
263,417✔
1151
      object_t *copy = value;
263,417✔
1152
      assert(copy != object);
263,417✔
1153
      ncopied++;
263,417✔
1154

1155
      copy->loc = object->loc;
263,417✔
1156

1157
      const object_class_t *class = classes[object->tag];
263,417✔
1158

1159
      imask_t has = class->has_map[object->kind];
263,417✔
1160
      for (int n = 0; has; has &= has - 1, n++) {
1,464,603✔
1161
         const uint64_t mask = has & -has;
1,201,186✔
1162
         const item_t *from = &(object->items[n]);
1,201,186✔
1163
         item_t *to = &(copy->items[n]);
1,201,186✔
1164

1165
         if (ITEM_IDENT & mask)
1,201,186✔
1166
            to->ident = from->ident;
218,971✔
1167
         else if (ITEM_OBJECT & mask) {
982,215✔
1168
            to->object = object_copy_map(from->object, ctx);
499,275✔
1169
            object_write_barrier(copy, to->object);
499,275✔
1170
         }
1171
         else if (ITEM_DOUBLE & mask)
482,940✔
1172
            to->dval = from->dval;
124✔
1173
         else if (ITEM_OBJ_ARRAY & mask) {
482,816✔
1174
            if (from->obj_array != NULL) {
173,609✔
1175
               // TODO: make a resize macro
1176
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
232,762✔
1177
                                            from->obj_array->count,
116,381✔
1178
                                            sizeof(object_t *));
1179
               to->obj_array->count =
116,381✔
1180
                  to->obj_array->limit = from->obj_array->count;
116,381✔
1181
               for (size_t i = 0; i < from->obj_array->count; i++) {
388,836✔
1182
                  object_t *o =
272,455✔
1183
                     object_copy_map(from->obj_array->items[i], ctx);
272,455✔
1184
                  to->obj_array->items[i] = o;
272,455✔
1185
                  object_write_barrier(copy, o);
272,455✔
1186
               }
1187
            }
1188
         }
1189
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
309,207✔
1190
            to->ival = from->ival;
309,207✔
1191
         else
1192
            should_not_reach_here();
1193
      }
1194
   }
1195

1196
   for (hash_iter_t it = HASH_BEGIN;
6,320✔
1197
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
269,737✔
1198
      object_t *copy = value;
263,417✔
1199
      if (ctx->callback[copy->tag] != NULL)
263,417✔
1200
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
260,228✔
1201
   }
1202

1203
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
6,320✔
UNCOV
1204
      debugf("copied %d objects into arena %s", ncopied,
×
1205
             istr(object_arena_name(global_arena)));
1206

1207
   for (unsigned i = 0; i < ctx->nroots; i++) {
18,518✔
1208
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
12,198✔
1209
      if (copy != NULL)
12,198✔
1210
         ctx->roots[i] = copy;
5,251✔
1211
   }
1212

1213
   hash_free(ctx->copy_map);
6,320✔
1214
}
6,320✔
1215

1216
size_t object_arena_default_size(void)
17,667✔
1217
{
1218
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
17,667✔
1219
}
1220

1221
object_arena_t *object_arena_new(size_t size, unsigned std)
36,667✔
1222
{
1223
   if (all_arenas.count == 0)
36,667✔
1224
      APUSH(all_arenas, NULL);   // Dummy null arena
5,373✔
1225

1226
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
36,667✔
1227
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
36,667✔
1228
   arena->alloc  = arena->base;
36,667✔
1229
   arena->limit  = (char *)arena->base + size;
36,667✔
1230
   arena->key    = all_arenas.count;
36,667✔
1231
   arena->source = OBJ_FRESH;
36,667✔
1232
   arena->std    = std;
36,667✔
1233

1234
   APUSH(all_arenas, arena);
36,667✔
1235

1236
   if (all_arenas.count == UINT16_MAX - 1)
36,667✔
1237
      fatal_trace("too many object arenas");
1238

1239
   return arena;
36,667✔
1240
}
1241

1242
void object_arena_freeze(object_arena_t *arena)
36,024✔
1243
{
1244
   ident_t name = object_arena_name(arena);
36,024✔
1245

1246
   if (arena->frozen)
36,024✔
1247
      fatal_trace("arena %s already frozen", istr(name));
1248

1249
   if (arena->source == OBJ_FRESH)
36,024✔
1250
      object_arena_gc(arena);
17,027✔
1251

1252
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
36,024✔
UNCOV
1253
      debugf("arena %s frozen (%d bytes)", istr(name),
×
UNCOV
1254
             (int)(arena->alloc - arena->base));
×
1255

1256
   chash_put(arena_lookup, name, arena);
36,024✔
1257

1258
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
36,024✔
1259
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
36,024✔
1260

1261
   if (next_page < arena->limit) {
36,024✔
1262
#if OBJECT_UNMAP_UNUSED
1263
      nvc_munmap(next_page, arena->limit - next_page);
1264
      arena->limit = next_page;
1265
#else
1266
      // This can be useful for debugging use-after-free
1267
      nvc_decommit(next_page, arena->limit - next_page);
17,027✔
1268
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
17,027✔
1269
#endif
1270
   }
1271

1272
   arena->frozen = true;
36,024✔
1273
}
36,024✔
1274

1275
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
32,817✔
1276
{
1277
   for (unsigned i = 0; i < arena->deps.count; i++)
90,239✔
1278
      (*fn)(arena_root(arena->deps.items[i]), context);
57,422✔
1279
}
32,817✔
1280

1281
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
11,544✔
1282
                              void *context)
1283
{
1284
   for (unsigned i = 0; i < arena->deps.count; i++) {
34,435✔
1285
      if (arena->deps.items[i]->obsolete)
22,891✔
1286
         (*fn)(arena_root(arena->deps.items[i]), context);
6✔
1287
   }
1288
}
11,544✔
1289

1290
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
12,402✔
1291
{
1292
   object_arena_t *arena = __object_arena(object);
12,402✔
1293
   assert(arena->frozen);
12,402✔
1294

1295
   *module = object_arena_name(arena);
12,402✔
1296

1297
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
12,402✔
1298
   if (arena->forward != NULL)
12,402✔
1299
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
5,685✔
1300
   else
1301
      *offset = index;
6,717✔
1302
}
12,402✔
1303

1304
static object_arena_t *arena_by_name(ident_t module)
7,292✔
1305
{
1306
   if (global_arena != NULL && object_arena_name(global_arena) == module)
7,292✔
UNCOV
1307
      return global_arena;
×
1308

1309
   object_arena_t *a = chash_get(arena_lookup, module);
7,292✔
1310
   if (a != NULL)
7,292✔
1311
      return a;
1312

1313
#if defined DEBUG && !defined __SANITIZE_THREAD__
1314
   for (int i = 1; i < all_arenas.count; i++)
4,561✔
1315
      assert(module != object_arena_name(all_arenas.items[i]));
3,927✔
1316
#endif
1317

1318
   return NULL;
1319
}
1320

1321
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
7,292✔
1322
                            object_load_fn_t loader)
1323
{
1324
   object_arena_t *arena = arena_by_name(module);
7,292✔
1325

1326
   if (arena == NULL) {
7,292✔
1327
      object_t *droot = NULL;
634✔
1328
      if (loader) droot = (*loader)(module);
634✔
1329

1330
      if (droot == NULL)
634✔
UNCOV
1331
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1332

1333
      arena = __object_arena(droot);
634✔
1334
   }
1335

1336
   assert(arena->frozen);
7,292✔
1337

1338
   void *ptr = NULL;
7,292✔
1339
   if (arena->forward != NULL) {
7,292✔
1340
      // TODO: could do binary search here
UNCOV
1341
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
×
UNCOV
1342
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
×
1343
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
×
UNCOV
1344
            break;
×
1345
         }
1346
      }
UNCOV
1347
      assert(ptr != NULL);
×
1348
   }
1349
   else
1350
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
7,292✔
1351

1352
   if (ptr > arena->limit)
7,292✔
1353
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
1354

1355
   object_t *obj = ptr;
7,292✔
1356
   if (obj->tag >= OBJECT_TAG_COUNT)
7,292✔
1357
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1358
                  istr(module), offset);
1359
   else if (obj->arena != arena->key)
7,292✔
1360
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
UNCOV
1361
                  obj->arena, arena->key, istr(module), offset);
×
1362

1363
   return obj;
7,292✔
1364
}
1365

1366
void freeze_global_arena(void)
26,244✔
1367
{
1368
   object_one_time_init();
26,244✔
1369

1370
   if (global_arena != NULL) {
26,244✔
1371
      object_arena_freeze(global_arena);
17,027✔
1372
      global_arena = NULL;
17,027✔
1373
   }
1374
}
26,244✔
1375

1376
void make_new_arena(void)
17,667✔
1377
{
1378
   freeze_global_arena();
17,667✔
1379
   global_arena = object_arena_new(object_arena_default_size(), standard());
17,667✔
1380
}
17,667✔
1381

1382
void discard_global_arena(void)
1✔
1383
{
1384
   if (global_arena == NULL)
1✔
1385
      return;
1386

1387
   object_one_time_init();
1✔
1388

1389
   for (void *p = global_arena->base; p != global_arena->alloc; ) {
7✔
1390
      assert(p < global_arena->alloc);
6✔
1391
      object_t *object = p;
6✔
1392

1393
      gc_free_external(object);
6✔
1394

1395
      const object_class_t *class = classes[object->tag];
6✔
1396
      const size_t size =
6✔
1397
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
6✔
1398
      p = (char *)p + size;
6✔
1399
   }
1400

1401
   nvc_munmap(global_arena->base, global_arena->limit - global_arena->base);
1✔
1402

1403
   assert(all_arenas.items[all_arenas.count - 1] == global_arena);
1✔
1404
   APOP(all_arenas);
1✔
1405

1406
   free(global_arena);
1✔
1407
   global_arena = NULL;
1✔
1408
}
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