Compare commits
4 Commits
6737ed86b0
...
a382b92cf0
| Author | SHA1 | Date | |
|---|---|---|---|
|
a382b92cf0
|
|||
|
c59f0252ed
|
|||
|
5c841a1428
|
|||
|
6d176e58f3
|
+2
-2
@@ -10,10 +10,10 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
// taken from furvm uwu :3 ^^ nya~ ngh~
|
// taken from furvm uwu :3 ^^ nya~ ngh~
|
||||||
static void print_thing(const furvm::thing<furvm::thing_allocator>& 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,13 +1,11 @@
|
|||||||
#ifndef FURVM_CONTEXT_HPP
|
#ifndef FURVM_CONTEXT_HPP
|
||||||
#define FURVM_CONTEXT_HPP
|
#define FURVM_CONTEXT_HPP
|
||||||
|
|
||||||
#include "furlang/arena.hpp"
|
|
||||||
#include "furvm/executor.hpp"
|
#include "furvm/executor.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/handle.hpp"
|
#include "furvm/handle.hpp"
|
||||||
#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 "furvm/thing_allocator.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -22,8 +20,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Constructs a context.
|
* @brief Constructs a context.
|
||||||
*/
|
*/
|
||||||
context()
|
context() {}
|
||||||
: m_thingAllocator(m_thingArena) {}
|
|
||||||
|
|
||||||
~context() = default;
|
~context() = default;
|
||||||
|
|
||||||
@@ -70,21 +67,12 @@ public:
|
|||||||
|
|
||||||
const std::vector<executor>& executors() const { return m_executors; }
|
const std::vector<executor>& executors() const { return m_executors; }
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* @brief Returns context's thing allocator.
|
|
||||||
*
|
|
||||||
* @return The thing allocator.
|
|
||||||
*/
|
|
||||||
thing_allocator<std::byte> thing_alloc() const { return m_thingAllocator; }
|
|
||||||
|
|
||||||
thing_type_store& tt_store() { return m_thingTypeStore; }
|
thing_type_store& tt_store() { return m_thingTypeStore; }
|
||||||
private:
|
private:
|
||||||
handle_container<mod_h> m_modules;
|
handle_container<mod_h> m_modules;
|
||||||
std::vector<executor> m_executors;
|
std::vector<executor> m_executors;
|
||||||
|
|
||||||
furlang::arena m_thingArena;
|
class thing_type_store m_thingTypeStore;
|
||||||
thing_allocator<std::byte> m_thingAllocator;
|
|
||||||
class thing_type_store m_thingTypeStore;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace furvm
|
} // namespace furvm
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -140,11 +140,6 @@ using mod_id = std::string;
|
|||||||
*/
|
*/
|
||||||
using mod_h = handle<mod, refcount_header<mod_id>>;
|
using mod_h = handle<mod, refcount_header<mod_id>>;
|
||||||
|
|
||||||
// thing_allocator.hpp
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class thing_allocator;
|
|
||||||
|
|
||||||
// thing.hpp
|
// thing.hpp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +156,7 @@ using thing_type_id = std::uint32_t;
|
|||||||
*
|
*
|
||||||
* A stack element. Think of it like of a value in C++ or I guess a class in java.
|
* A stack element. Think of it like of a value in C++ or I guess a class in java.
|
||||||
*/
|
*/
|
||||||
template <template <typename> typename Allocator = thing_allocator>
|
template <template <typename> typename Allocator = std::allocator>
|
||||||
class thing;
|
class thing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
#include "furlang/utility/hash.hpp"
|
#include "furlang/utility/hash.hpp"
|
||||||
#include "furvm/exceptions.hpp"
|
#include "furvm/exceptions.hpp"
|
||||||
#include "furvm/fwd.hpp"
|
#include "furvm/fwd.hpp"
|
||||||
#include "furvm/thing_allocator.hpp" // IWYU pragma: keep
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -201,6 +201,10 @@ public:
|
|||||||
std::size_t size;
|
std::size_t size;
|
||||||
std::byte* data;
|
std::byte* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct header {
|
||||||
|
thing_type type;
|
||||||
|
};
|
||||||
public:
|
public:
|
||||||
thing() {}
|
thing() {}
|
||||||
|
|
||||||
@@ -211,28 +215,49 @@ 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(m_size);
|
}
|
||||||
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.
|
* @brief Destructs a thing.
|
||||||
*/
|
*/
|
||||||
~thing() {
|
~thing() {
|
||||||
if (m_type.type != thing_type::Ref && m_data != nullptr && m_size > 0) m_allocator.deallocate(m_data, m_size);
|
if (!m_reference && m_data != nullptr) m_allocator.deallocate(m_data - sizeof(header), m_size + sizeof(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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),
|
||||||
other.m_data = nullptr;
|
m_data(other.m_data),
|
||||||
other.m_size = 0;
|
m_size(other.m_size),
|
||||||
|
m_allocator(std::move(other.m_allocator)) {
|
||||||
|
other.m_type = nullptr;
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,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_type = other.m_type;
|
m_reference = other.m_reference;
|
||||||
m_size = other.m_size;
|
m_type = other.m_type;
|
||||||
m_data = other.m_data;
|
m_size = other.m_size;
|
||||||
m_allocator = std::move(other.m_allocator);
|
m_data = other.m_data;
|
||||||
other.m_type.type = thing_type::Count;
|
m_allocator = std::move(other.m_allocator);
|
||||||
other.m_data = nullptr;
|
other.m_type = nullptr;
|
||||||
other.m_size = 0;
|
other.m_data = nullptr;
|
||||||
|
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;
|
||||||
@@ -288,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:
|
||||||
@@ -298,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");
|
||||||
@@ -310,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.
|
||||||
@@ -329,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.
|
||||||
@@ -352,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,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:
|
||||||
@@ -461,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>();
|
||||||
@@ -476,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;
|
||||||
@@ -491,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>>>
|
||||||
@@ -516,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.
|
||||||
*
|
*
|
||||||
@@ -535,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:
|
||||||
@@ -612,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,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>());
|
||||||
@@ -642,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,
|
||||||
@@ -718,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) {
|
||||||
@@ -757,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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
#ifndef FURVM_THING_ALLOCATOR_HPP
|
|
||||||
#define FURVM_THING_ALLOCATOR_HPP
|
|
||||||
|
|
||||||
#include "furlang/arena.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace furvm {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class thing_allocator {
|
|
||||||
template <typename>
|
|
||||||
friend class thing_allocator;
|
|
||||||
|
|
||||||
using dead_things = std::vector<std::pair<T*, std::size_t>>;
|
|
||||||
public:
|
|
||||||
using value_type = T; /**< Value type. */
|
|
||||||
public:
|
|
||||||
thing_allocator() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructs a thing allocator.
|
|
||||||
*
|
|
||||||
* @param arena Base arena allocator.
|
|
||||||
*/
|
|
||||||
explicit thing_allocator(furlang::arena& arena) noexcept
|
|
||||||
: m_arena(&arena), m_deadThings(std::make_shared<dead_things>()) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator(thing_allocator<U>&& other) noexcept
|
|
||||||
: m_arena(std::move(other.m_arena)), m_deadThings(std::move(other.m_deadThings)) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator& operator=(thing_allocator<U>&& other) noexcept {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
m_arena = std::move(other.m_arena);
|
|
||||||
m_deadThings = std::move(other.m_deadThings);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copy constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator(const thing_allocator<U>& other) noexcept
|
|
||||||
: m_arena(other.m_arena), m_deadThings(other.m_deadThings) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copy constructor.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
thing_allocator& operator=(const thing_allocator<U>& other) noexcept {
|
|
||||||
if (this == &other) return *this;
|
|
||||||
m_arena = other.m_arena;
|
|
||||||
m_deadThings = other.m_deadThings;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Returns a free chunk of memory.
|
|
||||||
*
|
|
||||||
* @param count Count of the things that must fit inside the chunk.
|
|
||||||
* @return The chunk.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] T* allocate(std::size_t count = 1) {
|
|
||||||
for (auto it = m_deadThings->begin(); it != m_deadThings->end(); ++it) {
|
|
||||||
if (it->second != count) continue;
|
|
||||||
T* data = it->first;
|
|
||||||
m_deadThings->erase(it);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return m_arena->allocate<T>(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Recycles the pointer.
|
|
||||||
*/
|
|
||||||
void deallocate(T* ptr, std::size_t count) noexcept { m_deadThings->emplace_back(ptr, count); }
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Compares two thing allocators for equality.
|
|
||||||
*
|
|
||||||
* @return true if the two things are equal.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
bool operator==(const thing_allocator<U>& other) const noexcept {
|
|
||||||
return m_arena == other.m_arena && m_deadThings == other.m_deadThings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compares two thing allocators for inequality.
|
|
||||||
*
|
|
||||||
* @return true if the two things are not equal.
|
|
||||||
*/
|
|
||||||
template <typename U>
|
|
||||||
bool operator!=(const thing_allocator<U>& other) const noexcept {
|
|
||||||
return m_arena != other.m_arena || m_deadThings != other.m_deadThings;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
furlang::arena* m_arena = nullptr;
|
|
||||||
|
|
||||||
std::shared_ptr<dead_things> m_deadThings;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace furvm
|
|
||||||
|
|
||||||
#endif // FURVM_THING_ALLOCATOR_HPP
|
|
||||||
+15
-29
@@ -48,13 +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()) },
|
|
||||||
m_context->thing_alloc() };
|
|
||||||
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) {
|
||||||
@@ -188,27 +181,22 @@ void executor::step() {
|
|||||||
switch (instr.type) {
|
switch (instr.type) {
|
||||||
case instruction_t::NoOperation: break;
|
case instruction_t::NoOperation: break;
|
||||||
case instruction_t::PushS8: {
|
case instruction_t::PushS8: {
|
||||||
push_thing({ (struct thing_type){ thing_type::S8 }, m_context->thing_alloc() }).get<thing_type::s8>() =
|
push_thing({ (struct thing_type){ thing_type::S8 } }).get<thing_type::s8>() = instr.arg.s8;
|
||||||
instr.arg.s8;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU8: {
|
case instruction_t::PushU8: {
|
||||||
push_thing({ (struct thing_type){ thing_type::U8 }, m_context->thing_alloc() }).get<thing_type::u8>() =
|
push_thing({ (struct thing_type){ thing_type::U8 } }).get<thing_type::u8>() = instr.arg.u8;
|
||||||
instr.arg.u8;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushS16: {
|
case instruction_t::PushS16: {
|
||||||
push_thing({ (struct thing_type){ thing_type::S16 }, m_context->thing_alloc() }).get<thing_type::s16>() =
|
push_thing({ (struct thing_type){ thing_type::S16 } }).get<thing_type::s16>() = instr.arg.s16;
|
||||||
instr.arg.s16;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU16: {
|
case instruction_t::PushU16: {
|
||||||
push_thing({ (struct thing_type){ thing_type::U16 }, m_context->thing_alloc() }).get<thing_type::u16>() =
|
push_thing({ (struct thing_type){ thing_type::U16 } }).get<thing_type::u16>() = instr.arg.u16;
|
||||||
instr.arg.u16;
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushS32: {
|
case instruction_t::PushS32: {
|
||||||
push_thing({ (struct thing_type){ thing_type::S32 }, m_context->thing_alloc() }).get<thing_type::s32>() =
|
push_thing({ (struct thing_type){ thing_type::S32 } }).get<thing_type::s32>() = instr.arg.s8; // NOLINT
|
||||||
instr.arg.s8; // NOLINT
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::PushU32: {
|
case instruction_t::PushU32: {
|
||||||
push_thing({ (struct thing_type){ thing_type::U32 }, m_context->thing_alloc() }).get<thing_type::u32>() =
|
push_thing({ (struct thing_type){ thing_type::U32 } }).get<thing_type::u32>() =
|
||||||
static_cast<thing_type::u32>(instr.arg.u8);
|
static_cast<thing_type::u32>(instr.arg.u8);
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Array: {
|
case instruction_t::Array: {
|
||||||
@@ -216,7 +204,7 @@ void executor::step() {
|
|||||||
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
if (type.type != thing_type::Array || type.value.array.type == nullptr || type.value.array.type == &type)
|
||||||
throw std::runtime_error("invalid array type");
|
throw std::runtime_error("invalid array type");
|
||||||
|
|
||||||
auto& array = push_thing({ type, m_context->thing_alloc() });
|
auto& array = push_thing({ type });
|
||||||
|
|
||||||
if (type.value.array.size == 0) {
|
if (type.value.array.size == 0) {
|
||||||
auto sizeThing = pop_thing();
|
auto sizeThing = pop_thing();
|
||||||
@@ -251,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();
|
||||||
@@ -310,13 +298,12 @@ void executor::step() {
|
|||||||
} break;
|
} break;
|
||||||
case instruction_t::Pointerof: {
|
case instruction_t::Pointerof: {
|
||||||
auto thing = pop_thing();
|
auto thing = pop_thing();
|
||||||
push_thing({ (struct thing_type){ thing_type::Ptr, m_context->tt_store().at(thing.type().id) },
|
push_thing({ (struct thing_type){ thing_type::Ptr, m_context->tt_store().at(thing.type().id) } }).get<void*>() =
|
||||||
m_context->thing_alloc() })
|
thing.raw();
|
||||||
.get<void*>() = thing.raw();
|
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Sizeof: {
|
case instruction_t::Sizeof: {
|
||||||
auto thing = pop_thing();
|
auto thing = pop_thing();
|
||||||
auto& size = push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() });
|
auto& size = push_thing({ (struct thing_type){ thing_type::U64 } });
|
||||||
switch (thing.type().type) {
|
switch (thing.type().type) {
|
||||||
case thing_type::S8:
|
case thing_type::S8:
|
||||||
case thing_type::S16:
|
case thing_type::S16:
|
||||||
@@ -339,18 +326,17 @@ void executor::step() {
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case instruction_t::Lengthof: {
|
case instruction_t::Lengthof: {
|
||||||
auto thing = pop_thing();
|
auto thing = pop_thing();
|
||||||
push_thing({ (struct thing_type){ thing_type::U64 }, m_context->thing_alloc() }).get<thing_type::u64>() =
|
push_thing({ (struct thing_type){ thing_type::U64 } }).get<thing_type::u64>() = thing.length();
|
||||||
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
@@ -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;
|
||||||
|
|||||||
+3
-5
@@ -1,7 +1,6 @@
|
|||||||
#include "furlang/arena.hpp"
|
#include "furlang/arena.hpp"
|
||||||
#include "furvm/furvm.hpp"
|
#include "furvm/furvm.hpp"
|
||||||
#include "furvm/thing.hpp"
|
#include "furvm/thing.hpp"
|
||||||
#include "furvm/thing_allocator.hpp"
|
|
||||||
|
|
||||||
#include "gtest/gtest.h" // IWYU pragma: keep
|
#include "gtest/gtest.h" // IWYU pragma: keep
|
||||||
|
|
||||||
@@ -10,12 +9,11 @@ namespace {
|
|||||||
// TODO: Basic program tests (e.g. for loops)
|
// TODO: Basic program tests (e.g. for loops)
|
||||||
|
|
||||||
TEST(Things, Ops) {
|
TEST(Things, Ops) {
|
||||||
furlang::arena arena;
|
furlang::arena arena;
|
||||||
furvm::thing_allocator<std::byte> alloc{ arena };
|
|
||||||
|
|
||||||
furvm::thing lhs{ furvm::thing_type{ furvm::thing_type::U32 }, alloc };
|
furvm::thing lhs{ furvm::thing_type{ furvm::thing_type::U32 } };
|
||||||
lhs.get<furvm::thing_type::u32>() = 6;
|
lhs.get<furvm::thing_type::u32>() = 6;
|
||||||
furvm::thing rhs{ furvm::thing_type{ furvm::thing_type::U32 }, alloc };
|
furvm::thing rhs{ furvm::thing_type{ furvm::thing_type::U32 } };
|
||||||
rhs.get<furvm::thing_type::u32>() = 7;
|
rhs.get<furvm::thing_type::u32>() = 7;
|
||||||
|
|
||||||
auto res = lhs.add(rhs);
|
auto res = lhs.add(rhs);
|
||||||
|
|||||||
Reference in New Issue
Block a user