#include "vm.h" #include "lib/commons.h" #include "lib/fun.h" #include "lib/heap.h" #include "lib/locals.h" #include "lib/mod.h" #include "lib/type.h" #include "lib/tysy.h" #include "program.h" #include "value.h" void vm_init(vm_t* vm, sym_t* sym, tysy_t* tysy, err_t* err) { assert(vm); assert(sym); assert(tysy); vm->top_frame = NULL; locals_init(&vm->locals); vm_frame_push(vm, NULL); vm->tysy = tysy; vm->err = err; heap_init(&vm->heap); } void vm_free(vm_t* vm) { assert(vm); while (vm_frame_pop(vm)) { } if (vm->top_frame) { vm_frame_free(vm->top_frame); free(vm->top_frame); } vm->top_frame = NULL; heap_free(&vm->heap); locals_free(&vm->locals); } void vm_frame_init(frame_t* frame) { assert(frame); frame->fun = NULL; frame->pc = 0; frame->bp = 0; frame->sp = 0; frame->prev = NULL; frame->program = NULL; frame->locals = NULL; frame->alloc.cap = 0; frame->alloc.size = 0; frame->alloc.data = NULL; } void vm_frame_free(frame_t* frame) { if (frame->prev != NULL) { vm_frame_free((frame_t*) frame->prev); frame->prev = NULL; } } void vm_frame_push(vm_t* vm, fun_t* fun) { assert(vm); frame_t* frame = malloc(sizeof(frame_t)); vm_frame_init(frame); frame->prev = (struct frame*) vm->top_frame; if (fun != NULL) { frame->program = fun->program; frame->locals = (locals_t*) fun->locals; } else { frame->locals = &vm->locals; } vm->top_frame = frame; } int vm_frame_pop(vm_t* vm) { assert(vm); if (vm->top_frame->fun) { vm->top_frame->fun = NULL; } if (vm->top_frame) { vm_clear_alloc(vm->top_frame); } if (vm->top_frame && vm->top_frame->prev) { frame_t* prev = (frame_t*) vm->top_frame->prev; vm->top_frame->prev = NULL; vm_frame_free(vm->top_frame); free(vm->top_frame); vm->top_frame = NULL; vm->top_frame = prev; return 1; } return 0; } void vm_push_new(vm_t* vm, value_t* value) { assert(value); vm->top_frame->stack[vm->top_frame->sp] = value; vm_push_alloc(vm->top_frame, value); vm->top_frame->sp++; } void vm_push(vm_t* vm, value_t* value) { assert(value); vm->top_frame->stack[vm->top_frame->sp] = value; vm->top_frame->allocated[vm->top_frame->sp] = 0; vm->top_frame->sp++; } value_t* vm_pop(vm_t* vm) { assert(vm); if (vm->top_frame->sp == 0) { assert(0); } value_t* val = vm->top_frame->stack[vm->top_frame->sp - 1]; vm->top_frame->sp--; return val; } size_t vm_stack_str(vm_t* vm, char* buffer, size_t size) { assert(vm); assert(buffer); size_t sz = 0; if (vm->top_frame->sp == 0) { sz += snprintf(buffer + sz, size - sz, "** empty **\n"); return sz; } for (size_t i=0; itop_frame->sp; i++) { value_t* val = vm->top_frame->stack[vm->top_frame->sp - 1 - i]; sz += snprintf(buffer + sz, size - sz, "| "); value_str(val, buffer + sz, size - sz); sz += snprintf(buffer + sz, size - sz, " |"); } return sz; } int vm_exec_mod(vm_t* vm, mod_t* mod) { assert(vm); vm->top_frame->pc = 0; vm->top_frame->program = &mod->program; while (vm->top_frame->pc < vm->top_frame->program->size) { program_t* program = vm->top_frame->program; int status = vm_exec_instr(vm, program->ops[vm->top_frame->pc], program->params[vm->top_frame->pc]); if (status != 0) { return status; } } return 0; } int vm_exec_instr(vm_t* vm, Opcode op, param_t param) { assert(vm); switch (op) { case OP_PUSH: { if (vm->top_frame->sp + 1 >= RZ_STACK_LIMIT) { fprintf(stderr, "Stack overflow\n"); abort(); } value_t* val = (value_t*) vm->top_frame->program->values.data[param]; vm_push(vm, val); vm->top_frame->pc++; } break; case OP_ASSERT: { value_t* value = vm_pop(vm); if (value->type->kind != TYPE_BOOL) { vm_ensure_type(vm, value, TYPE_BOOL); vm->top_frame->pc++; break; } if (!value->value.bool) { fprintf(stderr, "\33[31mASSERT\33[0m[:%d] assertion failed\n", value->line); return -1; } vm_push(vm, value); vm->top_frame->pc++; } break; case OP_NE: case OP_EQ: { value_t* rhs = vm_pop(vm); value_t* lhs = vm_pop(vm); int same_type = lhs->type->kind == rhs->type->kind; int eq = value_eq(lhs, rhs); value_t* res = tysy_new_bool(vm->tysy, (op == OP_EQ ? (same_type && eq) : (!same_type || !eq)), rhs->line); vm_push_new(vm, res); vm->top_frame->pc++; } break; case OP_LT: case OP_LE: case OP_GT: case OP_GE: { value_t* rhs = vm_pop(vm); value_t* lhs = vm_pop(vm); vm_ensure_type(vm, lhs, TYPE_NUM); vm_ensure_type(vm, rhs, TYPE_NUM); int val = 0; switch (op) { case OP_LT: val = lhs->value.num < rhs->value.num; break; case OP_LE: val = lhs->value.num <= rhs->value.num; break; case OP_GT: val = lhs->value.num > rhs->value.num; break; case OP_GE: val = lhs->value.num >= rhs->value.num; break; default: break; } value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); vm_push_new(vm, res); vm->top_frame->pc++; } break; case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MODULO: case OP_POW: { value_t* rhs = vm_pop(vm); value_t* lhs = vm_pop(vm); vm_ensure_type(vm, lhs, TYPE_NUM); vm_ensure_type(vm, rhs, TYPE_NUM); double val = 0; switch (op) { case OP_ADD: val = lhs->value.num + rhs->value.num; break; case OP_SUB: val = lhs->value.num - rhs->value.num; break; case OP_MUL: val = lhs->value.num * rhs->value.num; break; case OP_DIV: val = lhs->value.num / rhs->value.num; break; case OP_MODULO: val = fmod(lhs->value.num, rhs->value.num); break; case OP_POW: val = powf(lhs->value.num, rhs->value.num); break; default: assert(0); break; } value_t* res = tysy_new_num(vm->tysy, val, lhs->line); vm_push_new(vm, res); vm->top_frame->pc++; } break; case OP_STRCAT: { value_t* rhs = vm_pop(vm); value_t* lhs = vm_pop(vm); vm_ensure_type(vm, lhs, TYPE_STR); vm_ensure_type(vm, rhs, TYPE_STR); str_t res; str_init(&res); str_append(&res, lhs->value.str); str_append(&res, rhs->value.str); value_t* value = tysy_new_str(vm->tysy, res.data, lhs->line); str_free(&res); vm_push_new(vm, value); vm->top_frame->pc++; } break; case OP_STRDUP: { value_t* rhs = vm_pop(vm); value_t* lhs = vm_pop(vm); if (lhs->type->kind != TYPE_NUM) { value_t* tmp = lhs; lhs = rhs; rhs = tmp; } vm_ensure_type(vm, lhs, TYPE_NUM); vm_ensure_type(vm, rhs, TYPE_STR); str_t res; str_init(&res); for (int i=0; i<(int) lhs->value.num; i++) { str_append(&res, rhs->value.str); } value_t* value = tysy_new_str(vm->tysy, res.data, lhs->line); str_free(&res); vm_push_new(vm, value); vm->top_frame->pc++; } break; case OP_USUB: { value_t* lhs = vm_pop(vm); vm_ensure_type(vm, lhs, TYPE_NUM); double val = -lhs->value.num; value_t* res = tysy_new_num(vm->tysy, val, lhs->line); vm_push_new(vm, res); vm->top_frame->pc++; } break; case OP_NOT: { value_t* lhs = vm_pop(vm); vm_ensure_type(vm, lhs, TYPE_BOOL); int val = !lhs->value.bool; value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); vm_push_new(vm, res); vm->top_frame->pc++; } break; case OP_BRT: case OP_BRF: { value_t* value = vm_pop(vm); vm_ensure_type(vm, value, TYPE_BOOL); if (op == OP_BRT && value->value.bool) { vm->top_frame->pc = param; } else if (op == OP_BRF && !value->value.bool) { vm->top_frame->pc = param; } else { vm->top_frame->pc++; } } break; case OP_BR: { vm->top_frame->pc = param; } break; case OP_LOAD: { local_t* local = locals_try_load(vm->top_frame->locals, param); if (!local && vm->top_frame->fun) { fun_t* fun = vm->top_frame->fun; closure_entry_t* e = fun_try_find_closure(fun, param); if (e) { vm_push(vm, e->value); vm->top_frame->pc++; break; } } if (!local) { local = locals_try_load(&vm->locals, param); } if (!local) { char msg[RZ_STR_LIMIT]; snprintf(msg, RZ_STR_LIMIT, "Cannot load local variable '%d'.", param); if (vm->top_frame->sp == 0) { err_fatal(vm->err, msg, -1); err_dump(vm->err); abort(); } else { value_t* val = vm->top_frame->stack[vm->top_frame->sp - 1]; int line = val->line; err_fatal(vm->err, msg, line); err_dump(vm->err); abort(); } } else { assert(local->addr < vm->top_frame->sp); vm_push(vm, vm->top_frame->stack[local->addr]); } vm->top_frame->pc++; } break; case OP_STORE: { assert(vm->top_frame->sp > 0); if (vm->top_frame->fun) { fun_t* fun = vm->top_frame->fun; closure_entry_t* closure = fun_try_find_closure(fun, param); if (closure) { value_t* value = vm->top_frame->stack[vm->top_frame->sp - 1]; value_free(closure->value); free(closure->value); closure->value = value_new_clone(value); vm->top_frame->pc++; break; } } locals_store(vm->top_frame->locals, param, vm->top_frame->sp - 1); vm->top_frame->pc++; } break; case OP_MKFUN: { value_t* value = value_new_clone(vm_pop(vm)); assert(value->type->kind == TYPE_FUN); size_t addr = heap_alloc(&vm->heap, value); value_t* ref = tysy_new_ref(vm->tysy, addr, value->line); vm_push_new(vm, ref); frame_t* prev = (frame_t*) vm->top_frame->prev; locals_t* loc = &vm->locals; assert(value->type->kind == TYPE_FUN); fun_t* fun = value->value.fun; frame_t* frame = vm->top_frame; if (vm->top_frame->fun) { loc = (locals_t*) vm->top_frame->fun->locals; } else if (prev && prev->fun) { loc = (locals_t*) prev->fun->locals; frame = prev; } else if (prev) { loc = prev->locals; frame = prev; } if (loc) { for (size_t i=0; isize; i++) { int id = frame->locals->data[i]->id; value_t* loc_val = frame->stack[loc->data[i]->addr]; fun_capture(fun, id, loc_val); } } vm->top_frame->pc++; } break; case OP_DEREF: { value_t* value = vm_pop(vm); assert(value->type->kind == TYPE_REF); size_t addr = value->value.ref; vm_push(vm, heap_deref(&vm->heap, addr)); vm->top_frame->pc++; } break; case OP_CALL: { size_t const ARG_SZ = param; value_t* args[ARG_SZ]; value_t* fun_ref = vm_pop(vm); for (size_t i=0; iheap, fun_ref->value.ref); assert(fun_val); assert(fun_val->type->kind == TYPE_FUN); assert(fun_val->value.fun); fun_t* fun = fun_val->value.fun; vm->top_frame->pc++; vm_frame_push(vm, fun); vm->top_frame->fun = fun; vm_push(vm, fun_ref); locals_store(vm->top_frame->locals, fun->base, vm->top_frame->sp - 1); for (size_t i=0; iarg_base + (ARG_SZ - 1 - i); vm_push(vm, arg); locals_store(vm->top_frame->locals, addr, vm->top_frame->sp - 1); } } break; case OP_RET: { frame_t* frame = (frame_t*) vm->top_frame; frame_t* prev = (frame_t*) vm->top_frame->prev; value_t* ret = frame->stack[frame->sp - 1]; prev->stack[prev->sp] = ret; prev->sp++; for (size_t i=0; ialloc.size; i++) { if (frame->alloc.data[i] == ret) { frame->alloc.data[i] = NULL; vm_push_alloc(prev, ret); } } vm_frame_pop(vm); } break; default: { fprintf(stderr, "Cannot execute unknown opcode '%s'.\n", OpcodeStr[op]); abort(); }; } return 0; } void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want) { assert(vm); assert(value); TypeKind got = value->type->kind; int line = value->line; if (got != want) { char msg[RZ_STR_LIMIT]; snprintf(msg, RZ_STR_LIMIT, "type mismatch: %s expected, got %s.", TypeKindStr[want] + strlen("TYPE_"), TypeKindStr[got] + strlen("TYPE_")); err_fatal(vm->err, msg, line); err_dump(vm->err); } } void vm_push_alloc(frame_t* frame, value_t* value) { assert(frame); assert(value); if (frame->alloc.cap == 0) { frame->alloc.cap = 2; frame->alloc.data = malloc(sizeof(value_t*) * frame->alloc.cap); } if (frame->alloc.size >= frame->alloc.cap) { frame->alloc.cap *= 2; frame->alloc.data = realloc(frame->alloc.data, sizeof(value_t*) * frame->alloc.cap); } frame->alloc.data[frame->alloc.size] = value; frame->alloc.size++; } void vm_clear_alloc(frame_t* frame) { assert(frame); for (size_t i=0; ialloc.size; i++) { value_t* val = frame->alloc.data[i]; if (val) { value_free(val); free(val); } } free(frame->alloc.data); frame->alloc.data = NULL; }