diff --git a/furvm/include/furvm/thing.hpp b/furvm/include/furvm/thing.hpp index 9231132..bcf43b6 100644 --- a/furvm/include/furvm/thing.hpp +++ b/furvm/include/furvm/thing.hpp @@ -28,19 +28,10 @@ public: /** * @brief Constructs a thing. * - * @param type Thing type. + * @param type Thing type reference. * @param allocator Allocator for the thing's data. */ - thing(const type& type, const mod_container& modules = nullptr, const allocator_type& allocator = {}) - : thing(std::make_shared(type), modules, allocator) {} - - /** - * @brief Constructs a thing. - * - * @param type Thing type. - * @param allocator Allocator for the thing's data. - */ - thing(const type_p& type, const mod_container& modules = nullptr, const allocator_type& allocator = {}) + thing(const type_ref& type, const mod_container& modules = nullptr, const allocator_type& allocator = {}) : m_type(type), m_size(compute_size(resolve_type(type, modules))), m_modules(modules), m_allocator(allocator) { // TODO: Account for alignment m_data = m_allocator.allocate(m_size); @@ -94,15 +85,16 @@ public: * @return A clone of this thing. */ thing clone() const { - auto type = resolve_type(m_type, m_modules); + auto type = resolve_type(m_type, m_modules); + if (m_size == 0) { + return reference(); + } + thing res(type, m_modules, m_allocator); switch (type->t) { case type_t::Primitive: - case type_t::Reference: { - std::memcpy(res.m_data, m_data, m_size); - } break; case type_t::Array: { - copy_list(type, res.get(), get()); + copy_list(*type.type, res.get(), get()); } break; case type_t::Import: default: throw std::runtime_error("unreachable"); @@ -111,18 +103,18 @@ public: } public: /** - * @brief Returns the thing's type. + * @brief Returns the thing's type reference. * - * @return The type. - */ - constexpr auto type() const { return *m_type; } - - /** - * @brief Returns the thing's type. - * - * @return The shared pointer to the type. + * @return The type reference. */ auto type() { return m_type; } + + /** + * @brief Returns the thing's type reference. + * + * @return The type reference. + */ + constexpr auto type() const { return m_type; } public: /** * @brief Returns a raw data pointer. @@ -266,18 +258,9 @@ public: } } - thing reference() const { - thing res = { std::make_shared(m_type), m_modules, m_allocator }; - res.get() = m_data; - return std::move(res); - } + thing reference() const { return { m_type, m_data, m_modules, m_allocator }; } - thing resolve() const { - thing rsv = { resolve_type(m_type, m_modules), m_data, m_allocator }; - while (rsv.type()->t == type_t::Reference) - rsv = { rsv.m_type->reference, rsv.get(), std::move(rsv.m_allocator) }; - return rsv; - } + constexpr bool is_reference() const { return m_size == 0; } void resize(long_t newSize) { if (m_type->t != type_t::Array) throw bad_thing_access(); @@ -297,18 +280,18 @@ public: if (m_type->t != type_t::Array) throw bad_thing_access(); std::size_t elementSize = compute_size_na(*m_type->array.type); - thing res = { std::make_shared(*m_type->array.type), m_modules, m_allocator }; if (m_type->array.size == 0) { auto& array = get(); if (index < 0 || index >= array.dynamic.size) throw std::out_of_range("index out of range"); - res.get() = array.dynamic.data + (index * elementSize); - return std::move(res); + return { { m_type.mod, m_type->array.type }, + array.dynamic.data + (index * elementSize), + m_modules, + m_allocator }; } std::byte* data = reinterpret_cast(m_data)->data; if (index < 0 || index >= m_type->array.size) throw std::out_of_range("index out of range"); - res.get() = data + (index * elementSize); - return std::move(res); + return { { m_type.mod, m_type->array.type }, data + (index * elementSize), m_modules, m_allocator }; } std::size_t size() const { @@ -317,10 +300,12 @@ public: return m_type->array.size; } public: - static type_p resolve_type(const type_p& initType, const mod_container& modules) { - type_p rsv = initType; - while (rsv->t == type_t::Import) - rsv = *modules->at(initType->imp.mod)->type_at(initType->imp.type); + static type_ref resolve_type(const type_ref& initType, const mod_container& modules) { + type_ref rsv = initType; + while (rsv->t == type_t::Import) { + auto mod = modules->at(initType->imp.mod); + rsv = type_ref{ mod, mod->type_at(initType->imp.type) }; + } return rsv; } private: @@ -350,8 +335,7 @@ private: } switch (innerType->t) { - case type_t::Primitive: - case type_t::Reference: std::memcpy(dst.data, src.data, size); break; + case type_t::Primitive: std::memcpy(dst.data, src.data, size); break; case type_t::Array: for (std::size_t i = 0; i < size; ++i) { copy_list(*innerType->array.type, @@ -366,7 +350,6 @@ private: static std::size_t compute_size_na(const type_p& type) { switch (type->t) { case type_t::Primitive: return type->primitive; - case type_t::Reference: return sizeof(reference_t); case type_t::Array: { if (type->array.size == 0) return sizeof(array_t); return compute_size_na(*type->array.type) * type->array.size; @@ -377,23 +360,24 @@ private: throw std::runtime_error("unreachable"); } + static std::size_t compute_size_na(const type_ref& type) { return compute_size_na(*type.type); } + // NOTE: Align to 4 bytes static std::size_t compute_size(const type_p& type) { return (compute_size_na(type) + 3) & ~3; } + static std::size_t compute_size(const type_ref& type) { return compute_size(*type.type); } private: - thing(const type_p& type, std::byte* data, const allocator_type& allocator = {}) - : m_type(type), m_size(0), m_data(data), m_allocator(allocator) {} + thing(const type_ref& type, std::byte* data, const mod_container& modules, const allocator_type& allocator = {}) + : m_type(type), m_size(0), m_data(data), m_modules(modules), m_allocator(allocator) {} private: template thing binary_op(const thing& rhs, const Op& op) const { - const thing lhsRsv = resolve(); - const thing rhsRsv = rhs.resolve(); + if (m_type->t == type_t::Primitive && rhs.type()->t == type_t::Primitive) { + type_ref type = m_type->primitive >= rhs.type()->primitive ? m_type : rhs.type(); + std::size_t size = std::max(m_type->primitive, rhs.type()->primitive); - if (lhsRsv.type().t == type_t::Primitive && rhsRsv.type().t == type_t::Primitive) { - std::size_t size = std::max(lhsRsv.type().primitive, rhsRsv.type().primitive); + long_t result = op(integer(), rhs.integer()); - long_t result = op(lhsRsv.integer(), rhsRsv.integer()); - - thing res({ size }, m_modules, m_allocator); + thing res(type, m_modules, m_allocator); switch (size) { case sizeof(byte_t): res.get() = result; break; case sizeof(short_t): res.get() = result; break; @@ -407,7 +391,7 @@ private: throw std::runtime_error("unexpected operation"); } private: - type_p m_type; + type_ref m_type; std::size_t m_size; std::byte* m_data; diff --git a/furvm/include/furvm/type.hpp b/furvm/include/furvm/type.hpp index c8eb551..6e18d46 100644 --- a/furvm/include/furvm/type.hpp +++ b/furvm/include/furvm/type.hpp @@ -10,14 +10,12 @@ namespace furvm { enum class type_t : std::uint32_t { Primitive = 0, - Reference, Array, Import, }; using primitive_type = std::uint64_t; -using reference_type = type_p; /** * @brief Array type. @@ -41,21 +39,17 @@ struct import_type { struct type { type_t t; union { - primitive_type primitive; - reference_type reference; + primitive_type primitive{}; array_type array; import_type imp; }; type(type_t type) - : t(type), primitive(0) {} + : t(type) {} type(primitive_type primitive) : t(type_t::Primitive), primitive(primitive) {} - type(const reference_type& reference) - : t(type_t::Reference), reference(reference) {} - type(const type_h& type, std::size_t size = 0) : t(type_t::Array), array(array_type{ type, size }) {} @@ -67,7 +61,6 @@ struct type { ~type() { switch (t) { - case type_t::Reference: reference.~reference_type(); break; case type_t::Array: array.~array_type(); break; case type_t::Import: imp.~import_type(); break; default: break; @@ -78,7 +71,6 @@ struct type { : t(other.t) { switch (t) { case type_t::Primitive: primitive = other.primitive; break; - case type_t::Reference: reference = std::move(other.reference); break; case type_t::Array: array = std::move(other.array); break; case type_t::Import: imp = std::move(other.imp); break; } @@ -89,7 +81,6 @@ struct type { t = other.t; switch (t) { case type_t::Primitive: primitive = other.primitive; break; - case type_t::Reference: reference = std::move(other.reference); break; case type_t::Array: array = std::move(other.array); break; case type_t::Import: imp = std::move(other.imp); break; } @@ -101,7 +92,6 @@ struct type { : t(other.t) { switch (t) { case type_t::Primitive: primitive = other.primitive; break; - case type_t::Reference: reference = other.reference; break; case type_t::Array: array = other.array; break; case type_t::Import: imp = other.imp; break; } @@ -112,7 +102,6 @@ struct type { t = other.t; switch (t) { case type_t::Primitive: primitive = other.primitive; break; - case type_t::Reference: reference = other.reference; break; case type_t::Array: array = other.array; break; case type_t::Import: imp = other.imp; break; } @@ -126,8 +115,6 @@ using short_t = std::int16_t; /**< A 2-byte integer. */ using int_t = std::int32_t; /**< A 4-byte integer. */ using long_t = std::int64_t; /**< An 8-byte integer. */ -using reference_t = std::byte*; - /** * @brief Array type's data layout. */ @@ -159,6 +146,17 @@ inline static type intType = { sizeof(int_t) }; // NOLINT */ inline static type longType = { sizeof(long_t) }; // NOLINT +/** + * @brief Reference to a type. + */ +struct type_ref { + mod_h mod; + type_h type; + + class type* operator->() { return &**type; } + const class type* operator->() const { return &**type; } +}; + } // namespace furvm #endif // FURVM_TYPE_HPP diff --git a/furvm/src/executor.cpp b/furvm/src/executor.cpp index 5956af4..9c79263 100644 --- a/furvm/src/executor.cpp +++ b/furvm/src/executor.cpp @@ -83,6 +83,10 @@ thing_h executor::load_thing(variable_t variable) const { return frame.variables[variable]; } +static type_ref get_type_ref(const mod_h& mod, type_id id) { + return { mod, mod->type_at(id) }; +} + void executor::step() { if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return; @@ -92,7 +96,7 @@ void executor::step() { switch (instr) { case instruction_t::NoOperation: break; case instruction_t::PushB2I: { - push_thing({ *m_context->at("core")->type_at(2), m_context, m_context->thing_alloc() })->get() = + push_thing({ get_type_ref(m_context->at("core"), 2), m_context, m_context->thing_alloc() })->get() = frame.mod->byte(frame.position++); } break; case instruction_t::Array: { @@ -102,8 +106,8 @@ void executor::step() { (static_cast(frame.mod->byte(frame.position + 3)) << 24); frame.position += 4; - auto type = furvm::thing<>::resolve_type(*frame.mod->type_at(typeId), m_context); - if (type == nullptr || type->t != type_t::Array || *type->array.type == nullptr) + auto type = furvm::thing<>::resolve_type(get_type_ref(frame.mod, typeId), m_context); + if (*type.type == nullptr || type->t != type_t::Array || *type->array.type == nullptr) throw std::runtime_error("invalid array type"); auto array = push_thing({ type, m_context, m_context->thing_alloc() }); @@ -190,29 +194,27 @@ void executor::step() { push_thing(lhs->greater_equals(*rhs)); } break; case instruction_t::Pointerof: { - auto thing = pop_thing()->resolve(); - auto ptr = push_thing({ *m_context->at("core")->type_at(4), m_context, m_context->thing_alloc() }); - switch (furvm::thing<>::resolve_type(thing.type(), m_context)->t) { - case type_t::Primitive: ptr->get() = reinterpret_cast(thing.raw()); break; + auto thing = pop_thing(); + auto ptr = push_thing({ get_type_ref(m_context->at("core"), 4), m_context, m_context->thing_alloc() }); + switch (furvm::thing<>::resolve_type(thing->type(), m_context)->t) { + case type_t::Primitive: ptr->get() = reinterpret_cast(thing->raw()); break; case type_t::Array: - ptr->get() = reinterpret_cast(thing.get().data); + ptr->get() = reinterpret_cast(thing->get().data); break; - case type_t::Reference: case type_t::Import: default: throw std::runtime_error("unreachable"); } } break; case instruction_t::Sizeof: { - auto thing = pop_thing()->resolve(); - auto size = push_thing({ *m_context->at("core")->type_at(3), m_context, m_context->thing_alloc() }); - auto type = furvm::thing<>::resolve_type(thing.type(), m_context); + auto thing = pop_thing(); + auto size = push_thing({ get_type_ref(m_context->at("core"), 3), m_context, m_context->thing_alloc() }); + auto type = furvm::thing<>::resolve_type(thing->type(), m_context); switch (type->t) { case type_t::Primitive: size->get() = static_cast(type->primitive); break; case type_t::Array: size->get() = - (type->array.size == 0) ? thing.get().dynamic.size : static_cast(type->array.size); + (type->array.size == 0) ? thing->get().dynamic.size : static_cast(type->array.size); break; - case type_t::Reference: case type_t::Import: default: throw std::runtime_error("unreachable"); } diff --git a/furvm/src/main.cpp b/furvm/src/main.cpp index 3ec0e3e..6fdae55 100644 --- a/furvm/src/main.cpp +++ b/furvm/src/main.cpp @@ -11,16 +11,15 @@ #include static void print_thing(const furvm::thing& thing) { - auto rsv = thing.resolve(); - switch (rsv.type()->t) { + switch (thing.type()->t) { case furvm::type_t::Primitive: { - std::cout << rsv.integer(); + std::cout << thing.integer(); } break; case furvm::type_t::Array: { std::cout << "{ "; - for (furvm::long_t i = 0; i < rsv.size(); ++i) { + for (furvm::long_t i = 0; i < thing.size(); ++i) { if (i > 0) std::cout << ", "; - print_thing(rsv.at(i)); + print_thing(thing.at(i)); } std::cout << " }\n"; } break; @@ -52,6 +51,7 @@ int main(int argc, char** argv) { 0, 0, 0, + static_cast(furvm::instruction_t::Reference), static_cast(furvm::instruction_t::Call), 1, 0, diff --git a/furvm/src/module.cpp b/furvm/src/module.cpp index bfd4a13..dea96be 100644 --- a/furvm/src/module.cpp +++ b/furvm/src/module.cpp @@ -56,7 +56,6 @@ std::ostream& mod::serialize(std::ostream& os) const { auto type = *m_types.at(id); switch (type->t) { case type_t::Primitive: detail::serialize(os, type->primitive); break; - case type_t::Reference: throw std::runtime_error("reference type serialization is unimplemented"); case type_t::Array: { detail::serialize(os, type->array.size); detail::serialize(os, type->array.type.id()); @@ -134,7 +133,6 @@ mod mod::load(std::istream& is) { detail::load(is, primitive); mod.emplace_type(id, std::make_shared(primitive)).dispatch(); } break; - case type_t::Reference: throw std::runtime_error("reference type serialization is unimplemented"); case type_t::Array: { std::size_t size = 0; detail::load(is, size);