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

nickg / nvc / 14685578009

26 Apr 2025 08:04PM UTC coverage: 92.344% (-0.004%) from 92.348%
14685578009

push

github

nickg
Optimise iteration over object fields

112 of 121 new or added lines in 1 file covered. (92.56%)

42 existing lines in 2 files now uncovered.

69223 of 74962 relevant lines covered (92.34%)

392751.34 hits per line

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

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

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

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

33
typedef uint64_t mark_mask_t;
34

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

38
typedef enum { OBJ_DISK, OBJ_FRESH } obj_src_t;
39

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

417
static void object_one_time_init(void)
16,220,982✔
418
{
419
   INIT_ONCE({
16,220,982✔
420
         extern object_class_t tree_object;
421
         object_init(&tree_object);
422

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

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

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

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

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

438
         add_fault_handler(check_frozen_object_fault, NULL);
439

440
         arena_lookup = chash_new(64);
441
      });
442
}
16,220,982✔
443

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

451
   return false;
452
}
453

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

532
   imask_t has = class->has_map[object->kind];
347,542✔
533
   if ((has & (ITEM_OBJ_ARRAY | ITEM_TEXT | ITEM_NUMBER)) == 0)
347,542✔
534
      return;
535

536
   for (int n = 0; has; has &= has - 1, n++) {
411,393✔
537
      const uint64_t mask = has & -has;
339,224✔
538
      item_t *item = &(object->items[n]);
339,224✔
539
      if (ITEM_OBJ_ARRAY & mask)
339,224✔
540
         obj_array_free(&(item->obj_array));
88,520✔
541
      else if (ITEM_TEXT & mask)
250,704✔
NEW
542
         free(item->text);
×
543
      else if (ITEM_NUMBER & mask)
250,704✔
NEW
544
         number_free(&item->number);
×
545
   }
546
}
547

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

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

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

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

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

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

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

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

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

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

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

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

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

604
   arena->forward = forward;
15,610✔
605
   arena->live_bytes = woffset;
15,610✔
606

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

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

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

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

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

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

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

635
   imask_t has = class->has_map[object->kind];
516,016✔
636
   for (int n = 0; has; has &= has - 1, n++) {
2,706,908✔
637
      const uint64_t mask = has & -has;
2,190,892✔
638
      if (mask & deep_mask) {
2,190,892✔
639
         item_t *item = &(object->items[n]);
1,567,604✔
640
         if (ITEM_OBJECT & mask)
1,567,604✔
641
            object_visit(item->object, ctx);
369,175✔
642
         else if (ITEM_OBJ_ARRAY & mask) {
1,198,429✔
643
            if (item->obj_array != NULL) {
167,126✔
644
               for (unsigned j = 0; j < item->obj_array->count; j++)
444,620✔
645
                  object_visit(item->obj_array->items[j], ctx);
341,845✔
646
            }
647
         }
648
      }
649
   }
650

651
   if (visit) {
516,016✔
652
      if (ctx->postorder != NULL)
504,413✔
653
         (*ctx->postorder)(object, ctx->context);
504,413✔
654
      ctx->count++;
504,413✔
655
   }
656
}
657

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

671
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
8,095,281✔
672
{
673
   if (object == NULL)
8,095,281✔
674
      return NULL;
675

676
   if (!object_in_arena_p(ctx->arena, object))
6,747,702✔
677
      return object;
678

679
   const ptrdiff_t index =
4,477,161✔
680
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,477,161✔
681

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

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

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

712
   if (ctx->pre_fn[object->tag] != NULL)
3,731,697✔
713
      (*ctx->pre_fn[object->tag])(object, ctx->context);
×
714

715
   const imask_t skip_mask =
3,731,697✔
716
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER
717
      | ITEM_TEXT | ITEM_IDENT;
718

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

721
   imask_t has = class->has_map[object->kind];
3,731,697✔
722
   for (int n = 0; has; has &= has - 1, n++) {
20,040,615✔
723
      const uint64_t mask = has & -has;
16,308,926✔
724
      if (mask & ~skip_mask) {
16,308,926✔
725
         if (ITEM_OBJECT & mask) {
6,827,916✔
726
            object_t *o = object_rewrite(object->items[n].object, ctx);
5,370,948✔
727
            object->items[n].object = o;
5,370,944✔
728
            object_write_barrier(object, o);
5,370,944✔
729
         }
730
         else if (ITEM_OBJ_ARRAY & mask) {
1,456,968✔
731
            obj_array_t **a = &(object->items[n].obj_array);
1,456,968✔
732
            if (object->items[n].obj_array != NULL) {
1,456,968✔
733
               // The callback may add new items to the array so the
734
               // array pointer cannot be cached between iterations
735
               unsigned wptr = 0;
736
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,593,910✔
737
                  object_t *o = object->items[n].obj_array->items[i];
2,625,799✔
738
                  if ((o = object_rewrite(o, ctx))) {
2,625,799✔
739
                     object_write_barrier(object, o);
2,620,726✔
740
                     object->items[n].obj_array->items[wptr++] = o;
2,620,726✔
741
                  }
742
               }
743

744
               if (wptr == 0)
968,111✔
745
                  obj_array_free(a);
970✔
746
               else
747
                  (*a)->count = wptr;
967,141✔
748
            }
749
         }
750
         else
751
            should_not_reach_here();
752
      }
753
   }
