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

nickg / nvc / 18400004699

10 Oct 2025 07:44AM UTC coverage: 92.551% (-0.2%) from 92.769%
18400004699

push

github

nickg
Remove the precompile elaboration mode

40 of 40 new or added lines in 4 files covered. (100.0%)

156 existing lines in 12 files now uncovered.

74440 of 80431 relevant lines covered (92.55%)

439822.55 hits per line

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

91.84
/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_NUMBER      (I_NUMBER)
85

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

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

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

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

119
static ident_t object_arena_name(object_arena_t *arena)
274,881✔
120
{
121
   if (arena->alloc > arena->base) {
274,881✔
122
      object_t *root = arena_root(arena);
266,624✔
123

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

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

133
   return ident_new("???");
10,584✔
134
}
135

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

142
   if (count == 1)
185,055✔
143
      arena->mark_bits[first] = 0;
158,704✔
144
   else
145
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
26,351✔
146
}
185,055✔
147

148
static bool object_marked_p(object_t *object, generation_t generation)
11,434,527✔
149
{
150
   object_arena_t *arena = __object_arena(object);
11,434,527✔
151

152
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
11,434,527✔
153
   const uintptr_t word = bit / 64;
11,434,527✔
154

155
   if (unlikely(arena->mark_bits == NULL)) {
11,434,527✔
156
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
16,977✔
157
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
16,977✔
158
      arena->mark_bits = xmalloc(arena->mark_sz);
16,977✔
159
      arena->mark_bits[word] = 0;
16,977✔
160
      arena->mark_low = arena->mark_high = word;
16,977✔
161
      arena->generation = generation;
16,977✔
162
   }
163
   else if (arena->generation != generation) {
11,417,550✔
164
      arena->mark_bits[word] = 0;
61,523✔
165
      arena->mark_low = arena->mark_high = word;
61,523✔
166
      arena->generation = generation;
61,523✔
167
   }
168

169
   // Lazy zeroing of mark bits helps performance with large arenas
170
   if (word < arena->mark_low) {
11,434,527✔
171
      zero_mark_bits(arena, word, arena->mark_low - word);
14,327✔
172
      arena->mark_low = word;
14,327✔
173
   }
174
   else if (word > arena->mark_high) {
11,420,200✔
175
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
170,728✔
176
      arena->mark_high = word;
170,728✔
177
   }
178

179
   const uint64_t mask = UINT64_C(1) << (bit & 63);
11,434,527✔
180

181
   const bool marked = !!(arena->mark_bits[word] & mask);
11,434,527✔
182
   arena->mark_bits[word] |= mask;
11,434,527✔
183

184
   return marked;
11,434,527✔
185
}
186

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

192
object_t *arena_root(object_arena_t *arena)
334,356✔
193
{
194
   return arena->root ?: (object_t *)arena->base;
334,356✔
195
}
196

197
bool arena_frozen(object_arena_t *arena)
65,301✔
198
{
199
   return arena->frozen;
65,301✔
200
}
201

202
uint32_t arena_flags(object_arena_t *arena)
56,222✔
203
{
204
   return arena->flags;
56,222✔
205
}
206

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

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

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

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

227
   if (lhs_mask == rhs_mask || rhs == NULL)
4,947,816✔
228
      return;
229
   else if (lhs->arena == rhs->arena)
4,947,816✔
230
      return;
231

232
   object_arena_t *larena = __object_arena(lhs);
4,431,156✔
233
   object_arena_t *rarena = __object_arena(rhs);
4,431,156✔
234

235
   assert(!larena->frozen);
4,431,156✔
236
   assert(rarena->frozen);
4,431,156✔
237

238
   for (unsigned i = 0; i < larena->deps.count; i++) {
7,360,503✔
239
      if (larena->deps.items[i] == rarena)
7,324,128✔
240
         return;
241
   }
242

243
   APUSH(larena->deps, rarena);
36,375✔
244
}
245

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

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

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

264
void obj_array_add(obj_array_t **a, object_t *o)
1,276,400✔
265
{
266
   if (*a == NULL) {
1,276,400✔
267
      const int defsz = 8;
457,305✔
268
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
457,305✔
269
      (*a)->count = 0;
457,305✔
270
      (*a)->limit = defsz;
457,305✔
271
   }
272
   else if ((*a)->count == (*a)->limit) {
819,095✔
273
      (*a)->limit *= 2;
30,942✔
274
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
30,942✔
275
                         (*a)->limit, sizeof(object_t *));
276
   }
277

278
   (*a)->items[(*a)->count++] = o;
1,276,400✔
279
}
1,276,400✔
280

