thesis-lisp/vm/vmstate.c

188 lines
4.4 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, size_t global_pool_size) {
vm->stack = calloc(sizeof(uint64_t), stack_size);
vm->sp = stack_size;
vm->stack_size = stack_size;
vm->global_pool = calloc(sizeof(uint64_t), global_pool_size);
vm->global_pool_size = global_pool_size;
vector_init(&vm->ref_table, sizeof(struct vm_ref_entry));
vector_init(&vm->functions, sizeof(struct vm_func_entry));
vm->fp = 0;
vm->ip = 0;
}
struct vm_ref_entry *vm_add_ref(struct vm_state *vm) {
return vector_append(&vm->ref_table);
}
struct vm_func_entry *vm_add_function(struct vm_state *vm) {
return vector_append(&vm->functions);
}
//static int vm_eval_debug(struct vm_state *vm, uint32_t arg) {
// uint64_t w0;
// switch (arg) {
// case 0x01:
// w0 = pop(vm);
// printf("trace: ");
// vm_print(w0);
// printf("\n");
// return 0;
// default:
// fprintf(stderr, "Undefined debug argument: 0x%02hhx\n", arg);
// abort();
// }
//}
int vm_eval_opcode(struct vm_state *vm, uint32_t opcode) {
uint64_t w0, w1;
size_t i0;
ssize_t ii0;
struct vm_ref_entry *r0;
switch (opcode >> 24) {
case OP_NOT:
w0 = pop(vm);
if (null_q(w0)) {
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_LDG:
i0 = opcode & 0xFFFFFF;
assert(i0 < vm->global_pool_size);
push(vm, vm->global_pool[i0]);
return 0;
case OP_STG:
w0 = pop(vm);
i0 = opcode & 0xFFFFFF;
assert(i0 < vm->global_pool_size);
vm->global_pool[i0] = w0;
return 0;
//
case OP_TEST:
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(&vm->ref_table, i0);
assert(r0->flags & REF_NATIVE);
{
int (*func) (struct vm_state *) = (void *) r0->address;
assert(func);
return func(vm);
}
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:
return -1;
default:
fprintf(stderr, "Undefined opcode: 0x%02hhx\n", opcode >> 24);
abort();
}
}
int vm_eval_step(struct vm_state *vm) {
assert(vm->fp < vm->functions.size);
struct vm_func_entry *func = vector_ref(&vm->functions, vm->fp);
uint32_t opcode = func->bytecode[vm->ip++];
return vm_eval_opcode(vm, opcode);
}