diff --git a/src/compile/Compiler.cpp b/src/compile/Compiler.cpp index 119da79..9eb8bdf 100644 --- a/src/compile/Compiler.cpp +++ b/src/compile/Compiler.cpp @@ -179,7 +179,6 @@ Compiler::Compiler( VM &vm ) : vm(vm), - gcGuard(vm.heap.pauseGC()), enclosing(enclosing), source(source), srcMap(sourceLoc), @@ -619,6 +618,7 @@ void Compiler::handleTypeError( Compiler::Compiler(std::vector source, VM &vm) : vm(vm), + gcGuard(vm.heap.pauseGC()), source(source), curSrcLoc({1, 0}), param(vm.heap.alloc()), diff --git a/src/runtime/Env.cpp b/src/runtime/Env.cpp index abad001..51f807c 100644 --- a/src/runtime/Env.cpp +++ b/src/runtime/Env.cpp @@ -72,4 +72,7 @@ bool Env::isNatFn(const sexpr::Sym *sym) { return natFns.find(sym) != natFns.end(); } -const SymTable &Env::getSymTable() const { return symTable; } +const std::unordered_map & +Env::getSymTable() const { + return symTable; +} diff --git a/src/runtime/Env.hpp b/src/runtime/Env.hpp index feb5f59..ef170c3 100644 --- a/src/runtime/Env.hpp +++ b/src/runtime/Env.hpp @@ -4,7 +4,6 @@ #include "../sexpr/NatFn.hpp" #include "../sexpr/SExpr.hpp" #include "../sexpr/Sym.hpp" -#include "Symtable.hpp" #include #include #include @@ -15,17 +14,9 @@ namespace runtime { class Env { private: - SymTable symTable; - std::unordered_set< - const sexpr::Sym *, - sexpr::Sym::HashFunction, - sexpr::Sym::EqualFunction> - macros; - std::unordered_set< - const sexpr::Sym *, - sexpr::Sym::HashFunction, - sexpr::Sym::EqualFunction> - natFns; + std::unordered_map symTable; + std::unordered_set macros; + std::unordered_set natFns; void regMacro(const sexpr::Sym *sym); void regNative(const sexpr::Sym *sym); @@ -45,7 +36,8 @@ class Env { bool isMacro(const sexpr::Sym *sym); bool isNatFn(const sexpr::Sym *sym); - const SymTable &getSymTable() const; + const std::unordered_map & + getSymTable() const; }; } // namespace runtime diff --git a/src/runtime/Heap.cpp b/src/runtime/Heap.cpp index 00ef261..28faa92 100644 --- a/src/runtime/Heap.cpp +++ b/src/runtime/Heap.cpp @@ -13,7 +13,7 @@ void Heap::gc() { return; } #ifndef GC_STRESS_TEST - if (heap.size() < gcHeapSize) { + if (getBytesAlloced() < maxHeapSize) { return; } #endif @@ -28,11 +28,26 @@ void Heap::gc() { trace(sexpr); } - std::erase_if(heap, [&](const auto &unique) { - return !black.contains(unique.get()); - }); + std::apply([&](auto &&...args) { (args.free(black), ...); }, allocators); - gcHeapSize = heap.size() * FREESTORE_HEAP_GROWTH_FACTOR; + maxHeapSize = getBytesAlloced() * HEAP_GROWTH_FACTOR; +} + +void Heap::markRoots() { + if (vm.getClosure().has_value()) { + grey.push_back(vm.getClosure().value()); + } + + for (const auto &[sym, sExpr] : vm.getSymTable()) { + grey.push_back(sym); + grey.push_back(sExpr); + } + for (const auto &sExpr : vm.getStack()) { + grey.push_back(sExpr); + } + for (const auto &sExpr : vm.getCallFrames()) { + grey.push_back(sExpr.closure); + } } void Heap::mark(const SExpr *sexpr) { @@ -66,30 +81,16 @@ void Heap::trace(const SExpr *sExpr) { return; } } -void Heap::markRoots() { - if (vm.getClosure().has_value()) { - grey.push_back(vm.getClosure().value()); - } - for (const auto &[sym, sExpr] : vm.getSymTable()) { - grey.push_back(sym); - grey.push_back(sExpr); - } - for (const auto &sExpr : vm.getStack()) { - grey.push_back(sExpr); - } - for (const auto &sExpr : vm.getCallFrames()) { - grey.push_back(sExpr.closure); - } -} +Heap::Heap(VM &vm) : vm(vm), enableGC(true), maxHeapSize(INIT_HEAP_SIZE) {} -Heap::Heap(VM &vm) : vm(vm) { - for (Num::ValueType i{FREESTORE_INT_CACHE_MIN}; i <= FREESTORE_INT_CACHE_MAX; - i++) { - numCache.push_back(std::make_unique(i)); - } +std::size_t Heap::getBytesAlloced() { + std::size_t bytesAlloced = 0; + std::apply( + [&](auto &&...args) { ((bytesAlloced += args.getBytesAlloced()), ...); }, + allocators + ); + return bytesAlloced; } -GCGuard Heap::startGC() { return GCGuard(*this); } - GCGuard Heap::pauseGC() { return GCGuard(*this); } diff --git a/src/runtime/Heap.hpp b/src/runtime/Heap.hpp index cea1aab..58ed9dc 100644 --- a/src/runtime/Heap.hpp +++ b/src/runtime/Heap.hpp @@ -2,19 +2,18 @@ #define LISP_SRC_RUNTIME_FREESTORE_HPP_ #include "../sexpr/Bool.hpp" +#include "../sexpr/Closure.hpp" +#include "../sexpr/NatFn.hpp" #include "../sexpr/Nil.hpp" #include "../sexpr/Num.hpp" #include "../sexpr/SExpr.hpp" +#include "../sexpr/SExprs.hpp" +#include "../sexpr/String.hpp" #include "../sexpr/Undefined.hpp" -#include +#include "Suballocator.hpp" #include #include -#define FREESTORE_HEAP_GROWTH_FACTOR 2 -#define FREESTORE_INIT_HEAP_SIZE 512 -#define FREESTORE_INT_CACHE_MAX 256.0 -#define FREESTORE_INT_CACHE_MIN -16.0 - namespace runtime { class VM; @@ -26,11 +25,17 @@ class Heap { private: VM &vm; + std::tuple< + Suballocator, + Suballocator, + Suballocator, + Suballocator, + Suballocator, + Suballocator, + Suballocator> + allocators; bool enableGC; - size_t gcHeapSize; - - std::vector> heap; - std::vector> numCache; + size_t maxHeapSize; std::unordered_set black; std::deque grey; @@ -41,17 +46,18 @@ class Heap { void trace(const sexpr::SExpr *sexpr); public: + static constexpr std::size_t INIT_HEAP_SIZE = 1; + static constexpr std::size_t HEAP_GROWTH_FACTOR = 2; + Heap(VM &vm); - GCGuard startGC(); + std::size_t getBytesAlloced(); GCGuard pauseGC(); template const T *alloc(Args &&...args) { gc(); - auto unique = std::make_unique(std::forward(args)...); - const auto &ref = *unique.get(); - heap.emplace_back(std::move(unique)); - return &ref; + return std::get>(allocators) + .alloc(std::forward(args)...); } }; template <> inline const sexpr::Undefined *Heap::alloc() { @@ -64,20 +70,6 @@ template <> inline const sexpr::Bool *Heap::alloc(sexpr::Bool::ValueType &&val) { return sexpr::Bool::getInstance(val); } -template <> inline const sexpr::Num *Heap::alloc(sexpr::Num::ValueType &val) { - if (val >= FREESTORE_INT_CACHE_MIN && val <= FREESTORE_INT_CACHE_MAX && - floor(val) == val) { - return numCache.at(val - FREESTORE_INT_CACHE_MIN).get(); - } - gc(); - auto unique = std::make_unique(val); - const auto &ref = *unique; - heap.emplace_back(std::move(unique)); - return &ref; -} -template <> inline const sexpr::Num *Heap::alloc(double &&val) { - return alloc(val); -} } // namespace runtime diff --git a/src/runtime/Suballocator.hpp b/src/runtime/Suballocator.hpp new file mode 100644 index 0000000..57b2d70 --- /dev/null +++ b/src/runtime/Suballocator.hpp @@ -0,0 +1,128 @@ +#ifndef LISP_SRC_RUNTIME_ALLOCATOR_HPP_ +#define LISP_SRC_RUNTIME_ALLOCATOR_HPP_ + +#include "../sexpr/NatFn.hpp" +#include "../sexpr/Num.hpp" +#include "../sexpr/SExpr.hpp" +#include "../sexpr/Sym.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace runtime { + +template class Suballocator { +private: + std::list> handles; + +public: + std::size_t getBytesAlloced() { return sizeof(T) * handles.size(); } + + template const T *alloc(Args &&...args) { + return handles + .emplace_back(std::make_unique(std::forward(args)...)) + .get(); + } + + void free(const std::unordered_set &reachable) { + std::erase_if(handles, [&](const auto &handle) { + return !reachable.contains(handle.get()); + }); + } +}; + +template <> class Suballocator { +private: + std::list> handles; + std::unordered_map memo; + +public: + std::size_t getBytesAlloced() { return memo.size() * sizeof(sexpr::Sym); } + + const sexpr::Sym *alloc(const sexpr::Sym::ValueType v) { + if (memo.contains(v)) { + return memo.at(v); + } + + memo.insert(std::make_pair( + v, handles.emplace_back(std::make_unique(v)).get() + )); + return memo.at(v); + } + + void free(const std::unordered_set &reachable) { + std::erase_if(memo, [&](const auto &it) { + return !reachable.contains(it.second); + }); + std::erase_if(handles, [&](const auto &handle) { + return !reachable.contains(handle.get()); + }); + } +}; + +template <> class Suballocator { +private: + std::list> handles; + std::vector> cache; + +public: + static constexpr sexpr::Num::ValueType CACHE_MIN_VAL = -512.0; + static constexpr sexpr::Num::ValueType CACHE_MAX_VAL = 512.0; + + Suballocator() { + cache.reserve(CACHE_MAX_VAL - CACHE_MIN_VAL + 1); + for (auto i{CACHE_MIN_VAL}; i <= CACHE_MAX_VAL; ++i) { + cache.emplace_back(std::make_unique(i)); + } + } + + std::size_t getBytesAlloced() { + return sizeof(sexpr::Num) * (handles.size() + cache.size()); + } + + const sexpr::Num *alloc(const sexpr::Num::ValueType v) { + if (v >= CACHE_MIN_VAL && v <= CACHE_MAX_VAL && floor(v) == v) { + return cache[v - CACHE_MIN_VAL].get(); + } + + return handles.emplace_back(std::make_unique(v)).get(); + } + + void free(const std::unordered_set &reachable) { + std::erase_if(handles, [&](const auto &handle) { + return !reachable.contains(handle.get()); + }); + } +}; + +template <> class Suballocator { +private: + std::list> handles; + +public: + std::size_t getBytesAlloced() { + return sizeof(sexpr::NatFn) * handles.size(); + } + + template const sexpr::NatFn *alloc(Args &&...args) { + return handles + .emplace_back(std::make_unique(std::forward(args)... + )) + .get(); + } + + void free(const std::unordered_set &) {} +}; + +} // namespace runtime + +#endif diff --git a/src/runtime/Symset.hpp b/src/runtime/Symset.hpp deleted file mode 100644 index 9ab4e1b..0000000 --- a/src/runtime/Symset.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef LISP_SRC_RUNTIME_SYMSET_HPP_ -#define LISP_SRC_RUNTIME_SYMSET_HPP_ - -#include "../sexpr/Sym.hpp" -#include - -namespace runtime { - -using Symset = std::unordered_set< - const sexpr::Sym *, - sexpr::Sym::HashFunction, - sexpr::Sym::EqualFunction>; - -} - -#endif diff --git a/src/runtime/Symtable.hpp b/src/runtime/Symtable.hpp deleted file mode 100644 index 37d039b..0000000 --- a/src/runtime/Symtable.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef LISP_SRC_RUNTIME_SYMTABLE_HPP_ -#define LISP_SRC_RUNTIME_SYMTABLE_HPP_ - -#include "../sexpr/Sym.hpp" -#include - -namespace runtime { - -using SymTable = std::unordered_map< - const sexpr::Sym *, - const sexpr::SExpr *, - sexpr::Sym::HashFunction, - sexpr::Sym::EqualFunction>; - -} - -#endif diff --git a/src/runtime/VM.cpp b/src/runtime/VM.cpp index 7a7d7f2..a1d9548 100644 --- a/src/runtime/VM.cpp +++ b/src/runtime/VM.cpp @@ -10,7 +10,6 @@ #include "Heap.hpp" #include "StackIter.hpp" #include "StackPtr.hpp" -#include "Symtable.hpp" #include #include #include @@ -245,7 +244,7 @@ POP_JUMP_IF_FALSE: { goto *dispatchTable[readByte()]; MAKE_LIST: { - auto gcGuard = heap.pauseGC(); + const auto gcGuard = heap.pauseGC(); const auto start = stack.begin() + bp + readByte(); const auto &list = makeList(start); @@ -259,6 +258,7 @@ MAKE_NIL: { stack.push_back(heap.alloc()); } } VM::VM() : ip(0), bp(0), heap(*this) { + const auto gcGuard = heap.pauseGC(); env.defNatFns( {{heap.alloc("symbol?"), heap.alloc(typePred, 1, false)}, {heap.alloc("gensym"), heap.alloc(genSym, 0, false)}} @@ -335,7 +335,7 @@ VM::VM() : ip(0), bp(0), heap(*this) { void VM::load(const Prototype *main) { reset(); - auto gcGuard = heap.pauseGC(); + const auto gcGuard = heap.pauseGC(); closure = heap.alloc(main); stack.push_back(closure.value()); call(0); @@ -364,4 +364,7 @@ const std::vector &VM::getStack() const { return stack; } const std::vector &VM::getCallFrames() const { return callFrames; } -const SymTable &VM::getSymTable() const { return env.getSymTable(); } +const std::unordered_map & +VM::getSymTable() const { + return env.getSymTable(); +} diff --git a/src/runtime/VM.hpp b/src/runtime/VM.hpp index 66a9ad3..692d2e9 100644 --- a/src/runtime/VM.hpp +++ b/src/runtime/VM.hpp @@ -63,7 +63,8 @@ class VM { const std::optional &getClosure() const; const std::vector &getStack() const; const std::vector &getCallFrames() const; - const SymTable &getSymTable() const; + const std::unordered_map & + getSymTable() const; }; } // namespace runtime diff --git a/src/sexpr/Sym.cpp b/src/sexpr/Sym.cpp index 746b0db..7b544ca 100644 --- a/src/sexpr/Sym.cpp +++ b/src/sexpr/Sym.cpp @@ -14,14 +14,7 @@ bool Sym::equals(const SExpr &other) const { return false; } -size_t Sym::HashFunction::operator()(const Sym *sym) const { return sym->hash; } - -bool Sym::EqualFunction::operator()(const Sym *lhs, const Sym *rhs) const { - return lhs->hash == rhs->hash; -} - -Sym::Sym(const ValueType val) - : Atom(SExpr::Type::SYM), val(val), hash(std::hash()(val)) {} +Sym::Sym(const ValueType val) : Atom(SExpr::Type::SYM), val(val) {} bool Sym::classOf(const SExpr *sExpr) { return sExpr->type == SExpr::Type::SYM; diff --git a/src/sexpr/Sym.hpp b/src/sexpr/Sym.hpp index 69ab22a..b50052e 100644 --- a/src/sexpr/Sym.hpp +++ b/src/sexpr/Sym.hpp @@ -13,20 +13,9 @@ class Sym final : public Atom { bool equals(const SExpr &other) const override; public: - class HashFunction { - public: - size_t operator()(const Sym *sym) const; - }; - - class EqualFunction { - public: - bool operator()(const Sym *lhs, const Sym *rhs) const; - }; - using ValueType = std::string; const ValueType val; - const size_t hash; explicit Sym(const ValueType val);