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

nickg / nvc / 28742123901

05 Jul 2026 01:19PM UTC coverage: 92.117% (+0.004%) from 92.113%
28742123901

push

github

nickg
Handle calls between Verilog constant functions

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

205 existing lines in 6 files now uncovered.

79687 of 86506 relevant lines covered (92.12%)

659926.47 hits per line

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

85.54
/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)
14,631,368✔
107
{
108
   return (void *)object >= arena->base && (void *)object < arena->limit;
14,631,368✔
109
}
110

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

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

126
   return ident_new("???");
36,015✔
127
}
128

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

135
   if (count == 1)
239,313✔
136
      arena->mark_bits[first] = 0;
208,266✔
137
   else
138
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
31,047✔
139
}
239,313✔
140

141
static bool object_marked_p(object_t *object, generation_t generation)
15,696,477✔
142
{
143
   object_arena_t *arena = __object_arena(object);
15,696,477✔
144

145
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
15,696,477✔
146
   const uintptr_t word = bit / 64;
15,696,477✔
147

148
   if (unlikely(arena->mark_bits == NULL)) {
15,696,477✔
149
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
26,935✔
150
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
26,935✔
151
      arena->mark_bits = xmalloc(arena->mark_sz);
26,935✔
152
      arena->mark_bits[word] = 0;
26,935✔
153
      arena->mark_low = arena->mark_high = word;
26,935✔
154
      arena->generation = generation;
26,935✔
155
   }
156
   else if (arena->generation != generation) {
15,669,542✔
157
      arena->mark_bits[word] = 0;
77,748✔
158
      arena->mark_low = arena->mark_high = word;
77,748✔
159
      arena->generation = generation;
77,748✔
160
   }
161

162
   // Lazy zeroing of mark bits helps performance with large arenas
163
   if (word < arena->mark_low) {
15,696,477✔
164
      zero_mark_bits(arena, word, arena->mark_low - word);
9,752✔
165
      arena->mark_low = word;
9,752✔
166
   }
167
   else if (word > arena->mark_high) {
15,686,725✔
168
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
229,561✔
169
      arena->mark_high = word;
229,561✔
170
   }
171

172
   const uint64_t mask = UINT64_C(1) << (bit & 63);
15,696,477✔
173

174
   const bool marked = !!(arena->mark_bits[word] & mask);
15,696,477✔
175
   arena->mark_bits[word] |= mask;
15,696,477✔
176

177
   return marked;
15,696,477✔
178
}
179

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

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

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

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

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

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

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

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

220
   if (has & I_IDENT) {
314,150✔
221
      const int n = __builtin_popcountll(has & (I_IDENT - 1));
310,894✔
222
      return object->items[n].ident;
310,894✔
223
   }
224

225
   return NULL;
226
}
227

228
const char *object_kind_str(object_t *object)
×
229
{
230
   if (object == NULL)
×
231
      return NULL;
232

233
   const object_class_t *class = classes[object->tag];
×
234
   return class->kind_text_map[object->kind];
×
235
}
236

237
void __object_write_barrier(object_t *lhs, object_t *rhs)
6,361,619✔
238
{
239
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
6,361,619✔
240
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
6,361,619✔
241

242
   if (lhs_mask == rhs_mask || rhs == NULL)
6,361,619✔
243
      return;
244
   else if (lhs->arena == rhs->arena)
6,361,619✔
245
      return;
246

247
   object_arena_t *larena = __object_arena(lhs);
5,666,833✔
248
   object_arena_t *rarena = __object_arena(rhs);
5,666,833✔
249

250
   assert(!larena->frozen);
5,666,833✔
251
   assert(rarena->frozen);
5,666,833✔
252

253
   for (unsigned i = 0; i < larena->deps.count; i++) {
9,102,323✔
254
      if (larena->deps.items[i] == rarena)
9,051,996✔
255
         return;
256
   }
257

258
   APUSH(larena->deps, rarena);
50,327✔
259
}
260

261
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
262
{
263
   unsigned int item;
×
264
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
265
      ;
266

267
   assert(item < ARRAY_LEN(item_text_map));
×
268

269
   diag_t *d = diag_new(DIAG_FATAL, &(object->loc));
×
270
   diag_printf(d, "%s kind %s does not have item %s", class->name,
×
271
               class->kind_text_map[object->kind], item_text_map[item]);
×
272
   diag_set_consumer(NULL, NULL);
×
273
   diag_suppress(d, false);
×
274
   diag_emit(d);
×
275
   show_stacktrace();
×
276
   fatal_exit(EXIT_FAILURE);
×
277
}
278

279
void obj_array_add(obj_array_t **a, object_t *o)
1,760,387✔
280
{
281
   if (*a == NULL) {
1,760,387✔
282
      const int defsz = 8;
629,580✔
283
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
629,580✔
284
      (*a)->count = 0;
629,580✔
285
      (*a)->limit = defsz;
629,580✔
286
   }
287
   else if ((*a)->count == (*a)->limit) {
1,130,807✔
288
      (*a)->limit *= 2;
43,222✔
289
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
43,222✔
290
                         (*a)->limit, sizeof(object_t *));
291
   }
292

293
   (*a)->items[(*a)->count++] = o;
1,760,387✔
294
}
1,760,387✔
295

296
void obj_array_copy(obj_array_t **dst, const obj_array_t *src)
16,204✔
297
{
298
   if (src == NULL)
16,204✔
299
      return;
300

301
   if (*dst == NULL) {
16,204✔
302
      *dst = xmalloc_flex(sizeof(obj_array_t), src->count, sizeof(object_t *));
16,080✔
303
      (*dst)->count = 0;
16,080✔
304
      (*dst)->limit = src->count;
16,080✔
305
   }
306
   else if ((*dst)->count + src->count > (*dst)->limit) {
124✔
307
      (*dst)->limit = (*dst)->count + src->count;
124✔
308
      *dst = xrealloc_flex(*dst, sizeof(obj_array_t),
124✔
309
                           (*dst)->limit, sizeof(object_t *));
310
   }
311

312
   for (int i = 0; i < src->count; i++)
76,485✔
313
      (*dst)->items[(*dst)->count++] = src->items[i];
60,281✔
314
}
315

