refactor(furvm): improve the handle system

Absolute slop

Refs: #12
This commit is contained in:
2026-06-18 23:58:37 +02:00
parent e9b38e95a2
commit ec2c81fc56
13 changed files with 641 additions and 617 deletions
+3 -7
View File
@@ -4,13 +4,9 @@
namespace furvm {
context::context() {}
context::context()
: m_thingAllocator(m_thingArena) {}
void context::collect() {
for (auto& ref : m_things) {
if (ref->reference_count() != 0) continue;
ref = nullptr;
}
}
void context::collect() {}
} // namespace furvm
+26 -43
View File
@@ -28,21 +28,14 @@ struct executor::frame executor::frame() const {
return m_frames.top();
}
void executor::push_thing(const thing_h& thing) {
(*thing)->add_reference();
m_stack.push(thing);
}
void executor::push_thing(thing_h&& thing) {
(*thing)->add_reference();
m_stack.push(std::move(thing));
thing_h executor::push_thing(::furvm::thing<>&& thing) {
return m_stack.emplace(m_context->emplace_thing(std::move(thing)));
}
thing_h executor::pop_thing() {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
thing_h top = std::move(m_stack.top());
m_stack.pop();
(*top)->remove_reference();
return top;
}
@@ -54,20 +47,12 @@ thing_h executor::thing() const {
void executor::store_thing(variable_t variable, const thing_h& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if (*frame.variables[variable] != nullptr) {
(*frame.variables[variable])->remove_reference();
}
frame.variables[variable] = thing;
(*thing)->add_reference();
}
void executor::store_thing(variable_t variable, thing_h&& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if ((*frame.variables[variable]) != nullptr) {
(*frame.variables[variable])->remove_reference();
}
(*thing)->add_reference();
frame.variables[variable] = std::move(thing);
}
@@ -81,13 +66,11 @@ void executor::step() {
struct frame& frame = m_frames.top();
instruction_t instr = static_cast<instruction_t>((*frame.mod)->byte(frame.position++));
instruction_t instr = static_cast<instruction_t>((*frame.mod).byte(frame.position++));
switch (instr) {
case instruction_t::NoOperation: break;
case instruction_t::PushB2I: {
auto thing = m_context->emplace_thing(thing_t::Int32);
(*thing)->int32() = (*frame.mod)->byte(frame.position++);
push_thing(std::move(thing));
push_thing({ thing_t::Int32, m_context->m_thingAllocator })->int32() = frame.mod->byte(frame.position++);
} break;
case instruction_t::Drop: {
pop_thing();
@@ -96,81 +79,81 @@ void executor::step() {
push_thing(thing());
} break;
case instruction_t::Clone: {
push_thing(std::move(thing::clone(m_context, thing())));
push_thing(std::move(thing()->clone()));
} break;
case instruction_t::Add: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->add(m_context, rhs));
push_thing(lhs->add(*rhs));
} break;
case instruction_t::Sub: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->sub(m_context, rhs));
push_thing(lhs->sub(*rhs));
} break;
case instruction_t::Mul: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->mul(m_context, rhs));
push_thing(lhs->mul(*rhs));
} break;
case instruction_t::Div: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->div(m_context, rhs));
push_thing(lhs->div(*rhs));
} break;
case instruction_t::Mod: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->mod(m_context, rhs));
push_thing(lhs->mod(*rhs));
} break;
case instruction_t::Equals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->equals(m_context, rhs));
push_thing(lhs->equals(*rhs));
} break;
case instruction_t::NotEquals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->not_equals(m_context, rhs));
push_thing(lhs->not_equals(*rhs));
} break;
case instruction_t::LessThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->less_than(m_context, rhs));
push_thing(lhs->less_than(*rhs));
} break;
case instruction_t::GreaterThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->greater_than(m_context, rhs));
push_thing(lhs->greater_than(*rhs));
} break;
case instruction_t::LessEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->less_equals(m_context, rhs));
push_thing(lhs->less_equals(*rhs));
} break;
case instruction_t::GreaterEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing((*lhs)->greater_equals(m_context, rhs));
push_thing(lhs->greater_equals(*rhs));
} break;
case instruction_t::Load: {
variable_t variable = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
push_thing(load_thing(variable));
} break;
case instruction_t::Store: {
variable_t variable = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
store_thing(variable, std::move(pop_thing()));
} break;
case instruction_t::Call: {
function_id funcId = static_cast<std::uint16_t>((*frame.mod)->byte(frame.position)) |
(static_cast<std::uint16_t>((*frame.mod)->byte(frame.position + 1)) << 8);
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
const function_h& function = (*frame.mod)->function_at(funcId);
const function_h& function = frame.mod->function_at(funcId);
switch (function->type()) {
case function_t::Normal: push_frame(frame.mod, function); break;
case function_t::Native: function->native()(*this); break;
@@ -182,12 +165,12 @@ void executor::step() {
}
} break;
case instruction_t::Jump: {
frame.position += (*frame.mod)->byte(frame.position++);
frame.position += frame.mod->byte(frame.position++);
} break;
case instruction_t::JumpNotZero: {
byte offset = (*frame.mod)->byte(frame.position++);
byte offset = frame.mod->byte(frame.position++);
auto cond = pop_thing();
if ((*cond)->int32() != 0) frame.position += offset;
if (cond->int32() != 0) frame.position += offset;
} break;
case instruction_t::Return: {
pop_frame();
+2 -1
View File
@@ -5,6 +5,7 @@
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include "furvm/instruction.hpp"
#include "furvm/module.hpp"
#include "furvm/serializer.hpp"
#include "furvm/thing.hpp"
@@ -26,7 +27,7 @@ int main(void) {
auto context = std::make_shared<furvm::context>();
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end());
furvm::function_h mainFunc = (*mainModule)->emplace_function("main", 0);
furvm::function_h mainFunc = mainModule->emplace_function("main", 0);
furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(mainModule, mainFunc);
+24 -25
View File
@@ -6,7 +6,6 @@
#include <istream>
#include <ostream>
#include <stdexcept>
namespace furvm {
@@ -68,32 +67,32 @@ static inline void read_u8(std::istream& is, std::uint8_t& value) {
}
bool serializer::serialize_module(std::ostream& os, const mod_p& mod) {
os.write(reinterpret_cast<const char*>(MODULE_MAGIC), sizeof(MODULE_MAGIC));
write_u32(os, VERSION);
// os.write(reinterpret_cast<const char*>(MODULE_MAGIC), sizeof(MODULE_MAGIC));
// write_u32(os, VERSION);
write_u32(os, mod->m_functions.size());
for (function_id id = 0; id < static_cast<function_id>(mod->m_functions.size()); ++id) {
const auto& func = mod->function_at(id);
write_u16(os, id);
write_u8(os, static_cast<std::uint8_t>(func->type()));
switch (func->type()) {
case function_t::Normal: {
write_u64(os, func->position());
} break;
case function_t::Native: {
// TODO: Replace with a reference to the function's name from constant pool
throw std::runtime_error("cannot serialize native functions yet");
} break;
case function_t::Import: {
// TODO: Replace with a reference to the module's and function's name from constant pool
throw std::runtime_error("cannot serialize import functions yet");
} break;
}
}
// write_u32(os, mod->m_functions.size());
// for (function_id id = 0; id < static_cast<function_id>(mod->m_functions.size()); ++id) {
// const auto& func = mod->function_at(id);
// write_u16(os, id);
// write_u8(os, static_cast<std::uint8_t>(func->type()));
// switch (func->type()) {
// case function_t::Normal: {
// write_u64(os, func->position());
// } break;
// case function_t::Native: {
// // TODO: Replace with a reference to the function's name from constant pool
// throw std::runtime_error("cannot serialize native functions yet");
// } break;
// case function_t::Import: {
// // TODO: Replace with a reference to the module's and function's name from constant pool
// throw std::runtime_error("cannot serialize import functions yet");
// } break;
// }
// }
write_u64(os, mod->m_bytecode.size());
os.write(reinterpret_cast<const char*>(mod->m_bytecode.data()),
static_cast<std::streamsize>(mod->m_bytecode.size()));
// write_u64(os, mod->m_bytecode.size());
// os.write(reinterpret_cast<const char*>(mod->m_bytecode.data()),
// static_cast<std::streamsize>(mod->m_bytecode.size()));
return false;
}
-228
View File
@@ -1,228 +0,0 @@
// NOLINTBEGIN(cppcoreguidelines-no-malloc)
#include "furvm/thing.hpp"
#include "furvm/context.hpp" // IWYU pragma: keep
#include <cstdlib>
#include <cstring>
#include <stdexcept>
namespace furvm {
std::size_t thing_type_size(thing_t type) {
switch (type) {
case thing_t::Int32: return 4;
}
return 0;
}
thing::thing(thing_t type)
: m_type(type) {
std::size_t size = thing_type_size(type);
byte* data = new byte[size];
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
std::memcpy(data, &type, sizeof(type));
m_data = data + sizeof(type);
switch (m_type) {
// Primitives are zero-initialized.
default:
case thing_t::Int32: std::memset(m_data, 0, size);
}
}
thing::thing(thing_t type, void* data)
: m_type(type), m_ownData(false) {
std::size_t size = thing_type_size(type);
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
std::memcpy(data, &type, sizeof(type));
m_data = static_cast<char*>(data) + sizeof(type);
switch (m_type) {
// Primitives are zero-initialized.
default:
case thing_t::Int32: std::memset(m_data, 0, size);
}
}
thing::~thing() {
switch (m_type) {
// Primitives are not destructed.
default:
case thing_t::Int32: break;
}
if (m_ownData) delete[] static_cast<char*>(m_data);
}
thing::thing(thing&& other) noexcept
: m_type(other.m_type), m_refCount(other.m_refCount), m_data(other.m_data) {
other.m_type = {};
other.m_ownData = false;
other.m_refCount = {};
other.m_data = nullptr;
}
thing& thing::operator=(thing&& other) noexcept {
if (this == &other) return *this;
m_type = other.m_type;
m_refCount = other.m_refCount;
m_data = other.m_data;
other.m_type = {};
other.m_refCount = {};
other.m_data = nullptr;
return *this;
}
static constexpr std::uint16_t thing_type_pair(thing_t lhs, thing_t rhs) {
return (static_cast<std::uint16_t>(lhs) << 8) | static_cast<std::uint16_t>(rhs);
}
thing_h thing::clone(const context_p& context, const thing_h& thing) {
auto th = context->emplace_thing((*thing)->m_type);
switch ((*thing)->m_type) {
// Primitives
default: {
memcpy((*th)->m_data, (*thing)->m_data, thing_type_size((*thing)->m_type));
}
}
return std::move(th);
}
std::int32_t& thing::int32() {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
const std::int32_t& thing::int32() const {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
thing_h thing::add(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = int32() + (*rhs)->int32();
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::sub(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = int32() - (*rhs)->int32();
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::mul(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = int32() * (*rhs)->int32();
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::div(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = int32() / (*rhs)->int32();
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::mod(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = int32() % (*rhs)->int32();
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::equals(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() == (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::not_equals(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() != (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::less_than(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() < (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::greater_than(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() > (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::less_equals(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() <= (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
thing_h thing::greater_equals(const context_p& context, const thing_h& rhs) const {
switch (thing_type_pair(m_type, (*rhs)->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
thing_h res = context->emplace_thing(thing_t::Int32);
(*res)->int32() = static_cast<std::int32_t>(int32() >= (*rhs)->int32());
return res;
}
default: throw std::runtime_error("unreachable");
}
}
} // namespace furvm
// NOLINTEND(cppcoreguidelines-no-malloc)