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

nickg / nvc / 19789834065

29 Nov 2025 09:53PM UTC coverage: 92.566% (-0.009%) from 92.575%
19789834065

push

github

nickg
Avoid code generation for processes in cloned blocks

38 of 49 new or added lines in 4 files covered. (77.55%)

595 existing lines in 10 files now uncovered.

75316 of 81365 relevant lines covered (92.57%)

421911.4 hits per line

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

91.85
/src/object.c
1
//
2
//  Copyright (C) 2014-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "common.h"
20
#include "diag.h"
21
#include "fbuf.h"
22
#include "hash.h"
23
#include "ident.h"
24
#include "object.h"
25
#include "option.h"
26
#include "thread.h"
27

28
#include <string.h>
29
#include <stdlib.h>
30
#include <inttypes.h>
31
#include <signal.h>
32

33
typedef uint64_t mark_mask_t;
34

35
typedef A(object_arena_t *) arena_array_t;
36
typedef A(object_t **) object_ptr_array_t;
37

38
typedef enum { OBJ_DISK, OBJ_FRESH } obj_src_t;
39

40
typedef struct _object_arena {
41
   void           *base;
42
   void           *alloc;
43
   void           *limit;
44
   uint32_t       *forward;
45
   mark_mask_t    *mark_bits;
46
   size_t          mark_sz;
47
   size_t          mark_low;
48
   size_t          mark_high;
49
   uint32_t        live_bytes;
50
   uint32_t        flags;
51
   generation_t    generation;
52
   arena_key_t     key;
53
   arena_array_t   deps;
54
   object_t       *root;
55
   obj_src_t       source;
56
   vhdl_standard_t std;
57
   uint32_t        checksum;
58
   generation_t    copygen;
59
   bool            frozen;
60
   bool            obsolete;
61
} object_arena_t;
62

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

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

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

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

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

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

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

126
   return ident_new("???");
10,771✔
127
}
128

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

135
   if (count == 1)
172,337✔
136
      arena->mark_bits[first] = 0;
150,352✔
137
   else
138
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
21,985✔
139
}
172,337✔
140

141
static bool object_marked_p(object_t *object, generation_t generation)
11,057,177✔
142
{
143
   object_arena_t *arena = __object_arena(object);
11,057,177✔
144

145
   const uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
11,057,177✔
146
   const uintptr_t word = bit / 64;
11,057,177✔
147

148
   if (unlikely(arena->mark_bits == NULL)) {
11,057,177✔
149
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
17,148✔
150
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
17,148✔
151
      arena->mark_bits = xmalloc(arena->mark_sz);
17,148✔
152
      arena->mark_bits[word] = 0;
17,148✔
153
      arena->mark_low = arena->mark_high = word;
17,148✔
154
      arena->generation = generation;
17,148✔
155
   }
156
   else if (arena->generation != generation) {
11,040,029✔
157
      arena->mark_bits[word] = 0;
56,802✔
158
      arena->mark_low = arena->mark_high = word;
56,802✔
159
      arena->generation = generation;
56,802✔
160
   }
161

162
   // Lazy zeroing of mark bits helps performance with large arenas
163
   if (word < arena->mark_low) {
11,057,177✔
164
      zero_mark_bits(arena, word, arena->mark_low - word);
8,250✔
165
      arena->mark_low = word;
8,250✔
166
   }
167
   else if (word > arena->mark_high) {
11,048,927✔
168
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
164,087✔
169
      arena->mark_high = word;
164,087✔
170
   }
171

172
   const uint64_t mask = UINT64_C(1) << (bit & 63);
11,057,177✔
173

174
   const bool marked = !!(arena->mark_bits[word] & mask);
11,057,177✔
175
   arena->mark_bits[word] |= mask;
11,057,177✔
176

177
   return marked;
11,057,177✔
178
}
179

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

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

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

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

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

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

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

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

220
   if (has & I_IDENT) {
163,914✔
221
      const int n = __builtin_popcountll(has & (I_IDENT - 1));
161,587✔
222
      return object->items[n].ident;
161,587✔
223
   }
224

225
   return NULL;
226
}
227

228
void __object_write_barrier(object_t *lhs, object_t *rhs)
4,819,657✔
229
{
230
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
4,819,657✔
231
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
4,819,657✔
232

233
   if (lhs_mask == rhs_mask || rhs == NULL)
4,819,657✔
234
      return;
235
   else if (lhs->arena == rhs->arena)
4,819,657✔
236
      return;
237

238
   object_arena_t *larena = __object_arena(lhs);
4,306,199✔
239
   object_arena_t *rarena = __object_arena(rhs);
4,306,199✔
240

241
   assert(!larena->frozen);
4,306,199✔
242
   assert(rarena->frozen);
4,306,199✔
243

244
   for (unsigned i = 0; i < larena->deps.count; i++) {
6,928,562✔
245
      if (larena->deps.items[i] == rarena)
6,891,983✔
246
         return;
247
   }
248

249
   APUSH(larena->deps, rarena);
36,579✔
250
}
251

UNCOV
252
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
253
{
254
   unsigned int item;
×
255
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
256
      ;
257

258
   assert(item < ARRAY_LEN(item_text_map));
×
259

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

270
void obj_array_add(obj_array_t **a, object_t *o)
1,282,075✔
271
{
272
   if (*a == NULL) {
1,282,075✔
273
      const int defsz = 8;
459,723✔
274
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
459,723✔
275
      (*a)->count = 0;
459,723✔
276
      (*a)->limit = defsz;
459,723✔
277
   }
278
   else if ((*a)->count == (*a)->limit) {
822,352✔
279
      (*a)->limit *= 2;
31,602✔
280
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
31,602✔
281
                         (*a)->limit, sizeof(object_t *));
282
   }
283

284
   (*a)->items[(*a)->count++] = o;
1,282,075✔
285
}
1,282,075✔
286