316
void obj_array_free(obj_array_t **a)
141,003✔
317
{
318
   free(*a);
141,003✔
319
   *a = NULL;
141,003✔
320
}
141,003✔
321

322
void object_change_kind(const object_class_t *class, object_t *object, int kind)
28,672✔
323
{
28,672✔
324
   if (kind == object->kind)
28,672✔
325
      return;
×
326

327
   bool allow = false;
328
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
166,159✔
329
      allow = (class->change_allowed[i][0] == object->kind)
137,487✔
330
         && (class->change_allowed[i][1] == kind);
137,487✔
331
   }
332

333
   if (!allow)
28,672✔
334
      fatal_trace("cannot change %s kind %s to %s", class->name,
335
                  class->kind_text_map[object->kind],
×
336
                  class->kind_text_map[kind]);
×
337

338
   const imask_t old_has = class->has_map[object->kind];
28,672✔
339
   const imask_t new_has = class->has_map[kind];
28,672✔
340

341
   const int old_nitems = __builtin_popcountll(old_has);
28,672✔
342
   const int new_nitems = __builtin_popcountll(new_has);
28,672✔
343

344
   const int max_items = MAX(old_nitems, new_nitems);
28,672✔
345

346
   item_t tmp[max_items];
28,672✔
347
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
28,672✔
348

349
   int op = 0, np = 0;
28,672✔
350
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
1,116,352✔
351
      if ((old_has & mask) && (new_has & mask))
1,087,680✔
352
         object->items[np++] = tmp[op++];
96,778✔
353
      else if (old_has & mask) {
990,902✔
354
         if (ITEM_OBJ_ARRAY & mask)
150✔
355
            obj_array_free(&(tmp[op].obj_array));
150✔
356
         ++op;
150✔
357
      }
358
      else if (new_has & mask)
990,752✔
359
         memset(&(object->items[np++]), '\0', sizeof(item_t));
1,087,680✔
360
   }
361

362
   object->kind = kind;
28,672✔
363
}
364

365
static void object_init(object_class_t *class)
35,252✔
366
{
367
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
35,252✔
368

369
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
35,252✔
370

371
   assert(class->tag < ARRAY_LEN(classes));
35,252✔
372
   classes[class->tag] = class;
35,252✔
373

374
#ifdef DEBUG
375
   imask_t all_items = 0;
35,252✔
376
#endif
377

378
   for (int i = 0; i < class->last_kind; i++) {
2,423,575✔
379
      const int nitems = __builtin_popcountll(class->has_map[i]);
2,388,323✔
380
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
2,388,323✔
381
      DEBUG_ONLY(all_items |= class->has_map[i]);
2,388,323✔
382

383
      format_digest += knuth_hash(class->has_map[i] >> 32);
2,388,323✔
384
      format_digest += knuth_hash(class->has_map[i]);
2,388,323✔
385
   }
386

387
   bool changed = false;
52,878✔
388
   do {
52,878✔
389
      changed = false;
52,878✔
390
      for (int i = 0; i < class->last_kind; i++) {
4,556,321✔
391
         size_t max_size = class->object_size[i];
4,503,443✔
392
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
33,057,563✔
393
            if (class->change_allowed[j][0] == i)
28,554,120✔
394
               max_size = MAX(max_size,
237,951✔
395
                              class->object_size[class->change_allowed[j][1]]);
396
            else if (class->change_allowed[j][1] == i)
28,316,169✔
397
               max_size = MAX(max_size,
237,951✔
398
                              class->object_size[class->change_allowed[j][0]]);
399
         }
400

401
         if (max_size != class->object_size[i]) {
4,503,443✔
402
            class->object_size[i] = max_size;
61,691✔
403
            changed = true;
61,691✔
404
         }
405
      }
406
   } while (changed);
52,878✔
407

408
#ifdef DEBUG
409
   if (getenv("NVC_TREE_SIZES") != NULL) {
35,252✔
410
      for (int i = 0; i < class->last_kind; i++)
×
411
         printf("%-15s %d\n", class->kind_text_map[i],
×
412
                (int)class->object_size[i]);
×
413
   }
414

415
   const imask_t known_types =
35,252✔
416
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
417
      | ITEM_DOUBLE | ITEM_NUMBER;
418

419
   const imask_t missing = all_items & ~known_types;
35,252✔
420
   if (missing != 0) {
35,252✔
421
      int item;
422
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
423
         ;
424

425
      assert(item < ARRAY_LEN(item_text_map));
×
426
      fatal_trace("item %s does not have a type", item_text_map[item]);
427
   }
428
#endif
429
}
35,252✔
430

431
static void check_frozen_object_fault(int sig, void *addr,
×
432
                                      struct cpu_state *cpu, void *context)
433
{
434
#ifndef __MINGW32__
435
   if (sig != SIGSEGV && sig != SIGBUS)
×
436
      return;
437
#endif
438

439
   for (unsigned i = 1; i < all_arenas.count; i++) {
×
440
      object_arena_t *arena = AGET(all_arenas, i);
×
441
      if (!arena->frozen)
×
442
         continue;
×
443
      else if (addr < arena->base)
×
444
         continue;
×
445
      else if (addr >= arena->limit)
×
446
         continue;
×
447

448
      fatal_trace("Write to object in frozen arena %s [address=%p]",
449
                  istr(object_arena_name(arena)), addr);
450
   }
451
}
452

453
static void object_one_time_init(void)
145,498✔
454
{
455
   INIT_ONCE({
145,498✔
456
         extern object_class_t tree_object;
457
         object_init(&tree_object);
458

459
         extern object_class_t type_object;
460
         object_init(&type_object);
461

462
         extern object_class_t vlog_object;
463
         object_init(&vlog_object);
464

465
         extern object_class_t psl_object;
466
         object_init(&psl_object);
467

468
         // Increment this each time an incompatible change is made to
469
         // the on-disk format not expressed in the object items table
470
         const uint32_t format_fudge = 46;
471

472
         format_digest += format_fudge * UINT32_C(2654435761);
473

474
         add_fault_handler(check_frozen_object_fault, NULL);
475

476
         arena_lookup = chash_new(64);
477
      });
478
}
145,498✔
479

480
static bool is_gc_root(const object_class_t *class, int kind)
3,021,522✔
481
{
482
   for (int j = 0; j < class->gc_num_roots; j++) {
27,249,592✔
483
      if (class->gc_roots[j] == kind)
24,324,731✔
484
         return true;
485
   }
486

487
   return false;
488
}
489

