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