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

nickg / nvc / 15932465145

27 Jun 2025 05:38PM UTC coverage: 92.236% (+0.03%) from 92.204%
15932465145

push

github

nickg
Handle more expression kinds in build_sensitivity

3 of 22 new or added lines in 2 files covered. (13.64%)

260 existing lines in 8 files now uncovered.

70315 of 76234 relevant lines covered (92.24%)

565630.64 hits per line

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

92.33
/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)
12,857,397✔
108
{
109
   return (void *)object >= arena->base && (void *)object < arena->limit;
12,857,397✔
110
}
111

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

119
static ident_t object_arena_name(object_arena_t *arena)
420,772✔
120
{
121
   if (arena->alloc > arena->base) {
420,772✔
122
      object_t *root = arena_root(arena);
410,872✔
123

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

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

133
   return ident_new("???");
12,816✔
134
}
135

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

142
   if (count == 1)
229,846✔
143
      arena->mark_bits[first] = 0;
196,236✔
144
   else
145
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
33,610✔
146
}
229,846✔
147

148
static bool object_marked_p(object_t *object, generation_t generation)
13,719,448✔
149
{
150
   object_arena_t *arena = __object_arena(object);
13,719,448✔
151

152
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
13,719,448✔
153
   const uintptr_t word = bit / 64;
13,719,448✔
154

155
   if (unlikely(arena->mark_bits == NULL)) {
13,719,448✔
156
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
20,796✔
157
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
20,796✔
158
      arena->mark_bits = xmalloc(arena->mark_sz);
20,796✔
159
      arena->mark_bits[word] = 0;
20,796✔
160
      arena->mark_low = arena->mark_high = word;
20,796✔
161
      arena->generation = generation;
20,796✔
162
   }
163
   else if (arena->generation != generation) {
13,698,652✔
164
      arena->mark_bits[word] = 0;
77,575✔
165
      arena->mark_low = arena->mark_high = word;
77,575✔
166
      arena->generation = generation;
77,575✔
167
   }
168

169
   // Lazy zeroing of mark bits helps performance with large arenas
170
   if (word < arena->mark_low) {
13,719,448✔
171
      zero_mark_bits(arena, word, arena->mark_low - word);
18,905✔
172
      arena->mark_low = word;
18,905✔
173
   }
174
   else if (word > arena->mark_high) {
13,700,543✔
175
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
210,941✔
176
      arena->mark_high = word;
210,941✔
177
   }
178

179
   const uint64_t mask = UINT64_C(1) << (bit & 63);
13,719,448✔
180

181
   const bool marked = !!(arena->mark_bits[word] & mask);
13,719,448✔
182
   arena->mark_bits[word] |= mask;
13,719,448✔
183

184
   return marked;
13,719,448✔
185
}
186

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

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

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

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

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

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

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

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

227
   if (lhs_mask == rhs_mask || rhs == NULL)
5,887,052✔
228
      return;
229
   else if (lhs->arena == rhs->arena)
5,887,052✔
230
      return;
231

232
   object_arena_t *larena = __object_arena(lhs);
5,298,742✔
233
   object_arena_t *rarena = __object_arena(rhs);
5,298,742✔
234

235
   assert(!larena->frozen);
5,298,742✔
236
   assert(rarena->frozen);
5,298,742✔
237

238
   for (unsigned i = 0; i < larena->deps.count; i++) {
8,840,049✔
239
      if (larena->deps.items[i] == rarena)
8,795,329✔
240
         return;
241
   }
242

243
   APUSH(larena->deps, rarena);
44,720✔
244
}
245

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

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

UNCOV
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,517,019✔
265
{
266
   if (*a == NULL) {
1,517,019✔
267
      const int defsz = 8;
538,247✔
268
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
538,247✔
269
      (*a)->count = 0;
538,247✔
270
      (*a)->limit = defsz;
538,247✔
271
   }
272
   else if ((*a)->count == (*a)->limit) {
978,772✔
273
      (*a)->limit *= 2;
37,448✔
274
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
37,448✔
275
                         (*a)->limit, sizeof(object_t *));
276
   }
277

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

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

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

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

298
   if (!allow)
28,648✔
299
      fatal_trace("cannot change %s kind %s to %s", class->name,
UNCOV
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];
28,648✔
304
   const imask_t new_has = class->has_map[kind];
