cpp

Coverage Report

Created: 2022-09-21 22:22

/home/andy/git/oilshell/oil/mycpp/gc_obj.h
Line
Count
Source
1
#ifndef GC_OBJ_H
2
#define GC_OBJ_H
3
4
// Obj::heap_tag_ values.  They're odd numbers to distinguish them from vtable
5
// pointers.
6
//
7
enum Tag {
8
  Forwarded = 1,  // For the Cheney algorithm.
9
  Global = 3,     // Neither copy nor scan.
10
  Opaque = 5,     // Copy but don't scan.  List<int> and Str
11
  FixedSize = 7,  // Fixed size headers: consult field_mask_
12
  Scanned = 9,    // Copy AND scan for non-NULL pointers.
13
};
14
15
const int kZeroMask = 0;  // for types with no pointers
16
// no obj_len_ computed for global List/Slab/Dict
17
const int kNoObjLen = 0x0eadbeef;
18
19
// Why do we need this macro instead of using inheritance?
20
// - Because ASDL uses multiple inheritance for first class variants, but we
21
//   don't want multiple IMPLEMENTATION inheritance.  Instead we just generate
22
//   compatible layouts.
23
// - Similarly, GlobalStr is layout-compatible with Str.  It can't inherit from
24
//   Obj like Str, because of the constexpr issue with char[N].
25
26
// heap_tag_: one of Tag::
27
// type_tag_: ASDL tag (variant)
28
// field_mask_: for fixed length records, so max 16 fields
29
// obj_len_: number of bytes to copy
30
//   TODO: with a limitation of ~15 fields, we can encode obj_len_ in
31
//   field_mask_, and save space on many ASDL types.
32
//   And we can sort integers BEFORE pointers.
33
34
// TODO: ./configure could detect big or little endian, and then flip the
35
// fields in OBJ_HEADER?
36
//
37
// https://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
38
//
39
// Because we want to do (obj->heap_tag_ & 1 == 0) to distinguish it from
40
// vtable pointer.  We assume low bits of a pointer are 0 but not high bits.
41
42
#define OBJ_HEADER()    \
43
  uint8_t heap_tag_;    \
44
  uint8_t type_tag_;    \
45
  uint16_t field_mask_; \
46
  uint32_t obj_len_;
47
48
class Obj {
49
  // The unit of garbage collection.  It has a header describing how to find
50
  // the pointers within it.
51
  //
52
  // Note: Sorting ASDL fields by (non-pointer, pointer) is a good idea, but it
53
  // breaks down because mycpp has inheritance.  Could do this later.
54
55
 public:
56
  // Note: ASDL types are layout-compatible with Obj, but don't actually
57
  // inherit from it because of the 'multiple inheritance of implementation'
58
  // issue.  So they don't call this constructor.
59
  constexpr Obj(uint8_t heap_tag, uint16_t field_mask, int obj_len)
60
      : heap_tag_(heap_tag),
61
        type_tag_(0),
62
        field_mask_(field_mask),
63
4.20k
        obj_len_(obj_len) {
64
4.20k
  }
65
66
2.90k
  void SetObjLen(int obj_len) {
67
2.90k
    this->obj_len_ = obj_len;
68
2.90k
  }
69
70
  OBJ_HEADER()
71
72
  DISALLOW_COPY_AND_ASSIGN(Obj)
73
};
74
75
//
76
// Compile-time computation of GC field masks.
77
//
78
79
class _DummyObj {  // For maskbit()
80
 public:
81
  OBJ_HEADER()
82
  int first_field_;
83
};
84
85
574
constexpr int maskbit(int offset) {
86
574
  return 1 << ((offset - offsetof(_DummyObj, first_field_)) / sizeof(void*));
87
574
}
88
89
class _DummyObj_v {  // For maskbit_v()
90
 public:
91
  void* vtable;  // how the compiler does dynamic dispatch
92
  OBJ_HEADER()
93
  int first_field_;
94
};
95
96
1
constexpr int maskbit_v(int offset) {
97
1
  return 1 << ((offset - offsetof(_DummyObj_v, first_field_)) / sizeof(void*));
98
1
}
99
100
47
inline Obj* ObjHeader(Obj* obj) {
101
  // If we see a vtable pointer, return the Obj* header immediately following.
102
  // Otherwise just return Obj itself.
103
47
  return (obj->heap_tag_ & 0x1) == 0
104
47
             ? reinterpret_cast<Obj*>(reinterpret_cast<char*>(obj) +
105
1
                                      sizeof(void*))
106
47
             : obj;
107
47
}
108
109
#endif