287
void obj_array_copy(obj_array_t **dst, const obj_array_t *src)
11,261✔
288
{
289
   if (src == NULL)
11,261✔
290
      return;
291

292
   if (*dst == NULL) {
11,261✔
293
      *dst = xmalloc_flex(sizeof(obj_array_t), src->count, sizeof(object_t *));
11,169✔
294
      (*dst)->count = 0;
11,169✔
295
      (*dst)->limit = src->count;
11,169✔
296
   }
297
   else if ((*dst)->count + src->count > (*dst)->limit) {
92✔
298
      (*dst)->limit = (*dst)->count + src->count;
92✔
299
      *dst = xrealloc_flex(*dst, sizeof(obj_array_t),
92✔
300
                           (*dst)->limit, sizeof(object_t *));
301
   }
302

303
   for (int i = 0; i < src->count; i++)
53,193✔
304
      (*dst)->items[(*dst)->count++] = src->items[i];
41,932✔
305
}
306

307
void obj_array_free(obj_array_t **a)
97,942✔
308
{
309
   free(*a);
97,942✔
310
   *a = NULL;
97,942✔
311
}
97,942✔
312

313
void object_change_kind(const object_class_t *class, object_t *object, int kind)
24,276✔
314
{
24,276✔
315
   if (kind == object->kind)
24,276✔
UNCOV
316
      return;
×
317

318
   bool allow = false;
319
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
250,783✔
320
      allow = (class->change_allowed[i][0] == object->kind)
226,507✔
321
         && (class->change_allowed[i][1] == kind);
226,507✔
322
   }
323

324
   if (!allow)
24,276✔
325
      fatal_trace("cannot change %s kind %s to %s", class->name,
UNCOV
326
                  class->kind_text_map[object->kind],
×
UNCOV
327
                  class->kind_text_map[kind]);
×
328

329
   const imask_t old_has = class->has_map[object->kind];
24,276✔
330
   const imask_t new_has = class->has_map[kind];
24,276✔
331

332
   const int old_nitems = __builtin_popcountll(old_has);
24,276✔
333
   const int new_nitems = __builtin_popcountll(new_has);
24,276✔
334

335
   const int max_items = MAX(old_nitems, new_nitems);
24,276✔
336

337
   item_t tmp[max_items];
24,276✔
338
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
24,276✔
339

340
   int op = 0, np = 0;
24,276✔
341
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
979,417✔
342
      if ((old_has & mask) && (new_has & mask))
955,141✔
343
         object->items[np++] = tmp[op++];
86,983✔
344
      else if (old_has & mask) {
868,158✔
345
         if (ITEM_OBJ_ARRAY & mask)
146✔
346
            obj_array_free(&(tmp[op].obj_array));
116✔
347
         ++op;
146✔
348
      }
349
      else if (new_has & mask)
868,012✔
350
         memset(&(object->items[np++]), '\0', sizeof(item_t));
955,141✔
351
   }
352

353
   object->kind = kind;
24,276✔
354
}
355

356
static void object_init(object_class_t *class)
21,096✔
357
{
358
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
21,096✔
359

360
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
21,096✔
361

362
   assert(class->tag < ARRAY_LEN(classes));
21,096✔
363
   classes[class->tag] = class;
21,096✔
364

365
#ifdef DEBUG
366
   imask_t all_items = 0;
21,096✔
367
#endif
368

369
   for (int i = 0; i < class->last_kind; i++) {
1,408,158✔
370
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,387,062✔
371
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,387,062✔
372
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,387,062✔
373

374
      format_digest += knuth_hash(class->has_map[i] >> 32);
1,387,062✔
375
      format_digest += knuth_hash(class->has_map[i]);
1,387,062✔
376
   }
377

378
   bool changed = false;
31,644✔
379
   do {
31,644✔
380
      changed = false;
31,644✔
381
      for (int i = 0; i < class->last_kind; i++) {
2,673,918✔
382
         size_t max_size = class->object_size[i];
2,642,274✔
383
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
32,767,362✔
384
            if (class->change_allowed[j][0] == i)
30,125,088✔
385
               max_size = MAX(max_size,
253,152✔
386
                              class->object_size[class->change_allowed[j][1]]);
387
            else if (class->change_allowed[j][1] == i)
29,871,936✔
388
               max_size = MAX(max_size,
253,152✔
389
                              class->object_size[class->change_allowed[j][0]]);
390
         }
391

392
         if (max_size != class->object_size[i]) {
2,642,274✔
393
            class->object_size[i] = max_size;
63,288✔
394
            changed = true;
63,288✔
395
         }
396
      }
397
   } while (changed);
31,644✔
398

399
#ifdef DEBUG
400
   if (getenv("NVC_TREE_SIZES") != NULL) {
21,096✔
UNCOV
401
      for (int i = 0; i < class->last_kind; i++)
×
UNCOV
402
         printf("%-15s %d\n", class->kind_text_map[i],
×
403
                (int)class->object_size[i]);
×
404
   }
405

406
   const imask_t known_types =
21,096✔
407
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
408
      | ITEM_DOUBLE | ITEM_NUMBER;
409

410
   const imask_t missing = all_items & ~known_types;
21,096✔
411
   if (missing != 0) {
21,096✔
412
      int item;
UNCOV
413
      for (item = 0; (missing & (UINT64_C(1) << item)) == 0; item++)
×
414
         ;
415

UNCOV
416
      assert(item < ARRAY_LEN(item_text_map));
×
417
      fatal_trace("item %s does not have a type", item_text_map[item]);
418
   }
419
#endif
420
}
21,096✔
421

UNCOV
422
static void check_frozen_object_fault(int sig, void *addr,
×
423
                                      struct cpu_state *cpu, void *context)
424
{
425
#ifndef __MINGW32__
UNCOV
426
   if (sig != SIGSEGV && sig != SIGBUS)
×
427
      return;
428
#endif
429

UNCOV
430
   for (unsigned i = 1; i < all_arenas.count; i++) {
×
UNCOV
431
      object_arena_t *arena = AGET(all_arenas, i);
×
UNCOV
432
      if (!arena->frozen)
×
UNCOV
433
         continue;
×
UNCOV
434
      else if (addr < arena->base)
×
UNCOV
435
         continue;
×
UNCOV
436
      else if (addr >= arena->limit)
×
UNCOV
437
         continue;
×
438

439
      fatal_trace("Write to object in frozen arena %s [address=%p]",
440
                  istr(object_arena_name(arena)), addr);
441
   }
442
}
443

