Initial implementation of arena with gc roots

This commit is contained in:
Konstantin Nazarov 2024-07-20 22:42:49 +01:00
parent 8ff01726cc
commit baa10bb56b
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
7 changed files with 193 additions and 9 deletions

View file

@ -13,13 +13,18 @@ target_sources(vm_lib
PRIVATE PRIVATE
src/vm.cpp src/vm.cpp
src/common.cpp src/common.cpp
src/arena.cpp
PUBLIC PUBLIC
FILE_SET HEADERS FILE_SET HEADERS
BASE_DIRS src BASE_DIRS src
FILES FILES
src/vm.hpp src/arena.hpp
src/common.hpp src/common.hpp
src/error.hpp
src/pod.hpp
src/result.hpp
src/vm.hpp
) )
add_executable(vli src/vli.cpp) add_executable(vli src/vli.cpp)

8
src/arena.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "arena.hpp"
GcRootBase::GcRootBase(PodObject* ptr, GcRootList* node)
: _ptr(ptr), _node(node) {}
GcRootBase::~GcRootBase() {
if (_node != 0) _node->remove();
}

View file

@ -2,13 +2,86 @@
#include <concepts> #include <concepts>
#include "error.hpp"
#include "pod.hpp" #include "pod.hpp"
#include "result.hpp"
class Arena;
class GcRootList;
class GcRootBase {
public:
GcRootBase(PodObject* ptr, GcRootList* node);
~GcRootBase();
protected:
PodObject* _ptr;
GcRootList* _node;
};
template <class T>
requires std::derived_from<T, PodObject>
class GcRoot : public GcRootBase {
public:
GcRoot() : GcRootBase(0, 0) {}
GcRoot(T* ptr, GcRootList* node) : GcRootBase(ptr, node) {}
GcRoot(GcRoot&& rhs);
static Result<GcRoot<T>> create(T* ptr, Arena& arena);
T& operator*() { return *(T*)_ptr; }
};
template <class T>
requires std::derived_from<T, PodObject>
Result<GcRoot<T>> MkGcRoot(T* ptr, Arena& arena) {
return GcRoot<T>::create(ptr, arena);
}
class GcRootList {
public:
GcRootList() : _prev(0), _next(0), _root(0) {}
GcRootList(GcRootBase* root) : _prev(0), _next(0), _root(root) {}
void insert(GcRootList* node) {
GcRootList* next = _next;
_next = node;
node->_next = next;
node->_prev = this;
}
void remove() {
_prev = 0;
_next = 0;
_prev->_next = _next;
_next->_prev = _prev;
}
void update(GcRootBase* root) { _root = root; }
private:
GcRootList* _prev;
GcRootList* _next;
GcRootBase* _root;
};
class ArenaHeap { class ArenaHeap {
public: public:
ArenaHeap(uint8_t* buf, uint64_t bufsize) ArenaHeap(uint8_t* buf, uint64_t bufsize)
: buf(buf), bufsize(bufsize), boundary(0) {} : buf(buf), bufsize(bufsize), boundary(0) {}
Result<void*> alloc(uint64_t size) {
// Always align to 64 bits
if (size % 8 != 0) size += 8 - size % 8;
if (boundary + size >= bufsize) return ErrorCode::OutOfMemory;
void* res = buf + boundary;
boundary += size;
return res;
}
private: private:
uint8_t* buf; uint8_t* buf;
uint64_t bufsize; uint64_t bufsize;
@ -18,10 +91,25 @@ class ArenaHeap {
class Arena { class Arena {
public: public:
Arena(ArenaHeap* first, ArenaHeap* second) Arena(ArenaHeap* first, ArenaHeap* second)
: _heaps{first, second}, _current(0) {} : _heaps{first, second}, _gcroot(), _current(0) {}
template <class T>
Result<T*> alloc(uint64_t extra = 0) {
uint64_t objsize = sizeof(T) + extra;
auto ptr = _heaps[_current]->alloc(objsize);
if (ptr.has_value()) return (T*)ptr.value();
// TODO: trigger GC
return (T*)TRY(_heaps[_current]->alloc(objsize));
}
void add_root(GcRootList* node) { _gcroot.insert(node); }
private: private:
ArenaHeap* _heaps[2]; ArenaHeap* _heaps[2];
GcRootList _gcroot;
int _current; int _current;
}; };
@ -32,13 +120,6 @@ class StaticArenaHeap {
ArenaHeap* get() { return &_heap; } ArenaHeap* get() { return &_heap; }
template <class T>
T* alloc(uint64_t extra = 0)
requires std::derived_from<T, PodObject>
{
return 0;
}
private: private:
static const uint64_t heapsize = size - sizeof(ArenaHeap); static const uint64_t heapsize = size - sizeof(ArenaHeap);
ArenaHeap _heap; ArenaHeap _heap;
@ -54,3 +135,17 @@ class StaticArena {
StaticArenaHeap<(size - sizeof(Arena)) / 2> _heaps[2]; StaticArenaHeap<(size - sizeof(Arena)) / 2> _heaps[2];
Arena _arena; Arena _arena;
}; };
template <class T>
requires std::derived_from<T, PodObject>
Result<GcRoot<T>> GcRoot<T>::create(T* ptr, Arena& arena) {
auto lst = TRY(arena.alloc<GcRootList>());
arena.add_root(lst);
return GcRoot<T>(ptr, lst);
}
template <class T>
requires std::derived_from<T, PodObject>
GcRoot<T>::GcRoot(GcRoot<T>&& rhs) {
rhs._node->update(this);
}

View file

@ -2,6 +2,7 @@
#include <concepts> #include <concepts>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <iostream> #include <iostream>
#include "arena.hpp" #include "arena.hpp"
@ -82,18 +83,36 @@ class Nil : public Object {
class String : public Object { class String : public Object {
public: public:
String() : _value(0) {}
String(PodString* val) : _value(val) {} String(PodString* val) : _value(val) {}
virtual Tag tag() final { return Tag::String; } virtual Tag tag() final { return Tag::String; }
static Result<String> create(Arena& arena, char32_t* chars, int64_t size) {
auto pod_string = TRY(arena.alloc<PodString>(size * sizeof(char32_t)));
memcpy(pod_string->data, chars, size * sizeof(char32_t));
return String(pod_string);
}
private: private:
PodString* _value; PodString* _value;
}; };
class Symbol : public Object { class Symbol : public Object {
public: public:
Symbol() : _value(0) {}
Symbol(PodSymbol* val) : _value(val) {} Symbol(PodSymbol* val) : _value(val) {}
virtual Tag tag() final { return Tag::Symbol; } virtual Tag tag() final { return Tag::Symbol; }
static Result<Symbol> create(Arena& arena, char32_t* chars, int64_t size) {
auto pod_symbol = TRY(arena.alloc<PodSymbol>(size * sizeof(char32_t)));
memcpy(pod_symbol->data, chars, size * sizeof(char32_t));
return Symbol(pod_symbol);
}
private: private:
PodSymbol* _value; PodSymbol* _value;
}; };

8
src/die.hpp Normal file
View file

@ -0,0 +1,8 @@
#pragma once
#include <cstdio>
#define die(message) \
do { \
fprintf(stderr, "ERROR: %s", message); \
exit(EXIT_FAILURE); \
} while (0)

7
src/error.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
enum class ErrorCode {
Success,
OutOfMemory
};

42
src/result.hpp Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <utility>
#include "error.hpp"
template <class T>
class Result {
public:
Result(const T& res) : _value(res) {}
Result(T&& res) : _value(std::move(res)) {}
Result(ErrorCode err) : _error(err) {}
bool has_error() { return _error != ErrorCode::Success; }
bool has_value() { return !has_error(); }
T& value() { return _value; }
T& operator*() { return _value; }
T release_value() { return std::move(_value); }
ErrorCode error() { return _error; }
private:
T _value;
ErrorCode _error;
};
#define TRY(m) \
(({ \
auto ___res = (m); \
if (!___res.has_value()) return ___res.error(); \
std::move(___res); \
}).release_value())
#define DIEIF(m) \
(({ \
auto ___res = (m); \
if (!___res.has_value()) { \
fprintf(stderr, "%s:%d assertion failed\n", __FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
std::move(___res); \
}).release_value())