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

nickg / nvc / 6596778870

21 Oct 2023 10:19AM UTC coverage: 90.89% (-0.4%) from 91.261%
6596778870

push

github

nickg
Fix race condition in arena_by_name

11 of 11 new or added lines in 1 file covered. (100.0%)

49538 of 54503 relevant lines covered (90.89%)

584075.44 hits per line

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

88.52
/src/object.c
1
//
2
//  Copyright (C) 2014-2023  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 "hash.h"
22
#include "ident.h"
23
#include "lib.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
   bool            frozen;
45
   bool            has_locus;
46
   uint32_t       *forward;
47
   mark_mask_t    *mark_bits;
48
   size_t          mark_sz;
49
   generation_t    generation;
50
   arena_key_t     key;
51
   arena_array_t   deps;
52
   object_t       *root;
53
   obj_src_t       source;
54
   vhdl_standard_t std;
55
   uint32_t        checksum;
56
} object_arena_t;
57

58
#ifndef __SANITIZE_ADDRESS__
59
#define OBJECT_UNMAP_UNUSED 1
60
#endif
61

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

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

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

103
static inline bool object_in_arena_p(object_arena_t *arena, object_t *object)
104
{
105
   return (void *)object >= arena->base && (void *)object < arena->limit;
106
}
107

108
static inline object_arena_t *__object_arena(object_t *object)
109
{
110
   assert(object->arena < all_arenas.count);
111
   assert(object->arena != 0);
112
   return all_arenas.items[object->arena];
113
}
114

115
static ident_t object_arena_name(object_arena_t *arena)
339,426✔
116
{
117
   if (arena->alloc > arena->base) {
339,426✔
118
      object_t *root = arena_root(arena);
335,924✔
119

120
      const object_class_t *class = classes[root->tag];
335,924✔
121
      const imask_t has = class->has_map[root->kind];
335,924✔
122

123
      if (has & I_IDENT) {
335,924✔
124
         const int n = __builtin_popcountll(has & (I_IDENT - 1));
334,359✔
125
         return root->items[n].ident;
334,359✔
126
      }
127
   }
128

129
   return ident_new("???");
5,067✔
130
}
131

132
static bool object_marked_p(object_t *object, generation_t generation)
51,941,400✔
133
{
134
   object_arena_t *arena = __object_arena(object);
51,941,400✔
135

136
   if (arena->mark_bits == NULL) {
51,941,400✔
137
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
17,607✔
138
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
17,607✔
139
      arena->mark_bits = xcalloc(arena->mark_sz);
17,607✔
140
      arena->generation = generation;
17,607✔
141
   }
142
   else if (arena->generation != generation) {
51,923,800✔
143
      memset(arena->mark_bits, '\0', arena->mark_sz);
75,403✔
144
      arena->generation = generation;
75,403✔
145
   }
146

147
   uintptr_t bit = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
51,941,400✔
148
   uintptr_t word = bit / 64;
51,941,400✔
149
   uint64_t mask = UINT64_C(1) << (bit & 63);
51,941,400✔
150

151
   const bool marked = !!(arena->mark_bits[word] & mask);
51,941,400✔
152
   arena->mark_bits[word] |= mask;
51,941,400✔
153

154
   return marked;
51,941,400✔
155
}
156

157
void arena_set_checksum(object_arena_t *arena, uint32_t checksum)
15,979✔
158
{
159
   arena->checksum = checksum;
15,979✔
160
}
15,979✔
161

162
object_t *arena_root(object_arena_t *arena)
384,581✔
163
{
164
   return arena->root ?: (object_t *)arena->base;
384,581✔
165
}
166

167
bool arena_frozen(object_arena_t *arena)
27,115✔
168
{
169
   return arena->frozen;
27,115✔
170
}
171

172
object_arena_t *object_arena(object_t *object)
101,190✔
173
{
174
   return __object_arena(object);
101,190✔
175
}
176

177
void __object_write_barrier(object_t *lhs, object_t *rhs)
4,552,280✔
178
{
179
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
4,552,280✔
180
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
4,552,280✔
181

182
   if (lhs_mask == rhs_mask || rhs == NULL)
4,552,280✔
183
      return;
184
   else if (lhs->arena == rhs->arena)
4,552,280✔
185
      return;
186

187
   object_arena_t *larena = __object_arena(lhs);
4,090,650✔
188
   object_arena_t *rarena = __object_arena(rhs);
4,090,650✔
189

190
   assert(!larena->frozen);
4,090,650✔
191
   assert(rarena->frozen);
4,090,650✔
192

193
   for (unsigned i = 0; i < larena->deps.count; i++) {
6,576,720✔
194
      if (larena->deps.items[i] == rarena)
6,549,180✔
195
         return;
196
   }
197

198
   APUSH(larena->deps, rarena);
27,542✔
199
}
200

201
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
202
{
203
   unsigned int item;
×
204
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
205
      ;
×
206

207
   assert(item < ARRAY_LEN(item_text_map));
×
208

209
   diag_t *d = diag_new(DIAG_FATAL, &(object->loc));
×
210
   diag_printf(d, "%s kind %s does not have item %s", class->name,
×
211
               class->kind_text_map[object->kind], item_text_map[item]);
×
212
   diag_set_consumer(NULL, NULL);
×
213
   diag_suppress(d, false);
×
214
   diag_emit(d);
×
215
   show_stacktrace();
×
216
   fatal_exit(EXIT_FAILURE);
×
217
}
218

219
void item_without_type(imask_t mask)
×
220
{
221
   int item;
×
222
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
223
      ;
×
224

225
   assert(item < ARRAY_LEN(item_text_map));
×
226
   fatal_trace("item %s does not have a type", item_text_map[item]);
×
227
}
228

229
void obj_array_add(obj_array_t **a, object_t *o)
1,136,830✔
230
{
231
   if (*a == NULL) {
1,136,830✔
232
      const int defsz = 8;
421,133✔
233
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
421,133✔
234
      (*a)->count = 0;
421,133✔
235
      (*a)->limit = defsz;
421,133✔
236
   }
237
   else if ((*a)->count == (*a)->limit) {
715,694✔
238
      (*a)->limit *= 2;
27,821✔
239
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
27,821✔
240
                         (*a)->limit, sizeof(object_t *));
241
   }
242

243
   (*a)->items[(*a)->count++] = o;
1,136,830✔
244
}
1,136,830✔
245

246
void obj_array_free(obj_array_t **a)
63,892✔
247
{
248
   free(*a);
63,892✔
249
   *a = NULL;
63,892✔
250
}
63,892✔
251