281
void obj_array_free(obj_array_t **a)
94,221✔
282
{
283
   free(*a);
94,221✔
284
   *a = NULL;
94,221✔
285
}
94,221✔
286

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

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

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

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

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

309
   const int max_items = MAX(old_nitems, new_nitems);
24,190✔
310

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

314
   int op = 0, np = 0;
24,190✔
315
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
976,271✔
316
      if ((old_has & mask) && (new_has & mask))
952,081✔
317
         object->items[np++] = tmp[op++];
86,777✔
318
      else if (old_has & mask) {
865,304✔
319
         if (ITEM_OBJ_ARRAY & mask)
146✔
320
            obj_array_free(&(tmp[op].obj_array));
116✔
321
         ++op;
146✔
322
      }
323
      else if (new_has & mask)
865,158✔
324
         memset(&(object->items[np++]), '\0', sizeof(item_t));
952,081✔
325
   }
326

327
   object->kind = kind;
24,190✔
328
}
329

330
static void object_init(object_class_t *class)
20,844✔
331
{
332
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
20,844✔
333

334
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
20,844✔
335

336
   assert(class->tag < ARRAY_LEN(classes));
20,844✔
337
   classes[class->tag] = class;
20,844✔
338

339
#ifdef DEBUG
340
   imask_t all_items = 0;
20,844✔
341
#endif
342

343
   for (int i = 0; i < class->last_kind; i++) {
1,391,337✔
344
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,370,493✔
345
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,370,493✔
346
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,370,493✔
347

348
      format_digest += knuth_hash(class->has_map[i] >> 32);
1,370,493✔
349
      format_digest += knuth_hash(class->has_map[i]);
1,370,493✔
350
   }
351

352
   bool changed = false;
31,266✔
353
   do {
31,266✔
354
      changed = false;
31,266✔
355
      for (int i = 0; i < class->last_kind; i++) {
2,641,977✔
356
         size_t max_size = class->object_size[i];
2,610,711✔
357
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
32,375,943✔
358
            if (class->change_allowed[j][0] == i)
29,765,232✔
359
               max_size = MAX(max_size,
250,128✔
360
                              class->object_size[class->change_allowed[j][1]]);
361
            else if (class->change_allowed[j][1] == i)
29,515,104✔
362
               max_size = MAX(max_size,
250,128✔
363
                              class->object_size[class->change_allowed[j][0]]);
364
         }
365

366
         if (max_size != class->object_size[i]) {
2,610,711✔
367
            class->object_size[i] = max_size;
62,532✔
368
            changed = true;
62,532✔
369
         }
370
      }
371
   } while (changed);
31,266✔
372

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

380
   const imask_t known_types =
20,844✔
381
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
382
      | ITEM_DOUBLE | ITEM_NUMBER;
383

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

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

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

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

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

418
static void object_one_time_init(void)
25,275,065✔
419
{
420
   INIT_ONCE({
25,275,065✔
421
         extern object_class_t tree_object;
422
         object_init(&tree_object);
423

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

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

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

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

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

439
         add_fault_handler(check_frozen_object_fault, NULL);
440

441
         arena_lookup = chash_new(64);
442
      });
443
}
25,275,065✔
444

445
static bool is_gc_root(const object_class_t *class, int kind)
2,215,877✔
446
{
447
   for (int j = 0; j < class->gc_num_roots; j++) {
20,624,053✔
448
      if (class->gc_roots[j] == kind)
18,465,930✔
449
         return true;
450
   }
451

452
   return false;
453
}
454

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

461
   object_one_time_init();
25,230,763✔
462

463
   if (arena == NULL)
25,230,763✔
464
      arena = global_arena;
1,913,899✔
465

466
   if (unlikely(arena == NULL))
25,230,763✔
467
      fatal_trace("allocating object without active arena");
468

469
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
25,230,763✔
470

471
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
25,230,763✔
472

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

485
   object_t *object = arena->alloc;
25,230,763✔
486
   arena->alloc = (char *)arena->alloc + size;
25,230,763✔
487

488
   if (arena->root == NULL && is_gc_root(class, kind))
25,230,763✔
489
      arena->root = object;
35,993✔
490

491
   memset(object, '\0', size);
25,230,763✔
492

493
   object->kind  = kind;
25,230,763✔
494
   object->tag   = class->tag;
25,230,763✔
495
   object->arena = arena->key;
25,230,763✔
496
   object->loc   = LOC_INVALID;
25,230,763✔
497

498
   return object;
25,230,763✔
499
}
500

501
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,830,781✔
502
                              generation_t generation)
