348 lines
8.8 KiB
C++
348 lines
8.8 KiB
C++
#include "VM.hpp"
|
|
#include "lib/opcodes.hpp"
|
|
#include "Fun.hpp"
|
|
|
|
namespace roza
|
|
{
|
|
/*explicit*/ VM::VM(StatusLog& log)
|
|
: m_log { log }
|
|
, m_sp { 0 }
|
|
{
|
|
}
|
|
|
|
/*virtual*/ VM::~VM()
|
|
{
|
|
}
|
|
|
|
void VM::exec(std::shared_ptr<Program> program)
|
|
{
|
|
m_last_program = program;
|
|
while (m_pc < program->size())
|
|
{
|
|
switch (program->opcode(m_pc))
|
|
{
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
case OP_PUSH_CONST: {
|
|
param_t param = *program->param(m_pc);
|
|
push(param);
|
|
m_pc++;
|
|
} break;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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){
|
|
return std::make_shared<Value>(static_cast<int>(std::pow(lhs->as_int(),
|
|
rhs->as_int())),
|
|
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;
|
|
|
|
case OP_POP: {
|
|
pop();
|
|
m_pc++;
|
|
} break;
|
|
|
|
default:
|
|
m_log.fatal(SrcLoc {},
|
|
std::string()
|
|
+ "cannot execute opcode '"
|
|
+ OpcodeStr[program->opcode(m_pc)]
|
|
+ "'"); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string VM::string() const
|
|
{
|
|
std::stringstream ss;
|
|
|
|
for (size_t i=0; i<m_sp; i++)
|
|
{
|
|
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();
|
|
}
|
|
|
|
void VM::push(param_t param)
|
|
{
|
|
m_stack[m_sp] = param;
|
|
m_sp++;
|
|
}
|
|
|
|
param_t VM::pop()
|
|
{
|
|
assert(m_stack.size() > 0);
|
|
|
|
param_t res = m_stack[m_sp - 1];
|
|
m_sp--;
|
|
return res;
|
|
}
|
|
|
|
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++;
|
|
}
|
|
}
|