490
object_t *object_new(object_arena_t *arena,
48,183,908✔
491
                     const object_class_t *class, int kind)
492
{
493
   if (unlikely(kind >= class->last_kind))
48,183,908✔
494
      fatal_trace("invalid kind %d for %s object", kind, class->name);
495

496
   if (arena == NULL)
48,183,908✔
497
      arena = global_arena;
2,695,083✔
498

499
   if (unlikely(arena == NULL))
48,183,908✔
500
      fatal_trace("allocating object without active arena");
501

502
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
48,183,908✔
503

504
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
48,183,908✔
505

506
   if (unlikely(arena->limit - arena->alloc < size)) {
48,183,908✔
UNCOV
507
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
UNCOV
508
      diag_suppress(d, false);
×
509
      diag_printf(d, "memory exhausted while creating unit %s",
×
510
                  istr(object_arena_name(arena)));
511
      diag_hint(d, NULL, "The current limit is %zu bytes which you can "
×
512
                "increase with the $bold$-M$$ option, for example "
513
                "$bold$-M 32m$$", object_arena_default_size());
UNCOV
514
      diag_emit(d);
×
UNCOV
515
      fatal_exit(EXIT_FAILURE);
×
516
   }
517

518
   object_t *object = arena->alloc;
48,183,908✔
519
   arena->alloc = (char *)arena->alloc + size;
48,183,908✔
520

521
   if (arena->root == NULL && is_gc_root(class, kind))
48,183,908✔
522
      arena->root = object;
65,776✔
523

524
   object->kind  = kind;
48,183,908✔
525
   object->tag   = class->tag;
48,183,908✔
526
   object->arena = arena->key;
48,183,908✔
527
   object->loc   = LOC_INVALID;
48,183,908✔
528

529
   return object;
48,183,908✔
530
}
531

532
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
6,449,689✔
533
                              generation_t generation)
534
{
535
   if (object == NULL)
6,449,689✔
536
      return;
537
   else if (!object_in_arena_p(arena, object))
5,405,503✔
538
      return;
539
   else if (object_marked_p(object, generation))
3,357,630✔
540
      return;
541

542
   const object_class_t *class = classes[object->tag];
2,541,591✔
543

544
   imask_t has = class->has_map[object->kind];
2,541,591✔
545
   for (int n = 0; has; has &= has - 1, n++) {
13,127,338✔
546
      const uint64_t mask = has & -has;
10,585,747✔
547
      item_t *item = &(object->items[n]);
10,585,747✔
548
      if (ITEM_OBJECT & mask)
10,585,747✔
549
         gc_mark_from_root(item->object, arena, generation);
4,525,105✔
550
      else if (ITEM_OBJ_ARRAY & mask) {
6,060,642✔
551
         if (item->obj_array != NULL) {
1,099,867✔
552
            for (unsigned j = 0; j < item->obj_array->count; j++)
2,586,142✔
553
               gc_mark_from_root(item->obj_array->items[j], arena,
1,893,699✔
554
                                 generation);
555
         }
556
      }
557
   }
558
}
559

560
static void gc_free_external(object_t *object)
414,152✔
561
{
562
   const object_class_t *class = classes[object->tag];
414,152✔
563

564
   imask_t has = class->has_map[object->kind];
414,152✔
565
   if ((has & ITEM_OBJ_ARRAY) == 0)
414,152✔
566
      return;
567

568
   for (int n = 0; has; has &= has - 1, n++) {
504,697✔
569
      const uint64_t mask = has & -has;
420,110✔
570
      item_t *item = &(object->items[n]);
420,110✔
571
      if (ITEM_OBJ_ARRAY & mask)
420,110✔
572
         obj_array_free(&(item->obj_array));
139,780✔
573
   }
574
}
575

576
static void object_arena_gc(object_arena_t *arena)
23,882✔
577
{
578
   const generation_t generation = object_next_generation();
23,882✔
579
   const uint64_t start_ticks = get_timestamp_us();
23,882✔
580

581
   // Mark
582
   for (void *p = arena->base; p != arena->alloc; ) {
2,979,625✔
583
      assert(p < arena->alloc);
2,955,743✔
584
      object_t *object = p;
2,955,743✔
585

586
      const object_class_t *class = classes[object->tag];
2,955,743✔
587

588
      if (is_gc_root(class, object->kind))
2,955,743✔
589
         gc_mark_from_root(object, arena, generation);
30,885✔
590

591
      const size_t size =
2,955,743✔
592
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,955,743✔
593
      p = (char *)p + size;
2,955,743✔
594
   }
595

596
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
23,882✔
597
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
23,882✔
598

599
   // Must initialise here for the search in object_from_locus
600
   memset(forward, 0xff, fwdsz * sizeof(uint32_t));
23,882✔
601

602
   // Calculate forwarding addresses
603
   unsigned woffset = 0, live = 0, dead = 0;
23,882✔
604
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,979,625✔
605
      assert(rptr < arena->alloc);
2,955,743✔
606
      object_t *object = rptr;
2,955,743✔
607

608
      const object_class_t *class = classes[object->tag];
2,955,743✔
609

610
      const size_t size =
2,955,743✔
611
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,955,743✔
612

613
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,955,743✔
614
      if (!object_marked_p(object, generation)) {
2,955,743✔
615
         forward[index] = UINT32_MAX;
414,152✔
616
         gc_free_external(object);
414,152✔
617
         dead++;
414,152✔
618
      }
619
      else {
620
         forward[index] = woffset;
2,541,591✔
621
         woffset += size;
2,541,591✔
622
         live++;
2,541,591✔
623
      }
624

625
      rptr = (char *)rptr + size;
2,955,743✔
626
   }
627

628
   if (woffset == 0)
23,882✔
629
      fatal_trace("GC removed all objects from arena %s",
630
                  istr(object_arena_name(arena)));
631

632
   arena->forward = forward;
23,882✔
633
   arena->live_bytes = woffset;
23,882✔
634

635
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
23,882✔
UNCOV
636
      const int ticks = get_timestamp_us() - start_ticks;
×
UNCOV
637
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
638
             istr(object_arena_name(arena)), dead, live, ticks);
