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

nickg / nvc / 19617482215

23 Nov 2025 09:08PM UTC coverage: 92.559% (-0.01%) from 92.572%
19617482215

push

github

nickg
Structure sharing for component instances

139 of 147 new or added lines in 1 file covered. (94.56%)

327 existing lines in 10 files now uncovered.

75133 of 81173 relevant lines covered (92.56%)

453457.88 hits per line

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

91.7
/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)
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_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)
10,724,528✔
107
{
108
   return (void *)object >= arena->base && (void *)object < arena->limit;
10,724,528✔
109
}
110

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

118
static ident_t object_arena_name(object_arena_t *arena)
275,142✔
119
{
120
   if (arena->alloc > arena->base) {
275,142✔
121
      object_t *root = arena_root(arena);
266,832✔
122

123
      const object_class_t *class = classes[root->tag];
266,832✔
124
      const imask_t has = class->has_map[root->kind];
266,832✔
125

126
      if (has & I_IDENT) {
266,832✔
127
         const int n = __builtin_popcountll(has & (I_IDENT - 1));
264,505✔
128
         return root->items[n].ident;
264,505✔
129
      }
130
   }
131

132
   return ident_new("???");
10,637✔
133
}
134

135
static inline void zero_mark_bits(object_arena_t *arena, unsigned first,
177,300✔
136
                                  size_t count)
137
{
138
   assert(first + count <= arena->mark_sz);
177,300✔
139
   assert(first > arena->mark_high || first + count <= arena->mark_low);
177,300✔
140

141
   if (count == 1)
177,300✔
142
      arena->mark_bits[first] = 0;
153,450✔
143
   else
144
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
23,850✔
145
}
177,300✔
146

147
static bool object_marked_p(object_t *object, generation_t generation)
11,320,145✔
148
{
149
   object_arena_t *arena = __object_arena(object);
11,320,145✔
150

151
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
11,320,145✔
152
   const uintptr_t word = bit / 64;
11,320,145✔
153

154
   if (unlikely(arena->mark_bits == NULL)) {
11,320,145✔
155
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
17,106✔
156
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
17,106✔
157
      arena->mark_bits = xmalloc(arena->mark_sz);
17,106✔
158
      arena->mark_bits[word] = 0;
17,106✔
159
      arena->mark_low = arena->mark_high = word;
17,106✔
160
      arena->generation = generation;
17,106✔
161
   }
162
   else if (arena->generation != generation) {
11,303,039✔
163
      arena->mark_bits[word] = 0;
58,122✔
164
      arena->mark_low = arena->mark_high = word;
58,122✔
165
      arena->generation = generation;
58,122✔
166
   }
167

168
   // Lazy zeroing of mark bits helps performance with large arenas
169
   if (word < arena->mark_low) {
11,320,145✔
170
      zero_mark_bits(arena, word, arena->mark_low - word);
9,144✔
171
      arena->mark_low = word;
9,144✔
172
   }
173
   else if (word > arena->mark_high) {
11,311,001✔
174
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
168,156✔
175
      arena->mark_high = word;
168,156✔
176
   }
177

178
   const uint64_t mask = UINT64_C(1) << (bit & 63);
11,320,145✔
179

180
   const bool marked = !!(arena->mark_bits[word] & mask);
11,320,145✔
181
   arena->mark_bits[word] |= mask;
11,320,145✔
182

183
   return marked;
11,320,145✔
184
}
185

186
void arena_set_checksum(object_arena_t *arena, uint32_t checksum)
30,272✔
187
{
188
   arena->checksum = checksum;
30,272✔
189
}
30,272✔
190

191
object_t *arena_root(object_arena_t *arena)
333,573✔
192
{
193
   return arena->root ?: (object_t *)arena->base;
333,573✔
194
}
195

196
bool arena_frozen(object_arena_t *arena)
64,367✔
197
{
198
   return arena->frozen;
64,367✔
199
}
200

201
uint32_t arena_flags(object_arena_t *arena)
57,441✔
202
{
203
   return arena->flags;
57,441✔
204
}
205

206
void arena_set_flags(object_arena_t *arena, uint32_t flags)
8,088✔
207
{
208
   arena->flags |= flags;
8,088✔
209
}
8,088✔
210

211
void arena_set_obsolete(object_arena_t *arena, bool obsolete)
24✔
212
{
213
   arena->obsolete = true;
24✔
214
}
24✔
215

216
object_arena_t *object_arena(object_t *object)
269,497✔
217
{
218
   return __object_arena(object);
269,497✔
219
}
220

221
void __object_write_barrier(object_t *lhs, object_t *rhs)
4,878,711✔
222
{
223
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
4,878,711✔
224
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
4,878,711✔
225

226
   if (lhs_mask == rhs_mask || rhs == NULL)
4,878,711✔
227
      return;
228
   else if (lhs->arena == rhs->arena)
4,878,711✔
229
      return;
230

231
   object_arena_t *larena = __object_arena(lhs);
4,365,565✔
232
   object_arena_t *rarena = __object_arena(rhs);
4,365,565✔
233

234
   assert(!larena->frozen);
4,365,565✔
235
   assert(rarena->frozen);
4,365,565✔
236

237
   for (unsigned i = 0; i < larena->deps.count; i++) {
7,222,453✔
238
      if (larena->deps.items[i] == rarena)
7,185,926✔
239
         return;
240
   }
241

242
   APUSH(larena->deps, rarena);
36,527✔
243
}
244

UNCOV
245
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
246
{
UNCOV
247
   unsigned int item;
×
248
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
249
      ;
250

UNCOV
251
   assert(item < ARRAY_LEN(item_text_map));
×
252

UNCOV
253
   diag_t *d = diag_new(DIAG_FATAL, &(object->loc));
×
254
   diag_printf(d, "%s kind %s does not have item %s", class->name,
×
255
               class->kind_text_map[object->kind], item_text_map[item]);
×
256
   diag_set_consumer(NULL, NULL);
×
257
   diag_suppress(d, false);
×
258
   diag_emit(d);
×
259
   show_stacktrace();
×
260
   fatal_exit(EXIT_FAILURE);
×
261
}
262

