From 5da905cfde3af9fe627bd2311b75bb851c3b7488 Mon Sep 17 00:00:00 2001 From: "Mr.UNIX" Date: Fri, 5 Jul 2024 10:29:41 +0100 Subject: [PATCH] VM: add support for I64 type (WIP) --- include/vm.h | 42 ++++++- src/ast.cpp | 250 ++++++++++++++++++++++++++++-------------- src/vm.cpp | 268 +++++++++++++++++++++++++++++++++++++++++---- tests/vm_tests.cpp | 10 ++ 4 files changed, 463 insertions(+), 107 deletions(-) diff --git a/include/vm.h b/include/vm.h index 0eb16a0..55a061c 100644 --- a/include/vm.h +++ b/include/vm.h @@ -12,35 +12,67 @@ struct Instruction { AddI32, AddI32_RI, AddI32_GI, + AddI64, + AddI64_RI, + AddI64_GI, SubI32, SubI32_RI, SubI32_GI, + SubI64, + SubI64_RI, + SubI64_GI, MulI32, MulI32_RI, MulI32_GI, + MulI64, + MulI64_RI, + MulI64_GI, DivI32, DivI32_RI, DivI32_GI, + DivI64, + DivI64_RI, + DivI64_GI, ModI32, ModI32_RI, ModI32_GI, - LoadI32, + ModI64, + ModI64_RI, + ModI64_GI, GreaterI32, GreaterI32_RI, GreaterI32_GI, + GreaterI64, + GreaterI64_RI, + GreaterI64_GI, LessI32, LessI32_RI, LessI32_GI, + LessI64, + LessI64_RI, + LessI64_GI, GreaterEqualI32, + GreaterEqualI64, LessEqualI32, + LessEqualI64, EqualI32, + EqualI64, NotEqualI32, + NotEqualI64, IncrementI32, + IncrementI64, DecrementI32, + DecrementI64, StoreGlobalI32, + StoreGlobalI64, StoreLocalI32, + StoreLocalI64, + LoadI32, + LoadI64, LoadGlobalI32, + LoadGlobalI64, LoadLocalI32, + LoadLocalI64, Return, Call, JumpIfFalse, @@ -50,10 +82,15 @@ struct Instruction { void *ptr; size_t index; int32_t i32; + int64_t i64; struct { size_t index; int32_t i32; - } ri; + } ri32; + struct { + size_t index; + int64_t i64; + } ri64; } params{}; }; @@ -62,6 +99,7 @@ struct Variable { enum class Type { Invalid = 0, I32, + I64, Function } type; size_t index; diff --git a/src/ast.cpp b/src/ast.cpp index be85fc4..5161304 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -8,6 +8,89 @@ static inline void assert(bool condition, const char *message) { throw std::runtime_error("Assertion failed: " + std::string(message)); } +#define GENERATE_EMIT_FUNCTION(OPERATION) \ + static inline void emit##OPERATION(Program &program, Segment &segment, const std::string &identifier) { \ + size_t id; \ + bool isLocal; \ + Instruction instruction; \ + Variable::Type type; \ + if (segment.find_local(identifier) != -1) { \ + isLocal = true; \ + id = segment.find_local(identifier); \ + type = segment.locals[identifier].type; \ + } else if (program.find_global(identifier) != -1) { \ + isLocal = false; \ + id = program.find_global(identifier); \ + type = program.segments[0].locals[identifier].type; \ + } else { \ + throw std::runtime_error("[Node::compile] Identifier not found: " + identifier); \ + } \ + switch (type) { \ + case Variable::Type::I32: \ + instruction.type = isLocal ? Instruction::InstructionType::OPERATION##LocalI32 \ + : Instruction::InstructionType::OPERATION##GlobalI32; \ + instruction.params.index = id; \ + break; \ + case Variable::Type::I64: \ + instruction.type = isLocal ? Instruction::InstructionType::OPERATION##LocalI64 \ + : Instruction::InstructionType::OPERATION##GlobalI64; \ + instruction.params.index = id; \ + break; \ + default: \ + throw std::runtime_error("[Node::compile] Invalid variable type!"); \ + } \ + segment.instructions.push_back(instruction); \ + } +GENERATE_EMIT_FUNCTION(Load) +GENERATE_EMIT_FUNCTION(Store) + +#define GENERATE_EMIT_FUNCTION_RI(OPERATION) \ + static inline void emit##OPERATION##RI(Program &program, Segment &segment, Node *left, Node *right) { \ + Instruction instruction; \ + size_t id; \ + bool isLocal; \ + Variable::Type type; \ + if (segment.find_local(left->token.value) != -1) { \ + isLocal = true; \ + id = segment.find_local(left->token.value); \ + type = segment.locals[left->token.value].type; \ + } else if (program.find_global(left->token.value) != -1) { \ + isLocal = false; \ + id = program.find_global(left->token.value); \ + type = program.segments[0].locals[left->token.value].type; \ + } else { \ + throw std::runtime_error("[BinaryExpression::compile] Identifier not found: " + left->token.value); \ + } \ + switch (type) { \ + case Variable::Type::I32: \ + instruction.type = isLocal ? Instruction::InstructionType::OPERATION##I32_RI \ + : Instruction::InstructionType::OPERATION##I32_GI; \ + instruction.params.ri32 = { \ + .index = id, \ + .i32 = std::stoi(right->token.value), \ + }; \ + break; \ + case Variable::Type::I64: \ + instruction.type = isLocal ? Instruction::InstructionType::OPERATION##I64_RI \ + : Instruction::InstructionType::OPERATION##I64_GI; \ + instruction.params.ri64 = { \ + .index = id, \ + .i64 = std::stol(right->token.value), \ + }; \ + break; \ + default: \ + throw std::runtime_error("[BinaryExpression::compile] Invalid variable type!"); \ + } \ + segment.instructions.push_back(instruction); \ + } +GENERATE_EMIT_FUNCTION_RI(Add) +GENERATE_EMIT_FUNCTION_RI(Sub) +GENERATE_EMIT_FUNCTION_RI(Mul) +GENERATE_EMIT_FUNCTION_RI(Div) +GENERATE_EMIT_FUNCTION_RI(Mod) +GENERATE_EMIT_FUNCTION_RI(Greater) +GENERATE_EMIT_FUNCTION_RI(Less) + Node::Node(Token token) : token(std::move(token)) { nodeType = Type::Node; typeStr = "Node"; @@ -19,28 +102,25 @@ bool Node::operator==(const AbstractSyntaxTree &other) const { void Node::compile(Program &program, Segment &segment) const { switch (token.type) { case Number: { + try { + segment.instructions.push_back( + Instruction{ + .type = Instruction::InstructionType::LoadI32, + .params = {.i32 = std::stoi(token.value)}, + }); + return; + } catch (std::out_of_range &) { + goto long64; + } + long64: segment.instructions.push_back( Instruction{ - .type = Instruction::InstructionType::LoadI32, - .params = {.i32 = std::stoi(token.value)}, + .type = Instruction::InstructionType::LoadI64, + .params = {.i64 = std::stol(token.value)}, }); } break; case Identifier: { - if (segment.find_local(token.value) != -1) { - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::LoadLocalI32, - .params = {.index = segment.find_local(token.value)}, - }); - } else if (program.find_global(token.value) != -1) { - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::LoadGlobalI32, - .params = {.index = program.find_global(token.value)}, - }); - } else { - throw std::runtime_error("[Node::compile] Identifier not found: " + token.value); - } + emitLoad(program, segment, token.value); } break; default: throw std::runtime_error("[Node::compile] This should not be accessed!"); @@ -61,22 +141,11 @@ bool BinaryExpression::operator==(const AbstractSyntaxTree &other) const { *right == *otherBinaryExpression.right && op == otherBinaryExpression.op; } + void BinaryExpression::compile(Program &program, Segment &segment) const { if (op.type == Assign) { right->compile(program, segment); - if (segment.find_local(dynamic_cast(*left).token.value)) { - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::StoreLocalI32, - .params = {.index = segment.find_local(dynamic_cast(*left).token.value)}, - }); - } else if (program.find_global(dynamic_cast(*left).token.value) != -1) { - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::StoreGlobalI32, - .params = {.index = program.find_global(dynamic_cast(*left).token.value)}, - }); - } + emitStore(program, segment, dynamic_cast(*left).token.value); return; } @@ -84,55 +153,31 @@ void BinaryExpression::compile(Program &program, Segment &segment) const { left->nodeType == AbstractSyntaxTree::Type::Node && ((Node *) left)->token.type == Identifier && ((Node *) right)->token.type == Number) { - Instruction instruction; - bool isLocal = segment.find_local(((Node *) left)->token.value) != -1; - if (isLocal) { - instruction.params.ri = { - .index = segment.find_local(((Node *) left)->token.value), - .i32 = std::stoi(((Node *) right)->token.value), - }; - } else if (program.find_global(((Node *) left)->token.value) != -1) { - instruction.params.ri = { - .index = program.find_global(((Node *) left)->token.value), - .i32 = std::stoi(((Node *) right)->token.value), - }; - } else { - throw std::runtime_error("[BinaryExpression::compile] Identifier not found: " + ((Node *) left)->token.value); - } - switch (op.type) { case Plus: - instruction.type = isLocal ? Instruction::InstructionType::AddI32_RI - : Instruction::InstructionType::AddI32_GI; + emitAddRI(program, segment, (Node *) left, (Node *) right); break; case Minus: - instruction.type = isLocal ? Instruction::InstructionType::SubI32_RI - : Instruction::InstructionType::SubI32_GI; + emitSubRI(program, segment, (Node *) left, (Node *) right); break; case Multiply: - instruction.type = isLocal ? Instruction::InstructionType::MulI32_RI - : Instruction::InstructionType::MulI32_GI; + emitMulRI(program, segment, (Node *) left, (Node *) right); break; case Divide: - instruction.type = isLocal ? Instruction::InstructionType::DivI32_RI - : Instruction::InstructionType::DivI32_GI; + emitDivRI(program, segment, (Node *) left, (Node *) right); break; case Modulo: - instruction.type = isLocal ? Instruction::InstructionType::ModI32_RI - : Instruction::InstructionType::ModI32_GI; + emitModRI(program, segment, (Node *) left, (Node *) right); break; case Greater: - instruction.type = isLocal ? Instruction::InstructionType::GreaterI32_RI - : Instruction::InstructionType::GreaterI32_GI; + emitGreaterRI(program, segment, (Node *) left, (Node *) right); break; case Less: - instruction.type = isLocal ? Instruction::InstructionType::LessI32_RI - : Instruction::InstructionType::LessI32_GI; + emitLessRI(program, segment, (Node *) left, (Node *) right); break; default: throw std::runtime_error("[BinaryExpression::compile] Invalid operator: " + op.value); } - segment.instructions.push_back(instruction); return; } @@ -226,10 +271,23 @@ bool Declaration::operator==(const AbstractSyntaxTree &other) const { identifier == otherDeclaration.identifier && value.has_value() == otherDeclaration.value.has_value(); } + +static inline Variable::Type varTypeConvert(AbstractSyntaxTree *ast) { + if (ast->nodeType != AbstractSyntaxTree::Type::Node) + throw std::runtime_error("[Declaration::compile] Invalid type: " + ast->typeStr); + auto token = dynamic_cast(ast)->token; + switch (token.type) { + case I32: + return Variable::Type::I32; + case I64: + return Variable::Type::I64; + default: + throw std::runtime_error("[Declaration::compile] Invalid type: " + token.value); + } +} void Declaration::compile(Program &program, Segment &segment) const { if (!type.has_value()) throw std::runtime_error("[Declaration::compile] Type deduction is not implemented!"); - switch (type.value()->nodeType) { case AbstractSyntaxTree::Type::Node: { switch (((Node *) type.value())->token.type) { @@ -243,6 +301,16 @@ void Declaration::compile(Program &program, Segment &segment) const { }); segment.declare_variable(identifier.token.value, Variable::Type::I32); } break; + case I64: { + value.value()->compile(program, segment); + segment.instructions.push_back({ + .type = segment.id == 0 + ? Instruction::InstructionType::StoreGlobalI64 + : Instruction::InstructionType::StoreLocalI64, + .params = {.index = segment.locals.size()}, + }); + segment.declare_variable(identifier.token.value, Variable::Type::I64); + } break; default: throw std::runtime_error("[Declaration::compile] Unimplemented type handler!"); } @@ -255,7 +323,7 @@ void Declaration::compile(Program &program, Segment &segment) const { for (auto argument: functionDeclaration->arguments) { newSegment.locals[argument->identifier.token.value] = { .name = argument->identifier.token.value, - .type = Variable::Type::I32,// TODO: Handle all variable types + .type = varTypeConvert(functionDeclaration->returnType), .index = newSegment.locals.size(), }; } @@ -444,33 +512,53 @@ UnaryExpression::UnaryExpression(AbstractSyntaxTree *expression, Token op, Unary typeStr = "UnaryExpression"; assert(expression != nullptr, "Expression can't be null!"); } + void UnaryExpression::compile(Program &program, Segment &segment) const { - expression->compile(program, segment); - size_t index = segment.instructions.size() - 1; + if (expression->nodeType != AbstractSyntaxTree::Type::Node) + throw std::runtime_error("[UnaryExpression::compile] Invalid expression varType!"); + Node *node = dynamic_cast(expression); + if (node->token.type != Identifier) + throw std::runtime_error("[UnaryExpression::compile] Invalid expression varType!"); + + Variable::Type varType; + if (segment.find_local(node->token.value) != -1) { + varType = segment.locals[node->token.value].type; + } else if (program.find_global(node->token.value) != -1) { + varType = program.segments[0].locals[node->token.value].type; + } else { + throw std::runtime_error("[UnaryExpression::compile] Identifier not found: " + node->token.value); + } + + emitLoad(program, segment, node->token.value); switch (op.type) { case Increment: - segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::IncrementI32}); + switch (varType) { + case Variable::Type::I32: + segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::IncrementI32}); + break; + case Variable::Type::I64: + segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::IncrementI64}); + break; + default: + throw std::runtime_error("[UnaryExpression::compile] Invalid varType!"); + } break; case Decrement: - segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::DecrementI32}); + switch (varType) { + case Variable::Type::I32: + segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::DecrementI32}); + break; + case Variable::Type::I64: + segment.instructions.push_back(Instruction{.type = Instruction::InstructionType::DecrementI64}); + break; + default: + throw std::runtime_error("[UnaryExpression::compile] Invalid varType!"); + } break; default: throw std::runtime_error("[UnaryExpression::compile] Invalid operator: " + op.value); } - if (segment.instructions[index].type == Instruction::InstructionType::LoadLocalI32) - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::StoreLocalI32, - .params = {.index = segment.find_local(dynamic_cast(*expression).token.value)}, - }); - else if (segment.instructions[index].type == Instruction::InstructionType::LoadGlobalI32) - segment.instructions.push_back( - Instruction{ - .type = Instruction::InstructionType::StoreGlobalI32, - .params = {.index = program.find_global(dynamic_cast(*expression).token.value)}, - }); - else - throw std::runtime_error("[UnaryExpression::compile] Invalid expression type!"); + emitStore(program, segment, node->token.value); } bool UnaryExpression::operator==(const AbstractSyntaxTree &other) const { if (other.nodeType != nodeType) return false; diff --git a/src/vm.cpp b/src/vm.cpp index dd47ff5..69b1b3f 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1,6 +1,5 @@ #include "vm.h" #include -#include #include Program::Program() { @@ -108,13 +107,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::AddI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - auto result = *a + instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + auto result = *a + instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::AddI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - auto result = *a + instruction.params.ri.i32; + auto result = *a + instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::SubI32: { @@ -126,13 +125,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::SubI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - auto result = *a - instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + auto result = *a - instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::SubI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - auto result = *a - instruction.params.ri.i32; + auto result = *a - instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::MulI32: { @@ -144,13 +143,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::MulI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - auto result = *a * instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + auto result = *a * instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::MulI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - auto result = *a * instruction.params.ri.i32; + auto result = *a * instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::DivI32: { @@ -162,13 +161,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::DivI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - auto result = *a / instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + auto result = *a / instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::DivI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - auto result = *a / instruction.params.ri.i32; + auto result = *a / instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::ModI32: { @@ -180,13 +179,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::ModI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - auto result = *a % instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + auto result = *a % instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::ModI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - auto result = *a % instruction.params.ri.i32; + auto result = *a % instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::GreaterI32: { @@ -198,13 +197,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::GreaterI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - int32_t result = (*a) > instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + int32_t result = (*a) > instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; - case Instruction::InstructionType::GreaterI32_GI : { + case Instruction::InstructionType::GreaterI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - int32_t result = (*a) > instruction.params.ri.i32; + int32_t result = (*a) > instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::LessI32: { @@ -216,13 +215,13 @@ void VM::run(const Program &program) { pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::LessI32_RI: { - auto a = static_cast(getLocal(instruction.params.ri.index)); - int32_t result = (*a) < instruction.params.ri.i32; + auto a = static_cast(getLocal(instruction.params.ri32.index)); + int32_t result = (*a) < instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::LessI32_GI: { auto a = static_cast(getGlobal(instruction.params.index)); - int32_t result = (*a) < instruction.params.ri.i32; + int32_t result = (*a) < instruction.params.ri32.i32; pushStack(&result, sizeof(int32_t)); } break; case Instruction::InstructionType::IncrementI32: { @@ -275,8 +274,229 @@ void VM::run(const Program &program) { case Instruction::InstructionType::Jump: callStack.back().currentInstruction = instruction.params.index; continue; + case Instruction::AddI64: { + auto a = static_cast(popStack(sizeof(int64_t))); + auto b = static_cast(popStack(sizeof(int64_t))); + auto result = *a + *b; + free(a); + free(b); + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::AddI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + auto result = *a + instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::AddI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + auto result = *a + instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::SubI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + auto result = *a - *b; + free(a); + free(b); + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::SubI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + auto result = *a - instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::SubI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + auto result = *a - instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::MulI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + auto result = *a * *b; + free(a); + free(b); + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::MulI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + auto result = *a * instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::MulI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + auto result = *a * instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::DivI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + auto result = *a / *b; + free(a); + free(b); + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::DivI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + auto result = *a / instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::DivI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + auto result = *a / instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::ModI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + auto result = *a % *b; + free(a); + free(b); + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::ModI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + auto result = *a % instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::ModI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + auto result = *a % instruction.params.ri64.i64; + pushStack(&result, sizeof(int64_t)); + } break; + case Instruction::GreaterI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) > (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::GreaterI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + int32_t result = (*a) > instruction.params.ri64.i64; + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::GreaterI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + int32_t result = (*a) > instruction.params.ri64.i64; + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::LessI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) < (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::LessI64_RI: { + auto a = static_cast(getLocal(instruction.params.ri64.index)); + int32_t result = (*a) < instruction.params.ri64.i64; + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::LessI64_GI: { + auto a = static_cast(getGlobal(instruction.params.index)); + int32_t result = (*a) < instruction.params.ri64.i64; + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::GreaterEqualI32: { + auto b = static_cast(popStack(sizeof(int32_t))); + auto a = static_cast(popStack(sizeof(int32_t))); + int32_t result = (*a) >= (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::GreaterEqualI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) >= (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::LessEqualI32: { + auto b = static_cast(popStack(sizeof(int32_t))); + auto a = static_cast(popStack(sizeof(int32_t))); + int32_t result = (*a) <= (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::LessEqualI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) <= (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::EqualI32: { + auto b = static_cast(popStack(sizeof(int32_t))); + auto a = static_cast(popStack(sizeof(int32_t))); + int32_t result = (*a) == (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::EqualI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) == (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::NotEqualI32: { + auto b = static_cast(popStack(sizeof(int32_t))); + auto a = static_cast(popStack(sizeof(int32_t))); + int32_t result = (*a) != (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::NotEqualI64: { + auto b = static_cast(popStack(sizeof(int64_t))); + auto a = static_cast(popStack(sizeof(int64_t))); + int32_t result = (*a) != (*b); + free(a); + free(b); + pushStack(&result, sizeof(int32_t)); + } break; + case Instruction::IncrementI64: { + auto val = static_cast(popStack(sizeof(int64_t))); + auto newVal = *val + 1; + free(val); + pushStack(&newVal, sizeof(int64_t)); + } break; + case Instruction::DecrementI64: { + auto val = static_cast(popStack(sizeof(int64_t))); + auto newVal = *val - 1; + free(val); + pushStack(&newVal, sizeof(int64_t)); + } break; + case Instruction::StoreGlobalI64: { + auto val = static_cast(popStack(sizeof(int64_t))); + setGlobal(instruction.params.index, (void **) &val); + } break; + case Instruction::StoreLocalI64: { + auto val = static_cast(popStack(sizeof(int64_t))); + setLocal(instruction.params.index, (void **) &val); + } break; + case Instruction::LoadI64: { + pushStack(((void *) &instruction.params.i64), sizeof(int64_t)); + } break; + case Instruction::LoadGlobalI64: { + auto val = static_cast(getGlobal(instruction.params.index)); + pushStack((void *) val, sizeof(int64_t)); + } break; + case Instruction::LoadLocalI64: { + auto val = static_cast(getLocal(instruction.params.index)); + pushStack((void *) val, sizeof(int64_t)); + } break; default: - throw std::runtime_error("[VM::run] Unimplemented instruction!"); + throw std::runtime_error("[VM::run] Invalid instruction!"); } callStack.back().currentInstruction++; } diff --git a/tests/vm_tests.cpp b/tests/vm_tests.cpp index 71f03fb..f6850e4 100644 --- a/tests/vm_tests.cpp +++ b/tests/vm_tests.cpp @@ -58,6 +58,16 @@ TEST(VM, SimpleVariableDeclaration) { ASSERT_EQ(*static_cast(vm.topStack(sizeof(int32_t))), 42); } +// TODO: fix this +TEST(VM, SimpleI64VariableDeclaration) { + const char *input = "define a : i64 = 42;" + "a;"; + VM vm; + auto program = compile(input); + vm.run(program); + ASSERT_EQ(*static_cast(vm.topStack(sizeof(int64_t))), 42); +} + TEST(VM, SimpleVariableAssignment) { const char *input = "define a : i32 = 42; a = 43; a;"; VM vm;