From 1bf0b538cd04b46a9e005b580c34c80d62842d46 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Wed, 31 Jan 2018 21:40:19 -0500 Subject: [PATCH] Use computed goto in vm for GCC --- src/core/string.c | 2 +- src/core/vm.c | 1002 ++++++++++++++++++++++++--------------------- 2 files changed, 539 insertions(+), 465 deletions(-) diff --git a/src/core/string.c b/src/core/string.c index bcec0216..65f88e7f 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -107,7 +107,7 @@ const uint8_t *dst_cstring(const char *str) { static int32_t real_to_string_impl(uint8_t *buf, double x) { /* Use 16 decimal places to ignore one ulp errors for now */ - int count = snprintf((char *) buf, DST_BUFSIZE, "%.16g", x); + int count = snprintf((char *) buf, DST_BUFSIZE, "%.16gR", x); return (int32_t) count; } diff --git a/src/core/vm.c b/src/core/vm.c index 654ae89f..e8aac606 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -63,9 +63,85 @@ static int dst_continue(Dst *returnreg) { * Values stored here should be used immediately */ Dst retreg; -/* Eventually use computed gotos for more effient vm loop. */ +/* Use computed gotos for GCC, otherwise use switch */ +#ifdef __GNUCC__ +#define VM_START() {vm_next(); +#define VM_END() } +#define VM_OP(op) label_##op : +#define VM_DEFAULT() label_unknown_op: +#define vm_next() goto *op_lookup[*pc & 0xFF]; +static void *op_lookup[255] = { + &&label_DOP_NOOP, + &&label_DOP_ERROR, + &&label_DOP_TYPECHECK, + &&label_DOP_RETURN, + &&label_DOP_RETURN_NIL, + &&label_DOP_ADD_INTEGER, + &&label_DOP_ADD_IMMEDIATE, + &&label_DOP_ADD_REAL, + &&label_DOP_ADD, + &&label_DOP_SUBTRACT_INTEGER, + &&label_DOP_SUBTRACT_REAL, + &&label_DOP_SUBTRACT, + &&label_DOP_MULTIPLY_INTEGER, + &&label_DOP_MULTIPLY_IMMEDIATE, + &&label_DOP_MULTIPLY_REAL, + &&label_DOP_MULTIPLY, + &&label_DOP_DIVIDE_INTEGER, + &&label_DOP_DIVIDE_IMMEDIATE, + &&label_DOP_DIVIDE_REAL, + &&label_DOP_DIVIDE, + &&label_DOP_BAND, + &&label_DOP_BOR, + &&label_DOP_BXOR, + &&label_DOP_BNOT, + &&label_DOP_SHIFT_LEFT, + &&label_DOP_SHIFT_LEFT_IMMEDIATE, + &&label_DOP_SHIFT_RIGHT, + &&label_DOP_SHIFT_RIGHT_IMMEDIATE, + &&label_DOP_SHIFT_RIGHT_UNSIGNED, + &&label_DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, + &&label_DOP_MOVE_FAR, + &&label_DOP_MOVE_NEAR, + &&label_DOP_JUMP, + &&label_DOP_JUMP_IF, + &&label_DOP_JUMP_IF_NOT, + &&label_DOP_GREATER_THAN, + &&label_DOP_LESS_THAN, + &&label_DOP_EQUALS, + &&label_DOP_COMPARE, + &&label_DOP_LOAD_NIL, + &&label_DOP_LOAD_TRUE, + &&label_DOP_LOAD_FALSE, + &&label_DOP_LOAD_INTEGER, + &&label_DOP_LOAD_CONSTANT, + &&label_DOP_LOAD_UPVALUE, + &&label_DOP_LOAD_SELF, + &&label_DOP_SET_UPVALUE, + &&label_DOP_CLOSURE, + &&label_DOP_PUSH, + &&label_DOP_PUSH_2, + &&label_DOP_PUSH_3, + &&label_DOP_PUSH_ARRAY, + &&label_DOP_CALL, + &&label_DOP_TAILCALL, + &&label_DOP_TRANSFER, + &&label_DOP_GET, + &&label_DOP_PUT, + &&label_DOP_GET_INDEX, + &&label_DOP_PUT_INDEX, + &&label_DOP_LENGTH, + &&label_unknown_op +}; +#else +#define VM_START() for(;;){switch(*pc & 0xFF){ +#define VM_END() }} +#define VM_OP(op) case op : +#define VM_DEFAULT() default: #define vm_next() continue -#define vm_checkgc_next() dst_maybe_collect(); continue +#endif + +#define vm_checkgc_next() dst_maybe_collect(); vm_next() /* Used to extract bits from the opcode that correspond to arguments. * Pulls out unsigned integers */ @@ -125,529 +201,527 @@ static int dst_continue(Dst *returnreg) { * to this loop, adding the opcode to opcodes.h, and adding it to the assembler. * Some opcodes, especially ones that do arithmetic, are almost entirely * templated by the above macros. */ - for (;;) { + VM_START(); - /*dst_puts(dst_formatc("trace: %C\n", dst_asm_decode_instruction(*pc)));*/ + VM_DEFAULT(); + retreg = dst_wrap_string(dst_formatc("unknown opcode %d", *pc & 0xFF)); + goto vm_error; - switch (*pc & 0xFF) { + VM_OP(DOP_NOOP) + pc++; + vm_next(); - default: - retreg = dst_wrap_string(dst_formatc("unknown opcode %d", *pc & 0xFF)); - goto vm_error; + VM_OP(DOP_ERROR) + retreg = stack[oparg(1, 0xFF)]; + goto vm_error; - case DOP_NOOP: - pc++; - vm_next(); + VM_OP(DOP_TYPECHECK) + vm_assert((1 << dst_type(stack[oparg(1, 0xFF)])) & oparg(2, 0xFFFF), "typecheck failed"); + pc++; + vm_next(); - case DOP_ERROR: - retreg = stack[oparg(1, 0xFF)]; - goto vm_error; + VM_OP(DOP_RETURN) + retreg = stack[oparg(1, 0xFFFFFF)]; + goto vm_return; - case DOP_TYPECHECK: - vm_assert((1 << dst_type(stack[oparg(1, 0xFF)])) & oparg(2, 0xFFFF), - "typecheck failed"); - pc++; - vm_next(); + VM_OP(DOP_RETURN_NIL) + retreg = dst_wrap_nil(); + goto vm_return; - case DOP_RETURN: - retreg = stack[oparg(1, 0xFFFFFF)]; - goto vm_return; + VM_OP(DOP_ADD_INTEGER) + vm_binop_integer(+); - case DOP_RETURN_NIL: - retreg = dst_wrap_nil(); - goto vm_return; + VM_OP(DOP_ADD_IMMEDIATE) + vm_binop_immediate(+); - case DOP_ADD_INTEGER: - vm_binop_integer(+); + VM_OP(DOP_ADD_REAL) + vm_binop_real(+); - case DOP_ADD_IMMEDIATE: - vm_binop_immediate(+); + VM_OP(DOP_ADD) + vm_binop(+); - case DOP_ADD_REAL: - vm_binop_real(+); + VM_OP(DOP_SUBTRACT_INTEGER) + vm_binop_integer(-); - case DOP_ADD: - vm_binop(+); + VM_OP(DOP_SUBTRACT_REAL) + vm_binop_real(-); - case DOP_SUBTRACT_INTEGER: - vm_binop_integer(-); - - case DOP_SUBTRACT_REAL: - vm_binop_real(-); - - case DOP_SUBTRACT: - vm_binop(-); - - case DOP_MULTIPLY_INTEGER: - vm_binop_integer(*); - - case DOP_MULTIPLY_IMMEDIATE: - vm_binop_immediate(*); - - case DOP_MULTIPLY_REAL: - vm_binop_real(*); - - case DOP_MULTIPLY: - vm_binop(*); - - case DOP_DIVIDE_INTEGER: - vm_assert(dst_unwrap_integer(stack[oparg(3, 0xFF)]) != 0, "integer divide error"); - vm_assert(!(dst_unwrap_integer(stack[oparg(3, 0xFF)]) == -1 && - dst_unwrap_integer(stack[oparg(2, 0xFF)]) == INT32_MIN), - "integer divide error"); - vm_binop_integer(/); + VM_OP(DOP_SUBTRACT) + vm_binop(-); - case DOP_DIVIDE_IMMEDIATE: - { - int32_t op1 = dst_unwrap_integer(stack[oparg(2, 0xFF)]); - int32_t op2 = *((int32_t *)pc) >> 24; - /* Check for degenerate integer division (divide by zero, and dividing - * min value by -1). These checks could be omitted if the arg is not - * 0 or -1. */ - if (op2 == 0) - vm_throw("integer divide error"); - if (op2 == -1 && op1 == INT32_MIN) - vm_throw("integer divide error"); - else - stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); - pc++; - vm_next(); - } + VM_OP(DOP_MULTIPLY_INTEGER) + vm_binop_integer(*); - case DOP_DIVIDE_REAL: - vm_binop_real(/); + VM_OP(DOP_MULTIPLY_IMMEDIATE) + vm_binop_immediate(*); - case DOP_DIVIDE: - { - Dst op1 = stack[oparg(2, 0xFF)]; - Dst op2 = stack[oparg(3, 0xFF)]; - vm_assert(dst_checktype(op1, DST_INTEGER) || dst_checktype(op1, DST_REAL), "expected number"); - vm_assert(dst_checktype(op2, DST_INTEGER) || dst_checktype(op2, DST_REAL), "expected number"); - if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == 0) - vm_throw("integer divide error"); - if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == -1 && - dst_checktype(op1, DST_INTEGER) && dst_unwrap_integer(op1) == INT32_MIN) - vm_throw("integer divide error"); - stack[oparg(1, 0xFF)] = dst_checktype(op1, DST_INTEGER) - ? (dst_checktype(op2, DST_INTEGER) - ? dst_wrap_integer(dst_unwrap_integer(op1) / dst_unwrap_integer(op2)) - : dst_wrap_real((double)dst_unwrap_integer(op1) / dst_unwrap_real(op2))) - : (dst_checktype(op2, DST_INTEGER) - ? dst_wrap_real(dst_unwrap_real(op1) / (double)dst_unwrap_integer(op2)) - : dst_wrap_real(dst_unwrap_real(op1) / dst_unwrap_real(op2))); - pc++; - vm_next(); - } + VM_OP(DOP_MULTIPLY_REAL) + vm_binop_real(*); - case DOP_BAND: - vm_binop_integer(&); + VM_OP(DOP_MULTIPLY) + vm_binop(*); - case DOP_BOR: - vm_binop_integer(|); - - case DOP_BXOR: - vm_binop_integer(^); - - case DOP_BNOT: - stack[oparg(1, 0xFF)] = dst_wrap_integer(~dst_unwrap_integer(stack[oparg(2, 0xFFFF)])); - vm_next(); - - case DOP_SHIFT_RIGHT_UNSIGNED: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - (int32_t)(((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)])) - >> - dst_unwrap_integer(stack[oparg(3, 0xFF)])) - ); + VM_OP(DOP_DIVIDE_INTEGER) + vm_assert(dst_unwrap_integer(stack[oparg(3, 0xFF)]) != 0, "integer divide error"); + vm_assert(!(dst_unwrap_integer(stack[oparg(3, 0xFF)]) == -1 && + dst_unwrap_integer(stack[oparg(2, 0xFF)]) == INT32_MIN), + "integer divide error"); + vm_binop_integer(/); + + VM_OP(DOP_DIVIDE_IMMEDIATE) + { + int32_t op1 = dst_unwrap_integer(stack[oparg(2, 0xFF)]); + int32_t op2 = *((int32_t *)pc) >> 24; + /* Check for degenerate integer division (divide by zero, and dividing + * min value by -1). These checks could be omitted if the arg is not + * 0 or -1. */ + if (op2 == 0) + vm_throw("integer divide error"); + if (op2 == -1 && op1 == INT32_MIN) + vm_throw("integer divide error"); + else + stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); pc++; vm_next(); + } - case DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - (int32_t) (((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)])) >> oparg(3, 0xFF)) - ); + VM_OP(DOP_DIVIDE_REAL) + vm_binop_real(/); + + VM_OP(DOP_DIVIDE) + { + Dst op1 = stack[oparg(2, 0xFF)]; + Dst op2 = stack[oparg(3, 0xFF)]; + vm_assert(dst_checktype(op1, DST_INTEGER) || dst_checktype(op1, DST_REAL), "expected number"); + vm_assert(dst_checktype(op2, DST_INTEGER) || dst_checktype(op2, DST_REAL), "expected number"); + if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == 0) + vm_throw("integer divide error"); + if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == -1 && + dst_checktype(op1, DST_INTEGER) && dst_unwrap_integer(op1) == INT32_MIN) + vm_throw("integer divide error"); + stack[oparg(1, 0xFF)] = dst_checktype(op1, DST_INTEGER) + ? (dst_checktype(op2, DST_INTEGER) + ? dst_wrap_integer(dst_unwrap_integer(op1) / dst_unwrap_integer(op2)) + : dst_wrap_real((double)dst_unwrap_integer(op1) / dst_unwrap_real(op2))) + : (dst_checktype(op2, DST_INTEGER) + ? dst_wrap_real(dst_unwrap_real(op1) / (double)dst_unwrap_integer(op2)) + : dst_wrap_real(dst_unwrap_real(op1) / dst_unwrap_real(op2))); pc++; vm_next(); + } - case DOP_SHIFT_RIGHT: - vm_binop_integer(>>); + VM_OP(DOP_BAND) + vm_binop_integer(&); - case DOP_SHIFT_RIGHT_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - (int32_t)(dst_unwrap_integer(stack[oparg(2, 0xFF)]) >> oparg(3, 0xFF)) - ); + VM_OP(DOP_BOR) + vm_binop_integer(|); + + VM_OP(DOP_BXOR) + vm_binop_integer(^); + + VM_OP(DOP_BNOT) + stack[oparg(1, 0xFF)] = dst_wrap_integer(~dst_unwrap_integer(stack[oparg(2, 0xFFFF)])); + vm_next(); + + VM_OP(DOP_SHIFT_RIGHT_UNSIGNED) + stack[oparg(1, 0xFF)] = dst_wrap_integer( + (int32_t)(((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)])) + >> + dst_unwrap_integer(stack[oparg(3, 0xFF)])) + ); + pc++; + vm_next(); + + VM_OP(DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE) + stack[oparg(1, 0xFF)] = dst_wrap_integer( + (int32_t) (((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)])) >> oparg(3, 0xFF)) + ); + pc++; + vm_next(); + + VM_OP(DOP_SHIFT_RIGHT) + vm_binop_integer(>>); + + VM_OP(DOP_SHIFT_RIGHT_IMMEDIATE) + stack[oparg(1, 0xFF)] = dst_wrap_integer( + (int32_t)(dst_unwrap_integer(stack[oparg(2, 0xFF)]) >> oparg(3, 0xFF)) + ); + pc++; + vm_next(); + + VM_OP(DOP_SHIFT_LEFT) + vm_binop_integer(<<); + + VM_OP(DOP_SHIFT_LEFT_IMMEDIATE) + stack[oparg(1, 0xFF)] = dst_wrap_integer( + dst_unwrap_integer(stack[oparg(2, 0xFF)]) << oparg(3, 0xFF) + ); + pc++; + vm_next(); + + VM_OP(DOP_MOVE_NEAR) + stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; + pc++; + vm_next(); + + VM_OP(DOP_MOVE_FAR) + stack[oparg(2, 0xFFFF)] = stack[oparg(1, 0xFF)]; + pc++; + vm_next(); + + VM_OP(DOP_JUMP) + pc += (*(int32_t *)pc) >> 8; + vm_next(); + + VM_OP(DOP_JUMP_IF) + if (dst_truthy(stack[oparg(1, 0xFF)])) { + pc += (*(int32_t *)pc) >> 16; + } else { pc++; - vm_next(); + } + vm_next(); - case DOP_SHIFT_LEFT: - vm_binop_integer(<<); - - case DOP_SHIFT_LEFT_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - dst_unwrap_integer(stack[oparg(2, 0xFF)]) << oparg(3, 0xFF) - ); + VM_OP(DOP_JUMP_IF_NOT) + if (dst_truthy(stack[oparg(1, 0xFF)])) { pc++; - vm_next(); + } else { + pc += (*(int32_t *)pc) >> 16; + } + vm_next(); - case DOP_MOVE_NEAR: - stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; - pc++; - vm_next(); + VM_OP(DOP_LESS_THAN) + stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ) < 0); + pc++; + vm_next(); - case DOP_MOVE_FAR: - stack[oparg(2, 0xFFFF)] = stack[oparg(1, 0xFF)]; - pc++; - vm_next(); + VM_OP(DOP_GREATER_THAN) + stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ) > 0); + pc++; + vm_next(); - case DOP_JUMP: - pc += (*(int32_t *)pc) >> 8; - vm_next(); - - case DOP_JUMP_IF: - if (dst_truthy(stack[oparg(1, 0xFF)])) { - pc += (*(int32_t *)pc) >> 16; - } else { - pc++; - } - vm_next(); - - case DOP_JUMP_IF_NOT: - if (dst_truthy(stack[oparg(1, 0xFF)])) { - pc++; - } else { - pc += (*(int32_t *)pc) >> 16; - } - vm_next(); - - case DOP_LESS_THAN: - stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ) < 0); - pc++; - vm_next(); - - case DOP_GREATER_THAN: - stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ) > 0); - pc++; - vm_next(); - - case DOP_EQUALS: - stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_equals( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - )); - pc++; - vm_next(); - - case DOP_COMPARE: - stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_compare( + VM_OP(DOP_EQUALS) + stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_equals( stack[oparg(2, 0xFF)], stack[oparg(3, 0xFF)] )); + pc++; + vm_next(); + + VM_OP(DOP_COMPARE) + stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + )); + pc++; + vm_next(); + + VM_OP(DOP_LOAD_NIL) + stack[oparg(1, 0xFFFFFF)] = dst_wrap_nil(); + pc++; + vm_next(); + + VM_OP(DOP_LOAD_TRUE) + stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(1); + pc++; + vm_next(); + + VM_OP(DOP_LOAD_FALSE) + stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(0); + pc++; + vm_next(); + + VM_OP(DOP_LOAD_INTEGER) + stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); + pc++; + vm_next(); + + VM_OP(DOP_LOAD_CONSTANT) + { + int32_t index = oparg(2, 0xFFFF); + vm_assert(index < func->def->constants_length, "invalid constant"); + stack[oparg(1, 0xFF)] = func->def->constants[index]; pc++; vm_next(); + } - case DOP_LOAD_NIL: - stack[oparg(1, 0xFFFFFF)] = dst_wrap_nil(); - pc++; - vm_next(); + VM_OP(DOP_LOAD_SELF) + stack[oparg(1, 0xFFFFFF)] = dst_wrap_function(func); + pc++; + vm_next(); - case DOP_LOAD_TRUE: - stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(1); - pc++; - vm_next(); - - case DOP_LOAD_FALSE: - stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(0); - pc++; - vm_next(); - - case DOP_LOAD_INTEGER: - stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); - pc++; - vm_next(); - - case DOP_LOAD_CONSTANT: - { - int32_t index = oparg(2, 0xFFFF); - vm_assert(index < func->def->constants_length, "invalid constant"); - stack[oparg(1, 0xFF)] = func->def->constants[index]; - pc++; - vm_next(); + VM_OP(DOP_LOAD_UPVALUE) + { + int32_t eindex = oparg(2, 0xFF); + int32_t vindex = oparg(3, 0xFF); + DstFuncEnv *env; + vm_assert(func->def->environments_length > eindex, "invalid upvalue"); + env = func->envs[eindex]; + vm_assert(env->length > vindex, "invalid upvalue"); + if (env->offset) { + /* On stack */ + stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex]; + } else { + /* Off stack */ + stack[oparg(1, 0xFF)] = env->as.values[vindex]; } - - case DOP_LOAD_SELF: - stack[oparg(1, 0xFFFFFF)] = dst_wrap_function(func); pc++; vm_next(); + } - case DOP_LOAD_UPVALUE: - { - int32_t eindex = oparg(2, 0xFF); - int32_t vindex = oparg(3, 0xFF); - DstFuncEnv *env; - vm_assert(func->def->environments_length > eindex, "invalid upvalue"); - env = func->envs[eindex]; - vm_assert(env->length > vindex, "invalid upvalue"); - if (env->offset) { - /* On stack */ - stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex]; - } else { - /* Off stack */ - stack[oparg(1, 0xFF)] = env->as.values[vindex]; - } - pc++; - vm_next(); + VM_OP(DOP_SET_UPVALUE) + { + int32_t eindex = oparg(2, 0xFF); + int32_t vindex = oparg(3, 0xFF); + DstFuncEnv *env; + vm_assert(func->def->environments_length > eindex, "invalid upvalue"); + env = func->envs[eindex]; + vm_assert(env->length > vindex, "invalid upvalue"); + if (env->offset) { + env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)]; + } else { + env->as.values[vindex] = stack[oparg(1, 0xFF)]; } + pc++; + vm_next(); + } - case DOP_SET_UPVALUE: - { - int32_t eindex = oparg(2, 0xFF); - int32_t vindex = oparg(3, 0xFF); - DstFuncEnv *env; - vm_assert(func->def->environments_length > eindex, "invalid upvalue"); - env = func->envs[eindex]; - vm_assert(env->length > vindex, "invalid upvalue"); - if (env->offset) { - env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)]; - } else { - env->as.values[vindex] = stack[oparg(1, 0xFF)]; - } - pc++; - vm_next(); + VM_OP(DOP_CLOSURE) + { + DstFuncDef *fd; + vm_assert((int32_t)oparg(2, 0xFFFF) < func->def->defs_length, "invalid funcdef"); + fd = func->def->defs[(int32_t)oparg(2, 0xFFFF)]; + stack[oparg(1, 0xFF)] = dst_wrap_function(dst_function(fd, func)); + pc++; + vm_checkgc_next(); + } + + VM_OP(DOP_PUSH) + dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); + pc++; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + vm_checkgc_next(); + + VM_OP(DOP_PUSH_2) + dst_fiber_push2(dst_vm_fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFFFF)]); + pc++; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + vm_checkgc_next(); + + VM_OP(DOP_PUSH_3) + dst_fiber_push3(dst_vm_fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + pc++; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + vm_checkgc_next(); + + VM_OP(DOP_PUSH_ARRAY) + { + const Dst *vals; + int32_t len; + if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &vals, &len)) { + dst_fiber_pushn(dst_vm_fiber, vals, len); + } else { + vm_throw("expected array/tuple"); } + } + pc++; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + vm_checkgc_next(); - case DOP_CLOSURE: - { - DstFuncDef *fd; - vm_assert((int32_t)oparg(2, 0xFFFF) < func->def->defs_length, "invalid funcdef"); - fd = func->def->defs[(int32_t)oparg(2, 0xFFFF)]; - stack[oparg(1, 0xFF)] = dst_wrap_function(dst_function(fd, func)); - pc++; + VM_OP(DOP_CALL) + { + Dst callee = stack[oparg(2, 0xFFFF)]; + if (dst_checktype(callee, DST_FUNCTION)) { + func = dst_unwrap_function(callee); + dst_stack_frame(stack)->pc = pc; + dst_fiber_funcframe(dst_vm_fiber, func); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + pc = func->def->bytecode; vm_checkgc_next(); + } else if (dst_checktype(callee, DST_CFUNCTION)) { + DstArgs args; + args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart; + dst_fiber_cframe(dst_vm_fiber); + retreg = dst_wrap_nil(); + args.v = dst_vm_fiber->data + dst_vm_fiber->frame; + args.ret = &retreg; + if (dst_unwrap_cfunction(callee)(args)) { + goto vm_error; + } + goto vm_return_cfunc; } + vm_throw("expected function"); + } - case DOP_PUSH: - dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); - pc++; - stack = dst_vm_fiber->data + dst_vm_fiber->frame; + VM_OP(DOP_TAILCALL) + { + Dst callee = stack[oparg(1, 0xFFFFFF)]; + if (dst_checktype(callee, DST_FUNCTION)) { + func = dst_unwrap_function(callee); + dst_fiber_funcframe_tail(dst_vm_fiber, func); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + pc = func->def->bytecode; + vm_checkgc_next(); + } else if (dst_checktype(callee, DST_CFUNCTION)) { + DstArgs args; + args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart; + dst_fiber_cframe(dst_vm_fiber); + retreg = dst_wrap_nil(); + args.v = dst_vm_fiber->data + dst_vm_fiber->frame; + args.ret = &retreg; + if (dst_unwrap_cfunction(callee)(args)) { + goto vm_error; + } + goto vm_return_cfunc_tail; + } + vm_throw("expected function"); + } + + VM_OP(DOP_TRANSFER) + { + int status; + DstFiber *nextfiber; + DstStackFrame *frame = dst_stack_frame(stack); + Dst temp = stack[oparg(2, 0xFF)]; + retreg = stack[oparg(3, 0xFF)]; + vm_assert(dst_checktype(temp, DST_FIBER) || + dst_checktype(temp, DST_NIL), "expected fiber"); + nextfiber = dst_checktype(temp, DST_FIBER) + ? dst_unwrap_fiber(temp) + : dst_vm_fiber->parent; + /* Check for root fiber */ + if (NULL == nextfiber) { + frame->pc = pc; + *returnreg = retreg; + return 0; + } + status = nextfiber->status; + vm_assert(status == DST_FIBER_PENDING || + status == DST_FIBER_NEW, "can only transfer to new or pending fiber"); + frame->pc = pc; + dst_vm_fiber->status = DST_FIBER_PENDING; + dst_vm_fiber = nextfiber; + vm_init_fiber_state(); + if (status == DST_FIBER_PENDING) { + /* The next fiber is currently on a transfer instruction. */ + stack[oparg(1, 0xFF)] = retreg; + pc++; + } else { + /* The next fiber is new and is on the first instruction */ + if ((func->def->flags & DST_FUNCDEF_FLAG_VARARG) && + !func->def->arity) { + /* Fully var arg function */ + Dst *tup = dst_tuple_begin(1); + tup[0] = retreg; + stack[0] = dst_wrap_tuple(dst_tuple_end(tup)); + } else if (func->def->arity) { + /* Non zero arity function */ + stack[0] = retreg; + } + } vm_checkgc_next(); + } - case DOP_PUSH_2: - dst_fiber_push2(dst_vm_fiber, - stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFFFF)]); - pc++; - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - vm_checkgc_next(); - - case DOP_PUSH_3: - dst_fiber_push3(dst_vm_fiber, - stack[oparg(1, 0xFF)], + VM_OP(DOP_PUT) + dst_put(stack[oparg(1, 0xFF)], stack[oparg(2, 0xFF)], stack[oparg(3, 0xFF)]); - pc++; + ++pc; + vm_checkgc_next(); + + VM_OP(DOP_PUT_INDEX) + dst_setindex(stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + oparg(3, 0xFF)); + ++pc; + vm_checkgc_next(); + + VM_OP(DOP_GET) + stack[oparg(1, 0xFF)] = dst_get( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + ++pc; + vm_next(); + + VM_OP(DOP_GET_INDEX) + stack[oparg(1, 0xFF)] = dst_getindex( + stack[oparg(2, 0xFF)], + oparg(3, 0xFF)); + ++pc; + vm_next(); + + VM_OP(DOP_LENGTH) + stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_length(stack[oparg(2, 0xFFFF)])); + ++pc; + vm_next(); + + /* Return from c function. Simpler than retuning from dst function */ + vm_return_cfunc: + { + dst_fiber_popframe(dst_vm_fiber); + if (dst_update_fiber()) { + *returnreg = retreg; + return 0; + } stack = dst_vm_fiber->data + dst_vm_fiber->frame; - vm_checkgc_next(); - - case DOP_PUSH_ARRAY: - { - const Dst *vals; - int32_t len; - if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &vals, &len)) { - dst_fiber_pushn(dst_vm_fiber, vals, len); - } else { - vm_throw("expected array/tuple"); - } - } + stack[oparg(1, 0xFF)] = retreg; pc++; + vm_checkgc_next(); + } + + vm_return_cfunc_tail: + { + dst_fiber_popframe(dst_vm_fiber); + if (dst_update_fiber()) { + *returnreg = retreg; + return 0; + } + /* Fall through to normal return */ + } + + /* Handle returning from stack frame. Expect return value in retreg */ + vm_return: + { + dst_fiber_popframe(dst_vm_fiber); + if (dst_update_fiber()) { + *returnreg = retreg; + return 0; + } stack = dst_vm_fiber->data + dst_vm_fiber->frame; + func = dst_stack_frame(stack)->func; + pc = dst_stack_frame(stack)->pc; + stack[oparg(1, 0xFF)] = retreg; + pc++; vm_checkgc_next(); + } - case DOP_CALL: - { - Dst callee = stack[oparg(2, 0xFFFF)]; - if (dst_checktype(callee, DST_FUNCTION)) { - func = dst_unwrap_function(callee); - dst_stack_frame(stack)->pc = pc; - dst_fiber_funcframe(dst_vm_fiber, func); - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - pc = func->def->bytecode; - vm_checkgc_next(); - } else if (dst_checktype(callee, DST_CFUNCTION)) { - DstArgs args; - args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart; - dst_fiber_cframe(dst_vm_fiber); - retreg = dst_wrap_nil(); - args.v = dst_vm_fiber->data + dst_vm_fiber->frame; - args.ret = &retreg; - if (dst_unwrap_cfunction(callee)(args)) { - goto vm_error; - } - goto vm_return_cfunc; - } - vm_throw("expected function"); + /* Handle errors from c functions and vm opcodes */ + vm_error: + { + dst_vm_fiber->status = DST_FIBER_ERROR; + if (dst_update_fiber()) { + *returnreg = retreg; + return 1; } - - case DOP_TAILCALL: - { - Dst callee = stack[oparg(1, 0xFFFFFF)]; - if (dst_checktype(callee, DST_FUNCTION)) { - func = dst_unwrap_function(callee); - dst_fiber_funcframe_tail(dst_vm_fiber, func); - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - pc = func->def->bytecode; - vm_checkgc_next(); - } else if (dst_checktype(callee, DST_CFUNCTION)) { - DstArgs args; - args.n = dst_vm_fiber->stacktop - dst_vm_fiber->stackstart; - dst_fiber_cframe(dst_vm_fiber); - retreg = dst_wrap_nil(); - args.v = dst_vm_fiber->data + dst_vm_fiber->frame; - args.ret = &retreg; - if (dst_unwrap_cfunction(callee)(args)) { - goto vm_error; - } - goto vm_return_cfunc_tail; - } - vm_throw("expected function"); - } - - case DOP_TRANSFER: - { - int status; - DstFiber *nextfiber; - DstStackFrame *frame = dst_stack_frame(stack); - Dst temp = stack[oparg(2, 0xFF)]; - retreg = stack[oparg(3, 0xFF)]; - vm_assert(dst_checktype(temp, DST_FIBER) || - dst_checktype(temp, DST_NIL), "expected fiber"); - nextfiber = dst_checktype(temp, DST_FIBER) - ? dst_unwrap_fiber(temp) - : dst_vm_fiber->parent; - /* Check for root fiber */ - if (NULL == nextfiber) { - frame->pc = pc; - *returnreg = retreg; - return 0; - } - status = nextfiber->status; - vm_assert(status == DST_FIBER_PENDING || - status == DST_FIBER_NEW, "can only transfer to new or pending fiber"); - frame->pc = pc; - dst_vm_fiber->status = DST_FIBER_PENDING; - dst_vm_fiber = nextfiber; - vm_init_fiber_state(); - if (status == DST_FIBER_PENDING) { - /* The next fiber is currently on a transfer instruction. */ - stack[oparg(1, 0xFF)] = retreg; - pc++; - } else { - /* The next fiber is new and is on the first instruction */ - if ((func->def->flags & DST_FUNCDEF_FLAG_VARARG) && - !func->def->arity) { - /* Fully var arg function */ - Dst *tup = dst_tuple_begin(1); - tup[0] = retreg; - stack[0] = dst_wrap_tuple(dst_tuple_end(tup)); - } else if (func->def->arity) { - /* Non zero arity function */ - stack[0] = retreg; - } - } - vm_checkgc_next(); - } - - case DOP_PUT: - dst_put(stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)]); - ++pc; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + func = dst_stack_frame(stack)->func; + pc = dst_stack_frame(stack)->pc; + stack[oparg(1, 0xFF)] = retreg; + pc++; vm_checkgc_next(); - - case DOP_PUT_INDEX: - dst_setindex(stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFF)], - oparg(3, 0xFF)); - ++pc; - vm_checkgc_next(); - - case DOP_GET: - stack[oparg(1, 0xFF)] = dst_get( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)]); - ++pc; - vm_next(); - - case DOP_GET_INDEX: - stack[oparg(1, 0xFF)] = dst_getindex( - stack[oparg(2, 0xFF)], - oparg(3, 0xFF)); - ++pc; - vm_next(); - - /* Return from c function. Simpler than retuning from dst function */ - vm_return_cfunc: - { - dst_fiber_popframe(dst_vm_fiber); - if (dst_update_fiber()) { - *returnreg = retreg; - return 0; - } - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - stack[oparg(1, 0xFF)] = retreg; - pc++; - vm_checkgc_next(); - } - - vm_return_cfunc_tail: - { - dst_fiber_popframe(dst_vm_fiber); - if (dst_update_fiber()) { - *returnreg = retreg; - return 0; - } - /* Fall through to normal return */ - } - - /* Handle returning from stack frame. Expect return value in retreg */ - vm_return: - { - dst_fiber_popframe(dst_vm_fiber); - if (dst_update_fiber()) { - *returnreg = retreg; - return 0; - } - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - func = dst_stack_frame(stack)->func; - pc = dst_stack_frame(stack)->pc; - stack[oparg(1, 0xFF)] = retreg; - pc++; - vm_checkgc_next(); - } - - /* Handle errors from c functions and vm opcodes */ - vm_error: - { - dst_vm_fiber->status = DST_FIBER_ERROR; - if (dst_update_fiber()) { - *returnreg = retreg; - return 1; - } - stack = dst_vm_fiber->data + dst_vm_fiber->frame; - func = dst_stack_frame(stack)->func; - pc = dst_stack_frame(stack)->pc; - stack[oparg(1, 0xFF)] = retreg; - pc++; - vm_checkgc_next(); - } + } - } /* end switch */ - - } /* end for */ + VM_END() #undef oparg