diff --git a/Makefile b/Makefile index 9ec37d84..ab867166 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ PREFIX?=/usr/local BINDIR=$(PREFIX)/bin VERSION=\"0.0.0-beta\" -CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) +CFLAGS=-std=c99 -Wall -m32 -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) PREFIX=/usr/local DST_TARGET=dst DST_XXD=xxd @@ -78,8 +78,8 @@ $(DST_TARGET): $(DST_CORE_OBJECTS) CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST DST_UNIT_BINARIES=$(addprefix unittests/,\ - asm_test.out array_test.out buffer_test.out fiber_test.out parse_test.out \ - table_test.out) + asm_test.out array_test.out buffer_test.out fiber_test.out \ + nanbox_test.out parse_test.out table_test.out) %.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h $(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@ @@ -89,6 +89,7 @@ unit: $(DST_UNIT_BINARIES) unittests/asm_test.out unittests/buffer_test.out unittests/fiber_test.out + unittests/nanbox_test.out unittests/parse_test.out unittests/table_test.out diff --git a/core/string.c b/core/string.c index 707e5f28..e2612026 100644 --- a/core/string.c +++ b/core/string.c @@ -141,7 +141,7 @@ const uint8_t *dst_cstring(const char *str) { #define DST_BUFSIZE 36 static uint32_t real_to_string_impl(uint8_t *buf, double x) { - int count = snprintf((char *) buf, DST_BUFSIZE, "%.21gF", x); + int count = snprintf((char *) buf, DST_BUFSIZE, "%.21g", x); return (uint32_t) count; } diff --git a/core/syscalls.c b/core/syscalls.c index ad90c676..e6b12348 100644 --- a/core/syscalls.c +++ b/core/syscalls.c @@ -101,22 +101,36 @@ int dst_sys_struct(DstValue *argv, uint32_t argn) { int dst_sys_get(DstValue *argv, uint32_t argn) { uint32_t i; - if (argn < 2) { + DstValue ds; + if (argn < 1) { dst_vm_fiber->ret = dst_cstringv("expected at least 1 argument"); return 1; } - DstValue ds = argv[0]; + ds = argv[0]; for (i = 1; i < argn; i++) { - const char *err = dst_try_get(ds, argv[i], &ds); - if (NULL != err) { - dst_vm_fiber->ret = dst_cstringv(err); - return 1; - } + ds = dst_get(ds, argv[i]); + if (ds.type == DST_NIL) + break; } dst_vm_fiber->ret = ds; return 0; } +int dst_sys_put(DstValue *argv, uint32_t argn) { + DstValue ds, key, value; + if (argn < 3) { + dst_vm_fiber->ret = dst_cstringv("expected at least 3 arguments"); + return 1; + } + if(dst_sys_get(argv, argn - 2)) + return 1; + ds = dst_vm_fiber->ret; + key = argv[argn - 2]; + value = argv[argn - 1]; + dst_put(ds, key, value); + return 0; +} + DstCFunction dst_vm_syscalls[256] = { dst_sys_print, dst_sys_asm, @@ -124,5 +138,7 @@ DstCFunction dst_vm_syscalls[256] = { dst_sys_array, dst_sys_struct, dst_sys_table, + dst_sys_get, + dst_sys_put, NULL }; diff --git a/core/value.c b/core/value.c index 476f3f5f..586b5955 100644 --- a/core/value.c +++ b/core/value.c @@ -193,98 +193,80 @@ int dst_compare(DstValue x, DstValue y) { return 1; } -/* Get a value out af an associated data structure. - * Returns possible c error message, and NULL for no error. The - * useful return value is written to out on success */ -const char *dst_try_get(DstValue ds, DstValue key, DstValue *out) { - int64_t index; - DstValue ret; +/* Get a value out af an associated data structure. For invalid + * data structure or invalid key, returns nil. */ +DstValue dst_get(DstValue ds, DstValue key) { switch (ds.type) { case DST_ARRAY: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (index < 0 || index >= ds.as.array->count) - return "invalid array access"; - ret = ds.as.array->data[index]; + if (key.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < ds.as.array->count) + return ds.as.array->data[key.as.integer]; break; case DST_TUPLE: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (index < 0 || index >= dst_tuple_length(ds.as.tuple)) - return "invalid tuple access"; - ret = ds.as.tuple[index]; + if (key.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < dst_tuple_length(ds.as.tuple)) + return ds.as.tuple[key.as.integer]; break; case DST_BUFFER: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (index < 0 || index >= ds.as.buffer->count) - return "invalid buffer access"; - ret.type = DST_INTEGER; - ret.as.integer = ds.as.buffer->data[index]; + if (key.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < ds.as.buffer->count) + return dst_wrap_integer(ds.as.buffer->data[key.as.integer]); break; case DST_STRING: case DST_SYMBOL: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (index < 0 || index >= dst_string_length(ds.as.string)) - return "invalid string access"; - ret.type = DST_INTEGER; - ret.as.integer = ds.as.string[index]; + if (key.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < dst_string_length(ds.as.string)) + return dst_wrap_integer(ds.as.string[key.as.integer]); break; case DST_STRUCT: - ret = dst_struct_get(ds.as.st, key); - break; + return dst_struct_get(ds.as.st, key); case DST_TABLE: - ret = dst_table_get(ds.as.table, key); - break; + return dst_table_get(ds.as.table, key); default: - return "cannot get"; + break; } - *out = ret; - return NULL; + return dst_wrap_nil(); } /* Set a value in an associative data structure. Returns possible * error message, and NULL if no error. */ -const char *dst_try_put(DstValue ds, DstValue key, DstValue value) { - int64_t index; +void dst_put(DstValue ds, DstValue key, DstValue value) { switch (ds.type) { case DST_ARRAY: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (index < 0 || index >= ds.as.array->count) - return "invalid array access"; - ds.as.array->data[index] = value; - break; + if (key.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < ds.as.array->count) + ds.as.array->data[key.as.integer] = value; + return; case DST_BUFFER: - if (key.type != DST_INTEGER) return "expected integer key"; - index = key.as.integer; - if (value.type != DST_INTEGER) return "expected integer value"; - if (index < 0 || index >= ds.as.buffer->count) - return "invalid buffer access"; - ds.as.buffer->data[index] = (uint8_t) value.as.integer; - break; + if (key.type == DST_INTEGER && + value.type == DST_INTEGER && + key.as.integer >= 0 && + key.as.integer < ds.as.buffer->count) + ds.as.buffer->data[key.as.integer] = value.as.integer; + return; case DST_TABLE: dst_table_put(ds.as.table, key, value); - break; + return; default: - return "cannot set"; + return; } - return NULL; } /* Get the next key in an associative data structure. Used for iterating through an * associative data structure. */ -const char *dst_try_next(DstValue ds, DstValue key, DstValue *out) { +DstValue dst_next(DstValue ds, DstValue key) { switch(ds.type) { default: - return "expected table or struct"; + return dst_wrap_nil(); case DST_TABLE: - *out = dst_table_next(ds.as.table, key); - return NULL; + return dst_table_next(ds.as.table, key); case DST_STRUCT: - *out = dst_struct_next(ds.as.st, key); - return NULL; + return dst_struct_next(ds.as.st, key); } } diff --git a/core/vm.c b/core/vm.c index 5ee1eecc..5f7c9353 100644 --- a/core/vm.c +++ b/core/vm.c @@ -49,6 +49,11 @@ static int dst_update_fiber() { return 0; } +/* Eventually use computed gotos for more effient vm loop. */ +#define vm_next() continue +#define vm_checkgc_next() dst_maybe_collect(); continue + + /* Start running the VM from where it left off. */ int dst_continue() { @@ -69,21 +74,21 @@ int dst_continue() { stack[oparg(2, 0xFF)].as.integer op stack[oparg(3, 0xFF)].as.integer\ );\ pc++;\ - continue; + vm_next(); #define vm_binop_real(op)\ stack[oparg(1, 0xFF)] = dst_wrap_real(\ stack[oparg(2, 0xFF)].as.real op stack[oparg(3, 0xFF)].as.real\ );\ pc++;\ - continue; + vm_next(); #define vm_binop_immediate(op)\ stack[oparg(1, 0xFF)] = dst_wrap_integer(\ stack[oparg(2, 0xFF)].as.integer op (*((int32_t *)pc) >> 24)\ );\ pc++;\ - continue; + vm_next(); #define vm_binop(op)\ {\ @@ -99,7 +104,7 @@ int dst_continue() { ? dst_wrap_real(op1.as.real op dst_integer_to_real(op2.as.integer))\ : dst_wrap_real(op1.as.real op op2.as.real));\ pc++;\ - continue;\ + vm_next();\ } #define vm_init_fiber_state() \ @@ -120,344 +125,343 @@ int dst_continue() { switch (*pc & 0xFF) { default: - vm_throw("unknown opcode"); - break; + vm_throw("unknown opcode"); case DOP_NOOP: - pc++; - continue; + pc++; + vm_next(); case DOP_ERROR: - dst_vm_fiber->ret = stack[oparg(1, 0xFF)]; - goto vm_error; + dst_vm_fiber->ret = stack[oparg(1, 0xFF)]; + goto vm_error; case DOP_TYPECHECK: - vm_assert((1 << stack[oparg(1, 0xFF)].type) & oparg(2, 0xFFFF), - "typecheck failed"); - pc++; - continue; + vm_assert((1 << stack[oparg(1, 0xFF)].type) & oparg(2, 0xFFFF), + "typecheck failed"); + pc++; + vm_next(); case DOP_RETURN: - dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)]; - goto vm_return; + dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)]; + goto vm_return; case DOP_RETURN_NIL: - dst_vm_fiber->ret.type = DST_NIL; - goto vm_return; + dst_vm_fiber->ret.type = DST_NIL; + goto vm_return; case DOP_ADD_INTEGER: - vm_binop_integer(+); + vm_binop_integer(+); case DOP_ADD_IMMEDIATE: - vm_binop_immediate(+); + vm_binop_immediate(+); case DOP_ADD_REAL: - vm_binop_real(+); + vm_binop_real(+); case DOP_ADD: - vm_binop(+); + vm_binop(+); case DOP_SUBTRACT_INTEGER: - vm_binop_integer(-); + vm_binop_integer(-); case DOP_SUBTRACT_REAL: - vm_binop_real(-); + vm_binop_real(-); case DOP_SUBTRACT: - vm_binop(-); + vm_binop(-); case DOP_MULTIPLY_INTEGER: - vm_binop_integer(*); + vm_binop_integer(*); case DOP_MULTIPLY_IMMEDIATE: - vm_binop_immediate(*); + vm_binop_immediate(*); case DOP_MULTIPLY_REAL: - vm_binop_real(*); + vm_binop_real(*); case DOP_MULTIPLY: - vm_binop(*); + vm_binop(*); case DOP_DIVIDE_INTEGER: - vm_assert(stack[oparg(3, 0xFF)].as.integer != 0, "integer divide by zero"); - vm_assert(!(stack[oparg(3, 0xFF)].as.integer == -1 && - stack[oparg(2, 0xFF)].as.integer == DST_INTEGER_MIN), - "integer divide overflow"); - vm_binop_integer(/); + vm_assert(stack[oparg(3, 0xFF)].as.integer != 0, "integer divide by zero"); + vm_assert(!(stack[oparg(3, 0xFF)].as.integer == -1 && + stack[oparg(2, 0xFF)].as.integer == DST_INTEGER_MIN), + "integer divide overflow"); + vm_binop_integer(/); case DOP_DIVIDE_IMMEDIATE: - { - int64_t op1 = stack[oparg(2, 0xFF)].as.integer; - int64_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 by zero"); - if (op2 == -1) - vm_throw("integer divide overflow"); - else - stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); - pc++; - continue; - } + { + int64_t op1 = stack[oparg(2, 0xFF)].as.integer; + int64_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 by zero"); + if (op2 == -1) + vm_throw("integer divide overflow"); + else + stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); + pc++; + vm_next(); + } case DOP_DIVIDE_REAL: - vm_binop_real(/); + vm_binop_real(/); case DOP_DIVIDE: - { - DstValue op1 = stack[oparg(2, 0xFF)]; - DstValue op2 = stack[oparg(3, 0xFF)]; - vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number"); - vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number"); - if (op2.type == DST_INTEGER && op2.as.integer == 0) - op2 = dst_wrap_real(0.0); - if (op2.type == DST_INTEGER && op2.as.integer == -1 && - op1.type == DST_INTEGER && op1.as.integer == DST_INTEGER_MIN) - op2 = dst_wrap_real(-1); - stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER - ? op2.type == DST_INTEGER - ? dst_wrap_integer(op1.as.integer / op2.as.integer) - : dst_wrap_real(dst_integer_to_real(op1.as.integer) / op2.as.real) - : op2.type == DST_INTEGER - ? dst_wrap_real(op1.as.real / dst_integer_to_real(op2.as.integer)) - : dst_wrap_real(op1.as.real / op2.as.real); - pc++; - continue; - } + { + DstValue op1 = stack[oparg(2, 0xFF)]; + DstValue op2 = stack[oparg(3, 0xFF)]; + vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number"); + vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number"); + if (op2.type == DST_INTEGER && op2.as.integer == 0) + op2 = dst_wrap_real(0.0); + if (op2.type == DST_INTEGER && op2.as.integer == -1 && + op1.type == DST_INTEGER && op1.as.integer == DST_INTEGER_MIN) + op2 = dst_wrap_real(-1); + stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER + ? op2.type == DST_INTEGER + ? dst_wrap_integer(op1.as.integer / op2.as.integer) + : dst_wrap_real(dst_integer_to_real(op1.as.integer) / op2.as.real) + : op2.type == DST_INTEGER + ? dst_wrap_real(op1.as.real / dst_integer_to_real(op2.as.integer)) + : dst_wrap_real(op1.as.real / op2.as.real); + pc++; + vm_next(); + } case DOP_BAND: - vm_binop_integer(&); + vm_binop_integer(&); case DOP_BOR: - vm_binop_integer(|); + vm_binop_integer(|); case DOP_BXOR: - vm_binop_integer(^); + vm_binop_integer(^); case DOP_BNOT: - stack[oparg(1, 0xFF)] = dst_wrap_integer(~stack[oparg(2, 0xFFFF)].as.integer); - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer(~stack[oparg(2, 0xFFFF)].as.integer); + vm_next(); case DOP_SHIFT_RIGHT_UNSIGNED: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - stack[oparg(2, 0xFF)].as.uinteger - >> - stack[oparg(3, 0xFF)].as.uinteger - ); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.uinteger + >> + stack[oparg(3, 0xFF)].as.uinteger + ); + pc++; + vm_next(); case DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) - ); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) + ); + pc++; + vm_next(); case DOP_SHIFT_RIGHT: - vm_binop_integer(>>); + vm_binop_integer(>>); case DOP_SHIFT_RIGHT_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - (int64_t)(stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF)) - ); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer( + (int64_t)(stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF)) + ); + pc++; + vm_next(); case DOP_SHIFT_LEFT: - vm_binop_integer(<<); + vm_binop_integer(<<); case DOP_SHIFT_LEFT_IMMEDIATE: - stack[oparg(1, 0xFF)] = dst_wrap_integer( - stack[oparg(2, 0xFF)].as.integer << oparg(3, 0xFF) - ); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.integer << oparg(3, 0xFF) + ); + pc++; + vm_next(); case DOP_MOVE: - stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; - pc++; - continue; + stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; + pc++; + vm_next(); case DOP_JUMP: - pc += (*(int32_t *)pc) >> 8; - continue; + 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++; - } - continue; + 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; - } - continue; + if (dst_truthy(stack[oparg(1, 0xFF)])) { + pc++; + } else { + pc += (*(int32_t *)pc) >> 16; + } + vm_next(); case DOP_LESS_THAN: - stack[oparg(1, 0xFF)].type = DST_BOOLEAN; - stack[oparg(1, 0xFF)].as.boolean = dst_compare( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ) < 0; - pc++; - continue; + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ) < 0; + pc++; + vm_next(); case DOP_GREATER_THAN: - stack[oparg(1, 0xFF)].type = DST_BOOLEAN; - stack[oparg(1, 0xFF)].as.boolean = dst_compare( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ) > 0; - pc++; - continue; + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ) > 0; + pc++; + vm_next(); case DOP_EQUALS: - stack[oparg(1, 0xFF)].type = DST_BOOLEAN; - stack[oparg(1, 0xFF)].as.boolean = dst_equals( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ); - pc++; - continue; + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_equals( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ); + pc++; + vm_next(); case DOP_COMPARE: - stack[oparg(1, 0xFF)].type = DST_INTEGER; - stack[oparg(1, 0xFF)].as.integer = dst_compare( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)] - ); - pc++; - continue; + stack[oparg(1, 0xFF)].type = DST_INTEGER; + stack[oparg(1, 0xFF)].as.integer = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)] + ); + pc++; + vm_next(); case DOP_LOAD_NIL: - stack[oparg(1, 0xFFFFFF)].type = DST_NIL; - pc++; - continue; + stack[oparg(1, 0xFFFFFF)].type = DST_NIL; + pc++; + vm_next(); case DOP_LOAD_BOOLEAN: - stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF)); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF)); + pc++; + vm_next(); case DOP_LOAD_INTEGER: - stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); - pc++; - continue; + stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); + pc++; + vm_next(); case DOP_LOAD_CONSTANT: - vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); - stack[oparg(1, 0xFF)] = func->def->constants[oparg(2, 0xFFFF)]; - pc++; - continue; + vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); + stack[oparg(1, 0xFF)] = func->def->constants[oparg(2, 0xFFFF)]; + pc++; + vm_next(); case DOP_LOAD_UPVALUE: - { - uint32_t eindex = oparg(2, 0xFF); - uint32_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++; - continue; + { + uint32_t eindex = oparg(2, 0xFF); + uint32_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(); + } case DOP_SET_UPVALUE: - { - uint32_t eindex = oparg(2, 0xFF); - uint32_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++; - continue; + { + uint32_t eindex = oparg(2, 0xFF); + uint32_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_CLOSURE: - { - uint32_t i; - DstFunction *fn; - DstFuncDef *fd; - vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); - vm_assert(func->def->constants[oparg(2, 0xFFFF)].type == DST_NIL, "constant must be funcdef"); - fd = (DstFuncDef *)(func->def->constants[oparg(2, 0xFFFF)].as.pointer); - fn = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); - fn->envs = malloc(sizeof(DstFuncEnv *) * fd->environments_length); - if (NULL == fn->envs) { - DST_OUT_OF_MEMORY; - } - if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) { - /* Delayed capture of current stack frame */ - DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv)); - env->offset = dst_vm_fiber->frame; - env->as.fiber = dst_vm_fiber; - env->length = func->def->slotcount; - fn->envs[0] = env; - } else { - fn->envs[0] = NULL; - } - for (i = 1; i < fd->environments_length; ++i) { - uint32_t inherit = fd->environments[i]; - fn->envs[i] = func->envs[inherit]; - } - stack[oparg(1, 0xFF)] = dst_wrap_function(fn); - pc++; - break; + { + uint32_t i; + DstFunction *fn; + DstFuncDef *fd; + vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); + vm_assert(func->def->constants[oparg(2, 0xFFFF)].type == DST_NIL, "constant must be funcdef"); + fd = (DstFuncDef *)(func->def->constants[oparg(2, 0xFFFF)].as.pointer); + fn = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); + fn->envs = malloc(sizeof(DstFuncEnv *) * fd->environments_length); + if (NULL == fn->envs) { + DST_OUT_OF_MEMORY; } + if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) { + /* Delayed capture of current stack frame */ + DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv)); + env->offset = dst_vm_fiber->frame; + env->as.fiber = dst_vm_fiber; + env->length = func->def->slotcount; + fn->envs[0] = env; + } else { + fn->envs[0] = NULL; + } + for (i = 1; i < fd->environments_length; ++i) { + uint32_t inherit = fd->environments[i]; + fn->envs[i] = func->envs[inherit]; + } + stack[oparg(1, 0xFF)] = dst_wrap_function(fn); + pc++; + vm_checkgc_next(); + } case DOP_PUSH: - dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); - pc++; - break; + dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); + pc++; + vm_checkgc_next(); case DOP_PUSH_2: - dst_fiber_push2(dst_vm_fiber, - stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFFFF)]); - pc++; - break;; + dst_fiber_push2(dst_vm_fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFFFF)]); + pc++; + vm_checkgc_next(); case DOP_PUSH_3: - dst_fiber_push3(dst_vm_fiber, - stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)]); - pc++; - break; + dst_fiber_push3(dst_vm_fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + pc++; + vm_checkgc_next(); case DOP_PUSH_ARRAY: - { - uint32_t count; - const DstValue *array; - if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { - dst_fiber_pushn(dst_vm_fiber, array, count); - } else { - vm_throw("expected array or tuple"); - } - pc++; - break; + { + uint32_t count; + const DstValue *array; + if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { + dst_fiber_pushn(dst_vm_fiber, array, count); + } else { + vm_throw("expected array or tuple"); } + pc++; + vm_checkgc_next(); + } case DOP_CALL: { @@ -467,7 +471,7 @@ int dst_continue() { dst_fiber_funcframe(dst_vm_fiber, func); stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = func->def->bytecode; - break; + vm_checkgc_next(); } else if (callee.type == DST_CFUNCTION) { dst_fiber_cframe(dst_vm_fiber); dst_vm_fiber->ret.type = DST_NIL; @@ -475,13 +479,10 @@ int dst_continue() { dst_vm_fiber->data + dst_vm_fiber->frame, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { goto vm_error; - } else { - goto vm_return_cfunc; } - } else { - vm_throw("cannot call non-function type"); + goto vm_return_cfunc; } - break; + vm_throw("cannot call non-function type"); } case DOP_TAILCALL: @@ -492,7 +493,7 @@ int dst_continue() { dst_fiber_funcframe_tail(dst_vm_fiber, func); stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = func->def->bytecode; - break; + vm_checkgc_next(); } else if (callee.type == DST_CFUNCTION) { dst_fiber_cframe_tail(dst_vm_fiber); dst_vm_fiber->ret.type = DST_NIL; @@ -500,38 +501,33 @@ int dst_continue() { dst_vm_fiber->data + dst_vm_fiber->frame, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { goto vm_error; - } else { - goto vm_return_cfunc; } - } else { - vm_throw("expected function"); + goto vm_return_cfunc; } - break; + vm_throw("expected function"); } case DOP_SYSCALL: - { - DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; - vm_assert(NULL != f, "invalid syscall"); - dst_fiber_cframe(dst_vm_fiber); - dst_vm_fiber->ret.type = DST_NIL; - if (f(dst_vm_fiber->data + dst_vm_fiber->frame, - dst_vm_fiber->frametop - dst_vm_fiber->frame)) { - goto vm_error; - } else { - goto vm_return_cfunc; - } - continue; + { + DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; + vm_assert(NULL != f, "invalid syscall"); + dst_fiber_cframe(dst_vm_fiber); + dst_vm_fiber->ret.type = DST_NIL; + if (f(dst_vm_fiber->data + dst_vm_fiber->frame, + dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + goto vm_error; } + goto vm_return_cfunc; + } case DOP_LOAD_SYSCALL: - { - DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; - vm_assert(NULL != f, "invalid syscall"); - stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f); - pc++; - continue; - } + { + DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; + vm_assert(NULL != f, "invalid syscall"); + stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f); + pc++; + vm_next(); + } case DOP_TRANSFER: { @@ -557,49 +553,36 @@ int dst_continue() { vm_init_fiber_state(); stack[oparg(1, 0xFF)] = retvalue; pc++; - continue; + vm_next(); } case DOP_PUT: - { - const char *err = dst_try_put( - stack[oparg(1, 0xFF)], - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)]); - if (NULL != err) { - vm_throw(err); - } - ++pc; - } - continue; + dst_put(stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + ++pc; + vm_checkgc_next(); case DOP_PUT_INDEX: - dst_setindex( - stack[oparg(1, 0xFF)], + dst_setindex(stack[oparg(1, 0xFF)], stack[oparg(3, 0xFF)], oparg(3, 0xFF)); - ++pc; - continue; + ++pc; + vm_next(); case DOP_GET: - { - const char *err = dst_try_get( - stack[oparg(2, 0xFF)], - stack[oparg(3, 0xFF)], - stack + oparg(1, 0xFF)); - if (NULL != err) { - vm_throw(err); - } - ++pc; - } - continue; + 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(1, 0xFF)] = dst_getindex( stack[oparg(2, 0xFF)], oparg(3, 0xFF)); - ++pc; - continue; + ++pc; + vm_next(); /* Return from c function. Simpler than retuning from dst function */ vm_return_cfunc: @@ -610,7 +593,7 @@ int dst_continue() { return 0; stack[oparg(1, 0xFF)] = ret; pc++; - continue; + vm_checkgc_next(); } /* Handle returning from stack frame. Expect return value in fiber->ret */ @@ -624,7 +607,7 @@ int dst_continue() { pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; pc++; - continue; + vm_checkgc_next(); } /* Handle errors from c functions and vm opcodes */ @@ -638,16 +621,11 @@ int dst_continue() { pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; pc++; - continue; + vm_checkgc_next(); } } /* end switch */ - /* Check for collection every cycle. If the instruction definitely does - * not allocate memory, it can use continue instead of break to - * skip this check */ - dst_maybe_collect(); - } /* end for */ #undef oparg diff --git a/dsts/minimal.dsts b/dsts/minimal.dsts index 6f47ebe3..fd034b9a 100644 --- a/dsts/minimal.dsts +++ b/dsts/minimal.dsts @@ -1,6 +1,6 @@ { bytecode [ - (load-integer 0 10000) + (load-integer 0 15) (load-integer 1 0) (load-constant 3 lookup) @@ -8,10 +8,10 @@ (equals 2 1 0) (jump-if 2 :done) (push 0) - (shift-right-immediate 0 0 1) + (add-immediate 0 0 -1) (syscall 2 0) (get 2 3 0) - (push3 2 3 0) + (push 2) (syscall 2 0) (jump :label) diff --git a/unittests/array_test.c b/unittests/array_test.c index b98c55f0..f83ce260 100644 --- a/unittests/array_test.c +++ b/unittests/array_test.c @@ -1,6 +1,7 @@ #include "unit.h" #include + int main() { int64_t i; dst_init(); diff --git a/unittests/nanbox_test.c b/unittests/nanbox_test.c new file mode 100644 index 00000000..407c3e84 --- /dev/null +++ b/unittests/nanbox_test.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include "unit.h" + +/* Required interface for DstValue */ +/* wrap and unwrap for all types */ +/* Get type quickly */ +/* Check against type quickly */ +/* Small footprint */ +/* 64 bit integer support undecided */ + +/* dst_type(x) + * dst_checktype(x, t) + * dst_wrap_##TYPE(x) + * dst_unwrap_##TYPE(x) + * dst_truthy(x) + * dst_memclear(p, n) - clear memory for hash tables to nils + * dst_isnan(x) - check if value is nan + */ + +typedef union dst_t dst_t; +union dst_t { + uint64_t u64; + int64_t i64; + void *pointer; + double real; +}; + +/* dst_t */ + +/* This representation uses 48 bit pointers. The trade off vs. the LuaJIT style + * 47 bit payload representaion is that the type bits are no long contiguous. Type + * checking can still be fast, but typewise polymorphism takes a bit longer. However, + * we can avoid some annoying problems with a full 48 bit address space. */ + +enum dst_t_tag { + DST_T_NIL, + DST_T_TRUE, + DST_T_FALSE, + DST_T_INTEGER, + DST_T_FIBER, + DST_T_STRUCT, + DST_T_TUPLE, + DST_T_ARRAY, + DST_T_BUFFER, + DST_T_TABLE, + DST_T_USERDATA, + DST_T_FUNCTION, + DST_T_CFUNCTION, + DST_T_STRING, + DST_T_SYMBOL, + DST_T_REAL +}; + +/* |.......Tag.......|.......................Payload..................| */ +/* Non-double: t|11111111111|1ttt|xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ +/* Types of NIL, TRUE, and FALSE must have payload set to all 1s. */ + +/* Double (no NaNs): x xxxxxxxxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + +/* A simple scheme for nan boxed values */ +/* normal doubles, denormalized doubles, and infinities are doubles */ +/* Quiet nan is nil. Sign bit should be 0. */ + +#define DST_NANBOX_TYPEBITS 0x0007000000000000lu + +#ifdef DST_64 +#define DST_NANBOX_POINTERBITS 0x0000FFFFFFFFFFFFlu +#else +#define DST_NANBOX_POINTERBITS 0x00000000FFFFFFFFlu +#endif + +#define DST_NANBOX_QUIET_BIT 0x0008000000000000lu + +#define dst_nanbox_isreal(x) (!isnan((x).real)) + +#define dst_nanbox_tag(type) \ + ((((uint64_t)(type) & 0x8) << 12) | 0x7FF8 | (type)) + +#define dst_nanbox_tagbits(x) \ + ((x).u64 & 0xFFFF000000000000lu) + +#define dst_nanbox_payloadbits(x) \ + ((x).u64 & 0x0000FFFFFFFFFFFFlu) + +#define dst_nanbox_type(x) \ + (dst_nanbox_isreal(x) \ + ? DST_T_REAL \ + : (((x).u64 & DST_NANBOX_TYPEBITS) >> 48) | (((x).u64 >> 60) & 0x8)) + +#define dst_nanbox_checktype(x, t) \ + (((t) == DST_T_REAL) \ + ? dst_nanbox_isreal(x) \ + : (!dst_nanbox_isreal(x) && (((x).u64 >> 48) == dst_nanbox_tag(t)))) + +static inline void *dst_nanbox_to_pointer(dst_t x) { + x.i64 = (x.i64 << 16) >> 16; + return x.pointer; +} + +static inline dst_t dst_nanbox_from_pointer(void *p, uint64_t tagmask) { + dst_t ret; + ret.pointer = p; + ret.u64 &= DST_NANBOX_POINTERBITS; + ret.u64 |= tagmask; + return ret; +} + +static inline dst_t dst_nanbox_from_double(double d) { + dst_t ret; + ret.real = d; + /* Normalize NaNs to nil */ + if (d != d) + ret.u64 = dst_nanbox_tag(DST_T_NIL) << 48; + return ret; +} + +static inline dst_t dst_nanbox_from_bits(uint64_t bits) { + dst_t ret; + ret.u64 = bits; + return ret; +} + +#define dst_nanbox_truthy(x) \ + (~(dst_nanbox_checktype((x), DST_NIL) || dst_nanbox_checktype((x), DST_FALSE))) + +#define dst_nanbox_from_payload(t, p) \ + dst_nanbox_from_bits((dst_nanbox_tag(t) << 48) | (p)) + +#define dst_nanbox_wrap_(p, t) \ + dst_nanbox_from_pointer((p), (dst_nanbox_tag(t) << 48) | 0x7FF8000000000000lu) + +/* Wrap the simple types */ +#define dst_nanbox_wrap_nil() dst_nanbox_from_payload(DST_T_NIL, 0xFFFFFFFFFFFFlu) +#define dst_nanbox_wrap_true() dst_nanbox_from_payload(DST_T_TRUE, 0xFFFFFFFFFFFFlu) +#define dst_nanbox_wrap_false() dst_nanbox_from_payload(DST_T_FALSE, 0xFFFFFFFFFFFFlu) +#define dst_nanbox_wrap_boolean(b) dst_nanbox_from_payload((b) ? DST_T_TRUE : DST_T_FALSE, 0xFFFFFFFFFFFFlu) +#define dst_nanbox_wrap_integer(i) dst_nanbox_from_payload(DST_T_INTEGER, (uint32_t)(i)) +#define dst_nanbox_wrap_real(r) dst_nanbox_from_double(r) + +#define dst_nanbox_unwrap_boolean(x) \ + (((x).u64 >> 48) == dst_nanbox_tag(DST_T_TRUE)) + +#define dst_nanbox_unwrap_integer(x) \ + ((int32_t)((x).u64 & 0xFFFFFFFFlu)) + +#define dst_nanbox_unwrap_real(x) ((x).real) + +/* Wrap the pointer types */ +#define dst_nanbox_wrap_struct(s) dst_nanbox_wrap_((s), DST_T_STRUCT) +#define dst_nanbox_wrap_tuple(s) dst_nanbox_wrap_((s), DST_T_TUPLE) +#define dst_nanbox_wrap_fiber(s) dst_nanbox_wrap_((s), DST_T_FIBER) +#define dst_nanbox_wrap_array(s) dst_nanbox_wrap_((s), DST_T_ARRAY) +#define dst_nanbox_wrap_table(s) dst_nanbox_wrap_((s), DST_T_TABLE) +#define dst_nanbox_wrap_buffer(s) dst_nanbox_wrap_((s), DST_T_BUFFER) +#define dst_nanbox_wrap_string(s) dst_nanbox_wrap_((s), DST_T_STRING) +#define dst_nanbox_wrap_symbol(s) dst_nanbox_wrap_((s), DST_T_SYMBOL) +#define dst_nanbox_wrap_userdata(s) dst_nanbox_wrap_((s), DST_T_USERDATA) +#define dst_nanbox_wrap_function(s) dst_nanbox_wrap_((s), DST_T_FUNCTION) +#define dst_nanbox_wrap_cfunction(s) dst_nanbox_wrap_((s), DST_T_CFUNCTION) + +/* Unwrap the pointer types */ +#define dst_nanbox_unwrap_struct(x) ((const DstValue *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_tuple(x) ((const DstValue *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_fiber(x) ((DstFiber *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_array(x) ((DstArray *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_table(x) ((DstTable *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_buffer(x) ((DstBuffer *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_string(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_symbol(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_userdata(x) (dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_function(x) ((DstFunction *)dst_nanbox_to_pointer(x)) +#define dst_nanbox_unwrap_cfunction(x) ((DstCFunction)dst_nanbox_to_pointer(x)) + +void dst_nanbox_print(dst_t x) { + printf("hex: 0x%llx, " + "description: ", x.u64); + switch (dst_nanbox_type(x)) { + case DST_T_NIL: + printf("nil\n"); + break; + case DST_T_TRUE: + printf("true\n"); + break; + case DST_T_FALSE: + printf("false\n"); + break; + case DST_T_INTEGER: + printf("%dI\n", dst_nanbox_unwrap_integer(x)); + break; + case DST_T_STRUCT: + printf("\n", dst_nanbox_unwrap_struct(x)); + break; + case DST_T_TUPLE: + printf("\n", dst_nanbox_unwrap_tuple(x)); + break; + case DST_T_FIBER: + printf("\n", dst_nanbox_unwrap_fiber(x)); + break; + case DST_T_ARRAY: + printf("\n", dst_nanbox_unwrap_array(x)); + break; + case DST_T_TABLE: + printf("\n", dst_nanbox_unwrap_table(x)); + break; + case DST_T_STRING: + printf("\n", dst_nanbox_unwrap_string(x)); + break; + case DST_T_SYMBOL: + printf("\n", dst_nanbox_unwrap_symbol(x)); + break; + case DST_T_USERDATA: + printf("\n", dst_nanbox_unwrap_userdata(x)); + break; + case DST_T_FUNCTION: + printf("\n", dst_nanbox_unwrap_function(x)); + break; + case DST_T_CFUNCTION: + printf("\n", dst_nanbox_unwrap_cfunction(x)); + break; + case DST_T_BUFFER: + printf("\n", dst_nanbox_unwrap_buffer(x)); + break; + default: + printf("unknown type 0x%llu\n", dst_nanbox_type(x)); + case DST_T_REAL: + printf("%.21g\n", dst_nanbox_unwrap_real(x)); + break; + } +} + +int main() { + printf("--- nan box test ---\n"); + printf("sizeof(dst_t) = %d\n", sizeof(dst_t)); + + DstArray array; + + dst_nanbox_print(dst_nanbox_wrap_real(0.125)); + dst_nanbox_print(dst_nanbox_wrap_real(19236910.125)); + dst_nanbox_print(dst_nanbox_wrap_real(123120.125)); + dst_nanbox_print(dst_nanbox_wrap_real(0.0)); + dst_nanbox_print(dst_nanbox_wrap_real(1.0/0.0)); + dst_nanbox_print(dst_nanbox_wrap_real(1.0/-0.0)); + dst_nanbox_print(dst_nanbox_wrap_real(0.0/-0.0)); + dst_nanbox_print(dst_nanbox_wrap_real(0.0/0.0)); + dst_nanbox_print(dst_nanbox_wrap_true()); + dst_nanbox_print(dst_nanbox_wrap_false()); + dst_nanbox_print(dst_nanbox_wrap_integer(123)); + dst_nanbox_print(dst_nanbox_wrap_integer(-123)); + dst_nanbox_print(dst_nanbox_wrap_integer(0)); + + dst_nanbox_print(dst_nanbox_wrap_array(&array)); + dst_nanbox_print(dst_nanbox_wrap_table(&array)); + dst_nanbox_print(dst_nanbox_wrap_string(&array)); + dst_nanbox_print(dst_nanbox_wrap_buffer(&array)); + + printf("--- nan box test end ---\n"); +}