639
   }
640
}
23,882✔
641

642
void object_visit(object_t *object, object_visit_ctx_t *ctx)
968,466✔
643
{
644
   // If `deep' then will follow links above the tree originally passed
645
   // to tree_visit - e.g. following references back to their declarations
646

647
   if (object == NULL)
968,466✔
648
      return;
649
   else if (object_marked_p(object, ctx->generation))
730,122✔
650
      return;
651

652
   const object_class_t *class = classes[object->tag];
693,102✔
653

654
   const bool visit =
1,386,204✔
655
      (object->tag == ctx->tag && object->kind == ctx->kind)
693,102✔
656
      || ctx->kind == class->last_kind;
1,382,980✔
657

658
   if (visit && ctx->preorder != NULL)
693,102✔
UNCOV
659
      (*ctx->preorder)(object, ctx->context);
×
660

661
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
693,102✔
662

663
   imask_t has = class->has_map[object->kind];
693,102✔
664
   for (int n = 0; has; has &= has - 1, n++) {
3,635,108✔
665
      const uint64_t mask = has & -has;
2,942,006✔
666
      if (mask & deep_mask) {
2,942,006✔
667
         item_t *item = &(object->items[n]);
2,085,167✔
668
         if (ITEM_OBJECT & mask)
2,085,167✔
669
            object_visit(item->object, ctx);
483,688✔
670
         else if (ITEM_OBJ_ARRAY & mask) {
1,601,479✔
671
            if (item->obj_array != NULL) {
214,646✔
672
               for (unsigned j = 0; j < item->obj_array->count; j++)
602,931✔
673
                  object_visit(item->obj_array->items[j], ctx);
471,622✔
674
            }
675
         }
676
      }
677
   }
678

679
   if (visit) {
693,102✔
680
      if (ctx->postorder != NULL)
668,346✔
681
         (*ctx->postorder)(object, ctx->context);
668,346✔
682
      ctx->count++;
668,346✔
683
   }
684
}
685

686
static object_t *object_rewrite_iter(object_t *object,
5,001,375✔
687
                                     object_rewrite_ctx_t *ctx)
688
{
689
   // The callback may return a new object or a pointer to an existing
690
   // object in the same arena that that needs to be rewritten so
691
   // iterate rewriting until we reach a fixed point
692
   object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
5,001,375✔
693
   if (new == object)
5,001,370✔
694
      return new;
695
   else
696
      return object_rewrite(new, ctx);
74,915✔
697
}
698

699
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
11,005,221✔
700
{
701
   if (object == NULL)
11,005,221✔
702
      return NULL;
703

704
   if (!object_in_arena_p(ctx->arena, object))
9,225,865✔
705
      return object;
706

707
   const ptrdiff_t index =
6,305,421✔
708
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
6,305,421✔
709

710
   // New objects can be allocated while rewrite is in progress so we
711
   // need to check if the index is greater than the current cache size
712
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
6,305,421✔
713
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
97,301✔
714
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
97,301✔
715
                                  ctx->cache_sz);
716
   }
717

718
   if (object_marked_p(object, ctx->generation)) {
6,305,421✔
719
      if (ctx->cache[index] == (object_t *)-1) {
1,019,466✔
720
         // Found a circular reference: eagerly rewrite the object now
721
         // and break the cycle
722
         if (ctx->post_fn[object->tag] != NULL) {
1,492✔
723
            if (ctx->pre_fn[object->tag] != NULL)
98✔
724
               (*ctx->pre_fn[object->tag])(object, ctx->context);
23✔
725
            object_t *new = object_rewrite_iter(object, ctx);
98✔
726
            object_write_barrier(object, new);
98✔
727
            return (ctx->cache[index] = new);
98✔
728
         }
729
         else
730
            return (ctx->cache[index] = object);
1,394✔
731
      }
732
      else {
733
         // Already rewritten this tree so return the cached version
734
         return ctx->cache[index];
735
      }
736
   }
737

738
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
5,285,955✔
739

740
   if (ctx->pre_fn[object->tag] != NULL)
5,285,955✔
741
      (*ctx->pre_fn[object->tag])(object, ctx->context);
254,856✔
742

743
   const imask_t skip_mask =
5,285,955✔
744
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
745

746
   const object_class_t *class = classes[object->tag];
5,285,955✔
747

748
   imask_t has = class->has_map[object->kind];
5,285,955✔
749
   for (int n = 0; has; has &= has - 1, n++) {
27,320,454✔
750
      const uint64_t mask = has & -has;
22,034,509✔
751
      if (mask & ~skip_mask) {
22,034,509✔
752
         if (ITEM_OBJECT & mask) {
9,313,772✔
753
            object_t *o = object_rewrite(object->items[n].object, ctx);
7,262,501✔
754
            object->items[n].object = o;
7,262,496✔
755
            object_write_barrier(object, o);
7,262,496✔
756
         }
757
         else if (ITEM_OBJ_ARRAY & mask) {
2,051,271✔
758
            obj_array_t **a = &(object->items[n].obj_array);
2,051,271✔
759
            if (object->items[n].obj_array != NULL) {
2,051,271✔
760
               // The callback may add new items to the array so the
761
               // array pointer cannot be cached between iterations
762
               unsigned wptr = 0;
763
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
4,948,766✔
764
                  object_t *o = object->items[n].obj_array->items[i];
3,622,590✔
765
                  if ((o = object_rewrite(o, ctx))) {
3,622,590✔
766
                     object_write_barrier(object, o);
3,616,644✔
767
                     object->items[n].obj_array->items[wptr++] = o;
3,616,644✔
768
                  }
769
               }
770

771
               if (wptr == 0)
1,326,176✔
772
                  obj_array_free(a);
1,073✔
773
               else
774
                  (*a)->count = wptr;
1,325,103✔
775
            }
776
         }
777
         else
778
            should_not_reach_here();
779
      }
780
   }
781

782
   if (ctx->cache[index] != (object_t *)-1) {
5,285,945✔
783
      // The cache was already updated due to a circular reference
784
      return ctx->cache[index];
785
   }
786
   else if (ctx->post_fn[object->tag] != NULL) {
5,284,453✔
787
      object_t *new = object_rewrite_iter(object, ctx);
5,001,277✔
788
      object_write_barrier(object, new);
5,001,272✔
789
      return (ctx->cache[index] = new);
5,001,272✔
790
   }
