238 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "vm.hpp"
 | |
| #include "arch.hpp"
 | |
| #include <cstdint>
 | |
| #include <cstdlib>
 | |
| #include <format>
 | |
| #include <iostream>
 | |
| #include <utility>
 | |
| #include <vector>
 | |
| 
 | |
| 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<char>(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<Value>();
 | |
|             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<uint32_t>(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;
 | |
| }
 |