754

755
   if (ctx->cache[index] != (object_t *)-1) {
3,731,689✔
756
      // The cache was already updated due to a circular reference
757
      return ctx->cache[index];
758
   }
759
   else if (ctx->post_fn[object->tag] != NULL) {
3,730,594✔
760
      object_t *new = object_rewrite_iter(object, ctx);
3,514,095✔
761
      object_write_barrier(object, new);
3,514,091✔
762
      return (ctx->cache[index] = new);
3,514,091✔
763
   }
764
   else
765
      return (ctx->cache[index] = object);
216,499✔
766
}
767

768
static void object_write_ref(object_t *object, fbuf_t *f)
4,218,079✔
769
{
770
   if (object == NULL)
4,218,079✔
771
      fbuf_put_uint(f, 0);
723,998✔
772
   else {
773
      object_arena_t *arena = __object_arena(object);
3,494,081✔
774
      assert(arena->key != 0);
3,494,081✔
775
      fbuf_put_uint(f, arena->key);
3,494,081✔
776

777
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,494,081✔
778
      if (arena->forward != NULL)
3,494,081✔
779
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,175,583✔
780
      else
781
         fbuf_put_uint(f, index);
1,318,498✔
782
   }
783
}
4,218,079✔
784

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

800
   write_u32(format_digest, f);
10,660✔
801
   fbuf_put_uint(f, standard());
10,660✔
802
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
10,660✔
803
   fbuf_put_uint(f, arena->flags);
10,660✔
804
   fbuf_put_uint(f, arena->key);
10,660✔
805
   ident_write(object_arena_name(arena), ident_ctx);
10,660✔
806

807
   arena_key_t max_key = arena->key;
10,660✔
808
   for (unsigned i = 0; i < arena->deps.count; i++)
32,360✔
809
      max_key = MAX(max_key, arena->deps.items[i]->key);
21,700✔
810
   fbuf_put_uint(f, max_key);
10,660✔
811

812
   fbuf_put_uint(f, arena->deps.count);
10,660✔
813
   for (unsigned i = 0; i < arena->deps.count; i++) {
32,360✔
814
      fbuf_put_uint(f, arena->deps.items[i]->key);
21,700✔
815
      fbuf_put_uint(f, arena->deps.items[i]->std);
21,700✔
816
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
21,700✔
817
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
21,700✔
818
   }
819

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

823
      object_t *object = p;
1,866,736✔
824
      object_class_t *class = classes[object->tag];
1,866,736✔
825

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

828
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,866,736✔
829
      if (arena->forward[index] == UINT32_MAX)
1,866,736✔
830
         continue;   // Dead object
252,637✔
831

832
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,614,099✔
833
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,614,099✔
834

835
      if (class->has_loc)
1,614,099✔
836
         loc_write(&object->loc, loc_ctx);
1,514,576✔
837

838
      imask_t has = class->has_map[object->kind];