791
   else
792
      return (ctx->cache[index] = object);
283,176✔
793
}
794

795
static void object_write_ref(object_t *object, fbuf_t *f)
5,880,543✔
796
{
797
   if (object == NULL)
5,880,543✔
798
      fbuf_put_uint(f, 0);
978,966✔
799
   else {
800
      object_arena_t *arena = __object_arena(object);
4,901,577✔
801
      assert(arena->key != 0);
4,901,577✔
802
      fbuf_put_uint(f, arena->key);
4,901,577✔
803

804
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
4,901,577✔
805
      if (arena->forward != NULL)
4,901,577✔
806
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
3,220,750✔
807
      else
808
         fbuf_put_uint(f, index);
1,680,827✔
809
   }
810
}
5,880,543✔
811

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

827
   write_u32(format_digest, f);
17,792✔
828
   fbuf_put_uint(f, standard());
17,792✔
829
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
17,792✔
830
   fbuf_put_uint(f, arena->flags);
17,792✔
831
   fbuf_put_uint(f, arena->key);
17,792✔
832
   ident_write(object_arena_name(arena), ident_ctx);
17,792✔
833

834
   arena_key_t max_key = arena->key;
17,792✔
835
   for (unsigned i = 0; i < arena->deps.count; i++)
52,801✔
836
      max_key = MAX(max_key, arena->deps.items[i]->key);
35,009✔
837
   fbuf_put_uint(f, max_key);
17,792✔
838

839
   fbuf_put_uint(f, arena->deps.count);
17,792✔
840
   for (unsigned i = 0; i < arena->deps.count; i++) {
52,801✔
841
      fbuf_put_uint(f, arena->deps.items[i]->key);
35,009✔
842
      fbuf_put_uint(f, arena->deps.items[i]->std);
35,009✔
843
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
35,009✔
844
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
35,009✔
845
   }
846

847
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
2,734,890✔
848
      assert(p < arena->alloc);
2,717,098✔
849

850
      object_t *object = p;
2,717,098✔
851
      object_class_t *class = classes[object->tag];
2,717,098✔
852

853
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,717,098✔
854

855
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
2,717,098✔
856
      if (arena->forward[index] == UINT32_MAX)
2,717,098✔
857
         continue;   // Dead object
359,393✔
858

859
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
2,357,705✔
860
      fbuf_put_uint(f, object->tag | (object->kind << 2));
2,357,705✔
861

862
      if (class->has_loc)
2,357,705✔
863
         loc_write(&object->loc, loc_ctx);
2,224,032✔
864

865
      imask_t has = class->has_map[object->kind];
2,357,705✔
866
      for (int n = 0; has; has &= has - 1, n++) {
12,124,644✔
867
         const uint64_t mask = has & -has;
9,766,939✔
868
         item_t *item = &(object->items[n]);
9,766,939✔
869
         if (ITEM_IDENT & mask)
9,766,939✔
870
            ident_write(item->ident, ident_ctx);
1,820,047✔
871
         else if (ITEM_OBJECT & mask)
7,946,892✔
872
            object_write_ref(item->object, f);
4,255,338✔
873
         else if (ITEM_OBJ_ARRAY & mask) {
3,691,554✔
874
            if (item->obj_array != NULL) {
914,297✔
875
               const unsigned count = item->obj_array->count;
585,287✔
876
               fbuf_put_uint(f, count);
585,287✔
877
               for (unsigned i = 0; i < count; i++)
2,210,492✔
878
                  object_write_ref(item->obj_array->items[i], f);
1,625,205✔
879
            }
880
            else
881
               fbuf_put_uint(f, 0);
329,010✔
882
         }
883
         else if (ITEM_INT64 & mask)
2,777,257✔
884
            fbuf_put_int(f, item->ival);
467,349✔
885
         else if (ITEM_INT32 & mask)
2,309,908✔
886
            fbuf_put_int(f, item->ival);
2,107,189✔
887
         else if (ITEM_DOUBLE & mask)
202,719✔
888
            write_double(item->dval, f);
143,733✔
889
         else if (ITEM_NUMBER & mask)
58,986✔
890
            number_write(item->number, f);
58,986✔
891
         else
892
            should_not_reach_here();
893
      }
894
   }
895

896
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
17,792✔
897
}
17,792✔
898

899
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
116,567,778✔
900
{
901
   arena_key_t key = fbuf_get_uint(f);
116,567,778✔
902
   if (key == 0)
116,567,778✔
903
      return NULL;
904

905
   arena_key_t mapped = key_map[key];
99,132,016✔
906
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
99,132,016✔
907

908
   if (unlikely(mapped == 0))
99,132,016✔
909
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
910

911
   assert(mapped < all_arenas.count);
99,132,016✔
912
   assert(mapped > 0);
99,132,016✔
913

914
   object_arena_t *arena = all_arenas.items[mapped];
99,132,016✔
915
   assert(!arena->frozen || offset < arena->alloc - arena->base);
99,132,016✔
916

917
   return (object_t *)((char *)arena->base + offset);
99,132,016✔
918
}
919