28,648✔
305

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

309
   const int max_items = MAX(old_nitems, new_nitems);
28,648✔
310

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

314
   int op = 0, np = 0;
28,648✔
315
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
1,143,218✔
316
      if ((old_has & mask) && (new_has & mask))
1,114,570✔
317
         object->items[np++] = tmp[op++];
98,753✔
318
      else if (old_has & mask) {
1,015,817✔
319
         if (ITEM_OBJ_ARRAY & mask)
179✔
320
            obj_array_free(&(tmp[op].obj_array));
146✔
321
         ++op;
179✔
322
      }
323
      else if (new_has & mask)
1,015,638✔
324
         memset(&(object->items[np++]), '\0', sizeof(item_t));
1,114,570✔
325
   }
326

327
   object->kind = kind;
28,648✔
328
}
329

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

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

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

339
#ifdef DEBUG
340
   imask_t all_items = 0;
24,912✔
341
#endif
342

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

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

352
   bool changed = false;
37,368✔
353
   do {
37,368✔
354
      changed = false;
37,368✔
355
      for (int i = 0; i < class->last_kind; i++) {
2,989,440✔
356
         size_t max_size = class->object_size[i];
2,952,072✔
357
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
38,526,408✔
358
            if (class->change_allowed[j][0] == i)
35,574,336✔
359
               max_size = MAX(max_size,
298,944✔
360
                              class->object_size[class->change_allowed[j][1]]);
361
            else if (class->change_allowed[j][1] == i)
35,275,392✔
362
               max_size = MAX(max_size,
298,944✔
363
                              class->object_size[class->change_allowed[j][0]]);
364
         }
365

366
         if (max_size != class->object_size[i]) {
2,952,072✔
367
            class->object_size[i] = max_size;
74,736✔
368
            changed = true;
74,736✔
369
         }
370
      }
371
   } while (changed);
37,368✔
372

373
#ifdef DEBUG
374
   if (getenv("NVC_TREE_SIZES") != NULL) {
24,912✔
UNCOV
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 =
24,912✔
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;
24,912✔
385
   if (missing != 0) {
24,912✔
386
      int item;
UNCOV
387
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
388
         ;
389

UNCOV
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
}
24,912✔
395

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

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

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

416
static void object_one_time_init(void)
30,723,341✔
417
{
418
   INIT_ONCE({
30,723,341✔
419
         extern object_class_t tree_object;
420
         object_init(&tree_object);
421

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

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

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

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

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

437
         add_fault_handler(check_frozen_object_fault, NULL);
438

439
         arena_lookup = chash_new(64);
440
      });
441
}
30,723,341✔
442

443
static bool is_gc_root(const object_class_t *class, int kind)
2,632,076✔
444
{
445
   for (int j = 0; j < class->gc_num_roots; j++) {
24,588,125✔
446
      if (class->gc_roots[j] == kind)
22,026,515✔
447
         return true;
448
   }
449

450
   return false;
451
}
452

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

459
   object_one_time_init();
30,669,486✔
460

461
   if (arena == NULL)
30,669,486✔
462
      arena = global_arena;
2,250,177✔
463

464
   if (unlikely(arena == NULL))
30,669,486✔
465
      fatal_trace("allocating object without active arena");
466

467
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
30,669,486✔
468

469
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
30,669,486✔
470

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

483
   object_t *object = arena->alloc;
30,669,486✔
484
   arena->alloc = (char *)arena->alloc + size;
30,669,486✔
485

486
   if (arena->root == NULL && is_gc_root(class, kind))
30,669,486✔
487
      arena->root = object;
43,613✔
488

489
   memset(object, '\0', size);
30,669,486✔
490

491
   object->kind  = kind;
30,669,486✔
492
   object->tag   = class->tag;
30,669,486✔
493
   object->arena = arena->key;
30,669,486✔
494
   object->loc   = LOC_INVALID;
30,669,486✔
495

496
   return object;
30,669,486✔
497
}
498

499
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
5,730,179✔
500
                              generation_t generation)