444
static void object_one_time_init(void)
25,342,762✔
445
{
446
   INIT_ONCE({
25,342,762✔
447
         extern object_class_t tree_object;
448
         object_init(&tree_object);
449

450
         extern object_class_t type_object;
451
         object_init(&type_object);
452

453
         extern object_class_t vlog_object;
454
         object_init(&vlog_object);
455

456
         extern object_class_t psl_object;
457
         object_init(&psl_object);
458

459
         // Increment this each time a incompatible change is made to
460
         // the on-disk format not expressed in the object items table
461
         const uint32_t format_fudge = 45;
462

463
         format_digest += format_fudge * UINT32_C(2654435761);
464

465
         add_fault_handler(check_frozen_object_fault, NULL);
466

467
         arena_lookup = chash_new(64);
468
      });
469
}
25,342,762✔
470

471
static bool is_gc_root(const object_class_t *class, int kind)
2,144,507✔
472
{
473
   for (int j = 0; j < class->gc_num_roots; j++) {
19,894,598✔
474
      if (class->gc_roots[j] == kind)
17,808,046✔
475
         return true;
476
   }
477

478
   return false;
479
}
480

481
object_t *object_new(object_arena_t *arena,
25,298,008✔
482
                     const object_class_t *class, int kind)
483
{
484
   if (unlikely(kind >= class->last_kind))
25,298,008✔
485
      fatal_trace("invalid kind %d for %s object", kind, class->name);
486

487
   object_one_time_init();
25,298,008✔
488

489
   if (arena == NULL)
25,298,008✔
490
      arena = global_arena;
1,918,388✔
491

492
   if (unlikely(arena == NULL))
25,298,008✔
493
      fatal_trace("allocating object without active arena");
494

495
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
25,298,008✔
496

497
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
25,298,008✔
498

499
   if (unlikely(arena->limit - arena->alloc < size)) {
25,298,008✔
UNCOV
500
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
UNCOV
501
      diag_suppress(d, false);
×
UNCOV
502
      diag_printf(d, "memory exhausted while creating unit %s",
×
503
                  istr(object_arena_name(arena)));
UNCOV
504
      diag_hint(d, NULL, "The current limit is %zu bytes which you can "
×
505
                "increase with the $bold$-M$$ option, for example "
506
                "$bold$-M 32m$$", object_arena_default_size());
UNCOV
507
      diag_emit(d);
×
UNCOV
508
      fatal_exit(EXIT_FAILURE);
×
509
   }
510

511
   object_t *object = arena->alloc;
25,298,008✔
512
   arena->alloc = (char *)arena->alloc + size;
25,298,008✔
513

514
   if (arena->root == NULL && is_gc_root(class, kind))
25,298,008✔
515
      arena->root = object;
36,338✔
516

517
   memset(object, '\0', size);
25,298,008✔
518

519
   object->kind  = kind;
25,298,008✔
520
   object->tag   = class->tag;
25,298,008✔
521
   object->arena = arena->key;
25,298,008✔
522
   object->loc   = LOC_INVALID;
25,298,008✔
523

524
   return object;
25,298,008✔
525
}
526

527
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,754,811✔
528
                              generation_t generation)
529
{
530
   if (object == NULL)
4,754,811✔
531
      return;
532
   else if (!object_in_arena_p(arena, object))
3,956,080✔
533
      return;
534
   else if (object_marked_p(object, generation))
2,391,892✔
535
      return;
536

537
   const object_class_t *class = classes[object->tag];
1,800,050✔
538

539
   imask_t has = class->has_map[object->kind];
1,800,050✔
540
   for (int n = 0; has; has &= has - 1, n++) {
9,644,592✔
541
      const uint64_t mask = has & -has;
7,844,542✔
542
      item_t *item = &(object->items[n]);
7,844,542✔
543
      if (ITEM_OBJECT & mask)
7,844,542✔
544
         gc_mark_from_root(item->object, arena, generation);
3,353,777✔
545
      else if (ITEM_OBJ_ARRAY & mask) {
4,490,765✔
546
         if (item->obj_array != NULL) {
798,618✔
547
            for (unsigned j = 0; j < item->obj_array->count; j++)
1,886,086✔
548
               gc_mark_from_root(item->obj_array->items[j], arena,
1,379,417✔
549
                                 generation);
550
         }
551
      }
552
   }
553
}
554

555
static void gc_free_external(object_t *object)
308,104✔
556
{
557
   const object_class_t *class = classes[object->tag];
308,104✔
558

559
   imask_t has = class->has_map[object->kind];
308,104✔
560
   if ((has & ITEM_OBJ_ARRAY) == 0)
308,104✔
561
      return;
562

563
   for (int n = 0; has; has &= has - 1, n++) {
361,317✔
564
      const uint64_t mask = has & -has;
301,060✔
565
      item_t *item = &(object->items[n]);
301,060✔
566
      if (ITEM_OBJ_ARRAY & mask)
301,060✔
567
         obj_array_free(&(item->obj_array));
97,000✔
568
   }
569
}
570

