| 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 |   void* ObjectAddress() {
 | 
| 55 |     return reinterpret_cast<void*>(reinterpret_cast<char*>(this) +
 | 
| 56 |                                    sizeof(ObjHeader));
 | 
| 57 |   }
 | 
| 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 |   static ObjHeader* FromObject(const void* obj) {
 | 
| 63 |     return reinterpret_cast<ObjHeader*>(
 | 
| 64 |         static_cast<char*>(const_cast<void*>(obj)) - sizeof(ObjHeader));
 | 
| 65 |   }
 | 
| 66 | 
 | 
| 67 |   // Used by hand-written and generated classes
 | 
| 68 |   static constexpr ObjHeader ClassFixed(uint32_t field_mask, uint32_t obj_len) {
 | 
| 69 |     return {TypeTag::OtherClass, field_mask, HeapTag::FixedSize, kNotInPool,
 | 
| 70 |             kUndefinedId};
 | 
| 71 |   }
 | 
| 72 | 
 | 
| 73 |   // Classes with no inheritance (e.g. used by mycpp)
 | 
| 74 |   static constexpr ObjHeader ClassScanned(uint32_t num_pointers,
 | 
| 75 |                                           uint32_t obj_len) {
 | 
| 76 |     return {TypeTag::OtherClass, num_pointers, HeapTag::Scanned, kNotInPool,
 | 
| 77 |             kUndefinedId};
 | 
| 78 |   }
 | 
| 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 |                                    uint32_t obj_len) {
 | 
| 83 |     return {TypeTag::OtherClass, field_mask, heap_tag, kNotInPool,
 | 
| 84 |             kUndefinedId};
 | 
| 85 |   }
 | 
| 86 | 
 | 
| 87 |   // Used by ASDL.
 | 
| 88 |   static constexpr ObjHeader AsdlClass(uint8_t type_tag,
 | 
| 89 |                                        uint32_t num_pointers) {
 | 
| 90 |     return {type_tag, num_pointers, HeapTag::Scanned, kNotInPool, kUndefinedId};
 | 
| 91 |   }
 | 
| 92 | 
 | 
| 93 |   static constexpr ObjHeader BigStr() {
 | 
| 94 |     return {TypeTag::BigStr, kZeroMask, HeapTag::Opaque, kNotInPool,
 | 
| 95 |             kUndefinedId};
 | 
| 96 |   }
 | 
| 97 | 
 | 
| 98 |   static constexpr ObjHeader Slab(uint8_t heap_tag, uint32_t num_pointers) {
 | 
| 99 |     return {TypeTag::Slab, num_pointers, heap_tag, kNotInPool, kUndefinedId};
 | 
| 100 |   }
 | 
| 101 | 
 | 
| 102 |   static constexpr ObjHeader Tuple(uint32_t field_mask, uint32_t obj_len) {
 | 
| 103 |     return {TypeTag::Tuple, field_mask, HeapTag::FixedSize, kNotInPool,
 | 
| 104 |             kUndefinedId};
 | 
| 105 |   }
 | 
| 106 | 
 | 
| 107 |   // Used by GLOBAL_STR, GLOBAL_LIST, GLOBAL_DICT
 | 
| 108 |   static constexpr ObjHeader Global(uint8_t type_tag) {
 | 
| 109 |     return {type_tag, kZeroMask, HeapTag::Global, kNotInPool, kIsGlobal};
 | 
| 110 |   }
 | 
| 111 | };
 | 
| 112 | 
 | 
| 113 | inline int ObjectId(void* obj) {
 | 
| 114 |   ObjHeader* h = ObjHeader::FromObject(obj);
 | 
| 115 | 
 | 
| 116 |   // pool_id is 2 bits, so shift the 28 bit obj_id past it.
 | 
| 117 |   return (h->obj_id << 2) + h->pool_id;
 | 
| 118 | }
 | 
| 119 | 
 | 
| 120 | #define FIELD_MASK(header) (header).u_mask_npointers
 | 
| 121 | #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 | constexpr int maskbit(size_t offset) {
 | 
| 137 |   return 1 << (offset / sizeof(void*));
 | 
| 138 | }
 | 
| 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
 |