252
void object_change_kind(const object_class_t *class, object_t *object, int kind)
20,305✔
253
{
254
   if (kind == object->kind)
20,305✔
255
      return;
256

257
   bool allow = false;
258
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
213,016✔
259
      allow = (class->change_allowed[i][0] == object->kind)
192,711✔
260
         && (class->change_allowed[i][1] == kind);
192,711✔
261
   }
262

263
   if (!allow)
20,305✔
264
      fatal_trace("cannot change %s kind %s to %s", class->name,
×
265
                  class->kind_text_map[object->kind],
×
266
                  class->kind_text_map[kind]);
×
267

268
   const imask_t old_has = class->has_map[object->kind];
20,305✔
269
   const imask_t new_has = class->has_map[kind];
20,305✔
270

271
   const int old_nitems = __builtin_popcountll(old_has);
20,305✔
272
   const int new_nitems = __builtin_popcountll(new_has);
20,305✔
273

274
   const int max_items = MAX(old_nitems, new_nitems);
20,305✔
275

276
   item_t tmp[max_items];
20,305✔
277
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
20,305✔
278

279
   int op = 0, np = 0;
20,305✔
280
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
977,572✔
281
      if ((old_has & mask) && (new_has & mask))
957,267✔
282
         object->items[np++] = tmp[op++];
77,307✔
283
      else if (old_has & mask) {
879,960✔
284
         if (ITEM_OBJ_ARRAY & mask)
113✔
285
            obj_array_free(&(tmp[op].obj_array));
90✔
286
         ++op;
113✔
287
      }
288
      else if (new_has & mask)
879,847✔
289
         memset(&(object->items[np++]), '\0', sizeof(item_t));
957,267✔
290
   }
291

292
   object->kind = kind;
20,305✔
293
}
294

295
static void object_init(object_class_t *class)
15,344✔
296
{
297
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
15,344✔
298

299
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
15,344✔
300

301
   assert(class->tag < ARRAY_LEN(classes));
15,344✔
302
   classes[class->tag] = class;
15,344✔
303

304
   for (int i = 0; i < class->last_kind; i++) {
682,808✔
305
      const int nitems = __builtin_popcountll(class->has_map[i]);
667,464✔
306
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
667,464✔
307

308
      // Knuth's multiplicative hash
309
      format_digest +=
667,464✔
310
         (uint32_t)(class->has_map[i] >> 32) * UINT32_C(2654435761);
667,464✔
311
      format_digest +=
667,464✔
312
         (uint32_t)(class->has_map[i]) * UINT32_C(2654435761);
667,464✔
313
   }
314

315
   bool changed = false;
23,016✔
316
   do {
23,016✔
317
      changed = false;
23,016✔
318
      for (int i = 0; i < class->last_kind; i++) {
1,534,400✔
319
         size_t max_size = class->object_size[i];
1,511,380✔
320
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
21,765,500✔
321
            if (class->change_allowed[j][0] == i)
20,254,100✔
322
               max_size = MAX(max_size,
184,128✔
323
                              class->object_size[class->change_allowed[j][1]]);
324
            else if (class->change_allowed[j][1] == i)
20,070,000✔
325
               max_size = MAX(max_size,
184,128✔
326
                              class->object_size[class->change_allowed[j][0]]);
327
         }
328

329
         if (max_size != class->object_size[i]) {
1,511,380✔
330
            class->object_size[i] = max_size;
46,032✔
331
            changed = true;
46,032✔
332
         }
333
      }
334
   } while (changed);
23,016✔
335

336
   if (getenv("NVC_TREE_SIZES") != NULL) {
15,344✔
337
      for (int i = 0; i < class->last_kind; i++)
×
338
         printf("%-15s %d\n", class->kind_text_map[i],
×
339
                (int)class->object_size[i]);
×
340
   }
341
}
15,344✔
342

343
static void check_frozen_object_fault(int sig, void *addr,
×
344
                                      struct cpu_state *cpu, void *context)
345
{
346
   if (sig != SIGSEGV)
×
347
      return;
348

349
   for (unsigned i = 1; i < all_arenas.count; i++) {
×
350
      object_arena_t *arena = AGET(all_arenas, i);
×
351
      if (!arena->frozen)
×
352
         continue;
×
353
      else if (addr < arena->base)
×
354
         continue;
×
355
      else if (addr >= arena->limit)
×
356
         continue;
×
357

358
      fatal_trace("Write to object in frozen arena %s [address=%p]",
×
359
                  istr(object_arena_name(arena)), addr);
360
   }
361
}
362

363
void object_one_time_init(void)
9,581,160✔
364
{
365
   INIT_ONCE({
9,581,160✔
366
         extern object_class_t tree_object;
367
         object_init(&tree_object);
368

369
         extern object_class_t type_object;
370
         object_init(&type_object);
371

372
         extern object_class_t vlog_object;
373
         object_init(&vlog_object);
374

375
         extern object_class_t psl_object;
376
         object_init(&psl_object);
377

378
         // Increment this each time a incompatible change is made to
379
         // the on-disk format not expressed in the object items table
380
         const uint32_t format_fudge = 30;
381

382
         format_digest += format_fudge * UINT32_C(2654435761);
383

384
         add_fault_handler(check_frozen_object_fault, NULL);
385

386
         arena_lookup = chash_new(64);
387
      });
388
}
9,581,160✔
389

390
static bool is_gc_root(const object_class_t *class, int kind)
1,932,630✔
391
{
392
   for (int j = 0; j < class->gc_num_roots; j++) {
18,208,400✔
393
      if (class->gc_roots[j] == kind)
16,311,900✔
394
         return true;
395
   }
396

397
   return false;
398
}
399

400
object_t *object_new(object_arena_t *arena,
9,574,380✔
401
                     const object_class_t *class, int kind)
402
{
403
   if (unlikely(kind >= class->last_kind))
9,574,380✔
404
      fatal_trace("invalid kind %d for %s object", kind, class->name);
×
405

406
   object_one_time_init();
9,574,380✔
407

408
   if (arena == NULL)
9,574,380✔
409
      arena = global_arena;
1,671,760✔
410

411
   if (unlikely(arena == NULL))
9,574,380✔
412
      fatal_trace("allocating object without active arena");
×
413

414
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
9,574,380✔
415

416
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
9,574,380✔
417

418
   if (unlikely(arena->limit - arena->alloc < size)) {
9,574,380✔
419
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
420
      diag_suppress(d, false);
×
421
      diag_printf(d, "memory exhausted while creating unit %s",
×
422
                  istr(object_arena_name(arena)));
423
      diag_hint(d, NULL, "The current limit is %zu bytes which you can "
×
424
                "increase with the $bold$-M$$ option, for example "
425
                "$bold$-M 32m$$", object_arena_default_size());
426
      diag_emit(d);
×
427
      fatal_exit(EXIT_FAILURE);
×
428
   }
429

430
   object_t *object = arena->alloc;
9,574,380✔
431
   arena->alloc = (char *)arena->alloc + size;
9,574,380✔
432

433
   if (arena->root == NULL && is_gc_root(class, kind))
9,574,380✔
434
      arena->root = object;
19,681✔
435

436
   memset(object, '\0', size);
9,574,380✔
437

438
   object->kind  = kind;
9,574,380✔
439
   object->tag   = class->tag;
9,574,380✔
440
   object->arena = arena->key;
9,574,380✔
441
   object->loc   = LOC_INVALID;
9,574,380✔
442

443
   return object;
9,574,380✔
444
}
445

