cpp

Coverage Report

Created: 2024-03-13 14:13

/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
9.08k
  void* ObjectAddress() {
55
9.08k
    return reinterpret_cast<void*>(reinterpret_cast<char*>(this) +
56
9.08k
                                   sizeof(ObjHeader));
57
9.08k
  }
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
803
  static ObjHeader* FromObject(const void* obj) {
63
803
    return reinterpret_cast<ObjHeader*>(
64
803
        static_cast<char*>(const_cast<void*>(obj)) - sizeof(ObjHeader));
65
803
  }
66
67
  // Used by hand-written and generated classes
68
1.63k
  static constexpr ObjHeader ClassFixed(uint32_t field_mask, uint32_t obj_len) {
69
1.63k
    return {TypeTag::OtherClass, field_mask, HeapTag::FixedSize, kNotInPool,
70
1.63k
            kUndefinedId};
71
1.63k
  }
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
207
                                       uint32_t num_pointers) {
90
207
    return {type_tag, num_pointers, HeapTag::Scanned, kNotInPool, kUndefinedId};
91
207
  }
92
93
6.01k
  static constexpr ObjHeader BigStr() {
94
6.01k
    return {TypeTag::BigStr, kZeroMask, HeapTag::Opaque, kNotInPool,
95
6.01k
            kUndefinedId};
96
6.01k
  }
97
98
1.28k
  static constexpr ObjHeader Slab(uint8_t heap_tag, uint32_t num_pointers) {
99
1.28k
    return {TypeTag::Slab, num_pointers, heap_tag, kNotInPool, kUndefinedId};
100
1.28k
  }
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
205
  static constexpr ObjHeader Global(uint8_t type_tag) {
109
205
    return {type_tag, kZeroMask, HeapTag::Global, kNotInPool, kIsGlobal};
110
205
  }
111
};
112
113
39
inline int ObjectId(void* obj) {
114
39
  ObjHeader* h = ObjHeader::FromObject(obj);
115
116
  // pool_id is 2 bits, so shift the 28 bit obj_id past it.
117
39
  return (h->obj_id << 2) + h->pool_id;
118
39
}
119
120
58
#define FIELD_MASK(header) (header).u_mask_npointers
121
10
#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
2.42k
constexpr int maskbit(size_t offset) {
137
2.42k
  return 1 << (offset / sizeof(void*));
138
2.42k
}
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