-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgc.h
315 lines (274 loc) · 9.24 KB
/
gc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#pragma once
#define _USE_MATH_DEFINES
#include "objects/classes.h"
#include <cstddef> // android somehow doesn't have size_t in stdint
#include <cstdint>
#include <functional>
#ifndef DEBUG
//#define DEBUG
#endif
#ifndef DEBUG_GC
//#define DEBUG_GC
#endif
#if defined(DEBUG_GC) || defined(DEBUG_GC_CLEANUP)
#define GC_PRINT_CLEANUP
#endif
#ifndef GC_NUM_GENERATIONS
// number of GC generations
#define GC_NUM_GENERATIONS 4
#endif
#ifndef GC_NEXT_GEN_THRESHOLD
// number of times a child generation has to be gc'ed
// before a parent generation is considered for a gc
#define GC_NEXT_GEN_THRESHOLD 2
#endif
#if defined(_MSC_VER)
#define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
using size_t = std::size_t;
struct Value;
struct Expr;
struct Statement;
template <typename T, size_t n> struct CustomArray;
#ifdef GC_STRESS
#undef GC_STRESS
#define GC_STRESS 1
#else
#define GC_STRESS 0
#endif
#define GC_MIN_TRACKED_OBJECTS_CAP 32
struct GcObject {
// pointer to the class of this object
Class *klass;
// last 8 bits contains the type
//
// next 8 bits contains the refcount, which marks
// it as a temporary object, and makes the gc
// not release it. since only 8 bits are
// used, it can go upto 255, and then it will
// round down to 0. that should not be a problem
// for now. although, increaseRefCount() is not
// sufficient from getting an object gc'ed though,
// the gc has to track it as a temporary. So,
// use trackTemp and untrackTemp to keep it
// alive, or use *2 structs, which automatically
// does that for you, per scope.
//
// MSB contains the marker bit
uint64_t obj_priv;
// object types
enum class Type : std::uint8_t {
None,
#define OBJTYPE(n, c) n,
#include "objecttype.h"
};
inline const Class *getClass() { return klass; }
inline void setClass(Class *c) { klass = c; }
// last 8 bits
static constexpr uint64_t TypeBits = 0x00000000000000ff;
inline Type getType() { return (Type)((obj_priv & TypeBits)); }
inline void setType(Type t, const Class *c) {
klass = (Class *)c;
obj_priv = (uint64_t)(t);
}
// next 8 bits
static constexpr uint64_t RefCountBits = 0x000000000000ff00;
// sets a new refcount
inline void setRefCount(uint8_t value) {
// clear the existing count
obj_priv &= ~RefCountBits;
// set the new count
obj_priv |= (uint64_t)value << 8;
}
// gets the correct refcount
inline uint8_t getRefCount() { return (obj_priv & RefCountBits) >> 8; }
// the following are not checked for 0 < refcount < 128
// bad things will happen if that limit is crossed
inline void increaseRefCount() {
// add a 1 to the refcount
obj_priv += ((uint64_t)1 << 8);
}
inline void decreaseRefCount() {
// subtract a 1 from the refcount
obj_priv -= ((uint64_t)1 << 8);
}
// MSB
static constexpr uint64_t Marker = ((uintptr_t)1) << 63;
inline void markOwn() { obj_priv |= Marker; }
inline bool isMarked() { return obj_priv & Marker; }
inline void unmarkOwn() { obj_priv &= ~Marker; }
template <typename T> static Type getType() { return Type::None; };
#ifdef DEBUG_GC
// A pointer to the index of the generation that
// this object is stored into.
// calling depend() will cause this location
// to be set to NULL, and generations[0]->insert(this);
// to ensure that this object is garbage collected after
// the one that called the depend.
size_t gen, idx;
void depend_();
#endif
// basic type check
#define OBJTYPE(n, c) \
inline bool is##n() { return getType() == Type::n; }
#include "objecttype.h"
};
struct Gc {
// initializes all the core classes.
// this must be the first method
// that is called after the program
// is run
static void init();
// State of the garbage collector
static size_t totalAllocated;
static size_t next_gc;
static size_t max_gc;
// an array to track the allocated
// objects
using Generation = CustomArray<GcObject *, GC_MIN_TRACKED_OBJECTS_CAP>;
static Generation *generations[GC_NUM_GENERATIONS];
// number of times gc is performed, resets whenever
// it is equal to GC_NUM_GENERATIONS * GC_NEXT_GENERATION_THRESHOLD
static size_t gc_count;
// inserts at generations[0]
static void tracker_insert(GcObject *g);
// replacement for manual allocations
// to keep track of allocated bytes
static void *malloc(size_t bytes);
static void *calloc(size_t num, size_t size);
static void free(void *mem, size_t bytes);
static void *realloc(void *mem, size_t oldb, size_t newb);
// macros for getting the call site in debug mode
#ifdef DEBUG_GC
#define OBJTYPE(x, c) \
static void depend(const x *obj) { ((GcObject *)obj)->depend_(); }
#include "objecttype.h"
static void gc_print(const char *file, int line, const char *message);
static void *malloc_print(const char *file, int line, size_t bytes);
static void *calloc_print(const char *file, int line, size_t num,
size_t size);
static void *realloc_print(const char *file, int line, void *mem,
size_t oldb, size_t newb);
static void free_print(const char *file, int line, void *mem, size_t bytes);
#define Gc_malloc(x) Gc::malloc_print(__FILE__, __LINE__, (x))
#define Gc_calloc(x, y) Gc::calloc_print(__FILE__, __LINE__, (x), (y))
#define Gc_realloc(x, y, z) Gc::realloc_print(__FILE__, __LINE__, (x), (y), (z))
#define Gc_free(x, y) \
{ Gc::free_print(__FILE__, __LINE__, (x), (y)); }
// macros to warn against direct malloc/free calls
/*#define malloc(x) \
(std::wcout << __FILE__ << ":" << __LINE__ << " Using direct malloc!\n", \
::malloc((x)))
#define calloc(x, y) \
(std::wcout << __FILE__ << ":" << __LINE__ << " Using direct calloc!\n", \
::calloc((x), (y)))
#define realloc(x, y) \
(std::wcout << __FILE__ << ":" << __LINE__ << " Using direct realloc!\n", \
::realloc((x), (y)))
#define free(x) \
std::wcout << __FILE__ << ":" << __LINE__ << " Using direct free!\n"; \
::free((x));*/
#else
#define Gc_malloc(x) Gc::malloc(x)
#define Gc_calloc(x, y) Gc::calloc(x, y)
#define Gc_realloc(x, y, z) Gc::realloc(x, y, z)
#define Gc_free(x, y) Gc::free(x, y)
#endif
// marking and unmarking functions
static void mark(Value v);
static void mark(GcObject *p);
#define OBJTYPE(r, n) \
static void mark(r *val) { mark((GcObject *)val); }
#include "objecttype.h"
static void mark(Value *v, size_t num);
// this methods should be called by an
// object when it holds reference to an
// object which it does explicitly
// own
static void release(GcObject *obj);
static void release(Value v);
// clear
// sweep this particular generation
// unmarkedClassesHead holds the unmarked classes in a linked
// list to ensure that all of their objects have been released
// before they are released.
static void sweep(size_t genid, Class **unmarkedClassesHead);
// core gc method
// the flag forces a gc even if
// total_allocated < next_gc
static void gc(bool force = false);
// sets next_gc
static void setNextGC(size_t v);
// sets max_gc
static void setMaxGC(size_t v);
// memory management functions
static void *alloc(size_t s, GcObject::Type type, const Class *klass);
template <typename T> static T *alloc() {
return (T *)alloc(sizeof(T), GcObject::getType<T>(), Classes::get<T>());
}
// makes a contiguous allocation of a String
// object along with its characters, the string
// is not yet tracked by the gc
static String *allocString2(int numchar);
// release an untracked string
static void releaseString2(String *s);
// tuple is a contiguous array of fixed size
static Tuple *allocTuple2(int numobj);
// expressions and statements require custom sizes
static Expression *allocExpression2(size_t size);
static Statement * allocStatement2(size_t size);
// allocate an object with the given class
static Object *allocObject(const Class *klass);
static Set *temporaryObjects;
static void trackTemp(GcObject *g);
static void untrackTemp(GcObject *g);
// debug information
#ifdef DEBUG_GC
static size_t GcCounters[];
static void print_stat();
#endif
};
#define OBJTYPE(n, c) template <> GcObject::Type GcObject::getType<n>();
#include "objecttype.h"
template <typename T> struct GcTempObject {
private:
T *obj;
void track() {
if(obj)
Gc::trackTemp((GcObject *)obj);
}
void untrack() {
if(obj)
Gc::untrackTemp((GcObject *)obj);
}
public:
GcTempObject() : obj(nullptr) {}
GcTempObject(T *o) : obj(o) { track(); }
~GcTempObject() { untrack(); }
T *operator->() const { return obj; }
T &operator*() const { return *obj; }
GcTempObject<T> &operator=(T *o) {
untrack();
obj = o;
track();
return *this;
}
operator T *() const { return obj; }
void *operator new(size_t s) = delete;
// disallow copy construct and copy assign
GcTempObject(GcTempObject<T> &o) = delete;
GcTempObject<T> &operator=(const GcTempObject<T> &o) = delete;
// allow move construct and move assign
GcTempObject(GcTempObject<T> &&o) {
obj = o.obj; // it is already being tracked
o.obj = nullptr;
}
GcTempObject<T> &operator=(GcTempObject<T> &&o) {
untrack();
obj = o.obj; // it is already being tracked
o.obj = nullptr;
return *this;
}
};
#define OBJTYPE(x, c) using x##2 = GcTempObject<x>;
#include "objecttype.h"