446
static void gc_forward_one_pointer(object_t **pobject, object_arena_t *arena,
4,094,790✔
447
                                   uint32_t *forward)
448
{
449
   if (*pobject != NULL && object_in_arena_p(arena, *pobject)) {
4,094,790✔
450
      ptrdiff_t index = ((void *)*pobject - arena->base) >> OBJECT_ALIGN_BITS;
2,034,870✔
451
      *pobject = (object_t *)((char *)arena->base + forward[index]);
2,034,870✔
452
   }
453
}
4,094,790✔
454

455
static void gc_forward_pointers(object_t *object, object_arena_t *arena,
456
                                const object_class_t *class, uint32_t *forward)
457
{
458
   const imask_t has = class->has_map[object->kind];
459
   const int nitems = __builtin_popcountll(has);
460
   imask_t mask = 1;
461
   for (int i = 0; i < nitems; mask <<= 1) {
462
      if (has & mask) {
463
         item_t *item = &(object->items[i]);
464
         if (ITEM_OBJECT & mask)
465
            gc_forward_one_pointer(&(item->object), arena, forward);
466
         else if ((ITEM_OBJ_ARRAY & mask) && item->obj_array != NULL) {
467
            for (unsigned j = 0; j < item->obj_array->count; j++)
468
               gc_forward_one_pointer(&(item->obj_array->items[j]),
469
                                      arena, forward);
470
         }
471

472
         i++;
473
      }
474
   }
475
}
476

477
static void gc_mark_from_root(object_t *root, generation_t generation)
16,476✔
478
{
479
   object_visit_ctx_t ctx = {
16,476✔
480
      .count      = 0,
481
      .postorder  = NULL,
482
      .preorder   = NULL,
483
      .context    = NULL,
484
      .kind       = T_LAST_TREE_KIND,
485
      .generation = generation,
486
      .deep       = true
487
   };
488

489
   object_visit(root, &ctx);
16,476✔
490
}
16,476✔
491

492
static void gc_free_external(object_t *object)
292,958✔
493
{
494
   const object_class_t *class = classes[object->tag];
292,958✔
495
   const imask_t has = class->has_map[object->kind];
292,958✔
496
   const int nitems = __builtin_popcountll(has);
292,958✔
497
   imask_t mask = 1;
292,958✔
498
   for (int i = 0; i < nitems; mask <<= 1) {
13,036,200✔
499
      if (has & mask) {
12,743,200✔
500
         if (ITEM_OBJ_ARRAY & mask)
1,249,820✔
501
            obj_array_free(&(object->items[i].obj_array));
62,421✔
502
         else if (ITEM_TEXT & mask)
1,187,390✔
503
            free(&(object->items[i].text));
×
504
         else if (ITEM_NUMBER & mask)
1,187,390✔
505
            number_free(&(object->items[i].number));
×
506
         i++;
1,249,820✔
507
      }
508
   }
509
}
292,958✔
510

511
void object_arena_gc(object_arena_t *arena)
12,477✔
512
{
513
   const generation_t generation = object_next_generation();
12,477✔
514
   const uint64_t start_ticks = get_timestamp_us();
12,477✔
515

516
   // Mark
517
   for (void *p = arena->base; p != arena->alloc; ) {
1,925,420✔
518
      assert(p < arena->alloc);
1,912,950✔
519
      object_t *object = p;
1,912,950✔
520

521
      const object_class_t *class = classes[object->tag];
1,912,950✔
522

523
      if (is_gc_root(class, object->kind))
1,912,950✔
524
         gc_mark_from_root(object, generation);
16,476✔
525

526
      const size_t size =
1,912,950✔
527
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,912,950✔
528
      p = (char *)p + size;
1,912,950✔
529
   }
530

531
   // Calculate forwarding addresses
532
   const size_t fwdsz = (arena->alloc - arena->base) / OBJECT_ALIGN;
12,477✔
533
   uint32_t *forward = xmalloc_array(fwdsz, sizeof(uint32_t));
12,477✔
534
   unsigned woffset = 0, live = 0, dead = 0;
12,477✔
535
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
1,925,420✔
536
      assert(rptr < arena->alloc);
1,912,950✔
537
      object_t *object = rptr;
1,912,950✔
538

539
      const object_class_t *class = classes[object->tag];
1,912,950✔
540

541
      const size_t size =
1,912,950✔
542
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,912,950✔
543

544
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
1,912,950✔
545
      if (!object_marked_p(object, generation)) {
1,912,950✔
546
         forward[index] = UINT32_MAX;
292,958✔
547
         gc_free_external(object);
292,958✔
548
         dead++;
292,958✔
549
      }
550
      else {
551
         forward[index] = woffset;
1,619,990✔
552
         woffset += size;
1,619,990✔
553
         live++;
1,619,990✔
554
      }
555

556
      rptr = (char *)rptr + size;
1,912,950✔
557
   }
558

559
   if (dead == 0)
12,477✔
560
      goto skip_gc;
6,105✔
561
   else if (woffset == 0)
6,372✔
562
      fatal_trace("GC removed all objects from arena %s",
×
563
                  istr(object_arena_name(arena)));
564

565
   // Compact
566
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
1,851,880✔
567
      assert(rptr < arena->alloc);
1,845,510✔
568
      object_t *object = rptr;
1,845,510✔
569

570
      const object_class_t *class = classes[object->tag];
1,845,510✔
571

572
      const size_t size =
1,845,510✔
573
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,845,510✔
574

575
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
1,845,510✔
576
      if (forward[index] != UINT32_MAX) {
1,845,510✔
577
         void *wptr = (char *)arena->base + forward[index];
1,552,550✔
578
         assert(wptr <= rptr);
1,552,550✔
579

580
         gc_forward_pointers(object, arena, class, forward);
1,552,550✔
581

582
         if (wptr != rptr) memmove(wptr, rptr, size);
1,552,550✔
583
      }
