Files
alnyan 91961bcec5 Lots of changes with long commit message again
1. (list ...)
2. string-* functions
3. (native str) to resolve native functions at runtime
4. Memory fuckups in compiler core unit loading
5. Now can call non-identifiers in compiler
2021-04-09 00:18:48 +03:00

543 lines
14 KiB
C

#include "debug.h"
#include "error.h"
#include "op.h"
#include "unit.h"
#include "vmstate.h"
#include "vmval.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
static inline int64_t sximm(uint32_t in) {
if (in & 0x800000) {
return in | 0xFFFFFFFFFF000000;
} else {
return in;
}
}
static int vm_push_context(struct vm *vm) {
int res;
if ((res = stack_push(&vm->call_stack, vm->bp)) != 0) {
return res;
}
if ((res = stack_push(&vm->call_stack, vm->lp)) != 0) {
return res;
}
if ((res = stack_push(&vm->call_stack, vm->fp)) != 0) {
return res;
}
if ((res = stack_push(&vm->call_stack, vm->ip)) != 0) {
return res;
}
return 0;
}
static int vm_pop_context(struct vm *vm) {
int res;
if ((res = stack_pop(&vm->call_stack, &vm->ip)) != 0) {
return res;
}
if ((res = stack_pop(&vm->call_stack, &vm->fp)) != 0) {
return res;
}
if ((res = stack_pop(&vm->call_stack, &vm->lp)) != 0) {
return res;
}
if ((res = stack_pop(&vm->call_stack, &vm->bp)) != 0) {
return res;
}
return 0;
}
int vm_init(struct vm *vm, size_t stack_size) {
int res;
if ((res = stack_init(&vm->data_stack, stack_size)) != 0) {
return res;
}
if ((res = stack_init(&vm->call_stack, 1024)) != 0) {
return res;
}
vector_init(&vm->units, sizeof(struct vm_unit));
vm->state = STATE_INIT;
vm->flags = 0;
vm->lp = 0;
vm->fp = 0;
vm->ip = 0;
return 0;
}
void vm_free(struct vm *vm) {
struct vm_unit *unit;
stack_free(&vm->data_stack);
stack_free(&vm->call_stack);
for (size_t i = 0; i < vm->units.size; ++i) {
unit = vector_ref(&vm->units, i);
unit_free(unit);
}
vector_free(&vm->units);
}
struct vm_unit *vm_add_unit(struct vm *vm, size_t global_pool_size) {
struct vm_unit *unit = vector_append(&vm->units);
if (!unit) {
return unit;
}
unit->is_loaded = 0;
unit->global_pool = calloc(sizeof(uint64_t), global_pool_size);
unit->global_pool_size = global_pool_size;
vector_init(&unit->ref_table, sizeof(struct vm_ref_entry));
vector_init(&unit->functions, sizeof(struct vm_func_entry));
vector_init(&unit->strtab, sizeof(char *));
return unit;
}
// Call an entry from function table of a unit
int vm_call_unit_index(struct vm *vm, size_t lib_index, size_t fn_index) {
struct vm_func_entry *func;
struct vm_unit *unit;
int res;
uint64_t w;
unit = vector_ref(&vm->units, lib_index);
func = vector_ref(&unit->functions, fn_index);
if (func->argc > MAXARG) {
return -ERR_RANGE;
}
if ((res = vm_push_context(vm)) != 0) {
return res;
}
vm->lp = lib_index;
vm->fp = fn_index;
vm->ip = 0;
if (vm->call_stack.sp < func->argc + func->local_count) {
return -ERR_STACK_OVERFLOW;
}
vm->call_stack.sp -= func->argc;
for (size_t i = 0; i < func->argc; ++i) {
if ((res = stack_pop(&vm->data_stack, &w)) != 0) {
return res;
}
vm->call_stack.data[vm->call_stack.sp + i] = w;
}
vm->bp = vm->call_stack.sp;
// Reserve stack space for locals
vm->call_stack.sp -= func->local_count;
return 0;
}
static uint64_t *vm_local_ref(struct vm *vm, size_t index) {
// TODO index sanity checks
// [bp - index - 1]
++index;
return &vm->call_stack.data[vm->bp - index];
}
static uint64_t *vm_arg_ref(struct vm *vm, size_t index) {
// [bp + index]
return &vm->call_stack.data[vm->bp + index];
}
int vm_call_ref(struct vm *vm, struct vm_value *ref) {
switch (ref->type) {
case VT_CFUNC:
{
int (*func) (struct vm *) = (void *) ref->v_cfunc;
if (!func) {
return -ERR_OPERAND_TYPE;
}
return func(vm);
}
case VT_FUNC:
return vm_call_unit_index(vm, ref->v_func.lib_index, ref->v_func.fn_index);
default:
return -ERR_OPERAND_TYPE;
}
}
static int vm_read_ext_ref(struct vm *vm, struct vm_ref_entry *ref, uint64_t *w) {
struct vm_unit *unit;
int res;
unit = vector_ref(&vm->units, ref->unit_index);
if (!unit->is_loaded) {
// Before trying to access target value, eval the unit first
if ((res = vm_eval_unit(vm, ref->unit_index)) != 0) {
return res;
}
}
assert(ref->ref_index < unit->global_pool_size);
if (ref->ref_index >= unit->global_pool_size) {
return -ERR_RANGE;
}
*w = unit->global_pool[ref->ref_index];
return 0;
}
static int vm_eval_debug(struct vm *vm, struct vm_func_entry *func, uint32_t arg) {
(void) func;
uint64_t w0;
int res;
switch (arg) {
case OP_DEBUG_TRACE:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
printf("trace: ");
vm_print(stdout, w0);
printf("\n");
vm_unref(w0);
return 0;
case OP_DEBUG_BREAKPOINT:
vm->state = STATE_BREAKPOINT;
return 0;
default:
return -ERR_OPCODE_UNDEFINED;
}
}
int vm_eval_step(struct vm *vm) {
uint64_t w0, w1;
int64_t sw0, sw1;
uint64_t *wr0;
ssize_t ii0;
size_t i0;
struct vm_ref_entry *r0;
struct vm_value *v0;
int res;
if (vm->state != STATE_RUNNING) {
return -ERR_INVALID_STATE;
}
assert(vm->lp < vm->units.size);
struct vm_unit *unit = vector_ref(&vm->units, vm->lp);
assert(vm->fp < unit->functions.size);
struct vm_func_entry *func = vector_ref(&unit->functions, vm->fp);
uint32_t opcode = func->bytecode[vm->ip++];
switch (opcode >> 24) {
case OP_ADD:
if ((res = vm_pop_integer(vm, &sw0)) != 0) {
return res;
}
if ((res = vm_pop_integer(vm, &sw1)) != 0) {
return res;
}
return vm_push_integer(vm, sw0 + sw1);
case OP_SUB:
if ((res = vm_pop_integer(vm, &sw0)) != 0) {
return res;
}
if ((res = vm_pop_integer(vm, &sw1)) != 0) {
return res;
}
return vm_push_integer(vm, sw0 - sw1);
case OP_MUL:
if ((res = vm_pop_integer(vm, &sw0)) != 0) {
return res;
}
if ((res = vm_pop_integer(vm, &sw1)) != 0) {
return res;
}
return vm_push_integer(vm, sw0 * sw1);
case OP_NOT:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
res = vm_push_bool(vm, null_q(w0));
vm_unref(w0);
return res;
case OP_EQ:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if ((res = stack_pop(&vm->data_stack, &w1)) != 0) {
return res;
}
if (ref_q(w0) || ref_q(w1)) {
// TODO what do?
return -ERR_OPERAND_TYPE;
}
res = vm_push_bool(vm, w0 == w1);
vm_unref(w0);
vm_unref(w1);
return res;
case OP_NEQ:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if ((res = stack_pop(&vm->data_stack, &w1)) != 0) {
return res;
}
if (ref_q(w0) || ref_q(w1)) {
// TODO what do?
return -ERR_OPERAND_TYPE;
}
res = vm_push_bool(vm, w0 != w1);
vm_unref(w0);
vm_unref(w1);
return res;
//
case OP_LDNIL:
return stack_push(&vm->data_stack, FLAG_REF);
case OP_LDI:
return vm_push_integer(vm, sximm(opcode & 0xFFFFFF));
case OP_CAR:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if (!pair_q(w0)) {
return -ERR_OPERAND_TYPE;
}
vm_ref(getref(w0)->v_cons.fat_ar);
res = stack_push(&vm->data_stack, getref(w0)->v_cons.fat_ar);
vm_unref(w0);
return res;
case OP_CDR:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if (!pair_q(w0)) {
return -ERR_OPERAND_TYPE;
}
vm_ref(getref(w0)->v_cons.fat_dr);
res = stack_push(&vm->data_stack, getref(w0)->v_cons.fat_dr);
vm_unref(w0);
return res;
case OP_CONS:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if ((res = stack_pop(&vm->data_stack, &w1)) != 0) {
return res;
}
v0 = vm_cons(w0, w1);
vm_value_ref(v0);
res = vm_push_ref(vm, v0);
vm_unref(w0);
vm_unref(w1);
return res;
//
case OP_LDARG:
i0 = opcode & 0xFFFFFF;
if (i0 >= MAXARG) {
return -ERR_RANGE;
}
w0 = *vm_arg_ref(vm, i0);
vm_ref(w0);
return stack_push(&vm->data_stack, w0);
case OP_STARG:
i0 = opcode & 0xFFFFFF;
if (i0 >= MAXARG) {
return -ERR_RANGE;
}
wr0 = vm_arg_ref(vm, i0);
vm_unref(*wr0);
return stack_pop(&vm->data_stack, wr0);
case OP_LDG:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->global_pool_size) {
return -ERR_RANGE;
}
w0 = unit->global_pool[i0];
vm_ref(w0);
return stack_push(&vm->data_stack, w0);
case OP_STG:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->global_pool_size) {
return -ERR_RANGE;
}
vm_unref(unit->global_pool[i0]);
return stack_pop(&vm->data_stack, &unit->global_pool[i0]);
case OP_LDF:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->functions.size) {
return -ERR_RANGE;
}
v0 = vm_func(vm->lp, i0);
vm_value_ref(v0);
return vm_push_ref(vm, v0);
case OP_LDS:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->strtab.size) {
return -ERR_RANGE;
}
{
char **ref = vector_ref(&unit->strtab, i0);
v0 = vm_makestr(*ref);
vm_value_ref(v0);
return vm_push_ref(vm, v0);
}
case OP_STL:
i0 = opcode & 0xFFFFFF;
if (i0 >= func->local_count) {
return -ERR_RANGE;
}
wr0 = vm_local_ref(vm, i0);
vm_unref(*wr0);
return stack_pop(&vm->data_stack, wr0);
case OP_LDL:
i0 = opcode & 0xFFFFFF;
if (i0 >= func->local_count) {
return -ERR_RANGE;
}
w0 = *vm_local_ref(vm, i0);
vm_ref(w0);
return stack_push(&vm->data_stack, w0);
//
case OP_ISZ:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
res = vm_push_bool(vm, null_q(w0));
vm_unref(w0);
return res;
//
case OP_XCALL:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->ref_table.size) {
return -ERR_RANGE;
}
r0 = vector_ref(&unit->ref_table, i0);
if (r0->flags & REF_NATIVE) {
int (*func) (struct vm *) = (void *) r0->ref_native;
if (!func) {
return -ERR_OPERAND_TYPE;
}
return func(vm);
} else {
if ((res = vm_read_ext_ref(vm, r0, &w0)) != 0) {
return res;
}
if (!ref_q(w0) || null_q(w0)) {
return -ERR_OPERAND_TYPE;
}
return vm_call_ref(vm, getref(w0));
}
case OP_GCALL:
i0 = opcode & 0xFFFFFF;
if (i0 >= unit->global_pool_size) {
return -ERR_RANGE;
}
w0 = unit->global_pool[i0];
if (!ref_q(w0) || null_q(w0)) {
return -ERR_OPERAND_TYPE;
}
return vm_call_ref(vm, getref(w0));
case OP_CALL:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
if (!ref_q(w0) || null_q(w0)) {
return -ERR_OPERAND_TYPE;
}
res = vm_call_ref(vm, getref(w0));
vm_unref(w0);
return res;
case OP_BF:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
ii0 = sximm(opcode & 0xFFFFFF);
if (null_q(w0)) {
if (ii0 == 0) {
return -ERR_RANGE;
}
vm->ip += ii0 - 1;
}
vm_unref(w0);
return 0;
case OP_JMP:
ii0 = sximm(opcode & 0xFFFFFF);
if (ii0 == 0) {
return -ERR_RANGE;
}
vm->ip += ii0 - 1;
return 0;
case OP_RET:
// Drop locals from call stack
for (size_t i = 0; i < func->local_count; ++i) {
if ((res = stack_pop(&vm->call_stack, &w0)) != 0) {
return res;
}
vm_unref(w0);
}
// Drop references to arguments
for (size_t i = 0; i < func->argc; ++i) {
if ((res = stack_pop(&vm->call_stack, &w0)) != 0) {
return res;
}
vm_unref(w0);
}
return vm_pop_context(vm);
//
case OP_DEBUG:
return vm_eval_debug(vm, func, opcode & 0xFFFFFF);
default:
fprintf(stderr, "Undefined opcode: %02x\n", opcode >> 24);
return -ERR_OPCODE_UNDEFINED;
}
}
int vm_eval_unit(struct vm *vm, size_t index) {
struct vm_unit *unit, *curr_unit;
struct vm_func_entry *curr_func;
size_t csp;
int res;
unit = vector_ref(&vm->units, index);
csp = vm->call_stack.sp;
vm_call_unit_index(vm, index, 0);
// Run until return
while (vm->call_stack.sp != csp) {
if (vm->flags & VM_SINGLESTEP) {
curr_unit = vector_ref(&vm->units, vm->lp);
curr_func = vector_ref(&curr_unit->functions, vm->fp);
// Check if it's a breakpoint instruction and skip it
if (curr_func->bytecode[vm->ip] == (uint32_t) OP(OP_DEBUG, OP_DEBUG_BREAKPOINT)) {
++vm->ip;
continue;
}
if ((res = debug_breakpoint(vm)) != 0) {
return res;
}
}
if (vm->state == STATE_BREAKPOINT) {
if ((res = debug_breakpoint(vm)) != 0) {
return res;
}
vm->state = STATE_RUNNING;
} else if (vm->state == STATE_ERROR) {
res = -ERR_ERROR_EXIT;
break;
} else if (vm->state == STATE_EXITED) {
break;
}
if ((res = vm_eval_step(vm)) != 0) {
return res;
}
}
unit->is_loaded = 1;
return res;
}