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

nickg / nvc / 14813902120

03 May 2025 07:22PM UTC coverage: 92.348% (+0.004%) from 92.344%
14813902120

push

github

nickg
Fix macOS debug symbol UUID check

69241 of 74978 relevant lines covered (92.35%)

415922.8 hits per line

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

91.88
/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            copyflag;
60
   bool            frozen;
61
   bool            obsolete;
62
} object_arena_t;
63

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

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

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

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

108
static inline bool object_in_arena_p(object_arena_t *arena, object_t *object)
10,707,636✔
109
{
110
   return (void *)object >= arena->base && (void *)object < arena->limit;
10,707,636✔
111
}
112

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

120
static ident_t object_arena_name(object_arena_t *arena)
295,548✔
121
{
122
   if (arena->alloc > arena->base) {
295,548✔
123
      object_t *root = arena_root(arena);
289,202✔
124

125
      const object_class_t *class = classes[root->tag];
289,202✔
126
      const imask_t has = class->has_map[root->kind];
289,202✔
127

128
      if (has & I_IDENT) {
289,202✔
129
         const int n = __builtin_popcountll(has & (I_IDENT - 1));
286,976✔
130
         return root->items[n].ident;
286,976✔
131
      }
132
   }
133

134
   return ident_new("???");
8,572✔
135
}
136

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

143
   if (count == 1)
177,733✔
144
      arena->mark_bits[first] = 0;
152,252✔
145
   else
146
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
25,481✔
147
}
177,733✔
148

149
static bool object_marked_p(object_t *object, generation_t generation)
11,233,601✔
150
{
151
   object_arena_t *arena = __object_arena(object);
11,233,601✔
152

153
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
11,233,601✔
154
   const uintptr_t word = bit / 64;
11,233,601✔
155

156
   if (unlikely(arena->mark_bits == NULL)) {
11,233,601✔
157
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
16,033✔
158
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
16,033✔
159
      arena->mark_bits = xmalloc(arena->mark_sz);
16,033✔
160
      arena->mark_bits[word] = 0;
16,033✔
161
      arena->mark_low = arena->mark_high = word;
16,033✔
162
      arena->generation = generation;
16,033✔
163
   }
164
   else if (arena->generation != generation) {
11,217,568✔
165
      arena->mark_bits[word] = 0;
58,327✔
166
      arena->mark_low = arena->mark_high = word;
58,327✔
167
      arena->generation = generation;
58,327✔
168
   }
169

170
   // Lazy zeroing of mark bits helps performance with large arenas
171
   if (word < arena->mark_low) {
11,233,601✔
172
      zero_mark_bits(arena, word, arena->mark_low - word);
13,959✔
173
      arena->mark_low = word;
13,959✔
174
   }
175
   else if (word > arena->mark_high) {
11,219,642✔
176
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
163,774✔
177
      arena->mark_high = word;
163,774✔
178
   }
179

180
   const uint64_t mask = UINT64_C(1) << (bit & 63);
11,233,601✔
181

182
   const bool marked = !!(arena->mark_bits[word] & mask);
11,233,601✔
183
   arena->mark_bits[word] |= mask;
11,233,601✔
184

185
   return marked;
11,233,601✔
186
}
187

188
void arena_set_checksum(object_arena_t *arena, uint32_t checksum)
21,387✔
189
{
190
   arena->checksum = checksum;
21,387✔
191
}
21,387✔
192

193
object_t *arena_root(object_arena_t *arena)
355,768✔
194
{
195
   return arena->root ?: (object_t *)arena->base;
355,768✔
196
}
197

198
bool arena_frozen(object_arena_t *arena)
56,643✔
199
{
200
   return arena->frozen;
56,643✔
201
}
202

203
uint32_t arena_flags(object_arena_t *arena)
41,081✔
204
{
205
   return arena->flags;
41,081✔
206
}
207

208
void arena_set_flags(object_arena_t *arena, uint32_t flags)
7,734✔
209
{
210
   arena->flags |= flags;
7,734✔
211
}
7,734✔
212

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

218
object_arena_t *object_arena(object_t *object)
221,842✔
219
{
220
   return __object_arena(object);
221,842✔
221
}
222

223
void __object_write_barrier(object_t *lhs, object_t *rhs)
4,934,659✔
224
{
225
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
4,934,659✔
226
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
4,934,659✔
227

228
   if (lhs_mask == rhs_mask || rhs == NULL)
4,934,659✔
229
      return;
230
   else if (lhs->arena == rhs->arena)
4,934,659✔
231
      return;
232

233
   object_arena_t *larena = __object_arena(lhs);
4,416,984✔
234
   object_arena_t *rarena = __object_arena(rhs);
4,416,984✔
235

236
   assert(!larena->frozen);
4,416,984✔
237
   assert(rarena->frozen);
4,416,984✔
238

239
   for (unsigned i = 0; i < larena->deps.count; i++) {
7,455,051✔
240
      if (larena->deps.items[i] == rarena)
7,420,402✔
241
         return;
242
   }
243

244
   APUSH(larena->deps, rarena);
34,649✔
245
}
246

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

253
   assert(item < ARRAY_LEN(item_text_map));
×
254

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

265
void obj_array_add(obj_array_t **a, object_t *o)
1,252,118✔
266
{
267
   if (*a == NULL) {
1,252,118✔
268
      const int defsz = 8;
444,404✔
269
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
444,404✔
270
      (*a)->count = 0;
444,404✔
271
      (*a)->limit = defsz;
444,404✔
272
   }
273
   else if ((*a)->count == (*a)->limit) {
807,714✔
274
      (*a)->limit *= 2;
30,873✔
275
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
30,873✔
276
                         (*a)->limit, sizeof(object_t *));
277
   }
278

279
   (*a)->items[(*a)->count++] = o;
1,252,118✔
280
}
1,252,118✔
281

282
void obj_array_free(obj_array_t **a)
89,606✔
283
{
284
   free(*a);
89,606✔
285
   *a = NULL;
89,606✔
286
}
89,606✔
287

288
void object_change_kind(const object_class_t *class, object_t *object, int kind)
23,742✔
289
{
23,742✔
290
   if (kind == object->kind)
23,742✔
291
      return;
×
292

293
   bool allow = false;
294
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
246,152✔
295
      allow = (class->change_allowed[i][0] == object->kind)
222,410✔
296
         && (class->change_allowed[i][1] == kind);
222,410✔
297
   }
298

299
   if (!allow)
23,742✔
300
      fatal_trace("cannot change %s kind %s to %s", class->name,
301
                  class->kind_text_map[object->kind],
×
302
                  class->kind_text_map[kind]);
×
303

304
   const imask_t old_has = class->has_map[object->kind];
23,742✔
305
   const imask_t new_has = class->has_map[kind];
23,742✔
306

307
   const int old_nitems = __builtin_popcountll(old_has);
23,742✔
308
   const int new_nitems = __builtin_popcountll(new_has);
23,742✔
309

310
   const int max_items = MAX(old_nitems, new_nitems);
23,742✔
311

312
   item_t tmp[max_items];
23,742✔
313
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
23,742✔
314

315
   int op = 0, np = 0;
23,742✔
316
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
961,543✔
317
      if ((old_has & mask) && (new_has & mask))
937,801✔
318
         object->items[np++] = tmp[op++];
86,131✔
319
      else if (old_has & mask) {
851,670✔
320
         if (ITEM_OBJ_ARRAY & mask)
146✔
321
            obj_array_free(&(tmp[op].obj_array));
116✔
322
         ++op;
146✔
323
      }
324
      else if (new_has & mask)
851,524✔
325
         memset(&(object->items[np++]), '\0', sizeof(item_t));
937,801✔
326
   }
327

328
   object->kind = kind;
23,742✔
329
}
330

331
static void object_init(object_class_t *class)
19,388✔
332
{
333
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
19,388✔
334

335
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
19,388✔
336

337
   assert(class->tag < ARRAY_LEN(classes));
19,388✔
338
   classes[class->tag] = class;
19,388✔
339

340
#ifdef DEBUG
341
   imask_t all_items = 0;
19,388✔
342
#endif
343

344
   for (int i = 0; i < class->last_kind; i++) {
1,105,116✔
345
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,085,728✔
346
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,085,728✔
347
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,085,728✔
348

349
      format_digest += knuth_hash(class->has_map[i] >> 32);
1,085,728✔
350
      format_digest += knuth_hash(class->has_map[i]);
1,085,728✔
351
   }
352

353
   bool changed = false;
29,082✔
354
   do {
29,082✔
355
      changed = false;
29,082✔
356
      for (int i = 0; i < class->last_kind; i++) {
2,268,396✔
357
         size_t max_size = class->object_size[i];
2,239,314✔
358
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
29,925,378✔
359
            if (class->change_allowed[j][0] == i)
27,686,064✔
360
               max_size = MAX(max_size,
232,656✔
361
                              class->object_size[class->change_allowed[j][1]]);
362
            else if (class->change_allowed[j][1] == i)
27,453,408✔
363
               max_size = MAX(max_size,
232,656✔
364
                              class->object_size[class->change_allowed[j][0]]);
365
         }
366

367
         if (max_size != class->object_size[i]) {
2,239,314✔
368
            class->object_size[i] = max_size;
58,164✔
369
            changed = true;
58,164✔
370
         }
371
      }
372
   } while (changed);
29,082✔
373

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

381
   const imask_t known_types =
19,388✔
382
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
383
      | ITEM_DOUBLE | ITEM_TEXT | ITEM_NUMBER;
384

385
   const imask_t missing = all_items & ~known_types;
19,388✔
386
   if (missing != 0) {
19,388✔
387
      int item;
388
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
389
         ;
390

391
      assert(item < ARRAY_LEN(item_text_map));
×
392
      fatal_trace("item %s does not have a type", item_text_map[item]);
393
   }
394
#endif
395
}
19,388✔
396

397
static void check_frozen_object_fault(int sig, void *addr,
×
398
                                      struct cpu_state *cpu, void *context)
399
{
400
   if (sig != SIGSEGV)
×
401
      return;
402

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)
16,220,982✔
418
{
419
   INIT_ONCE({
16,220,982✔
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
}
16,220,982✔
443

444
static bool is_gc_root(const object_class_t *class, int kind)
2,166,942✔
445
{
446
   for (int j = 0; j < class->gc_num_roots; j++) {
20,313,553✔
447
      if (class->gc_roots[j] == kind)
18,194,051✔
448
         return true;
449
   }
450

451
   return false;
452
}
453

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

460
   object_one_time_init();
16,186,385✔
461

462
   if (arena == NULL)
16,186,385✔
463
      arena = global_arena;
1,878,243✔
464

465
   if (unlikely(arena == NULL))
16,186,385✔
466
      fatal_trace("allocating object without active arena");
467

468
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
16,186,385✔
469

470
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
16,186,385✔
471

472
   if (unlikely(arena->limit - arena->alloc < size)) {
16,186,385✔
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)));
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());
480
      diag_emit(d);
×
481
      fatal_exit(EXIT_FAILURE);
×
482
   }
483

484
   object_t *object = arena->alloc;
16,186,385✔
485
   arena->alloc = (char *)arena->alloc + size;
16,186,385✔
486

487
   if (arena->root == NULL && is_gc_root(class, kind))
16,186,385✔
488
      arena->root = object;
26,928✔
489

490
   memset(object, '\0', size);
16,186,385✔
491

492
   object->kind  = kind;
16,186,385✔
493
   object->tag   = class->tag;
16,186,385✔
494
   object->arena = arena->key;
16,186,385✔
495
   object->loc   = LOC_INVALID;
16,186,385✔
496

497
   return object;
16,186,385✔
498
}
499

500
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,764,217✔
501
                              generation_t generation)
502
{
503
   if (object == NULL)
4,764,217✔
504
      return;
505
   else if (!object_in_arena_p(arena, object))
3,959,934✔
506
      return;
507
   else if (object_marked_p(object, generation))
2,370,479✔
508
      return;
509

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

512
   const imask_t has = class->has_map[object->kind];
1,792,471✔
513
   const int nitems = __builtin_popcountll(has);
1,792,471✔
514
   imask_t mask = 1;
1,792,471✔
515
   for (int i = 0; i < nitems; mask <<= 1) {
73,487,302✔
516
      if (has & mask) {
71,694,831✔
517
         item_t *item = &(object->items[i++]);
7,853,894✔
518
         if (ITEM_OBJECT & mask)
7,853,894✔
519
            gc_mark_from_root(item->object, arena, generation);
3,375,054✔
520
         else if (ITEM_OBJ_ARRAY & mask) {
4,478,840✔
521
            if (item->obj_array != NULL) {
785,139✔
522
               for (unsigned j = 0; j < item->obj_array->count; j++)
1,872,240✔
523
                  gc_mark_from_root(item->obj_array->items[j], arena,
1,368,651✔
524
                                    generation);
525
            }
526
         }
527
      }
528
   }
529
}
530

531
static void gc_free_external(object_t *object)
347,542✔
532
{
533
   const object_class_t *class = classes[object->tag];
347,542✔
534
   const imask_t has = class->has_map[object->kind];
347,542✔
535
   const int nitems = __builtin_popcountll(has);
347,542✔
536
   imask_t mask = 1;
347,542✔
537
   for (int i = 0; i < nitems; mask <<= 1) {
15,387,384✔
538
      if (has & mask) {
15,039,842✔
539
         item_t *item = &(object->items[i++]);
1,483,257✔
540
         if (ITEM_OBJ_ARRAY & mask)
1,483,257✔
541
            obj_array_free(&(item->obj_array));
88,520✔
542
         else if (ITEM_TEXT & mask)
1,394,737✔
543
            free(item->text);
×
544
         else if (ITEM_NUMBER & mask)
1,394,737✔
545
            number_free(&item->number);
×
546
      }
547
   }
548
}
347,542✔
549

550
static void object_arena_gc(object_arena_t *arena)
15,610✔
551
{
552
   const generation_t generation = object_next_generation();
15,610✔
553
   const uint64_t start_ticks = get_timestamp_us();
15,610✔
554

555
   // Mark
556
   for (void *p = arena->base; p != arena->alloc; ) {
2,155,623✔
557
      assert(p < arena->alloc);
2,140,013✔
558
      object_t *object = p;
2,140,013✔
559

560
      const object_class_t *class = classes[object->tag];
2,140,013✔
561

562
      if (is_gc_root(class, object->kind))
2,140,013✔
563
         gc_mark_from_root(object, arena, generation);
20,512✔
564

565
      const size_t size =
2,140,013✔
566
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,140,013✔
567
      p = (char *)p + size;
2,140,013✔
568
   }
569

570
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
15,610✔
571
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
15,610✔
572

573
   // Must initialise here for the search in object_from_locus
574
   memset(forward, 0xff, fwdsz * sizeof(uint32_t));
15,610✔
575

576
   // Calculate forwarding addresses
577
   unsigned woffset = 0, live = 0, dead = 0;
15,610✔
578
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,155,623✔
579
      assert(rptr < arena->alloc);
2,140,013✔
580
      object_t *object = rptr;
2,140,013✔
581

582
      const object_class_t *class = classes[object->tag];
2,140,013✔
583

584
      const size_t size =
2,140,013✔
585
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,140,013✔
586

587
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,140,013✔
588
      if (!object_marked_p(object, generation)) {
2,140,013✔
589
         forward[index] = UINT32_MAX;
347,542✔
590
         gc_free_external(object);
347,542✔
591
         dead++;
347,542✔
592
      }
593
      else {
594
         forward[index] = woffset;
1,792,471✔
595
         woffset += size;
1,792,471✔
596
         live++;
1,792,471✔
597
      }
598

599
      rptr = (char *)rptr + size;
2,140,013✔
600
   }
601

602
   if (woffset == 0)
15,610✔
603
      fatal_trace("GC removed all objects from arena %s",
604
                  istr(object_arena_name(arena)));
605

606
   arena->forward = forward;
15,610✔
607
   arena->live_bytes = woffset;
15,610✔
608

609
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
15,610✔
610
      const int ticks = get_timestamp_us() - start_ticks;
×
611
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
612
             istr(object_arena_name(arena)), dead, live, ticks);
613
   }
614
}
15,610✔
615

616
void object_visit(object_t *object, object_visit_ctx_t *ctx)
722,531✔
617
{
618
   // If `deep' then will follow links above the tree originally passed
619
   // to tree_visit - e.g. following references back to their declarations
620

621
   if (object == NULL)
722,531✔
622
      return;
623
   else if (object_marked_p(object, ctx->generation))
540,780✔
624
      return;
625

626
   const object_class_t *class = classes[object->tag];
516,016✔
627

628
   const bool visit =
1,032,032✔
629
      (object->tag == ctx->tag && object->kind == ctx->kind)
516,016✔
630
      || ctx->kind == class->last_kind;
1,029,961✔
631

632
   if (visit && ctx->preorder != NULL)
516,016✔
633
      (*ctx->preorder)(object, ctx->context);
×
634

635
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
516,016✔
636

637
   const imask_t has = class->has_map[object->kind];
516,016✔
638
   const int nitems = __builtin_popcountll(has);
516,016✔
639
   imask_t mask = 1;
516,016✔
640
   for (int i = 0; i < nitems; mask <<= 1) {
21,677,701✔
641
      if (has & mask & deep_mask) {
21,161,685✔
642
         item_t *item = &(object->items[i]);
1,567,604✔
643
         if (ITEM_OBJECT & mask)
1,567,604✔
644
            object_visit(item->object, ctx);
369,175✔
645
         else if (ITEM_OBJ_ARRAY & mask) {
1,198,429✔
646
            if (item->obj_array != NULL) {
167,126✔
647
               for (unsigned j = 0; j < item->obj_array->count; j++)
444,620✔
648
                  object_visit(item->obj_array->items[j], ctx);
341,845✔
649
            }
650
         }
651
      }
652

653
      if (has & mask)
21,161,685✔
654
         i++;
2,190,892✔
655
   }
656

657
   if (visit) {
516,016✔
658
      if (ctx->postorder != NULL)
504,413✔
659
         (*ctx->postorder)(object, ctx->context);
504,413✔
660
      ctx->count++;
504,413✔
661
   }
662
}
663

664
static object_t *object_rewrite_iter(object_t *object,
3,514,133✔
665
                                     object_rewrite_ctx_t *ctx)
666
{
667
   // The callback may return a new object or a pointer to an existing
668
   // object in the same arena that that needs to be rewritten so
669
   // iterate rewriting until we reach a fixed point
670
   object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,514,133✔
671
   if (new == object)
3,514,129✔
672
      return new;
673
   else
674
      return object_rewrite(new, ctx);
70,374✔
675
}
676

677
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
8,095,281✔
678
{
679
   if (object == NULL)
8,095,281✔
680
      return NULL;
681

682
   if (!object_in_arena_p(ctx->arena, object))
6,747,702✔
683
      return object;
684

685
   const ptrdiff_t index =
4,477,161✔
686
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,477,161✔
687

688
   // New objects can be allocated while rewrite is in progress so we
689
   // need to check if the index is greater than the current cache size
690
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
4,477,161✔
691
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
71,513✔
692
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
71,513✔
693
                                  ctx->cache_sz);
694
   }
695

696
   if (object_marked_p(object, ctx->generation)) {
4,477,161✔
697
      if (ctx->cache[index] == (object_t *)-1) {
745,464✔
698
         // Found a circular reference: eagerly rewrite the object now
699
         // and break the cycle
700
         if (ctx->post_fn[object->tag] != NULL) {
1,095✔
701
            if (ctx->pre_fn[object->tag] != NULL)
38✔
702
               (*ctx->pre_fn[object->tag])(object, ctx->context);
×
703
            object_t *new = object_rewrite_iter(object, ctx);
38✔
704
            object_write_barrier(object, new);
38✔
705
            return (ctx->cache[index] = new);
38✔
706
         }
707
         else
708
            return (ctx->cache[index] = object);
1,057✔
709
      }
710
      else {
711
         // Already rewritten this tree so return the cached version
712
         return ctx->cache[index];
713
      }
714
   }
715

716
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,731,697✔
717

718
   if (ctx->pre_fn[object->tag] != NULL)
3,731,697✔
719
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
720

721
   const imask_t skip_mask =
3,731,697✔
722
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER
723
      | ITEM_TEXT | ITEM_IDENT;
724

725
   const object_class_t *class = classes[object->tag];
3,731,697✔
726

727
   const imask_t has = class->has_map[object->kind];
3,731,697✔
728
   const int nitems = __builtin_popcountll(has);
3,731,697✔
729
   imask_t mask = 1;
3,731,697✔
730
   for (int n = 0; n < nitems; mask <<= 1) {
154,492,914✔
731
      if (has & mask & ~skip_mask) {
150,761,225✔
732
         if (ITEM_OBJECT & mask) {
6,827,916✔
733
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,370,948✔
734
            object->items[n].object = o;
5,370,944✔
735
            object_write_barrier(object, o);
5,370,944✔
736
         }
737
         else if (ITEM_OBJ_ARRAY & mask) {
1,456,968✔
738
            obj_array_t **a = &(object->items[n].obj_array);
1,456,968✔
739
            if (object->items[n].obj_array != NULL) {
1,456,968✔
740
               // The callback may add new items to the array so the
741
               // array pointer cannot be cached between iterations
742
               unsigned wptr = 0;
743
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,593,910✔
744
                  object_t *o = object->items[n].obj_array->items[i];
2,625,799✔
745
                  if ((o = object_rewrite(o, ctx))) {
2,625,799✔
746
                     object_write_barrier(object, o);
2,620,726✔
747
                     object->items[n].obj_array->items[wptr++] = o;
2,620,726✔
748
                  }
749
               }
750

751
               if (wptr == 0)
968,111✔
752
                  obj_array_free(a);
970✔
753
               else
754
                  (*a)->count = wptr;
967,141✔
755
            }
756
         }
757
         else
758
            should_not_reach_here();
759
      }
760

761
      if (has & mask)
150,761,217✔
762
         n++;
16,308,918✔
763
   }
764

765
   if (ctx->cache[index] != (object_t *)-1) {
3,731,689✔
766
      // The cache was already updated due to a circular reference
767
      return ctx->cache[index];
768
   }
769
   else if (ctx->post_fn[object->tag] != NULL) {
3,730,594✔
770
      object_t *new = object_rewrite_iter(object, ctx);
3,514,095✔
771
      object_write_barrier(object, new);
3,514,091✔
772
      return (ctx->cache[index] = new);
3,514,091✔
773
   }
774
   else
775
      return (ctx->cache[index] = object);
216,499✔
776
}
777

778
static void object_write_ref(object_t *object, fbuf_t *f)
4,218,079✔
779
{
780
   if (object == NULL)
4,218,079✔
781
      fbuf_put_uint(f, 0);
723,998✔
782
   else {
783
      object_arena_t *arena = __object_arena(object);
3,494,081✔
784
      assert(arena->key != 0);
3,494,081✔
785
      fbuf_put_uint(f, arena->key);
3,494,081✔
786

787
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,494,081✔
788
      if (arena->forward != NULL)
3,494,081✔
789
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,175,583✔
790
      else
791
         fbuf_put_uint(f, index);
1,318,498✔
792
   }
793
}
4,218,079✔
794

795
void object_write(object_t *root, fbuf_t *f, ident_wr_ctx_t ident_ctx,
10,660✔
796
                  loc_wr_ctx_t *loc_ctx)
797
{
798
   object_arena_t *arena = __object_arena(root);
10,660✔
799
   if (root != arena_root(arena))
10,660✔
800
      fatal_trace("must write root object first");
801
   else if (arena->source == OBJ_DISK)
10,660✔
802
      fatal_trace("writing arena %s originally read from disk",
803
                  istr(object_arena_name(arena)));
804
   else if (!arena->frozen)
10,660✔
805
      fatal_trace("arena %s must be frozen before writing to disk",
806
                  istr(object_arena_name(arena)));
807
   else if (arena->obsolete)
10,660✔
808
      fatal_trace("writing obsolete arena %s", istr(object_arena_name(arena)));
809

810
   write_u32(format_digest, f);
10,660✔
811
   fbuf_put_uint(f, standard());
10,660✔
812
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
10,660✔
813
   fbuf_put_uint(f, arena->flags);
10,660✔
814
   fbuf_put_uint(f, arena->key);
10,660✔
815
   ident_write(object_arena_name(arena), ident_ctx);
10,660✔
816

817
   arena_key_t max_key = arena->key;
10,660✔
818
   for (unsigned i = 0; i < arena->deps.count; i++)
32,360✔
819
      max_key = MAX(max_key, arena->deps.items[i]->key);
21,700✔
820
   fbuf_put_uint(f, max_key);
10,660✔
821

822
   fbuf_put_uint(f, arena->deps.count);
10,660✔
823
   for (unsigned i = 0; i < arena->deps.count; i++) {
32,360✔
824
      fbuf_put_uint(f, arena->deps.items[i]->key);
21,700✔
825
      fbuf_put_uint(f, arena->deps.items[i]->std);
21,700✔
826
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
21,700✔
827
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
21,700✔
828
   }
829

830
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
1,877,396✔
831
      assert(p < arena->alloc);
1,866,736✔
832

833
      object_t *object = p;
1,866,736✔
834
      object_class_t *class = classes[object->tag];
1,866,736✔
835

836
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,866,736✔
837

838
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,866,736✔
839
      if (arena->forward[index] == UINT32_MAX)
1,866,736✔
840
         continue;   // Dead object
252,637✔
841

842
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,614,099✔
843
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,614,099✔
844

845
      if (class->has_loc)
1,614,099✔
846
         loc_write(&object->loc, loc_ctx);
1,514,576✔
847

848
      const imask_t has = class->has_map[object->kind];
1,614,099✔
849
      const int nitems = __builtin_popcountll(has);
1,614,099✔
850
      imask_t mask = 1;
1,614,099✔
851
      for (int n = 0; n < nitems; mask <<= 1) {
66,935,653✔
852
         if (has & mask) {
65,321,554✔
853
            item_t *item = &(object->items[n]);
7,026,349✔
854
            if (ITEM_IDENT & mask)
7,026,349✔
855
               ident_write(item->ident, ident_ctx);
1,309,037✔
856
            else if (ITEM_OBJECT & mask)
5,717,312✔
857
               object_write_ref(item->object, f);
3,087,708✔
858
            else if (ITEM_OBJ_ARRAY & mask) {
2,629,604✔
859
               if (item->obj_array != NULL) {
611,474✔
860
                  const unsigned count = item->obj_array->count;
399,521✔
861
                  fbuf_put_uint(f, count);
399,521✔
862
                  for (unsigned i = 0; i < count; i++)
1,529,892✔
863
                     object_write_ref(item->obj_array->items[i], f);
1,130,371✔
864
               }
865
               else
866
                  fbuf_put_uint(f, 0);
211,953✔
867
            }
868
            else if (ITEM_INT64 & mask)
2,018,130✔
869
               fbuf_put_int(f, item->ival);
360,044✔
870
            else if (ITEM_INT32 & mask)
1,658,086✔
871
               fbuf_put_int(f, item->ival);
1,550,776✔
872
            else if (ITEM_DOUBLE & mask)
107,310✔
873
               write_double(item->dval, f);
106,299✔
874
            else if (ITEM_TEXT & mask) {
1,011✔
875
               const size_t len = strlen(item->text);
360✔
876
               fbuf_put_uint(f, len);
360✔
877
               write_raw(item->text, len, f);
360✔
878
            }
879
            else if (ITEM_NUMBER & mask)
651✔
880
               number_write(item->number, f);
651✔
881
            else
882
               should_not_reach_here();
883
            n++;
7,026,349✔
884
         }
885
      }
886
   }
887

888
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
10,660✔
889
}
10,660✔
890

891
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
36,103,648✔
892
{
893
   arena_key_t key = fbuf_get_uint(f);
36,103,648✔
894
   if (key == 0)
36,103,648✔
895
      return NULL;
896

897
   arena_key_t mapped = key_map[key];
30,672,470✔
898
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
30,672,470✔
899

900
   if (unlikely(mapped == 0))
30,672,470✔
901
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
902

903
   assert(mapped < all_arenas.count);
30,672,470✔
904
   assert(mapped > 0);
30,672,470✔
905

906
   object_arena_t *arena = all_arenas.items[mapped];
30,672,470✔
907
   assert(!arena->frozen || offset < arena->alloc - arena->base);
30,672,470✔
908

909
   return (object_t *)((char *)arena->base + offset);
30,672,470✔
910
}
911

912
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
10,736✔
913
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
914
{
915
   object_one_time_init();
10,736✔
916

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

924
   const vhdl_standard_t std = fbuf_get_uint(f);
10,736✔
925

926
   // If this is the first design unit we've loaded then allow it to set
927
   // the default standard
928
   if (all_arenas.count == 0)
10,736✔
929
      set_default_standard(std);
459✔
930

931
   if (std > standard())
10,736✔
932
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
933
            "is more recent that the currently selected standard %s",
934
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
935

936
   const unsigned size = fbuf_get_uint(f);
10,730✔
937
   if (size & OBJECT_PAGE_MASK)
10,730✔
938
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
939

940
   object_arena_t *arena = object_arena_new(size, std);
10,730✔
941
   arena->source = OBJ_DISK;
10,730✔
942
   arena->flags  = fbuf_get_uint(f);
10,730✔
943

944
   arena_key_t key = fbuf_get_uint(f);
10,730✔
945
   ident_t name = ident_read(ident_ctx);
10,730✔
946

947
   arena_key_t max_key = fbuf_get_uint(f);
10,730✔
948

949
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
10,730✔
950
   key_map[key] = arena->key;
10,730✔
951

952
   const int ndeps = fbuf_get_uint(f);
10,730✔
953
   for (int i = 0; i < ndeps; i++) {
21,598✔
954
      arena_key_t dkey = fbuf_get_uint(f);
10,871✔
955
      vhdl_standard_t dstd = fbuf_get_uint(f);
10,871✔
956
      uint32_t checksum = fbuf_get_uint(f);
10,871✔
957
      ident_t dep = ident_read(ident_ctx);
10,871✔
958

959
      object_arena_t *a = NULL;
10,871✔
960
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
48,435✔
961
         if (dep == object_arena_name(all_arenas.items[j]))
37,564✔
962
            a = all_arenas.items[j];
8,201✔
963
      }
964

965
      if (a == NULL) {
10,871✔
966
         object_t *droot = NULL;
2,670✔
967
         if (loader_fn) droot = (*loader_fn)(dep);
2,670✔
968

969
         if (droot == NULL)
2,670✔
970
            fatal("%s depends on %s which cannot be found",
×
971
                  fbuf_file_name(f), istr(dep));
972

973
         a = __object_arena(droot);
2,670✔
974
      }
975

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

992
      APUSH(arena->deps, a);
10,868✔
993

994
      assert(dkey <= max_key);
10,868✔
995
      key_map[dkey] = a->key;
10,868✔
996
   }
997

998
   for (;;) {
13,991,274✔
999
      const uint64_t hdr = fbuf_get_uint(f);
13,991,274✔
1000
      if (hdr == UINT16_MAX) break;
13,991,274✔
1001

1002
      const unsigned tag = hdr & 3;
13,980,547✔
1003
      const unsigned kind = hdr >> 2;
13,980,547✔
1004

1005
      assert(tag < OBJECT_TAG_COUNT);
13,980,547✔
1006

1007
      const object_class_t *class = classes[tag];
13,980,547✔
1008

1009
      object_t *object = object_new(arena, class, kind);
13,980,547✔
1010

1011
      if (class->has_loc)
13,980,547✔
1012
         loc_read(&(object->loc), loc_ctx);
12,229,605✔
1013

1014
      const imask_t has = class->has_map[object->kind];
13,980,547✔
1015
      const int nitems = __builtin_popcountll(has);
13,980,547✔
1016
      imask_t mask = 1;
13,980,547✔
1017
      for (int n = 0; n < nitems; mask <<= 1) {
589,760,037✔
1018
         if (has & mask) {
575,779,490✔
1019
            item_t *item = &(object->items[n]);
64,085,733✔
1020
            if (ITEM_IDENT & mask)
64,085,733✔
1021
               item->ident = ident_read(ident_ctx);
13,645,935✔
1022
            else if (ITEM_OBJECT & mask)
50,439,798✔
1023
               item->object = object_read_ref(f, key_map);
23,850,504✔
1024
            else if (ITEM_OBJ_ARRAY & mask) {
26,589,294✔
1025
               const unsigned count = fbuf_get_uint(f);
6,481,653✔
1026
               if (count > 0) {
6,481,653✔
1027
                  item->obj_array = xmalloc_flex(sizeof(obj_array_t),
4,301,388✔
1028
                                                 count, sizeof(object_t *));
1029
                  item->obj_array->count =
4,301,388✔
1030
                     item->obj_array->limit = count;
4,301,388✔
1031
                  for (unsigned i = 0; i < count; i++) {
16,554,532✔
1032
                     object_t *o = object_read_ref(f, key_map);
12,253,144✔
1033
                     item->obj_array->items[i] = o;
12,253,144✔
1034
                  }
1035
               }
1036
            }
1037
            else if (ITEM_INT64 & mask)
20,107,641✔
1038
               item->ival = fbuf_get_int(f);
2,844,682✔
1039
            else if (ITEM_INT32 & mask)
17,262,959✔
1040
               item->ival = fbuf_get_int(f);
16,891,598✔
1041
            else if (ITEM_DOUBLE & mask)
371,361✔
1042
               item->dval = read_double(f);
371,361✔
1043
            else if (ITEM_TEXT & mask) {
×
1044
               const size_t len = fbuf_get_uint(f);
×
1045
               item->text = xmalloc(len + 1);
×
1046
               read_raw(item->text, len, f);
×
1047
               item->text[len] = '\0';
×
1048
            }
1049
            else if (ITEM_NUMBER & mask)
×
1050
               item->number = number_read(f);
×
1051
            else
1052
               should_not_reach_here();
1053
            n++;
64,085,733✔
1054
         }
1055
      }
1056
   }
1057

1058
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
10,727✔
1059

1060
   object_arena_freeze(arena);
10,727✔
1061
   return (object_t *)arena->base;
10,727✔
1062
}
1063

1064
unsigned object_next_generation(void)
63,225✔
1065
{
1066
   return next_generation++;
63,225✔
1067
}
1068

1069
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,456,592✔
1070
{
1071
   if (object == NULL)
3,456,592✔
1072
      return false;
1073

1074
   object_arena_t *arena = __object_arena(object);
2,828,542✔
1075
   if (arena->copygen != ctx->generation || !arena->copyflag)
2,828,542✔
1076
      return false;
1077

1078
   if (ctx->copy_map == NULL)
1,705,168✔
1079
      ctx->copy_map = hash_new(1024);
7,944✔
1080

1081
   if (object_marked_p(object, ctx->generation))
1,705,168✔
1082
      return hash_get(ctx->copy_map, object) != NULL;
378,105✔
1083

1084
   const object_class_t *class = classes[object->tag];
1,327,063✔
1085

1086
   bool marked = false;
1,327,063✔
1087
   if (ctx->should_copy[object->tag] != NULL)
1,327,063✔
1088
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,323,710✔
1089

1090
   object_t *copy = NULL;
1,323,710✔
1091
   if (marked) {
1,323,710✔
1092
      copy = object_new(global_arena, class, object->kind);
56,237✔
1093
      hash_put(ctx->copy_map, object, copy);
56,237✔
1094
   }
1095

1096
   const imask_t has = class->has_map[object->kind];
1,327,063✔
1097
   const int nitems = __builtin_popcountll(has);
1,327,063✔
1098
   imask_t mask = 1;
1,327,063✔
1099
   for (int n = 0; n < nitems; mask <<= 1) {
54,861,453✔
1100
      if (has & mask) {
53,534,390✔
1101
         item_t *item = &(object->items[n]);
5,752,473✔
1102
         if (ITEM_OBJECT & mask)
5,752,473✔
1103
            marked |= object_copy_mark(item->object, ctx);
2,549,551✔
1104
         else if (ITEM_OBJ_ARRAY & mask) {
3,202,922✔
1105
            if (item->obj_array != NULL) {
483,880✔
1106
               for (unsigned i = 0; i < item->obj_array->count; i++) {
1,208,524✔
1107
                  object_t *o = item->obj_array->items[i];
892,468✔
1108
                  marked |= object_copy_mark(o, ctx);
892,468✔
1109
               }
1110
            }
1111
         }
1112
         n++;
5,752,473✔
1113
      }
1114
   }
1115

1116
   if (marked && copy == NULL) {
1,327,063✔
1117
      copy = object_new(global_arena, class, object->kind);
271,358✔
1118
      hash_put(ctx->copy_map, object, copy);
271,358✔
1119
   }
1120

1121
   return marked;
1122
}
1123

1124
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
941,965✔
1125
{
1126
   if (object == NULL)
941,965✔
1127
      return NULL;
1128

1129
   object_t *map = hash_get(ctx->copy_map, object);
795,331✔
1130
   return map ?: object;
795,331✔
1131
}
1132

1133
static bool object_copy_root_closure(object_arena_t *a, object_copy_ctx_t *ctx)
77,299✔
1134
{
1135
   bool include = false;
77,299✔
1136

1137
   if (a->copygen == ctx->generation)
77,299✔
1138
      return a->copyflag;
41,132✔
1139

1140
   for (int i = 0; i < ctx->nroots; i++)
98,904✔
1141
      include |= (__object_arena(ctx->roots[i]) == a);
62,737✔
1142

1143
   for (int i = 0; i < a->deps.count; i++)
98,893✔
1144
      include |= object_copy_root_closure(a->deps.items[i], ctx);
62,726✔
1145

1146
   a->copygen  = ctx->generation;
36,167✔
1147
   a->copyflag = include;
36,167✔
1148

1149
   return include;
36,167✔
1150
}
1151

1152
void object_copy(object_copy_ctx_t *ctx)
7,944✔
1153
{
1154
   for (int i = 0; i < ctx->nroots; i++) {
22,517✔
1155
      object_arena_t *a = __object_arena(ctx->roots[i]);
14,573✔
1156
      object_copy_root_closure(a, ctx);
14,573✔
1157
   }
1158

1159
   for (int i = 0; i < ctx->nroots; i++)
22,517✔
1160
      (void)object_copy_mark(ctx->roots[i], ctx);
14,573✔
1161

1162
   unsigned ncopied = 0;
7,944✔
1163
   const void *key;
7,944✔
1164
   void *value;
7,944✔
1165
   for (hash_iter_t it = HASH_BEGIN;
7,944✔
1166
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
335,539✔
1167
      const object_t *object = key;
327,595✔
1168
      object_t *copy = value;
327,595✔
1169
      ncopied++;
327,595✔
1170

1171
      copy->loc = object->loc;
327,595✔
1172

1173
      const object_class_t *class = classes[object->tag];
327,595✔
1174

1175
      const imask_t has = class->has_map[object->kind];
327,595✔
1176
      const int nitems = __builtin_popcountll(has);
327,595✔
1177
      imask_t mask = 1;
327,595✔
1178
      for (int n = 0; n < nitems; mask <<= 1) {
13,341,643✔
1179
         if (has & mask) {
13,014,048✔
1180
            const item_t *from = &(object->items[n]);
1,470,846✔
1181
            item_t *to = &(copy->items[n]);
1,470,846✔
1182

1183
            if (ITEM_IDENT & mask)
1,470,846✔
1184
               to->ident = from->ident;
254,524✔
1185
            else if (ITEM_OBJECT & mask) {
1,216,322✔
1186
               to->object = object_copy_map(from->object, ctx);
627,092✔
1187
               object_write_barrier(copy, to->object);
627,092✔
1188
            }
1189
            else if (ITEM_DOUBLE & mask)
589,230✔
1190
               to->dval = from->dval;
115✔
1191
            else if (ITEM_OBJ_ARRAY & mask) {
589,115✔
1192
               if (from->obj_array != NULL) {
205,588✔
1193
                  // TODO: make a resize macro
1194
                  to->obj_array = xmalloc_flex(sizeof(obj_array_t),
291,928✔
1195
                                               from->obj_array->count,
145,964✔
1196
                                               sizeof(object_t *));
1197
                  to->obj_array->count =
145,964✔
1198
                     to->obj_array->limit = from->obj_array->count;
145,964✔
1199
                  for (size_t i = 0; i < from->obj_array->count; i++) {
460,837✔
1200
                     object_t *o =
314,873✔
1201
                        object_copy_map(from->obj_array->items[i], ctx);
314,873✔
1202
                     to->obj_array->items[i] = o;
314,873✔
1203
                     object_write_barrier(copy, o);
314,873✔
1204
                  }
1205
               }
1206
            }
1207
            else if ((ITEM_INT64 & mask) || (ITEM_INT32 & mask))
383,527✔
1208
               to->ival = from->ival;
383,527✔
1209
            else
1210
               should_not_reach_here();
1211
            n++;
1,470,846✔
1212
         }
1213
      }
1214
   }
1215

1216
   for (hash_iter_t it = HASH_BEGIN;
7,944✔
1217
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
335,539✔
1218
      object_t *copy = value;
327,595✔
1219
      if (ctx->callback[copy->tag] != NULL)
327,595✔
1220
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
327,583✔
1221
   }
1222

1223
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
7,944✔
1224
      debugf("copied %d objects into arena %s", ncopied,
×
1225
             istr(object_arena_name(global_arena)));
1226

1227
   for (unsigned i = 0; i < ctx->nroots; i++) {
22,517✔
1228
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
14,573✔
1229
      if (copy != NULL)
14,573✔
1230
         ctx->roots[i] = copy;
6,438✔
1231
   }
1232

1233
   hash_free(ctx->copy_map);
7,944✔
1234
}
7,944✔
1235