584

585
      rptr = (char *)rptr + size;
1,845,510✔
586
   }
587

588
   arena->alloc = (char *)arena->base + woffset;
6,372✔
589

590
 skip_gc:
12,477✔
591
   if (arena->has_locus) {
12,477✔
592
      // Need to keep a copy of the forwarding table for loci created
593
      // before freezing
594
      arena->forward = forward;
3,036✔
595
   }
596
   else
597
      free(forward);
9,441✔
598

599
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL)) {
12,477✔
600
      const int ticks = get_timestamp_us() - start_ticks;
×
601
      debugf("GC: %s: freed %d objects; %d allocated [%d us]",
×
602
             istr(object_arena_name(arena)), dead, live, ticks);
603
   }
604
}
12,477✔
605

606
void object_visit(object_t *object, object_visit_ctx_t *ctx)
52,499,700✔
607
{
608
   // If `deep' then will follow links above the tree originally passed
609
   // to tree_visit - e.g. following references back to their declarations
610

611
   if (object == NULL)
52,499,700✔
612
      return;
613
   else if (object_marked_p(object, ctx->generation))
44,599,400✔
614
      return;
615

616
   const object_class_t *class = classes[object->tag];
20,908,000✔
617

618
   const bool visit =
41,816,000✔
619
      (object->tag == ctx->tag && object->kind == ctx->kind)
17,460,700✔
620
      || ctx->kind == class->last_kind;
38,367,000✔
621

622
   if (visit && ctx->preorder != NULL)
20,908,000✔
623
      (*ctx->preorder)(object, ctx->context);
×
624

625
   const imask_t deep_mask = I_TYPE | I_REF;
20,908,000✔
626

627
   const imask_t has = class->has_map[object->kind];
20,908,000✔
628
   const int nitems = __builtin_popcountll(has);
20,908,000✔
629
   imask_t mask = 1;
20,908,000✔
630
   for (int i = 0; i < nitems; mask <<= 1) {
865,079,000✔
631
      if (has & mask & ~(ctx->deep ? 0 : deep_mask)) {
866,356,000✔
632
         item_t *item = &(object->items[i]);
97,552,800✔
633
         if (ITEM_IDENT & mask)
97,552,800✔
634
            ;
635
         else if (ITEM_OBJECT & mask)
74,719,700✔
636
            object_visit(item->object, ctx);
30,967,200✔
637
         else if (ITEM_OBJ_ARRAY & mask) {
43,752,500✔
638
            if (item->obj_array != NULL) {
11,034,000✔
639
               for (unsigned j = 0; j < item->obj_array->count; j++)
28,955,800✔
640
                  object_visit(item->obj_array->items[j], ctx);
21,506,400✔
641
            }
642
         }
643
         else if (ITEM_INT64 & mask)
32,718,500✔
644
            ;
645
         else if (ITEM_INT32 & mask)
28,025,200✔
646
            ;
647
         else if (ITEM_DOUBLE & mask)
567,260✔
648
            ;
649
         else if (ITEM_TEXT & mask)
356✔
650
            ;
651
         else if (ITEM_NUMBER & mask)
191✔
652
            ;
653
         else
654
            item_without_type(mask);
×
655
      }
656

657
      if (has & mask)
844,171,000✔
658
         i++;
98,260,000✔
659
   }
660

661
   if (visit) {
20,908,000✔
662
      if (ctx->postorder != NULL)
17,434,200✔
663
         (*ctx->postorder)(object, ctx->context);
518,270✔
664
      ctx->count++;
17,434,200✔
665
   }
666
}
667

668
static object_t *object_rewrite_iter(object_t *object,
3,143,010✔
669
                                     object_rewrite_ctx_t *ctx)
670
{
671
   // The callback may return a new object or a pointer to an existing
672
   // object in the same arena that that needs to be rewritten so
673
   // iterate rewriting until we reach a fixed point
674
   for (;;) {
3,198,240✔
675
      object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,198,240✔
676
      if (new == object || (object = object_rewrite(new, ctx)) == NULL)
3,198,230✔
677
         return new;
3,143,000✔
678
   }
679
}
680

681
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
7,159,080✔
682
{
683
   if (object == NULL)
7,159,080✔
684
      return NULL;
685

686
   if (!object_in_arena_p(ctx->arena, object))
5,998,930✔
687
      return object;
688

689
   const ptrdiff_t index =
3,944,730✔
690
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
3,944,730✔
691

692
   // New objects can be allocated while rewrite is in progress so we
693
   // need to check if the inex is greater than the current cache size
694
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
3,944,730✔
695
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
53,449✔
696
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
53,449✔
697
                                  ctx->cache_sz);
698
   }
699

700
   if (object_marked_p(object, ctx->generation)) {
3,944,730✔
701
      if (ctx->cache[index] == (object_t *)-1) {
611,836✔
702
         // Found a circular reference: eagerly rewrite the object now
703
         // and break the cycle
704
         if (ctx->post_fn[object->tag] != NULL) {
888✔
705
            if (ctx->pre_fn[object->tag] != NULL)
15✔
706
               (*ctx->pre_fn[object->tag])(object, ctx->context);
3✔
707
            object_t *new = object_rewrite_iter(object, ctx);
15✔
708
            object_write_barrier(object, new);
15✔
709
            return (ctx->cache[index] = new);
15✔
710
         }
711
         else
712
            return (ctx->cache[index] = object);
873✔
713
      }
714
      else {
715
         // Already rewritten this tree so return the cached version
716
         return ctx->cache[index];
717
      }
718
   }
719

720
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,332,900✔
721

722
   if (ctx->pre_fn[object->tag] != NULL)
3,332,900✔
723
      (*ctx->pre_fn[object->tag])(object, ctx->context);
1,693,050✔
724

725
   const imask_t skip_mask = I_REF;
3,332,900✔
726

727
   const object_class_t *class = classes[object->tag];
3,332,900✔
728

729
   const imask_t has = class->has_map[object->kind];
3,332,900✔
730
   const int nitems = __builtin_popcountll(has);
3,332,900✔
731
   imask_t mask = 1;
3,332,900✔
732
   for (int n = 0; n < nitems; mask <<= 1) {
136,273,000✔
733
      if (has & mask & ~skip_mask) {
132,940,000✔
734
         if (ITEM_IDENT & mask)
12,872,400✔
735
            ;
736
         else if (ITEM_OBJECT & mask) {
10,176,200✔
737
            object_t *o = object->items[n].object;
4,666,210✔
738
            object->items[n].object = object_rewrite(o, ctx);
4,666,210✔
739
            object_write_barrier(object, o);
4,666,200✔
740
         }
741
         else if (ITEM_OBJ_ARRAY & mask) {
5,510,030✔
742
            obj_array_t **a = &(object->items[n].obj_array);
1,339,700✔
743
            if (object->items[n].obj_array != NULL) {
1,339,700✔
744
               // The callback may add new items to the array so the
745
               // array pointer cannot be cached between iterations
746
               unsigned wptr = 0;
747
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,339,430✔
748
                  object_t *o = object->items[n].obj_array->items[i];
2,409,410✔
749
                  if ((o = object_rewrite(o, ctx))) {
2,409,410✔
750
                     object_write_barrier(object, o);
2,404,080✔
751
                     object->items[n].obj_array->items[wptr++] = o;
2,404,080✔
752
                  }
753
               }
754

755
               if (wptr == 0)
930,027✔
756
                  obj_array_free(a);
1,381✔
757
               else
758
                  (*a)->count = wptr;
928,646✔
759
            }
760
         }
761
         else if (ITEM_INT64 & mask)
4,170,330✔
762
            ;
763
         else if (ITEM_INT32 & mask)
3,408,150✔
764
            ;
765
         else if (ITEM_DOUBLE & mask)
212,165✔
766
            ;
767
         else
768
            item_without_type(mask);
×
769
      }
770

771
      if (has & mask)
132,940,000✔
772
         n++;
14,518,500✔
773
   }
774

775
   if (ctx->cache[index] != (object_t *)-1) {
3,332,890✔
776
      // The cache was already updated due to a circular reference
777
      return ctx->cache[index];
778
   }
779
   else if (ctx->post_fn[object->tag] != NULL) {
3,332,000✔
780
      object_t *new = object_rewrite_iter(object, ctx);
3,142,990✔
781
      object_write_barrier(object, new);
3,142,990✔
782
      return (ctx->cache[index] = new);
3,142,990✔
783
   }
784
   else
785
      return (ctx->cache[index] = object);
189,008✔
786
}
787

788
static void object_write_ref(object_t *object, fbuf_t *f)
3,949,000✔
789
{
790
   if (object == NULL)
3,949,000✔
791
      fbuf_put_uint(f, 0);
651,036✔
792
   else {
793
      object_arena_t *arena = __object_arena(object);
3,297,960✔
794
      assert(arena->key != 0);
3,297,960✔
795
      fbuf_put_uint(f, arena->key);
3,297,960✔
796

797
      ptrdiff_t offset = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,297,960✔
798
      fbuf_put_uint(f, offset);
3,297,960✔
799
   }
800
}
3,949,000✔
801

802
void object_write(object_t *root, fbuf_t *f, ident_wr_ctx_t ident_ctx,
9,209✔
803
                  loc_wr_ctx_t *loc_ctx)
804
{
805
   object_arena_t *arena = __object_arena(root);
9,209✔
806

807
   write_u32(format_digest, f);
9,209✔
808
   fbuf_put_uint(f, standard());
9,209✔
809
   fbuf_put_uint(f, arena->limit - arena->base);
9,209✔
810

811
   if (root != arena_root(arena))
9,209✔
812
      fatal_trace("must write root object first");
×
813
   else if (arena->source == OBJ_DISK)
9,209✔
814
      fatal_trace("writing arena %s originally read from disk",
×
815
                  istr(object_arena_name(arena)));
816
   else if (!arena->frozen)
9,209✔
817
      fatal_trace("arena %s must be frozen before writing to disk",
×
818
                  istr(object_arena_name(arena)));
819

820
   fbuf_put_uint(f, arena->key);
9,209✔
821
   ident_write(object_arena_name(arena), ident_ctx);
9,209✔
822

823
   arena_key_t max_key = arena->key;
9,209✔
824
   for (unsigned i = 0; i < arena->deps.count; i++)
28,362✔
825
      max_key = MAX(max_key, arena->deps.items[i]->key);
19,153✔
826
   fbuf_put_uint(f, max_key);
9,209✔
827

828
   fbuf_put_uint(f, arena->deps.count);
9,209✔
829
   for (unsigned i = 0; i < arena->deps.count; i++) {
28,362✔
830
      fbuf_put_uint(f, arena->deps.items[i]->key);
19,153✔
831
      fbuf_put_uint(f, arena->deps.items[i]->std);
19,153✔
832
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
19,153✔
833
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
19,153✔
834
   }
835

836
   for (void *p = arena->base; p != arena->alloc; ) {
1,513,660✔
837
      assert(p < arena->alloc);
1,504,450✔
838

839
      object_t *object = p;
1,504,450✔
840
      object_class_t *class = classes[object->tag];
1,504,450✔
841

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

845
      if (class->has_loc)
1,504,450✔
846
         loc_write(&object->loc, loc_ctx);
1,415,000✔
847

848
      const imask_t has = class->has_map[object->kind];
1,504,450✔
849
      const int nitems = __builtin_popcountll(has);
1,504,450✔
850
      imask_t mask = 1;
1,504,450✔
851
      for (int n = 0; n < nitems; mask <<= 1) {
61,433,500✔
852
         if (has & mask) {
59,929,100✔
853
            item_t *item = &(object->items[n]);
6,541,180✔
854
            if (ITEM_IDENT & mask)
6,541,180✔
855
               ident_write(item->ident, ident_ctx);
1,220,870✔
856
            else if (ITEM_OBJECT & mask)
5,320,310✔
857
               object_write_ref(item->object, f);
2,834,640✔
858
            else if (ITEM_OBJ_ARRAY & mask) {
2,485,670✔
859
               if (item->obj_array != NULL) {
614,232✔
860
                  const unsigned count = item->obj_array->count;
420,100✔
861
                  fbuf_put_uint(f, count);
420,100✔
862
                  for (unsigned i = 0; i < count; i++)
1,534,460✔
863
                     object_write_ref(item->obj_array->items[i], f);
1,114,360✔
864
               }
865
               else
866
                  fbuf_put_uint(f, 0);
194,132✔
867
            }
868
            else if (ITEM_INT64 & mask)
1,871,440✔
869
               fbuf_put_int(f, item->ival);
337,457✔
870
            else if (ITEM_INT32 & mask)
1,533,980✔
871
               fbuf_put_int(f, item->ival);
1,441,220✔
872
            else if (ITEM_DOUBLE & mask)
92,757✔
873
               write_double(item->dval, f);
92,640✔
874
            else if (ITEM_TEXT & mask) {
117✔
875
               const size_t len = strlen(item->text);
54✔
876
               fbuf_put_uint(f, len);
54✔
877
               write_raw(item->text, len, f);
54✔
878
            }
879
            else if (ITEM_NUMBER & mask)
63✔
880
               number_write(item->number, f);
63✔
881
            else
882
               item_without_type(mask);
×
883
            n++;
6,541,180✔
884
         }
885
      }
886

887
      p = (char *)p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,504,450✔
888
   }
889

890
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
9,209✔
891
}
9,209✔
892

893
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
19,444,600✔
894
{
895
   arena_key_t key = fbuf_get_uint(f);
19,444,600✔
896
   if (key == 0)
19,444,600✔
897
      return NULL;
898

899
   arena_key_t mapped = key_map[key];
16,552,100✔
900
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
16,552,100✔
901

902
   if (unlikely(mapped == 0))
16,552,100✔
903
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
×
904

905
   assert(mapped < all_arenas.count);
16,552,100✔
906
   assert(mapped > 0);
16,552,100✔
907

908
   object_arena_t *arena = all_arenas.items[mapped];
16,552,100✔
909
   assert(!arena->frozen || offset < arena->alloc - arena->base);
16,552,100✔
910

911
   return (object_t *)((char *)arena->base + offset);
16,552,100✔
912
}
913

914
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
6,779✔
915
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
916
{
917
   object_one_time_init();
6,779✔
918

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

926
   const vhdl_standard_t std = fbuf_get_uint(f);
6,779✔
927

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

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

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

942
   object_arena_t *arena = object_arena_new(size, std);
6,773✔
943
   arena->source = OBJ_DISK;
6,773✔
944

945
   arena_key_t key = fbuf_get_uint(f);
6,773✔
946
   ident_t name = ident_read(ident_ctx);
6,773✔
947

948
   arena_key_t max_key = fbuf_get_uint(f);
6,773✔
949

950
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
6,773✔
951
   key_map[key] = arena->key;
6,773✔
952

953
   const int ndeps = fbuf_get_uint(f);
6,773✔
954
   for (int i = 0; i < ndeps; i++) {
12,092✔
955
      arena_key_t dkey = fbuf_get_uint(f);
5,322✔
956
      vhdl_standard_t dstd = fbuf_get_uint(f);
5,322✔
957
      uint32_t checksum = fbuf_get_uint(f);
5,322✔
958
      ident_t dep = ident_read(ident_ctx);
5,322✔
959

960
      object_arena_t *a = NULL;
5,322✔
961
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
23,933✔
962
         if (dep == object_arena_name(all_arenas.items[j]))
18,611✔
963
            a = all_arenas.items[j];
3,846✔
964
      }
965

966
      if (a == NULL) {
5,322✔
967
         object_t *droot = NULL;
1,476✔
968
         if (loader_fn) droot = (*loader_fn)(dep);
1,476✔
969

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

974
         a = __object_arena(droot);
1,476✔
975
      }
976

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

993
      APUSH(arena->deps, a);
5,319✔
994

995
      assert(dkey <= max_key);
5,319✔
996
      key_map[dkey] = a->key;
5,319✔
997
   }
998

999
   for (;;) {
7,616,660✔
1000
      const uint64_t hdr = fbuf_get_uint(f);
7,616,660✔
1001
      if (hdr == UINT16_MAX) break;
7,616,660✔
1002

1003
      const unsigned tag = hdr & 3;
7,609,900✔
1004
      const unsigned kind = hdr >> 2;
7,609,900✔
1005

1006
      assert(tag < OBJECT_TAG_COUNT);
7,609,900✔
1007

1008
      const object_class_t *class = classes[tag];
7,609,900✔
1009

1010
      object_t *object = object_new(arena, class, kind);
7,609,900✔
1011

1012
      if (class->has_loc)
7,609,900✔
1013
         loc_read(&(object->loc), loc_ctx);
6,483,750✔
1014

1015
      const imask_t has = class->has_map[object->kind];
7,609,900✔
1016
      const int nitems = __builtin_popcountll(has);
7,609,900✔
1017
      imask_t mask = 1;
7,609,900✔
1018
      for (int n = 0; n < nitems; mask <<= 1) {
315,052,000✔
1019
         if (has & mask) {
307,442,000✔
1020
            item_t *item = &(object->items[n]);
35,310,800✔
1021
            if (ITEM_IDENT & mask)
35,310,800✔
1022
               item->ident = ident_read(ident_ctx);
7,928,100✔
1023
            else if (ITEM_OBJECT & mask)
27,382,700✔
1024
               item->object = object_read_ref(f, key_map);
12,068,600✔
1025
            else if (ITEM_OBJ_ARRAY & mask) {
15,314,200✔
1026
               const unsigned count = fbuf_get_uint(f);
3,801,150✔
1027
               if (count > 0) {
3,801,150✔
1028
                  item->obj_array = xmalloc_flex(sizeof(obj_array_t),
2,603,050✔
1029
                                                 count, sizeof(object_t *));
1030
                  item->obj_array->count =
2,603,050✔
1031
                     item->obj_array->limit = count;
2,603,050✔
1032
                  for (unsigned i = 0; i < count; i++) {
9,979,070✔
1033
                     object_t *o = object_read_ref(f, key_map);
7,376,020✔
1034
                     item->obj_array->items[i] = o;
7,376,020✔
1035
                  }
1036
               }
1037
            }
1038
            else if (ITEM_INT64 & mask)
11,513,000✔
1039
               item->ival = fbuf_get_int(f);
1,673,350✔
1040
            else if (ITEM_INT32 & mask)
9,839,660✔
1041
               item->ival = fbuf_get_int(f);
9,660,760✔
1042
            else if (ITEM_DOUBLE & mask)
178,894✔
1043
               item->dval = read_double(f);
178,894✔
1044
            else if (ITEM_TEXT & mask) {
×
1045
               const size_t len = fbuf_get_uint(f);
×
1046
               item->text = xmalloc(len + 1);
×
1047
               read_raw(item->text, len, f);
×
1048
               item->text[len] = '\0';
×
1049
            }
1050
            else if (ITEM_NUMBER & mask)
×
1051
               item->number = number_read(f);
×
1052
            else
1053
               item_without_type(mask);
×
1054
            n++;
35,310,800✔
1055
         }
1056
      }
1057
   }
1058

1059
   object_arena_freeze(arena);
6,770✔
1060
   return (object_t *)arena->base;
6,770✔
1061
}
1062

1063
unsigned object_next_generation(void)
51,566✔
1064
{
1065
   return next_generation++;
51,566✔
1066
}
1067

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

1073
   unsigned pos = 0;