920
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
41,232✔
921
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
922
{
923
   object_one_time_init();
41,232✔
924

925
   const uint32_t ver = read_u32(f);
41,232✔
926
   if (ver != format_digest)
41,232✔
UNCOV
927
      fatal("%s: serialised format digest is %x expected %x. This design "
×
928
            "unit uses a library format from an earlier version of "
929
            PACKAGE_NAME " and should be reanalysed.",
930
            fbuf_file_name(f), ver, format_digest);
931

932
   const vhdl_standard_t std = fbuf_get_uint(f);
41,232✔
933

934
   // If this is the first design unit we've loaded then allow it to set
935
   // the default standard
936
   if (all_arenas.count == 0)
41,232✔
937
      set_default_standard(std);
1,904✔
938

939
   if (std > standard())
41,232✔
940
      fatal("%s: design unit was analysed using standard revision %s which "
8✔
941
            "is more recent that the currently selected standard %s",
942
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
943

944
   const unsigned size = fbuf_get_uint(f);
41,224✔
945
   if (size & OBJECT_PAGE_MASK)
41,224✔
UNCOV
946
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
947

948
   object_arena_t *arena = object_arena_new(size, std);
41,224✔
949
   arena->source = OBJ_DISK;
41,224✔
950
   arena->flags  = fbuf_get_uint(f);
41,224✔
951

952
   arena_key_t key = fbuf_get_uint(f);
41,224✔
953
   ident_t name = ident_read(ident_ctx);
41,224✔
954

955
   arena_key_t max_key = fbuf_get_uint(f);
41,224✔
956

957
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
41,224✔
958
   key_map[key] = arena->key;
41,224✔
959

960
   const int ndeps = fbuf_get_uint(f);
41,224✔
961
   for (int i = 0; i < ndeps; i++) {
99,016✔
962
      arena_key_t dkey = fbuf_get_uint(f);
57,796✔
963
      vhdl_standard_t dstd = fbuf_get_uint(f);
57,796✔
964
      uint32_t checksum = fbuf_get_uint(f);
57,796✔
965
      ident_t dep = ident_read(ident_ctx);
57,796✔
966

967
      object_arena_t *a = NULL;
57,796✔
968
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
286,597✔
969
         if (dep == object_arena_name(all_arenas.items[j]))
228,801✔
970
            a = all_arenas.items[j];
44,378✔
971
      }
972

973
      if (a == NULL) {
57,796✔
974
         object_t *droot = NULL;
13,418✔
975
         if (loader_fn) droot = (*loader_fn)(dep);
13,418✔
976

977
         if (droot == NULL)
13,418✔
UNCOV
978
            fatal("%s depends on %s which cannot be found",
×
979
                  fbuf_file_name(f), istr(dep));
980

981
         a = __object_arena(droot);
13,418✔
982
      }
983

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

1000
      APUSH(arena->deps, a);
57,792✔
1001

1002
      assert(dkey <= max_key);
57,792✔
1003
      key_map[dkey] = a->key;
57,792✔
1004
   }
1005

1006
   for (;;) {
45,194,273✔
1007
      const uint64_t hdr = fbuf_get_uint(f);
45,194,273✔
1008
      if (hdr == UINT16_MAX) break;
45,194,273✔
1009

1010
      const unsigned tag = hdr & 3;
45,153,053✔
1011
      const unsigned kind = hdr >> 2;
45,153,053✔
1012

1013
      assert(tag < OBJECT_TAG_COUNT);
45,153,053✔
1014

1015
      const object_class_t *class = classes[tag];
45,153,053✔
1016

1017
      object_t *object = object_new(arena, class, kind);
45,153,053✔
1018

1019
      if (class->has_loc)
45,153,053✔
1020
         loc_read(&(object->loc), loc_ctx);
40,902,901✔
1021

1022
      imask_t has = class->has_map[object->kind];
45,153,053✔
1023
      for (int n = 0; has; has &= has - 1, n++) {
246,248,118✔
1024
         const uint64_t mask = has & -has;
201,095,065✔
1025
         item_t *item = &(object->items[n]);
201,095,065✔
1026
         if (ITEM_IDENT & mask)
201,095,065✔
1027
            item->ident = ident_read(ident_ctx);
40,402,209✔
1028
         else if (ITEM_OBJECT & mask)
160,692,856✔
1029
            item->object = object_read_ref(f, key_map);
81,768,137✔
1030
         else if (ITEM_OBJ_ARRAY & mask) {
78,924,719✔
1031
            const unsigned count = fbuf_get_uint(f);
18,876,172✔
1032
            if (count > 0) {
18,876,172✔
1033
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
12,418,909✔
1034
                                              count, sizeof(object_t *));
1035
               item->obj_array->count =
12,418,909✔
1036
                  item->obj_array->limit = count;
12,418,909✔
1037
               for (unsigned i = 0; i < count; i++) {
47,218,550✔
1038
                  object_t *o = object_read_ref(f, key_map);
34,799,641✔
1039
                  item->obj_array->items[i] = o;
34,799,641✔
1040
               }
1041
            }
1042
         }
1043
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
60,048,547✔
1044
            item->ival = fbuf_get_int(f);
58,506,277✔
1045
         else if (ITEM_DOUBLE & mask)
1,542,270✔
1046
            item->dval = read_double(f);
1,527,309✔
1047
         else if (ITEM_NUMBER & mask)
14,961✔
1048
            item->number = number_read(f);
14,961✔
1049
         else
1050
            should_not_reach_here();
1051
      }
1052
   }
1053

1054
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
41,220✔
1055

1056
   object_arena_freeze(arena);
41,220✔
1057
   return (object_t *)arena->base;
41,220✔
1058
}
1059

1060
unsigned object_next_generation(void)
91,536✔
1061
{
1062
   return next_generation++;
91,536✔
1063
}
1064

1065
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
4,406,456✔
1066
{
1067
   if (object == NULL)
4,406,456✔
1068
      return false;
1069

1070
   object_arena_t *arena = __object_arena(object);
3,640,609✔
1071
   if (arena->copygen != ctx->generation)
3,640,609✔
1072
      return false;
1073

1074
   bool marked = false;
2,330,508✔
1075
   if (object_marked_p(object, ctx->generation)) {
2,330,508✔
1076
      object_t *map = hash_get(ctx->copy_map, object);
542,116✔
1077
      if (map == object)
542,116✔
1078
         marked = true;   // Marked as root
1079
      else
1080
         return map != NULL;
525,530✔
1081
   }
1082

1083
   if (!marked && ctx->should_copy[object->tag] != NULL)
1,788,392✔
1084
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,696,982✔
1085

1086
   const object_class_t *class = classes[object->tag];
1,804,978✔
1087

1088
   object_t *copy = NULL;
1,804,978✔
1089
   if (marked) {
1,804,978✔
1090
      copy = object_new(global_arena, class, object->kind);
45,401✔
1091
      hash_put(ctx->copy_map, object, copy);
45,401✔
1092
   }
1093

1094
   imask_t has = class->has_map[object->kind];
1,804,978✔
1095
   for (int n = 0; has; has &= has - 1, n++) {
9,123,999✔
1096
      const uint64_t mask = has & -has;
7,319,021✔
1097
      item_t *item = &(object->items[n]);
7,319,021✔
1098
      if (ITEM_OBJECT & mask)
7,319,021✔
1099
         marked |= object_copy_mark(item->object, ctx);
3,236,229✔
1100
      else if (ITEM_OBJ_ARRAY & mask) {
4,082,792✔
1101
         if (item->obj_array != NULL) {
627,868✔
1102
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,550,369✔
1103
               object_t *o = item->obj_array->items[i];
1,152,985✔
1104
               marked |= object_copy_mark(o, ctx);
1,152,985✔
1105
            }
1106
         }
1107
      }
1108
   }