263
void obj_array_add(obj_array_t **a, object_t *o)
1,283,851✔
264
{
265
   if (*a == NULL) {
1,283,851✔
266
      const int defsz = 8;
460,412✔
267
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
460,412✔
268
      (*a)->count = 0;
460,412✔
269
      (*a)->limit = defsz;
460,412✔
270
   }
271
   else if ((*a)->count == (*a)->limit) {
823,439✔
272
      (*a)->limit *= 2;
31,151✔
273
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
31,151✔
274
                         (*a)->limit, sizeof(object_t *));
275
   }
276

277
   (*a)->items[(*a)->count++] = o;
1,283,851✔
278
}
1,283,851✔
279

280
void obj_array_free(obj_array_t **a)
83,569✔
281
{
282
   free(*a);
83,569✔
283
   *a = NULL;
83,569✔
284
}
83,569✔
285

286
void object_change_kind(const object_class_t *class, object_t *object, int kind)
24,255✔
287
{
24,255✔
288
   if (kind == object->kind)
24,255✔
UNCOV
289
      return;
×
290

291
   bool allow = false;
292
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
250,624✔
293
      allow = (class->change_allowed[i][0] == object->kind)
226,369✔
294
         && (class->change_allowed[i][1] == kind);
226,369✔
295
   }
296

297
   if (!allow)
24,255✔
298
      fatal_trace("cannot change %s kind %s to %s", class->name,
UNCOV
299
                  class->kind_text_map[object->kind],
×
300
                  class->kind_text_map[kind]);
×
301

302
   const imask_t old_has = class->has_map[object->kind];
24,255✔
303
   const imask_t new_has = class->has_map[kind];
24,255✔
304

305
   const int old_nitems = __builtin_popcountll(old_has);
24,255✔
306
   const int new_nitems = __builtin_popcountll(new_has);
24,255✔
307

308
   const int max_items = MAX(old_nitems, new_nitems);
24,255✔
309

310
   item_t tmp[max_items];
24,255✔
311
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
24,255✔
312

313
   int op = 0, np = 0;
24,255✔
314
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
978,706✔
315
      if ((old_has & mask) && (new_has & mask))
954,451✔
316
         object->items[np++] = tmp[op++];
86,953✔
317
      else if (old_has & mask) {
867,498✔
318
         if (ITEM_OBJ_ARRAY & mask)
146✔
319
            obj_array_free(&(tmp[op].obj_array));
116✔
320
         ++op;
146✔
321
      }
322
      else if (new_has & mask)
867,352✔
323
         memset(&(object->items[np++]), '\0', sizeof(item_t));
954,451✔
324
   }
325

326
   object->kind = kind;
24,255✔
327
}
328

329
static void object_init(object_class_t *class)
21,036✔
330
{
331
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
21,036✔
332

333
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
21,036✔
334

335
   assert(class->tag < ARRAY_LEN(classes));
21,036✔
336
   classes[class->tag] = class;
21,036✔
337

338
#ifdef DEBUG
339
   imask_t all_items = 0;
21,036✔
340
#endif
341

342
   for (int i = 0; i < class->last_kind; i++) {
1,404,153✔
343
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,383,117✔
344
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,383,117✔
345
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,383,117✔
346

347
      format_digest += knuth_hash(class->has_map[i] >> 32);
1,383,117✔
348
      format_digest += knuth_hash(class->has_map[i]);
1,383,117✔
349
   }
350

351
   bool changed = false;
31,554✔
352
   do {
31,554✔
353
      changed = false;
31,554✔
354
      for (int i = 0; i < class->last_kind; i++) {
2,666,313✔
355
         size_t max_size = class->object_size[i];
2,634,759✔
356
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
32,674,167✔
357
            if (class->change_allowed[j][0] == i)
30,039,408✔
358
               max_size = MAX(max_size,
252,432✔
359
                              class->object_size[class->change_allowed[j][1]]);
360
            else if (class->change_allowed[j][1] == i)
29,786,976✔
361
               max_size = MAX(max_size,
252,432✔
362
                              class->object_size[class->change_allowed[j][0]]);
363
         }
364

365
         if (max_size != class->object_size[i]) {
2,634,759✔
366
            class->object_size[i] = max_size;
63,108✔
367
            changed = true;
63,108✔
368
         }
369
      }
370
   } while (changed);
31,554✔
371

372
#ifdef DEBUG
373
   if (getenv("NVC_TREE_SIZES") != NULL) {
21,036✔
UNCOV
374
      for (int i = 0; i < class->last_kind; i++)
×
375
         printf("%-15s %d\n", class->kind_text_map[i],
×
376
                (int)class->object_size[i]);
×
377
   }
378

379
   const imask_t known_types =
21,036✔
380
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
381
      | ITEM_DOUBLE | ITEM_NUMBER;
382

383
   const imask_t missing = all_items & ~known_types;
21,036✔
384
   if (missing != 0) {
21,036✔
385
      int item;
UNCOV
386
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
387
         ;
388

UNCOV
389
      assert(item < ARRAY_LEN(item_text_map));
×
390
      fatal_trace("item %s does not have a type", item_text_map[item]);
391
   }
392
#endif
393
}
21,036✔
394

UNCOV
395
static void check_frozen_object_fault(int sig, void *addr,
×
396
                                      struct cpu_state *cpu, void *context)
397
{
398
#ifndef __MINGW32__
UNCOV
399
   if (sig != SIGSEGV && sig != SIGBUS)
×
400
      return;
401
#endif
402

UNCOV
403
   for (unsigned i = 1; i < all_arenas.count; i++) {
×
404
      object_arena_t *arena = AGET(all_arenas, i);
×
405
      if (!arena->frozen)
×
406
         continue;
×
407
      else if (addr < arena->base)
×
408
         continue;
×
409
      else if (addr >= arena->limit)
×
410
         continue;
×
411

412
      fatal_trace("Write to object in frozen arena %s [address=%p]",
413
                  istr(object_arena_name(arena)), addr);
414
   }
415
}
416

417
static void object_one_time_init(void)
25,345,053✔
418
{
419
   INIT_ONCE({
25,345,053✔
420
         extern object_class_t tree_object;
421
         object_init(&tree_object);
422

423
         extern object_class_t type_object;
424
         object_init(&type_object);
425

426
         extern object_class_t vlog_object;
427
         object_init(&vlog_object);
428

429
         extern object_class_t psl_object;
430
         object_init(&psl_object);
431

432
         // Increment this each time a incompatible change is made to
433
         // the on-disk format not expressed in the object items table
434
         const uint32_t format_fudge = 45;
435

436
         format_digest += format_fudge * UINT32_C(2654435761);
437

438
         add_fault_handler(check_frozen_object_fault, NULL);
439

440
         arena_lookup = chash_new(64);
441
      });
442
}
25,345,053✔
443

