OILS / mycpp / gc_alloc.h View on Github | oils.pub

228 lines, 145 significant
1// gc_alloc.h: Functions that wrap gHeap.Allocate()
2
3#ifndef MYCPP_GC_ALLOC_H
4#define MYCPP_GC_ALLOC_H
5
6#include <string.h> // strlen
7
8#include <new> // placement new
9#include <utility> // std::forward
10
11#include "mycpp/gc_obj.h" // for RawObject, ObjHeader
12#include "mycpp/gc_slab.h" // for NewSlab()
13#include "mycpp/gc_str.h" // for NewStr()
14
15#if defined(BUMP_LEAK)
16 #include "mycpp/bump_leak_heap.h"
17extern BumpLeakHeap gHeap;
18#elif defined(MARK_SWEEP)
19 #include "mycpp/mark_sweep_heap.h"
20extern MarkSweepHeap gHeap;
21#endif
22
23// mycpp generates code that keeps track of the root set
24class StackRoot {
25 public:
26 StackRoot(void* root) {
27 RawObject** obj = reinterpret_cast<RawObject**>(root);
28 gHeap.PushRoot(obj);
29 }
30
31 ~StackRoot() {
32 gHeap.PopRoot();
33 }
34};
35
36// sugar for tests
37class StackRoots {
38 public:
39 // Note: void** seems logical, because these are pointers to pointers, but
40 // the C++ compiler doesn't like it.
41 StackRoots(std::initializer_list<void*> roots) {
42 n_ = roots.size();
43
44#if VALIDATE_ROOTS
45 int i = 0;
46#endif
47
48 for (auto root : roots) { // can't use roots[i]
49 RawObject** obj = reinterpret_cast<RawObject**>(root);
50#if VALIDATE_ROOTS
51 ValidateRoot(*obj);
52 i++;
53#endif
54
55 gHeap.PushRoot(obj);
56 }
57 }
58
59 ~StackRoots() {
60 for (int i = 0; i < n_; ++i) {
61 gHeap.PopRoot();
62 }
63 }
64
65 private:
66 int n_;
67};
68
69// Note:
70// - This function causes code bloat due to template expansion on hundreds of
71// types. Could switch to a GC_NEW() macro
72// - GCC generates slightly larger code if you factor out void* place and new
73// (place) T()
74//
75// Variadic templates:
76// https://eli.thegreenplace.net/2014/variadic-templates-in-c/
77template <typename T, typename... Args>
78T* Alloc(Args&&... args) {
79 // Alloc() allocates space for both a header and object and guarantees that
80 // they're adjacent in memory (so that they're at known offsets from one
81 // another). However, this means that the address that the object is
82 // constructed at is offset from the address returned by the memory allocator
83 // (by the size of the header), and therefore may not be sufficiently aligned.
84 // Here we assert that the object will be sufficiently aligned by making the
85 // equivalent assertion that zero padding would be required to align it.
86 // Note: the required padding is given by the following (according to
87 // https://en.wikipedia.org/wiki/Data_structure_alignment):
88 // `padding = -offset & (align - 1)`.
89 static_assert((-sizeof(ObjHeader) & (alignof(T) - 1)) == 0,
90 "Expected no padding");
91
92 DCHECK(gHeap.is_initialized_);
93
94 constexpr size_t num_bytes = sizeof(ObjHeader) + sizeof(T);
95#if MARK_SWEEP
96 int obj_id;
97 int pool_id;
98 void* place = gHeap.Allocate(num_bytes, &obj_id, &pool_id);
99#else
100 void* place = gHeap.Allocate(num_bytes);
101#endif
102 ObjHeader* header = new (place) ObjHeader(T::obj_header());
103#if MARK_SWEEP
104 header->obj_id = obj_id;
105 #ifndef NO_POOL_ALLOC
106 header->pool_id = pool_id;
107 #endif
108#endif
109 void* obj = header->ObjectAddress();
110 // Now that mycpp generates code to initialize every field, we should
111 // get rid of this.
112 // TODO: fix uftrace failure, maybe by upgrading, or working around
113 memset(obj, 0, sizeof(T));
114 return new (obj) T(std::forward<Args>(args)...);
115}
116
117//
118// String "Constructors". We need these because of the "flexible array"
119// pattern. I don't think "new BigStr()" can do that, and placement new would
120// require mycpp to generate 2 statements everywhere.
121//
122
123inline BigStr* NewStr(int len) {
124 if (len == 0) { // e.g. BufLineReader::readline() can use this optimization
125 return kEmptyString;
126 }
127
128 int obj_len = kStrHeaderSize + len + 1; // NUL terminator
129 const size_t num_bytes = sizeof(ObjHeader) + obj_len;
130#if MARK_SWEEP
131 int obj_id;
132 int pool_id;
133 void* place = gHeap.Allocate(num_bytes, &obj_id, &pool_id);
134#else
135 void* place = gHeap.Allocate(num_bytes);
136#endif
137 ObjHeader* header = new (place) ObjHeader(BigStr::obj_header());
138
139 auto s = new (header->ObjectAddress()) BigStr();
140
141 s->data_[len] = '\0'; // NUL terminate
142 s->len_ = len;
143 s->hash_ = 0;
144 s->is_hashed_ = 0;
145
146#if MARK_SWEEP
147 header->obj_id = obj_id;
148 #ifndef NO_POOL_ALLOC
149 header->pool_id = pool_id;
150 #endif
151#endif
152 return s;
153}
154
155// Call OverAllocatedStr() when you don't know the length of the string up
156// front, e.g. with snprintf(). CALLER IS RESPONSIBLE for calling
157// s->MaybeShrink() afterward!
158inline BigStr* OverAllocatedStr(int len) {
159 int obj_len = kStrHeaderSize + len + 1; // NUL terminator
160 const size_t num_bytes = sizeof(ObjHeader) + obj_len;
161#if MARK_SWEEP
162 int obj_id;
163 int pool_id;
164 void* place = gHeap.Allocate(num_bytes, &obj_id, &pool_id);
165#else
166 void* place = gHeap.Allocate(num_bytes);
167#endif
168 ObjHeader* header = new (place) ObjHeader(BigStr::obj_header());
169 auto s = new (header->ObjectAddress()) BigStr();
170 s->hash_ = 0;
171 s->is_hashed_ = 0;
172
173#if MARK_SWEEP
174 header->obj_id = obj_id;
175 #ifndef NO_POOL_ALLOC
176 header->pool_id = pool_id;
177 #endif
178#endif
179 return s;
180}
181
182// Copy C string into the managed heap.
183inline BigStr* StrFromC(const char* data, int len) {
184 // Optimization that could be taken out once we have SmallStr
185 if (len == 0) {
186 return kEmptyString;
187 }
188 BigStr* s = NewStr(len);
189 memcpy(s->data_, data, len);
190 DCHECK(s->data_[len] == '\0'); // should be true because Heap was zeroed
191
192 return s;
193}
194
195inline BigStr* StrFromC(const char* data) {
196 return StrFromC(data, strlen(data));
197}
198
199// Create a slab with a number of entries of a certain type.
200// Note: entries will be zero'd because we use calloc(). TODO: Consider
201// zeroing them separately.
202template <typename T>
203inline Slab<T>* NewSlab(int len) {
204 int obj_len = len * sizeof(T);
205 const size_t num_bytes = sizeof(ObjHeader) + obj_len;
206#if MARK_SWEEP
207 int obj_id;
208 int pool_id;
209 void* place = gHeap.Allocate(num_bytes, &obj_id, &pool_id);
210#else
211 void* place = gHeap.Allocate(num_bytes);
212#endif
213 ObjHeader* header = new (place) ObjHeader(Slab<T>::obj_header(len));
214 void* obj = header->ObjectAddress();
215 if (std::is_pointer<T>()) {
216 memset(obj, 0, obj_len);
217 }
218 auto slab = new (obj) Slab<T>(len);
219#if MARK_SWEEP
220 header->obj_id = obj_id;
221 #ifndef NO_POOL_ALLOC
222 header->pool_id = pool_id;
223 #endif
224#endif
225 return slab;
226}
227
228#endif // MYCPP_GC_ALLOC_H