cpp

Coverage Report

Created: 2023-01-21 22:37

/home/andy/git/oilshell/oil/mycpp/gc_obj.h
Line
Count
Source
1
#ifndef MYCPP_GC_OBJ_H
2
#define MYCPP_GC_OBJ_H
3
4
#include <stdint.h>  // uint8_t
5
6
namespace HeapTag {
7
const int Global = 0;     // Don't mark or sweep.
8
                          // Cheney: Don't copy or scan.
9
const int Opaque = 1;     // Mark and sweep, e.g. List<int>, Str.
10
                          // Cheney: Copy, but don't scan.
11
const int FixedSize = 2;  // Consult field_mask for children
12
const int Scanned = 3;    // Scan a contiguous range of children
13
14
const int Forwarded = 4;  // For the Cheney algorithm.
15
};                        // namespace HeapTag
16
17
// This "enum" starts from the end of the valid type_tag range.
18
// asdl/gen_cpp.py starts from 1 for variants, or 64 for shared variants.
19
namespace TypeTag {
20
const int OtherClass = 127;  // non-ASDL class
21
const int Str = 126;
22
const int Slab = 125;
23
const int Tuple = 124;
24
};  // namespace TypeTag
25
26
const int kIsHeader = 1;
27
28
const unsigned kZeroMask = 0;  // for types with no pointers
29
30
// no obj_len computed for global List/Slab/Dict
31
const int kNoObjLen = 0x0badbeef;
32
const int kNoObjId = 42;
33
34
const int kMaxObjId = (1 << 24) - 1;  // 24 bits
35
36
// The first member of every GC-managed object is 'ObjHeader header_'.
37
// (There's no inheritance!)
38
struct ObjHeader {
39
  unsigned is_header : 1;  // To distinguish from vtable pointer
40
                           // Overlaps with RawObject::points_to_header
41
  unsigned type_tag : 7;   // TypeTag, ASDL variant / shared variant
42
#if defined(MARK_SWEEP) || defined(BUMP_LEAK)
43
  unsigned obj_id : 24;  // For some user-defined classes, so max 16 fields
44
#else
45
  unsigned field_mask : 24;  // Cheney needs field_maks AND obj_len
46
#endif
47
48
#if defined(MARK_SWEEP) || defined(BUMP_LEAK)
49
  unsigned heap_tag : 2;                  // HeapTag::Opaque, etc.
50
  unsigned u_mask_npointers_strlen : 30;  // Mark-sweep: derive Str length, Slab
51
                                          // length
52
#else
53
  unsigned heap_tag : 3;     // Cheney also needs HeapTag::Forwarded
54
  unsigned obj_len : 29;     // Cheney: number of bytes to copy
55
#endif
56
};
57
58
// TODO: we could determine the max statically!
59
const int kFieldMaskBits = 16;
60
61
#if defined(MARK_SWEEP) || defined(BUMP_LEAK)
62
119
  #define FIELD_MASK(header) (header).u_mask_npointers_strlen
63
32.6k
  #define STR_LEN(header) (header).u_mask_npointers_strlen
64
10
  #define NUM_POINTERS(header) (header).u_mask_npointers_strlen
65
66
#else
67
  #define FIELD_MASK(header) (header).field_mask
68
                             // TODO: derive from obj_len
69
  #define STR_LEN(header) -1
70
  #define NUM_POINTERS(header) \
71
    ((header.obj_len - kSlabHeaderSize) / sizeof(void*))
72
#endif
73
74
// A RawObject* is like a void* -- it can point to any C++ object.  The object
75
// may start with either ObjHeader, or vtable pointer then an ObjHeader.
76
struct RawObject {
77
  unsigned points_to_header : 1;  // same as ObjHeader::is_header
78
  unsigned pad : 31;
79
};
80
81
// TODO: ./configure could detect endian-ness, and reorder the fields in
82
// ObjHeader.  See mycpp/demo/gc_header.cc.
83
84
// Used by hand-written and generated classes
85
#define GC_CLASS_FIXED(header_, field_mask, obj_len)                         \
86
  header_ {                                                                  \
87
    kIsHeader, TypeTag::OtherClass, kNoObjId, HeapTag::FixedSize, field_mask \
88
  }