444
static bool is_gc_root(const object_class_t *class, int kind)
2,188,829✔
445
{
446
   for (int j = 0; j < class->gc_num_roots; j++) {
20,314,068✔
447
      if (class->gc_roots[j] == kind)
18,184,199✔
448
         return true;
449
   }
450

451
   return false;
452
}
453

454
object_t *object_new(object_arena_t *arena,
25,300,404✔
455
                     const object_class_t *class, int kind)
456
{
457
   if (unlikely(kind >= class->last_kind))
25,300,404✔
458
      fatal_trace("invalid kind %d for %s object", kind, class->name);
459

460
   object_one_time_init();
25,300,404✔
461

462
   if (arena == NULL)
25,300,404✔
463
      arena = global_arena;
1,916,947✔
464

465
   if (unlikely(arena == NULL))
25,300,404✔
466
      fatal_trace("allocating object without active arena");
467

468
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
25,300,404✔
469

470
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
25,300,404✔
471

472
   if (unlikely(arena->limit - arena->alloc < size)) {
25,300,404✔
UNCOV
473
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
474
      diag_suppress(d, false);
×
475
      diag_printf(d, "memory exhausted while creating unit %s",
×
476
                  istr(object_arena_name(arena)));
UNCOV
477
      diag_hint(d, NULL, "The current limit is %zu bytes which you can "
×
478
                "increase with the $bold$-M$$ option, for example "
479
                "$bold$-M 32m$$", object_arena_default_size());
UNCOV
480
      diag_emit(d);
×
481
      fatal_exit(EXIT_FAILURE);
×
482
   }
483

484
   object_t *object = arena->alloc;
25,300,404✔
485
   arena->alloc = (char *)arena->alloc + size;
25,300,404✔
486

487
   if (arena->root == NULL && is_gc_root(class, kind))
25,300,404✔
488
      arena->root = object;
36,254✔
489

490
   memset(object, '\0', size);
25,300,404✔
491

492
   object->kind  = kind;
25,300,404✔
493
   object->tag   = class->tag;
25,300,404✔
494
   object->arena = arena->key;
25,300,404✔
495
   object->loc   = LOC_INVALID;
25,300,404✔
496

497
   return object;
25,300,404✔
498
}
499

500
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,810,032✔
501
                              generation_t generation)
502
{
503
   if (object == NULL)
4,810,032✔
504
      return;
505
   else if (!object_in_arena_p(arena, object))
3,999,170✔
506
      return;
507
   else if (object_marked_p(object, generation))
2,414,246✔
508
      return;
509

510
   const object_class_t *class = classes[object->tag];
1,817,603✔
511

512
   imask_t has = class->has_map[object->kind];
1,817,603✔
513
   for (int n = 0; has; has &= has - 1, n++) {
9,749,144✔
514
      const uint64_t mask = has & -has;
7,931,541✔
515
      item_t *item = &(object->items[n]);
7,931,541✔
516
      if (ITEM_OBJECT & mask)
7,931,541✔
517
         gc_mark_from_root(item->object, arena, generation);
3,391,127✔
518
      else if (ITEM_OBJ_ARRAY & mask) {
4,540,414✔
519
         if (item->obj_array != NULL) {
813,299✔
520
            for (unsigned j = 0; j < item->obj_array->count; j++)
1,911,858✔
521
               gc_mark_from_root(item->obj_array->items[j], arena,
1,396,199✔
522
                                 generation);
523
         }
524
      }
525
   }
526
}
527

528
static void gc_free_external(object_t *object)
334,957✔
529
{
530
   const object_class_t *class = classes[object->tag];
334,957✔
531

532
   imask_t has = class->has_map[object->kind];
334,957✔
533
   if ((has & ITEM_OBJ_ARRAY) == 0)
334,957✔
534
      return;
535

536
   for (int n = 0; has; has &= has - 1, n++) {
381,383✔
537
      const uint64_t mask = has & -has;
314,171✔
538
      item_t *item = &(object->items[n]);
314,171✔
539
      if (ITEM_OBJ_ARRAY & mask)
314,171✔
540
         obj_array_free(&(item->obj_array));
82,626✔
541
   }
542
}
543

544
static void object_arena_gc(object_arena_t *arena)
16,663✔
545
{
546
   const generation_t generation = object_next_generation();
16,663✔
547
   const uint64_t start_ticks = get_timestamp_us();
16,663✔
548

549
   // Mark
550
   for (void *p = arena->base; p != arena->alloc; ) {
2,169,223✔
551
      assert(p < arena->alloc);
2,152,560✔
552
      object_t *object = p;
2,152,560✔
553

554
      const object_class_t *class = classes[object->tag];
2,152,560✔
555

556
      if (is_gc_root(class, object->kind))
2,152,560✔
557
         gc_mark_from_root(object, arena, generation);
22,706✔
558

559
      const size_t size =
2,152,560✔
560
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,152,560✔
561
      p = (char *)p + size;
2,152,560✔
562
   }
563

564
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
16,663✔
565
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
16,663✔
566

567
   // Must initialise here for the search in object_from_locus
568
   memset(forward, 0xff, fwdsz * sizeof(uint32_t));
16,663✔
569

570
   // Calculate forwarding addresses
571
   unsigned woffset = 0, live = 0, dead = 0;
16,663✔
572
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,169,223✔
573
      assert(rptr < arena->alloc);
2,152,560✔
574
      object_t *object = rptr;
2,152,560✔
575

576
      const object_class_t *class = classes[object->tag];
2,152,560✔
577

578
      const size_t size =
2,152,560✔
579
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,152,560✔
580

581
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,152,560✔
582
      if (!object_marked_p(object, generation)) {
2,152,560✔
583
         forward[index] = UINT32_MAX;
334,957✔
584
         gc_free_external(object);
334,957✔
585
         dead++;
334,957✔
586
      }
587
      else {
588
         forward[index] = woffset;
1,817,603✔
589
         woffset += size;
1,817,603✔
590
         live++;
1,817,603✔
591
      }
592

593
      rptr = (char *)rptr + size;
2,152,560✔
594
   }
595

596
   if (woffset == 0)