501
{
502
   if (object == NULL)
5,730,179✔
503
      return;
504
   else if (!object_in_arena_p(arena, object))
4,756,628✔
505
      return;
506
   else if (object_marked_p(object, generation))
2,847,166✔
507
      return;
508

509
   const object_class_t *class = classes[object->tag];
2,154,416✔
510

511
   imask_t has = class->has_map[object->kind];
2,154,416✔
512
   for (int n = 0; has; has &= has - 1, n++) {
11,598,459✔
513
      const uint64_t mask = has & -has;
9,444,043✔
514
      item_t *item = &(object->items[n]);
9,444,043✔
515
      if (ITEM_OBJECT & mask)
9,444,043✔
516
         gc_mark_from_root(item->object, arena, generation);
4,038,188✔
517
      else if (ITEM_OBJ_ARRAY & mask) {
5,405,855✔
518
         if (item->obj_array != NULL) {
964,687✔
519
            for (unsigned j = 0; j < item->obj_array->count; j++)
2,279,301✔
520
               gc_mark_from_root(item->obj_array->items[j], arena,
1,665,138✔
521
                                 generation);
522
         }
523
      }
524
   }
525
}
526

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

531
   imask_t has = class->has_map[object->kind];
434,046✔
532
   if ((has & (ITEM_OBJ_ARRAY | ITEM_NUMBER)) == 0)
434,046✔
533
      return;
534

535
   for (int n = 0; has; has &= has - 1, n++) {
539,872✔
536
      const uint64_t mask = has & -has;
444,780✔
537
      item_t *item = &(object->items[n]);
444,780✔
538
      if (ITEM_OBJ_ARRAY & mask)
444,780✔
539
         obj_array_free(&(item->obj_array));
117,815✔
540
      else if (ITEM_NUMBER & mask)
326,965✔
UNCOV
541
         number_free(&item->number);
×
542
   }
543
}
544

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

550
   // Mark
551
   for (void *p = arena->base; p != arena->alloc; ) {
2,608,727✔
552
      assert(p < arena->alloc);
2,588,462✔
553
      object_t *object = p;
2,588,462✔
554

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

557
      if (is_gc_root(class, object->kind))
2,588,462✔
558
         gc_mark_from_root(object, arena, generation);
26,853✔
559

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

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

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

571
   // Calculate forwarding addresses
572
   unsigned woffset = 0, live = 0, dead = 0;
20,265✔
573
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,608,727✔
574
      assert(rptr < arena->alloc);
2,588,462✔
575
      object_t *object = rptr;
2,588,462✔
576

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

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

582
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,588,462✔
583
      if (!object_marked_p(object, generation)) {
2,588,462✔
584
         forward[index] = UINT32_MAX;
434,046✔
585
         gc_free_external(object);
434,046✔
586
         dead++;
434,046✔
587
      }
588
      else {
589
         forward[index] = woffset;
2,154,416✔
590
         woffset += size;
2,154,416✔
591
         live++;
2,154,416✔
592
      }
593

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

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

601
   arena->forward = forward;
20,265✔
602
   arena->live_bytes = woffset;
20,265✔
603

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

611
void object_visit(object_t *object, object_visit_ctx_t *ctx)
950,363✔
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)
950,363✔
617
      return;
618
   else if (object_marked_p(object, ctx->generation))
711,430✔
619
      return;
620

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

623
   const bool visit =
1,352,622✔
624
      (object->tag == ctx->tag && object->kind == ctx->kind)
676,311✔
625
      || ctx->kind == class->last_kind;
1,349,868✔
626

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

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

632
   imask_t has = class->has_map[object->kind];
676,311✔
633
   for (int n = 0; has; has &= has - 1, n++) {
3,544,518✔
634
      const uint64_t mask = has & -has;
2,868,207✔
635
      if (mask & deep_mask) {
2,868,207✔
636
         item_t *item = &(object->items[n]);
2,054,107✔
637
         if (ITEM_OBJECT & mask)
2,054,107✔
638
            object_visit(item->object, ctx);
485,018✔
639
         else if (ITEM_OBJ_ARRAY & mask) {
1,569,089✔
640
            if (item->obj_array != NULL) {
220,309✔
641
               for (unsigned j = 0; j < item->obj_array->count; j++)
585,386✔
642
                  object_visit(item->obj_array->items[j], ctx);
450,026✔
643
            }
644
         }
645
      }
646
   }
