diff --git a/CMakeLists.txt b/CMakeLists.txt index f5c2d7e..cb7f166 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,18 @@ target_sources(vm_lib PRIVATE src/vm.cpp src/common.cpp + src/arena.cpp PUBLIC FILE_SET HEADERS BASE_DIRS src FILES - src/vm.hpp + src/arena.hpp src/common.hpp + src/error.hpp + src/pod.hpp + src/result.hpp + src/vm.hpp ) add_executable(vli src/vli.cpp) diff --git a/src/arena.cpp b/src/arena.cpp new file mode 100644 index 0000000..7f8a4b4 --- /dev/null +++ b/src/arena.cpp @@ -0,0 +1,8 @@ +#include "arena.hpp" + +GcRootBase::GcRootBase(PodObject* ptr, GcRootList* node) + : _ptr(ptr), _node(node) {} + +GcRootBase::~GcRootBase() { + if (_node != 0) _node->remove(); +} diff --git a/src/arena.hpp b/src/arena.hpp index 1f6322c..c8b9ed4 100644 --- a/src/arena.hpp +++ b/src/arena.hpp @@ -2,13 +2,86 @@ #include +#include "error.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 + requires std::derived_from +class GcRoot : public GcRootBase { + public: + GcRoot() : GcRootBase(0, 0) {} + GcRoot(T* ptr, GcRootList* node) : GcRootBase(ptr, node) {} + GcRoot(GcRoot&& rhs); + + static Result> create(T* ptr, Arena& arena); + + T& operator*() { return *(T*)_ptr; } +}; + +template + requires std::derived_from +Result> MkGcRoot(T* ptr, Arena& arena) { + return GcRoot::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 { public: ArenaHeap(uint8_t* buf, uint64_t bufsize) : buf(buf), bufsize(bufsize), boundary(0) {} + Result 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: uint8_t* buf; uint64_t bufsize; @@ -18,10 +91,25 @@ class ArenaHeap { class Arena { public: Arena(ArenaHeap* first, ArenaHeap* second) - : _heaps{first, second}, _current(0) {} + : _heaps{first, second}, _gcroot(), _current(0) {} + + template + Result 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: ArenaHeap* _heaps[2]; + GcRootList _gcroot; int _current; }; @@ -32,13 +120,6 @@ class StaticArenaHeap { ArenaHeap* get() { return &_heap; } - template - T* alloc(uint64_t extra = 0) - requires std::derived_from - { - return 0; - } - private: static const uint64_t heapsize = size - sizeof(ArenaHeap); ArenaHeap _heap; @@ -54,3 +135,17 @@ class StaticArena { StaticArenaHeap<(size - sizeof(Arena)) / 2> _heaps[2]; Arena _arena; }; + +template + requires std::derived_from +Result> GcRoot::create(T* ptr, Arena& arena) { + auto lst = TRY(arena.alloc()); + arena.add_root(lst); + return GcRoot(ptr, lst); +} + +template + requires std::derived_from +GcRoot::GcRoot(GcRoot&& rhs) { + rhs._node->update(this); +} diff --git a/src/common.hpp b/src/common.hpp index b8a68ea..9520008 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "arena.hpp" @@ -82,18 +83,36 @@ class Nil : public Object { class String : public Object { public: + String() : _value(0) {} String(PodString* val) : _value(val) {} virtual Tag tag() final { return Tag::String; } + static Result create(Arena& arena, char32_t* chars, int64_t size) { + auto pod_string = TRY(arena.alloc(size * sizeof(char32_t))); + + memcpy(pod_string->data, chars, size * sizeof(char32_t)); + + return String(pod_string); + } + private: PodString* _value; }; class Symbol : public Object { public: + Symbol() : _value(0) {} Symbol(PodSymbol* val) : _value(val) {} virtual Tag tag() final { return Tag::Symbol; } + static Result create(Arena& arena, char32_t* chars, int64_t size) { + auto pod_symbol = TRY(arena.alloc(size * sizeof(char32_t))); + + memcpy(pod_symbol->data, chars, size * sizeof(char32_t)); + + return Symbol(pod_symbol); + } + private: PodSymbol* _value; }; diff --git a/src/die.hpp b/src/die.hpp new file mode 100644 index 0000000..cf6a3b6 --- /dev/null +++ b/src/die.hpp @@ -0,0 +1,8 @@ +#pragma once +#include + +#define die(message) \ + do { \ + fprintf(stderr, "ERROR: %s", message); \ + exit(EXIT_FAILURE); \ + } while (0) diff --git a/src/error.hpp b/src/error.hpp new file mode 100644 index 0000000..695c0ae --- /dev/null +++ b/src/error.hpp @@ -0,0 +1,7 @@ +#pragma once + +enum class ErrorCode { + Success, + OutOfMemory + +}; diff --git a/src/result.hpp b/src/result.hpp new file mode 100644 index 0000000..3a0e32f --- /dev/null +++ b/src/result.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "error.hpp" + +template +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())