refactor(furvm): improve things

Refs: #62
This commit is contained in:
2026-08-15 22:15:35 +02:00
parent c59f0252ed
commit a382b92cf0
6 changed files with 99 additions and 89 deletions
+1 -1
View File
@@ -13,7 +13,7 @@
static void print_thing(const furvm::thing<>& thing) { static void print_thing(const furvm::thing<>& thing) {
using namespace furvm; using namespace furvm;
switch (thing.true_type().type) { switch (thing.type().type) {
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break; case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break; case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break; case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;
+1
View File
@@ -7,6 +7,7 @@
#include "furvm/module.hpp" // IWYU pragma: keep #include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/thing.hpp" // IWYU pragma: keep #include "furvm/thing.hpp" // IWYU pragma: keep
#include <cstddef>
#include <utility> #include <utility>
#include <vector> #include <vector>
-2
View File
@@ -171,8 +171,6 @@ private:
thing_type thing_type_impl(mod_h mod, mod_type type) const; 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_type* mod_to_thing_type(const mod_h& mod, const mod_type& type) const;
thing<> make_reference(const thing<>& thing) const;
private: private:
static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs); static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs);
private: private:
+85 -68
View File
@@ -7,6 +7,7 @@
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include <algorithm> #include <algorithm>
#include <cassert>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <functional> #include <functional>
@@ -214,32 +215,47 @@ public:
* @param allocator Allocator for the thing's data. * @param allocator Allocator for the thing's data.
*/ */
thing(const thing_type& type, const allocator_type& allocator = {}) thing(const thing_type& type, const allocator_type& allocator = {})
: m_type(type), m_size(compute_size(type)), m_allocator(allocator) { : m_size(compute_size_na(type)), m_allocator(allocator) {
if (m_type.type == thing_type::Ref) return; assert(type.type != thing_type::Ref);
// TODO: Account for alignment allocate(type);
m_data = m_allocator.allocate(sizeof(header) + m_size); }
header* hdr = reinterpret_cast<header*>(m_data); /* NOTE: Furvm forbids allocating references on the heap.
hdr->type = type; * This limitation is required for the current implementation of references.
* Essentialy, references are special things that point directly to other thing's data.
m_data += sizeof(header); * The distinction between a reference and the owner is stored inside the reference's
std::memset(m_data, 0, m_size); * 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. * @brief Destructs a thing.
*/ */
~thing() { ~thing() {
if (m_type.type != thing_type::Ref && m_data != nullptr && m_size > 0) if (!m_reference && m_data != nullptr) m_allocator.deallocate(m_data - sizeof(header), m_size + sizeof(header));
m_allocator.deallocate(m_data - sizeof(header), m_size);
} }
/** /**
* @brief Move constructor. * @brief Move constructor.
*/ */
thing(thing&& other) noexcept 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)) { : m_reference(other.m_reference),
other.m_type.type = thing_type::Count; 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_data = nullptr;
other.m_size = 0; other.m_size = 0;
} }
@@ -249,38 +265,41 @@ public:
*/ */
thing& operator=(thing&& other) noexcept { thing& operator=(thing&& other) noexcept {
if (this == &other) return *this; if (this == &other) return *this;
m_reference = other.m_reference;
m_type = other.m_type; m_type = other.m_type;
m_size = other.m_size; m_size = other.m_size;
m_data = other.m_data; m_data = other.m_data;
m_allocator = std::move(other.m_allocator); m_allocator = std::move(other.m_allocator);
other.m_type.type = thing_type::Count; other.m_type = nullptr;
other.m_data = nullptr; other.m_data = nullptr;
other.m_size = 0; other.m_size = 0;
return *this; return *this;
} }
thing(const thing& other) thing(const thing& other)
: m_type(other.m_type), m_size(other.m_size), m_allocator(other.m_allocator) { : m_reference(other.m_reference), m_size(other.m_size), m_allocator(other.m_allocator) {
if (m_type.type == thing_type::Ref) { if (m_reference) {
m_type = other.m_type;
m_data = other.m_data; m_data = other.m_data;
return; return;
} }
m_data = m_allocator.allocate(m_size); allocate(other.type());
other.copy(*this); other.copy(*this);
} }
thing& operator=(const thing& other) { thing& operator=(const thing& other) {
if (this == &other) return *this; if (this == &other) return *this;
m_type = other.m_type; m_reference = other.m_reference;
m_size = other.m_size; m_size = other.m_size;
m_allocator = std::move(other.m_allocator); 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; m_data = other.m_data;
return *this; return *this;
} }
m_data = m_allocator.allocate(m_size); allocate(other.type());
other.copy(*this); other.copy(*this);
return *this; return *this;
@@ -297,7 +316,7 @@ public:
} }
private: private:
void copy(thing<>& dst) const { void copy(thing<>& dst) const {
switch (m_type.type) { switch (m_type->type) {
case thing_type::S8: case thing_type::S8:
case thing_type::S16: case thing_type::S16:
case thing_type::S32: case thing_type::S32:
@@ -307,8 +326,8 @@ private:
case thing_type::U32: case thing_type::U32:
case thing_type::U64: case thing_type::U64:
case thing_type::Ptr: std::memcpy(dst.m_data, m_data, m_size); return; 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::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::Ref: // TODO: Implement arrays of references (I think they're possible).
case thing_type::Count: break; case thing_type::Count: break;
} }
throw std::runtime_error("unreachable"); throw std::runtime_error("unreachable");
@@ -319,16 +338,7 @@ public:
* *
* @return The type. * @return The type.
*/ */
constexpr thing_type type() const { return m_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; }
/** /**
* @brief Checks if the thing is of a specified type. * @brief Checks if the thing is of a specified type.
@@ -338,21 +348,21 @@ public:
* @param type Type to compare. * @param type Type to compare.
* @return true if the types match. * @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: public:
/** /**
* @brief Returns a raw data pointer. * @brief Returns a raw data pointer.
* *
* @return The data pointer. * @return The data pointer.
*/ */
void* raw() { return m_data; } std::byte* raw() { return m_data; }
/** /**
* @brief Returns a raw data pointer. * @brief Returns a raw data pointer.
* *
* @return The data pointer. * @return The data pointer.
*/ */
const void* raw() const { return m_data; } const std::byte* raw() const { return m_data; }
public: public:
/** /**
* @brief Returns the thing's value. * @brief Returns the thing's value.
@@ -361,7 +371,7 @@ public:
*/ */
template <typename T> template <typename T>
T& get() { 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<T*>(m_data)); return *std::launder(reinterpret_cast<T*>(m_data));
} }
@@ -372,7 +382,7 @@ public:
*/ */
template <typename T> template <typename T>
const T& get() const { 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<const T*>(m_data)); return *std::launder(reinterpret_cast<const T*>(m_data));
} }
public: public:
@@ -470,7 +480,7 @@ public:
* @return The integer value. * @return The integer value.
*/ */
thing_type::s64 integer() const { thing_type::s64 integer() const {
switch (true_type().type) { switch (type().type) {
case thing_type::S8: return get<thing_type::s8>(); case thing_type::S8: return get<thing_type::s8>();
case thing_type::S16: return get<thing_type::s16>(); case thing_type::S16: return get<thing_type::s16>();
case thing_type::S32: return get<thing_type::s32>(); case thing_type::S32: return get<thing_type::s32>();
@@ -485,11 +495,11 @@ public:
void resize(thing_type::u64 newSize) { void resize(thing_type::u64 newSize) {
if (!is(thing_type::Array)) throw bad_thing_access(); 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<dynamic_array>(); auto& array = get<dynamic_array>();
if (newSize < 0 || newSize == array.size) return; 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::byte* newData = new std::byte[innerSize * newSize];
std::memcpy(newData, array.data, innerSize * std::min(static_cast<thing_type::u64>(array.size), newSize)); std::memcpy(newData, array.data, innerSize * std::min(static_cast<thing_type::u64>(array.size), newSize));
array.size = newSize; array.size = newSize;
@@ -500,24 +510,29 @@ public:
thing at(thing_type::u64 index) const { thing at(thing_type::u64 index) const {
if (!is(thing_type::Array)) throw bad_thing_access(); if (!is(thing_type::Array)) throw bad_thing_access();
std::size_t elementSize = compute_size_na(*true_type().value.array.type); thing ref = {};
if (true_type().value.array.size == 0) { ref.m_reference = true;
ref.m_size = compute_size_na(*type().value.array.type);
if (type().value.array.size == 0) {
auto& array = get<dynamic_array>(); auto& array = get<dynamic_array>();
if (index < 0 || index >= array.size) throw std::out_of_range("index out of range"); 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; return ref;
} }
if (index < 0 || index >= true_type().value.array.size) throw std::out_of_range("index out of range"); if (index < 0 || index >= 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); ref.m_type = type().value.array.type;
ref.m_data = m_data + (index * ref.m_size);
return ref; return ref;
} }
thing_type::u64 length() const { thing_type::u64 length() const {
if (!is(thing_type::Array)) throw bad_thing_access(); if (!is(thing_type::Array)) throw bad_thing_access();
return true_type().value.array.size == 0 ? get<dynamic_array>().size : true_type().value.array.size; return type().value.array.size == 0 ? get<dynamic_array>().size : type().value.array.size;
} }
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
@@ -525,18 +540,6 @@ public:
return visit_primitive([](auto value) { return static_cast<T>(value); }); return visit_primitive([](auto value) { return static_cast<T>(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. * @brief Self-explainatory.
* *
@@ -544,9 +547,9 @@ public:
*/ */
void assign(thing&& thing) { void assign(thing&& thing) {
class thing rhs = std::move(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 // TODO: Move this to another function
switch (true_type().type) { switch (type().type) {
case thing_type::S8: case thing_type::S8:
case thing_type::S16: case thing_type::S16:
case thing_type::S32: case thing_type::S32:
@@ -621,10 +624,10 @@ private:
case thing_type::U32: return sizeof(thing_type::u32); case thing_type::U32: return sizeof(thing_type::u32);
case thing_type::U64: return sizeof(thing_type::u64); case thing_type::U64: return sizeof(thing_type::u64);
case thing_type::Ptr: return sizeof(void*); case thing_type::Ptr: return sizeof(void*);
case thing_type::Ref: return compute_size_na(*type.value.typeRef);
case thing_type::Array: case thing_type::Array:
return type.value.array.size == 0 ? sizeof(dynamic_array) return type.value.array.size == 0 ? sizeof(dynamic_array)
: compute_size_na(*type.value.array.type) * type.value.array.size; : compute_size_na(*type.value.array.type) * type.value.array.size;
case thing_type::Ref:
case thing_type::Count: break; case thing_type::Count: break;
} }
@@ -636,7 +639,7 @@ private:
private: private:
template <typename Func> template <typename Func>
decltype(auto) visit_primitive(Func&& func) const { decltype(auto) visit_primitive(Func&& func) const {
switch (true_type().type) { switch (type().type) {
case thing_type::S8: return std::forward<Func>(func)(get<thing_type::s8>()); case thing_type::S8: return std::forward<Func>(func)(get<thing_type::s8>());
case thing_type::S16: return std::forward<Func>(func)(get<thing_type::s16>()); case thing_type::S16: return std::forward<Func>(func)(get<thing_type::s16>());
case thing_type::S32: return std::forward<Func>(func)(get<thing_type::s32>()); case thing_type::S32: return std::forward<Func>(func)(get<thing_type::s32>());
@@ -651,7 +654,7 @@ private:
template <typename Op> template <typename Op>
thing binary_op(const thing& rhs, const Op& op) const { 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] = { static constexpr enum thing_type::type promotions[8 * 8] = {
// S8 // S8
thing_type::S8, thing_type::S8,
@@ -727,7 +730,7 @@ private:
thing_type::U64, 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 }; thing res = { thing_type{ resultType }, m_allocator };
switch (resultType) { switch (resultType) {
@@ -766,7 +769,21 @@ private:
throw std::runtime_error("unexpected operation"); throw std::runtime_error("unexpected operation");
} }
private: 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<header*>(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::size_t m_size = 0;
std::byte* m_data = nullptr; std::byte* m_data = nullptr;
+3 -9
View File
@@ -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); 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) { bool executor::compare_thing_types(const thing_type& lhs, const thing_type& rhs) {
if (lhs.type != rhs.type) return false; if (lhs.type != rhs.type) return false;
switch (lhs.type) { switch (lhs.type) {
@@ -245,7 +239,7 @@ void executor::step() {
push_thing(top_thing()); push_thing(top_thing());
} break; } break;
case instruction_t::Reference: { case instruction_t::Reference: {
push_thing(std::move(make_reference(pop_thing()))); push_thing(thing<>::make_reference(pop_thing()));
} break; } break;
case instruction_t::Add: { case instruction_t::Add: {
auto rhs = pop_thing(); auto rhs = pop_thing();
@@ -336,13 +330,13 @@ void executor::step() {
push_thing({ (struct thing_type){ thing_type::U64 } }).get<thing_type::u64>() = thing.length(); push_thing({ (struct thing_type){ thing_type::U64 } }).get<thing_type::u64>() = thing.length();
} break; } break;
case instruction_t::Load: { 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; } break;
case instruction_t::Store: { case instruction_t::Store: {
store_thing(instr.arg.u16, std::move(pop_thing())); store_thing(instr.arg.u16, std::move(pop_thing()));
} break; } break;
case instruction_t::LoadGlobal: { 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; } break;
case instruction_t::StoreGlobal: { case instruction_t::StoreGlobal: {
frame.mod->store_global_variable(instr.arg.u16, std::move(pop_thing())); frame.mod->store_global_variable(instr.arg.u16, std::move(pop_thing()));
+1 -1
View File
@@ -13,7 +13,7 @@
static void print_thing(const furvm::thing<>& thing) { static void print_thing(const furvm::thing<>& thing) {
using namespace furvm; using namespace furvm;
switch (thing.true_type().type) { switch (thing.type().type) {
case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break; case thing_type::S8: std::cout << thing.cast_to<thing_type::s16>(); break;
case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break; case thing_type::S16: std::cout << thing.get<thing_type::s16>(); break;
case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break; case thing_type::S32: std::cout << thing.get<thing_type::s32>(); break;