647

648
   if (visit) {
676,311✔
649
      if (ctx->postorder != NULL)
660,940✔
650
         (*ctx->postorder)(object, ctx->context);
660,940✔
651
      ctx->count++;
660,940✔
652
   }
653
}
654

655
static object_t *object_rewrite_iter(object_t *object,
4,218,396✔
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);
4,218,396✔
662
   if (new == object)
4,218,391✔
663
      return new;
664
   else
665
      return object_rewrite(new, ctx);
91,049✔
666
}
667

668
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
9,719,680✔
669
{
670
   if (object == NULL)
9,719,680✔
671
      return NULL;
672

673
   if (!object_in_arena_p(ctx->arena, object))
8,100,769✔
674
      return object;
675

676
   const ptrdiff_t index =
5,388,777✔
677
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
5,388,777✔
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)) {
5,388,777✔
682
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
92,954✔
683
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
92,954✔
684
                                  ctx->cache_sz);
685
   }
686

687
   if (object_marked_p(object, ctx->generation)) {
5,388,777✔
688
      if (ctx->cache[index] == (object_t *)-1) {
907,799✔
689
         // Found a circular reference: eagerly rewrite the object now
690
         // and break the cycle
691
         if (ctx->post_fn[object->tag] != NULL) {
1,333✔
692
            if (ctx->pre_fn[object->tag] != NULL)
49✔
UNCOV
693
               (*ctx->pre_fn[object->tag])(object, ctx->context);
×
694
            object_t *new = object_rewrite_iter(object, ctx);
49✔
695
            object_write_barrier(object, new);
49✔
696
            return (ctx->cache[index] = new);
49✔
697
         }
698
         else
699
            return (ctx->cache[index] = object);
1,284✔
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
4,480,978✔
708

709
   if (ctx->pre_fn[object->tag] != NULL)
4,480,978✔
UNCOV
710
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
711

712
   const imask_t skip_mask =
4,480,978✔
713
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
714

715
   const object_class_t *class = classes[object->tag];
4,480,978✔
716

717
   imask_t has = class->has_map[object->kind];
4,480,978✔
718
   for (int n = 0; has; has &= has - 1, n++) {
24,051,555✔
719
      const uint64_t mask = has & -has;
19,570,587✔
720
      if (mask & ~skip_mask) {
19,570,587✔
721
         if (ITEM_OBJECT & mask) {
8,185,531✔
722
            object_t *o = object_rewrite(object->items[n].object, ctx);
6,416,509✔
723
            object->items[n].object = o;
6,416,504✔
724
            object_write_barrier(object, o);
6,416,504✔
725
         }
726
         else if (ITEM_OBJ_ARRAY & mask) {
1,769,022✔
727
            obj_array_t **a = &(object->items[n].obj_array);
1,769,022✔
728
            if (object->items[n].obj_array != NULL) {
1,769,022✔
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++) {
4,344,696✔
733
                  object_t *o = object->items[n].obj_array->items[i];
3,174,922✔
734
                  if ((o = object_rewrite(o, ctx))) {
3,174,922✔
735
                     object_write_barrier(object, o);
3,168,484✔
736
                     object->items[n].obj_array->items[wptr++] = o;
3,168,484✔
737
                  }
738
               }
739

740
               if (wptr == 0)
1,169,774✔
741
                  obj_array_free(a);
1,218✔
742
               else
743
                  (*a)->count = wptr;
1,168,556✔
744
            }
745
         }
746
         else
747
            should_not_reach_here();
748
      }
749
   }
750

751
   if (ctx->cache[index] != (object_t *)-1) {
4,480,968✔
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) {
4,479,635✔
756
      object_t *new = object_rewrite_iter(object, ctx);
4,218,347✔
757
      object_write_barrier(object, new);
4,218,342✔
758
      return (ctx->cache[index] = new);
4,218,342✔
759
   }
760
   else
761
      return (ctx->cache[index] = object);
261,288✔
762
}
763