1074
   for (; pos < ctx->nroots; pos++) {
5,991,750✔
1075
      if (object->arena == ctx->roots[pos]->arena)
4,940,900✔
1076
         break;
1077
   }
1078

1079
   if (pos == ctx->nroots)
2,535,160✔
1080
      return false;
1081

1082
   if (ctx->copy_map == NULL)
1,484,310✔
1083
      ctx->copy_map = hash_new(1024);
6,581✔
1084

1085
   if (object_marked_p(object, ctx->generation))
1,484,310✔
1086
      return hash_get(ctx->copy_map, object) != NULL;
310,655✔
1087

1088
   const object_class_t *class = classes[object->tag];
1,173,650✔
1089

1090
   bool marked = false;
1,173,650✔
1091
   if (ctx->should_copy[object->tag] != NULL)
1,173,650✔
1092
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,138,030✔
1093

1094
   object_t *copy = NULL;
1,138,030✔
1095
   if (marked) {
1,138,030✔
1096
      copy = object_new(global_arena, class, object->kind);
48,956✔
1097
      hash_put(ctx->copy_map, object, copy);
48,956✔
1098
   }
1099

1100
   const imask_t has = class->has_map[object->kind];
1,173,650✔
1101
   const int nitems = __builtin_popcountll(has);
1,173,650✔
1102
   imask_t mask = 1;
1,173,650✔
1103
   for (int n = 0; n < nitems; mask <<= 1) {
48,164,100✔
1104
      if (has & mask) {
46,990,400✔
1105
         item_t *item = &(object->items[n]);
5,084,140✔
1106
         if (ITEM_IDENT & mask)
5,084,140✔
1107
            ;
1108
         else if (ITEM_OBJECT & mask)
4,141,130✔
1109
            marked |= object_copy_mark(item->object, ctx);
2,233,890✔
1110
         else if (ITEM_DOUBLE & mask)
1,907,240✔
1111
            ;
1112
         else if (ITEM_OBJ_ARRAY & mask) {
1,820,520✔
1113
            if (item->obj_array != NULL) {
445,205✔
1114
               for (unsigned i = 0; i < item->obj_array->count; i++) {
1,126,270✔
1115
                  object_t *o = item->obj_array->items[i];
820,228✔
1116
                  marked |= object_copy_mark(o, ctx);
820,228✔
1117
               }
1118
            }
1119
         }
1120
         else if (ITEM_INT64 & mask)
1,375,320✔
1121
            ;
1122
         else if (ITEM_INT32 & mask)
1,100,050✔
1123
            ;
1124
         else
1125
            item_without_type(mask);
×
1126
         n++;
5,084,140✔
1127
      }
1128
   }
1129

1130
   if (marked && copy == NULL) {
1,173,650✔
1131
      copy = object_new(global_arena, class, object->kind);
243,772✔
1132
      hash_put(ctx->copy_map, object, copy);
243,772✔
1133
   }
1134

1135
   return marked;
1136
}
1137

1138
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
1139
{
1140
   if (object == NULL)
1141
      return NULL;
1142

1143
   object_t *map = hash_get(ctx->copy_map, object);
1144
   return map ?: object;
1145
}
1146

1147
void object_copy(object_copy_ctx_t *ctx)
6,581✔
1148
{
1149
   for (unsigned i = 0; i < ctx->nroots; i++)
18,535✔
1150
      (void)object_copy_mark(ctx->roots[i], ctx);
11,954✔
1151

1152
   unsigned ncopied = 0;
6,581✔
1153
   const void *key;
6,581✔
1154
   void *value;
6,581✔
1155
   for (hash_iter_t it = HASH_BEGIN;
305,890✔
1156
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
299,309✔
1157
      const object_t *object = key;
292,728✔
1158
      object_t *copy = value;
292,728✔
1159
      ncopied++;
292,728✔
1160

1161
      copy->loc = object->loc;
292,728✔
1162

1163
      const object_class_t *class = classes[object->tag];
292,728✔
1164

1165
      const imask_t has = class->has_map[object->kind];
292,728✔
1166
      const int nitems = __builtin_popcountll(has);
292,728✔
1167
      imask_t mask = 1;
292,728✔
1168
      for (int n = 0; n < nitems; mask <<= 1) {
11,514,000✔
1169
         if (has & mask) {
11,221,300✔
1170
            const item_t *from = &(object->items[n]);
1,302,180✔
1171
            item_t *to = &(copy->items[n]);
1,302,180✔
1172

1173
            if (ITEM_IDENT & mask)
1,302,180✔
1174
               to->ident = from->ident;
223,689✔
1175
            else if (ITEM_OBJECT & mask) {
1,078,490✔
1176
               to->object = object_copy_map(from->object, ctx);
552,131✔
1177
               object_write_barrier(copy, to->object);
552,131✔
1178
            }
1179
            else if (ITEM_DOUBLE & mask)
526,361✔
1180
               to->dval = from->dval;
63✔
1181
            else if (ITEM_OBJ_ARRAY & mask) {
526,298✔
1182
               if (from->obj_array != NULL) {
183,436✔
1183
                  // TODO: make a resize macro
1184
                  to->obj_array = xmalloc_flex(sizeof(obj_array_t),
268,062✔
1185
                                               from->obj_array->count,
134,031✔
1186
                                               sizeof(object_t *));
1187
                  to->obj_array->count =
134,031✔
1188
                     to->obj_array->limit = from->obj_array->count;
134,031✔
1189
                  for (size_t i = 0; i < from->obj_array->count; i++) {
416,311✔
1190
                     object_t *o =
282,280✔
1191
                        object_copy_map(from->obj_array->items[i], ctx);
282,280✔
1192
                     to->obj_array->items[i] = o;
282,280✔
1193
                     object_write_barrier(copy, o);
282,280✔
1194
                  }
1195
               }
1196
            }
1197
            else if ((ITEM_INT64 & mask) || (ITEM_INT32 & mask))
342,862✔
1198
               to->ival = from->ival;
342,862✔
1199
            else
1200
               item_without_type(mask);
×
1201
            n++;
1,302,180✔
1202
         }
1203
      }
1204
   }
1205

1206
   for (hash_iter_t it = HASH_BEGIN;
305,890✔
1207
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
299,309✔
1208
      object_t *copy = value;
292,728✔
1209
      if (ctx->callback[copy->tag] != NULL)
292,728✔
1210
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
292,728✔
1211
   }
1212

1213
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
6,581✔
1214
      debugf("copied %d objects into arena %s", ncopied,
×
1215
             istr(object_arena_name(global_arena)));
1216

1217
   for (unsigned i = 0; i < ctx->nroots; i++) {
18,535✔
1218
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
11,954✔
1219
      if (copy != NULL)
11,954✔
1220
         ctx->roots[i] = copy;
5,140✔
1221
   }
1222

1223
   hash_free(ctx->copy_map);
6,581✔
1224
}
6,581✔
1225