1109

1110
   if (marked && copy == NULL) {
1,804,978✔
1111
      copy = object_new(global_arena, class, object->kind);
290,371✔
1112
      hash_put(ctx->copy_map, object, copy);
290,371✔
1113
   }
1114

1115
   return marked;
1116
}
1117

1118
void object_copy_mark_root(object_t *object, object_copy_ctx_t *ctx)
17,053✔
1119
{
1120
   object_arena_t *arena = __object_arena(object);
17,053✔
1121

1122
   arena->copygen = ctx->generation;
17,053✔
1123

1124
   if (!object_marked_p(object, ctx->generation))
17,053✔
1125
      hash_put(ctx->copy_map, object, object);
16,586✔
1126
}
17,053✔
1127

1128
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
981,998✔
1129
{
1130
   if (object == NULL)
981,998✔
1131
      return NULL;
1132

1133
   object_t *map = hash_get(ctx->copy_map, object);
839,756✔
1134
   return map ?: object;
839,756✔
1135
}
1136

1137
void object_copy_begin(object_copy_ctx_t *ctx)
9,283✔
1138
{
1139
   ctx->copy_map = hash_new(1024);
9,283✔
1140

1141
   for (int i = 0; i < ctx->nroots; i++)
26,525✔
1142
      __object_arena(ctx->roots[i])->copygen = ctx->generation;
17,242✔
1143
}
9,283✔
1144

1145
void object_copy_finish(object_copy_ctx_t *ctx)
9,283✔
1146
{
1147
   for (int i = 0; i < ctx->nroots; i++)
26,525✔
1148
      (void)object_copy_mark(ctx->roots[i], ctx);
17,242✔
1149

1150
   unsigned ncopied = 0;
9,283✔
1151
   const void *key;
9,283✔
1152
   void *value;
9,283✔
1153
   for (hash_iter_t it = HASH_BEGIN;
9,283✔
1154
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
345,055✔
1155
      const object_t *object = key;
335,772✔
1156
      object_t *copy = value;
335,772✔
1157
      assert(copy != object);
335,772✔
1158
      ncopied++;
335,772✔
1159

1160
      copy->loc = object->loc;
335,772✔
1161

1162
      const object_class_t *class = classes[object->tag];
335,772✔
1163

1164
      imask_t has = class->has_map[object->kind];
335,772✔
1165
      for (int n = 0; has; has &= has - 1, n++) {
1,847,207✔
1166
         const uint64_t mask = has & -has;
1,511,435✔
1167
         const item_t *from = &(object->items[n]);
1,511,435✔
1168
         item_t *to = &(copy->items[n]);
1,511,435✔
1169

1170
         if (ITEM_IDENT & mask)
1,511,435✔
1171
            to->ident = from->ident;
276,314✔
1172
         else if (ITEM_OBJECT & mask) {
1,235,121✔
1173
            to->object = object_copy_map(from->object, ctx);
628,582✔
1174
            object_write_barrier(copy, to->object);
628,582✔
1175
         }
1176
         else if (ITEM_DOUBLE & mask)
606,539✔
1177
            to->dval = from->dval;
182✔
1178
         else if (ITEM_OBJ_ARRAY & mask) {
606,357✔
1179
            if (from->obj_array != NULL) {
223,109✔
1180
               // TODO: make a resize macro
1181
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
299,784✔
1182
                                            from->obj_array->count,
149,892✔
1183
                                            sizeof(object_t *));
1184
               to->obj_array->count =
149,892✔
1185
                  to->obj_array->limit = from->obj_array->count;
149,892✔
1186
               for (size_t i = 0; i < from->obj_array->count; i++) {
503,308✔
1187
                  object_t *o =
353,416✔
1188
                     object_copy_map(from->obj_array->items[i], ctx);
353,416✔
1189
                  to->obj_array->items[i] = o;
353,416✔
1190
                  object_write_barrier(copy, o);
353,416✔
1191
               }
1192
            }
1193
         }
1194
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
383,248✔
1195
            to->ival = from->ival;
383,248✔
1196
         else
1197
            should_not_reach_here();
1198
      }
1199
   }
1200

1201
   for (hash_iter_t it = HASH_BEGIN;
9,283✔
1202
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
345,055✔
1203
      object_t *copy = value;
335,772✔
1204
      if (ctx->callback[copy->tag] != NULL)
335,772✔
1205
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
317,841✔
1206
   }
1207

1208
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
9,283✔
UNCOV
1209
      debugf("copied %d objects into arena %s", ncopied,
×
1210
             istr(object_arena_name(global_arena)));
1211

1212
   for (unsigned i = 0; i < ctx->nroots; i++) {
26,525✔
1213
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
17,242✔
1214
      if (copy != NULL)
17,242✔
1215
         ctx->roots[i] = copy;
7,261✔
1216
   }
1217

1218
   hash_free(ctx->copy_map);
9,283✔
1219
}
9,283✔
1220

1221
size_t object_arena_default_size(void)
24,557✔
1222
{
1223
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
24,557✔
1224
}
1225

1226
object_arena_t *object_arena_new(size_t size, unsigned std)
65,781✔
1227
{
1228
   object_one_time_init();
65,781✔
1229

1230
   if (all_arenas.count == 0)
65,781✔
1231
      APUSH(all_arenas, NULL);   // Dummy null arena
8,800✔
1232

1233
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
65,781✔
1234
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
65,781✔
1235
   arena->alloc  = arena->base;
65,781✔
1236
   arena->limit  = (char *)arena->base + size;
65,781✔
1237
   arena->key    = all_arenas.count;
65,781✔
1238
   arena->source = OBJ_FRESH;
65,781✔
1239
   arena->std    = std;
65,781✔
1240

1241
   APUSH(all_arenas, arena);
65,781✔
1242

1243
   if (all_arenas.count == UINT16_MAX - 1)
65,781✔
1244
      fatal_trace("too many object arenas");
1245

1246
   return arena;
65,781✔
1247
}
1248

1249
void object_arena_freeze(object_arena_t *arena)
65,102✔
1250
{
1251
   ident_t name = object_arena_name(arena);
65,102✔
1252

1253
   if (arena->frozen)
65,102✔
1254
      fatal_trace("arena %s already frozen", istr(name));
1255

1256
   if (arena->source == OBJ_FRESH)
65,102✔
1257
      object_arena_gc(arena);
23,882✔
1258

1259
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
65,102✔
UNCOV
1260
      debugf("arena %s frozen (%d bytes)", istr(name),
×
UNCOV
1261
             (int)(arena->alloc - arena->base));
×
1262

1263
   chash_put(arena_lookup, name, arena);
65,102✔
1264

1265
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
65,102✔
1266
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
65,102✔
1267

1268
   if (next_page < arena->limit) {
65,102✔
1269
#if OBJECT_UNMAP_UNUSED
1270
      nvc_munmap(next_page, arena->limit - next_page);
1271
      arena->limit = next_page;
1272
#else
1273
      // This can be useful for debugging use-after-free
1274
      nvc_decommit(next_page, arena->limit - next_page);
23,882✔
1275
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
23,882✔
1276
#endif
1277
   }
1278

1279
   arena->frozen = true;
65,102✔
1280
}
65,102✔
1281

1282
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
62,747✔
1283
{
1284
   for (unsigned i = 0; i < arena->deps.count; i++)
169,771✔
1285
      (*fn)(arena_root(arena->deps.items[i]), context);
107,024✔
1286
}
62,747✔
1287

1288
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
17,792✔
1289
                              void *context)
1290
{
1291
   for (unsigned i = 0; i < arena->deps.count; i++) {
52,801✔
1292
      if (arena->deps.items[i]->obsolete)
35,009✔
1293
         (*fn)(arena_root(arena->deps.items[i]), context);
8✔
1294
   }
1295
}
17,792✔
1296

UNCOV
1297
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
×
1298
{
1299
   object_arena_t *arena = __object_arena(object);
×
UNCOV
1300
   assert(arena->frozen);
×
1301

1302
   *module = object_arena_name(arena);
×
1303

1304
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
×
UNCOV
1305
   if (arena->forward != NULL) {
×
1306
      assert(arena->forward[index] != UINT32_MAX);   // Was GC'd
×
1307
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
×
1308
   }
1309
   else
UNCOV
1310
      *offset = index;
×
UNCOV
1311
}
×
1312

1313
static object_arena_t *arena_by_name(ident_t module)
×
1314
{
1315
   if (global_arena != NULL && object_arena_name(global_arena) == module)
×
UNCOV
1316
      return global_arena;
×
1317

1318
   object_arena_t *a = chash_get(arena_lookup, module);
×
UNCOV
1319
   if (a != NULL)
×
1320
      return a;
1321

1322
#if defined DEBUG && !defined __SANITIZE_THREAD__
UNCOV
1323
   for (int i = 1; i < all_arenas.count; i++)
×
UNCOV
1324
      assert(module != object_arena_name(all_arenas.items[i]));
×
1325
#endif
1326

1327
   return NULL;
1328
}
1329

UNCOV
1330
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
×
1331
                            object_load_fn_t loader)
1332
{
UNCOV
1333
   object_arena_t *arena = arena_by_name(module);
×
1334

1335
   if (arena == NULL) {
×
UNCOV
1336
      object_t *droot = NULL;
×
1337
      if (loader) droot = (*loader)(module);
×
1338

1339
      if (droot == NULL)
×
UNCOV
1340
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1341

1342
      arena = __object_arena(droot);
×
1343
   }
1344

UNCOV
1345
   assert(arena->frozen);
×
1346

1347
   void *ptr = NULL;
×
UNCOV
1348
   if (arena->forward != NULL) {
×
1349
      // TODO: could do binary search here
1350
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
×
UNCOV
1351
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
×
1352
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
×
1353
            break;
×
1354
         }
1355
      }
UNCOV
1356
      assert(ptr != NULL);
×
1357
   }
1358
   else
UNCOV
1359
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
×
1360

1361
   if (ptr > arena->limit)
×
1362
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
1363

UNCOV
1364
   object_t *obj = ptr;
×
UNCOV
1365
   if (obj->tag >= OBJECT_TAG_COUNT)
×
1366
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1367
                  istr(module), offset);
UNCOV
1368
   else if (obj->arena != arena->key)
×
1369
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
1370
                  obj->arena, arena->key, istr(module), offset);
×
1371

1372
   return obj;
×
1373
}
1374

1375
void freeze_global_arena(void)
38,485✔
1376
{
1377
   object_one_time_init();
38,485✔
1378

1379
   if (global_arena != NULL) {
38,485✔
1380
      object_arena_freeze(global_arena);
23,882✔
1381
      global_arena = NULL;
23,882✔
1382
   }
1383
}
38,485✔
1384

1385
void make_new_arena(void)
24,557✔
1386
{
1387
   freeze_global_arena();
24,557✔
1388
   global_arena = object_arena_new(object_arena_default_size(), standard());
24,557✔
1389
}
24,557✔
1390

UNCOV
1391
void discard_global_arena(void)
×
1392
{
1393
   if (global_arena == NULL)
×
1394
      return;
1395

UNCOV
1396
   object_one_time_init();
×
1397

1398
   for (void *p = global_arena->base; p != global_arena->alloc; ) {
×
UNCOV
1399
      assert(p < global_arena->alloc);
×
1400
      object_t *object = p;
×
1401

1402
      gc_free_external(object);
×
1403

1404
      const object_class_t *class = classes[object->tag];
×
UNCOV
1405
      const size_t size =
×
1406
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
×
1407
      p = (char *)p + size;
×
1408
   }
1409

UNCOV
1410
   nvc_munmap(global_arena->base, global_arena->limit - global_arena->base);
×
1411

1412
   assert(all_arenas.items[all_arenas.count - 1] == global_arena);
×
UNCOV
1413
   APOP(all_arenas);
×
1414

1415
   free(global_arena);
×
UNCOV
1416
   global_arena = NULL;
×
1417
}
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