2021-04-06 17:27:02 +03:00
|
|
|
#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;
|
|
|
|
|
2021-04-06 18:22:12 +03:00
|
|
|
vm->call_stack_size = 1024;
|
|
|
|
vm->call_stack = calloc(sizeof(uint64_t), vm->call_stack_size);
|
|
|
|
vm->csp = vm->call_stack_size;
|
|
|
|
|
2021-04-06 17:27:02 +03:00
|
|
|
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();
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
2021-04-06 18:22:12 +03:00
|
|
|
void vm_call_index(struct vm_state *vm, size_t index) {
|
|
|
|
struct vm_func_entry *func;
|
|
|
|
assert(index < vm->functions.size);
|
2021-04-07 00:03:42 +03:00
|
|
|
assert(vm->csp > 2);
|
2021-04-06 18:22:12 +03:00
|
|
|
|
2021-04-07 00:03:42 +03:00
|
|
|
vm->call_stack[--vm->csp] = vm->fp;
|
2021-04-06 18:22:12 +03:00
|
|
|
vm->call_stack[--vm->csp] = vm->ip;
|
|
|
|
vm->fp = index;
|
|
|
|
vm->ip = 0;
|
|
|
|
|
|
|
|
func = vector_ref(&vm->functions, index);
|
|
|
|
assert(func->argc <= MAXARG);
|
|
|
|
for (size_t i = 0; i < func->argc; ++i) {
|
2021-04-07 00:03:42 +03:00
|
|
|
func->arg_regs[i] = pop(vm);
|
2021-04-06 18:22:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void vm_call_ref(struct vm_state *vm, struct vm_value *ref) {
|
|
|
|
assert(ref->type == VT_FUNC);
|
|
|
|
assert(ref->v_func.lib_index == 0);
|
|
|
|
vm_call_index(vm, ref->v_func.fn_index);
|
|
|
|
}
|
|
|
|
|
2021-04-07 00:03:42 +03:00
|
|
|
int vm_eval_opcode(struct vm_state *vm, struct vm_func_entry *func, uint32_t opcode) {
|
2021-04-06 17:27:02 +03:00
|
|
|
uint64_t w0, w1;
|
2021-04-07 00:03:42 +03:00
|
|
|
int64_t sw0, sw1;
|
2021-04-06 17:27:02 +03:00
|
|
|
size_t i0;
|
|
|
|
ssize_t ii0;
|
|
|
|
struct vm_ref_entry *r0;
|
|
|
|
|
|
|
|
switch (opcode >> 24) {
|
2021-04-07 00:03:42 +03:00
|
|
|
case OP_ADD:
|
|
|
|
sw0 = pop_integer(vm);
|
|
|
|
sw1 = pop_integer(vm);
|
|
|
|
push_integer(vm, sw0 + sw1);
|
|
|
|
return 0;
|
2021-04-06 17:27:02 +03:00
|
|
|
case OP_NOT:
|
|
|
|
w0 = pop(vm);
|
|
|
|
if (null_q(w0)) {
|
|
|
|
push_integer(vm, 1);
|
|
|
|
} else {
|
|
|
|
push_ref(vm, NULL);
|
|
|
|
}
|
|
|
|
return 0;
|
2021-04-06 18:22:12 +03:00
|
|
|
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;
|
2021-04-06 17:27:02 +03:00
|
|
|
//
|
|
|
|
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;
|
|
|
|
//
|
2021-04-06 18:22:12 +03:00
|
|
|
case OP_LDARG:
|
|
|
|
i0 = opcode & 0xFFFFFF;
|
|
|
|
assert(i0 < MAXARG);
|
2021-04-07 00:03:42 +03:00
|
|
|
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;
|
2021-04-06 18:22:12 +03:00
|
|
|
return 0;
|
2021-04-06 17:27:02 +03:00
|
|
|
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;
|
2021-04-06 18:22:12 +03:00
|
|
|
case OP_LDF:
|
|
|
|
i0 = opcode & 0xFFFFFF;
|
|
|
|
assert(i0 < vm->functions.size);
|
|
|
|
push_ref(vm, vm_func(0, i0));
|
|
|
|
return 0;
|
|
|
|
case OP_STL:
|
2021-04-07 00:03:42 +03:00
|
|
|
i0 = opcode & 0xFFFFFF;
|
|
|
|
assert(i0 < func->local_count);
|
|
|
|
func->local_regs[i0] = pop(vm);
|
|
|
|
return 0;
|
2021-04-06 18:22:12 +03:00
|
|
|
case OP_LDL:
|
2021-04-07 00:03:42 +03:00
|
|
|
i0 = opcode & 0xFFFFFF;
|
|
|
|
assert(i0 < func->local_count);
|
|
|
|
push(vm, func->local_regs[i0]);
|
|
|
|
return 0;
|
2021-04-06 17:27:02 +03:00
|
|
|
//
|
2021-04-06 18:22:12 +03:00
|
|
|
case OP_ISZ:
|
2021-04-06 17:27:02 +03:00
|
|
|
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);
|
|
|
|
}
|
2021-04-06 18:22:12 +03:00
|
|
|
case OP_GCALL:
|
|
|
|
i0 = opcode & 0xFFFFFF;
|
|
|
|
assert(i0 < vm->global_pool_size);
|
|
|
|
w0 = vm->global_pool[i0];
|
|
|
|
assert(func_q(w0));
|
|
|
|
vm_call_ref(vm, getref(w0));
|
|
|
|
return 0;
|
2021-04-07 00:03:42 +03:00
|
|
|
case OP_CALL:
|
|
|
|
w0 = pop(vm);
|
|
|
|
assert(func_q(w0));
|
|
|
|
vm_call_ref(vm, getref(w0));
|
|
|
|
return 0;
|
2021-04-06 17:27:02 +03:00
|
|
|
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:
|
2021-04-07 00:03:42 +03:00
|
|
|
if (vm->csp == vm->call_stack_size) {
|
|
|
|
// Return from main
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
vm->ip = vm->call_stack[vm->csp++];
|
|
|
|
vm->fp = vm->call_stack[vm->csp++];
|
|
|
|
return 0;
|
2021-04-06 17:27:02 +03:00
|
|
|
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++];
|
2021-04-07 00:03:42 +03:00
|
|
|
return vm_eval_opcode(vm, func, opcode);
|
2021-04-06 17:27:02 +03:00
|
|
|
}
|