cpp

Coverage Report

Created: 2022-08-03 12:23

/home/andy/git/oilshell/oil/mycpp/gc_heap.h
Line
Count
Source (jump to first uncovered line)
1
// mycpp/gc_heap.h
2
//
3
// A garbage collected heap that looks like statically typed Python: Str,
4
// List<T>, Dict<K, V>.
5
6
#ifndef GC_HEAP_H
7
#define GC_HEAP_H
8
9
#include <cassert>  // assert()
10
#include <cstddef>  // max_align_t
11
#include <cstdint>  // max_align_t
12
#include <cstdlib>  // malloc
13
#include <cstring>  // memcpy
14
#include <initializer_list>
15
#include <new>      // placement new
16
#include <utility>  // std::forward
17
18
#include "mycpp/common.h"
19
20
// Design Notes:
21
22
// It's a semi-space collector using the Cheney algorithm.  (Later we may add a
23
// "large object space", managed by mark-and-sweep after each copy step.)
24
25
// Influences / Prior art:
26
//
27
// - OCaml / ZINC machine - statically typed, everything is a pointer (no value
28
//   types).
29
// - femtolisp - Cheney collector; used to bootstrap Juliia.
30
// - Other: ES shell, Lua, Python and especially Python dicts.
31
32
// Design:
33
//
34
// - Graph of application types:
35
//   - Str*
36
//   - List<T>*
37
//   - Dict<K, V>*
38
//   - User-defined classes, which may have a vtable pointer.
39
// - Graph of GC nodes: everything is an Obj* with an 8 byte header
40
//   - 1 byte heap tag, for Cheney
41
//   - 1 byte type tag for Zephyr ASDL unions
42
//   - 2 byte / 16-bit field bit mask for following pointers on user-defined
43
//     classes and List / Dict "headers"
44
//   - 4 bytes length for Cheney to determine how much to copy.
45
//
46
// Operations that resize:
47
//
48
// - List::append() and extend() can realloc
49
// - Dict::set() can realloc and rehash (TODO)
50
//
51
// Important Types:
52
//
53
// - Slab<T> to trace the object graph.  Slab<int> is opaque, but Slab<T*>
54
//   requires tracing.
55
//   - A List<T> is a fixed-size structure, with int fields and a pointer
56
//     to a single Slab<T> (the items).
57
//   - A Dict<K, V> is also fixed-size, with pointers to 3 slabs: the index
58
//     Slab<int>, the keys Slab<K>, and the index Slab<V>.
59
//
60
// "Ghost" layout types:
61
//
62
// - LayoutFixed - for walking up to 16 fields of a user-defined type.
63
// - LayoutForwarded - for storing the forwarding pointer that the Cheney
64
//   algorithm uses.
65
//
66
// Stack rooting API:
67
//
68
//   StackRoots _roots({&mystr, &mydict, &mylist});
69
//
70
// This pushes local variables onto the global data structure managed by the
71
// GC.
72
73
// TODO: Dicts should actually use hashing!  Test computational complexity.
74
75
// Memory allocation APIs:
76
//
77
// - gc_heap::Alloc<Foo>(x)
78
//   The typed public API.  An alternative to new Foo(x).  mycpp/ASDL should
79
//   generate these calls.
80
// - AllocStr(length), StrFromC(), NewList, NewDict: gc_heap::Alloc() doesn't
81
// work
82
//   for these types for various reasons
83
// - Heap::Allocate()
84
//   The untyped internal API.  For AllocStr() and NewSlab().
85
// - malloc() -- for say yajl to use.  Manually deallocated.
86
// - new/delete -- shouldn't be in Oil?
87
88
// Slab Sizing with 8-byte slab header
89
//
90
//   16 - 8 =  8 = 1 eight-byte or  2 four-byte elements
91
//   32 - 8 = 24 = 3 eight-byte or  6 four-byte elements
92
//   64 - 8 = 56 = 7 eight-byte or 14 four-byte elements
93
94
// #defines for degbugging:
95
//
96
// GC_EVERY_ALLOC: Collect() on every Allocate().  Exposes many bugs!
97
// GC_PROTECT: Use mprotect()
98
// GC_VERBOSE: Log when we collect
99
// GC_STATS: Collect more stats.  TODO: Rename this?
100
101
// Obj::heap_tag_ values.  They're odd numbers to distinguish them from vtable
102
// pointers.
103
namespace Tag {
104
const int Forwarded = 1;  // For the Cheney algorithm.
105
const int Global = 3;     // Neither copy nor scan.
106
const int Opaque = 5;     // Copy but don't scan.  List<int> and Str
107
const int FixedSize = 7;  // Fixed size headers: consult field_mask_
108
const int Scanned = 9;    // Copy AND scan for non-NULL pointers.
109
}  // namespace Tag
110
111
// Silly definition for passing types like GlobalList<T, N> and initializer
112
// lists like {1, 2, 3} to macros
113
114
#define COMMA ,
115
116
namespace gc_heap {
117
118
template <class T>
119
class List;
120
121
constexpr int kMask = alignof(max_align_t) - 1;  // e.g. 15 or 7
122
123
// Align returned pointers to the worst case of 8 bytes (64-bit pointers)
124
4.60k
inline size_t aligned(size_t n) {
125
  // https://stackoverflow.com/questions/2022179/c-quick-calculation-of-next-multiple-of-4
126
  // return (n + 7) & ~7;
127
128
4.60k
  return (n + kMask) & ~kMask;
129
4.60k
}
130
131
class Obj;
132
133
const int kMaxRoots = 4 * 1024;  // related to C stack size
134
135
class Space {
136
 public:
137
63
  Space() {
138
63
  }
139
  void Init(int space_size);
140
141
  void Free();
142
143
465
  void Clear() {
144
    // Slab scanning relies on 0 bytes (nullptr).  e.g. for a List<Token*>*.
145
    // Note: I noticed that memset() of say 400 MiB is pretty expensive.  Does
146
    // it makes sense to zero the slabs instead?
147
465
#ifndef NO_GC_HACK
148
    // When not collecting, we need a huge 400 MiB heap.  Try to save startup
149
    // time by not doing this.
150
465
    memset(begin_, 0, size_);
151
465
#endif
152
465
  }
153
154
#if GC_PROTECT
155
  // To maintain invariants
156
  void Protect();
157
  void Unprotect();
158
#endif
159
#if GC_STATS
160
  void AssertValid(void* p) {
161
    if (begin_ <= p && p < begin_ + size_) {
162
      return;
163
    }
164
    log("p = %p isn't between %p and %p", begin_, begin_ + size_);
165
    InvalidCodePath();
166
  }
167
#endif
168
169
  char* begin_;
170
  int size_;  // number of bytes
171
};
172
173
class Heap {
174
 public:
175
31
  Heap() {  // default constructor does nothing -- relies on zero initialization
176
31
  }
177
178
  // Real initialization with the initial heap size.  The heap grows with
179
  // allocations.
180
40
  void Init(int space_size) {
181
    // malloc() and memset()
182
40
    from_space_.Init(space_size);
183
40
    to_space_.Init(space_size);
184
185
40
    free_ = from_space_.begin_;  // where we allocate from
186
40
    limit_ = free_ + space_size;
187
188
40
    roots_top_ = 0;
189
190
#if GC_STATS
191
    num_collections_ = 0;
192
    num_heap_growths_ = 0;
193
    num_forced_growths_ = 0;
194
    num_live_objs_ = 0;
195
#endif
196
40
  }
197
198
3.24k
  void* Bump(int n) {
199
3.24k
    char* p = free_;
200
3.24k
    free_ += n;
201
#if GC_STATS
202
    num_live_objs_++;
203
#endif
204
3.24k
    return p;
205
3.24k
  }
206
207
3.24k
  void* Allocate(int num_bytes) {
208
3.24k
    int n = aligned(num_bytes);
209
    // log("n = %d, p = %p", n, p);
210
211
#if GC_EVERY_ALLOC
212
    Collect();  // force collection to find problems early
213
#endif
214
215
3.24k
    if (free_ + n <= limit_) {  // Common case: we have space for it.
216
2.90k
      return Bump(n);
217
2.90k
    }
218
219
#if GC_STATS
220
      // log("GC free_ %p,  from_space_ %p, space_size_ %d", free_, from_space_,
221
      //    space_size_);
222
#endif
223
224
344
    Collect();  // Try to free some space.
225
226
    // log("after GC: from begin %p, free_ %p,  n %d, limit_ %p",
227
    //    from_space_.begin_, free_, n, limit_);
228
229
344
    if (free_ + n <= limit_) {  // Now we have space for it.
230
333
      return Bump(n);
231
333
    }
232
233
    // It's STILL too small.  Resize to_space_ to ENSURE that allocation will
234
    // succeed, copy the heap to it, then allocate the object.
235
11
    int multiple = 2;
236
11
    while (from_space_.size_ + n > to_space_.size_ * multiple) {
237
0
      multiple *= 2;
238
0
    }
239
    // log("=== FORCED by multiple of %d", multiple);
240
11
    to_space_.Free();
241
11
    to_space_.Init(to_space_.size_ * multiple);
242
243
11
    Collect();
244
#if GC_STATS
245
    num_forced_growths_++;
246
#endif
247
248
11
    return Bump(n);
249
344
  }
250
251
364
  void Swap() {
252
    // Swap spaces for next collection.
253
364
    char* tmp = from_space_.begin_;
254
364
    from_space_.begin_ = to_space_.begin_;
255
364
    to_space_.begin_ = tmp;
256
257
364
    int tmp2 = from_space_.size_;
258
364
    from_space_.size_ = to_space_.size_;
259
364
    to_space_.size_ = tmp2;
260
364
  }
261
262
11.7k
  void PushRoot(Obj** p) {
263
    // log("PushRoot %d", roots_top_);
264
11.7k
    roots_[roots_top_++] = p;
265
    // TODO: This should be like a malloc() failure?
266
11.7k
    assert(roots_top_ < kMaxRoots);
267
11.7k
  }
268
269
11.7k
  void PopRoot() {
270
11.7k
    roots_top_--;
271
    // log("PopRoot %d", roots_top_);
272
11.7k
  }
273
274
  Obj* Relocate(Obj* obj, Obj* header);
275
276
  // mutates free_ and other variables
277
  void Collect();
278
279
#if GC_STATS
280
  void Report() {
281
    log("-----");
282
    log("num collections = %d", num_collections_);
283
    log("num heap growths = %d", num_heap_growths_);
284
    log("num forced heap growths = %d", num_forced_growths_);
285
    log("num live objects = %d", num_live_objs_);
286
287
    log("from_space_ %p", from_space_.begin_);
288
    log("to_space %p", to_space_.begin_);
289
    log("-----");
290
  }
291
#endif
292
293
  Space from_space_;  // space we allocate from
294
  Space to_space_;    // space that the collector copies to
295
296
  char* free_;   // next place to allocate, from_space_ <= free_ < limit_
297
  char* limit_;  // end of space we're allocating from
298
299
  // Stack roots.  The obvious data structure is a linked list, but an array
300
  // has better locality.
301
  //
302
  // femtolisp uses a global pointer to dynamically-allocated growable array,
303
  // with initial N_STACK = 262144!  Kind of arbitrary.
304
305
  int roots_top_;
306
  Obj** roots_[kMaxRoots];  // These are pointers to Obj* pointers
307
308
#if GC_STATS
309
  int num_collections_;
310
  int num_heap_growths_;
311
  int num_forced_growths_;  // when a single allocation is too big
312
  int num_live_objs_;
313
#endif
314
};
315
316
// The heap is a (compound) global variable.  Notes:
317
// - The default constructor does nothing, to avoid initialization order
318
//   problems.
319
// - For some applications, this can be thread_local rather than global.
320
extern Heap gHeap;
321
322
class StackRoots {
323
 public:
324
8.32k
  StackRoots(std::initializer_list<void*> roots) {
325
8.32k
    n_ = roots.size();
326
11.4k
    for (auto root : roots) {  // can't use roots[i]
327
11.4k
      gHeap.PushRoot(reinterpret_cast<Obj**>(root));
328
11.4k
    }
329
8.32k
  }
330
331
8.32k
  ~StackRoots() {
332
    // TODO: optimize this
333
19.8k
    for (int i = 0; i < n_; ++i) {
334
11.4k
      gHeap.PopRoot();
335
11.4k
    }
336
8.32k
  }
337
338
 private:
339
  int n_;
340
};
341
342
#if GC_STATS
343
void ShowFixedChildren(Obj* obj);
344
#endif
345
346
template <typename T>
347
class Local {
348
  // We can garbage collect at any Alloc() invocation, so we need a level of
349
  // indirection for locals / pointers directly on the stack.  Pointers on the
350
  // heap are updated by the Cheney GC algorithm.
351
352
 public:
353
  Local() : raw_pointer_(nullptr) {
354
  }
355
356
  // IMPLICIT conversion.  No 'explicit'.
357
0
  Local(T* raw_pointer) : raw_pointer_(raw_pointer) {
358
    // TODO(Jesse): Does this get called?
359
    // Is this NotImplemented() or InvalidCodePath() ??
360
0
    assert(0);
361
    // gHeap.PushRoot(this);
362
0
  }
363
364
  // Copy constructor, e.g. f(mylocal) where f(Local<T> param);
365
  Local(const Local& other) : raw_pointer_(other.raw_pointer_) {
366
    // TODO(Jesse): Does this get called?
367
    // Is this NotImplemented() or InvalidCodePath() ??
368
    assert(0);
369
    // gHeap.PushRoot(this);
370
  }
371
372
  void operator=(const Local& other) {  // Assignment operator
373
    raw_pointer_ = other.raw_pointer_;
374
375
    // Note: we could try to avoid PushRoot() as an optimization.  Example:
376
    //
377
    // Local<Str> a = StrFromC("foo");
378
    // Local<Str> b;
379
    // b = a;  // invokes operator=, it's already a root
380
    //
381
    // Local<Str> c = myfunc();  // T* constructor takes care of PushRoot()
382
383
    // log("operator=");
384
385
    // However the problem is that then we'll have an unbalanced PopRoot().
386
    // So we keep it for now.
387
    gHeap.PushRoot(this);
388
  }
389
390
0
  ~Local() {
391
0
    gHeap.PopRoot();
392
0
  }
393
394
  // This cast operator overload allows:
395
  //
396
  // Local<Str> s = StrFromC("foo");
397
  // node->mystr = s;  // convert from Local to raw
398
  //
399
  // As well as:
400
  //
401
  // Local<List<Str*>> strings = Alloc<List<Str*>>();
402
  // strings->append(StrFromC("foo"));  // convert from local to raw
403
  //
404
  // The heap should NOT have locals!  List<Str> and not List<Local<Str>>.
405
  //
406
  // Note: This could be considered dangerous if we don't maintain
407
  // discipline.
408
  //
409
  // https://www.informit.com/articles/article.aspx?p=31529&seqNum=7
410
  //
411
  // Putting .get() at the call site in mycpp is more explicit. The
412
  // readability of the generated code is important!
413
#if 1
414
0
  operator T*() {
415
0
    return raw_pointer_;
416
0
  }
417
#endif
418
419
  // Allows ref->field and ref->method()
420
0
  T* operator->() const {
421
    // log("operator->");
422
0
    return raw_pointer_;
423
0
  }
Unexecuted instantiation: _ZNK7gc_heap5LocalI4BaseEptEv
Unexecuted instantiation: _ZNK7gc_heap5LocalI7DerivedEptEv
424
  T* Get() const {
425
    return raw_pointer_;
426
  }
427
  // called by the garbage collector when moved to a new location!
428
  void Update(T* moved) {
429
    raw_pointer_ = moved;
430
  }
431
432
  // Dereference to get the real value.  Doesn't seem like we need this.
433
#if 0
434
  T operator*() const {
435
    //log("operator*");
436
    return *raw_pointer_;
437
  }
438
#endif
439
440
  T* raw_pointer_;
441
};
442
443
template <typename T>
444
class Param : public Local<T> {
445
  // This could be an optimization like SpiderMonkey's Handle<T> vs Rooted<T>.
446
  // We use the names Param<T> and Local<T>.
447
448
 public:
449
  // hm this is awkward, I think we should NOT inherit!  We should only
450
  // convert.
451
  Param(const Local<T>& other) : Local<T>(nullptr) {
452
    this->raw_pointer_ = other.raw_pointer_;
453
  }
454
455
  ~Param() {  // do not PopRoot()
456
  }
457
458
  // Construct from T* -- PushRoot()
459
  // Construct from Local<T> -- we don't need to PushRoot()
460
  // operator= -- I don't think we need to PushRoot()
461
};
462
463
// Variadic templates:
464
// https://eli.thegreenplace.net/2014/variadic-templates-in-c/
465
template <typename T, typename... Args>
466
736
T* Alloc(Args&&... args) {
467
736
  void* place = gHeap.Allocate(sizeof(T));
468
736
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
736
}
_ZN7gc_heap5AllocINS_4ListIPNS_3StrEEEJEEEPT_DpOT0_
Line
Count
Source
466
75
T* Alloc(Args&&... args) {
467
75
  void* place = gHeap.Allocate(sizeof(T));
468
75
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
75
}
_ZN7gc_heap5AllocINS_4ListIiEEJEEEPT_DpOT0_
Line
Count
Source
466
322
T* Alloc(Args&&... args) {
467
322
  void* place = gHeap.Allocate(sizeof(T));
468
322
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
322
}
_ZN7gc_heap5AllocINS_4ListIbEEJEEEPT_DpOT0_
Line
Count
Source
466
2
T* Alloc(Args&&... args) {
467
2
  void* place = gHeap.Allocate(sizeof(T));
468
2
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
2
}
_ZN7gc_heap5AllocINS_4ListIdEEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocINS_4DictIiPNS_3StrEEEJEEEPT_DpOT0_
Line
Count
Source
466
2
T* Alloc(Args&&... args) {
467
2
  void* place = gHeap.Allocate(sizeof(T));
468
2
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
2
}
_ZN7gc_heap5AllocINS_4DictIPNS_3StrEiEEJEEEPT_DpOT0_
Line
Count
Source
466
8
T* Alloc(Args&&... args) {
467
8
  void* place = gHeap.Allocate(sizeof(T));
468
8
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
8
}
_ZN7gc_heap5AllocINS_4DictIPNS_3StrES3_EEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocINS_4DictIiiEEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocI5PointJiiEEEPT_DpOT0_
Line
Count
Source
466
2
T* Alloc(Args&&... args) {
467
2
  void* place = gHeap.Allocate(sizeof(T));
468
2
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
2
}
_ZN7gc_heap5AllocI4LineJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocI10DerivedObjJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN5mylib13BufLineReaderEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN5mylib15CFileLineReaderEJRP8_IO_FILEEEEPT_DpOT0_
Line
Count
Source
466
3
T* Alloc(Args&&... args) {
467
3
  void* place = gHeap.Allocate(sizeof(T));
468
3
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
3
}
Unexecuted instantiation: _ZN7gc_heap5AllocI4BaseJiEEEPT_DpOT0_
Unexecuted instantiation: _ZN7gc_heap5AllocI7DerivedJiiEEEPT_DpOT0_
_ZN7gc_heap5AllocIN14asdl_generated17arith_expr__ConstEJiEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
Unexecuted instantiation: _ZN7gc_heap5AllocIN14asdl_generated17arith_expr__ConstEJRiEEEPT_DpOT0_
_ZN7gc_heap5AllocIN7classes10TextOutputEJRPN5mylib6WriterEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN7classes7DerivedEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN5mylib9BufWriterEJEEEPT_DpOT0_
Line
Count
Source
466
33
T* Alloc(Args&&... args) {
467
33
  void* place = gHeap.Allocate(sizeof(T));
468
33
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
33
}
Unexecuted instantiation: _ZN7gc_heap5AllocIN7classes10TextOutputEJRPN5mylib9BufWriterEEEEPT_DpOT0_
_ZN7gc_heap5AllocI6Tuple2IiPNS_3StrEEJiRS3_EEEPT_DpOT0_
Line
Count
Source
466
5
T* Alloc(Args&&... args) {
467
5
  void* place = gHeap.Allocate(sizeof(T));
468
5
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
5
}
_ZN7gc_heap5AllocIN10containers5PointEJiiEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN12control_flow10ParseErrorEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocI6Tuple2IPNS_3StrEiEJRS3_iEEEPT_DpOT0_
Line
Count
Source
466
3
T* Alloc(Args&&... args) {
467
3
  void* place = gHeap.Allocate(sizeof(T));
468
3
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
3
}
_ZN7gc_heap5AllocINS_4ListIP6Tuple2IPNS_3StrEiEEEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocINS_4ListIP6Tuple2IiPNS_3StrEEEEJEEEPT_DpOT0_
Line
Count
Source
466
2
T* Alloc(Args&&... args) {
467
2
  void* place = gHeap.Allocate(sizeof(T));
468
2
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
2
}
Unexecuted instantiation: _ZN7gc_heap5AllocI19NotImplementedErrorJEEEPT_DpOT0_
_ZN7gc_heap5AllocIN7modules3DogEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN7module13CatEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN7modules6SphinxEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocINS_4ListIPN10hnode_asdl5fieldEEEJEEEPT_DpOT0_
Line
Count
Source
466
34
T* Alloc(Args&&... args) {
467
34
  void* place = gHeap.Allocate(sizeof(T));
468
34
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
34
}
_ZN7gc_heap5AllocINS_4ListIPN10hnode_asdl7hnode_tEEEJEEEPT_DpOT0_
Line
Count
Source
466
34
T* Alloc(Args&&... args) {
467
34
  void* place = gHeap.Allocate(sizeof(T));
468
34
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
34
}
_ZN7gc_heap5AllocIN10hnode_asdl13hnode__RecordEJRPNS_3StrEPNS_4ListIPNS1_5fieldEEEbS5_S5_PNS6_IPNS1_7hnode_tEEEEEEPT_DpOT0_
Line
Count
Source
466
34
T* Alloc(Args&&... args) {
467
34
  void* place = gHeap.Allocate(sizeof(T));
468
34
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
34
}
Unexecuted instantiation: _ZN7gc_heap5AllocIN10hnode_asdl11hnode__LeafEJRPNS_3StrENS1_7color_eEEEEPT_DpOT0_
_ZN7gc_heap5AllocIN10hnode_asdl11hnode__LeafEJRPNS_3StrERNS1_7color_eEEEEPT_DpOT0_
Line
Count
Source
466
20
T* Alloc(Args&&... args) {
467
20
  void* place = gHeap.Allocate(sizeof(T));
468
20
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
20
}
Unexecuted instantiation: _ZN7gc_heap5AllocIN6format10AnsiOutputEJRPN5mylib6WriterEEEEPT_DpOT0_
Unexecuted instantiation: _ZN7gc_heap5AllocIN6format10TextOutputEJRPN5mylib6WriterEEEEPT_DpOT0_
Unexecuted instantiation: _ZN7gc_heap5AllocIN6format10TextOutputEJPN5mylib9BufWriterEEEEPT_DpOT0_
Unexecuted instantiation: _ZN7gc_heap5AllocIN6format10HtmlOutputEJPN5mylib9BufWriterEEEEPT_DpOT0_
_ZN7gc_heap5AllocIN6format10AnsiOutputEJPN5mylib9BufWriterEEEEPT_DpOT0_
Line
Count
Source
466
31
T* Alloc(Args&&... args) {
467
31
  void* place = gHeap.Allocate(sizeof(T));
468
31
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
31
}
_ZN7gc_heap5AllocI6Tuple2IPNS_3StrEiEJRS3_RiEEEPT_DpOT0_
Line
Count
Source
466
22
T* Alloc(Args&&... args) {
467
22
  void* place = gHeap.Allocate(sizeof(T));
468
22
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
22
}
_ZN7gc_heap5AllocIN6format14_PrettyPrinterEJiEEEPT_DpOT0_
Line
Count
Source
466
8
T* Alloc(Args&&... args) {
467
8
  void* place = gHeap.Allocate(sizeof(T));
468
8
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
8
}
_ZN7gc_heap5AllocIN5parse10ParseErrorEJPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
5
T* Alloc(Args&&... args) {
467
5
  void* place = gHeap.Allocate(sizeof(T));
468
5
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
5
}
_ZN7gc_heap5AllocIN9expr_asdl9expr__VarEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
9
T* Alloc(Args&&... args) {
467
9
  void* place = gHeap.Allocate(sizeof(T));
468
9
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
9
}
_ZN7gc_heap5AllocIN9expr_asdl11expr__ConstEJiEEEPT_DpOT0_
Line
Count
Source
466
14
T* Alloc(Args&&... args) {
467
14
  void* place = gHeap.Allocate(sizeof(T));
468
14
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
14
}
_ZN7gc_heap5AllocIN9expr_asdl12expr__BinaryEJRPNS_3StrERPNS1_6expr_tES8_EEEPT_DpOT0_
Line
Count
Source
466
14
T* Alloc(Args&&... args) {
467
14
  void* place = gHeap.Allocate(sizeof(T));
468
14
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
14
}
_ZN7gc_heap5AllocIN5parse5LexerEJRPNS_3StrEEEEPT_DpOT0_
Line
Count
Source
466
14
T* Alloc(Args&&... args) {
467
14
  void* place = gHeap.Allocate(sizeof(T));
468
14
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
14
}
_ZN7gc_heap5AllocIN5parse6ParserEJRPNS1_5LexerEEEEPT_DpOT0_
Line
Count
Source
466
13
T* Alloc(Args&&... args) {
467
13
  void* place = gHeap.Allocate(sizeof(T));
468
13
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
13
}
_ZN7gc_heap5AllocIN6format10AnsiOutputEJPN5mylib6WriterEEEEPT_DpOT0_
Line
Count
Source
466
8
T* Alloc(Args&&... args) {
467
8
  void* place = gHeap.Allocate(sizeof(T));
468
8
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
8
}
_ZN7gc_heap5AllocIN15scoped_resource7MyErrorEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN15scoped_resource8DirStackEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN7strings3FooEJEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
_ZN7gc_heap5AllocIN9test_cast11ColorOutputEJRPN5mylib9BufWriterEEEEPT_DpOT0_
Line
Count
Source
466
1
T* Alloc(Args&&... args) {
467
1
  void* place = gHeap.Allocate(sizeof(T));
468
1
  assert(place != nullptr);
469
  // placement new
470
0
  return new (place) T(std::forward<Args>(args)...);
471
1
}
472
473
// Return the size of a resizeable allocation.  For now we just round up by
474
// powers of 2. This could be optimized later.  CPython has an interesting
475
// policy in listobject.c.
476
//
477
// https://stackoverflow.com/questions/466204/rounding-up-to-next-power-of-2
478
972
inline int RoundUp(int n) {
479
  // minimum size
480
972
  if (n < 8) {
481
447
    return 8;
482
447
  }
483
484
  // TODO: what if int isn't 32 bits?
485
525
  n--;
486
525
  n |= n >> 1;
487
525
  n |= n >> 2;
488
525
  n |= n >> 4;
489
525
  n |= n >> 8;
490
525
  n |= n >> 16;
491
525
  n++;
492
525
  return n;
493
972
}
494
495
const int kZeroMask = 0;  // for types with no pointers
496
// no obj_len_ computed for global List/Slab/Dict
497
const int kNoObjLen = 0x0eadbeef;
498
499
// Why do we need this macro instead of using inheritance?
500
// - Because ASDL uses multiple inheritance for first class variants, but we
501
//   don't want multiple IMPLEMENTATION inheritance.  Instead we just generate
502
//   compatible layouts.
503
// - Similarly, GlobalStr is layout-compatible with Str.  It can't inherit from
504
//   Obj like Str, because of the constexpr issue with char[N].
505
506
// heap_tag_: one of Tag::
507
// type_tag_: ASDL tag (variant)
508
// field_mask_: for fixed length records, so max 16 fields
509
// obj_len_: number of bytes to copy
510
//   TODO: with a limitation of ~15 fields, we can encode obj_len_ in
511
//   field_mask_, and save space on many ASDL types.
512
//   And we can sort integers BEFORE pointers.
513
514
// TODO: ./configure could detect big or little endian, and then flip the
515
// fields in OBJ_HEADER?
516
//
517
// https://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
518
//
519
// Because we want to do (obj->heap_tag_ & 1 == 0) to distinguish it from
520
// vtable pointer.  We assume low bits of a pointer are 0 but not high bits.
521
522
#define OBJ_HEADER()    \
523
  uint8_t heap_tag_;    \
524
  uint8_t type_tag_;    \
525
  uint16_t field_mask_; \
526
  uint32_t obj_len_;
527
528
class Obj {
529
  // The unit of garbage collection.  It has a header describing how to find
530
  // the pointers within it.
531
  //
532
  // Note: Sorting ASDL fields by (non-pointer, pointer) is a good idea, but it
533
  // breaks down because mycpp has inheritance.  Could do this later.
534
535
 public:
536
  // Note: ASDL types are layout-compatible with Obj, but don't actually
537
  // inherit from it because of the 'multiple inheritance of implementation'
538
  // issue.  So they don't call this constructor.
539
  constexpr Obj(uint8_t heap_tag, uint16_t field_mask, int obj_len)
540
      : heap_tag_(heap_tag),
541
        type_tag_(0),
542
        field_mask_(field_mask),
543
3.89k
        obj_len_(obj_len) {
544
3.89k
  }
545
546
2.00k
  void SetObjLen(int obj_len) {
547
2.00k
    this->obj_len_ = obj_len;
548
2.00k
  }
549
550
  OBJ_HEADER()
551
552
  DISALLOW_COPY_AND_ASSIGN(Obj)
553
};
554
555
}  // namespace gc_heap
556
557
#endif  // GC_HEAP_H