89
90
// Classes with no inheritance (e.g. used by mycpp)
91
#define GC_CLASS_SCANNED(header_, num_pointers, obj_len)                     \
92
  header_ {                                                                  \
93
    kIsHeader, TypeTag::OtherClass, kNoObjId, HeapTag::Scanned, num_pointers \
94
  }
95
96
// Used by frontend/flag_gen.py.  TODO: Sort fields and use GC_CLASS_SCANNED
97
#define GC_CLASS(header_, heap_tag, field_mask, obj_len)           \
98
  header_ {                                                        \
99
    kIsHeader, TypeTag::OtherClass, kNoObjId, heap_tag, field_mask \
100
  }
101
102
// Used by ASDL.  TODO: Sort fields and use GC_CLASS_SCANNED
103
#define GC_ASDL_CLASS(header_, type_tag, field_mask, obj_len)     \
104
  header_ {                                                       \
105
    kIsHeader, type_tag, kNoObjId, HeapTag::FixedSize, field_mask \
106
  }
107
108
#define GC_STR(header_)                                            \
109
  header_ {                                                        \
110
    kIsHeader, TypeTag::Str, kZeroMask, HeapTag::Opaque, kNoObjLen \
111
  }
112
113
#define GC_SLAB(header_, heap_tag, num_pointers)                \
114
  header_ {                                                     \
115
    kIsHeader, TypeTag::Slab, kZeroMask, heap_tag, num_pointers \
116
  }
117
118
#define GC_TUPLE(header_, field_mask, obj_len)                          \
119
  header_ {                                                             \
120
    kIsHeader, TypeTag::Tuple, kNoObjId, HeapTag::FixedSize, field_mask \
121
  }
122
123
// TODO: could omit this in BUMP_LEAK mode
124
#define GC_OBJ(var_name) ObjHeader var_name
125
126
//
127
// Compile-time computation of GC field masks.
128
//
129
130
class _DummyObj {  // For maskbit()
131
 public:
132
  ObjHeader header_;
133
  int first_field_;
134
};
135
136
// maskbit(field_offset) returns a bit in mask that you can bitwise-or (|) with
137
// other bits.
138
//
139
// - Note that we only call maskbit() on offsets of pointer fields, which must
140
//   be POINTER-ALIGNED.
141
// - _DummyObj is used in case ObjHeader requires padding, then
142
//   sizeof(ObjHeader) != offsetof(_DummyObj, first_field_)
143
144
1.75k
constexpr int maskbit(int offset) {
145
1.75k
  return 1 << ((offset - offsetof(_DummyObj, first_field_)) / sizeof(void*));
146
1.75k
}
147
148
class _DummyObj_v {  // For maskbit_v()
149
 public:
150
  void* vtable;  // how the compiler does dynamic dispatch
151
  ObjHeader header_;
152
  int first_field_;
153
};
154
155
// maskbit_v(field_offset) is like maskbit(), but accounts for the vtable
156
// pointer.
157
158
94
constexpr int maskbit_v(int offset) {
159
94
  return 1 << ((offset - offsetof(_DummyObj_v, first_field_)) / sizeof(void*));
160
94
}
161
162
9.89k
inline ObjHeader* FindObjHeader(RawObject* obj) {
163
9.89k
  if (obj->points_to_header) {
164
9.66k
    return reinterpret_cast<ObjHeader*>(obj);
165
9.66k
  } else {
166
    // We saw a vtable pointer, so return the ObjHeader* header that
167
    // immediately follows.
168
233
    return reinterpret_cast<ObjHeader*>(reinterpret_cast<char*>(obj) +
169
233
                                        sizeof(void*));
170
233
  }
171
9.89k
}
172
173
// The "homogeneous" layout of objects with HeapTag::FixedSize.  LayoutFixed is
174
// for casting; it isn't a real type.
175
176
class LayoutFixed {
177
 public:
178
  ObjHeader header_;
179
  // only the entries denoted in field_mask will be valid
180
  RawObject* children_[16];
181
};
182
183
#endif  // MYCPP_GC_OBJ_H