cpp

Coverage Report

Created: 2022-11-10 11:34

/home/andy/git/oilshell/oil/mycpp/gc_obj.h
Line
Count
Source
1
#ifndef GC_OBJ_H
2
#define GC_OBJ_H
3
4
// Obj::heap_tag_ values.  They're odd numbers to distinguish them from vtable
5
// pointers.
6
//
7
enum Tag {
8
  Forwarded = 1,  // For the Cheney algorithm.
9
  Global = 3,     // Neither copy nor scan.
10
  Opaque = 5,     // Copy but don't scan.  List<int> and Str
11
  FixedSize = 7,  // Fixed size headers: consult field_mask_
12
  Scanned = 9,    // Copy AND scan for non-NULL pointers.
13
};
14
15
const int kZeroMask = 0;  // for types with no pointers
16
// no obj_len_ computed for global List/Slab/Dict
17
const int kNoObjLen = 0x0badbeef;
18
19
// Why do we need this macro instead of using inheritance?
20
// - Because ASDL uses multiple inheritance for first class variants, but we
21
//   don't want multiple IMPLEMENTATION inheritance.  Instead we just generate
22
//   compatible layouts.
23
// - Similarly, GlobalStr is layout-compatible with Str.  It can't inherit from
24
//   Obj like Str, because of the constexpr issue with char[N].
25
26
// heap_tag_: one of Tag::
27
// type_tag_: ASDL tag (variant)
28
// field_mask_: for fixed length records, so max 16 fields
29
// obj_len_: number of bytes to copy
30
//   TODO: with a limitation of ~15 fields, we can encode obj_len_ in
31
//   field_mask_, and save space on many ASDL types.
32
//   And we can sort integers BEFORE pointers.
33
34
// TODO: ./configure could detect big or little endian, and then flip the
35
// fields in OBJ_HEADER?
36
//
37
// https://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
38
//
39
// Because we want to do (obj->heap_tag_ & 1 == 0) to distinguish it from
40
// vtable pointer.  We assume low bits of a pointer are 0 but not high bits.
41
42
#define OBJ_HEADER()    \
43
  uint8_t heap_tag_;    \
44
  uint8_t type_tag_;    \
45
  uint16_t field_mask_; \
46
  uint32_t obj_len_;
47
48
class Obj {
49
  // The unit of garbage collection.  It has a header describing how to find
50
  // the pointers within it.
51
  //
52
  // Note: Sorting ASDL fields by (non-pointer, pointer) is a good idea, but it
53
  // breaks down because mycpp has inheritance.  Could do this later.
54
55
 public:
56
  // Note: ASDL types are layout-compatible with Obj, but don't actually
57
  // inherit from it because of the 'multiple inheritance of implementation'
58
  // issue.  So they don't call this constructor.
59
  constexpr Obj(uint8_t heap_tag, uint16_t field_mask, int obj_len)
60
      : heap_tag_(heap_tag),
61
        type_tag_(0),
62
        field_mask_(field_mask),
63
4.32k
        obj_len_(obj_len) {
64
4.32k
  }
65
66
2.89k
  void SetObjLen(int obj_len) {
67
2.89k
    obj_len_ = obj_len;
68
2.89k
  }
69
70
  OBJ_HEADER()
71
72
  DISALLOW_COPY_AND_ASSIGN(Obj)
73
};
74
75
//
76
// Compile-time computation of GC field masks.
77
//
78
79
class _DummyObj {  // For maskbit()
80
 public:
81
  OBJ_HEADER()
82
  int first_field_;
83
};
84
85
// maskbit(field_offset) returns a bit in mask that you can bitwise-or (|) with
86
// other bits.
87
//
88
// - Note that we only call maskbit() on offsets of pointer fields, which must
89
//   be POINTER-ALIGNED.
90
// - _DummyObj is used in case OBJ_HEADER() requires padding, then
91
//   sizeof(Obj) != offsetof(_DummyObj, first_field_)
92
93
984
constexpr int maskbit(int offset) {
94
984
  return 1 << ((offset - offsetof(_DummyObj, first_field_)) / sizeof(void*));
95
984
}
96
97
class _DummyObj_v {  // For maskbit_v()
98
 public:
99
  void* vtable;  // how the compiler does dynamic dispatch
100
  OBJ_HEADER()
101
  int first_field_;
102
};
103
104
// maskbit_v(field_offset) is like maskbit(), but accounts for the vtable
105
// pointer.
106
107
84
constexpr int maskbit_v(int offset) {
108
84
  return 1 << ((offset - offsetof(_DummyObj_v, first_field_)) / sizeof(void*));
109
84
}
110
111
187
inline Obj* ObjHeader(Obj* obj) {
112
  // If we see a vtable pointer, return the Obj* header immediately following.
113
  // Otherwise just return Obj itself.
114
187
  return (obj->heap_tag_ & 0x1) == 0
115
187
             ? reinterpret_cast<Obj*>(reinterpret_cast<char*>(obj) +
116
9
                                      sizeof(void*))
117
187
             : obj;
118
187
}
119
120
#endif