/home/uke/oil/mycpp/gc_obj.h
Line | Count | Source (jump to first uncovered line) |
1 | | #ifndef MYCPP_GC_OBJ_H |
2 | | #define MYCPP_GC_OBJ_H |
3 | | |
4 | | #include <stdint.h> // uint8_t |
5 | | |
6 | | #include "mycpp/common.h" |
7 | | |
8 | | namespace HeapTag { |
9 | | const int Global = 0; // Don't mark or sweep. |
10 | | const int Opaque = 1; // e.g. List<int>, BigStr |
11 | | // Mark and sweep, but don't trace children |
12 | | const int FixedSize = 2; // Consult field_mask for children |
13 | | const int Scanned = 3; // Scan a contiguous range of children |
14 | | }; // namespace HeapTag |
15 | | |
16 | | // These tags are mainly for debugging. Oils is a statically typed program, so |
17 | | // we don't need runtime types in general. |
18 | | // This "enum" starts from the end of the valid type_tag range. |
19 | | // asdl/gen_cpp.py starts from 1 for variants, or 64 for shared variants. |
20 | | namespace TypeTag { |
21 | | const int OtherClass = 127; // non-ASDL class |
22 | | const int BigStr = 126; // asserted in dynamic StrFormat() |
23 | | const int Slab = 125; |
24 | | const int Tuple = 124; |
25 | | const int List = 123; |
26 | | const int Dict = 122; |
27 | | }; // namespace TypeTag |
28 | | |
29 | | const int kNotInPool = 0; |
30 | | const int kInPool = 1; |
31 | | |
32 | | const unsigned kZeroMask = 0; // for types with no pointers |
33 | | |
34 | | const int kMaxObjId = (1 << 28) - 1; // 28 bits means 512 Mi objects per pool |
35 | | const int kIsGlobal = kMaxObjId; // for debugging, not strictly needed |
36 | | |
37 | | const int kUndefinedId = 0; // Uninitialized object ID |
38 | | |
39 | | // Every GC-managed object is preceded in memory by an ObjHeader. |
40 | | // TODO: ./configure could detect endian-ness, and reorder the fields in |
41 | | // ObjHeader. See mycpp/demo/gc_header.cc. |
42 | | struct ObjHeader { |
43 | | unsigned type_tag : 8; // TypeTag, ASDL variant / shared variant |
44 | | // Depending on heap_tag, up to 24 fields or 2**24 = 16 Mi pointers to scan |
45 | | unsigned u_mask_npointers : 24; |
46 | | |
47 | | unsigned heap_tag : 2; // HeapTag::Opaque, etc. |
48 | | unsigned pool_id : 2; // 0 for malloc(), or 1 2 3 for fixed sized pools |
49 | | unsigned obj_id : 28; // 1 Gi unique objects |
50 | | |
51 | | // Returns the address of the GC managed object associated with this header. |
52 | | // Note: this relies on there being no padding between the header and the |
53 | | // object. See Alloc<T>() and GcGlobal<T> for relevant static_assert()s. |
54 | 2.94k | void* ObjectAddress() { |
55 | 2.94k | return reinterpret_cast<void*>(reinterpret_cast<char*>(this) + |
56 | 2.94k | sizeof(ObjHeader)); |
57 | 2.94k | } |
58 | | |
59 | | // Returns the header for the given GC managed object. |
60 | | // Note: this relies on there being no padding between the header and the |
61 | | // object. See Alloc<T>() and GcGlobal<T> for relevant static_assert()s. |
62 | 94 | static ObjHeader* FromObject(const void* obj) { |
63 | 94 | return reinterpret_cast<ObjHeader*>( |
64 | 94 | static_cast<char*>(const_cast<void*>(obj)) - sizeof(ObjHeader)); |
65 | 94 | } |
66 | | |
67 | | // Used by hand-written and generated classes |
68 | 577 | static constexpr ObjHeader ClassFixed(uint32_t field_mask, uint32_t obj_len) { |
69 | 577 | return {TypeTag::OtherClass, field_mask, HeapTag::FixedSize, kNotInPool, |
70 | 577 | kUndefinedId}; |
71 | 577 | } |
72 | | |
73 | | // Classes with no inheritance (e.g. used by mycpp) |
74 | | static constexpr ObjHeader ClassScanned(uint32_t num_pointers, |
75 | 0 | uint32_t obj_len) { |
76 | 0 | return {TypeTag::OtherClass, num_pointers, HeapTag::Scanned, kNotInPool, |
77 | 0 | kUndefinedId}; |
78 | 0 | } |
79 | | |
80 | | // Used by frontend/flag_gen.py. TODO: Sort fields and use GC_CLASS_SCANNED |
81 | | static constexpr ObjHeader Class(uint8_t heap_tag, uint32_t field_mask, |
82 | 0 | uint32_t obj_len) { |
83 | 0 | return {TypeTag::OtherClass, field_mask, heap_tag, kNotInPool, |
84 | 0 | kUndefinedId}; |
85 | 0 | } |
86 | | |
87 | | // Used by ASDL. |
88 | | static constexpr ObjHeader AsdlClass(uint8_t type_tag, |
89 | 0 | uint32_t num_pointers) { |
90 | 0 | return {type_tag, num_pointers, HeapTag::Scanned, kNotInPool, kUndefinedId}; |
91 | 0 | } |
92 | | |
93 | 1.94k | static constexpr ObjHeader BigStr() { |
94 | 1.94k | return {TypeTag::BigStr, kZeroMask, HeapTag::Opaque, kNotInPool, |
95 | 1.94k | kUndefinedId}; |
96 | 1.94k | } |
97 | | |
98 | 513 | static constexpr ObjHeader Slab(uint8_t heap_tag, uint32_t num_pointers) { |
99 | 513 | return {TypeTag::Slab, num_pointers, heap_tag, kNotInPool, kUndefinedId}; |
100 | 513 | } |
101 | | |
102 | 19 | static constexpr ObjHeader Tuple(uint32_t field_mask, uint32_t obj_len) { |
103 | 19 | return {TypeTag::Tuple, field_mask, HeapTag::FixedSize, kNotInPool, |
104 | 19 | kUndefinedId}; |
105 | 19 | } |
106 | | |
107 | | // Used by GLOBAL_STR, GLOBAL_LIST, GLOBAL_DICT |
108 | 3 | static constexpr ObjHeader Global(uint8_t type_tag) { |
109 | 3 | return {type_tag, kZeroMask, HeapTag::Global, kNotInPool, kIsGlobal}; |
110 | 3 | } |
111 | | }; |
112 | | |
113 | 0 | inline int ObjectId(void* obj) { |
114 | 0 | ObjHeader* h = ObjHeader::FromObject(obj); |
115 | 0 |
|
116 | 0 | // pool_id is 2 bits, so shift the 28 bit obj_id past it. |
117 | 0 | return (h->obj_id << 2) + h->pool_id; |
118 | 0 | } |
119 | | |
120 | 29 | #define FIELD_MASK(header) (header).u_mask_npointers |
121 | 5 | #define NUM_POINTERS(header) (header).u_mask_npointers |
122 | | |
123 | | // A RawObject* is like a void*. We use it to represent GC managed objects. |
124 | | struct RawObject; |
125 | | |
126 | | // |
127 | | // Compile-time computation of GC field masks. |
128 | | // |
129 | | |
130 | | // maskbit(field_offset) returns a bit in mask that you can bitwise-or (|) with |
131 | | // other bits. |
132 | | // |
133 | | // - Note that we only call maskbit() on offsets of pointer fields, which must |
134 | | // be POINTER-ALIGNED. |
135 | | |
136 | 914 | constexpr int maskbit(size_t offset) { |
137 | 914 | return 1 << (offset / sizeof(void*)); |
138 | 914 | } |
139 | | |
140 | | // A wrapper for a GC object and its header. For creating global GC objects, |
141 | | // like GlobalStr. |
142 | | // TODO: Make this more ergonomic by automatically initializing header |
143 | | // with T::obj_header() and providing a forwarding constructor for obj. |
144 | | template <typename T> |
145 | | class GcGlobalImpl { |
146 | | public: |
147 | | ObjHeader header; |
148 | | T obj; |
149 | | |
150 | | // This class only exists to write the static_assert. If you try to put the |
151 | | // static_assert directly in the outer class you get a compiler error that |
152 | | // taking the offsets is an 'invalid use of incomplete type'. Doing it this |
153 | | // way means the type gets completed before the assert. |
154 | | struct Internal { |
155 | | using type = GcGlobalImpl<T>; |
156 | | static_assert(offsetof(type, obj) - sizeof(ObjHeader) == |
157 | | offsetof(type, header), |
158 | | "ObjHeader doesn't fit"); |
159 | | }; |
160 | | |
161 | | DISALLOW_COPY_AND_ASSIGN(GcGlobalImpl); |
162 | | }; |
163 | | |
164 | | // Refer to `Internal::type` to force Internal to be instantiated. |
165 | | template <typename T> |
166 | | using GcGlobal = typename GcGlobalImpl<T>::Internal::type; |
167 | | |
168 | | // The "homogeneous" layout of objects with HeapTag::FixedSize. LayoutFixed is |
169 | | // for casting; it isn't a real type. |
170 | | |
171 | | // TODO: we could determine the max of all objects statically! |
172 | | const int kFieldMaskBits = 24; |
173 | | |
174 | | struct LayoutFixed { |
175 | | // only the entries denoted in field_mask will be valid |
176 | | RawObject* children_[kFieldMaskBits]; |
177 | | }; |
178 | | |
179 | | #endif // MYCPP_GC_OBJ_H |