764
static void object_write_ref(object_t *object, fbuf_t *f)
5,045,577✔
765
{
766
   if (object == NULL)
5,045,577✔
767
      fbuf_put_uint(f, 0);
873,932✔
768
   else {
769
      object_arena_t *arena = __object_arena(object);
4,171,645✔
770
      assert(arena->key != 0);
4,171,645✔
771
      fbuf_put_uint(f, arena->key);
4,171,645✔
772

773
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
4,171,645✔
774
      if (arena->forward != NULL)
4,171,645✔
775
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,612,702✔
776
      else
777
         fbuf_put_uint(f, index);
1,558,943✔
778
   }
779
}
5,045,577✔
780

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

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

803
   arena_key_t max_key = arena->key;
14,283✔
804
   for (unsigned i = 0; i < arena->deps.count; i++)
43,274✔
805
      max_key = MAX(max_key, arena->deps.items[i]->key);
28,991✔
806
   fbuf_put_uint(f, max_key);
14,283✔
807

808
   fbuf_put_uint(f, arena->deps.count);
14,283✔
809
   for (unsigned i = 0; i < arena->deps.count; i++) {
43,274✔
810
      fbuf_put_uint(f, arena->deps.items[i]->key);
28,991✔
811
      fbuf_put_uint(f, arena->deps.items[i]->std);
28,991✔
812
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
28,991✔
813
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
28,991✔
814
   }
815

816
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
2,259,762✔
817
      assert(p < arena->alloc);
2,245,479✔
818

819
      object_t *object = p;
2,245,479✔
820
      object_class_t *class = classes[object->tag];
2,245,479✔
821

822
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,245,479✔
823

824
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
2,245,479✔
825
      if (arena->forward[index] == UINT32_MAX)
2,245,479✔
826
         continue;   // Dead object
312,023✔
827

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

831
      if (class->has_loc)
1,933,456✔
832
         loc_write(&object->loc, loc_ctx);
1,813,225✔
833

834
      imask_t has = class->has_map[object->kind];
1,933,456✔
835
      for (int n = 0; has; has &= has - 1, n++) {
10,352,310✔
836
         const uint64_t mask = has & -has;
8,418,854✔
837
         item_t *item = &(object->items[n]);
8,418,854✔
838
         if (ITEM_IDENT & mask)
8,418,854✔
839
            ident_write(item->ident, ident_ctx);
1,576,647✔
840
         else if (ITEM_OBJECT & mask)
6,842,207✔
841
            object_write_ref(item->object, f);
3,680,962✔
842
         else if (ITEM_OBJ_ARRAY & mask) {
3,161,245✔
843
            if (item->obj_array != NULL) {
745,432✔
844
               const unsigned count = item->obj_array->count;
482,473✔
845
               fbuf_put_uint(f, count);
482,473✔
846
               for (unsigned i = 0; i < count; i++)
1,847,088✔
847
                  object_write_ref(item->obj_array->items[i], f);
1,364,615✔
848
            }
849
            else
850
               fbuf_put_uint(f, 0);
262,959✔
851
         }
852
         else if (ITEM_INT64 & mask)
2,415,813✔
853
            fbuf_put_int(f, item->ival);
432,126✔
854
         else if (ITEM_INT32 & mask)
1,983,687✔
855
            fbuf_put_int(f, item->ival);
1,849,515✔
856
         else if (ITEM_DOUBLE & mask)
134,172✔
857
            write_double(item->dval, f);
132,564✔
858
         else if (ITEM_NUMBER & mask)
1,608✔
859
            number_write(item->number, f);
1,608✔
860
         else
861
            should_not_reach_here();
862
      }
863
   }
864

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

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

874
   arena_key_t mapped = key_map[key];
61,562,062✔
875
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
61,562,062✔
876

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

880
   assert(mapped < all_arenas.count);
61,562,062✔
881
   assert(mapped > 0);
61,562,062✔
882

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

886
   return (object_t *)((char *)arena->base + offset);
61,562,062✔
887
}
888

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

894
   const uint32_t ver = read_u32(f);
22,760✔
895
   if (ver != format_digest)
22,760✔
UNCOV
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);
22,760✔
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)
22,760✔
906
      set_default_standard(std);
627✔
907

908
   if (std > standard())
22,760✔
909
      fatal("%s: design unit was analysed using standard revision %s which "
8✔
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);
22,752✔
914
   if (size & OBJECT_PAGE_MASK)
22,752✔
UNCOV
915
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
916

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

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

