Make garbage collection work
This commit is contained in:
parent
426d3d7029
commit
f87822933a
4 changed files with 198 additions and 7 deletions
142
src/arena.cpp
142
src/arena.cpp
|
@ -1,8 +1,150 @@
|
||||||
#include "arena.hpp"
|
#include "arena.hpp"
|
||||||
|
|
||||||
|
#include "error.hpp"
|
||||||
|
|
||||||
GcRootBase::GcRootBase(PodObject* ptr, GcRootList* node)
|
GcRootBase::GcRootBase(PodObject* ptr, GcRootList* node)
|
||||||
: _ptr(ptr), _node(node) {}
|
: _ptr(ptr), _node(node) {}
|
||||||
|
|
||||||
GcRootBase::~GcRootBase() {
|
GcRootBase::~GcRootBase() {
|
||||||
if (_node != 0) _node->remove();
|
if (_node != 0) _node->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> Arena::gc() {
|
||||||
|
std::swap(_first, _second);
|
||||||
|
_first->reset();
|
||||||
|
|
||||||
|
TRY(gc_roots());
|
||||||
|
|
||||||
|
_second->reset();
|
||||||
|
return Result<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> Arena::gc_roots() {
|
||||||
|
auto node = _gcroot.next();
|
||||||
|
while (node != 0) {
|
||||||
|
node = TRY(gc_root(node));
|
||||||
|
node = node->next();
|
||||||
|
}
|
||||||
|
return Result<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<GcRootList*> Arena::gc_root(GcRootList* node) {
|
||||||
|
auto pod = TRY(gc_pod(node->_root->_ptr));
|
||||||
|
auto lst = TRY(alloc<GcRootList>());
|
||||||
|
node->_root->_ptr = pod;
|
||||||
|
node->_root->_node = lst;
|
||||||
|
|
||||||
|
lst->_root = node->_root;
|
||||||
|
|
||||||
|
GcRootList* prev = node->_prev;
|
||||||
|
GcRootList* next = node->_next;
|
||||||
|
|
||||||
|
prev->_next = lst;
|
||||||
|
if (next) next->_prev = lst;
|
||||||
|
lst->_prev = prev;
|
||||||
|
lst->_next = next;
|
||||||
|
|
||||||
|
// Not strictly needed, but useful for debugging
|
||||||
|
node->_prev = 0;
|
||||||
|
node->_next = 0;
|
||||||
|
node->_root = 0;
|
||||||
|
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_pod(PodObject* obj) {
|
||||||
|
switch (obj->header.tag) {
|
||||||
|
case Tag::Nil:
|
||||||
|
return gc_nil((PodNil*)obj);
|
||||||
|
case Tag::Bool:
|
||||||
|
return gc_bool((PodBool*)obj);
|
||||||
|
case Tag::Int64:
|
||||||
|
return gc_int64((PodInt64*)obj);
|
||||||
|
case Tag::Float:
|
||||||
|
return gc_float((PodFloat*)obj);
|
||||||
|
case Tag::String:
|
||||||
|
return gc_string((PodString*)obj);
|
||||||
|
case Tag::Symbol:
|
||||||
|
return gc_symbol((PodSymbol*)obj);
|
||||||
|
case Tag::Syntax:
|
||||||
|
return gc_syntax((PodSyntax*)obj);
|
||||||
|
case Tag::Pair:
|
||||||
|
return gc_pair((PodPair*)obj);
|
||||||
|
case Tag::ByteArray:
|
||||||
|
return gc_bytearray((PodByteArray*)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_nil(PodNil* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodNil>());
|
||||||
|
nobj->header.tag = Tag::Nil;
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_bool(PodBool* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodBool>());
|
||||||
|
nobj->header.tag = Tag::Bool;
|
||||||
|
nobj->value = obj->value;
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_int64(PodInt64* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodInt64>());
|
||||||
|
nobj->header.tag = Tag::Int64;
|
||||||
|
nobj->value = obj->value;
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_float(PodFloat* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodFloat>());
|
||||||
|
nobj->header.tag = Tag::Float;
|
||||||
|
nobj->value = obj->value;
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_string(PodString* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodString>(obj->size * sizeof(char32_t)));
|
||||||
|
nobj->header.tag = Tag::String;
|
||||||
|
nobj->size = obj->size;
|
||||||
|
memcpy(nobj->data, obj->data, obj->size * sizeof(char32_t));
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_symbol(PodSymbol* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodSymbol>(obj->size * sizeof(char32_t)));
|
||||||
|
nobj->header.tag = Tag::Symbol;
|
||||||
|
nobj->size = obj->size;
|
||||||
|
memcpy(nobj->data, obj->data, obj->size * sizeof(char32_t));
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_syntax(PodSyntax* obj) {
|
||||||
|
return ErrorCode::NotImplemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_pair(PodPair* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodPair>());
|
||||||
|
|
||||||
|
nobj->header.tag = Tag::Pair;
|
||||||
|
|
||||||
|
PodObject* first = obj->first.get(obj);
|
||||||
|
PodObject* rest = obj->rest.get(obj);
|
||||||
|
|
||||||
|
first = TRY(gc_pod(first));
|
||||||
|
rest = TRY(gc_pod(rest));
|
||||||
|
|
||||||
|
nobj->first = OffPtr<PodObject>(nobj, first);
|
||||||
|
nobj->rest = OffPtr<PodObject>(nobj, rest);
|
||||||
|
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PodObject*> Arena::gc_bytearray(PodByteArray* obj) {
|
||||||
|
auto nobj = TRY(alloc<PodByteArray>(obj->size));
|
||||||
|
nobj->header.tag = Tag::ByteArray;
|
||||||
|
nobj->size = obj->size;
|
||||||
|
memcpy(nobj->data, obj->data, obj->size);
|
||||||
|
return nobj;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ class GcRootList;
|
||||||
|
|
||||||
class GcRootBase {
|
class GcRootBase {
|
||||||
public:
|
public:
|
||||||
|
friend class Arena;
|
||||||
|
|
||||||
GcRootBase() : _ptr(0), _node(0){};
|
GcRootBase() : _ptr(0), _node(0){};
|
||||||
GcRootBase(PodObject* ptr, GcRootList* node);
|
GcRootBase(PodObject* ptr, GcRootList* node);
|
||||||
|
|
||||||
|
@ -54,6 +56,8 @@ Result<GcRoot<T>> MkGcRoot(T* ptr, Arena& arena) {
|
||||||
|
|
||||||
class GcRootList {
|
class GcRootList {
|
||||||
public:
|
public:
|
||||||
|
friend class Arena;
|
||||||
|
|
||||||
GcRootList() : _prev(0), _next(0), _root(0) {}
|
GcRootList() : _prev(0), _next(0), _root(0) {}
|
||||||
|
|
||||||
GcRootList(GcRootBase* root) : _prev(0), _next(0), _root(root) {}
|
GcRootList(GcRootBase* root) : _prev(0), _next(0), _root(root) {}
|
||||||
|
@ -72,6 +76,7 @@ class GcRootList {
|
||||||
}
|
}
|
||||||
|
|
||||||
GcRootList* next() { return _next; }
|
GcRootList* next() { return _next; }
|
||||||
|
GcRootList* prev() { return _prev; }
|
||||||
|
|
||||||
void update(GcRootBase* root) { _root = root; }
|
void update(GcRootBase* root) { _root = root; }
|
||||||
|
|
||||||
|
@ -105,6 +110,11 @@ class ArenaHeap {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
boundary = 0;
|
||||||
|
ASAN_POISON_MEMORY_REGION(buf, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t* buf;
|
uint8_t* buf;
|
||||||
uint64_t bufsize;
|
uint64_t bufsize;
|
||||||
|
@ -114,20 +124,35 @@ class ArenaHeap {
|
||||||
class Arena {
|
class Arena {
|
||||||
public:
|
public:
|
||||||
Arena(ArenaHeap* first, ArenaHeap* second)
|
Arena(ArenaHeap* first, ArenaHeap* second)
|
||||||
: _heaps{first, second}, _gcroot(), _current(0) {}
|
: _first(first), _second(second), _gcroot() {}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
Result<T*> alloc(uint64_t extra = 0) {
|
Result<T*> alloc(uint64_t extra = 0) {
|
||||||
uint64_t objsize = sizeof(T) + extra;
|
uint64_t objsize = sizeof(T) + extra;
|
||||||
auto ptr = _heaps[_current]->alloc(objsize);
|
auto ptr = _first->alloc(objsize);
|
||||||
|
|
||||||
if (ptr.has_value()) return (T*)ptr.value();
|
if (ptr.has_value()) return (T*)ptr.value();
|
||||||
|
|
||||||
// TODO: trigger GC
|
// TODO: trigger GC
|
||||||
|
|
||||||
return (T*)TRY(_heaps[_current]->alloc(objsize));
|
return (T*)TRY(_first->alloc(objsize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> gc();
|
||||||
|
Result<void> gc_roots();
|
||||||
|
Result<GcRootList*> gc_root(GcRootList* node);
|
||||||
|
|
||||||
|
Result<PodObject*> gc_pod(PodObject* obj);
|
||||||
|
Result<PodObject*> gc_nil(PodNil* obj);
|
||||||
|
Result<PodObject*> gc_bool(PodBool* obj);
|
||||||
|
Result<PodObject*> gc_int64(PodInt64* obj);
|
||||||
|
Result<PodObject*> gc_float(PodFloat* obj);
|
||||||
|
Result<PodObject*> gc_string(PodString* obj);
|
||||||
|
Result<PodObject*> gc_symbol(PodSymbol* obj);
|
||||||
|
Result<PodObject*> gc_syntax(PodSyntax* obj);
|
||||||
|
Result<PodObject*> gc_pair(PodPair* obj);
|
||||||
|
Result<PodObject*> gc_bytearray(PodByteArray* obj);
|
||||||
|
|
||||||
void add_root(GcRootList* node) { _gcroot.insert(node); }
|
void add_root(GcRootList* node) { _gcroot.insert(node); }
|
||||||
|
|
||||||
uint64_t root_count() {
|
uint64_t root_count() {
|
||||||
|
@ -139,9 +164,9 @@ class Arena {
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ArenaHeap* _heaps[2];
|
ArenaHeap* _first;
|
||||||
|
ArenaHeap* _second;
|
||||||
GcRootList _gcroot;
|
GcRootList _gcroot;
|
||||||
int _current;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint64_t size>
|
template <uint64_t size>
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Object {
|
||||||
virtual Tag tag() = 0;
|
virtual Tag tag() = 0;
|
||||||
virtual Result<Value> copy(Arena& arena) = 0;
|
virtual Result<Value> copy(Arena& arena) = 0;
|
||||||
virtual PodObject* pod() = 0;
|
virtual PodObject* pod() = 0;
|
||||||
|
virtual void move(Object*) = 0;
|
||||||
virtual ~Object() = default;
|
virtual ~Object() = default;
|
||||||
Object() = default;
|
Object() = default;
|
||||||
Object(const Object&) = delete;
|
Object(const Object&) = delete;
|
||||||
|
@ -46,6 +47,8 @@ class Nil : public Object {
|
||||||
|
|
||||||
virtual Result<Value> copy(Arena& arena) final;
|
virtual Result<Value> copy(Arena& arena) final;
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Nil(std::move(_value)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GcRoot<PodNil> _value;
|
GcRoot<PodNil> _value;
|
||||||
};
|
};
|
||||||
|
@ -64,6 +67,10 @@ class ByteArray : public Object {
|
||||||
virtual Tag tag() final { return Tag::String; }
|
virtual Tag tag() final { return Tag::String; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final {
|
||||||
|
new (obj) ByteArray(std::move(_value));
|
||||||
|
}
|
||||||
|
|
||||||
static Result<ByteArray> create(Arena& arena, PodByteArray* obj) {
|
static Result<ByteArray> create(Arena& arena, PodByteArray* obj) {
|
||||||
return ByteArray(TRY(MkGcRoot(obj, arena)));
|
return ByteArray(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -164,6 +171,8 @@ class String : public Object {
|
||||||
virtual Tag tag() final { return Tag::String; }
|
virtual Tag tag() final { return Tag::String; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) String(std::move(_value)); }
|
||||||
|
|
||||||
static Result<String> create(Arena& arena, PodString* obj) {
|
static Result<String> create(Arena& arena, PodString* obj) {
|
||||||
return String(TRY(MkGcRoot(obj, arena)));
|
return String(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -262,6 +271,8 @@ class Symbol : public Object {
|
||||||
virtual Tag tag() final { return Tag::Symbol; }
|
virtual Tag tag() final { return Tag::Symbol; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Symbol(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Symbol> create(Arena& arena, PodSymbol* obj) {
|
static Result<Symbol> create(Arena& arena, PodSymbol* obj) {
|
||||||
return Symbol(TRY(MkGcRoot(obj, arena)));
|
return Symbol(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -298,6 +309,8 @@ class Syntax : public Object {
|
||||||
virtual Tag tag() final { return Tag::Syntax; }
|
virtual Tag tag() final { return Tag::Syntax; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Syntax(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Syntax> create(Arena& arena, PodSyntax* obj) {
|
static Result<Syntax> create(Arena& arena, PodSyntax* obj) {
|
||||||
return Syntax(TRY(MkGcRoot(obj, arena)));
|
return Syntax(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -318,6 +331,8 @@ class Pair : public Object {
|
||||||
virtual Tag tag() final { return Tag::Pair; }
|
virtual Tag tag() final { return Tag::Pair; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Pair(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Pair> create(Arena& arena, PodPair* obj) {
|
static Result<Pair> create(Arena& arena, PodPair* obj) {
|
||||||
return Pair(TRY(MkGcRoot(obj, arena)));
|
return Pair(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -340,6 +355,8 @@ class Int64 : public Object {
|
||||||
virtual Tag tag() final { return Tag::Int64; }
|
virtual Tag tag() final { return Tag::Int64; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Int64(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Int64> create(Arena& arena, PodInt64* obj) {
|
static Result<Int64> create(Arena& arena, PodInt64* obj) {
|
||||||
return Int64(TRY(MkGcRoot(obj, arena)));
|
return Int64(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -368,6 +385,8 @@ class Float : public Object {
|
||||||
virtual Tag tag() final { return Tag::Float; }
|
virtual Tag tag() final { return Tag::Float; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Float(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Float> create(Arena& arena, PodFloat* obj) {
|
static Result<Float> create(Arena& arena, PodFloat* obj) {
|
||||||
return Float(TRY(MkGcRoot(obj, arena)));
|
return Float(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -397,6 +416,8 @@ class Bool : public Object {
|
||||||
virtual Tag tag() final { return Tag::Bool; }
|
virtual Tag tag() final { return Tag::Bool; }
|
||||||
virtual PodObject* pod() final { return _value.get(); }
|
virtual PodObject* pod() final { return _value.get(); }
|
||||||
|
|
||||||
|
virtual void move(Object* obj) final { new (obj) Bool(std::move(_value)); }
|
||||||
|
|
||||||
static Result<Bool> create(Arena& arena, PodBool* obj) {
|
static Result<Bool> create(Arena& arena, PodBool* obj) {
|
||||||
return Bool(TRY(MkGcRoot(obj, arena)));
|
return Bool(TRY(MkGcRoot(obj, arena)));
|
||||||
}
|
}
|
||||||
|
@ -423,7 +444,8 @@ class Value {
|
||||||
Value() { new (buf) Nil(); }
|
Value() { new (buf) Nil(); }
|
||||||
~Value() { ((Object*)buf)->~Object(); }
|
~Value() { ((Object*)buf)->~Object(); }
|
||||||
Value(Value&& val) {
|
Value(Value&& val) {
|
||||||
memcpy(buf, val.buf, 24);
|
// TODO: repoint the gc root properly
|
||||||
|
((Object*)val.buf)->move((Object*)buf);
|
||||||
new (val.buf) Nil();
|
new (val.buf) Nil();
|
||||||
}
|
}
|
||||||
Value(const Value&) = delete;
|
Value(const Value&) = delete;
|
||||||
|
@ -437,7 +459,7 @@ class Value {
|
||||||
|
|
||||||
Value& operator=(Value&& val) {
|
Value& operator=(Value&& val) {
|
||||||
((Object*)buf)->~Object();
|
((Object*)buf)->~Object();
|
||||||
memcpy(buf, val.buf, 24);
|
((Object*)val.buf)->move((Object*)buf);
|
||||||
new (val.buf) Nil();
|
new (val.buf) Nil();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ int main() {
|
||||||
auto writer = Writer(arena);
|
auto writer = Writer(arena);
|
||||||
auto s2 = DIEIF(writer.write_one(r));
|
auto s2 = DIEIF(writer.write_one(r));
|
||||||
|
|
||||||
|
DIEIF(arena.gc());
|
||||||
|
|
||||||
DIEIF(debug_print(arena, s2));
|
DIEIF(debug_print(arena, s2));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue