roza/lib/VM.cpp

348 lines
8.8 KiB
C++
Raw Normal View History

2023-08-30 18:06:26 +00:00
#include "VM.hpp"
2023-08-30 22:31:19 +00:00
#include "lib/opcodes.hpp"
2023-09-01 20:38:12 +00:00
#include "Fun.hpp"
2023-08-30 18:06:26 +00:00
namespace roza
{
/*explicit*/ VM::VM(StatusLog& log)
: m_log { log }
2023-09-01 20:38:12 +00:00
, m_sp { 0 }
2023-08-30 18:06:26 +00:00
{
}
/*virtual*/ VM::~VM()
{
}
void VM::exec(std::shared_ptr<Program> program)
{
2023-08-30 22:31:19 +00:00
m_last_program = program;
2023-08-30 18:06:26 +00:00
while (m_pc < program->size())
{
switch (program->opcode(m_pc))
{
2023-08-31 23:22:51 +00:00
2023-09-01 20:38:12 +00:00
case OP_CALL: {
size_t arity = *program->param(m_pc);
std::vector<param_t> args;
for (size_t i=0; i<arity; i++)
{
param_t val = pop();
args.insert(std::begin(args), val);
}
auto fun = program->value(pop())->as_fun();
var_map vars;
for (size_t i=0; i<args.size(); i++)
{
if (program->has_value(args[i]))
{
vars.insert({i, program->value(args[i])});
}
}
// Prepare
Frame frame {
vars,
m_pc,
m_sp,
m_bp
};
m_frames.push_back(frame);
m_pc = 0;
m_bp = m_sp;
// Execute
exec(fun->program());
// Clear
size_t sz = m_sp - m_bp;
std::vector<param_t> rets;
for (size_t i=0; i<sz; i++)
{
rets.insert(rets.begin(), pop());
}
// Restore
m_pc = m_frames.back().pc;
m_sp = m_frames.back().sp;
m_bp = m_frames.back().bp;
auto ty = std::static_pointer_cast<FunTy>(fun->type());
if (ty->get_output()->base() != TY_NIL)
{
push(program->push_value(fun->program()->value(rets.back())));
}
m_frames.pop_back();
m_pc++;
} break;
case OP_RET: {
m_pc = program->size();
} break;
2023-08-31 23:22:51 +00:00
case OP_BRF: {
auto value = program->value(pop());
if (!value->as_bool())
{
m_pc = *program->param(m_pc);
}
else
{
m_pc++;
}
} break;
case OP_BR: {
m_pc = *program->param(m_pc);
} break;
case OP_LOAD_GLOBAL: {
int addr = *program->param(m_pc);
auto value = m_globals[addr];
push(program->push_value(value));
m_pc++;
} break;
case OP_STORE_GLOBAL: {
auto value = program->value(pop());
int addr = *program->param(m_pc);
m_globals[addr] = value;
m_pc++;
} break;
2023-09-01 20:38:12 +00:00
case OP_LOAD_LOCAL: {
int addr = *program->param(m_pc);
auto value = m_frames.back().locals[addr];
push(program->push_value(value));
m_pc++;
} break;
case OP_STORE_LOCAL: {
auto value = program->value(pop());
int addr = *program->param(m_pc);
m_frames.back().locals[addr] = value;
m_pc++;
} break;
case OP_ASSERT: {
auto value = program->value(pop());
if (!value->as_bool())
{
m_log.fatal(value->loc(), "assertion failed");
}
m_pc++;
} break;
2023-08-30 18:06:26 +00:00
case OP_PUSH_CONST: {
param_t param = *program->param(m_pc);
push(param);
m_pc++;
} break;
2023-08-31 09:37:13 +00:00
case OP_EQ: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->equals(*rhs),
lhs->loc());
});
} break;
case OP_ILT: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() < rhs->as_int(),
lhs->loc());
});
} break;
case OP_IGT: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() > rhs->as_int(),
lhs->loc());
});
} break;
2023-08-31 09:07:03 +00:00
case OP_IMP: {
apply_binop(program, [](auto lhs, auto rhs){
bool a = lhs->as_bool();
bool b = rhs->as_bool();
return std::make_shared<Value>(!a || b,
lhs->loc());
});
} break;
case OP_AND: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_bool() && rhs->as_bool(),
lhs->loc());
});
} break;
case OP_OR: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_bool() || rhs->as_bool(),
lhs->loc());
});
} break;
case OP_NOT: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(!lhs->as_bool(),
lhs->loc());
});
} break;
2023-08-30 22:31:19 +00:00
case OP_IADD: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() + rhs->as_int(),
lhs->loc());
});
} break;
case OP_ISUB: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() - rhs->as_int(),
lhs->loc());
});
} break;
case OP_IMUL: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() * rhs->as_int(),
lhs->loc());
});
} break;
case OP_IDIV: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() / rhs->as_int(),
lhs->loc());
});
} break;
case OP_IMOD: {
apply_binop(program, [](auto lhs, auto rhs){
return std::make_shared<Value>(lhs->as_int() % rhs->as_int(),
lhs->loc());
});
} break;
case OP_IPOW: {
apply_binop(program, [](auto lhs, auto rhs){
2023-08-31 09:07:03 +00:00
return std::make_shared<Value>(static_cast<int>(std::pow(lhs->as_int(),
rhs->as_int())),
2023-08-30 22:31:19 +00:00
lhs->loc());
});
} break;
case OP_IUADD: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(lhs->as_int(),
lhs->loc());
});
} break;
case OP_IUSUB: {
apply_unop(program, [](auto lhs){
return std::make_shared<Value>(-lhs->as_int(),
lhs->loc());
});
} break;
2023-08-30 18:06:26 +00:00
case OP_POP: {
pop();
m_pc++;
} break;
default:
m_log.fatal(SrcLoc {},
std::string()
+ "cannot execute opcode '"
+ OpcodeStr[program->opcode(m_pc)]
+ "'"); break;
}
}
}
2023-08-30 22:31:19 +00:00
std::string VM::string() const
{
std::stringstream ss;
2023-09-01 20:38:12 +00:00
for (size_t i=0; i<m_sp; i++)
2023-08-30 22:31:19 +00:00
{
ss << i << "\t";
if (m_last_program && m_last_program->has_value(m_stack[i]))
{
ss << m_stack[i]
<< "\t("
<< m_last_program->value(m_stack[i])->string()
<< ")"
<< std::endl;
}
else
{
ss << m_stack[i] << std::endl;
}
}
return ss.str();
}
2023-08-30 18:06:26 +00:00
void VM::push(param_t param)
{
2023-09-01 20:38:12 +00:00
m_stack[m_sp] = param;
m_sp++;
2023-08-30 18:06:26 +00:00
}
param_t VM::pop()
{
assert(m_stack.size() > 0);
2023-09-01 20:38:12 +00:00
param_t res = m_stack[m_sp - 1];
m_sp--;
2023-08-30 18:06:26 +00:00
return res;
}
2023-08-30 22:31:19 +00:00
void VM::apply_binop(std::shared_ptr<Program> program, binop_t op)
{
auto rhs = program->value(pop());
auto lhs = program->value(pop());
auto val = op(lhs, rhs);
push(program->push_value(val));
m_pc++;
}
void VM::apply_unop(std::shared_ptr<Program> program, unop_t op)
{
auto lhs = program->value(pop());
auto val = op(lhs);
push(program->push_value(val));
m_pc++;
}
2023-08-30 18:06:26 +00:00
}