/home/andy/git/oilshell/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 | 8.42k | void* ObjectAddress() { |
55 | 8.42k | return reinterpret_cast<void*>(reinterpret_cast<char*>(this) + |
56 | 8.42k | sizeof(ObjHeader)); |
57 | 8.42k | } |
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 | 745 | static ObjHeader* FromObject(const void* obj) { |
63 | 745 | return reinterpret_cast<ObjHeader*>( |
64 | 745 | static_cast<char*>(const_cast<void*>(obj)) - sizeof(ObjHeader)); |
65 | 745 | } |
66 | | |
67 | | // Used by hand-written and generated classes |
68 | 1.60k | static constexpr ObjHeader ClassFixed(uint32_t field_mask, uint32_t obj_len) { |
69 | 1.60k | return {TypeTag::OtherClass, field_mask, HeapTag::FixedSize, kNotInPool, |
70 | 1.60k | kUndefinedId}; |
71 | 1.60k | } |
72 | | |
73 | | // Classes with no inheritance (e.g. used by mycpp) |
74 | | static constexpr ObjHeader ClassScanned(uint32_t num_pointers, |
75 | 65 | uint32_t obj_len) { |
76 | 65 | return {TypeTag::OtherClass, num_pointers, HeapTag::Scanned, kNotInPool, |
77 | 65 | kUndefinedId}; |
78 | 65 | } |
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 | 205 | uint32_t num_pointers) { |
90 | 205 | return {type_tag, num_pointers, HeapTag::Scanned, kNotInPool, kUndefinedId}; |
91 | 205 | } |
92 | | |
93 | 5.39k | static constexpr ObjHeader BigStr() { |
94 | 5.39k | return {TypeTag::BigStr, kZeroMask, HeapTag::Opaque, kNotInPool, |
95 | 5.39k | kUndefinedId}; |
96 | 5.39k | } |
97 | | |
98 | 1.27k | static constexpr ObjHeader Slab(uint8_t heap_tag, uint32_t num_pointers) { |
99 | 1.27k | return {TypeTag::Slab, num_pointers, heap_tag, kNotInPool, kUndefinedId}; |
100 | 1.27k | } |
101 | | |
102 | 89 | static constexpr ObjHeader Tuple(uint32_t field_mask, uint32_t obj_len) { |
103 | 89 | return {TypeTag::Tuple, field_mask, HeapTag::FixedSize, kNotInPool, |
104 | 89 | kUndefinedId}; |
105 | 89 | } |
106 | | |
107 | | // Used by GLOBAL_STR, GLOBAL_LIST, GLOBAL_DICT |
108 | 129 | static constexpr ObjHeader Global(uint8_t type_tag) { |
109 | 129 | return {type_tag, kZeroMask, HeapTag::Global, kNotInPool, kIsGlobal}; |
110 | 129 | } |
111 | | }; |
112 | | |
113 | 58 | #define FIELD_MASK(header) (header).u_mask_npointers |
114 | 10 | #define NUM_POINTERS(header) (header).u_mask_npointers |
115 | | |
116 | | // A RawObject* is like a void*. We use it to represent GC managed objects. |
117 | | struct RawObject; |
118 | | |
119 | | // |
120 | | // Compile-time computation of GC field masks. |
121 | | // |
122 | | |
123 | | // maskbit(field_offset) returns a bit in mask that you can bitwise-or (|) with |
124 | | // other bits. |
125 | | // |
126 | | // - Note that we only call maskbit() on offsets of pointer fields, which must |
127 | | // be POINTER-ALIGNED. |
128 | | |
129 | 2.37k | constexpr int maskbit(size_t offset) { |
130 | 2.37k | return 1 << (offset / sizeof(void*)); |
131 | 2.37k | } |
132 | | |
133 | | // A wrapper for a GC object and its header. For creating global GC objects, |
134 | | // like GlobalStr. |
135 | | // TODO: Make this more ergonomic by automatically initializing header |
136 | | // with T::obj_header() and providing a forwarding constructor for obj. |
137 | | template <typename T> |
138 | | class GcGlobalImpl { |
139 | | public: |
140 | | ObjHeader header; |
141 | | T obj; |
142 | | |
143 | | // This class only exists to write the static_assert. If you try to put the |
144 | | // static_assert directly in the outer class you get a compiler error that |
145 | | // taking the offsets is an 'invalid use of incomplete type'. Doing it this |
146 | | // way means the type gets completed before the assert. |
147 | | struct Internal { |
148 | | using type = GcGlobalImpl<T>; |
149 | | static_assert(offsetof(type, obj) - sizeof(ObjHeader) == |
150 | | offsetof(type, header), |
151 | | "ObjHeader doesn't fit"); |
152 | | }; |
153 | | |
154 | | DISALLOW_COPY_AND_ASSIGN(GcGlobalImpl); |
155 | | }; |
156 | | |
157 | | // Refer to `Internal::type` to force Internal to be instantiated. |
158 | | template <typename T> |
159 | | using GcGlobal = typename GcGlobalImpl<T>::Internal::type; |
160 | | |
161 | | // The "homogeneous" layout of objects with HeapTag::FixedSize. LayoutFixed is |
162 | | // for casting; it isn't a real type. |
163 | | |
164 | | // TODO: we could determine the max of all objects statically! |
165 | | const int kFieldMaskBits = 24; |
166 | | |
167 | | struct LayoutFixed { |
168 | | // only the entries denoted in field_mask will be valid |
169 | | RawObject* children_[kFieldMaskBits]; |
170 | | }; |
171 | | |
172 | | #endif // MYCPP_GC_OBJ_H |