571
static void object_arena_gc(object_arena_t *arena)
16,693✔
572
{
573
   const generation_t generation = object_next_generation();
16,693✔
574
   const uint64_t start_ticks = get_timestamp_us();
16,693✔
575

576
   // Mark
577
   for (void *p = arena->base; p != arena->alloc; ) {
2,124,847✔
578
      assert(p < arena->alloc);
2,108,154✔
579
      object_t *object = p;
2,108,154✔
580

581
      const object_class_t *class = classes[object->tag];
2,108,154✔
582

583
      if (is_gc_root(class, object->kind))
2,108,154✔
584
         gc_mark_from_root(object, arena, generation);
21,617✔
585

586
      const size_t size =
2,108,154✔
587
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,108,154✔
588
      p = (char *)p + size;
2,108,154✔
589
   }
590

591
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
16,693✔
592
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
16,693✔
593

594
   // Must initialise here for the search in object_from_locus
595
   memset(forward, 0xff, fwdsz * sizeof(uint32_t));
16,693✔
596

597
   // Calculate forwarding addresses
598
   unsigned woffset = 0, live = 0, dead = 0;
16,693✔
599
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,124,847✔
600
      assert(rptr < arena->alloc);
2,108,154✔
601
      object_t *object = rptr;
2,108,154✔
602

603
      const object_class_t *class = classes[object->tag];
2,108,154✔
604

605
      const size_t size =
2,108,154✔
606
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,108,154✔
607

608
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,108,154✔
609
      if (!object_marked_p(object, generation)) {
2,108,154✔
610
         forward[index] = UINT32_MAX;
308,104✔
611
         gc_free_external(object);
308,104✔
612
         dead++;
308,104✔
613
      }
614
      else {
615
         forward[index] = woffset;
1,800,050✔
616
         woffset += size;
1,800,050✔
617
         live++;
1,800,050✔
618
      }
619

620
      rptr = (char *)rptr + size;
2,108,154✔
621
   }
622

623
   if (woffset == 0)
16,693✔
624
      fatal_trace("GC removed all objects from arena %s",
625
                  istr(object_arena_name(arena)));
626

627
   arena->forward = forward;
16,693✔
628
   arena->live_bytes = woffset;
16,693✔
629

630
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
16,693✔
UNCOV
631
      const int ticks = get_timestamp_us() - start_ticks;
×
UNCOV
632
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
633
             istr(object_arena_name(arena)), dead, live, ticks);
634
   }
635
}
16,693✔
636

637
void object_visit(object_t *object, object_visit_ctx_t *ctx)
741,370✔
638
{
639
   // If `deep' then will follow links above the tree originally passed
640
   // to tree_visit - e.g. following references back to their declarations
641

642
   if (object == NULL)
741,370✔
643
      return;
644
   else if (object_marked_p(object, ctx->generation))
558,068✔
645
      return;
646

647
   const object_class_t *class = classes[object->tag];
530,870✔
648

649
   const bool visit =
1,061,740✔
650
      (object->tag == ctx->tag && object->kind == ctx->kind)
530,870✔
651
      || ctx->kind == class->last_kind;
1,059,691✔
652

653
   if (visit && ctx->preorder != NULL)
530,870✔
UNCOV
654
      (*ctx->preorder)(object, ctx->context);
×
655

656
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
530,870✔
657

658
   imask_t has = class->has_map[object->kind];
530,870✔
659
   for (int n = 0; has; has &= has - 1, n++) {
2,789,172✔
660
      const uint64_t mask = has & -has;
2,258,302✔
661
      if (mask & deep_mask) {
2,258,302✔
662
         item_t *item = &(object->items[n]);
1,599,972✔
663
         if (ITEM_OBJECT & mask)
1,599,972✔
664
            object_visit(item->object, ctx);
369,909✔
665
         else if (ITEM_OBJ_ARRAY & mask) {
1,230,063✔
666
            if (item->obj_array != NULL) {
165,872✔
667
               for (unsigned j = 0; j < item->obj_array->count; j++)
462,272✔
668
                  object_visit(item->obj_array->items[j], ctx);
360,651✔
669
            }
670
         }
671
      }
672
   }
673

674
   if (visit) {
530,870✔
675
      if (ctx->postorder != NULL)
519,482✔
676
         (*ctx->postorder)(object, ctx->context);
519,482✔
677
      ctx->count++;
519,482✔
678
   }
679
}
680

681
static object_t *object_rewrite_iter(object_t *object,
3,462,501✔
682
                                     object_rewrite_ctx_t *ctx)
683
{
684
   // The callback may return a new object or a pointer to an existing
685
   // object in the same arena that that needs to be rewritten so
686
   // iterate rewriting until we reach a fixed point
687
   object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,462,501✔
688
   if (new == object)
3,462,497✔
689
      return new;
690
   else
691
      return object_rewrite(new, ctx);
56,132✔
692
}
693

694
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
7,957,106✔
695
{
696
   if (object == NULL)
7,957,106✔
697
      return NULL;
698

699
   if (!object_in_arena_p(ctx->arena, object))
6,633,100✔
700
      return object;
701

702
   const ptrdiff_t index =
4,425,135✔
703
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,425,135✔
704

705
   // New objects can be allocated while rewrite is in progress so we
706
   // need to check if the index is greater than the current cache size
707
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
4,425,135✔
708
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
68,713✔
709
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
68,713✔
710
                                  ctx->cache_sz);
711
   }
712

713
   if (object_marked_p(object, ctx->generation)) {
4,425,135✔
714
      if (ctx->cache[index] == (object_t *)-1) {
744,492✔
715
         // Found a circular reference: eagerly rewrite the object now
716
         // and break the cycle
717
         if (ctx->post_fn[object->tag] != NULL) {
1,107✔
718
            if (ctx->pre_fn[object->tag] != NULL)
50✔
UNCOV
719
               (*ctx->pre_fn[object->tag])(object, ctx->context);
×
720
            object_t *new = object_rewrite_iter(object, ctx);
50✔
721
            object_write_barrier(object, new);
50✔
722
            return (ctx->cache[index] = new);
50✔
723
         }
724
         else
725
            return (ctx->cache[index] = object);
1,057✔
726
      }
727
      else {
728
         // Already rewritten this tree so return the cached version
729
         return ctx->cache[index];
730
      }
731
   }
732

733
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,680,643✔
734

735
   if (ctx->pre_fn[object->tag] != NULL)
3,680,643✔
UNCOV
736
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
737

738
   const imask_t skip_mask =
3,680,643✔
739
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER | ITEM_IDENT;
740

741
   const object_class_t *class = classes[object->tag];
3,680,643✔
742

743
   imask_t has = class->has_map[object->kind];
3,680,643✔
744
   for (int n = 0; has; has &= has - 1, n++) {
19,721,020✔
745
      const uint64_t mask = has & -has;
16,040,385✔
746
      if (mask & ~skip_mask) {
16,040,385✔
747
         if (ITEM_OBJECT & mask) {
6,726,289✔
748
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,269,187✔
749
            object->items[n].object = o;
5,269,183✔
750
            object_write_barrier(object, o);
5,269,183✔
751
         }
752
         else if (ITEM_OBJ_ARRAY & mask) {
1,457,102✔
753
            obj_array_t **a = &(object->items[n].obj_array);
1,457,102✔
754
            if (object->items[n].obj_array != NULL) {
1,457,102✔
755
               // The callback may add new items to the array so the
756
               // array pointer cannot be cached between iterations
757
               unsigned wptr = 0;
758
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,549,694✔
759
                  object_t *o = object->items[n].obj_array->items[i];
2,601,246✔
760
                  if ((o = object_rewrite(o, ctx))) {
2,601,246✔
761
                     object_write_barrier(object, o);
2,596,869✔
762
                     object->items[n].obj_array->items[wptr++] = o;
2,596,869✔
763
                  }
764
               }
765

766
               if (wptr == 0)
948,448✔
767
                  obj_array_free(a);
826✔
768
               else
769
                  (*a)->count = wptr;
947,622✔
770
            }
771
         }
772
         else
773
            should_not_reach_here();
774
      }
775
   }
776

777
   if (ctx->cache[index] != (object_t *)-1) {
3,680,635✔
778
      // The cache was already updated due to a circular reference
779
      return ctx->cache[index];
780
   }
781
   else if (ctx->post_fn[object->tag] != NULL) {
3,679,528✔
782
      object_t *new = object_rewrite_iter(object, ctx);
3,462,451✔
783
      object_write_barrier(object, new);
3,462,447✔
784
      return (ctx->cache[index] = new);
3,462,447✔
785
   }
786
   else
787
      return (ctx->cache[index] = object);
217,077✔
788
}
789

790
static void object_write_ref(object_t *object, fbuf_t *f)
4,260,998✔
791
{
792
   if (object == NULL)
4,260,998✔
793
      fbuf_put_uint(f, 0);
733,659✔
794
   else {
795
      object_arena_t *arena = __object_arena(object);
3,527,339✔
796
      assert(arena->key != 0);
3,527,339✔
797
      fbuf_put_uint(f, arena->key);
3,527,339✔
798

799
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,527,339✔
800
      if (arena->forward != NULL)
3,527,339✔
801
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,219,283✔
802
      else
803
         fbuf_put_uint(f, index);
1,308,056✔
804
   }
805
}
4,260,998✔
806

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

822
   write_u32(format_digest, f);
11,333✔
823
   fbuf_put_uint(f, standard());
11,333✔
824
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
11,333✔
825
   fbuf_put_uint(f, arena->flags);
11,333✔
826
   fbuf_put_uint(f, arena->key);
11,333✔
827
   ident_write(object_arena_name(arena), ident_ctx);
11,333✔
828

829
   arena_key_t max_key = arena->key;
11,333✔
830
   for (unsigned i = 0; i < arena->deps.count; i++)
33,878✔
831
      max_key = MAX(max_key, arena->deps.items[i]->key);
22,545✔
832
   fbuf_put_uint(f, max_key);
11,333✔
833

834
   fbuf_put_uint(f, arena->deps.count);
11,333✔
835
   for (unsigned i = 0; i < arena->deps.count; i++) {
33,878✔
836
      fbuf_put_uint(f, arena->deps.items[i]->key);
22,545✔
837
      fbuf_put_uint(f, arena->deps.items[i]->std);
22,545✔
838
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
22,545✔
839
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
22,545✔
840
   }
841

842
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
1,913,240✔
843
      assert(p < arena->alloc);
1,901,907✔
844

845
      object_t *object = p;
1,901,907✔
846
      object_class_t *class = classes[object->tag];
1,901,907✔
847

848
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,901,907✔
849

850
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,901,907✔
851
      if (arena->forward[index] == UINT32_MAX)
1,901,907✔
852
         continue;   // Dead object
260,176✔
853

854
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,641,731✔
855
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,641,731✔
856

857
      if (class->has_loc)
1,641,731✔
858
         loc_write(&object->loc, loc_ctx);
1,540,648✔
859

860
      imask_t has = class->has_map[object->kind];
1,641,731✔
861
      for (int n = 0; has; has &= has - 1, n++) {
8,747,000✔
862
         const uint64_t mask = has & -has;
7,105,269✔
863
         item_t *item = &(object->items[n]);
7,105,269✔
864
         if (ITEM_IDENT & mask)
7,105,269✔
865
            ident_write(item->ident, ident_ctx);
1,325,745✔
866
         else if (ITEM_OBJECT & mask)
5,779,524✔
867
            object_write_ref(item->object, f);
3,112,414✔
868
         else if (ITEM_OBJ_ARRAY & mask) {
2,667,110✔
869
            if (item->obj_array != NULL) {
633,059✔
870
               const unsigned count = item->obj_array->count;
410,611✔
871
               fbuf_put_uint(f, count);
410,611✔
872
               for (unsigned i = 0; i < count; i++)
1,559,195✔
873
                  object_write_ref(item->obj_array->items[i], f);
1,148,584✔
874
            }
875
            else
876
               fbuf_put_uint(f, 0);
222,448✔
877
         }
878
         else if (ITEM_INT64 & mask)
2,034,051✔
879
            fbuf_put_int(f, item->ival);
362,112✔
880
         else if (ITEM_INT32 & mask)
1,671,939✔
881
            fbuf_put_int(f, item->ival);
1,558,322✔
882
         else if (ITEM_DOUBLE & mask)
113,617✔
883
            write_double(item->dval, f);
107,890✔
884
         else if (ITEM_NUMBER & mask)
5,727✔
885
            number_write(item->number, f);
5,727✔
886
         else
887
            should_not_reach_here();
888
      }
889
   }
890

891
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
11,333✔
892
}
11,333✔
893