16,663✔
597
      fatal_trace("GC removed all objects from arena %s",
598
                  istr(object_arena_name(arena)));
599

600
   arena->forward = forward;
16,663✔
601
   arena->live_bytes = woffset;
16,663✔
602

603
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
16,663✔
UNCOV
604
      const int ticks = get_timestamp_us() - start_ticks;
×
605
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
606
             istr(object_arena_name(arena)), dead, live, ticks);
607
   }
608
}
16,663✔
609

610
void object_visit(object_t *object, object_visit_ctx_t *ctx)
742,961✔
611
{
612
   // If `deep' then will follow links above the tree originally passed
613
   // to tree_visit - e.g. following references back to their declarations
614

615
   if (object == NULL)
742,961✔
616
      return;
617
   else if (object_marked_p(object, ctx->generation))
559,215✔
618
      return;
619

620
   const object_class_t *class = classes[object->tag];
532,034✔
621

622
   const bool visit =
1,064,068✔
623
      (object->tag == ctx->tag && object->kind == ctx->kind)
532,034✔
624
      || ctx->kind == class->last_kind;
1,061,951✔
625

626
   if (visit && ctx->preorder != NULL)
532,034✔
UNCOV
627
      (*ctx->preorder)(object, ctx->context);
×
628

629
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
532,034✔
630

631
   imask_t has = class->has_map[object->kind];
532,034✔
632
   for (int n = 0; has; has &= has - 1, n++) {
2,795,404✔
633
      const uint64_t mask = has & -has;
2,263,370✔
634
      if (mask & deep_mask) {
2,263,370✔
635
         item_t *item = &(object->items[n]);
1,603,897✔
636
         if (ITEM_OBJECT & mask)
1,603,897✔
637
            object_visit(item->object, ctx);
370,941✔
638
         else if (ITEM_OBJ_ARRAY & mask) {
1,232,956✔
639
            if (item->obj_array != NULL) {
166,264✔
640
               for (unsigned j = 0; j < item->obj_array->count; j++)
463,087✔
641
                  object_visit(item->obj_array->items[j], ctx);
361,122✔
642
            }
643
         }
644
      }
645
   }
646

647
   if (visit) {
532,034✔
648
      if (ctx->postorder != NULL)
520,240✔
649
         (*ctx->postorder)(object, ctx->context);
520,240✔
650
      ctx->count++;
520,240✔
651
   }
652
}
653

654
static object_t *object_rewrite_iter(object_t *object,
3,505,931✔
655
                                     object_rewrite_ctx_t *ctx)
656
{
657
   // The callback may return a new object or a pointer to an existing
658
   // object in the same arena that that needs to be rewritten so
659
   // iterate rewriting until we reach a fixed point
660
   object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,505,931✔
661
   if (new == object)
3,505,927✔
662
      return new;
663
   else
664
      return object_rewrite(new, ctx);
63,330✔
665
}
666

667
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
8,069,208✔
668
{
669
   if (object == NULL)
8,069,208✔
670
      return NULL;
671

672
   if (!object_in_arena_p(ctx->arena, object))
6,725,358✔
673
      return object;
674

675
   const ptrdiff_t index =
4,478,877✔
676
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,478,877✔
677

678
   // New objects can be allocated while rewrite is in progress so we
679
   // need to check if the index is greater than the current cache size
680
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
4,478,877✔
681
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
70,500✔
682
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
70,500✔
683
                                  ctx->cache_sz);
684
   }
685

686
   if (object_marked_p(object, ctx->generation)) {
4,478,877✔
687
      if (ctx->cache[index] == (object_t *)-1) {
753,004✔
688
         // Found a circular reference: eagerly rewrite the object now
689
         // and break the cycle
690
         if (ctx->post_fn[object->tag] != NULL) {
1,107✔
691
            if (ctx->pre_fn[object->tag] != NULL)
50✔
UNCOV
692
               (*ctx->pre_fn[object->tag])(object, ctx->context);
×
693
            object_t *new = object_rewrite_iter(object, ctx);
50✔
694
            object_write_barrier(object, new);
50✔
695
            return (ctx->cache[index] = new);
50✔
696
         }
697
         else
698
            return (ctx->cache[index] = object);
1,057✔
699
      }
700
      else {
701
         // Already rewritten this tree so return the cached version
702
         return ctx->cache[index];
703
      }
704
   }
705

706
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,725,873✔
707

708
   if (ctx->pre_fn[object->tag] != NULL)
3,725,873✔
UNCOV
709
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
710

711
   const imask_t skip_mask =
3,725,873✔
712
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
713

714
   const object_class_t *class = classes[object->tag];
3,725,873✔
715

716
   imask_t has = class->has_map[object->kind];
3,725,873✔
717
   for (int n = 0; has; has &= has - 1, n++) {
19,969,117✔
718
      const uint64_t mask = has & -has;
16,243,252✔
719
      if (mask & ~skip_mask) {
16,243,252✔
720
         if (ITEM_OBJECT & mask) {
6,815,672✔
721
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,342,228✔
722
            object->items[n].object = o;
5,342,224✔
723
            object_write_barrier(object, o);
5,342,224✔
724
         }
725
         else if (ITEM_OBJ_ARRAY & mask) {
1,473,444✔
726
            obj_array_t **a = &(object->items[n].obj_array);
1,473,444✔
727
            if (object->items[n].obj_array != NULL) {
1,473,444✔
728
               // The callback may add new items to the array so the
729
               // array pointer cannot be cached between iterations
730
               unsigned wptr = 0;
731
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,603,328✔
732
                  object_t *o = object->items[n].obj_array->items[i];
2,634,579✔
733
                  if ((o = object_rewrite(o, ctx))) {
2,634,579✔
734
                     object_write_barrier(object, o);
2,629,931✔
735
                     object->items[n].obj_array->items[wptr++] = o;
2,629,931✔
736
                  }
737
               }
738

739
               if (wptr == 0)
968,749✔
740
                  obj_array_free(a);
827✔
741
               else
742
                  (*a)->count = wptr;
967,922✔
743
            }
744
         }
745
         else
746
            should_not_reach_here();
747
      }
748
   }
749

750
   if (ctx->cache[index] != (object_t *)-1) {
3,725,865✔
751
      // The cache was already updated due to a circular reference
752
      return ctx->cache[index];
753
   }