1226
size_t object_arena_default_size(void)
12,931✔
1227
{
1228
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
12,931✔
1229
}
1230

1231
object_arena_t *object_arena_new(size_t size, unsigned std)
19,704✔
1232
{
1233
   if (all_arenas.count == 0)
19,704✔
1234
      APUSH(all_arenas, NULL);   // Dummy null arena
3,833✔
1235

1236
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
19,704✔
1237
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
19,704✔
1238
   arena->alloc  = arena->base;
19,704✔
1239
   arena->limit  = (char *)arena->base + size;
19,704✔
1240
   arena->key    = all_arenas.count;
19,704✔
1241
   arena->source = OBJ_FRESH;
19,704✔
1242
   arena->std    = std;
19,704✔
1243

1244
   APUSH(all_arenas, arena);
19,704✔
1245

1246
   if (all_arenas.count == UINT16_MAX - 1)
19,704✔
1247
      fatal_trace("too many object arenas");
×
1248

1249
   return arena;
19,704✔
1250
}
1251

1252
void object_arena_freeze(object_arena_t *arena)
19,247✔
1253
{
1254
   ident_t name = object_arena_name(arena);
19,247✔
1255

1256
   if (arena->frozen)
19,247✔
1257
      fatal_trace("arena %s already frozen", istr(name));
×
1258

1259
   if (arena->source == OBJ_FRESH)
19,247✔
1260
      object_arena_gc(arena);
12,477✔
1261

1262
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
19,247✔
1263
      debugf("arena %s frozen (%d bytes)", istr(name),
×
1264
             (int)(arena->alloc - arena->base));
×
1265

1266
   chash_put(arena_lookup, name, arena);
19,247✔
1267

1268
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
19,247✔
1269
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
19,247✔
1270

1271
   if (next_page < arena->limit) {
19,247✔
1272
#if OBJECT_UNMAP_UNUSED
1273
      nvc_munmap(next_page, arena->limit - next_page);
1274
      arena->limit = next_page;
1275
#else
1276
      // This can be useful for debugging use-after-free
1277
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
19,247✔
1278
#endif
1279
   }
1280

1281
   arena->frozen = true;
19,247✔
1282
}
19,247✔
1283

1284
void object_arena_walk_deps(object_arena_t *arena, object_arena_deps_fn_t fn,
18,524✔
1285
                            void *context)
1286
{
1287
   for (unsigned i = 0; i < arena->deps.count; i++)
71,348✔
1288
      (*fn)(object_arena_name(arena->deps.items[i]), context);
52,824✔
1289
}
18,524✔
1290

1291
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
190,367✔
1292
{
1293
   object_arena_t *arena = __object_arena(object);
190,367✔
1294
   *module = object_arena_name(arena);
190,367✔
1295

1296
   *offset = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
190,367✔
1297

1298
   if (!arena->frozen) {
190,367✔
1299
      *offset = -*offset;
38,424✔
1300
      arena->has_locus = true;
38,424✔
1301
   }
1302
}
190,367✔
1303

1304
static object_arena_t *arena_by_name(ident_t module)
81,099✔
1305
{
1306
   if (global_arena != NULL && object_arena_name(global_arena) == module)
81,099✔
1307
      return global_arena;
6,760✔
1308

1309
   object_arena_t *a = chash_get(arena_lookup, module);
74,339✔
1310
   if (a != NULL)
74,339✔
1311
      return a;
1312

1313
#if defined DEBUG && !defined __SANITIZE_THREAD__
1314
   for (int i = 1; i < all_arenas.count; i++)
879✔
1315
      assert(module != object_arena_name(all_arenas.items[i]));
764✔
1316
#endif
1317

1318
   return NULL;
1319
}
1320

1321
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
71,924✔
1322
                            object_load_fn_t loader)
1323
{
1324
   object_arena_t *arena = arena_by_name(module);
71,924✔
1325

1326
   if (arena == NULL) {
71,924✔
1327
      object_t *droot = NULL;
115✔
1328
      if (loader) droot = (*loader)(module);
115✔
1329

1330
      if (droot == NULL)
115✔
1331
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1332

1333
      arena = __object_arena(droot);
115✔
1334
   }
1335

1336
   if (offset < 0 && arena->frozen && arena->forward == NULL)
71,924✔
1337
      fatal_trace("locus %s%+"PRIiPTR" was created before arena was frozen",
×
1338
                  istr(module), offset);
1339
   else if (offset < 0 && arena->frozen)
71,924✔
1340
      offset = arena->forward[-offset] >> OBJECT_ALIGN_BITS;
4,758✔
1341
   else if (offset < 0)
67,166✔
1342
      offset = -offset;
1343

1344
   void *ptr = (char *)arena->base + (offset << OBJECT_ALIGN_BITS);
71,924✔
1345

1346
   if (ptr > arena->limit)
71,924✔
1347
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
×
1348

1349
   object_t *obj = ptr;
71,924✔
1350
   if (obj->tag >= OBJECT_TAG_COUNT)
71,924✔
1351
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
×
1352
                  istr(module), offset);
1353
   else if (obj->arena != arena->key)
71,924✔
1354
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
×
1355
                  obj->arena, arena->key, istr(module), offset);
×
1356

1357
   return obj;
71,924✔
1358
}
1359

1360
void object_fixup_locus(ident_t module, ptrdiff_t *offset)
90,558✔
1361
{
1362
   if (*offset < 0) {
90,558✔
1363
      object_arena_t *arena = arena_by_name(module);
9,175✔
1364
      if (!arena->frozen)
9,175✔
1365
         fatal_trace("cannot fixup locus %s%+"PRIiPTR" as arena not yet frozen",
×
1366
                     istr(module), *offset);
1367

1368
      *offset = arena->forward[-*offset] >> OBJECT_ALIGN_BITS;
9,175✔
1369
   }
1370
}
90,558✔
1371

1372
void freeze_global_arena(void)
19,802✔
1373
{
1374
   if (global_arena != NULL) {
19,802✔
1375
      object_arena_freeze(global_arena);
12,477✔
1376
      global_arena = NULL;
12,477✔
1377
   }
1378
}
19,802✔
1379

1380
void make_new_arena(void)
12,931✔
1381
{
1382
   freeze_global_arena();
12,931✔
1383
   global_arena = object_arena_new(object_arena_default_size(), standard());
12,931✔
1384
}
12,931✔
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