924
   arena_key_t max_key = fbuf_get_uint(f);
22,752✔
925

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

929
   const int ndeps = fbuf_get_uint(f);
22,752✔
930
   for (int i = 0; i < ndeps; i++) {
50,533✔
931
      arena_key_t dkey = fbuf_get_uint(f);
27,785✔
932
      vhdl_standard_t dstd = fbuf_get_uint(f);
27,785✔
933
      uint32_t checksum = fbuf_get_uint(f);
27,785✔
934
      ident_t dep = ident_read(ident_ctx);
27,785✔
935

936
      object_arena_t *a = NULL;
27,785✔
937
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
129,875✔
938
         if (dep == object_arena_name(all_arenas.items[j]))
102,090✔
939
            a = all_arenas.items[j];
22,853✔
940
      }
941

942
      if (a == NULL) {
27,785✔
943
         object_t *droot = NULL;
4,932✔
944
         if (loader_fn) droot = (*loader_fn)(dep);
4,932✔
945

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

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

953
      if (a->std != dstd)
27,785✔
UNCOV
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) {
27,785✔
958
         diag_t *d = diag_new(DIAG_FATAL, NULL);
4✔
959
         diag_suppress(d, false);
4✔
960
         diag_printf(d, "%s: design unit depends on %s with checksum %08x "
4✔
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 "
4✔
964
                   "be reanalysed", istr(name));
965
         diag_emit(d);
4✔
966
         fatal_exit(EXIT_FAILURE);
4✔
967
      }
968

969
      APUSH(arena->deps, a);
27,781✔
970

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

975
   for (;;) {
28,036,794✔
976
      const uint64_t hdr = fbuf_get_uint(f);
28,036,794✔
977
      if (hdr == UINT16_MAX) break;
28,036,794✔
978

979
      const unsigned tag = hdr & 3;
28,014,046✔
980
      const unsigned kind = hdr >> 2;
28,014,046✔
981

982
      assert(tag < OBJECT_TAG_COUNT);
28,014,046✔
983

984
      const object_class_t *class = classes[tag];
28,014,046✔
985

986
      object_t *object = object_new(arena, class, kind);
28,014,046✔
987

988
      if (class->has_loc)
28,014,046✔
989
         loc_read(&(object->loc), loc_ctx);
25,328,237✔
990

991
      imask_t has = class->has_map[object->kind];
28,014,046✔
992
      for (int n = 0; has; has &= has - 1, n++) {
153,444,021✔
993
         const uint64_t mask = has & -has;
125,429,975✔
994
         item_t *item = &(object->items[n]);
125,429,975✔
995
         if (ITEM_IDENT & mask)
125,429,975✔
996
            item->ident = ident_read(ident_ctx);
25,346,348✔
997
         else if (ITEM_OBJECT & mask)
100,083,627✔
998
            item->object = object_read_ref(f, key_map);
50,503,986✔
999
         else if (ITEM_OBJ_ARRAY & mask) {
49,579,641✔
1000
            const unsigned count = fbuf_get_uint(f);
11,587,367✔
1001
            if (count > 0) {
11,587,367✔
1002
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
7,760,957✔
1003
                                              count, sizeof(object_t *));
1004
               item->obj_array->count =
7,760,957✔
1005
                  item->obj_array->limit = count;
7,760,957✔
1006
               for (unsigned i = 0; i < count; i++) {
29,725,458✔
1007
                  object_t *o = object_read_ref(f, key_map);
21,964,501✔
1008
                  item->obj_array->items[i] = o;
21,964,501✔
1009
               }
1010
            }
1011
         }
1012
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
37,992,274✔
1013
            item->ival = fbuf_get_int(f);
37,016,579✔
1014
         else if (ITEM_DOUBLE & mask)
975,695✔
1015
            item->dval = read_double(f);
975,695✔
UNCOV
1016
         else if (ITEM_NUMBER & mask)
×
UNCOV
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);
22,748✔
1024

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

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

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

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

1043
   if (ctx->copy_map == NULL)
2,183,613✔
1044
      ctx->copy_map = hash_new(1024);
10,770✔
1045

1046
   if (object_marked_p(object, ctx->generation))
2,183,613✔
1047
      return hash_get(ctx->copy_map, object) != NULL;
483,196✔
1048

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