894
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
59,786,305✔
895
{
896
   arena_key_t key = fbuf_get_uint(f);
59,786,305✔
897
   if (key == 0)
59,786,305✔
898
      return NULL;
899

900
   arena_key_t mapped = key_map[key];
50,771,167✔
901
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
50,771,167✔
902

903
   if (unlikely(mapped == 0))
50,771,167✔
904
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
905

906
   assert(mapped < all_arenas.count);
50,771,167✔
907
   assert(mapped > 0);
50,771,167✔
908

909
   object_arena_t *arena = all_arenas.items[mapped];
50,771,167✔
910
   assert(!arena->frozen || offset < arena->alloc - arena->base);
50,771,167✔
911

912
   return (object_t *)((char *)arena->base + offset);
50,771,167✔
913
}
914

915
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
19,023✔
916
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
917
{
918
   object_one_time_init();
19,023✔
919

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

927
   const vhdl_standard_t std = fbuf_get_uint(f);
19,023✔
928

929
   // If this is the first design unit we've loaded then allow it to set
930
   // the default standard
931
   if (all_arenas.count == 0)
19,023✔
932
      set_default_standard(std);
478✔
933

934
   if (std > standard())
19,023✔
935
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
936
            "is more recent that the currently selected standard %s",
937
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
938

939
   const unsigned size = fbuf_get_uint(f);
19,017✔
940
   if (size & OBJECT_PAGE_MASK)
19,017✔
UNCOV
941
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
942

943
   object_arena_t *arena = object_arena_new(size, std);
19,017✔
944
   arena->source = OBJ_DISK;
19,017✔
945
   arena->flags  = fbuf_get_uint(f);
19,017✔
946

947
   arena_key_t key = fbuf_get_uint(f);
19,017✔
948
   ident_t name = ident_read(ident_ctx);
19,017✔
949

950
   arena_key_t max_key = fbuf_get_uint(f);
19,017✔
951

952
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
19,017✔
953
   key_map[key] = arena->key;
19,017✔
954

955
   const int ndeps = fbuf_get_uint(f);
19,017✔
956
   for (int i = 0; i < ndeps; i++) {
42,174✔
957
      arena_key_t dkey = fbuf_get_uint(f);
23,160✔
958
      vhdl_standard_t dstd = fbuf_get_uint(f);
23,160✔
959
      uint32_t checksum = fbuf_get_uint(f);
23,160✔
960
      ident_t dep = ident_read(ident_ctx);
23,160✔
961

962
      object_arena_t *a = NULL;
23,160✔
963
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
109,201✔
964
         if (dep == object_arena_name(all_arenas.items[j]))
86,041✔
965
            a = all_arenas.items[j];
18,901✔
966
      }
967

968
      if (a == NULL) {
23,160✔
969
         object_t *droot = NULL;
4,259✔
970
         if (loader_fn) droot = (*loader_fn)(dep);
4,259✔
971

972
         if (droot == NULL)
4,259✔
UNCOV
973
            fatal("%s depends on %s which cannot be found",
×
974
                  fbuf_file_name(f), istr(dep));
975

976
         a = __object_arena(droot);
4,259✔
977
      }
978

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

995
      APUSH(arena->deps, a);
23,157✔
996

997
      assert(dkey <= max_key);
23,157✔
998
      key_map[dkey] = a->key;
23,157✔
999
   }
1000

1001
   for (;;) {
23,140,441✔
1002
      const uint64_t hdr = fbuf_get_uint(f);
23,140,441✔
1003
      if (hdr == UINT16_MAX) break;
23,140,441✔
1004

1005
      const unsigned tag = hdr & 3;
23,121,427✔
1006
      const unsigned kind = hdr >> 2;
23,121,427✔
1007

1008
      assert(tag < OBJECT_TAG_COUNT);
23,121,427✔
1009

1010
      const object_class_t *class = classes[tag];
23,121,427✔
1011

1012
      object_t *object = object_new(arena, class, kind);
23,121,427✔
1013

1014
      if (class->has_loc)
23,121,427✔
1015
         loc_read(&(object->loc), loc_ctx);
20,858,151✔
1016

1017
      imask_t has = class->has_map[object->kind];
23,121,427✔
1018
      for (int n = 0; has; has &= has - 1, n++) {
126,820,401✔
1019
         const uint64_t mask = has & -has;
103,698,974✔
1020
         item_t *item = &(object->items[n]);
103,698,974✔
1021
         if (ITEM_IDENT & mask)
103,698,974✔
1022
            item->ident = ident_read(ident_ctx);
21,012,082✔
1023
         else if (ITEM_OBJECT & mask)
82,686,892✔
1024
            item->object = object_read_ref(f, key_map);
41,557,138✔
1025
         else if (ITEM_OBJ_ARRAY & mask) {
41,129,754✔
1026
            const unsigned count = fbuf_get_uint(f);
9,662,984✔
1027
            if (count > 0) {
9,662,984✔
1028
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
6,460,542✔
1029
                                              count, sizeof(object_t *));
1030
               item->obj_array->count =
6,460,542✔
1031
                  item->obj_array->limit = count;
6,460,542✔
1032
               for (unsigned i = 0; i < count; i++) {
24,689,709✔
1033
                  object_t *o = object_read_ref(f, key_map);
18,229,167✔
1034
                  item->obj_array->items[i] = o;
18,229,167✔
1035
               }
1036
            }
1037
         }
1038
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
31,466,770✔
1039
            item->ival = fbuf_get_int(f);
30,672,835✔
1040
         else if (ITEM_DOUBLE & mask)
793,935✔
1041
            item->dval = read_double(f);
793,935✔
UNCOV
1042
         else if (ITEM_NUMBER & mask)
×
UNCOV
1043
            item->number = number_read(f);
×
1044
         else
1045
            should_not_reach_here();
1046
      }
1047
   }
1048

1049
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
19,014✔
1050

1051
   object_arena_freeze(arena);
19,014✔
1052
   return (object_t *)arena->base;
19,014✔
1053
}
1054

1055
unsigned object_next_generation(void)
64,225✔
1056
{
1057
   return next_generation++;
64,225✔
1058
}
1059

1060
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,128,893✔
1061
{
1062
   if (object == NULL)
3,128,893✔
1063
      return false;
1064

1065
   object_arena_t *arena = __object_arena(object);
2,564,183✔
1066
   if (arena->copygen != ctx->generation)
2,564,183✔
1067
      return false;
1068

1069
   if (ctx->copy_map == NULL)
1,573,928✔
1070
      ctx->copy_map = hash_new(1024);
6,181✔
1071

1072
   if (object_marked_p(object, ctx->generation))
1,573,928✔
1073
      return hash_get(ctx->copy_map, object) != NULL;
361,946✔
1074

1075
   const object_class_t *class = classes[object->tag];
1,211,982✔
1076

1077
   bool marked = false;
1,211,982✔
1078
   if (ctx->should_copy[object->tag] != NULL)
1,211,982✔
1079
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,210,212✔
1080

1081
   object_t *copy = NULL;
1,210,212✔
1082
   if (marked) {
1,210,212✔
1083
      copy = object_new(global_arena, class, object->kind);
38,550✔
1084
      hash_put(ctx->copy_map, object, copy);
38,550✔
1085
   }
1086

1087
   imask_t has = class->has_map[object->kind];
1,211,982✔
1088
   for (int n = 0; has; has &= has - 1, n++) {
6,414,276✔
1089
      const uint64_t mask = has & -has;
5,202,294✔
1090
      item_t *item = &(object->items[n]);
5,202,294✔
1091
      if (ITEM_OBJECT & mask)
5,202,294✔
1092
         marked |= object_copy_mark(item->object, ctx);
2,301,997✔
1093
      else if (ITEM_OBJ_ARRAY & mask) {
2,900,297✔
1094
         if (item->obj_array != NULL) {
435,393✔
1095
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,092,234✔
1096
               object_t *o = item->obj_array->items[i];
814,858✔
1097
               marked |= object_copy_mark(o, ctx);
814,858✔
1098
            }
1099
         }
1100
      }
1101
   }
1102

1103
   if (marked && copy == NULL) {
1,211,982✔
1104
      copy = object_new(global_arena, class, object->kind);
219,643✔
1105
      hash_put(ctx->copy_map, object, copy);
219,643✔
1106
   }
1107

1108
   return marked;
1109
}
1110

1111
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
753,037✔
1112
{
1113
   if (object == NULL)
753,037✔
1114
      return NULL;
1115

1116
   object_t *map = hash_get(ctx->copy_map, object);
640,034✔
1117
   return map ?: object;
640,034✔
1118
}
1119

1120
void object_copy(object_copy_ctx_t *ctx)
6,181✔
1121
{
1122
   for (int i = 0; i < ctx->nroots; i++)
18,219✔
1123
      __object_arena(ctx->roots[i])->copygen = ctx->generation;
12,038✔
1124

1125
   for (int i = 0; i < ctx->nroots; i++)
18,219✔
1126
      (void)object_copy_mark(ctx->roots[i], ctx);
12,038✔
1127

1128
   unsigned ncopied = 0;
6,181✔
1129
   const void *key;
6,181✔
1130
   void *value;
6,181✔
1131
   for (hash_iter_t it = HASH_BEGIN;
6,181✔
1132
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
264,374✔
1133
      const object_t *object = key;
258,193✔
1134
      object_t *copy = value;
258,193✔
1135
      ncopied++;
258,193✔
1136

1137
      copy->loc = object->loc;
258,193✔
1138

1139
      const object_class_t *class = classes[object->tag];
258,193✔
1140

1141
      imask_t has = class->has_map[object->kind];
258,193✔
1142
      for (int n = 0; has; has &= has - 1, n++) {
1,435,750✔
1143
         const uint64_t mask = has & -has;
1,177,557✔
1144
         const item_t *from = &(object->items[n]);
1,177,557✔
1145
         item_t *to = &(copy->items[n]);
1,177,557✔
1146

1147
         if (ITEM_IDENT & mask)
1,177,557✔
1148
            to->ident = from->ident;
215,293✔
1149
         else if (ITEM_OBJECT & mask) {
962,264✔
1150
            to->object = object_copy_map(from->object, ctx);
489,427✔
1151
            object_write_barrier(copy, to->object);
489,427✔
1152
         }
1153
         else if (ITEM_DOUBLE & mask)
472,837✔
1154
            to->dval = from->dval;
124✔
1155
         else if (ITEM_OBJ_ARRAY & mask) {
472,713✔
1156
            if (from->obj_array != NULL) {
169,515✔
1157
               // TODO: make a resize macro
1158
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
227,276✔
1159
                                            from->obj_array->count,
113,638✔
1160
                                            sizeof(object_t *));
1161
               to->obj_array->count =
113,638✔
1162
                  to->obj_array->limit = from->obj_array->count;
113,638✔
1163
               for (size_t i = 0; i < from->obj_array->count; i++) {
377,248✔
1164
                  object_t *o =
263,610✔
1165
                     object_copy_map(from->obj_array->items[i], ctx);
263,610✔
1166
                  to->obj_array->items[i] = o;
263,610✔
1167
                  object_write_barrier(copy, o);
263,610✔
1168
               }
1169
            }
1170
         }
1171
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
303,198✔
1172
            to->ival = from->ival;
303,198✔
1173
         else
1174
            should_not_reach_here();
1175
      }
1176
   }
1177

1178
   for (hash_iter_t it = HASH_BEGIN;
6,181✔
1179
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
264,374✔
1180
      object_t *copy = value;
258,193✔
1181
      if (ctx->callback[copy->tag] != NULL)
258,193✔
1182
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
255,004✔
1183
   }
1184

1185
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
6,181✔
UNCOV
1186
      debugf("copied %d objects into arena %s", ncopied,
×
1187
             istr(object_arena_name(global_arena)));
1188

1189
   for (unsigned i = 0; i < ctx->nroots; i++) {
18,219✔
1190
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
12,038✔
1191
      if (copy != NULL)
12,038✔
1192
         ctx->roots[i] = copy;
5,219✔
1193
   }
1194

