#include "vm.hpp" #include "arch.hpp" #include #include #include #include #include #include using namespace sliger; void VM::run_until_done() { while (!done()) { run_instruction(); } } void VM::run_n_instructions(size_t amount) { for (size_t i = 0; !done() and i < amount; ++i) { run_instruction(); } } void VM::run_instruction() { std::cout << "stack:\n"; this->print_stack(); std::cout << std::format("pc = {}\n", this->pc); auto op = eat_op(); switch (op) { case Op::Nop: // nothing break; case Op::PushNull: this->stack.push_back(Null {}); break; case Op::PushInt: { assert_program_has(1); auto value = eat_int32(); this->stack.push_back(Int { value }); break; } case Op::PushBool: { assert_program_has(1); auto value = eat_int32(); this->stack.push_back(Bool { .value = value != 0 }); break; } case Op::PushString: { assert_program_has(1); auto string_length = eat_uint32(); assert_program_has(string_length); auto value = std::string(); for (uint32_t i = 0; i < string_length; ++i) { auto ch = eat_uint32(); value.push_back(static_cast(ch)); } stack_push(String { .value = std::move(value) }); break; } case Op::PushPtr: { assert_program_has(1); auto value = eat_uint32(); this->stack.push_back(Ptr { value }); break; } case Op::Pop: { assert_stack_has(1); this->stack.pop_back(); break; } case Op::LoadLocal: { auto loc = eat_uint32(); assert_fn_stack_has(loc); auto value = fn_stack_at(loc); stack_push(value); break; } case Op::StoreLocal: { auto loc = eat_uint32(); assert_fn_stack_has(loc + 1); auto value = stack_pop(); fn_stack_at(loc) = value; break; } case Op::Call: { assert_program_has(1); auto arg_count = eat_uint32(); assert_stack_has(arg_count + 1); auto fn_ptr = stack_pop(); auto arguments = std::vector(); for (uint32_t i = 0; i < arg_count; ++i) { arguments.push_back(stack_pop()); } stack_push(Ptr { .value = this->pc }); stack_push(Ptr { .value = this->bp }); for (size_t i = arguments.size(); i > 0; --i) { stack_push(std::move(arguments.at(i - 1))); } this->pc = fn_ptr.as_ptr().value; this->bp = static_cast(this->stack.size()); if (this->opts.flame_graph) { this->flame_graph.report_call( fn_ptr.as_ptr().value, this->instruction_counter); } break; } case Op::Return: { assert_stack_has(3); auto ret_val = stack_pop(); auto bp_val = stack_pop(); auto pc_val = stack_pop(); this->bp = bp_val.as_ptr().value; stack_push(ret_val); this->pc = pc_val.as_ptr().value; if (this->opts.flame_graph) { this->flame_graph.report_return(this->instruction_counter); } break; } case Op::Jump: { assert_stack_has(1); auto addr = stack_pop(); this->pc = addr.as_ptr().value; break; } case Op::JumpIfFalse: { assert_stack_has(2); auto addr = stack_pop(); auto cond = stack_pop(); if (!cond.as_bool().value) { this->pc = addr.as_ptr().value; } break; } case Op::Add: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left + right; stack_push(Int { .value = value }); break; } case Op::Subtract: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left - right; stack_push(Int { .value = value }); break; } case Op::Multiply: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left * right; stack_push(Int { .value = value }); break; } case Op::Divide: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left / right; stack_push(Int { .value = value }); break; } case Op::Remainder: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left % right; stack_push(Int { .value = value }); break; } case Op::Equal: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left == right; stack_push(Bool { .value = value }); break; } case Op::LessThan: { assert_stack_has(2); auto right = stack_pop().as_int().value; auto left = stack_pop().as_int().value; auto value = left < right; stack_push(Bool { .value = value }); break; } case Op::And: { assert_stack_has(2); auto right = stack_pop().as_bool().value; auto left = stack_pop().as_bool().value; auto value = left && right; stack_push(Bool { .value = value }); break; } case Op::Or: { assert_stack_has(2); auto right = stack_pop().as_bool().value; auto left = stack_pop().as_bool().value; auto value = left || right; stack_push(Bool { .value = value }); break; } case Op::Xor: { assert_stack_has(2); auto right = stack_pop().as_bool().value; auto left = stack_pop().as_bool().value; auto value = (left || !right) || (!left && right); stack_push(Bool { .value = value }); break; } case Op::Not: { assert_stack_has(1); auto value = !stack_pop().as_bool().value; stack_push(Bool { .value = value }); break; } case Op::SourceMap: { assert_program_has(3); auto index = eat_int32(); auto line = eat_int32(); auto col = eat_int32(); if (opts.code_coverage) { this->code_coverage.report_cover(this->current_pos); } this->current_pos = { index, line, col }; break; } } this->instruction_counter += 1; }