#include "furvm/executor.hpp" #include "furvm/context.hpp" // IWYU pragma: keep #include "furvm/exceptions.hpp" #include "furvm/function.hpp" // IWYU pragma: keep #include "furvm/fwd.hpp" #include "furvm/instruction.hpp" #include "furvm/thing.hpp" #include #include namespace furvm { executor::executor(egzekutor, executor_handle id, const context_p& context) : m_id(id), m_context(context) {} executor::~executor() { m_context->m_executors[m_id] = nullptr; } executor_p executor::create(const context_p& context) { auto ex = std::make_shared(egzekutor{}, context->m_executors.size(), context); context->m_executors.push_back(ex); return std::move(ex); } void executor::push_frame(const function_p& function) { if (function->type() != function_t::Normal) return; m_frames.emplace((struct executor::frame){ function->mod(), function->position(), m_stack.size() }); } struct executor::frame executor::pop_frame() { if (m_frames.empty()) throw stack_underflow(); struct executor::frame frame = m_frames.top(); m_frames.pop(); return frame; } struct executor::frame executor::frame() const { return m_frames.top(); } void executor::push_thing(const thing_p& thing) { thing->add_reference(); m_stack.push(thing); } void executor::push_thing(thing_p&& thing) { thing->add_reference(); m_stack.push(std::move(thing)); } thing_p executor::pop_thing() { if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow(); thing_p top = std::move(m_stack.top()); m_stack.pop(); top->remove_reference(); return top; } thing_p executor::thing() const { if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow(); return m_stack.top(); } void executor::store_thing(variable_t variable, const thing_p& 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_p&& 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); } thing_p executor::load_thing(variable_t variable) const { const auto& frame = m_frames.top(); return frame.variables[variable]; } void executor::step() { if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return; struct frame& frame = m_frames.top(); instruction_t instr = static_cast(frame.mod->byte(frame.position++)); switch (instr) { case instruction_t::NoOperation: break; case instruction_t::PushB2I: { auto thing = thing::create(m_context, thing_t::Int32); thing->int32() = frame.mod->byte(frame.position++); push_thing(std::move(thing)); } break; case instruction_t::Drop: { pop_thing(); } break; case instruction_t::Duplicate: { push_thing(thing()); } break; case instruction_t::Clone: { push_thing(std::move(thing::clone(thing()))); } break; case instruction_t::Add: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs + rhs); } break; case instruction_t::Sub: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs - rhs); } break; case instruction_t::Mul: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs * rhs); } break; case instruction_t::Div: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs / rhs); } break; case instruction_t::Mod: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs % rhs); } break; case instruction_t::Equals: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs == rhs); } break; case instruction_t::NotEquals: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs != rhs); } break; case instruction_t::LessThan: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs < rhs); } break; case instruction_t::GreaterThan: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs > rhs); } break; case instruction_t::LessEqual: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs <= rhs); } break; case instruction_t::GreaterEqual: { auto rhs = pop_thing(); auto lhs = pop_thing(); push_thing(lhs >= rhs); } break; case instruction_t::Load: { variable_t variable = static_cast(frame.mod->byte(frame.position)) | (static_cast(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(frame.mod->byte(frame.position)) | (static_cast(frame.mod->byte(frame.position + 1)) << 8); frame.position += 2; store_thing(variable, std::move(pop_thing())); } break; case instruction_t::Call: { function_handle funcId = static_cast(frame.mod->byte(frame.position)) | (static_cast(frame.mod->byte(frame.position + 1)) << 8); frame.position += 2; const function_p& function = frame.mod->function_at(funcId); switch (function->type()) { case function_t::Normal: push_frame(function); break; case function_t::Native: function->native()(*this); break; case function_t::Import: { const mod_p& impMod = m_context->m_modules.at(function->imported_module()); push_frame(impMod->function_at(function->imported_function())); } break; } } break; case instruction_t::Jump: { frame.position += frame.mod->byte(frame.position++); } break; case instruction_t::JumpNotZero: { byte offset = frame.mod->byte(frame.position++); auto cond = pop_thing(); if (cond->int32() != 0) frame.position += offset; } break; case instruction_t::Return: { pop_frame(); if (m_frames.empty()) m_flags = m_flags | executor_flags::Done; } break; case instruction_t::ReturnValue: { auto value = pop_thing(); pop_frame(); push_thing(std::move(value)); } break; case instruction_t::PushConstant: throw std::runtime_error("unimplemented"); default: throw std::runtime_error("unknown instruction"); } } } // namespace furvm