503
{
504
   if (object == NULL)
4,830,781✔
505
      return;
506
   else if (!object_in_arena_p(arena, object))
4,012,295✔
507
      return;
508
   else if (object_marked_p(object, generation))
2,419,204✔
509
      return;
510

511
   const object_class_t *class = classes[object->tag];
1,826,119✔
512

513
   imask_t has = class->has_map[object->kind];
1,826,119✔
514
   for (int n = 0; has; has &= has - 1, n++) {
9,791,413✔
515
      const uint64_t mask = has & -has;
7,965,294✔
516
      item_t *item = &(object->items[n]);
7,965,294✔
517
      if (ITEM_OBJECT & mask)
7,965,294✔
518
         gc_mark_from_root(item->object, arena, generation);
3,413,785✔
519
      else if (ITEM_OBJ_ARRAY & mask) {
4,551,509✔
520
         if (item->obj_array != NULL) {
810,768✔
521
            for (unsigned j = 0; j < item->obj_array->count; j++)
1,912,947✔
522
               gc_mark_from_root(item->obj_array->items[j], arena,
1,395,235✔
523
                                 generation);
524
         }
525
      }
526
   }
527
}
528

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

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

537
   for (int n = 0; has; has &= has - 1, n++) {
423,738✔
538
      const uint64_t mask = has & -has;
349,034✔
539
      item_t *item = &(object->items[n]);
349,034✔
540
      if (ITEM_OBJ_ARRAY & mask)
349,034✔
541
         obj_array_free(&(item->obj_array));
93,143✔
542
   }
543
}
544

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

550
   // Mark
551
   for (void *p = arena->base; p != arena->alloc; ) {
2,196,405✔
552
      assert(p < arena->alloc);
2,179,869✔
553
      object_t *object = p;
2,179,869✔
554

555
      const object_class_t *class = classes[object->tag];
2,179,869✔
556

557
      if (is_gc_root(class, object->kind))
2,179,869✔
558
         gc_mark_from_root(object, arena, generation);
21,761✔
559

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

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

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

571
   // Calculate forwarding addresses
572
   unsigned woffset = 0, live = 0, dead = 0;
16,536✔
573
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,196,405✔
574
      assert(rptr < arena->alloc);
2,179,869✔
575
      object_t *object = rptr;
2,179,869✔
576

577
      const object_class_t *class = classes[object->tag];
2,179,869✔
578

579
      const size_t size =
2,179,869✔
580
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,179,869✔
581

582
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,179,869✔
583
      if (!object_marked_p(object, generation)) {
2,179,869✔
584
         forward[index] = UINT32_MAX;
353,750✔
585
         gc_free_external(object);
353,750✔
586
         dead++;
353,750✔
587
      }
588
      else {
589
         forward[index] = woffset;
1,826,119✔
590
         woffset += size;
1,826,119✔
591
         live++;
1,826,119✔
592
      }
593

594
      rptr = (char *)rptr + size;
2,179,869✔
595
   }
596

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

601
   arena->forward = forward;
16,536✔
602
   arena->live_bytes = woffset;
16,536✔
603

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

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

616
   if (object == NULL)
729,693✔
617
      return;
618
   else if (object_marked_p(object, ctx->generation))
545,377✔
619
      return;
620

621
   const object_class_t *class = classes[object->tag];
517,691✔
622

623
   const bool visit =
1,035,382✔
624
      (object->tag == ctx->tag && object->kind == ctx->kind)
517,691✔
625
      || ctx->kind == class->last_kind;
1,033,292✔
626

627
   if (visit && ctx->preorder != NULL)
517,691✔
628
      (*ctx->preorder)(object, ctx->context);
×
629

630
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
517,691✔
631

632
   imask_t has = class->has_map[object->kind];
517,691✔
633
   for (int n = 0; has; has &= has - 1, n++) {
2,724,735✔
634
      const uint64_t mask = has & -has;
2,207,044✔
635
      if (mask & deep_mask) {
2,207,044✔
636
         item_t *item = &(object->items[n]);
1,579,682✔
637
         if (ITEM_OBJECT & mask)
1,579,682✔
638
            object_visit(item->object, ctx);
372,440✔
639
         else if (ITEM_OBJ_ARRAY & mask) {
1,207,242✔
640
            if (item->obj_array != NULL) {
168,788✔
641
               for (unsigned j = 0; j < item->obj_array->count; j++)
448,919✔
642
                  object_visit(item->obj_array->items[j], ctx);
345,626✔
643
            }
644
         }
645
      }
646
   }
647

648
   if (visit) {
517,691✔
649
      if (ctx->postorder != NULL)
506,002✔
650
         (*ctx->postorder)(object, ctx->context);
506,002✔
651
      ctx->count++;
506,002✔
652
   }
653
}
654

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