1,614,099✔
839
      for (int n = 0; has; has &= has - 1, n++) {
8,640,448✔
840
         const uint64_t mask = has & -has;
7,026,349✔
841
         item_t *item = &(object->items[n]);
7,026,349✔
842
         if (ITEM_IDENT & mask)
7,026,349✔
843
            ident_write(item->ident, ident_ctx);
1,309,037✔
844
         else if (ITEM_OBJECT & mask)
5,717,312✔
845
            object_write_ref(item->object, f);
3,087,708✔
846
         else if (ITEM_OBJ_ARRAY & mask) {
2,629,604✔
847
            if (item->obj_array != NULL) {
611,474✔
848
               const unsigned count = item->obj_array->count;
399,521✔
849
               fbuf_put_uint(f, count);
399,521✔
850
               for (unsigned i = 0; i < count; i++)
1,529,892✔
851
                  object_write_ref(item->obj_array->items[i], f);
1,130,371✔
852
            }
853
            else
854
               fbuf_put_uint(f, 0);
211,953✔
855
         }
856
         else if (ITEM_INT64 & mask)
2,018,130✔
857
            fbuf_put_int(f, item->ival);
360,044✔
858
         else if (ITEM_INT32 & mask)
1,658,086✔
859
            fbuf_put_int(f, item->ival);
1,550,776✔
860
         else if (ITEM_DOUBLE & mask)
107,310✔
861
            write_double(item->dval, f);
106,299✔
862
         else if (ITEM_TEXT & mask) {
1,011✔
863
            const size_t len = strlen(item->text);
360✔
864
            fbuf_put_uint(f, len);
360✔
865
            write_raw(item->text, len, f);
360✔
866
         }
867
         else if (ITEM_NUMBER & mask)
651✔
868
            number_write(item->number, f);
651✔
869
         else
870
            should_not_reach_here();
871
      }
872
   }
873

874
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
10,660✔
875
}
10,660✔
876

877
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
36,103,648✔
878
{
879
   arena_key_t key = fbuf_get_uint(f);
36,103,648✔
880
   if (key == 0)
36,103,648✔
881
      return NULL;
882

883
   arena_key_t mapped = key_map[key];
30,672,470✔
884
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
30,672,470✔
885

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

889
   assert(mapped < all_arenas.count);
30,672,470✔
890
   assert(mapped > 0);
30,672,470✔
891

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

895
   return (object_t *)((char *)arena->base + offset);
30,672,470✔
896
}
897

898
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
10,736✔
899
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
900
{
901
   object_one_time_init();
10,736✔
902

903
   const uint32_t ver = read_u32(f);
10,736✔
904
   if (ver != format_digest)
10,736✔
905
      fatal("%s: serialised format digest is %x expected %x. This design "
×
906
            "unit uses a library format from an earlier version of "
907
            PACKAGE_NAME " and should be reanalysed.",
908
            fbuf_file_name(f), ver, format_digest);
909

910
   const vhdl_standard_t std = fbuf_get_uint(f);
10,736✔
911

912
   // If this is the first design unit we've loaded then allow it to set
913
   // the default standard
914
   if (all_arenas.count == 0)
10,736✔
915
      set_default_standard(std);
459✔
916

917
   if (std > standard())
10,736✔
918
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
919
            "is more recent that the currently selected standard %s",
920
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
921

922
   const unsigned size = fbuf_get_uint(f);
10,730✔
923
   if (size & OBJECT_PAGE_MASK)
10,730✔
924
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
925

926
   object_arena_t *arena = object_arena_new(size, std);
10,730✔
927
   arena->source = OBJ_DISK;
10,730✔
928
   arena->flags  = fbuf_get_uint(f);
10,730✔
929

930
   arena_key_t key = fbuf_get_uint(f);
10,730✔
931
   ident_t name = ident_read(ident_ctx);
10,730✔
932

933
   arena_key_t max_key = fbuf_get_uint(f);
10,730✔
934

935
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
10,730✔
936
   key_map[key] = arena->key;
10,730✔
937

938
   const int ndeps = fbuf_get_uint(f);
10,730✔
939
   for (int i = 0; i < ndeps; i++) {
21,598✔
940
      arena_key_t dkey = fbuf_get_uint(f);
10,871✔
941
      vhdl_standard_t dstd = fbuf_get_uint(f);
10,871✔
942
      uint32_t checksum = fbuf_get_uint(f);
10,871✔
943
      ident_t dep = ident_read(ident_ctx);
10,871✔
944

945
      object_arena_t *a = NULL;
10,871✔
946
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
48,263✔
947
         if (dep == object_arena_name(all_arenas.items[j]))
37,392✔
948
            a = all_arenas.items[j];
8,201✔
949
      }
950

951
      if (a == NULL) {
10,871✔
952
         object_t *droot = NULL;
2,670✔
953
         if (loader_fn) droot = (*loader_fn)(dep);
2,670✔
954

955
         if (droot == NULL)
2,670✔
956
            fatal("%s depends on %s which cannot be found",
×
957
                  fbuf_file_name(f), istr(dep));
958

959
         a = __object_arena(droot);
2,670✔
960
      }
961

962
      if (a->std != dstd)
10,871✔
963
         fatal("%s: design unit depends on %s version of %s but conflicting "
×
964
               "%s version has been loaded", fbuf_file_name(f),
965
               standard_text(dstd), istr(dep), standard_text(a->std));
966
      else if (a->checksum != checksum) {
10,871✔
967
         diag_t *d = diag_new(DIAG_FATAL, NULL);
3✔
968
         diag_suppress(d, false);
3✔
969
         diag_printf(d, "%s: design unit depends on %s with checksum %08x "
3✔
970
                     "but the current version in the library has checksum %08x",
971
                     fbuf_file_name(f), istr(dep), checksum, a->checksum);
972
         diag_hint(d, NULL, "this usually means %s is outdated and needs to "
3✔
973
                   "be reanalysed", istr(name));
974
         diag_emit(d);
3✔
975
         fatal_exit(EXIT_FAILURE);
3✔
976
      }
977

978
      APUSH(arena->deps, a);
10,868✔
979

980
      assert(dkey <= max_key);
10,868✔
981
      key_map[dkey] = a->key;
10,868✔
982
   }
