1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-22 21:23:16 +00:00

Add test nanbox implementation. Works for 32 bit and 64 bit x86

This commit is contained in:
bakpakin 2017-11-26 19:31:40 -05:00
parent 412d40d09f
commit 68f5ea4361
8 changed files with 619 additions and 382 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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
};

View File

@ -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);
}
}

596
core/vm.c
View File

@ -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

View File

@ -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)

View File

@ -1,6 +1,7 @@
#include "unit.h"
#include <dst/dst.h>
int main() {
int64_t i;
dst_init();

259
unittests/nanbox_test.c Normal file
View File

@ -0,0 +1,259 @@
#include <dst/dst.h>
#include <stdbool.h>
#include <math.h>
#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("<struct %p>\n", dst_nanbox_unwrap_struct(x));
break;
case DST_T_TUPLE:
printf("<tuple %p>\n", dst_nanbox_unwrap_tuple(x));
break;
case DST_T_FIBER:
printf("<fiber %p>\n", dst_nanbox_unwrap_fiber(x));
break;
case DST_T_ARRAY:
printf("<array %p>\n", dst_nanbox_unwrap_array(x));
break;
case DST_T_TABLE:
printf("<table %p>\n", dst_nanbox_unwrap_table(x));
break;
case DST_T_STRING:
printf("<string %p>\n", dst_nanbox_unwrap_string(x));
break;
case DST_T_SYMBOL:
printf("<symbol %p>\n", dst_nanbox_unwrap_symbol(x));
break;
case DST_T_USERDATA:
printf("<userdata %p>\n", dst_nanbox_unwrap_userdata(x));
break;
case DST_T_FUNCTION:
printf("<function %p>\n", dst_nanbox_unwrap_function(x));
break;
case DST_T_CFUNCTION:
printf("<cfunction %p>\n", dst_nanbox_unwrap_cfunction(x));
break;
case DST_T_BUFFER:
printf("<buffer %p>\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");
}