668
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
8,167,935✔
669
{
670
   if (object == NULL)
8,167,935✔
671
      return NULL;
672

673
   if (!object_in_arena_p(ctx->arena, object))
6,806,215✔
674
      return object;
675

676
   const ptrdiff_t index =
4,530,511✔
677
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,530,511✔
678

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

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

707
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,768,357✔
708

709
   if (ctx->pre_fn[object->tag] != NULL)
3,768,357✔
710
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
711

712
   const imask_t skip_mask =
3,768,357✔
713
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
714

715
   const object_class_t *class = classes[object->tag];
3,768,357✔
716

717
   imask_t has = class->has_map[object->kind];
3,768,357✔
718
   for (int n = 0; has; has &= has - 1, n++) {
20,198,632✔
719
      const uint64_t mask = has & -has;
16,430,283✔
720
      if (mask & ~skip_mask) {
16,430,283✔
721
         if (ITEM_OBJECT & mask) {
6,896,366✔
722
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,409,690✔
723
            object->items[n].object = o;
5,409,686✔
724
            object_write_barrier(object, o);
5,409,686✔
725
         }
726
         else if (ITEM_OBJ_ARRAY & mask) {
1,486,676✔
727
            obj_array_t **a = &(object->items[n].obj_array);
1,486,676✔
728
            if (object->items[n].obj_array != NULL) {
1,486,676✔
729
               // The callback may add new items to the array so the
730
               // array pointer cannot be cached between iterations
731
               unsigned wptr = 0;
732
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,640,381✔
733
                  object_t *o = object->items[n].obj_array->items[i];
2,655,722✔
734
                  if ((o = object_rewrite(o, ctx))) {
2,655,722✔
735
                     object_write_barrier(object, o);
2,650,708✔
736
                     object->items[n].obj_array->items[wptr++] = o;
2,650,708✔
737
                  }
738
               }
739

740
               if (wptr == 0)
984,659✔
741
                  obj_array_free(a);
962✔
742
               else
743
                  (*a)->count = wptr;
983,697✔
744
            }
745
         }
746
         else
747
            should_not_reach_here();
748
      }
749
   }
750

751
   if (ctx->cache[index] != (object_t *)-1) {
3,768,349✔
752
      // The cache was already updated due to a circular reference
753
      return ctx->cache[index];
754
   }
755
   else if (ctx->post_fn[object->tag] != NULL) {
3,767,242✔
756
      object_t *new = object_rewrite_iter(object, ctx);
3,547,279✔
757
      object_write_barrier(object, new);
3,547,275✔
758
      return (ctx->cache[index] = new);
3,547,275✔
759
   }
760
   else
761
      return (ctx->cache[index] = object);
219,963✔
762
}
763

764
static void object_write_ref(object_t *object, fbuf_t *f)
4,250,886✔
765
{
766
   if (object == NULL)
4,250,886✔
767
      fbuf_put_uint(f, 0);
732,068✔
768
   else {
769
      object_arena_t *arena = __object_arena(object);
3,518,818✔
770
      assert(arena->key != 0);
3,518,818✔
771
      fbuf_put_uint(f, arena->key);
3,518,818✔
772

773
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,518,818✔
774
      if (arena->forward != NULL)
3,518,818✔
775
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,209,942✔
776
      else
777
         fbuf_put_uint(f, index);
1,308,876✔
778
   }
779
}
4,250,886✔
780

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

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

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

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

816
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
1,903,675✔
817
      assert(p < arena->alloc);
1,892,435✔
818

819
      object_t *object = p;
1,892,435✔
820
      object_class_t *class = classes[object->tag];
1,892,435✔
821

822
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,892,435✔
823

824
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,892,435✔
825
      if (arena->forward[index] == UINT32_MAX)
1,892,435✔
826
         continue;   // Dead object
257,543✔
827

828
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,634,892✔
829
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,634,892✔
830

831
      if (class->has_loc)
1,634,892✔
832
         loc_write(&object->loc, loc_ctx);
1,534,014✔
833

834
      imask_t has = class->has_map[object->kind];