1051
   bool marked = false;
1,700,417✔
1052
   if (ctx->should_copy[object->tag] != NULL)
1,700,417✔
1053
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,695,953✔
1054

1055
   object_t *copy = NULL;
1,695,953✔
1056
   if (marked) {
1,695,953✔
1057
      copy = object_new(global_arena, class, object->kind);
73,218✔
1058
      hash_put(ctx->copy_map, object, copy);
73,218✔
1059
   }
1060

1061
   imask_t has = class->has_map[object->kind];
1,700,417✔
1062
   for (int n = 0; has; has &= has - 1, n++) {
9,054,050✔
1063
      const uint64_t mask = has & -has;
7,353,633✔
1064
      item_t *item = &(object->items[n]);
7,353,633✔
1065
      if (ITEM_OBJECT & mask)
7,353,633✔
1066
         marked |= object_copy_mark(item->object, ctx);
3,247,759✔
1067
      else if (ITEM_OBJ_ARRAY & mask) {
4,105,874✔
1068
         if (item->obj_array != NULL) {
628,156✔
1069
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,555,796✔
1070
               object_t *o = item->obj_array->items[i];
1,147,285✔
1071
               marked |= object_copy_mark(o, ctx);
1,147,285✔
1072
            }
1073
         }
1074
      }
1075
   }
1076

1077
   if (marked && copy == NULL) {
1,700,417✔
1078
      copy = object_new(global_arena, class, object->kind);
332,045✔
1079
      hash_put(ctx->copy_map, object, copy);
332,045✔
1080
   }
1081

1082
   return marked;
1083
}
1084

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

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

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

1098
   if (a->copygen == ctx->generation)
103,446✔
1099
      return a->copyflag;
54,956✔
1100

1101
   for (int i = 0; i < ctx->nroots; i++)
132,412✔
1102
      include |= (__object_arena(ctx->roots[i]) == a);
83,922✔
1103

1104
   for (int i = 0; i < a->deps.count; i++)
132,305✔
1105
      include |= object_copy_root_closure(a->deps.items[i], ctx);
83,815✔
1106

1107
   a->copygen  = ctx->generation;
48,490✔
1108
   a->copyflag = include;
48,490✔
1109

1110
   return include;
48,490✔
1111
}
1112

1113
void object_copy(object_copy_ctx_t *ctx)
10,770✔
1114
{
1115
   for (int i = 0; i < ctx->nroots; i++) {
30,401✔
1116
      object_arena_t *a = __object_arena(ctx->roots[i]);
19,631✔
1117
      object_copy_root_closure(a, ctx);
19,631✔
1118
   }
1119

1120
   for (int i = 0; i < ctx->nroots; i++)
30,401✔
1121
      (void)object_copy_mark(ctx->roots[i], ctx);
19,631✔
1122

1123
   unsigned ncopied = 0;
10,770✔
1124
   const void *key;
10,770✔
1125
   void *value;
10,770✔
1126
   for (hash_iter_t it = HASH_BEGIN;
10,770✔
1127
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
416,033✔
1128
      const object_t *object = key;
405,263✔
1129
      object_t *copy = value;
405,263✔
1130
      ncopied++;
405,263✔
1131

1132
      copy->loc = object->loc;
405,263✔
1133

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

1136
      imask_t has = class->has_map[object->kind];
405,263✔
1137
      for (int n = 0; has; has &= has - 1, n++) {
2,223,614✔
1138
         const uint64_t mask = has & -has;
1,818,351✔
1139
         const item_t *from = &(object->items[n]);
1,818,351✔
1140
         item_t *to = &(copy->items[n]);
1,818,351✔
1141

1142
         if (ITEM_IDENT & mask)
1,818,351✔
1143
            to->ident = from->ident;
313,760✔
1144
         else if (ITEM_OBJECT & mask) {
1,504,591✔
1145
            to->object = object_copy_map(from->object, ctx);
773,149✔
1146
            object_write_barrier(copy, to->object);
773,149✔
1147
         }
1148
         else if (ITEM_DOUBLE & mask)
731,442✔
1149
            to->dval = from->dval;
153✔
1150
         else if (ITEM_OBJ_ARRAY & mask) {
731,289✔
1151
            if (from->obj_array != NULL) {
258,399✔
1152
               // TODO: make a resize macro
1153
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
367,814✔
1154
                                            from->obj_array->count,
183,907✔
1155
                                            sizeof(object_t *));
1156
               to->obj_array->count =
183,907✔
1157
                  to->obj_array->limit = from->obj_array->count;
183,907✔
1158
               for (size_t i = 0; i < from->obj_array->count; i++) {
577,519✔
1159
                  object_t *o =
393,612✔
1160
                     object_copy_map(from->obj_array->items[i], ctx);
393,612✔
1161
                  to->obj_array->items[i] = o;
393,612✔
1162
                  object_write_barrier(copy, o);
393,612✔
1163
               }
1164
            }
1165
         }
1166
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
472,890✔
1167
            to->ival = from->ival;
472,890✔
1168
         else
1169
            should_not_reach_here();
1170
      }
1171
   }