1195
   hash_free(ctx->copy_map);
6,181✔
1196
}
6,181✔
1197

1198
size_t object_arena_default_size(void)
17,326✔
1199
{
1200
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
17,326✔
1201
}
1202

1203
object_arena_t *object_arena_new(size_t size, unsigned std)
36,343✔
1204
{
1205
   if (all_arenas.count == 0)
36,343✔
1206
      APUSH(all_arenas, NULL);   // Dummy null arena
5,264✔
1207

1208
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
36,343✔
1209
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
36,343✔
1210
   arena->alloc  = arena->base;
36,343✔
1211
   arena->limit  = (char *)arena->base + size;
36,343✔
1212
   arena->key    = all_arenas.count;
36,343✔
1213
   arena->source = OBJ_FRESH;
36,343✔
1214
   arena->std    = std;
36,343✔
1215

1216
   APUSH(all_arenas, arena);
36,343✔
1217

1218
   if (all_arenas.count == UINT16_MAX - 1)
36,343✔
1219
      fatal_trace("too many object arenas");
1220

1221
   return arena;
36,343✔
1222
}
1223

1224
void object_arena_freeze(object_arena_t *arena)
35,707✔
1225
{
1226
   ident_t name = object_arena_name(arena);
35,707✔
1227

1228
   if (arena->frozen)
35,707✔
1229
      fatal_trace("arena %s already frozen", istr(name));
1230

1231
   if (arena->source == OBJ_FRESH)
35,707✔
1232
      object_arena_gc(arena);
16,693✔
1233

1234
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
35,707✔
UNCOV
1235
      debugf("arena %s frozen (%d bytes)", istr(name),
×
UNCOV
1236
             (int)(arena->alloc - arena->base));
×
1237

1238
   chash_put(arena_lookup, name, arena);
35,707✔
1239

1240
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
35,707✔
1241
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
35,707✔
1242

1243
   if (next_page < arena->limit) {
35,707✔
1244
#if OBJECT_UNMAP_UNUSED
1245
      nvc_munmap(next_page, arena->limit - next_page);
1246
      arena->limit = next_page;
1247
#else
1248
      // This can be useful for debugging use-after-free
1249
      nvc_decommit(next_page, arena->limit - next_page);
16,693✔
1250
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
16,693✔
1251
#endif
1252
   }
1253

1254
   arena->frozen = true;
35,707✔
1255
}
35,707✔
1256

1257
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
42,249✔
1258
{
1259
   for (unsigned i = 0; i < arena->deps.count; i++)
142,931✔
1260
      (*fn)(arena_root(arena->deps.items[i]), context);
100,682✔
1261
}
42,249✔
1262

1263
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
11,333✔
1264
                              void *context)
1265
{
1266
   for (unsigned i = 0; i < arena->deps.count; i++) {
33,878✔
1267
      if (arena->deps.items[i]->obsolete)
22,545✔
1268
         (*fn)(arena_root(arena->deps.items[i]), context);
6✔
1269
   }
1270
}
11,333✔
1271

1272
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
12,630✔
1273
{
1274
   object_arena_t *arena = __object_arena(object);
12,630✔
1275
   assert(arena->frozen);
12,630✔
1276

1277
   *module = object_arena_name(arena);
12,630✔
1278

1279
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
12,630✔
1280
   if (arena->forward != NULL)
12,630✔
1281
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
5,850✔
1282
   else
1283
      *offset = index;
6,780✔
1284
}
12,630✔
1285

1286
static object_arena_t *arena_by_name(ident_t module)
7,319✔
1287
{
1288
   if (global_arena != NULL && object_arena_name(global_arena) == module)
7,319✔
UNCOV
1289
      return global_arena;
×
1290

1291
   object_arena_t *a = chash_get(arena_lookup, module);
7,319✔
1292
   if (a != NULL)
7,319✔
1293
      return a;
1294

1295
#if defined DEBUG && !defined __SANITIZE_THREAD__
1296
   for (int i = 1; i < all_arenas.count; i++)
4,546✔
1297
      assert(module != object_arena_name(all_arenas.items[i]));
3,915✔
1298
#endif
1299

1300
   return NULL;
1301
}
1302

1303
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
7,319✔
1304
                            object_load_fn_t loader)
1305
{
1306
   object_arena_t *arena = arena_by_name(module);
7,319✔
1307

1308
   if (arena == NULL) {
7,319✔
1309
      object_t *droot = NULL;
631✔
1310
      if (loader) droot = (*loader)(module);
631✔
1311

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

1315
      arena = __object_arena(droot);
631✔
1316
   }
1317

1318
   assert(arena->frozen);
7,319✔
1319

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

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

1337
   object_t *obj = ptr;
7,319✔
1338
   if (obj->tag >= OBJECT_TAG_COUNT)
7,319✔
1339
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1340
                  istr(module), offset);
1341
   else if (obj->arena != arena->key)
7,319✔
1342
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
UNCOV
1343
                  obj->arena, arena->key, istr(module), offset);
×
1344

1345
   return obj;
7,319✔
1346
}
1347

1348
void freeze_global_arena(void)
25,730✔
1349
{
1350
   object_one_time_init();
25,730✔
1351

1352
   if (global_arena != NULL) {
25,730✔
1353
      object_arena_freeze(global_arena);
16,693✔
1354
      global_arena = NULL;
16,693✔
1355
   }
1356
}
25,730✔
1357

1358
void make_new_arena(void)
17,326✔
1359
{
1360
   freeze_global_arena();
17,326✔
1361
   global_arena = object_arena_new(object_arena_default_size(), standard());
17,326✔
1362
}
17,326✔
1363

1364
void discard_global_arena(void)
1✔
1365
{
1366
   object_one_time_init();
1✔
1367

1368
   if (global_arena != NULL) {
1✔
1369
      nvc_munmap(global_arena->base, global_arena->limit - global_arena->base);
1✔
1370

1371
      assert(all_arenas.items[all_arenas.count - 1] == global_arena);
1✔
1372
      APOP(all_arenas);
1✔
1373

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

© 2026 Coveralls, Inc