1,634,892✔
835
      for (int n = 0; has; has &= has - 1, n++) {
8,721,115✔
836
         const uint64_t mask = has & -has;
7,086,223✔
837
         item_t *item = &(object->items[n]);
7,086,223✔
838
         if (ITEM_IDENT & mask)
7,086,223✔
839
            ident_write(item->ident, ident_ctx);
1,321,632✔
840
         else if (ITEM_OBJECT & mask)
5,764,591✔
841
            object_write_ref(item->object, f);
3,106,396✔
842
         else if (ITEM_OBJ_ARRAY & mask) {
2,658,195✔
843
            if (item->obj_array != NULL) {
628,030✔
844
               const unsigned count = item->obj_array->count;
407,909✔
845
               fbuf_put_uint(f, count);
407,909✔
846
               for (unsigned i = 0; i < count; i++)
1,552,399✔
847
                  object_write_ref(item->obj_array->items[i], f);
1,144,490✔
848
            }
849
            else
850
               fbuf_put_uint(f, 0);
220,121✔
851
         }
852
         else if (ITEM_INT64 & mask)
2,030,165✔
853
            fbuf_put_int(f, item->ival);
362,132✔
854
         else if (ITEM_INT32 & mask)
1,668,033✔
855
            fbuf_put_int(f, item->ival);
1,555,750✔
856
         else if (ITEM_DOUBLE & mask)
112,283✔
857
            write_double(item->dval, f);
107,654✔
858
         else if (ITEM_NUMBER & mask)
4,629✔
859
            number_write(item->number, f);
4,629✔
860
         else
861
            should_not_reach_here();
862
      }
863
   }
864

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

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

874
   arena_key_t mapped = key_map[key];
50,464,540✔
875
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
50,464,540✔
876

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

880
   assert(mapped < all_arenas.count);
50,464,540✔
881
   assert(mapped > 0);
50,464,540✔
882

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

886
   return (object_t *)((char *)arena->base + offset);
50,464,540✔
887
}
888

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

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

901
   const vhdl_standard_t std = fbuf_get_uint(f);
18,839✔
902

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

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

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

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

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

924
   arena_key_t max_key = fbuf_get_uint(f);
18,833✔
925

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

929
   const int ndeps = fbuf_get_uint(f);
18,833✔
930
   for (int i = 0; i < ndeps; i++) {
41,801✔
931
      arena_key_t dkey = fbuf_get_uint(f);
22,971✔
932
      vhdl_standard_t dstd = fbuf_get_uint(f);
22,971✔
933
      uint32_t checksum = fbuf_get_uint(f);
22,971✔
934
      ident_t dep = ident_read(ident_ctx);
22,971✔
935

936
      object_arena_t *a = NULL;
22,971✔
937
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
108,433✔
938
         if (dep == object_arena_name(all_arenas.items[j]))
85,462✔
939
            a = all_arenas.items[j];
18,783✔
940
      }
941

942
      if (a == NULL) {
22,971✔
943
         object_t *droot = NULL;
4,188✔
944
         if (loader_fn) droot = (*loader_fn)(dep);
4,188✔
945

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

950
         a = __object_arena(droot);
4,188✔
951
      }
952

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

969
      APUSH(arena->deps, a);
22,968✔
970

971
      assert(dkey <= max_key);
22,968✔
972
      key_map[dkey] = a->key;
22,968✔
973
   }
974

975
   for (;;) {
23,001,388✔
976
      const uint64_t hdr = fbuf_get_uint(f);
23,001,388✔
977
      if (hdr == UINT16_MAX) break;
23,001,388✔
978

979
      const unsigned tag = hdr & 3;
22,982,558✔
980
      const unsigned kind = hdr >> 2;
22,982,558✔
981

982
      assert(tag < OBJECT_TAG_COUNT);
22,982,558✔
983

984
      const object_class_t *class = classes[tag];
22,982,558✔
985

986
      object_t *object = object_new(arena, class, kind);
22,982,558✔
987

988
      if (class->has_loc)
22,982,558✔
989
         loc_read(&(object->loc), loc_ctx);
20,741,708✔
990

991
      imask_t has = class->has_map[object->kind];
22,982,558✔
992
      for (int n = 0; has; has &= has - 1, n++) {
126,027,057✔
993
         const uint64_t mask = has & -has;
103,044,499✔
994
         item_t *item = &(object->items[n]);
103,044,499✔
995
         if (ITEM_IDENT & mask)
103,044,499✔
996
            item->ident = ident_read(ident_ctx);
20,862,488✔
997
         else if (ITEM_OBJECT & mask)
82,182,011✔
998
            item->object = object_read_ref(f, key_map);
41,336,509✔
999
         else if (ITEM_OBJ_ARRAY & mask) {
40,845,502✔
1000
            const unsigned count = fbuf_get_uint(f);
9,590,845✔
1001
            if (count > 0) {
9,590,845✔
1002
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
6,413,292✔
1003
                                              count, sizeof(object_t *));
1004
               item->obj_array->count =
6,413,292✔
1005
                  item->obj_array->limit = count;
6,413,292✔
1006
               for (unsigned i = 0; i < count; i++) {
24,505,525✔
1007
                  object_t *o = object_read_ref(f, key_map);
18,092,233✔
1008
                  item->obj_array->items[i] = o;
18,092,233✔
1009
               }
1010
            }
1011
         }
1012
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
31,254,657✔
1013
            item->ival = fbuf_get_int(f);
30,463,305✔
1014
         else if (ITEM_DOUBLE & mask)
791,352✔
1015
            item->dval = read_double(f);
791,352✔
1016
         else if (ITEM_NUMBER & mask)
×
1017
            item->number = number_read(f);
×
1018
         else
1019
            should_not_reach_here();
1020
      }