1172

1173
   for (hash_iter_t it = HASH_BEGIN;
10,770✔
1174
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
416,033✔
1175
      object_t *copy = value;
405,263✔
1176
      if (ctx->callback[copy->tag] != NULL)
405,263✔
1177
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
405,223✔
1178
   }
1179

1180
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
10,770✔
UNCOV
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++) {
30,401✔
1185
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
19,631✔
1186
      if (copy != NULL)
19,631✔
1187
         ctx->roots[i] = copy;
8,581✔
1188
   }
1189

1190
   hash_free(ctx->copy_map);
10,770✔
1191
}
10,770✔
1192

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

1198
object_arena_t *object_arena_new(size_t size, unsigned std)
43,619✔
1199
{
1200
   if (all_arenas.count == 0)
43,619✔
1201
      APUSH(all_arenas, NULL);   // Dummy null arena
6,215✔
1202

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

1211
   APUSH(all_arenas, arena);
43,619✔
1212

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

1216
   return arena;
43,619✔
1217
}
1218

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

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

1226
   if (arena->source == OBJ_FRESH)
43,013✔
1227
      object_arena_gc(arena);
20,265✔
1228

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

1233
   chash_put(arena_lookup, name, arena);
43,013✔
1234

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

1238
   if (next_page < arena->limit) {
43,013✔
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);
20,265✔
1245
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
20,265✔
1246
#endif
1247
   }
1248

1249
   arena->frozen = true;
43,013✔
1250
}
43,013✔
1251

1252
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
53,978✔
1253
{
1254
   for (unsigned i = 0; i < arena->deps.count; i++)
187,309✔
1255
      (*fn)(object_arena_name(arena->deps.items[i]), context);
133,331✔
1256
}
53,978✔
1257

1258
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
14,283✔
1259
                              void *context)
1260
{
1261
   for (unsigned i = 0; i < arena->deps.count; i++) {
43,274✔
1262
      if (arena->deps.items[i]->obsolete)
28,991✔
1263
         (*fn)(object_arena_name(arena->deps.items[i]), context);
8✔
1264
   }
1265
}
14,283✔
1266

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

1272
   *module = object_arena_name(arena);
95,183✔
1273

1274
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
95,183✔
1275
   if (arena->forward != NULL)
95,183✔
1276
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
49,971✔
1277
   else
1278
      *offset = index;
45,212✔
1279
}
95,183✔
1280

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

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

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

1295
   return NULL;
1296
}
1297

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

1303
   if (arena == NULL) {
42,225✔
1304
      object_t *droot = NULL;
626✔
1305
      if (loader) droot = (*loader)(module);
626✔
1306

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

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

1313
   assert(arena->frozen);
42,225✔
1314

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

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

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

1340
   return obj;
42,225✔
1341
}
1342

1343
void freeze_global_arena(void)
31,095✔
1344
{
1345
   object_one_time_init();
31,095✔
1346

1347
   if (global_arena != NULL) {
31,095✔
1348
      object_arena_freeze(global_arena);
20,265✔
1349
      global_arena = NULL;
20,265✔
1350
   }
1351
}
31,095✔
1352

1353
void make_new_arena(void)
20,867✔
1354
{
1355
   freeze_global_arena();
20,867✔
1356
   global_arena = object_arena_new(object_arena_default_size(), standard());
20,867✔
1357
}
20,867✔
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