983

984
   for (;;) {
13,991,274✔
985
      const uint64_t hdr = fbuf_get_uint(f);
13,991,274✔
986
      if (hdr == UINT16_MAX) break;
13,991,274✔
987

988
      const unsigned tag = hdr & 3;
13,980,547✔
989
      const unsigned kind = hdr >> 2;
13,980,547✔
990

991
      assert(tag < OBJECT_TAG_COUNT);
13,980,547✔
992

993
      const object_class_t *class = classes[tag];
13,980,547✔
994

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

997
      if (class->has_loc)
13,980,547✔
998
         loc_read(&(object->loc), loc_ctx);
12,229,605✔
999

1000
      imask_t has = class->has_map[object->kind];
13,980,547✔
1001
      for (int n = 0; has; has &= has - 1, n++) {
78,066,280✔
1002
         const uint64_t mask = has & -has;
64,085,733✔
1003
         item_t *item = &(object->items[n]);
64,085,733✔
1004
         if (ITEM_IDENT & mask)
64,085,733✔
1005
            item->ident = ident_read(ident_ctx);
13,645,935✔
1006
         else if (ITEM_OBJECT & mask)
50,439,798✔
1007
            item->object = object_read_ref(f, key_map);
23,850,504✔
1008
         else if (ITEM_OBJ_ARRAY & mask) {
26,589,294✔
1009
            const unsigned count = fbuf_get_uint(f);
6,481,653✔
1010
            if (count > 0) {
6,481,653✔
1011
               item->obj_array = xmalloc_flex(sizeof(obj_array_t),
4,301,388✔
1012
                                              count, sizeof(object_t *));
1013
               item->obj_array->count =
4,301,388✔
1014
                  item->obj_array->limit = count;
4,301,388✔
1015
               for (unsigned i = 0; i < count; i++) {
16,554,532✔
1016
                  object_t *o = object_read_ref(f, key_map);
12,253,144✔
1017
                  item->obj_array->items[i] = o;
12,253,144✔
1018
               }
1019
            }
1020
         }
1021
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
20,107,641✔
1022
            item->ival = fbuf_get_int(f);
19,736,280✔
1023
         else if (ITEM_DOUBLE & mask)
371,361✔
1024
            item->dval = read_double(f);
371,361✔
NEW
1025
         else if (ITEM_TEXT & mask) {
×
NEW
1026
            const size_t len = fbuf_get_uint(f);
×
NEW
1027
            item->text = xmalloc(len + 1);
×
NEW
1028
            read_raw(item->text, len, f);
×
NEW
1029
            item->text[len] = '\0';
×
1030
         }
NEW
1031
         else if (ITEM_NUMBER & mask)
×
NEW
1032
            item->number = number_read(f);
×
1033
         else
1034
            should_not_reach_here();
1035
      }
1036
   }
1037

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

1040
   object_arena_freeze(arena);
10,727✔
1041
   return (object_t *)arena->base;
10,727✔
1042
}
1043

1044
unsigned object_next_generation(void)
63,225✔
1045
{
1046
   return next_generation++;
63,225✔
1047
}
1048

1049
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,456,592✔
1050
{
1051
   if (object == NULL)
3,456,592✔
1052
      return false;
1053

1054
   object_arena_t *arena = __object_arena(object);
2,828,542✔
1055
   if (arena->copygen != ctx->generation || !arena->copyflag)
2,828,542✔
1056
      return false;
1057

1058
   if (ctx->copy_map == NULL)
1,705,168✔
1059
      ctx->copy_map = hash_new(1024);
7,944✔
1060

1061
   if (object_marked_p(object, ctx->generation))
1,705,168✔
1062
      return hash_get(ctx->copy_map, object) != NULL;
378,105✔
1063

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

1066
   bool marked = false;
1,327,063✔
1067
   if (ctx->should_copy[object->tag] != NULL)
1,327,063✔
1068
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,323,710✔
1069

1070
   object_t *copy = NULL;
1,323,710✔
1071
   if (marked) {
1,323,710✔
1072
      copy = object_new(global_arena, class, object->kind);
56,237✔
1073
      hash_put(ctx->copy_map, object, copy);
56,237✔
1074
   }
1075

1076
   imask_t has = class->has_map[object->kind];
1,327,063✔
1077
   for (int n = 0; has; has &= has - 1, n++) {
7,079,536✔
1078
      const uint64_t mask = has & -has;
5,752,473✔
1079
      item_t *item = &(object->items[n]);
5,752,473✔
1080
      if (ITEM_OBJECT & mask)
5,752,473✔
1081
         marked |= object_copy_mark(item->object, ctx);
2,549,551✔
1082
      else if (ITEM_OBJ_ARRAY & mask) {
3,202,922✔
1083
         if (item->obj_array != NULL) {
483,880✔
1084
            for (unsigned i = 0; i < item->obj_array->count; i++) {
1,208,524✔
1085
               object_t *o = item->obj_array->items[i];
892,468✔
1086
               marked |= object_copy_mark(o, ctx);
892,468✔
1087
            }
1088
         }
1089
      }
1090
   }
1091

1092
   if (marked && copy == NULL) {
1,327,063✔
1093
      copy = object_new(global_arena, class, object->kind);
271,358✔
1094
      hash_put(ctx->copy_map, object, copy);
271,358✔
1095
   }
1096

1097
   return marked;
1098
}
1099

1100
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
941,965✔
1101
{
1102
   if (object == NULL)
941,965✔
1103
      return NULL;
1104

1105
   object_t *map = hash_get(ctx->copy_map, object);
795,331✔
1106
   return map ?: object;
795,331✔
1107
}
1108

1109
static bool object_copy_root_closure(object_arena_t *a, object_copy_ctx_t *ctx)
77,299✔
1110
{
1111
   bool include = false;
77,299✔
1112

1113
   if (a->copygen == ctx->generation)
77,299✔
1114
      return a->copyflag;
41,132✔
1115

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

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

1122
   a->copygen  = ctx->generation;
36,167✔
1123
   a->copyflag = include;
36,167✔
1124

1125
   return include;
36,167✔
1126
}
1127