1021
   }
1022

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

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

1029
unsigned object_next_generation(void)
66,978✔
1030
{
1031
   return next_generation++;
66,978✔
1032
}
1033

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

1039
   object_arena_t *arena = __object_arena(object);
2,884,878✔
1040
   if (arena->copygen != ctx->generation || !arena->copyflag)
2,884,878✔
1041
      return false;
1042

1043
   if (ctx->copy_map == NULL)
1,759,566✔
1044
      ctx->copy_map = hash_new(1024);
8,669✔
1045

1046
   if (object_marked_p(object, ctx->generation))
1,759,566✔
1047
      return hash_get(ctx->copy_map, object) != NULL;
393,964✔
1048

1049
   const object_class_t *class = classes[object->tag];
1,365,602✔
1050

1051
   bool marked = false;
1,365,602✔
1052
   if (ctx->should_copy[object->tag] != NULL)
1,365,602✔
1053
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,362,087✔
1054

1055
   object_t *copy = NULL;
1,362,087✔
1056
   if (marked) {
1,362,087✔
1057
      copy = object_new(global_arena, class, object->kind);
57,840✔
1058
      hash_put(ctx->copy_map, object, copy);
57,840✔
1059
   }
1060

1061
   imask_t has = class->has_map[object->kind];
1,365,602✔
1062
   for (int n = 0; has; has &= has - 1, n++) {
7,239,312✔
1063
      const uint64_t mask = has & -has;
5,873,710✔
1064
      item_t *item = &(object->items[n]);
5,873,710✔
1065
      if (ITEM_OBJECT & mask)
5,873,710✔
1066
         marked |= object_copy_mark(item->object, ctx);
2,594,471✔
1067
      else if (ITEM_OBJ_ARRAY & mask) {
3,279,239✔
1068
         if (item->obj_array != NULL) {
506,084✔
1069
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,245,269✔
1070
               object_t *o = item->obj_array->items[i];
916,657✔
1071
               marked |= object_copy_mark(o, ctx);
916,657✔
1072
            }
1073
         }
1074
      }
1075
   }
1076

1077
   if (marked && copy == NULL) {
1,365,602✔
1078
      copy = object_new(global_arena, class, object->kind);
276,466✔
1079
      hash_put(ctx->copy_map, object, copy);
276,466✔
1080
   }
1081

1082
   return marked;
1083
}
1084

1085
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
957,921✔
1086
{
1087
   if (object == NULL)
957,921✔
1088
      return NULL;
1089

1090
   object_t *map = hash_get(ctx->copy_map, object);
809,569✔
1091
   return map ?: object;
809,569✔
1092
}
1093

1094
static bool object_copy_root_closure(object_arena_t *a, object_copy_ctx_t *ctx)
80,586✔
1095
{
1096
   bool include = false;
80,586✔
1097

1098
   if (a->copygen == ctx->generation)
80,586✔
1099
      return a->copyflag;
42,430✔
1100

1101
   for (int i = 0; i < ctx->nroots; i++)
104,011✔
1102
      include |= (__object_arena(ctx->roots[i]) == a);
65,855✔
1103

1104
   for (int i = 0; i < a->deps.count; i++)
103,137✔
1105
      include |= object_copy_root_closure(a->deps.items[i], ctx);
64,981✔
1106

1107
   a->copygen  = ctx->generation;
38,156✔
1108
   a->copyflag = include;
38,156✔
1109

1110
   return include;
38,156✔
1111
}
1112