754
   else if (ctx->post_fn[object->tag] != NULL) {
3,724,758✔
755
      object_t *new = object_rewrite_iter(object, ctx);
3,505,881✔
756
      object_write_barrier(object, new);
3,505,877✔
757
      return (ctx->cache[index] = new);
3,505,877✔
758
   }
759
   else
760
      return (ctx->cache[index] = object);
218,877✔
761
}
762

763
static void object_write_ref(object_t *object, fbuf_t *f)
4,257,254✔
764
{
765
   if (object == NULL)
4,257,254✔
766
      fbuf_put_uint(f, 0);
733,395✔
767
   else {
768
      object_arena_t *arena = __object_arena(object);
3,523,859✔
769
      assert(arena->key != 0);
3,523,859✔
770
      fbuf_put_uint(f, arena->key);
3,523,859✔
771

772
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,523,859✔
773
      if (arena->forward != NULL)
3,523,859✔
774
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,216,766✔
775
      else
776
         fbuf_put_uint(f, index);
1,307,093✔
777
   }
778
}
4,257,254✔
779

780
void object_write(object_t *root, fbuf_t *f, ident_wr_ctx_t ident_ctx,
11,312✔
781
                  loc_wr_ctx_t *loc_ctx)
782
{
783
   object_arena_t *arena = __object_arena(root);
11,312✔
784
   if (root != arena_root(arena))
11,312✔
785
      fatal_trace("must write root object first");
786
   else if (arena->source == OBJ_DISK)
11,312✔
787
      fatal_trace("writing arena %s originally read from disk",
788
                  istr(object_arena_name(arena)));
789
   else if (!arena->frozen)
11,312✔
790
      fatal_trace("arena %s must be frozen before writing to disk",
791
                  istr(object_arena_name(arena)));
792
   else if (arena->obsolete)
11,312✔
793
      fatal_trace("writing obsolete arena %s", istr(object_arena_name(arena)));
794

795
   write_u32(format_digest, f);
11,312✔
796
   fbuf_put_uint(f, standard());
11,312✔
797
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
11,312✔
798
   fbuf_put_uint(f, arena->flags);
11,312✔
799
   fbuf_put_uint(f, arena->key);
11,312✔
800
   ident_write(object_arena_name(arena), ident_ctx);
11,312✔
801

802
   arena_key_t max_key = arena->key;
11,312✔
803
   for (unsigned i = 0; i < arena->deps.count; i++)
33,830✔
804
      max_key = MAX(max_key, arena->deps.items[i]->key);
22,518✔
805
   fbuf_put_uint(f, max_key);
11,312✔
806

807
   fbuf_put_uint(f, arena->deps.count);
11,312✔
808
   for (unsigned i = 0; i < arena->deps.count; i++) {
33,830✔
809
      fbuf_put_uint(f, arena->deps.items[i]->key);
22,518✔
810
      fbuf_put_uint(f, arena->deps.items[i]->std);
22,518✔
811
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
22,518✔
812
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
22,518✔
813
   }
814

815
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
1,911,570✔
816
      assert(p < arena->alloc);
1,900,258✔
817

818
      object_t *object = p;
1,900,258✔
819
      object_class_t *class = classes[object->tag];
1,900,258✔
820

821
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,900,258✔
822

823
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,900,258✔
824
      if (arena->forward[index] == UINT32_MAX)
1,900,258✔
825
         continue;   // Dead object
260,531✔
826

827
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,639,727✔
828
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,639,727✔
829

830
      if (class->has_loc)
1,639,727✔
831
         loc_write(&object->loc, loc_ctx);
1,538,683✔
832

833
      imask_t has = class->has_map[object->kind];
1,639,727✔
834
      for (int n = 0; has; has &= has - 1, n++) {
8,738,264✔
835
         const uint64_t mask = has & -has;
7,098,537✔
836
         item_t *item = &(object->items[n]);
7,098,537✔
837
         if (ITEM_IDENT & mask)
7,098,537✔
838
            ident_write(item->ident, ident_ctx);
1,323,918✔
839
         else if (ITEM_OBJECT & mask)
5,774,619✔
840
            object_write_ref(item->object, f);
3,110,221✔
841
         else if (ITEM_OBJ_ARRAY & mask) {
2,664,398✔
842
            if (item->obj_array != NULL) {
631,430✔
843
               const unsigned count = item->obj_array->count;
409,561✔
844
               fbuf_put_uint(f, count);
409,561✔
845
               for (unsigned i = 0; i < count; i++)
1,556,594✔
846
                  object_write_ref(item->obj_array->items[i], f);
1,147,033✔
847
            }
848
            else
849
               fbuf_put_uint(f, 0);
221,869✔
850
         }
851
         else if (ITEM_INT64 & mask)
2,032,968✔
852
            fbuf_put_int(f, item->ival);
362,031✔
853
         else if (ITEM_INT32 & mask)
1,670,937✔
854
            fbuf_put_int(f, item->ival);
1,557,431✔
855
         else if (ITEM_DOUBLE & mask)
113,506✔
856
            write_double(item->dval, f);
107,848✔
857
         else if (ITEM_NUMBER & mask)
5,658✔
858
            number_write(item->number, f);
5,658✔
859
         else
860
            should_not_reach_here();
861
      }
862
   }
863

864
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
11,312✔
865
}
11,312✔
866

867
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
59,677,444✔
868
{
869
   arena_key_t key = fbuf_get_uint(f);
59,677,444✔
870
   if (key == 0)
59,677,444✔
871
      return NULL;
872

873
   arena_key_t mapped = key_map[key];
50,679,112✔
874
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
50,679,112✔
875

876
   if (unlikely(mapped == 0))
50,679,112✔
877
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
878

879
   assert(mapped < all_arenas.count);
50,679,112✔
880
   assert(mapped > 0);
50,679,112✔
881

882
   object_arena_t *arena = all_arenas.items[mapped];
50,679,112✔
883
   assert(!arena->frozen || offset < arena->alloc - arena->base);
50,679,112✔
884

885
   return (object_t *)((char *)arena->base + offset);
50,679,112✔
886
}
887

888
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
18,969✔
889
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
890
{
891
   object_one_time_init();
18,969✔
892

893
   const uint32_t ver = read_u32(f);
18,969✔
894
   if (ver != format_digest)
18,969✔
UNCOV
895
      fatal("%s: serialised format digest is %x expected %x. This design "
×
896
            "unit uses a library format from an earlier version of "
897
            PACKAGE_NAME " and should be reanalysed.",
898
            fbuf_file_name(f), ver, format_digest);
899

900
   const vhdl_standard_t std = fbuf_get_uint(f);
18,969✔
901

902
   // If this is the first design unit we've loaded then allow it to set
903
   // the default standard
904
   if (all_arenas.count == 0)
18,969✔
905
      set_default_standard(std);
475✔
906

907
   if (std > standard())
18,969✔
908
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
909
            "is more recent that the currently selected standard %s",
910
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
911

912
   const unsigned size = fbuf_get_uint(f);
18,963✔
913
   if (size & OBJECT_PAGE_MASK)
18,963✔
UNCOV
914
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
915

916
   object_arena_t *arena = object_arena_new(size, std);
18,963✔
917
   arena->source = OBJ_DISK;
18,963✔
918
   arena->flags  = fbuf_get_uint(f);
18,963✔
919

920
   arena_key_t key = fbuf_get_uint(f);
18,963✔
921
   ident_t name = ident_read(ident_ctx);
18,963✔
922

923
   arena_key_t max_key = fbuf_get_uint(f);
18,963✔
924

925
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
18,963✔
926
   key_map[key] = arena->key;
18,963✔
927

928
   const int ndeps = fbuf_get_uint(f);
18,963✔
929
   for (int i = 0; i < ndeps; i++) {
42,057✔
930
      arena_key_t dkey = fbuf_get_uint(f);
23,097✔
931
      vhdl_standard_t dstd = fbuf_get_uint(f);
23,097✔
932
      uint32_t checksum = fbuf_get_uint(f);
23,097✔
933
      ident_t dep = ident_read(ident_ctx);
23,097✔
934

935
      object_arena_t *a = NULL;
23,097✔
936
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
108,987✔
937
         if (dep == object_arena_name(all_arenas.items[j]))
85,890✔
938
            a = all_arenas.items[j];
18,850✔
939
      }
940

941
      if (a == NULL) {
23,097✔
942
         object_t *droot = NULL;
4,247✔
943
         if (loader_fn) droot = (*loader_fn)(dep);
4,247✔
944

945
         if (droot == NULL)
4,247✔
UNCOV
946
            fatal("%s depends on %s which cannot be found",
×
947
                  fbuf_file_name(f), istr(dep));
948

949
         a = __object_arena(droot);
4,247✔
950
      }
951

952
      if (a->std != dstd)
23,097✔
UNCOV
953
         fatal("%s: design unit depends on %s version of %s but conflicting "
×
954
               "%s version has been loaded", fbuf_file_name(f),
955
               standard_text(dstd), istr(dep), standard_text(a->std));
956
      else if (a->checksum != checksum) {
23,097✔
957
         diag_t *d = diag_new(DIAG_FATAL, NULL);
3✔
958
         diag_suppress(d, false);
3✔
959
         diag_printf(d, "%s: design unit depends on %s with checksum %08x "
3✔
960
                     "but the current version in the library has checksum %08x",
961
                     fbuf_file_name(f), istr(dep), checksum, a->checksum);
962
         diag_hint(d, NULL, "this usually means %s is outdated and needs to "
3✔
963
                   "be reanalysed", istr(name));
964
         diag_emit(d);
3✔
965
         fatal_exit(EXIT_FAILURE);
3✔
966
      }
967

968
      APUSH(arena->deps, a);
23,094✔
969

970
      assert(dkey <= max_key);
23,094✔
971
      key_map[dkey] = a->key;
23,094✔
972
   }
973

974
   for (;;) {
23,098,006✔
975
      const uint64_t hdr = fbuf_get_uint(f);
23,098,006✔
976
      if (hdr == UINT16_MAX) break;
23,098,006✔
977

978
      const unsigned tag = hdr & 3;
23,079,046✔
979
      const unsigned kind = hdr >> 2;
23,079,046✔
980

981
      assert(tag < OBJECT_TAG_COUNT);
23,079,046✔
982

983
      const object_class_t *class = classes[tag];
23,079,046✔
984

985
      object_t *object = object_new(arena, class, kind);
23,079,046✔
986

987
      if (class->has_loc)
23,079,046✔
988
         loc_read(&(object->loc), loc_ctx);
20,821,944✔
989

990
      imask_t has = class->has_map[object->kind];
23,079,046✔
991
      for (int n = 0; has; has &= has - 1, n++) {
126,579,417✔
992
         const uint64_t mask = has & -has;
103,500,371✔
993
         item_t *item = &(object->items[n]);
103,500,371✔
994
         if (ITEM_IDENT & mask)
103,500,371✔
995
            item->ident = ident_read(ident_ctx);
20,969,098✔
996
         else if (ITEM_OBJECT & mask)
82,531,273✔
997
            item->object = object_read_ref(f, key_map);
41,486,725✔
998
         else if (ITEM_OBJ_ARRAY & mask) {
41,044,548✔
999
            const unsigned count = fbuf_get_uint(f);
9,640,958✔
1000
            if (count > 0) {
9,640,958✔
1001
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
6,446,358✔
1002
                                              count, sizeof(object_t *));
1003
               item->obj_array->count =
6,446,358✔
1004
                  item->obj_array->limit = count;
6,446,358✔
1005
               for (unsigned i = 0; i < count; i++) {
24,637,077✔
1006
                  object_t *o = object_read_ref(f, key_map);
18,190,719✔
1007
                  item->obj_array->items[i] = o;
18,190,719✔
1008
               }
1009
            }
1010
         }
1011
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
31,403,590✔
1012
            item->ival = fbuf_get_int(f);
30,610,606✔
1013
         else if (ITEM_DOUBLE & mask)
792,984✔
1014
            item->dval = read_double(f);
792,984✔
UNCOV
1015
         else if (ITEM_NUMBER & mask)
×
1016
            item->number = number_read(f);
×
1017
         else
1018
            should_not_reach_here();
1019
      }
1020
   }
1021

1022
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
18,960✔
1023

1024
   object_arena_freeze(arena);
18,960✔
1025
   return (object_t *)arena->base;
18,960✔
1026
}
1027

1028
unsigned object_next_generation(void)
64,163✔
1029
{
1030
   return next_generation++;
64,163✔
1031
}
1032

1033
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,415,745✔
1034
{
1035
   if (object == NULL)
3,415,745✔
1036
      return false;
1037

1038
   object_arena_t *arena = __object_arena(object);
2,797,056✔
1039
   if (arena->copygen != ctx->generation)
2,797,056✔
1040
      return false;
1041

1042
   if (ctx->copy_map == NULL)
1,715,247✔
1043
      ctx->copy_map = hash_new(1024);
7,531✔
1044

1045
   if (object_marked_p(object, ctx->generation))
1,715,247✔
1046
      return hash_get(ctx->copy_map, object) != NULL;
388,624✔
1047

1048
   const object_class_t *class = classes[object->tag];
1,326,623✔
1049

1050
   bool marked = false;
1,326,623✔
1051
   if (ctx->should_copy[object->tag] != NULL)
1,326,623✔
1052
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,324,795✔
1053

1054
   object_t *copy = NULL;
1,324,795✔
1055
   if (marked) {
1,324,795✔
1056
      copy = object_new(global_arena, class, object->kind);
52,165✔
1057
      hash_put(ctx->copy_map, object, copy);
52,165✔
1058
   }
1059

1060
   imask_t has = class->has_map[object->kind];
1,326,623✔
1061
   for (int n = 0; has; has &= has - 1, n++) {
7,022,066✔
1062
      const uint64_t mask = has & -has;
5,695,443✔
1063
      item_t *item = &(object->items[n]);
5,695,443✔
1064
      if (ITEM_OBJECT & mask)
5,695,443✔
1065
         marked |= object_copy_mark(item->object, ctx);
2,512,956✔
1066
      else if (ITEM_OBJ_ARRAY & mask) {
3,182,487✔
1067
         if (item->obj_array != NULL) {
483,158✔
1068
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,197,010✔
1069
               object_t *o = item->obj_array->items[i];
887,999✔
1070
               marked |= object_copy_mark(o, ctx);
887,999✔
1071
            }
1072
         }
1073
      }
1074
   }
1075

1076
   if (marked && copy == NULL) {
1,326,623✔
1077
      copy = object_new(global_arena, class, object->kind);
252,246✔
1078
      hash_put(ctx->copy_map, object, copy);
252,246✔
1079
   }
1080

1081
   return marked;
1082
}
1083

1084
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
881,444✔
1085
{
1086
   if (object == NULL)
881,444✔
1087
      return NULL;
1088

1089
   object_t *map = hash_get(ctx->copy_map, object);
745,557✔
1090
   return map ?: object;
745,557✔
1091
}
1092

1093
void object_copy(object_copy_ctx_t *ctx)
7,531✔
1094
{
1095
   for (int i = 0; i < ctx->nroots; i++)
22,321✔
1096
      __object_arena(ctx->roots[i])->copygen = ctx->generation;
14,790✔
1097

1098
   for (int i = 0; i < ctx->nroots; i++)
22,321✔
1099
      (void)object_copy_mark(ctx->roots[i], ctx);
14,790✔
1100

1101
   unsigned ncopied = 0;
7,531✔
1102
   const void *key;
7,531✔
1103
   void *value;
7,531✔
1104
   for (hash_iter_t it = HASH_BEGIN;
7,531✔
1105
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
311,942✔
1106
      const object_t *object = key;
304,411✔
1107
      object_t *copy = value;
304,411✔
1108
      ncopied++;
304,411✔
1109

1110
      copy->loc = object->loc;
304,411✔
1111

1112
      const object_class_t *class = classes[object->tag];
304,411✔
1113

1114
      imask_t has = class->has_map[object->kind];
304,411✔
1115
      for (int n = 0; has; has &= has - 1, n++) {
1,686,206✔
1116
         const uint64_t mask = has & -has;
1,381,795✔
1117
         const item_t *from = &(object->items[n]);
1,381,795✔
1118
         item_t *to = &(copy->items[n]);
1,381,795✔
1119

1120
         if (ITEM_IDENT & mask)
1,381,795✔
1121
            to->ident = from->ident;
245,721✔
1122
         else if (ITEM_OBJECT & mask) {
1,136,074✔
1123
            to->object = object_copy_map(from->object, ctx);
580,210✔
1124
            object_write_barrier(copy, to->object);
580,210✔
1125
         }
1126
         else if (ITEM_DOUBLE & mask)
555,864✔
1127
            to->dval = from->dval;
124✔
1128
         else if (ITEM_OBJ_ARRAY & mask) {
555,740✔
1129
            if (from->obj_array != NULL) {
198,429✔
1130
               // TODO: make a resize macro
1131
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
269,928✔
1132
                                            from->obj_array->count,
134,964✔
1133
                                            sizeof(object_t *));
1134
               to->obj_array->count =
134,964✔
1135
                  to->obj_array->limit = from->obj_array->count;
134,964✔
1136
               for (size_t i = 0; i < from->obj_array->count; i++) {
436,198✔
1137
                  object_t *o =
301,234✔
1138
                     object_copy_map(from->obj_array->items[i], ctx);
301,234✔
1139
                  to->obj_array->items[i] = o;
301,234✔
1140
                  object_write_barrier(copy, o);
301,234✔
1141
               }
1142
            }
1143
         }
1144
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
357,311✔
1145
            to->ival = from->ival;
357,311✔
1146
         else
1147
            should_not_reach_here();
1148
      }
1149
   }
1150

1151
   for (hash_iter_t it = HASH_BEGIN;
7,531✔
1152
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
311,942✔
1153
      object_t *copy = value;
304,411✔
1154
      if (ctx->callback[copy->tag] != NULL)
304,411✔
1155
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
301,222✔
1156
   }
1157

1158
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
7,531✔
UNCOV
1159
      debugf("copied %d objects into arena %s", ncopied,
×
1160
             istr(object_arena_name(global_arena)));
1161

1162
   for (unsigned i = 0; i < ctx->nroots; i++) {
22,321✔
1163
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
14,790✔
1164
      if (copy != NULL)
14,790✔
1165
         ctx->roots[i] = copy;
6,592✔
1166
   }
1167

1168
   hash_free(ctx->copy_map);
7,531✔
1169
}
7,531✔
1170

1171
size_t object_arena_default_size(void)
17,296✔
1172
{
1173
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
17,296✔
1174
}
1175

1176
object_arena_t *object_arena_new(size_t size, unsigned std)
36,259✔
1177
{
1178
   if (all_arenas.count == 0)
36,259✔
1179
      APUSH(all_arenas, NULL);   // Dummy null arena
5,249✔
1180

1181
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
36,259✔
1182
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
36,259✔
1183
   arena->alloc  = arena->base;
36,259✔
1184
   arena->limit  = (char *)arena->base + size;
36,259✔
1185
   arena->key    = all_arenas.count;
36,259✔
1186
   arena->source = OBJ_FRESH;
36,259✔
1187
   arena->std    = std;
36,259✔
1188

1189
   APUSH(all_arenas, arena);
36,259✔
1190

1191
   if (all_arenas.count == UINT16_MAX - 1)
36,259✔
1192
      fatal_trace("too many object arenas");
1193

1194
   return arena;
36,259✔
1195
}
1196

1197
void object_arena_freeze(object_arena_t *arena)
35,623✔
1198
{
1199
   ident_t name = object_arena_name(arena);
35,623✔
1200

1201
   if (arena->frozen)
35,623✔
1202
      fatal_trace("arena %s already frozen", istr(name));
1203

1204
   if (arena->source == OBJ_FRESH)
35,623✔
1205
      object_arena_gc(arena);
16,663✔
1206

1207
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
35,623✔
UNCOV
1208
      debugf("arena %s frozen (%d bytes)", istr(name),
×
UNCOV
1209
             (int)(arena->alloc - arena->base));
×
1210

1211
   chash_put(arena_lookup, name, arena);
35,623✔
1212

1213
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
35,623✔
1214
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
35,623✔
1215

1216
   if (next_page < arena->limit) {
35,623✔
1217
#if OBJECT_UNMAP_UNUSED
1218
      nvc_munmap(next_page, arena->limit - next_page);
1219
      arena->limit = next_page;
1220
#else
1221
      // This can be useful for debugging use-after-free
1222
      nvc_decommit(next_page, arena->limit - next_page);
16,663✔
1223
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
16,663✔
1224
#endif
1225
   }
1226

1227
   arena->frozen = true;
35,623✔
1228
}
35,623✔
1229

1230
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
42,564✔
1231
{
1232
   for (unsigned i = 0; i < arena->deps.count; i++)
145,812✔
1233
      (*fn)(object_arena_name(arena->deps.items[i]), context);
103,248✔
1234
}
42,564✔
1235

1236
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
11,312✔
1237
                              void *context)
1238
{
1239
   for (unsigned i = 0; i < arena->deps.count; i++) {
33,830✔
1240
      if (arena->deps.items[i]->obsolete)
22,518✔
1241
         (*fn)(object_arena_name(arena->deps.items[i]), context);
6✔
1242
   }
1243
}
11,312✔
1244

1245
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
12,630✔
1246
{
1247
   object_arena_t *arena = __object_arena(object);
12,630✔
1248
   assert(arena->frozen);
12,630✔
1249

1250
   *module = object_arena_name(arena);
12,630✔
1251

1252
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
12,630✔
1253
   if (arena->forward != NULL)
12,630✔
1254
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
5,850✔
1255
   else
1256
      *offset = index;
6,780✔
1257
}
12,630✔
1258

1259
static object_arena_t *arena_by_name(ident_t module)
7,319✔
1260
{
1261
   if (global_arena != NULL && object_arena_name(global_arena) == module)
7,319✔
UNCOV
1262
      return global_arena;
×
1263

1264
   object_arena_t *a = chash_get(arena_lookup, module);
7,319✔
1265
   if (a != NULL)
7,319✔
1266
      return a;
1267

1268
#if defined DEBUG && !defined __SANITIZE_THREAD__
1269
   for (int i = 1; i < all_arenas.count; i++)
4,546✔
1270
      assert(module != object_arena_name(all_arenas.items[i]));
3,915✔
1271
#endif
1272

1273
   return NULL;
1274
}
1275

1276
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
7,319✔
1277
                            object_load_fn_t loader)
1278
{
1279
   object_arena_t *arena = arena_by_name(module);
7,319✔
1280

1281
   if (arena == NULL) {
7,319✔
1282
      object_t *droot = NULL;
631✔
1283
      if (loader) droot = (*loader)(module);
631✔
1284

1285
      if (droot == NULL)
631✔
UNCOV
1286
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1287

1288
      arena = __object_arena(droot);
631✔
1289
   }
1290

1291
   assert(arena->frozen);
7,319✔
1292

1293
   void *ptr = NULL;
7,319✔
1294
   if (arena->forward != NULL) {
7,319✔
1295
      // TODO: could do binary search here
UNCOV
1296
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
×
UNCOV
1297
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
×
UNCOV
1298
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
×
UNCOV
1299
            break;
×
1300
         }
1301
      }
UNCOV
1302
      assert(ptr != NULL);
×
1303
   }
1304
   else
1305
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
7,319✔
1306

1307
   if (ptr > arena->limit)
7,319✔
1308
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
1309

1310
   object_t *obj = ptr;
7,319✔
1311
   if (obj->tag >= OBJECT_TAG_COUNT)
7,319✔
1312
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1313
                  istr(module), offset);
1314
   else if (obj->arena != arena->key)
7,319✔
1315
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
UNCOV
1316
                  obj->arena, arena->key, istr(module), offset);
×
1317

1318
   return obj;
7,319✔
1319
}
1320

1321
void freeze_global_arena(void)
25,679✔
1322
{
1323
   object_one_time_init();
25,679✔
1324

1325
   if (global_arena != NULL) {
25,679✔
1326
      object_arena_freeze(global_arena);
16,663✔
1327
      global_arena = NULL;
16,663✔
1328
   }
1329
}
25,679✔
1330

1331
void make_new_arena(void)
17,296✔
1332
{
1333
   freeze_global_arena();
17,296✔
1334
   global_arena = object_arena_new(object_arena_default_size(), standard());
17,296✔
1335
}
17,296✔
1336

1337
void discard_global_arena(void)
1✔
1338
{
1339
   object_one_time_init();
1✔
1340

1341
   if (global_arena != NULL) {
1✔
1342
      nvc_munmap(global_arena->base, global_arena->limit - global_arena->base);
1✔
1343

1344
      assert(all_arenas.items[all_arenas.count - 1] == global_arena);
1✔
1345
      APOP(all_arenas);
1✔
1346

1347
      free(global_arena);
1✔
1348
      global_arena = NULL;
1✔
1349
   }
1350
}
1✔
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