1128
void object_copy(object_copy_ctx_t *ctx)
7,944✔
1129
{
1130
   for (int i = 0; i < ctx->nroots; i++) {
22,517✔
1131
      object_arena_t *a = __object_arena(ctx->roots[i]);
14,573✔
1132
      object_copy_root_closure(a, ctx);
14,573✔
1133
   }
1134

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

1138
   unsigned ncopied = 0;
7,944✔
1139
   const void *key;
7,944✔
1140
   void *value;
7,944✔
1141
   for (hash_iter_t it = HASH_BEGIN;
7,944✔
1142
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
335,539✔
1143
      const object_t *object = key;
327,595✔
1144
      object_t *copy = value;
327,595✔
1145
      ncopied++;
327,595✔
1146

1147
      copy->loc = object->loc;
327,595✔
1148

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

1151
      imask_t has = class->has_map[object->kind];
327,595✔
1152
      for (int n = 0; has; has &= has - 1, n++) {
1,798,441✔
1153
         const uint64_t mask = has & -has;
1,470,846✔
1154
         const item_t *from = &(object->items[n]);
1,470,846✔
1155
         item_t *to = &(copy->items[n]);
1,470,846✔
1156

1157
         if (ITEM_IDENT & mask)
1,470,846✔
1158
            to->ident = from->ident;
254,524✔
1159
         else if (ITEM_OBJECT & mask) {
1,216,322✔
1160
            to->object = object_copy_map(from->object, ctx);
627,092✔
1161
            object_write_barrier(copy, to->object);
627,092✔
1162
         }
1163
         else if (ITEM_DOUBLE & mask)
589,230✔
1164
            to->dval = from->dval;
115✔
1165
         else if (ITEM_OBJ_ARRAY & mask) {
589,115✔
1166
            if (from->obj_array != NULL) {
205,588✔
1167
               // TODO: make a resize macro
1168
               to->obj_array = xmalloc_flex(sizeof(obj_array_t),
291,928✔
1169
                                            from->obj_array->count,
145,964✔
1170
                                            sizeof(object_t *));
1171
               to->obj_array->count =
145,964✔
1172
                  to->obj_array->limit = from->obj_array->count;
145,964✔
1173
               for (size_t i = 0; i < from->obj_array->count; i++) {
460,837✔
1174
                  object_t *o =
314,873✔
1175
                     object_copy_map(from->obj_array->items[i], ctx);
314,873✔
1176
                  to->obj_array->items[i] = o;
314,873✔
1177
                  object_write_barrier(copy, o);
314,873✔
1178
               }
1179
            }
1180
         }
1181
         else if ((ITEM_INT64 | ITEM_INT32) & mask)
383,527✔
1182
            to->ival = from->ival;
383,527✔
1183
         else
1184
            should_not_reach_here();
1185
      }
1186
   }
1187

1188
   for (hash_iter_t it = HASH_BEGIN;
7,944✔
1189
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
335,539✔
1190
      object_t *copy = value;
327,595✔
1191
      if (ctx->callback[copy->tag] != NULL)
327,595✔
1192
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
327,583✔
1193
   }
1194

1195
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
7,944✔
1196
      debugf("copied %d objects into arena %s", ncopied,
×
1197
             istr(object_arena_name(global_arena)));
1198

1199
   for (unsigned i = 0; i < ctx->nroots; i++) {
22,517✔
1200
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
14,573✔
1201
      if (copy != NULL)
14,573✔
1202
         ctx->roots[i] = copy;
6,438✔
1203
   }
1204

1205
   hash_free(ctx->copy_map);
7,944✔
1206
}
7,944✔
1207

1208
size_t object_arena_default_size(void)
16,203✔
1209
{
1210
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
16,203✔
1211
}
1212

1213
object_arena_t *object_arena_new(size_t size, unsigned std)
26,933✔
1214
{
1215
   if (all_arenas.count == 0)
26,933✔
1216
      APUSH(all_arenas, NULL);   // Dummy null arena
4,837✔
1217

1218
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
26,933✔
1219
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
26,933✔
1220
   arena->alloc  = arena->base;
26,933✔
1221
   arena->limit  = (char *)arena->base + size;
26,933✔
1222
   arena->key    = all_arenas.count;
26,933✔
1223
   arena->source = OBJ_FRESH;
26,933✔
1224
   arena->std    = std;
26,933✔
1225

1226
   APUSH(all_arenas, arena);
26,933✔
1227

1228
   if (all_arenas.count == UINT16_MAX - 1)
26,933✔
1229
      fatal_trace("too many object arenas");
1230

1231
   return arena;
26,933✔
1232
}
1233

