#include "vm.h" #include "test.h" #include /// Create and destroy a vm. TEST_CASE(vm_create_destroy) { Vm* vm = vm_new(); TEST_TRUE(vm != 0); vm_del(&vm); } // Exit with an implicit 0 exit code. TEST_CASE(vm_exit_implicit) { // clang-format off const Inst instructions[] = { vmExit(), }; // clang-format on Vm* vm = vm_new(); TEST_TRUE(vm != 0); const int exit_code = vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); TEST_TRUE(exit_code == 0); vm_del(&vm); } // Exit with an explicit exit code. TEST_CASE(vm_exit_explicit) { const int32_t expected = 17; // clang-format off const Inst instructions[] = { vmPushI32(expected), vmExit(), }; // clang-format on Vm* vm = vm_new(); TEST_TRUE(vm != 0); const int exit_code = vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); TEST_TRUE(exit_code == expected); vm_del(&vm); } /// Add two i32 numbers. TEST_CASE(vm_add_i32) { const int n1 = 2; const int n2 = 3; // clang-format off const Inst instructions[] = { vmPushI32(n1), vmPushI32(n2), vmAdd(I32), vmExit(), }; // clang-format on Vm* vm = vm_new(); TEST_TRUE(vm != 0); const int exit_code = vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); TEST_EQUAL(exit_code, n1 + n2); vm_del(&vm); } /// Sum an array of numbers with 4 add instructions. TEST_CASE(vm_sum_array_i32_explicit) { const int vals[5] = {1, 2, 3, 4, 5}; // clang-format off const Inst instructions[] = { vmPushI32(vals[0]), vmPushI32(vals[1]), vmPushI32(vals[2]), vmPushI32(vals[3]), vmPushI32(vals[4]), vmAdd(I32), vmAdd(I32), vmAdd(I32), vmAdd(I32), vmExit(), }; // clang-format on int sum = 0; for (size_t i = 0; i < sizeof(vals) / sizeof(vals[0]); ++i) { sum += vals[i]; } Vm* vm = vm_new(); TEST_TRUE(vm != 0); const int exit_code = vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); TEST_EQUAL(exit_code, sum); vm_del(&vm); } /// Sum an array of numbers with a loop. TEST_CASE(vm_sum_array_i32_loop) { const int vals[5] = {1, 2, 3, 4, 5}; const Label loop_label = 0; const Label counter_index = 0; // clang-format off const Inst instructions[] = { vmPushI32(vals[0]), vmPushI32(vals[1]), vmPushI32(vals[2]), vmPushI32(vals[3]), vmPushI32(vals[4]), vmLocal(I32, counter_index), vmPushI32(sizeof(vals) / sizeof(vals[0]) - 1), vmLocalWr(I32, counter_index), vmLoop(loop_label), vmAdd(I32), vmLocalRd(I32, counter_index), vmDec(I32), // TODO: Could be useful to have a function that writes the local but // leaves its value on the stack. vmLocalWr(I32, counter_index), vmLocalRd(I32, counter_index), // TODO: Perhaps we should expect the comparison value to also be pushed // to the stack. vmCmpI32(0), vmBr_if(false, loop_label), vmEnd(), vmExit(), }; // clang-format on int sum = 0; for (size_t i = 0; i < sizeof(vals) / sizeof(vals[0]); ++i) { sum += vals[i]; } Vm* vm = vm_new(); TEST_TRUE(vm != 0); const int exit_code = vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); TEST_EQUAL(exit_code, sum); vm_del(&vm); } // Call a function to add two numbers. TEST_CASE(vm_function_call) { const Label func = 0; const Label a = 0; const Label b = 1; const int32_t a_val = 3; const int32_t b_val = 5; const int32_t expected = a + b; // clang-format off const Inst instructions[] = { /* Function definition */ vmFunc(func), vmArg(I32, b), vmArg(I32, a), vmAdd(I32), vmEnd(), /* Main program */ vmPushI32(a_val), vmPushI32(b_val), vmCall(func), vmExit(), }; // clang-format on Vm* vm = vm_new(); TEST_TRUE(vm != 0); // const int exit_code = // vm_run(vm, instructions, sizeof(instructions) / sizeof(Inst)); vm_del(&vm); } int main() { return 0; }