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

nickg / nvc / 12967565412

25 Jan 2025 06:48PM UTC coverage: 92.184% (+0.02%) from 92.165%
12967565412

push

github

nickg
Only compact objects when writing to disk

112 of 119 new or added lines in 13 files covered. (94.12%)

606 existing lines in 10 files now uncovered.

64153 of 69592 relevant lines covered (92.18%)

520365.25 hits per line

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

92.09
/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
} object_arena_t;
62

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

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

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

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

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

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

119
static ident_t object_arena_name(object_arena_t *arena)
288,865✔
120
{
121
   if (arena->alloc > arena->base) {
288,865✔
122
      object_t *root = arena_root(arena);
282,311✔
123

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

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

133
   return ident_new("???");
8,780✔
134
}
135

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

142
   if (count == 1)
182,457✔
143
      arena->mark_bits[first] = 0;
156,773✔
144
   else
145
      memset(arena->mark_bits + first, '\0', count * sizeof(uint64_t));
25,684✔
146
}
182,457✔
147

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

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

155
   if (unlikely(arena->mark_bits == NULL)) {
11,121,909✔
156
      const size_t nbits = (arena->limit - arena->base) / OBJECT_ALIGN;
15,849✔
157
      arena->mark_sz = ALIGN_UP(nbits, 64) / 8;
15,849✔
158
      arena->mark_bits = xmalloc(arena->mark_sz);
15,849✔
159
      arena->mark_bits[word] = 0;
15,849✔
160
      arena->mark_low = arena->mark_high = word;
15,849✔
161
      arena->generation = generation;
15,849✔
162
   }
163
   else if (arena->generation != generation) {
11,106,060✔
164
      arena->mark_bits[word] = 0;
57,638✔
165
      arena->mark_low = arena->mark_high = word;
57,638✔
166
      arena->generation = generation;
57,638✔
167
   }
168

169
   // Lazy zeroing of mark bits helps performance with large arenas
170
   if (word < arena->mark_low) {
11,121,909✔
171
      zero_mark_bits(arena, word, arena->mark_low - word);
14,021✔
172
      arena->mark_low = word;
14,021✔
173
   }
174
   else if (word > arena->mark_high) {
11,107,888✔
175
      zero_mark_bits(arena, arena->mark_high + 1, word - arena->mark_high);
168,436✔
176
      arena->mark_high = word;
168,436✔
177
   }
178

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

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

184
   return marked;
11,121,909✔
185
}
186

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

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

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

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

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

212
object_arena_t *object_arena(object_t *object)
210,248✔
213
{
214
   return __object_arena(object);
210,248✔
215
}
216

217
void __object_write_barrier(object_t *lhs, object_t *rhs)
4,798,803✔
218
{
219
   const uintptr_t lhs_mask = (uintptr_t)lhs & ~OBJECT_PAGE_MASK;
4,798,803✔
220
   const uintptr_t rhs_mask = (uintptr_t)rhs & ~OBJECT_PAGE_MASK;
4,798,803✔
221

222
   if (lhs_mask == rhs_mask || rhs == NULL)
4,798,803✔
223
      return;
224
   else if (lhs->arena == rhs->arena)
4,798,803✔
225
      return;
226

227
   object_arena_t *larena = __object_arena(lhs);
4,307,888✔
228
   object_arena_t *rarena = __object_arena(rhs);
4,307,888✔
229

230
   assert(!larena->frozen);
4,307,888✔
231
   assert(rarena->frozen);
4,307,888✔
232

233
   for (unsigned i = 0; i < larena->deps.count; i++) {
7,167,865✔
234
      if (larena->deps.items[i] == rarena)
7,133,736✔
235
         return;
236
   }
237

238
   APUSH(larena->deps, rarena);
34,129✔
239
}
240

241
void object_lookup_failed(object_class_t *class, object_t *object, imask_t mask)
×
242
{
243
   unsigned int item;
×
244
   for (item = 0; (mask & (UINT64_C(1) << item)) == 0; item++)
×
245
      ;
246

247
   assert(item < ARRAY_LEN(item_text_map));
×
248

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

259
void obj_array_add(obj_array_t **a, object_t *o)
1,278,476✔
260
{
261
   if (*a == NULL) {
1,278,476✔
262
      const int defsz = 8;
479,740✔
263
      *a = xmalloc_flex(sizeof(obj_array_t), defsz, sizeof(object_t *));
479,740✔
264
      (*a)->count = 0;
479,740✔
265
      (*a)->limit = defsz;
479,740✔
266
   }
267
   else if ((*a)->count == (*a)->limit) {
798,736✔
268
      (*a)->limit *= 2;
30,789✔
269
      *a = xrealloc_flex(*a, sizeof(obj_array_t),
30,789✔
270
                         (*a)->limit, sizeof(object_t *));
271
   }
272

273
   (*a)->items[(*a)->count++] = o;
1,278,476✔
274
}
1,278,476✔
275

276
void obj_array_free(obj_array_t **a)
86,877✔
277
{
278
   free(*a);
86,877✔
279
   *a = NULL;
86,877✔
280
}
86,877✔
281

282
void object_change_kind(const object_class_t *class, object_t *object, int kind)
23,365✔
283
{
23,365✔
284
   if (kind == object->kind)
23,365✔
285
      return;
×
286

287
   bool allow = false;
288
   for (size_t i = 0; (class->change_allowed[i][0] != -1) && !allow; i++) {
243,023✔
289
      allow = (class->change_allowed[i][0] == object->kind)
219,658✔
290
         && (class->change_allowed[i][1] == kind);
219,658✔
291
   }
292

293
   if (!allow)
23,365✔
294
      fatal_trace("cannot change %s kind %s to %s", class->name,
295
                  class->kind_text_map[object->kind],
×
296
                  class->kind_text_map[kind]);
×
297

298
   const imask_t old_has = class->has_map[object->kind];
23,365✔
299
   const imask_t new_has = class->has_map[kind];
23,365✔
300

301
   const int old_nitems = __builtin_popcountll(old_has);
23,365✔
302
   const int new_nitems = __builtin_popcountll(new_has);
23,365✔
303

304
   const int max_items = MAX(old_nitems, new_nitems);
23,365✔
305

306
   item_t tmp[max_items];
23,365✔
307
   memcpy(tmp, object->items, sizeof(item_t) * max_items);
23,365✔
308

309
   int op = 0, np = 0;
23,365✔
310
   for (imask_t mask = 1; np < new_nitems; mask <<= 1) {
946,456✔
311
      if ((old_has & mask) && (new_has & mask))
923,091✔
312
         object->items[np++] = tmp[op++];
84,998✔
313
      else if (old_has & mask) {
838,093✔
314
         if (ITEM_OBJ_ARRAY & mask)
141✔
315
            obj_array_free(&(tmp[op].obj_array));
115✔
316
         ++op;
141✔
317
      }
318
      else if (new_has & mask)
837,952✔
319
         memset(&(object->items[np++]), '\0', sizeof(item_t));
923,091✔
320
   }
321

322
   object->kind = kind;
23,365✔
323
}
324

325
static void object_init(object_class_t *class)
19,056✔
326
{
327
   class->object_size = xmalloc_array(class->last_kind, sizeof(size_t));
19,056✔
328

329
   assert(class->last_kind < (1 << (sizeof(uint8_t) * 8)));
19,056✔
330

331
   assert(class->tag < ARRAY_LEN(classes));
19,056✔
332
   classes[class->tag] = class;
19,056✔
333

334
#ifdef DEBUG
335
   imask_t all_items = 0;
19,056✔
336
#endif
337

338
   for (int i = 0; i < class->last_kind; i++) {
1,076,664✔
339
      const int nitems = __builtin_popcountll(class->has_map[i]);
1,057,608✔
340
      class->object_size[i] = sizeof(object_t) + (nitems * sizeof(item_t));
1,057,608✔
341
      DEBUG_ONLY(all_items |= class->has_map[i]);
1,057,608✔
342

343
      // Knuth's multiplicative hash
344
      format_digest +=
1,057,608✔
345
         (uint32_t)(class->has_map[i] >> 32) * UINT32_C(2654435761);
1,057,608✔
346
      format_digest +=
1,057,608✔
347
         (uint32_t)(class->has_map[i]) * UINT32_C(2654435761);
1,057,608✔
348
   }
349

350
   bool changed = false;
28,584✔
351
   do {
28,584✔
352
      changed = false;
28,584✔
353
      for (int i = 0; i < class->last_kind; i++) {
2,210,496✔
354
         size_t max_size = class->object_size[i];
2,181,912✔
355
         for (size_t j = 0; class->change_allowed[j][0] != -1; j++) {
29,165,208✔
356
            if (class->change_allowed[j][0] == i)
26,983,296✔
357
               max_size = MAX(max_size,
228,672✔
358
                              class->object_size[class->change_allowed[j][1]]);
359
            else if (class->change_allowed[j][1] == i)
26,754,624✔
360
               max_size = MAX(max_size,
228,672✔
361
                              class->object_size[class->change_allowed[j][0]]);
362
         }
363

364
         if (max_size != class->object_size[i]) {
2,181,912✔
365
            class->object_size[i] = max_size;
57,168✔
366
            changed = true;
57,168✔
367
         }
368
      }
369
   } while (changed);
28,584✔
370

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

378
   const imask_t known_types =
19,056✔
379
      ITEM_IDENT | ITEM_OBJECT | ITEM_OBJ_ARRAY | ITEM_INT64 | ITEM_INT32
380
      | ITEM_DOUBLE | ITEM_TEXT | ITEM_NUMBER;
381

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

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

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

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

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

414
static void object_one_time_init(void)
15,787,774✔
415
{
416
   INIT_ONCE({
15,787,774✔
417
         extern object_class_t tree_object;
418
         object_init(&tree_object);
419

420
         extern object_class_t type_object;
421
         object_init(&type_object);
422

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

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

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

433
         format_digest += format_fudge * UINT32_C(2654435761);
434

435
         add_fault_handler(check_frozen_object_fault, NULL);
436

437
         arena_lookup = chash_new(64);
438
      });
439
}
15,787,774✔
440

441
static bool is_gc_root(const object_class_t *class, int kind)
2,148,301✔
442
{
443
   for (int j = 0; j < class->gc_num_roots; j++) {
20,143,479✔
444
      if (class->gc_roots[j] == kind)
18,042,001✔
445
         return true;
446
   }
447

448
   return false;
449
}
450

451
object_t *object_new(object_arena_t *arena,
15,753,608✔
452
                     const object_class_t *class, int kind)
453
{
454
   if (unlikely(kind >= class->last_kind))
15,753,608✔
455
      fatal_trace("invalid kind %d for %s object", kind, class->name);
456

457
   object_one_time_init();
15,753,608✔
458

459
   if (arena == NULL)
15,753,608✔
460
      arena = global_arena;
1,852,783✔
461

462
   if (unlikely(arena == NULL))
15,753,608✔
463
      fatal_trace("allocating object without active arena");
464

465
   const size_t size = ALIGN_UP(class->object_size[kind], OBJECT_ALIGN);
15,753,608✔
466

467
   assert(((uintptr_t)arena->alloc & (OBJECT_ALIGN - 1)) == 0);
15,753,608✔
468

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

481
   object_t *object = arena->alloc;
15,753,608✔
482
   arena->alloc = (char *)arena->alloc + size;
15,753,608✔
483

484
   if (arena->root == NULL && is_gc_root(class, kind))
15,753,608✔
485
      arena->root = object;
26,629✔
486

487
   memset(object, '\0', size);
15,753,608✔
488

489
   object->kind  = kind;
15,753,608✔
490
   object->tag   = class->tag;
15,753,608✔
491
   object->arena = arena->key;
15,753,608✔
492
   object->loc   = LOC_INVALID;
15,753,608✔
493

494
   return object;
15,753,608✔
495
}
496

497
static void gc_mark_from_root(object_t *object, object_arena_t *arena,
4,743,962✔
498
                              generation_t generation)
499
{
500
   if (object == NULL)
4,743,962✔
501
      return;
502
   else if (!object_in_arena_p(arena, object))
3,938,850✔
503
      return;
504
   else if (object_marked_p(object, generation))
2,363,643✔
505
      return;
506

507
   const object_class_t *class = classes[object->tag];
1,787,830✔
508

509
   const imask_t has = class->has_map[object->kind];
1,787,830✔
510
   const int nitems = __builtin_popcountll(has);
1,787,830✔
511
   imask_t mask = 1;
1,787,830✔
512
   for (int i = 0; i < nitems; mask <<= 1) {
73,162,256✔
513
      if (has & mask) {
71,374,426✔
514
         item_t *item = &(object->items[i++]);
7,830,612✔
515
         if (ITEM_OBJECT & mask)
7,830,612✔
516
            gc_mark_from_root(item->object, arena, generation);
3,313,284✔
517
         else if (ITEM_OBJ_ARRAY & mask) {
4,517,328✔
518
            if (item->obj_array != NULL) {
823,291✔
519
               for (unsigned j = 0; j < item->obj_array->count; j++)
1,958,546✔
520
                  gc_mark_from_root(item->obj_array->items[j], arena,
1,410,484✔
521
                                    generation);
522
            }
523
         }
524
      }
525
   }
526
}
527

528
static void gc_free_external(object_t *object)
333,842✔
529
{
530
   const object_class_t *class = classes[object->tag];
333,842✔
531
   const imask_t has = class->has_map[object->kind];
333,842✔
532
   const int nitems = __builtin_popcountll(has);
333,842✔
533
   imask_t mask = 1;
333,842✔
534
   for (int i = 0; i < nitems; mask <<= 1) {
14,823,552✔
535
      if (has & mask) {
14,489,710✔
536
         item_t *item = &(object->items[i++]);
1,424,146✔
537
         if (ITEM_OBJ_ARRAY & mask)
1,424,146✔
538
            obj_array_free(&(item->obj_array));
85,783✔
539
         else if (ITEM_TEXT & mask)
1,338,363✔
NEW
540
            free(item->text);
×
541
         else if (ITEM_NUMBER & mask)
1,338,363✔
NEW
542
            number_free(&item->number);
×
543
      }
544
   }
545
}
333,842✔
546

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

552
   // Mark
553
   for (void *p = arena->base; p != arena->alloc; ) {
2,137,021✔
554
      assert(p < arena->alloc);
2,121,672✔
555
      object_t *object = p;
2,121,672✔
556

557
      const object_class_t *class = classes[object->tag];
2,121,672✔
558

559
      if (is_gc_root(class, object->kind))
2,121,672✔
560
         gc_mark_from_root(object, arena, generation);
20,194✔
561

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

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

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

573
   // Calculate forwarding addresses
574
   unsigned woffset = 0, live = 0, dead = 0;
15,349✔
575
   for (void *rptr = arena->base; rptr != arena->alloc; ) {
2,137,021✔
576
      assert(rptr < arena->alloc);
2,121,672✔
577
      object_t *object = rptr;
2,121,672✔
578

579
      const object_class_t *class = classes[object->tag];
2,121,672✔
580

581
      const size_t size =
2,121,672✔
582
         ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
2,121,672✔
583

584
      ptrdiff_t index = (rptr - arena->base) >> OBJECT_ALIGN_BITS;
2,121,672✔
585
      if (!object_marked_p(object, generation)) {
2,121,672✔
586
         forward[index] = UINT32_MAX;
333,842✔
587
         gc_free_external(object);
333,842✔
588
         dead++;
333,842✔
589
      }
590
      else {
591
         forward[index] = woffset;
1,787,830✔
592
         woffset += size;
1,787,830✔
593
         live++;
1,787,830✔
594
      }
595

596
      rptr = (char *)rptr + size;
2,121,672✔
597
   }
598

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

603
   arena->forward = forward;
15,349✔
604
   arena->live_bytes = woffset;
15,349✔
605

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

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

618
   if (object == NULL)
740,551✔
619
      return;
620
   else if (object_marked_p(object, ctx->generation))
556,430✔
621
      return;
622

623
   const object_class_t *class = classes[object->tag];
531,694✔
624

625
   const bool visit =
1,063,388✔
626
      (object->tag == ctx->tag && object->kind == ctx->kind)
531,694✔
627
      || ctx->kind == class->last_kind;
1,061,304✔
628

629
   if (visit && ctx->preorder != NULL)
531,694✔
UNCOV
630
      (*ctx->preorder)(object, ctx->context);
×
631

632
   const imask_t deep_mask = ~(ctx->deep ? 0 : I_TYPE | I_REF);
531,694✔
633

634
   const imask_t has = class->has_map[object->kind];
531,694✔
635
   const int nitems = __builtin_popcountll(has);
531,694✔
636
   imask_t mask = 1;
531,694✔
637
   for (int i = 0; i < nitems; mask <<= 1) {
22,436,184✔
638
      if (has & mask & deep_mask) {
21,904,490✔
639
         item_t *item = &(object->items[i]);
1,608,608✔
640
         if (ITEM_OBJECT & mask)
1,608,608✔
641
            object_visit(item->object, ctx);
375,567✔
642
         else if (ITEM_OBJ_ARRAY & mask) {
1,233,041✔
643
            if (item->obj_array != NULL) {
166,933✔
644
               for (unsigned j = 0; j < item->obj_array->count; j++)
458,461✔
645
                  object_visit(item->obj_array->items[j], ctx);
353,558✔
646
            }
647
         }
648
      }
649

650
      if (has & mask)
21,904,490✔
651
         i++;
2,258,469✔
652
   }
653

654
   if (visit) {
531,694✔
655
      if (ctx->postorder != NULL)
519,758✔
656
         (*ctx->postorder)(object, ctx->context);
519,758✔
657
      ctx->count++;
519,758✔
658
   }
659
}
660

661
static object_t *object_rewrite_iter(object_t *object,
3,455,967✔
662
                                     object_rewrite_ctx_t *ctx)
663
{
664
   // The callback may return a new object or a pointer to an existing
665
   // object in the same arena that that needs to be rewritten so
666
   // iterate rewriting until we reach a fixed point
667
   for (;;) {
3,517,522✔
668
      object_t *new = (*ctx->post_fn[object->tag])(object, ctx->context);
3,517,522✔
669
      if (new == object || (object = object_rewrite(new, ctx)) == NULL)
3,517,518✔
670
         return new;
3,455,963✔
671
   }
672
}
673

674
object_t *object_rewrite(object_t *object, object_rewrite_ctx_t *ctx)
7,921,483✔
675
{
676
   if (object == NULL)
7,921,483✔
677
      return NULL;
678

679
   if (!object_in_arena_p(ctx->arena, object))
6,593,367✔
680
      return object;
681

682
   const ptrdiff_t index =
4,390,902✔
683
      ((void *)object - ctx->arena->base) >> OBJECT_ALIGN_BITS;
4,390,902✔
684

685
   // New objects can be allocated while rewrite is in progress so we
686
   // need to check if the inex is greater than the current cache size
687
   if (unlikely(ctx->cache == NULL || index >= ctx->cache_sz)) {
4,390,902✔
688
      ctx->cache_sz = (ctx->arena->alloc - ctx->arena->base) / OBJECT_ALIGN;
67,242✔
689
      ctx->cache = xrealloc_array(ctx->cache, sizeof(object_t *),
67,242✔
690
                                  ctx->cache_sz);
691
   }
692

693
   if (object_marked_p(object, ctx->generation)) {
4,390,902✔
694
      if (ctx->cache[index] == (object_t *)-1) {
719,761✔
695
         // Found a circular reference: eagerly rewrite the object now
696
         // and break the cycle
697
         if (ctx->post_fn[object->tag] != NULL) {
1,068✔
698
            if (ctx->pre_fn[object->tag] != NULL)
35✔
699
               (*ctx->pre_fn[object->tag])(object, ctx->context);
3✔
700
            object_t *new = object_rewrite_iter(object, ctx);
35✔
701
            object_write_barrier(object, new);
35✔
702
            return (ctx->cache[index] = new);
35✔
703
         }
704
         else
705
            return (ctx->cache[index] = object);
1,033✔
706
      }
707
      else {
708
         // Already rewritten this tree so return the cached version
709
         return ctx->cache[index];
710
      }
711
   }
712

713
   ctx->cache[index] = (object_t *)-1;  // Rewrite in progress marker
3,671,141✔
714

715
   if (ctx->pre_fn[object->tag] != NULL)
3,671,141✔
716
      (*ctx->pre_fn[object->tag])(object, ctx->context);
1,856,594✔
717

718
   const imask_t skip_mask =
3,671,141✔
719
      I_REF | ITEM_INT64 | ITEM_INT32 | ITEM_DOUBLE | ITEM_NUMBER
720
      | ITEM_TEXT | ITEM_IDENT;
721

722
   const object_class_t *class = classes[object->tag];
3,671,141✔
723

724
   const imask_t has = class->has_map[object->kind];
3,671,141✔
725
   const int nitems = __builtin_popcountll(has);
3,671,141✔
726
   imask_t mask = 1;
3,671,141✔
727
   for (int n = 0; n < nitems; mask <<= 1) {
151,743,039✔
728
      if (has & mask & ~skip_mask) {
148,071,906✔
729
         if (ITEM_OBJECT & mask) {
6,666,828✔
730
            object_t *o = object->items[n].object;
5,156,862✔
731
            object->items[n].object = object_rewrite(o, ctx);
5,156,862✔
732
            object_write_barrier(object, o);
5,156,858✔
733
         }
734
         else if (ITEM_OBJ_ARRAY & mask) {
1,509,966✔
735
            obj_array_t **a = &(object->items[n].obj_array);
1,509,966✔
736
            if (object->items[n].obj_array != NULL) {
1,509,966✔
737
               // The callback may add new items to the array so the
738
               // array pointer cannot be cached between iterations
739
               unsigned wptr = 0;
740
               for (size_t i = 0; i < object->items[n].obj_array->count; i++) {
3,711,059✔
741
                  object_t *o = object->items[n].obj_array->items[i];
2,670,604✔
742
                  if ((o = object_rewrite(o, ctx))) {
2,670,604✔
743
                     object_write_barrier(object, o);
2,665,788✔
744
                     object->items[n].obj_array->items[wptr++] = o;
2,665,788✔
745
                  }
746
               }
747

748
               if (wptr == 0)
1,040,455✔
749
                  obj_array_free(a);
979✔
750
               else
751
                  (*a)->count = wptr;
1,039,476✔
752
            }
753
         }
754
         else
755
            should_not_reach_here();
756
      }
757

758
      if (has & mask)
148,071,898✔
759
         n++;
16,033,069✔
760
   }
761

762
   if (ctx->cache[index] != (object_t *)-1) {
3,671,133✔
763
      // The cache was already updated due to a circular reference
764
      return ctx->cache[index];
765
   }
766
   else if (ctx->post_fn[object->tag] != NULL) {
3,670,065✔
767
      object_t *new = object_rewrite_iter(object, ctx);
3,455,932✔
768
      object_write_barrier(object, new);
3,455,928✔
769
      return (ctx->cache[index] = new);
3,455,928✔
770
   }
771
   else
772
      return (ctx->cache[index] = object);
214,133✔
773
}
774

775
static void object_write_ref(object_t *object, fbuf_t *f)
4,193,195✔
776
{
777
   if (object == NULL)
4,193,195✔
778
      fbuf_put_uint(f, 0);
724,035✔
779
   else {
780
      object_arena_t *arena = __object_arena(object);
3,469,160✔
781
      assert(arena->key != 0);
3,469,160✔
782
      fbuf_put_uint(f, arena->key);
3,469,160✔
783

784
      ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
3,469,160✔
785
      if (arena->forward != NULL)
3,469,160✔
786
         fbuf_put_uint(f, arena->forward[index] >> OBJECT_ALIGN_BITS);
2,166,191✔
787
      else
788
         fbuf_put_uint(f, index);
1,302,969✔
789
   }
790
}
4,193,195✔
791

792
void object_write(object_t *root, fbuf_t *f, ident_wr_ctx_t ident_ctx,
10,469✔
793
                  loc_wr_ctx_t *loc_ctx)
794
{
795
   object_arena_t *arena = __object_arena(root);
10,469✔
796
   if (root != arena_root(arena))
10,469✔
797
      fatal_trace("must write root object first");
798
   else if (arena->source == OBJ_DISK)
10,469✔
799
      fatal_trace("writing arena %s originally read from disk",
800
                  istr(object_arena_name(arena)));
801
   else if (!arena->frozen)
10,469✔
802
      fatal_trace("arena %s must be frozen before writing to disk",
803
                  istr(object_arena_name(arena)));
804

805
   write_u32(format_digest, f);
10,469✔
806
   fbuf_put_uint(f, standard());
10,469✔
807
   fbuf_put_uint(f, ALIGN_UP(arena->live_bytes, OBJECT_PAGE_SZ));
10,469✔
808
   fbuf_put_uint(f, arena->flags);
10,469✔
809
   fbuf_put_uint(f, arena->key);
10,469✔
810
   ident_write(object_arena_name(arena), ident_ctx);
10,469✔
811

812
   arena_key_t max_key = arena->key;
10,469✔
813
   for (unsigned i = 0; i < arena->deps.count; i++)
31,819✔
814
      max_key = MAX(max_key, arena->deps.items[i]->key);
21,350✔
815
   fbuf_put_uint(f, max_key);
10,469✔
816

817
   fbuf_put_uint(f, arena->deps.count);
10,469✔
818
   for (unsigned i = 0; i < arena->deps.count; i++) {
31,819✔
819
      fbuf_put_uint(f, arena->deps.items[i]->key);
21,350✔
820
      fbuf_put_uint(f, arena->deps.items[i]->std);
21,350✔
821
      fbuf_put_uint(f, arena->deps.items[i]->checksum);
21,350✔
822
      ident_write(object_arena_name(arena->deps.items[i]), ident_ctx);
21,350✔
823
   }
824

825
   for (void *p = arena->base, *next; p != arena->alloc; p = next) {
1,860,966✔
826
      assert(p < arena->alloc);
1,850,497✔
827

828
      object_t *object = p;
1,850,497✔
829
      object_class_t *class = classes[object->tag];
1,850,497✔
830

831
      next = p + ALIGN_UP(class->object_size[object->kind], OBJECT_ALIGN);
1,850,497✔
832

833
      ptrdiff_t index = (p - arena->base) >> OBJECT_ALIGN_BITS;
1,850,497✔
834
      if (arena->forward[index] == UINT32_MAX)
1,850,497✔
835
         continue;   // Dead object
243,047✔
836

837
      STATIC_ASSERT(OBJECT_TAG_COUNT <= 4);
1,607,450✔
838
      fbuf_put_uint(f, object->tag | (object->kind << 2));
1,607,450✔
839

840
      if (class->has_loc)
1,607,450✔
841
         loc_write(&object->loc, loc_ctx);
1,508,971✔
842

843
      const imask_t has = class->has_map[object->kind];
1,607,450✔
844
      const int nitems = __builtin_popcountll(has);
1,607,450✔
845
      imask_t mask = 1;
1,607,450✔
846
      for (int n = 0; n < nitems; mask <<= 1) {
66,530,225✔
847
         if (has & mask) {
64,922,775✔
848
            item_t *item = &(object->items[n]);
6,995,829✔
849
            if (ITEM_IDENT & mask)
6,995,829✔
850
               ident_write(item->ident, ident_ctx);
1,304,250✔
851
            else if (ITEM_OBJECT & mask)
5,691,579✔
852
               object_write_ref(item->object, f);
3,025,678✔
853
            else if (ITEM_OBJ_ARRAY & mask) {
2,665,901✔
854
               if (item->obj_array != NULL) {
646,059✔
855
                  const unsigned count = item->obj_array->count;
439,496✔
856
                  fbuf_put_uint(f, count);
439,496✔
857
                  for (unsigned i = 0; i < count; i++)
1,607,013✔
858
                     object_write_ref(item->obj_array->items[i], f);
1,167,517✔
859
               }
860
               else
861
                  fbuf_put_uint(f, 0);
206,563✔
862
            }
863
            else if (ITEM_INT64 & mask)
2,019,842✔
864
               fbuf_put_int(f, item->ival);
364,317✔
865
            else if (ITEM_INT32 & mask)
1,655,525✔
866
               fbuf_put_int(f, item->ival);
1,549,277✔
867
            else if (ITEM_DOUBLE & mask)
106,248✔
868
               write_double(item->dval, f);
105,237✔
869
            else if (ITEM_TEXT & mask) {
1,011✔
870
               const size_t len = strlen(item->text);
360✔
871
               fbuf_put_uint(f, len);
360✔
872
               write_raw(item->text, len, f);
360✔
873
            }
874
            else if (ITEM_NUMBER & mask)
651✔
875
               number_write(item->number, f);
651✔
876
            else
877
               should_not_reach_here();
878
            n++;
6,995,829✔
879
         }
880
      }
881
   }
882

883
   fbuf_put_uint(f, UINT16_MAX);   // End of objects marker
10,469✔
884
}
10,469✔
885

886
static object_t *object_read_ref(fbuf_t *f, const arena_key_t *key_map)
34,935,389✔
887
{
888
   arena_key_t key = fbuf_get_uint(f);
34,935,389✔
889
   if (key == 0)
34,935,389✔
890
      return NULL;
891

892
   arena_key_t mapped = key_map[key];
29,648,834✔
893
   ptrdiff_t offset = fbuf_get_uint(f) << OBJECT_ALIGN_BITS;
29,648,834✔
894

895
   if (unlikely(mapped == 0))
29,648,834✔
896
      fatal_trace("%s missing dependency with key %d", fbuf_file_name(f), key);
897

898
   assert(mapped < all_arenas.count);
29,648,834✔
899
   assert(mapped > 0);
29,648,834✔
900

901
   object_arena_t *arena = all_arenas.items[mapped];
29,648,834✔
902
   assert(!arena->frozen || offset < arena->alloc - arena->base);
29,648,834✔
903

904
   return (object_t *)((char *)arena->base + offset);
29,648,834✔
905
}
906

907
object_t *object_read(fbuf_t *f, object_load_fn_t loader_fn,
10,709✔
908
                      ident_rd_ctx_t ident_ctx, loc_rd_ctx_t *loc_ctx)
909
{
910
   object_one_time_init();
10,709✔
911

912
   const uint32_t ver = read_u32(f);
10,709✔
913
   if (ver != format_digest)
10,709✔
UNCOV
914
      fatal("%s: serialised format digest is %x expected %x. This design "
×
915
            "unit uses a library format from an earlier version of "
916
            PACKAGE_NAME " and should be reanalysed.",
917
            fbuf_file_name(f), ver, format_digest);
918

919
   const vhdl_standard_t std = fbuf_get_uint(f);
10,709✔
920

921
   // If this is the first design unit we've loaded then allow it to set
922
   // the default standard
923
   if (all_arenas.count == 0)
10,709✔
924
      set_default_standard(std);
453✔
925

926
   if (std > standard())
10,709✔
927
      fatal("%s: design unit was analysed using standard revision %s which "
6✔
928
            "is more recent that the currently selected standard %s",
929
            fbuf_file_name(f), standard_text(std), standard_text(standard()));
930

931
   const unsigned size = fbuf_get_uint(f);
10,703✔
932
   if (size & OBJECT_PAGE_MASK)
10,703✔
UNCOV
933
      fatal("%s: arena size %x bad alignment", fbuf_file_name(f), size);
×
934

935
   object_arena_t *arena = object_arena_new(size, std);
10,703✔
936
   arena->source = OBJ_DISK;
10,703✔
937
   arena->flags  = fbuf_get_uint(f);
10,703✔
938

939
   arena_key_t key = fbuf_get_uint(f);
10,703✔
940
   ident_t name = ident_read(ident_ctx);
10,703✔
941

942
   arena_key_t max_key = fbuf_get_uint(f);
10,703✔
943

944
   arena_key_t *key_map LOCAL = xcalloc_array(max_key + 1, sizeof(arena_key_t));
10,703✔
945
   key_map[key] = arena->key;
10,703✔
946

947
   const int ndeps = fbuf_get_uint(f);
10,703✔
948
   for (int i = 0; i < ndeps; i++) {
21,671✔
949
      arena_key_t dkey = fbuf_get_uint(f);
10,971✔
950
      vhdl_standard_t dstd = fbuf_get_uint(f);
10,971✔
951
      uint32_t checksum = fbuf_get_uint(f);
10,971✔
952
      ident_t dep = ident_read(ident_ctx);
10,971✔
953

954
      object_arena_t *a = NULL;
10,971✔
955
      for (unsigned j = 1; a == NULL && j < all_arenas.count; j++) {
50,776✔
956
         if (dep == object_arena_name(all_arenas.items[j]))
39,805✔
957
            a = all_arenas.items[j];
8,307✔
958
      }
959

960
      if (a == NULL) {
10,971✔
961
         object_t *droot = NULL;
2,664✔
962
         if (loader_fn) droot = (*loader_fn)(dep);
2,664✔
963

964
         if (droot == NULL)
2,664✔
UNCOV
965
            fatal("%s depends on %s which cannot be found",
×
966
                  fbuf_file_name(f), istr(dep));
967

968
         a = __object_arena(droot);
2,664✔
969
      }
970

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

987
      APUSH(arena->deps, a);
10,968✔
988

989
      assert(dkey <= max_key);
10,968✔
990
      key_map[dkey] = a->key;
10,968✔
991
   }
992

993
   for (;;) {
13,578,744✔
994
      const uint64_t hdr = fbuf_get_uint(f);
13,578,744✔
995
      if (hdr == UINT16_MAX) break;
13,578,744✔
996

997
      const unsigned tag = hdr & 3;
13,568,044✔
998
      const unsigned kind = hdr >> 2;
13,568,044✔
999

1000
      assert(tag < OBJECT_TAG_COUNT);
13,568,044✔
1001

1002
      const object_class_t *class = classes[tag];
13,568,044✔
1003

1004
      object_t *object = object_new(arena, class, kind);
13,568,044✔
1005

1006
      if (class->has_loc)
13,568,044✔
1007
         loc_read(&(object->loc), loc_ctx);
11,849,888✔
1008

1009
      const imask_t has = class->has_map[object->kind];
13,568,044✔
1010
      const int nitems = __builtin_popcountll(has);
13,568,044✔
1011
      imask_t mask = 1;
13,568,044✔
1012
      for (int n = 0; n < nitems; mask <<= 1) {
570,899,177✔
1013
         if (has & mask) {
557,331,133✔
1014
            item_t *item = &(object->items[n]);
62,218,093✔
1015
            if (ITEM_IDENT & mask)
62,218,093✔
1016
               item->ident = ident_read(ident_ctx);
13,287,400✔
1017
            else if (ITEM_OBJECT & mask)
48,930,693✔
1018
               item->object = object_read_ref(f, key_map);
22,805,467✔
1019
            else if (ITEM_OBJ_ARRAY & mask) {
26,125,226✔
1020
               const unsigned count = fbuf_get_uint(f);
6,515,329✔
1021
               if (count > 0) {
6,515,329✔
1022
                  item->obj_array = xmalloc_flex(sizeof(obj_array_t),
4,388,486✔
1023
                                                 count, sizeof(object_t *));
1024
                  item->obj_array->count =
4,388,486✔
1025
                     item->obj_array->limit = count;
4,388,486✔
1026
                  for (unsigned i = 0; i < count; i++) {
16,518,408✔
1027
                     object_t *o = object_read_ref(f, key_map);
12,129,922✔
1028
                     item->obj_array->items[i] = o;
12,129,922✔
1029
                  }
1030
               }
1031
            }
1032
            else if (ITEM_INT64 & mask)
19,609,897✔
1033
               item->ival = fbuf_get_int(f);
2,782,538✔
1034
            else if (ITEM_INT32 & mask)
16,827,359✔
1035
               item->ival = fbuf_get_int(f);
16,473,908✔
1036
            else if (ITEM_DOUBLE & mask)
353,451✔
1037
               item->dval = read_double(f);
353,451✔
UNCOV
1038
            else if (ITEM_TEXT & mask) {
×
UNCOV
1039
               const size_t len = fbuf_get_uint(f);
×
UNCOV
1040
               item->text = xmalloc(len + 1);
×
UNCOV
1041
               read_raw(item->text, len, f);
×
UNCOV
1042
               item->text[len] = '\0';
×
1043
            }
UNCOV
1044
            else if (ITEM_NUMBER & mask)
×
UNCOV
1045
               item->number = number_read(f);
×
1046
            else
1047
               should_not_reach_here();
1048
            n++;
62,218,093✔
1049
         }
1050
      }
1051
   }
1052

1053
   assert(ALIGN_UP(arena->alloc - arena->base, OBJECT_PAGE_SZ) == size);
10,700✔
1054

1055
   object_arena_freeze(arena);
10,700✔
1056
   return (object_t *)arena->base;
10,700✔
1057
}
1058

1059
unsigned object_next_generation(void)
62,297✔
1060
{
1061
   return next_generation++;
62,297✔
1062
}
1063

1064
static bool object_copy_mark(object_t *object, object_copy_ctx_t *ctx)
3,429,803✔
1065
{
1066
   if (object == NULL)
3,429,803✔
1067
      return false;
1068

1069
   object_arena_t *arena = __object_arena(object);
2,805,796✔
1070
   if (arena->copygen != ctx->generation || !arena->copyflag)
2,805,796✔
1071
      return false;
1072

1073
   if (ctx->copy_map == NULL)
1,689,262✔
1074
      ctx->copy_map = hash_new(1024);
7,872✔
1075

1076
   if (object_marked_p(object, ctx->generation))
1,689,262✔
1077
      return hash_get(ctx->copy_map, object) != NULL;
375,008✔
1078

1079
   const object_class_t *class = classes[object->tag];
1,314,254✔
1080

1081
   bool marked = false;
1,314,254✔
1082
   if (ctx->should_copy[object->tag] != NULL)
1,314,254✔
1083
      marked = (*ctx->should_copy[object->tag])(object, ctx->pred_context);
1,311,009✔
1084

1085
   object_t *copy = NULL;
1,311,009✔
1086
   if (marked) {
1,311,009✔
1087
      copy = object_new(global_arena, class, object->kind);
55,931✔
1088
      hash_put(ctx->copy_map, object, copy);
55,931✔
1089
   }
1090

1091
   const imask_t has = class->has_map[object->kind];
1,314,254✔
1092
   const int nitems = __builtin_popcountll(has);
1,314,254✔
1093
   imask_t mask = 1;
1,314,254✔
1094
   for (int n = 0; n < nitems; mask <<= 1) {
54,415,701✔
1095
      if (has & mask) {
53,101,447✔
1096
         item_t *item = &(object->items[n]);
5,709,903✔
1097
         if (ITEM_OBJECT & mask)
5,709,903✔
1098
            marked |= object_copy_mark(item->object, ctx);
2,491,132✔
1099
         else if (ITEM_OBJ_ARRAY & mask) {
3,218,771✔
1100
            if (item->obj_array != NULL) {
512,904✔
1101
               for (unsigned i = 0; i < item->obj_array->count; i++) {
1,275,046✔
1102
                  object_t *o = item->obj_array->items[i];
924,240✔
1103
                  marked |= object_copy_mark(o, ctx);
924,240✔
1104
               }
1105
            }
1106
         }
1107
         n++;
5,709,903✔
1108
      }
1109
   }
1110

1111
   if (marked && copy == NULL) {
1,314,254✔
1112
      copy = object_new(global_arena, class, object->kind);
276,850✔
1113
      hash_put(ctx->copy_map, object, copy);
276,850✔
1114
   }
1115

1116
   return marked;
1117
}
1118

1119
static object_t *object_copy_map(object_t *object, object_copy_ctx_t *ctx)
948,926✔
1120
{
1121
   if (object == NULL)
948,926✔
1122
      return NULL;
1123

1124
   object_t *map = hash_get(ctx->copy_map, object);
797,706✔
1125
   return map ?: object;
797,706✔
1126
}
1127

1128
static bool object_copy_root_closure(object_arena_t *a, object_copy_ctx_t *ctx)
76,751✔
1129
{
1130
   bool include = false;
76,751✔
1131

1132
   if (a->copygen == ctx->generation)
76,751✔
1133
      return a->copyflag;
40,860✔
1134

1135
   for (int i = 0; i < ctx->nroots; i++)
98,081✔
1136
      include |= (__object_arena(ctx->roots[i]) == a);
62,190✔
1137

1138
   for (int i = 0; i < a->deps.count; i++)
98,211✔
1139
      include |= object_copy_root_closure(a->deps.items[i], ctx);
62,320✔
1140

1141
   a->copygen  = ctx->generation;
35,891✔
1142
   a->copyflag = include;
35,891✔
1143

1144
   return include;
35,891✔
1145
}
1146

1147
void object_copy(object_copy_ctx_t *ctx)
7,872✔
1148
{
1149
   for (int i = 0; i < ctx->nroots; i++) {
22,303✔
1150
      object_arena_t *a = __object_arena(ctx->roots[i]);
14,431✔
1151
      object_copy_root_closure(a, ctx);
14,431✔
1152
   }
1153

1154
   for (int i = 0; i < ctx->nroots; i++)
22,303✔
1155
      (void)object_copy_mark(ctx->roots[i], ctx);
14,431✔
1156

1157
   unsigned ncopied = 0;
7,872✔
1158
   const void *key;
7,872✔
1159
   void *value;
7,872✔
1160
   for (hash_iter_t it = HASH_BEGIN;
7,872✔
1161
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
340,653✔
1162
      const object_t *object = key;
332,781✔
1163
      object_t *copy = value;
332,781✔
1164
      ncopied++;
332,781✔
1165

1166
      copy->loc = object->loc;
332,781✔
1167

1168
      const object_class_t *class = classes[object->tag];
332,781✔
1169

1170
      const imask_t has = class->has_map[object->kind];
332,781✔
1171
      const int nitems = __builtin_popcountll(has);
332,781✔
1172
      imask_t mask = 1;
332,781✔
1173
      for (int n = 0; n < nitems; mask <<= 1) {
13,443,352✔
1174
         if (has & mask) {
13,110,571✔
1175
            const item_t *from = &(object->items[n]);
1,487,932✔
1176
            item_t *to = &(copy->items[n]);
1,487,932✔
1177

1178
            if (ITEM_IDENT & mask)
1,487,932✔
1179
               to->ident = from->ident;
255,593✔
1180
            else if (ITEM_OBJECT & mask) {
1,232,339✔
1181
               to->object = object_copy_map(from->object, ctx);
627,025✔
1182
               object_write_barrier(copy, to->object);
627,025✔
1183
            }
1184
            else if (ITEM_DOUBLE & mask)
605,314✔
1185
               to->dval = from->dval;
115✔
1186
            else if (ITEM_OBJ_ARRAY & mask) {
605,199✔
1187
               if (from->obj_array != NULL) {
211,625✔
1188
                  // TODO: make a resize macro
1189
                  to->obj_array = xmalloc_flex(sizeof(obj_array_t),
306,946✔
1190
                                               from->obj_array->count,
153,473✔
1191
                                               sizeof(object_t *));
1192
                  to->obj_array->count =
153,473✔
1193
                     to->obj_array->limit = from->obj_array->count;
153,473✔
1194
                  for (size_t i = 0; i < from->obj_array->count; i++) {
475,374✔
1195
                     object_t *o =
321,901✔
1196
                        object_copy_map(from->obj_array->items[i], ctx);
321,901✔
1197
                     to->obj_array->items[i] = o;
321,901✔
1198
                     object_write_barrier(copy, o);
321,901✔
1199
                  }
1200
               }
1201
            }
1202
            else if ((ITEM_INT64 & mask) || (ITEM_INT32 & mask))
393,574✔
1203
               to->ival = from->ival;
393,574✔
1204
            else
1205
               should_not_reach_here();
1206
            n++;
1,487,932✔
1207
         }
1208
      }
1209
   }
1210

1211
   for (hash_iter_t it = HASH_BEGIN;
7,872✔
1212
        hash_iter(ctx->copy_map, &it, &key, &value); ) {
340,653✔
1213
      object_t *copy = value;
332,781✔
1214
      if (ctx->callback[copy->tag] != NULL)
332,781✔
1215
         (*ctx->callback[copy->tag])(copy, ctx->callback_context);
332,781✔
1216
   }
1217

1218
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
7,872✔
UNCOV
1219
      debugf("copied %d objects into arena %s", ncopied,
×
1220
             istr(object_arena_name(global_arena)));
1221

1222
   for (unsigned i = 0; i < ctx->nroots; i++) {
22,303✔
1223
      object_t *copy = hash_get(ctx->copy_map, ctx->roots[i]);
14,431✔
1224
      if (copy != NULL)
14,431✔
1225
         ctx->roots[i] = copy;
6,388✔
1226
   }
1227

1228
   hash_free(ctx->copy_map);
7,872✔
1229
}
7,872✔
1230

1231
size_t object_arena_default_size(void)
15,930✔
1232
{
1233
   return ALIGN_UP(opt_get_size(OPT_ARENA_SIZE), OBJECT_PAGE_SZ);
15,930✔
1234
}
1235

1236
object_arena_t *object_arena_new(size_t size, unsigned std)
26,633✔
1237
{
1238
   if (all_arenas.count == 0)
26,633✔
1239
      APUSH(all_arenas, NULL);   // Dummy null arena
4,754✔
1240

1241
   object_arena_t *arena = xcalloc(sizeof(object_arena_t));
26,633✔
1242
   arena->base   = nvc_memalign(OBJECT_PAGE_SZ, size);
26,633✔
1243
   arena->alloc  = arena->base;
26,633✔
1244
   arena->limit  = (char *)arena->base + size;
26,633✔
1245
   arena->key    = all_arenas.count;
26,633✔
1246
   arena->source = OBJ_FRESH;
26,633✔
1247
   arena->std    = std;
26,633✔
1248

1249
   APUSH(all_arenas, arena);
26,633✔
1250

1251
   if (all_arenas.count == UINT16_MAX - 1)
26,633✔
1252
      fatal_trace("too many object arenas");
1253

1254
   return arena;
26,633✔
1255
}
1256

1257
void object_arena_freeze(object_arena_t *arena)
26,049✔
1258
{
1259
   ident_t name = object_arena_name(arena);
26,049✔
1260

1261
   if (arena->frozen)
26,049✔
1262
      fatal_trace("arena %s already frozen", istr(name));
1263

1264
   if (arena->source == OBJ_FRESH)
26,049✔
1265
      object_arena_gc(arena);
15,349✔
1266

1267
   if (opt_get_verbose(OPT_OBJECT_VERBOSE, NULL))
26,049✔
UNCOV
1268
      debugf("arena %s frozen (%d bytes)", istr(name),
×
UNCOV
1269
             (int)(arena->alloc - arena->base));
×
1270

1271
   chash_put(arena_lookup, name, arena);
26,049✔
1272

1273
   void *next_page = ALIGN_UP(arena->alloc, OBJECT_PAGE_SZ);
26,049✔
1274
   nvc_memprotect(arena->base, next_page - arena->base, MEM_RO);
26,049✔
1275

1276
   if (next_page < arena->limit) {
26,049✔
1277
#if OBJECT_UNMAP_UNUSED
1278
      nvc_munmap(next_page, arena->limit - next_page);
1279
      arena->limit = next_page;
1280
#else
1281
      // This can be useful for debugging use-after-free
1282
      nvc_decommit(next_page, arena->limit - next_page);
15,349✔
1283
      nvc_memprotect(next_page, arena->limit - next_page, MEM_NONE);
15,349✔
1284
#endif
1285
   }
1286

1287
   arena->frozen = true;
26,049✔
1288
}
26,049✔
1289

1290
void object_arena_walk_deps(object_arena_t *arena, object_arena_deps_fn_t fn,
30,796✔
1291
                            void *context)
1292
{
1293
   for (unsigned i = 0; i < arena->deps.count; i++)
109,699✔
1294
      (*fn)(object_arena_name(arena->deps.items[i]), context);
78,903✔
1295
}
30,796✔
1296

1297
void object_locus(object_t *object, ident_t *module, ptrdiff_t *offset)
104,054✔
1298
{
1299
   object_arena_t *arena = __object_arena(object);
104,054✔
1300
   assert(arena->frozen);
104,054✔
1301

1302
   *module = object_arena_name(arena);
104,054✔
1303

1304
   const ptrdiff_t index = ((void *)object - arena->base) >> OBJECT_ALIGN_BITS;
104,054✔
1305
   if (arena->forward != NULL)
104,054✔
1306
      *offset = arena->forward[index] >> OBJECT_ALIGN_BITS;
47,828✔
1307
   else
1308
      *offset = index;
56,226✔
1309
}
104,054✔
1310

1311
static object_arena_t *arena_by_name(ident_t module)
41,807✔
1312
{
1313
   if (global_arena != NULL && object_arena_name(global_arena) == module)
41,807✔
UNCOV
1314
      return global_arena;
×
1315

1316
   object_arena_t *a = chash_get(arena_lookup, module);
41,807✔
1317
   if (a != NULL)
41,807✔
1318
      return a;
1319

1320
#if defined DEBUG && !defined __SANITIZE_THREAD__
1321
   for (int i = 1; i < all_arenas.count; i++)
8,785✔
1322
      assert(module != object_arena_name(all_arenas.items[i]));
7,812✔
1323
#endif
1324

1325
   return NULL;
1326
}
1327

1328
object_t *object_from_locus(ident_t module, ptrdiff_t offset,
41,807✔
1329
                            object_load_fn_t loader)
1330
{
1331
   object_arena_t *arena = arena_by_name(module);
41,807✔
1332

1333
   if (arena == NULL) {
41,807✔
1334
      object_t *droot = NULL;
973✔
1335
      if (loader) droot = (*loader)(module);
973✔
1336

1337
      if (droot == NULL)
973✔
UNCOV
1338
         fatal("cannot find object locus %s%+"PRIiPTR, istr(module), offset);
×
1339

1340
      arena = __object_arena(droot);
973✔
1341
   }
1342

1343
   assert(arena->frozen);
41,807✔
1344

1345
   void *ptr = NULL;
41,807✔
1346
   if (arena->forward != NULL) {
41,807✔
1347
      // TODO: could do binary search here
1348
      for (int i = 0; i < (arena->alloc - arena->base) / OBJECT_ALIGN; i++) {
51,508,665✔
1349
         if (arena->forward[i] == offset << OBJECT_ALIGN_BITS) {
51,508,665✔
1350
            ptr = arena->base + (i << OBJECT_ALIGN_BITS);
30,420✔
1351
            break;
30,420✔
1352
         }
1353
      }
1354
      assert(ptr != NULL);
30,420✔
1355
   }
1356
   else
1357
      ptr = arena->base + (offset << OBJECT_ALIGN_BITS);
11,387✔
1358

1359
   if (ptr > arena->limit)
41,807✔
1360
      fatal_trace("invalid object locus %s%+"PRIiPTR, istr(module), offset);
1361

1362
   object_t *obj = ptr;
41,807✔
1363
   if (obj->tag >= OBJECT_TAG_COUNT)
41,807✔
1364
      fatal_trace("invalid tag %d for object locus %s%+"PRIiPTR, obj->tag,
1365
                  istr(module), offset);
1366
   else if (obj->arena != arena->key)
41,807✔
1367
      fatal_trace("invalid arena key %d != %d for object locus %s%+"PRIiPTR,
UNCOV
1368
                  obj->arena, arena->key, istr(module), offset);
×
1369

1370
   return obj;
41,807✔
1371
}
1372

1373
void freeze_global_arena(void)
23,457✔
1374
{
1375
   object_one_time_init();
23,457✔
1376

1377
   if (global_arena != NULL) {
23,457✔
1378
      object_arena_freeze(global_arena);
15,349✔
1379
      global_arena = NULL;
15,349✔
1380
   }
1381
}
23,457✔
1382

1383
void make_new_arena(void)
15,930✔
1384
{
1385
   freeze_global_arena();
15,930✔
1386
   global_arena = object_arena_new(object_arena_default_size(), standard());
15,930✔
1387
}
15,930✔
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