1113
void object_copy(object_copy_ctx_t *ctx)
8,669✔
1114
{
1115
   for (int i = 0; i < ctx->nroots; i++) {
24,274✔
1116
      object_arena_t *a = __object_arena(ctx->roots[i]);
15,605✔
1117
      object_copy_root_closure(a, ctx);
15,605✔
1118
   }
1119

1120
   for (int i = 0; i < ctx->nroots; i++)
24,274✔
1121
      (void)object_copy_mark(ctx->roots[i], ctx);
15,605✔
1122

1123
   unsigned ncopied = 0;
8,669✔
1124
   const void *key;
8,669✔
1125
   void *value;
8,669✔
1126
   for (hash_iter_t it = HASH_BEGIN;
8,669✔
1127
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
342,975✔
1128
      const object_t *object = key;
334,306✔
1129
      object_t *copy = value;
334,306✔
1130
      ncopied++;
334,306✔
1131

1132
      copy->loc = object->loc;
334,306✔
1133

1134
      const object_class_t *class = classes[object->tag];
334,306✔
1135

1136
      imask_t has = class->has_map[object->kind];
334,306✔
1137
      for (int n = 0; has; has &= has - 1, n++) {
1,830,164✔
1138
         const uint64_t mask = has & -has;
1,495,858✔
1139
         const item_t *from = &(object->items[n]);
1,495,858✔
1140
         item_t *to = &(copy->items[n]);
1,495,858✔
1141

1142
         if (ITEM_IDENT & mask)
1,495,858✔
1143
            to->ident = from->ident;
259,880✔
1144
         else if (ITEM_OBJECT & mask) {
1,235,978✔
1145
            to->object = object_copy_map(from->object, ctx);
636,317✔
1146
            object_write_barrier(copy, to->object);
636,317✔
1147
         }
1148
         else if (ITEM_DOUBLE & mask)
599,661✔
1149
            to->dval = from->dval;
115✔
1150
         else if (ITEM_OBJ_ARRAY & mask) {
599,546✔
1151
            if (from->obj_array != NULL) {
211,284✔
1152
               // TODO: make a resize macro
1153
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
299,606✔
1154
                                            from->obj_array->count,
149,803✔
1155
                                            sizeof(object_t *));
1156
               to->obj_array->count =
149,803✔
1157
                  to->obj_array->limit = from->obj_array->count;
149,803✔
1158
               for (size_t i = 0; i < from->obj_array->count; i++) {
471,407✔
1159
                  object_t *o =
321,604✔
1160
                     object_copy_map(from->obj_array->items[i], ctx);
321,604✔
1161
                  to->obj_array->items[i] = o;
321,604✔
1162
                  object_write_barrier(copy, o);
321,604✔
1163
               }
1164
            }
1165
         }
1166
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
388,262✔
1167
            to->ival = from->ival;
388,262✔
1168
         else
1169
            should_not_reach_here();
1170
      }
1171
   }
1172

1173
   for (hash_iter_t it = HASH_BEGIN;
8,669✔
1174
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
342,975✔
1175
      object_t *copy = value;
334,306✔
1176
      if (ctx->callback[copy->tag] != NULL)
334,306✔
1177
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
331,330✔
1178
   }
1179

1180
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
8,669✔
1181
      debugf("copied %d objects into arena %s", ncopied,
×
1182
             istr(object_arena_name(global_arena)));
1183

1184
   for (unsigned i = 0; i < ctx->nroots; i++) {
24,274✔
1185
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
15,605✔
1186
      if (copy != NULL)
15,605✔
1187
         ctx->roots[i] = copy;
6,840✔
1188
   }
1189

1190
   hash_free(ctx->copy_map);
8,669✔
1191
}
8,669✔
1192

1193
size_t object_arena_default_size(void)
17,165✔
1194
{
1195
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
17,165✔
1196
}
1197

1198
object_arena_t *object_arena_new(size_t size, unsigned std)
35,998✔
1199
{
1200
   if (all_arenas.count == 0)
35,998✔
1201
      APUSH(all_arenas, NULL);   // Dummy null arena
5,201✔
1202

1203
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
35,998✔
1204
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
35,998✔
1205
   arena->alloc  = arena->base;
35,998✔
1206
   arena->limit  = (char *)arena->base + size;
35,998✔
1207
   arena->key    = all_arenas.count;
35,998✔
1208
   arena->source = OBJ_FRESH;
35,998✔
1209
   arena->std    = std;
35,998✔
1210

1211
   APUSH(all_arenas, arena);
35,998✔
1212

1213
   if (all_arenas.count == UINT16_MAX - 1)
35,998✔
1214
      fatal_trace("too many object arenas");
1215

1216
   return arena;
35,998✔
1217
}
1218

1219
void object_arena_freeze(object_arena_t *arena)
35,366✔
1220
{
1221
   ident_t name = object_arena_name(arena);
35,366✔
1222

1223
   if (arena->frozen)
35,366✔
1224
      fatal_trace("arena %s already frozen", istr(name));
1225

1226
   if (arena->source == OBJ_FRESH)
35,366✔
1227
      object_arena_gc(arena);
16,536✔
1228

1229
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
35,366✔
1230
      debugf("arena %s frozen (%d bytes)", istr(name),
×
1231
             (int)(arena->alloc - arena->base));
×
1232

1233
   chash_put(arena_lookup, name, arena);
35,366✔
1234

1235
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
35,366✔
1236
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
35,366✔
1237

1238
   if (next_page < arena->limit) {
35,366✔
1239
#if OBJECT_UNMAP_UNUSED
1240
      nvc_munmap(next_page, arena->limit - next_page);
1241
      arena->limit = next_page;
1242
#else
1243
      // This can be useful for debugging use-after-free
1244
      nvc_decommit(next_page, arena->limit - next_page);
16,536✔
1245
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
16,536✔
1246
#endif
1247
   }
1248

1249
   arena->frozen = true;
35,366✔
1250
}
35,366✔
1251