1236
size_t object_arena_default_size(void)
16,203✔
1237
{
1238
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
16,203✔
1239
}
1240

1241
object_arena_t *object_arena_new(size_t size, unsigned std)
26,933✔
1242
{
1243
   if (all_arenas.count == 0)
26,933✔
1244
      APUSH(all_arenas, NULL);   // Dummy null arena
4,837✔
1245

1246
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
26,933✔
1247
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
26,933✔
1248
   arena->alloc  = arena->base;
26,933✔
1249
   arena->limit  = (char *)arena->base + size;
26,933✔
1250
   arena->key    = all_arenas.count;
26,933✔
1251
   arena->source = OBJ_FRESH;
26,933✔
1252
   arena->std    = std;
26,933✔
1253

1254
   APUSH(all_arenas, arena);
26,933✔
1255

1256
   if (all_arenas.count == UINT16_MAX - 1)
26,933✔
1257
      fatal_trace("too many object arenas");
1258

1259
   return arena;
26,933✔
1260
}
1261

1262
void object_arena_freeze(object_arena_t *arena)
26,337✔
1263
{
1264
   ident_t name = object_arena_name(arena);
26,337✔
1265

1266
   if (arena->frozen)
26,337✔
1267
      fatal_trace("arena %s already frozen", istr(name));
1268

1269
   if (arena->source == OBJ_FRESH)
26,337✔
1270
      object_arena_gc(arena);
15,610✔
1271

1272
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
26,337✔
1273
      debugf("arena %s frozen (%d bytes)", istr(name),
×
1274
             (int)(arena->alloc - arena->base));
×
1275

1276
   chash_put(arena_lookup, name, arena);
26,337✔
1277

1278
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
26,337✔
1279
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
26,337✔
1280

1281
   if (next_page < arena->limit) {
26,337✔
1282
#if OBJECT_UNMAP_UNUSED
1283
      nvc_munmap(next_page, arena->limit - next_page);
1284
      arena->limit = next_page;
1285
#else
1286
      // This can be useful for debugging use-after-free
1287
      nvc_decommit(next_page, arena->limit - next_page);
15,610✔
1288
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
15,610✔
1289
#endif
1290
   }
1291

1292
   arena->frozen = true;
26,337✔
1293
}
26,337✔
1294

1295
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
28,407✔
1296
{
1297
   for (unsigned i = 0; i < arena->deps.count; i++)
113,627✔
1298
      (*fn)(object_arena_name(arena->deps.items[i]), context);
85,220✔
1299
}
28,407✔
1300

1301
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
10,660✔
1302
                              void *context)
1303
{
1304
   for (unsigned i = 0; i < arena->deps.count; i++) {
32,360✔
1305
      if (arena->deps.items[i]->obsolete)
21,700✔
1306
         (*fn)(object_arena_name(arena->deps.items[i]), context);
6✔
1307
   }
1308
}
10,660✔
1309

1310
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
105,088✔
1311
{
1312
   object_arena_t *arena = __object_arena(object);
105,088✔
1313
   assert(arena->frozen);
105,088✔
1314

1315
   *module = object_arena_name(arena);
105,088✔
1316

1317
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
105,088✔
1318
   if (arena->forward != NULL)
105,088✔
1319
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
49,486✔
1320
   else
1321
      *offset = index;
55,602✔
1322
}
105,088✔
1323

1324
static object_arena_t *arena_by_name(ident_t module)
41,645✔
1325
{
1326
   if (global_arena != NULL && object_arena_name(global_arena) == module)
41,645✔
1327
      return global_arena;
×
1328

1329
   object_arena_t *a = chash_get(arena_lookup, module);
41,645✔
1330
   if (a != NULL)
41,645✔
1331
      return a;
1332

1333
#if defined DEBUG && !defined __SANITIZE_THREAD__
1334
   for (int i = 1; i < all_arenas.count; i++)
8,660✔
1335
      assert(module != object_arena_name(all_arenas.items[i]));
7,671✔
1336
#endif
1337

1338
   return NULL;
1339
}
1340

1341
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
41,645✔
1342
                            object_load_fn_t loader)
1343
{
1344
   object_arena_t *arena = arena_by_name(module);
41,645✔
1345

1346
   if (arena == NULL) {
41,645✔
1347
      object_t *droot = NULL;
989✔
1348
      if (loader) droot = (*loader)(module);
989✔
1349

1350
      if (droot == NULL)
989✔
1351
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1352

1353
      arena = __object_arena(droot);
989✔
1354
   }
1355

1356
   assert(arena->frozen);
41,645✔
1357

1358
   void *ptr = NULL;
41,645✔
1359
   if (arena->forward != NULL) {
41,645✔
1360
      // TODO: could do binary search here
1361
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
52,021,515✔
1362
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
52,021,515✔
1363
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
30,946✔
1364
            break;
30,946✔
1365
         }
1366
      }
1367
      assert(ptr != NULL);
30,946✔
1368
   }
1369
   else
1370
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
10,699✔
1371

1372
   if (ptr > arena->limit)
41,645✔
1373
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
1374

1375
   object_t *obj = ptr;
41,645✔
1376
   if (obj->tag >= OBJECT_TAG_COUNT)
41,645✔
1377
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1378
                  istr(module), offset);
1379
   else if (obj->arena != arena->key)
41,645✔
1380
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
1381
                  obj->arena, arena->key, istr(module), offset);
×
1382

1383
   return obj;
41,645✔
1384
}
1385

1386
void freeze_global_arena(void)
23,861✔
1387
{
1388
   object_one_time_init();
23,861✔
1389

1390
   if (global_arena != NULL) {
23,861✔
1391
      object_arena_freeze(global_arena);
15,610✔
1392
      global_arena = NULL;
15,610✔
1393
   }
1394
}
23,861✔
1395

1396
void make_new_arena(void)
16,203✔
1397
{
1398
   freeze_global_arena();
16,203✔
1399
   global_arena = object_arena_new(object_arena_default_size(), standard());
16,203✔
1400
}
16,203✔
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