1234
void object_arena_freeze(object_arena_t *arena)
26,337✔
1235
{
1236
   ident_t name = object_arena_name(arena);
26,337✔
1237

1238
   if (arena->frozen)
26,337✔
1239
      fatal_trace("arena %s already frozen", istr(name));
1240

1241
   if (arena->source == OBJ_FRESH)
26,337✔
1242
      object_arena_gc(arena);
15,610✔
1243

1244
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
26,337✔
1245
      debugf("arena %s frozen (%d bytes)", istr(name),
×
1246
             (int)(arena->alloc - arena->base));
×
1247

1248
   chash_put(arena_lookup, name, arena);
26,337✔
1249

1250
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
26,337✔
1251
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
26,337✔
1252

1253
   if (next_page < arena->limit) {
26,337✔
1254
#if OBJECT_UNMAP_UNUSED
1255
      nvc_munmap(next_page, arena->limit - next_page);
1256
      arena->limit = next_page;
1257
#else
1258
      // This can be useful for debugging use-after-free
1259
      nvc_decommit(next_page, arena->limit - next_page);
15,610✔
1260
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
15,610✔
1261
#endif
1262
   }
1263

1264
   arena->frozen = true;
26,337✔
1265
}
26,337✔
1266

1267
void arena_walk_deps(object_arena_t *arena, arena_deps_fn_t fn, void *context)
28,407✔
1268
{
1269
   for (unsigned i = 0; i < arena->deps.count; i++)
113,627✔
1270
      (*fn)(object_arena_name(arena->deps.items[i]), context);
85,220✔
1271
}
28,407✔
1272

1273
void arena_walk_obsolete_deps(object_arena_t *arena, arena_deps_fn_t fn,
10,660✔
1274
                              void *context)
1275
{
1276
   for (unsigned i = 0; i < arena->deps.count; i++) {
32,360✔
1277
      if (arena->deps.items[i]->obsolete)
21,700✔
1278
         (*fn)(object_arena_name(arena->deps.items[i]), context);
6✔
1279
   }
1280
}
10,660✔
1281

1282
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
105,088✔
1283
{
1284
   object_arena_t *arena = __object_arena(object);
105,088✔
1285
   assert(arena->frozen);
105,088✔
1286

1287
   *module = object_arena_name(arena);
105,088✔
1288

1289
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
105,088✔
1290
   if (arena->forward != NULL)
105,088✔
1291
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
49,486✔
1292
   else
1293
      *offset = index;
55,602✔
1294
}
105,088✔
1295

1296
static object_arena_t *arena_by_name(ident_t module)
41,645✔
1297
{
1298
   if (global_arena != NULL && object_arena_name(global_arena) == module)
41,645✔
1299
      return global_arena;
×
1300

1301
   object_arena_t *a = chash_get(arena_lookup, module);
41,645✔
1302
   if (a != NULL)
41,645✔
1303
      return a;
1304

1305
#if defined DEBUG && !defined __SANITIZE_THREAD__
1306
   for (int i = 1; i < all_arenas.count; i++)
8,660✔
1307
      assert(module != object_arena_name(all_arenas.items[i]));
7,671✔
1308
#endif
1309

1310
   return NULL;
1311
}
1312

1313
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
41,645✔
1314
                            object_load_fn_t loader)
1315
{
1316
   object_arena_t *arena = arena_by_name(module);
41,645✔
1317

1318
   if (arena == NULL) {
41,645✔
1319
      object_t *droot = NULL;
989✔
1320
      if (loader) droot = (*loader)(module);
989✔
1321

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

1325
      arena = __object_arena(droot);
989✔
1326
   }
1327

1328
   assert(arena->frozen);
41,645✔
1329

1330
   void *ptr = NULL;
41,645✔
1331
   if (arena->forward != NULL) {
41,645✔
1332
      // TODO: could do binary search here
1333
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
52,021,515✔
1334
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
52,021,515✔
1335
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
30,946✔
1336
            break;
30,946✔
1337
         }
1338
      }
1339
      assert(ptr != NULL);
30,946✔
1340
   }
1341
   else
1342
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
10,699✔
1343

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

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

1355
   return obj;
41,645✔
1356
}
1357

1358
void freeze_global_arena(void)
23,861✔
1359
{
1360
   object_one_time_init();
23,861✔
1361

1362
   if (global_arena != NULL) {
23,861✔
1363
      object_arena_freeze(global_arena);
15,610✔
1364
      global_arena = NULL;
15,610✔
1365
   }
1366
}
23,861✔
1367

1368
void make_new_arena(void)
16,203✔
1369
{
1370
   freeze_global_arena();
16,203✔
1371
   global_arena = object_arena_new(object_arena_default_size(), standard());
16,203✔
1372
}
16,203✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc