thesis-lisp/vm/vmstate.c

323 lines
8.2 KiB
C

#include "vmstate.h"
#include "vmval.h"
#include "vm.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 inline uint64_t encode_ref(struct vm_value *ref) {
uintptr_t addr = (uintptr_t) ref;
assert(!(addr & 1));
if (sizeof(uintptr_t) == 8) {
return (addr >> 1) | FLAG_REF;
} else {
abort();
}
}
uint64_t pop(struct vm_state *vm) {
assert(vm->sp != vm->stack_size);
return vm->stack[vm->sp++];
}
void push(struct vm_state *vm, uint64_t w) {
assert(vm->sp > 0);
vm->stack[--vm->sp] = w;
}
void push_ref(struct vm_state *vm, struct vm_value *ref) {
assert(vm->sp > 0);
vm->stack[--vm->sp] = encode_ref(ref);
}
void push_integer(struct vm_state *vm, int64_t w) {
push(vm, w & ~FLAG_REF);
}
uint64_t pop_integer(struct vm_state *vm) {
uint64_t w = pop(vm);
assert(!(w & FLAG_REF));
if (w & (1ULL << 62)) {
w |= 1ULL << 63;
}
return w;
}
void vm_state_init(struct vm_state *vm, size_t stack_size) {
vm->stack = calloc(sizeof(uint64_t), stack_size);
vm->sp = stack_size;
vm->stack_size = stack_size;
vm->call_stack_size = 1024;
vm->call_stack = calloc(sizeof(uint64_t), vm->call_stack_size);
vm->csp = vm->call_stack_size;
vector_init(&vm->units, sizeof(struct vm_unit));
vm->lp = 0;
vm->fp = 0;
vm->ip = 0;
}
struct vm_unit *vm_add_unit(struct vm_state *vm, size_t global_pool_size) {
struct vm_unit *unit = vector_append(&vm->units);
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));
return unit;
}
struct vm_ref_entry *unit_add_ref(struct vm_unit *u) {
return vector_append(&u->ref_table);
}
struct vm_func_entry *unit_add_function(struct vm_unit *u) {
return vector_append(&u->functions);
}
// Call an entry from function table of a unit
void vm_call_unit_index(struct vm_state *vm, size_t lib_index, size_t fn_index) {
struct vm_func_entry *func;
struct vm_unit *unit;
assert(vm->csp >= 3);
unit = vector_ref(&vm->units, lib_index);
func = vector_ref(&unit->functions, fn_index);
vm->call_stack[--vm->csp] = vm->lp;
vm->call_stack[--vm->csp] = vm->fp;
vm->call_stack[--vm->csp] = vm->ip;
vm->lp = lib_index;
vm->fp = fn_index;
vm->ip = 0;
assert(func->argc <= MAXARG);
for (size_t i = 0; i < func->argc; ++i) {
func->arg_regs[i] = pop(vm);
}
}
void vm_call_ref(struct vm_state *vm, struct vm_value *ref) {
assert(ref->type == VT_FUNC);
vm_call_unit_index(vm, ref->v_func.lib_index, ref->v_func.fn_index);
}
static uint64_t vm_read_ext_ref(struct vm_state *vm, struct vm_ref_entry *ref) {
struct vm_unit *unit;
unit = vector_ref(&vm->units, ref->unit_index);
if (!unit->is_loaded) {
// Before trying to access target value, eval the unit first
int res = vm_eval_unit(vm, ref->unit_index);
assert(res == 0);
}
assert(ref->ref_index < unit->global_pool_size);
return unit->global_pool[ref->ref_index];
}
static int vm_eval_debug(struct vm_state *vm, struct vm_func_entry *func, uint32_t arg) {
(void) func;
uint64_t w0;
switch (arg) {
case 0x01:
w0 = pop(vm);
printf("trace: ");
vm_print(w0);
printf("\n");
return 0;
default:
fprintf(stderr, "Undefined debug opcode: %02x\n", arg);
abort();
}
}
int vm_eval_step(struct vm_state *vm) {
uint64_t w0, w1;
int64_t sw0, sw1;
size_t i0;
ssize_t ii0;
struct vm_ref_entry *r0;
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:
sw0 = pop_integer(vm);
sw1 = pop_integer(vm);
push_integer(vm, sw0 + sw1);
return 0;
case OP_NOT:
w0 = pop(vm);
if (null_q(w0)) {
push_integer(vm, 1);
} else {
push_ref(vm, NULL);
}
return 0;
case OP_EQ:
w0 = pop(vm);
w1 = pop(vm);
if (ref_q(w0) || ref_q(w1)) {
assert(0 && "Ref cmp not implemented yet");
}
if (w0 == w1) {
push_integer(vm, 1);
} else {
push_ref(vm, NULL);
}
return 0;
//
case OP_LDNIL:
push_ref(vm, NULL);
return 0;
case OP_LDI:
push_integer(vm, sximm(opcode & 0xFFFFFF));
return 0;
case OP_CAR:
w0 = pop(vm);
assert(pair_q(w0));
push(vm, getref(w0)->v_cons.fat_ar);
return 0;
case OP_CDR:
w0 = pop(vm);
assert(pair_q(w0));
push(vm, getref(w0)->v_cons.fat_dr);
return 0;
case OP_CONS:
w0 = pop(vm);
w1 = pop(vm);
push_ref(vm, vm_cons(w0, w1));
return 0;
//
case OP_LDARG:
i0 = opcode & 0xFFFFFF;
assert(i0 < MAXARG);
push(vm, func->arg_regs[i0]);
return 0;
case OP_STARG:
i0 = opcode & 0xFFFFFF;
w0 = pop(vm);
assert(i0 < MAXARG);
func->arg_regs[i0] = w0;
return 0;
case OP_LDG:
i0 = opcode & 0xFFFFFF;
assert(i0 < unit->global_pool_size);
push(vm, unit->global_pool[i0]);
return 0;
case OP_STG:
w0 = pop(vm);
i0 = opcode & 0xFFFFFF;
assert(i0 < unit->global_pool_size);
unit->global_pool[i0] = w0;
return 0;
case OP_LDF:
i0 = opcode & 0xFFFFFF;
assert(i0 < unit->functions.size);
push_ref(vm, vm_func(vm->lp, i0));
return 0;
case OP_STL:
i0 = opcode & 0xFFFFFF;
assert(i0 < func->local_count);
func->local_regs[i0] = pop(vm);
return 0;
case OP_LDL:
i0 = opcode & 0xFFFFFF;
assert(i0 < func->local_count);
push(vm, func->local_regs[i0]);
return 0;
//
case OP_ISZ:
w0 = pop(vm);
if (null_q(w0)) {
push_integer(vm, 1);
} else {
push_ref(vm, NULL);
}
return 0;
//
case OP_XCALL:
i0 = opcode & 0xFFFFFF;
r0 = vector_ref(&unit->ref_table, i0);
if (r0->flags & REF_NATIVE) {
int (*func) (struct vm_state *) = (void *) r0->ref_native;
assert(func);
return func(vm);
} else {
w0 = vm_read_ext_ref(vm, r0);
assert(func_q(w0));
vm_call_ref(vm, getref(w0));
return 0;
}
case OP_GCALL:
i0 = opcode & 0xFFFFFF;
assert(i0 < unit->global_pool_size);
w0 = unit->global_pool[i0];
assert(func_q(w0));
vm_call_ref(vm, getref(w0));
return 0;
case OP_CALL:
w0 = pop(vm);
assert(func_q(w0));
vm_call_ref(vm, getref(w0));
return 0;
case OP_BF:
w0 = pop(vm);
ii0 = sximm(opcode & 0xFFFFFF);
if (null_q(w0)) {
assert(ii0 != 0);
vm->ip += ii0 - 1;
}
return 0;
case OP_JMP:
ii0 = sximm(opcode & 0xFFFFFF);
assert(ii0 != 0);
vm->ip += ii0 - 1;
return 0;
case OP_RET:
assert(vm->call_stack_size - vm->csp >= 2);
vm->ip = vm->call_stack[vm->csp++];
vm->fp = vm->call_stack[vm->csp++];
vm->lp = vm->call_stack[vm->csp++];
return 0;
//
case OP_DEBUG:
return vm_eval_debug(vm, func, opcode & 0xFFFFFF);
default:
fprintf(stderr, "Undefined opcode: 0x%02hhx\n", opcode >> 24);
abort();
}
}
int vm_eval_unit(struct vm_state *vm, size_t index) {
struct vm_unit *unit;
size_t csp = vm->csp;
unit = vector_ref(&vm->units, index);
vm_call_unit_index(vm, index, 0);
// Run until return
while (vm->csp != csp) {
int res = vm_eval_step(vm);
if (res == -1) {
return -1;
}
}
unit->is_loaded = 1;
return 0;
}