8702298cd2
Define native functions inside the module instead of the function itself (native functions hold only the key to the definition), introduce private functions, add parameter count to function and fix some bugs. Closes: #20 Closes: #25
201 lines
6.6 KiB
C++
201 lines
6.6 KiB
C++
#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 <cstdint>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace furvm {
|
|
|
|
void executor::push_frame(const mod_h& mod, function function) {
|
|
mod_h modInst = mod;
|
|
while (function.type() == function_t::Import) {
|
|
modInst = m_context->module_at(function.imp().mod);
|
|
function = *modInst->function_at(function.imp().function);
|
|
}
|
|
|
|
std::vector<thing_h> args;
|
|
args.reserve(function.param_count());
|
|
for (size_t i = 0; i < function.param_count(); ++i)
|
|
args.push_back(pop_thing());
|
|
|
|
switch (function.type()) {
|
|
case function_t::Normal: {
|
|
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size(), std::move(args) });
|
|
} break;
|
|
case function_t::Native: {
|
|
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), std::move(args) });
|
|
modInst->get_native_function(function.name())(*this);
|
|
m_frames.pop();
|
|
} break;
|
|
default: throw std::runtime_error("unexpected function type");
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
return top;
|
|
}
|
|
|
|
thing_h 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_h& thing) {
|
|
auto& frame = m_frames.top();
|
|
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
|
|
frame.variables[variable] = thing;
|
|
}
|
|
|
|
void executor::store_thing(variable_t variable, thing_h&& thing) {
|
|
auto& frame = m_frames.top();
|
|
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
|
|
frame.variables[variable] = std::move(thing);
|
|
}
|
|
|
|
thing_h 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<instruction_t>((*frame.mod).byte(frame.position++));
|
|
switch (instr) {
|
|
case instruction_t::NoOperation: break;
|
|
case instruction_t::PushB2I: {
|
|
push_thing({ thing_t::Int32, m_context->thing_alloc() })->int32() = frame.mod->byte(frame.position++);
|
|
} 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()));
|
|
} break;
|
|
case instruction_t::Add: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->add(*rhs));
|
|
} break;
|
|
case instruction_t::Sub: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->sub(*rhs));
|
|
} break;
|
|
case instruction_t::Mul: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->mul(*rhs));
|
|
} break;
|
|
case instruction_t::Div: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->div(*rhs));
|
|
} break;
|
|
case instruction_t::Mod: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->mod(*rhs));
|
|
} break;
|
|
case instruction_t::Equals: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->equals(*rhs));
|
|
} break;
|
|
case instruction_t::NotEquals: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->not_equals(*rhs));
|
|
} break;
|
|
case instruction_t::LessThan: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->less_than(*rhs));
|
|
} break;
|
|
case instruction_t::GreaterThan: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->greater_than(*rhs));
|
|
} break;
|
|
case instruction_t::LessEqual: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
push_thing(lhs->less_equals(*rhs));
|
|
} break;
|
|
case instruction_t::GreaterEqual: {
|
|
auto rhs = pop_thing();
|
|
auto lhs = pop_thing();
|
|
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);
|
|
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);
|
|
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);
|
|
frame.position += 2;
|
|
push_frame(frame.mod, *frame.mod->function_at(funcId));
|
|
} break;
|
|
case instruction_t::Jump: {
|
|
frame.position += ((std::int8_t)frame.mod->byte(frame.position)) + 1;
|
|
} break;
|
|
case instruction_t::JumpNotZero: {
|
|
byte offset = frame.mod->byte(frame.position++);
|
|
auto cond = pop_thing();
|
|
if (cond->int32() != 0) frame.position += (std::int8_t)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
|