1252
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
42,480✔
1253
{
1254
   for (unsigned i = 0; i < arena->deps.count; i++)
146,275✔
1255
      (*fn)(object_arena_name(arena->deps.items[i]), context);
103,795✔
1256
}
42,480✔
1257

1258
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
11,240✔
1259
                              void *context)
1260
{
1261
   for (unsigned i = 0; i < arena->deps.count; i++) {
33,707✔
1262
      if (arena->deps.items[i]->obsolete)
22,467✔
1263
         (*fn)(object_arena_name(arena->deps.items[i]), context);
6✔
1264
   }
1265
}
11,240✔
1266

1267
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
12,630✔
1268
{
1269
   object_arena_t *arena = __object_arena(object);
12,630✔
1270
   assert(arena->frozen);
12,630✔
1271

1272
   *module = object_arena_name(arena);
12,630✔
1273

1274
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
12,630✔
1275
   if (arena->forward != NULL)
12,630✔
1276
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
5,850✔
1277
   else
1278
      *offset = index;
6,780✔
1279
}
12,630✔
1280

1281
static object_arena_t *arena_by_name(ident_t module)
7,319✔
1282
{
1283
   if (global_arena != NULL && object_arena_name(global_arena) == module)
7,319✔
1284
      return global_arena;
×
1285

1286
   object_arena_t *a = chash_get(arena_lookup, module);
7,319✔
1287
   if (a != NULL)
7,319✔
1288
      return a;
1289

1290
#if defined DEBUG && !defined __SANITIZE_THREAD__
1291
   for (int i = 1; i < all_arenas.count; i++)
4,546✔
1292
      assert(module != object_arena_name(all_arenas.items[i]));
3,915✔
1293
#endif
1294

1295
   return NULL;
1296
}
1297

1298
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
7,319✔
1299
                            object_load_fn_t loader)
1300
{
1301
   object_arena_t *arena = arena_by_name(module);
7,319✔
1302

1303
   if (arena == NULL) {
7,319✔
1304
      object_t *droot = NULL;
631✔
1305
      if (loader) droot = (*loader)(module);
631✔
1306

1307
      if (droot == NULL)
631✔
1308
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1309

1310
      arena = __object_arena(droot);
631✔
1311
   }
1312

1313
   assert(arena->frozen);
7,319✔
1314

1315
   void *ptr = NULL;
7,319✔
1316
   if (arena->forward != NULL) {
7,319✔
1317
      // TODO: could do binary search here
UNCOV
1318
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
×
UNCOV
1319
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
×
UNCOV
1320
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
×
UNCOV
1321
            break;
×
1322
         }
1323
      }
UNCOV
1324
      assert(ptr != NULL);
×
1325
   }
1326
   else
1327
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
7,319✔
1328

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

1332
   object_t *obj = ptr;
7,319✔
1333
   if (obj->tag >= OBJECT_TAG_COUNT)
7,319✔
1334
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1335
                  istr(module), offset);
1336
   else if (obj->arena != arena->key)
7,319✔
1337
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
1338
                  obj->arena, arena->key, istr(module), offset);
×
1339

1340
   return obj;
7,319✔
1341
}
1342

1343
void freeze_global_arena(void)
25,462✔
1344
{
1345
   object_one_time_init();
25,462✔
1346

1347
   if (global_arena != NULL) {
25,462✔
1348
      object_arena_freeze(global_arena);
16,536✔
1349
      global_arena = NULL;
16,536✔
1350
   }
1351
}
25,462✔
1352

1353
void make_new_arena(void)
17,165✔
1354
{
1355
   freeze_global_arena();
17,165✔
1356
   global_arena = object_arena_new(object_arena_default_size(), standard());
17,165✔
1357
}
17,165✔
1358

1359
void discard_global_arena(void)
1✔
1360
{
1361
   object_one_time_init();
1✔
1362

1363
   if (global_arena != NULL) {
1✔
1364
      nvc_munmap(global_arena->base, global_arena->limit - global_arena->base);
1✔
1365

1366
      assert(all_arenas.items[all_arenas.count - 1] == global_arena);
1✔
1367
      APOP(all_arenas);
1✔
1368

1369
      free(global_arena);
1✔
1370
      global_arena = NULL;
1✔
1371
   }
1372
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc