From a382b92cf0c48457455b7e26128d2331eb6d61a6 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sat, 15 Aug 2026 22:15:35 +0200 Subject: [PATCH] refactor(furvm): improve things Refs: #62 --- furdb/src/main.cpp | 2 +- furvm/include/furvm/context.hpp | 1 + furvm/include/furvm/executor.hpp | 2 - furvm/include/furvm/thing.hpp | 169 +++++++++++++++++-------------- furvm/src/executor.cpp | 12 +-- furvm/src/main.cpp | 2 +- 6 files changed, 99 insertions(+), 89 deletions(-) diff --git a/furdb/src/main.cpp b/furdb/src/main.cpp index 54c9c83..d604531 100644 --- a/furdb/src/main.cpp +++ b/furdb/src/main.cpp @@ -13,7 +13,7 @@ static void print_thing(const furvm::thing<>& thing) { using namespace furvm; - switch (thing.true_type().type) { + switch (thing.type().type) { case thing_type::S8: std::cout << thing.cast_to(); break; case thing_type::S16: std::cout << thing.get(); break; case thing_type::S32: std::cout << thing.get(); break; diff --git a/furvm/include/furvm/context.hpp b/furvm/include/furvm/context.hpp index a21a299..62b37bc 100644 --- a/furvm/include/furvm/context.hpp +++ b/furvm/include/furvm/context.hpp @@ -7,6 +7,7 @@ #include "furvm/module.hpp" // IWYU pragma: keep #include "furvm/thing.hpp" // IWYU pragma: keep +#include #include #include diff --git a/furvm/include/furvm/executor.hpp b/furvm/include/furvm/executor.hpp index b9f4ca5..6edb88a 100644 --- a/furvm/include/furvm/executor.hpp +++ b/furvm/include/furvm/executor.hpp @@ -171,8 +171,6 @@ private: thing_type thing_type_impl(mod_h mod, mod_type type) const; thing_type* mod_to_thing_type(const mod_h& mod, const mod_type& type) const; - - thing<> make_reference(const thing<>& thing) const; private: static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs); private: diff --git a/furvm/include/furvm/thing.hpp b/furvm/include/furvm/thing.hpp index dd50337..0f1e9cf 100644 --- a/furvm/include/furvm/thing.hpp +++ b/furvm/include/furvm/thing.hpp @@ -7,6 +7,7 @@ #include "furvm/fwd.hpp" #include +#include #include #include #include @@ -214,34 +215,49 @@ public: * @param allocator Allocator for the thing's data. */ thing(const thing_type& type, const allocator_type& allocator = {}) - : m_type(type), m_size(compute_size(type)), m_allocator(allocator) { - if (m_type.type == thing_type::Ref) return; - // TODO: Account for alignment - m_data = m_allocator.allocate(sizeof(header) + m_size); + : m_size(compute_size_na(type)), m_allocator(allocator) { + assert(type.type != thing_type::Ref); + allocate(type); + } - header* hdr = reinterpret_cast(m_data); - hdr->type = type; - - m_data += sizeof(header); - std::memset(m_data, 0, m_size); + /* NOTE: Furvm forbids allocating references on the heap. + * This limitation is required for the current implementation of references. + * Essentialy, references are special things that point directly to other thing's data. + * The distinction between a reference and the owner is stored inside the reference's + * thing instance, which makes it impossible to represent them on the heap; however, + * the same does not apply to the executor's stack, nor it should apply to compound + * types in the future. + * + * TODO: Reword the note above. + */ + static thing make_reference(const thing& owner) { + thing ref; + ref.m_reference = true; + ref.m_data = owner.m_data; + ref.m_type = owner.m_type; + ref.m_size = owner.m_size; + return ref; } /** * @brief Destructs a thing. */ ~thing() { - if (m_type.type != thing_type::Ref && m_data != nullptr && m_size > 0) - m_allocator.deallocate(m_data - sizeof(header), m_size); + if (!m_reference && m_data != nullptr) m_allocator.deallocate(m_data - sizeof(header), m_size + sizeof(header)); } /** * @brief Move constructor. */ thing(thing&& other) noexcept - : m_type(other.m_type), m_data(other.m_data), m_size(other.m_size), m_allocator(std::move(other.m_allocator)) { - other.m_type.type = thing_type::Count; - other.m_data = nullptr; - other.m_size = 0; + : m_reference(other.m_reference), + m_type(other.m_type), + m_data(other.m_data), + m_size(other.m_size), + m_allocator(std::move(other.m_allocator)) { + other.m_type = nullptr; + other.m_data = nullptr; + other.m_size = 0; } /** @@ -249,38 +265,41 @@ public: */ thing& operator=(thing&& other) noexcept { if (this == &other) return *this; - m_type = other.m_type; - m_size = other.m_size; - m_data = other.m_data; - m_allocator = std::move(other.m_allocator); - other.m_type.type = thing_type::Count; - other.m_data = nullptr; - other.m_size = 0; + m_reference = other.m_reference; + m_type = other.m_type; + m_size = other.m_size; + m_data = other.m_data; + m_allocator = std::move(other.m_allocator); + other.m_type = nullptr; + other.m_data = nullptr; + other.m_size = 0; return *this; } thing(const thing& other) - : m_type(other.m_type), m_size(other.m_size), m_allocator(other.m_allocator) { - if (m_type.type == thing_type::Ref) { + : m_reference(other.m_reference), m_size(other.m_size), m_allocator(other.m_allocator) { + if (m_reference) { + m_type = other.m_type; m_data = other.m_data; return; } - m_data = m_allocator.allocate(m_size); + allocate(other.type()); other.copy(*this); } thing& operator=(const thing& other) { if (this == &other) return *this; - m_type = other.m_type; + m_reference = other.m_reference; m_size = other.m_size; m_allocator = std::move(other.m_allocator); - if (m_type.type == thing_type::Ref) { + if (m_reference) { + m_type = other.m_type; m_data = other.m_data; return *this; } - m_data = m_allocator.allocate(m_size); + allocate(other.type()); other.copy(*this); return *this; @@ -297,7 +316,7 @@ public: } private: void copy(thing<>& dst) const { - switch (m_type.type) { + switch (m_type->type) { case thing_type::S8: case thing_type::S16: case thing_type::S32: @@ -307,8 +326,8 @@ private: case thing_type::U32: case thing_type::U64: case thing_type::Ptr: std::memcpy(dst.m_data, m_data, m_size); return; - case thing_type::Array: copy_list(m_type, dst.m_data, m_data); return; - case thing_type::Ref: throw std::runtime_error("cannot copy references"); + case thing_type::Array: copy_list(*m_type, dst.m_data, m_data); return; + case thing_type::Ref: // TODO: Implement arrays of references (I think they're possible). case thing_type::Count: break; } throw std::runtime_error("unreachable"); @@ -319,16 +338,7 @@ public: * * @return The type. */ - constexpr thing_type type() const { return m_type; } - - /** - * @brief Returns the thing's true type. - * - * If the thing is a reference, returns the referenced type. - * - * @return The true type. - */ - constexpr thing_type true_type() const { return (m_type.type == thing_type::Ref) ? *m_type.value.typeRef : m_type; } + constexpr thing_type type() const { return *m_type; } /** * @brief Checks if the thing is of a specified type. @@ -338,21 +348,21 @@ public: * @param type Type to compare. * @return true if the types match. */ - constexpr bool is(enum thing_type::type type) const { return true_type().type == type; } + constexpr bool is(enum thing_type::type type) const { return m_type->type == type; } public: /** * @brief Returns a raw data pointer. * * @return The data pointer. */ - void* raw() { return m_data; } + std::byte* raw() { return m_data; } /** * @brief Returns a raw data pointer. * * @return The data pointer. */ - const void* raw() const { return m_data; } + const std::byte* raw() const { return m_data; } public: /** * @brief Returns the thing's value. @@ -361,7 +371,7 @@ public: */ template T& get() { - if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access(); + if (compute_size_na(*m_type) != sizeof(T)) throw bad_thing_access(); return *std::launder(reinterpret_cast(m_data)); } @@ -372,7 +382,7 @@ public: */ template const T& get() const { - if (compute_size_na(m_type) != sizeof(T)) throw bad_thing_access(); + if (compute_size_na(*m_type) != sizeof(T)) throw bad_thing_access(); return *std::launder(reinterpret_cast(m_data)); } public: @@ -470,7 +480,7 @@ public: * @return The integer value. */ thing_type::s64 integer() const { - switch (true_type().type) { + switch (type().type) { case thing_type::S8: return get(); case thing_type::S16: return get(); case thing_type::S32: return get(); @@ -485,11 +495,11 @@ public: void resize(thing_type::u64 newSize) { if (!is(thing_type::Array)) throw bad_thing_access(); - if (true_type().value.array.size > 0) throw std::runtime_error("cannot resize a static array"); + if (type().value.array.size > 0) throw std::runtime_error("cannot resize a static array"); auto& array = get(); if (newSize < 0 || newSize == array.size) return; - std::size_t innerSize = compute_size_na(*true_type().value.array.type); + std::size_t innerSize = compute_size_na(*type().value.array.type); std::byte* newData = new std::byte[innerSize * newSize]; std::memcpy(newData, array.data, innerSize * std::min(static_cast(array.size), newSize)); array.size = newSize; @@ -500,24 +510,29 @@ public: thing at(thing_type::u64 index) const { if (!is(thing_type::Array)) throw bad_thing_access(); - std::size_t elementSize = compute_size_na(*true_type().value.array.type); - if (true_type().value.array.size == 0) { + thing ref = {}; + ref.m_reference = true; + ref.m_size = compute_size_na(*type().value.array.type); + + if (type().value.array.size == 0) { auto& array = get(); if (index < 0 || index >= array.size) throw std::out_of_range("index out of range"); - thing ref = { { thing_type::Ref, true_type().value.array.type }, m_allocator }; - ref.m_data = array.data + (index * elementSize); + + ref.m_type = type().value.array.type; + ref.m_data = array.data + (index * ref.m_size); return ref; } - if (index < 0 || index >= true_type().value.array.size) throw std::out_of_range("index out of range"); - thing ref = { { thing_type::Ref, true_type().value.array.type }, m_allocator }; - ref.m_data = m_data + (index * elementSize); + if (index < 0 || index >= type().value.array.size) throw std::out_of_range("index out of range"); + + ref.m_type = type().value.array.type; + ref.m_data = m_data + (index * ref.m_size); return ref; } thing_type::u64 length() const { if (!is(thing_type::Array)) throw bad_thing_access(); - return true_type().value.array.size == 0 ? get().size : true_type().value.array.size; + return type().value.array.size == 0 ? get().size : type().value.array.size; } template >> @@ -525,18 +540,6 @@ public: return visit_primitive([](auto value) { return static_cast(value); }); } - /** - * @brief Changes reference thing's referenced thing. - * - * Yes. - * - * @param thing Thing. - */ - void reference(const thing& thing) { - if (m_type.type != thing_type::Ref || *m_type.value.typeRef != thing.type()) throw bad_thing_access(); - m_data = thing.m_data; - } - /** * @brief Self-explainatory. * @@ -544,9 +547,9 @@ public: */ void assign(thing&& thing) { class thing rhs = std::move(thing); - if (true_type() != rhs.true_type()) throw std::runtime_error("thing type mismatch"); + if (type() != rhs.type()) throw std::runtime_error("thing type mismatch"); // TODO: Move this to another function - switch (true_type().type) { + switch (type().type) { case thing_type::S8: case thing_type::S16: case thing_type::S32: @@ -621,10 +624,10 @@ private: case thing_type::U32: return sizeof(thing_type::u32); case thing_type::U64: return sizeof(thing_type::u64); case thing_type::Ptr: return sizeof(void*); - case thing_type::Ref: return compute_size_na(*type.value.typeRef); case thing_type::Array: return type.value.array.size == 0 ? sizeof(dynamic_array) : compute_size_na(*type.value.array.type) * type.value.array.size; + case thing_type::Ref: case thing_type::Count: break; } @@ -636,7 +639,7 @@ private: private: template decltype(auto) visit_primitive(Func&& func) const { - switch (true_type().type) { + switch (type().type) { case thing_type::S8: return std::forward(func)(get()); case thing_type::S16: return std::forward(func)(get()); case thing_type::S32: return std::forward(func)(get()); @@ -651,7 +654,7 @@ private: template thing binary_op(const thing& rhs, const Op& op) const { - if (thing_type::is_primitive(true_type().type) && thing_type::is_primitive(true_type().type)) { + if (thing_type::is_primitive(type().type) && thing_type::is_primitive(type().type)) { static constexpr enum thing_type::type promotions[8 * 8] = { // S8 thing_type::S8, @@ -727,7 +730,7 @@ private: thing_type::U64, }; - enum thing_type::type resultType = promotions[true_type().type + (rhs.true_type().type * 8)]; + enum thing_type::type resultType = promotions[type().type + (rhs.type().type * 8)]; thing res = { thing_type{ resultType }, m_allocator }; switch (resultType) { @@ -766,7 +769,21 @@ private: throw std::runtime_error("unexpected operation"); } private: - thing_type m_type; + void allocate(const thing_type& type) { + m_data = m_allocator.allocate(sizeof(header) + compute_size(type)); + + header* hdr = reinterpret_cast(m_data); + hdr->type = type; + m_type = &hdr->type; + + m_data += sizeof(header); + std::memset(m_data, 0, m_size); + } +private: + // A flag indicating whether the thing instance owns the data, or not. + bool m_reference = false; + + thing_type* m_type = nullptr; std::size_t m_size = 0; std::byte* m_data = nullptr; diff --git a/furvm/src/executor.cpp b/furvm/src/executor.cpp index 4cfb689..e3e42be 100644 --- a/furvm/src/executor.cpp +++ b/furvm/src/executor.cpp @@ -48,12 +48,6 @@ thing_type* executor::mod_to_thing_type(const mod_h& mod, const mod_type& type) return m_context->tt_store().insert(thingType); } -thing<> executor::make_reference(const thing<>& thing) const { - furvm::thing<> ref = { (struct thing_type){ thing_type::Ref, m_context->tt_store().insert(thing.type()) } }; - ref.reference(thing); - return std::move(ref); -} - bool executor::compare_thing_types(const thing_type& lhs, const thing_type& rhs) { if (lhs.type != rhs.type) return false; switch (lhs.type) { @@ -245,7 +239,7 @@ void executor::step() { push_thing(top_thing()); } break; case instruction_t::Reference: { - push_thing(std::move(make_reference(pop_thing()))); + push_thing(thing<>::make_reference(pop_thing())); } break; case instruction_t::Add: { auto rhs = pop_thing(); @@ -336,13 +330,13 @@ void executor::step() { push_thing({ (struct thing_type){ thing_type::U64 } }).get() = thing.length(); } break; case instruction_t::Load: { - push_thing(make_reference(load_thing(instr.arg.u16))); + push_thing(std::move(thing<>::make_reference(load_thing(instr.arg.u16)))); } break; case instruction_t::Store: { store_thing(instr.arg.u16, std::move(pop_thing())); } break; case instruction_t::LoadGlobal: { - push_thing(make_reference(frame.mod->load_global_variable(instr.arg.u16))); + push_thing(thing<>::make_reference(frame.mod->load_global_variable(instr.arg.u16))); } break; case instruction_t::StoreGlobal: { frame.mod->store_global_variable(instr.arg.u16, std::move(pop_thing())); diff --git a/furvm/src/main.cpp b/furvm/src/main.cpp index 78a9170..91accef 100644 --- a/furvm/src/main.cpp +++ b/furvm/src/main.cpp @@ -13,7 +13,7 @@ static void print_thing(const furvm::thing<>& thing) { using namespace furvm; - switch (thing.true_type().type) { + switch (thing.type().type) { case thing_type::S8: std::cout << thing.cast_to(); break; case thing_type::S16: std::cout << thing.get(); break; case thing_type::S32: std::cout << thing.get(); break;