From 3274e87a451dbd2e09c3e4eba0939a00fd46ff5d Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 12 Mar 2017 18:23:27 -0400 Subject: [PATCH] Work on simplifying calling procedure --- Makefile | 4 +- compile.c | 12 +- datatypes.h | 3 - disasm.c | 7 +- gst.h | 1 + main.c | 7 +- sample.gst | 20 +-- stl.c | 4 +- thread.c | 233 +++++++++++++++++++++++++++++++ thread.h | 41 ++++++ util.h | 2 +- vm.c | 393 ++++++++++++---------------------------------------- 12 files changed, 382 insertions(+), 345 deletions(-) create mode 100644 thread.c create mode 100644 thread.h diff --git a/Makefile b/Makefile index 94f8362f..2d64790d 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ TARGET=interp PREFIX=/usr/local # C sources -HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h stl.h disasm.h -SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c disasm.c +HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h stl.h disasm.h thread.h +SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c disasm.c thread.c OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) all: $(TARGET) diff --git a/compile.c b/compile.c index 2775a3a0..824c73ca 100644 --- a/compile.c +++ b/compile.c @@ -1246,22 +1246,22 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, GstValue *form) { /* Free up some slots */ compiler_drop_slot(c, scope, callee); compiler_tracker_free(c, scope, &tracker); - /* Push arguments to stack */ - gst_buffer_push_u16(c->vm, buffer, GST_OP_PSH); - gst_buffer_push_u16(c->vm, buffer, callee.index); - gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1); - /* Write the location of all of the arguments */ - compiler_tracker_write(c, &tracker, 0); /* If this is in tail position do a tail call. */ if (opts.isTail) { gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL); + gst_buffer_push_u16(c->vm, buffer, callee.index); + gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1); ret.hasReturned = 1; ret.isNil = 1; } else { ret = compiler_get_target(c, opts); gst_buffer_push_u16(c->vm, buffer, GST_OP_CAL); + gst_buffer_push_u16(c->vm, buffer, callee.index); gst_buffer_push_u16(c->vm, buffer, ret.index); + gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1); } + /* Write the location of all of the arguments */ + compiler_tracker_write(c, &tracker, 0); return ret; } } diff --git a/datatypes.h b/datatypes.h index a188ff7d..99a901cb 100644 --- a/datatypes.h +++ b/datatypes.h @@ -165,8 +165,6 @@ struct Gst { /* Return state */ const char *crash; GstValue ret; /* Returned value from gst_start. Also holds errors. */ - /* Temporary array for use in function dispatch */ - GstValue tempArray[GST_MAX_SEARCH_DEPTH]; }; /* Bytecode */ @@ -210,7 +208,6 @@ enum GstOpCode { GST_OP_UTY, /* End try block */ GST_OP_RET, /* Return from function */ GST_OP_RTN, /* Return nil */ - GST_OP_PSH, /* Push a stack frame */ GST_OP_CAL, /* Call function */ GST_OP_TCL /* Tail call */ }; diff --git a/disasm.c b/disasm.c index 18d24ef6..b75df0dd 100644 --- a/disasm.c +++ b/disasm.c @@ -193,14 +193,11 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) { case GST_OP_RTN: current += dasm_fixed_op(out, current, "returnNil", 0); break; - case GST_OP_PSH: - current += dasm_varg_op(out, current, "push", 1); - break; case GST_OP_CAL: - current += dasm_fixed_op(out, current, "call", 1); + current += dasm_varg_op(out, current, "call", 2); break; case GST_OP_TCL: - current += dasm_fixed_op(out, current, "tailCall", 0); + current += dasm_varg_op(out, current, "tailCall", 1); break; } fprintf(out, "\n"); diff --git a/gst.h b/gst.h index 7110b9c2..0e3a876b 100644 --- a/gst.h +++ b/gst.h @@ -8,5 +8,6 @@ #include "compile.h" #include "value.h" #include "stl.h" +#include "thread.h" #endif // gst_h_INCLUDED diff --git a/main.c b/main.c index 5f59db74..3976f113 100644 --- a/main.c +++ b/main.c @@ -48,7 +48,7 @@ void debug_repl(FILE *in, FILE *out) { if (out) { fprintf(out, "\n"); fprintf(out, "%s\n", buffer); - for (i = 0; i < p.index; ++i) { + for (i = 1; i < p.index; ++i) { fprintf(out, " "); } fprintf(out, "^\n"); @@ -78,7 +78,10 @@ void debug_repl(FILE *in, FILE *out) { } /* Print asm */ - /* gst_dasm_function(stdout, func.data.function); */ + //if (out) { + //fprintf(out, "\n"); + //gst_dasm_function(out, func.data.function); + //} /* Execute function */ if (gst_run(&vm, func)) { diff --git a/sample.gst b/sample.gst index ae1d3026..70a8b4ea 100644 --- a/sample.gst +++ b/sample.gst @@ -1,18 +1,6 @@ -# GST is a general purpose language. It is small, not slow, and supports meta- -# programming. It also should be structured and static enough to easily scale to -# large programs. Lastly, it is interoperable with C and C++. - -# Syntax - There is very little syntax. This simplifies parsing and makes macros -# easier to implement, which are useful in metaprogramming. -(+ 1 2 3) - -# Unlike most lisps, it is not a pure functional language. Also unlike lisp, gst does -# not make much use of a list data structure, instead using arrays and objects for -# better performance at runtime. (do - (:= a 1) - (while (< a 1025) - (print a) - (:= a (* a 2)) - ) + (:= a (set-class {} {"call" (fn [self a] (set self "last" a) (print self) self)})) + (a 1) + (a 2) + (a 3) ) diff --git a/stl.c b/stl.c index fbca725b..a5061969 100644 --- a/stl.c +++ b/stl.c @@ -44,8 +44,8 @@ int gst_stl_callforeach(Gst *vm) { uint32_t argCount = gst_count_args(vm); uint32_t i; if (argCount) { - for (i = 0; i < argCount - 1; ++i) - gst_call(vm, func, 1, vm->thread->data + vm->thread->count + 1 + i); + for (i = 1; i < argCount; ++i) + gst_call(vm, func, 1, vm->thread->data + vm->thread->count + i); return GST_RETURN_OK; } else { gst_c_throwc(vm, "expected at least one argument"); diff --git a/thread.c b/thread.c new file mode 100644 index 00000000..4f7987a7 --- /dev/null +++ b/thread.c @@ -0,0 +1,233 @@ +#include "datatypes.h" +#include "thread.h" +#include "vm.h" +#include "util.h" +#include "ds.h" + +/* Create a new thread */ +GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity) { + GstThread *thread = gst_alloc(vm, sizeof(GstThread)); + GstValue *data, *stack; + if (capacity < GST_FRAME_SIZE) capacity = GST_FRAME_SIZE; + data = gst_alloc(vm, sizeof(GstValue) * capacity); + thread->capacity = capacity; + thread->count = GST_FRAME_SIZE; + thread->data = data; + thread->status = GST_THREAD_PENDING; + stack = data + GST_FRAME_SIZE; + gst_frame_size(stack) = 0; + gst_frame_prevsize(stack) = 0; + gst_frame_ret(stack) = 0; + gst_frame_errloc(stack) = 0; + gst_frame_callee(stack) = callee; + gst_frame_pc(stack) = NULL; + gst_frame_env(stack) = NULL; + gst_frame_errjmp(stack) = NULL; + gst_thread_endframe(vm, thread); + return thread; +} + +/* Ensure that the thread has enough EXTRA capacity */ +void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra) { + GstValue *newData, *stack; + uint32_t usedCapacity, neededCapacity, newCapacity; + stack = thread->data + thread->count; + usedCapacity = thread->count + gst_frame_size(stack) + GST_FRAME_SIZE; + neededCapacity = usedCapacity + extra; + if (thread->capacity >= neededCapacity) return; + newCapacity = 2 * neededCapacity; + newData = gst_alloc(vm, sizeof(GstValue) * newCapacity); + gst_memcpy(newData, thread->data, sizeof(GstValue) * usedCapacity); + thread->data = newData; + thread->capacity = newCapacity; +} + +/* Push a value on the current stack frame*/ +void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) { + GstValue *stack; + gst_thread_ensure_extra(vm, thread, 1); + stack = thread->data + thread->count; + stack[gst_frame_size(stack)++] = x; +} + +/* Push n nils onto the stack */ +void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n) { + GstValue *stack, *current, *end; + gst_thread_ensure_extra(vm, thread, n); + stack = thread->data + thread->count; + current = stack + gst_frame_size(stack); + end = current + n; + for (; current < end; ++current) { + current->type = GST_NIL; + } + gst_frame_size(stack) += n; +} + +/* Package up extra args after and including n into tuple at n*/ +void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n) { + GstValue *stack = thread->data + thread->count; + uint32_t size = gst_frame_size(stack); + if (n >= size) { + gst_thread_pushnil(vm, thread, n - size + 1); + stack = thread->data + thread->count; + stack[n].type = GST_TUPLE; + stack[n].data.tuple = gst_tuple(vm, 0); + } else { + uint32_t i; + GstValue *tuple = gst_tuple(vm, size - n); + for (i = n; i < size; ++i) + tuple[i - n] = stack[i]; + stack[n].type = GST_TUPLE; + stack[n].data.tuple = tuple; + gst_frame_size(stack) = n + 1; + } +} + +/* Push a stack frame to a thread, with space for arity arguments. Returns the new + * stack. */ +GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity) { + uint32_t frameOffset, recurCount; + GstValue *oldStack, *newStack; + + /* Push the frame */ + gst_thread_ensure_extra(vm, thread, GST_FRAME_SIZE + arity + 4); + oldStack = thread->data + thread->count; + frameOffset = gst_frame_size(oldStack) + GST_FRAME_SIZE; + newStack = oldStack + frameOffset; + gst_frame_prevsize(newStack) = gst_frame_size(oldStack); + gst_frame_env(newStack) = NULL; + gst_frame_errjmp(newStack) = NULL; + gst_frame_size(newStack) = 0; + thread->count += frameOffset; + + /* Get the true callee of next stack frame */ + recurCount = 200; + recur: + if (!recurCount) { + return NULL; + } + switch(callee.type) { + default: return NULL; + case GST_FUNCTION: + gst_frame_callee(newStack) = callee; + gst_frame_pc(newStack) = callee.data.function->def->byteCode; + break; + case GST_CFUNCTION: + gst_frame_callee(newStack) = callee; + gst_frame_pc(newStack) = NULL; + break; + case GST_OBJECT: + { + GstObject *meta = callee.data.object->meta; + if (meta == NULL) return NULL; + gst_thread_push(vm, thread, callee); + callee = gst_object_get_cstring(meta, "call"); + newStack = thread->data + thread->count; + --recurCount; + goto recur; + } + case GST_USERDATA: + { + GstObject *meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta; + gst_thread_push(vm, thread, callee); + callee = gst_object_get_cstring(meta, "call"); + newStack = thread->data + thread->count; + --recurCount; + goto recur; + } + } + + /* Ensure the extra space and initialize to nil */ + gst_thread_pushnil(vm, thread, arity); + + /* Return ok */ + return thread->data + thread->count; +} + +/* After pushing arguments to a stack frame created with gst_thread_beginframe, call this + * to finalize the frame before starting a function call. */ +void gst_thread_endframe(Gst *vm, GstThread *thread) { + GstValue *stack = thread->data + thread->count; + GstValue callee = gst_frame_callee(stack); + if (callee.type == GST_FUNCTION) { + GstFunction *fn = callee.data.function; + gst_frame_pc(stack) = fn->def->byteCode; + if (fn->def->flags & GST_FUNCDEF_FLAG_VARARG) { + uint32_t arity = fn->def->arity; + gst_thread_tuplepack(vm, thread, arity); + } else { + uint32_t locals = fn->def->locals; + if (gst_frame_size(stack) < locals) { + gst_thread_pushnil(vm, thread, locals - gst_frame_size(stack)); + } + } + } +} + +/* Pop a stack frame from the thread. Returns the new stack frame, or + * NULL if there are no more frames */ +GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) { + GstValue *stack = thread->data + thread->count; + uint32_t prevsize = gst_frame_prevsize(stack); + GstValue *nextstack = stack - GST_FRAME_SIZE - prevsize; + GstFuncEnv *env = gst_frame_env(stack); + + /* Check for closures */ + if (env != NULL) { + uint32_t size = gst_frame_size(stack); + env->thread = NULL; + env->stackOffset = size; + env->values = gst_alloc(vm, sizeof(GstValue) * size); + gst_memcpy(env->values, stack, sizeof(GstValue) * size); + } + + /* Shrink stack */ + thread->count -= GST_FRAME_SIZE + prevsize; + + /* Check if the stack is empty, and if so, return null */ + if (thread->count) + return nextstack; + else + return NULL; +} + +/* Move the current stack frame over its parent stack frame, allowing + * for primitive tail calls. */ +GstValue *gst_thread_tail(Gst *vm, GstThread *thread) { + GstFuncEnv *env; + GstValue *stack = thread->data + thread->count; + GstValue *nextStack = gst_thread_popframe(vm, thread); + uint32_t i; + + if (nextStack == NULL) return NULL; + env = gst_frame_env(nextStack); + + /* Check for old closures */ + if (env != NULL) { + uint32_t size = gst_frame_size(stack); + env->thread = NULL; + env->stackOffset = size; + env->values = gst_alloc(vm, sizeof(GstValue) * size); + gst_memcpy(env->values, stack, sizeof(GstValue) * size); + } + + /* Modify new closure */ + env = gst_frame_env(stack); + if (env != NULL) { + env->stackOffset = thread->count; + } + + /* Copy over (some of) stack frame. Leave ret and prevsize untouched. */ + gst_frame_env(nextStack) = env; + gst_frame_size(nextStack) = gst_frame_size(stack); + gst_frame_pc(nextStack) = gst_frame_pc(stack); + gst_frame_errjmp(nextStack) = gst_frame_errjmp(stack); + gst_frame_errloc(nextStack) = gst_frame_errloc(stack); + gst_frame_callee(nextStack) = gst_frame_callee(stack); + + /* Copy stack arguments */ + for (i = 0; i < gst_frame_size(nextStack); ++i) + nextStack[i] = stack[i]; + + return nextStack; +} diff --git a/thread.h b/thread.h new file mode 100644 index 00000000..e186ee51 --- /dev/null +++ b/thread.h @@ -0,0 +1,41 @@ +#ifndef thread_h_INCLUDED +#define thread_h_INCLUDED + +#include "datatypes.h" + +/* Get the current stack frame */ +#define gst_thread_stack(t) ((t)->data + (t)->count) + +/* Create a new thread */ +GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity); + +/* Ensure that the thread has enough EXTRA capacity */ +void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra); + +/* Push a value on the current stack frame*/ +void gst_thread_push(Gst *vm, GstThread *thread, GstValue x); + +/* Push n nils onto the stack */ +void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n); + +/* Package up extra args after and including n into tuple at n*/ +void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n); + +/* Push a stack frame to a thread, with space for arity arguments. Returns the new + * stack. */ +GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity); + +/* After pushing arguments to a stack frame created with gst_thread_beginframe, call this + * to finalize the frame before starting a function call. */ +void gst_thread_endframe(Gst *vm, GstThread *thread); + +/* Pop a stack frame from the thread. Returns the new stack frame, or + * NULL if there are no more frames */ +GstValue *gst_thread_popframe(Gst *vm, GstThread *thread); + +/* Move the current stack frame over its parent stack frame, allowing + * for primitive tail calls. Return new stack. */ +GstValue *gst_thread_tail(Gst *vm, GstThread *thread); + +#endif // thread_h_INCLUDED + diff --git a/util.h b/util.h index 622f4cac..5a396c88 100644 --- a/util.h +++ b/util.h @@ -51,7 +51,7 @@ /* Size of stack frame in number of values */ #define GST_FRAME_SIZE 5 -/* Macros for referencing that a stack frame given a stack */ +/* Macros for referencing a stack frame given a stack */ #define gst_frame_callee(s) (*(s - 1)) #define gst_frame_size(s) ((s - 2)->data.hws[0]) #define gst_frame_prevsize(s) ((s - 2)->data.hws[1]) diff --git a/vm.c b/vm.c index c1d7d85f..453c8881 100644 --- a/vm.c +++ b/vm.c @@ -3,6 +3,7 @@ #include "value.h" #include "ds.h" #include "gc.h" +#include "thread.h" /* Macros for errors in the vm */ @@ -23,40 +24,6 @@ static const char GST_EXPECTED_FUNCTION[] = "expected function"; static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number"; static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number"; -/* Load a function into the VM. The function will be called with - * no arguments when run */ -static void gst_load(Gst *vm, GstValue callee) { - uint32_t startCapacity; - uint32_t locals, i; - uint16_t *pc; - GstValue *stack; - GstThread *thread = gst_alloc(vm, sizeof(GstThread)); - if (callee.type == GST_FUNCTION) { - locals = callee.data.function->def->locals; - pc = callee.data.function->def->byteCode; - } else if (callee.type == GST_CFUNCTION) { - locals = 0; - pc = NULL; - } else { - return; - } - startCapacity = locals + GST_FRAME_SIZE + 10; - thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity); - thread->capacity = startCapacity; - thread->count = GST_FRAME_SIZE; - vm->thread = thread; - stack = thread->data + GST_FRAME_SIZE; - gst_frame_prevsize(stack) = 0; - gst_frame_size(stack) = locals; - gst_frame_callee(stack) = callee; - gst_frame_env(stack) = NULL; - gst_frame_errjmp(stack) = NULL; - gst_frame_pc(stack) = pc; - /* Nil arguments */ - for (i = 0; i < locals; ++i) - stack[i].type = GST_NIL; -} - /* Contextual macro to state in function with VM */ #define GST_STATE_SYNC() do { \ thread = *vm->thread; \ @@ -358,7 +325,6 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { case GST_OP_ERR: /* Throw error */ vm->ret = stack[pc[1]]; goto vm_error; - break; case GST_OP_TRY: /* Begin try block */ gst_frame_errloc(stack) = pc[1]; @@ -372,201 +338,84 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { break; case GST_OP_RTN: /* Return nil */ - vm->ret.type = GST_NIL; - goto ret; + stack = gst_thread_popframe(vm, &thread); + if (thread.count < stackBase) { + vm->ret.type = GST_NIL; + return GST_RETURN_OK; + } + stack[gst_frame_ret(stack)].type = GST_NIL; + break; case GST_OP_RET: /* Return */ - vm->ret = stack[pc[1]]; - goto ret; - - case GST_OP_PSH: /* Push stack frame */ - { - GstValue *nextStack; - uint32_t argSlots, fullArgSlots, arity, prefixCount, varArgs, tupleCount, i, locals, nextCount; - - /* Get arguments to op */ - temp = stack[pc[1]]; - arity = pc[2]; - - /* Get the size of next stack frame */ - prefixCount = 0; - recur: - switch(temp.type) { - default: gst_error(vm, GST_EXPECTED_FUNCTION); - case GST_FUNCTION: - { - GstFunction *fn = temp.data.function; - locals = fn->def->locals; - varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG; - argSlots = fn->def->arity; - if (arity + prefixCount > argSlots) { - fullArgSlots = argSlots; - tupleCount = arity + prefixCount - fullArgSlots; - } else { - fullArgSlots = arity + prefixCount; - tupleCount = 0; - } - break; - } - case GST_CFUNCTION: - { - locals = argSlots = fullArgSlots = arity + prefixCount; - varArgs = tupleCount = 0; - break; - } - case GST_OBJECT: - { - GstObject *meta = temp.data.object->meta; - if (meta == NULL) gst_error(vm, GST_EXPECTED_FUNCTION); - vm->tempArray[prefixCount++] = temp; - temp = gst_object_get_cstring(meta, "call"); - goto recur; - } - case GST_USERDATA: - { - GstObject *meta = ((GstUserdataHeader *)temp.data.pointer - 1)->meta; - vm->tempArray[prefixCount++] = temp; - temp = gst_object_get_cstring(meta, "call"); - goto recur; - } - } - - /* Get next frame size */ - nextCount = thread.count + gst_frame_size(stack) + GST_FRAME_SIZE; - - /* Ensure capacity */ - if (nextCount + locals > thread.capacity) { - uint32_t newCap = (nextCount + locals) * 2; - GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap); - gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue)); - thread.data = newData; - thread.capacity = newCap; - stack = thread.data + thread.count; - } - - /* Set up the new stack frame */ - nextStack = thread.data + nextCount; - gst_frame_prevsize(nextStack) = gst_frame_size(stack); - gst_frame_size(nextStack) = locals; - gst_frame_ret(nextStack) = 0; - gst_frame_env(nextStack) = NULL; - gst_frame_callee(nextStack) = temp; - gst_frame_errjmp(nextStack) = NULL; - - /* Write prefix args to stack */ - for (i = 0; i < prefixCount; ++i) - nextStack[i] = vm->tempArray[i]; - - /* Write arguments to new stack */ - for (; i < fullArgSlots; ++i) - nextStack[i] = stack[pc[3 + i - prefixCount]]; - - /* Clear rest of stack */ - for (; i < locals; ++i) - nextStack[i].type = GST_NIL; - - /* Check for varargs and put them in a tuple */ - if (varArgs) { - GstValue *tuple; - uint32_t j; - tuple = gst_tuple(vm, tupleCount); - for (j = argSlots; j < arity; ++j) - if (j < prefixCount) - tuple[j - argSlots] = vm->tempArray[j - prefixCount]; - else - tuple[j - argSlots] = stack[pc[3 + j - prefixCount]]; - nextStack[argSlots].type = GST_TUPLE; - nextStack[argSlots].data.tuple = tuple; - } - - /* Increment pc */ - pc += 3 + arity; + temp = stack[pc[1]]; + stack = gst_thread_popframe(vm, &thread); + if (thread.count < stackBase) { + vm->ret = temp; + return GST_RETURN_OK; } + stack[gst_frame_ret(stack)] = temp; break; case GST_OP_CAL: /* Call */ case GST_OP_TCL: /* Tail call */ - if (pc[0] == GST_OP_CAL) { - gst_frame_ret(stack) = pc[1]; - gst_frame_pc(stack) = pc + 2; - thread.count += gst_frame_size(stack) + GST_FRAME_SIZE; - stack = thread.data + thread.count; - } else { - uint32_t i; - GstValue *nextStack = stack + gst_frame_size(stack) + GST_FRAME_SIZE; - uint32_t nextSize = gst_frame_size(nextStack); - /* Check for closures */ - if (gst_frame_env(stack) != NULL) { - gst_frame_env(stack)->thread = NULL; - gst_frame_env(stack)->stackOffset = gst_frame_size(stack); - gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack)); - gst_memcpy(gst_frame_env(stack)->values, - thread.data + thread.count, - gst_frame_size(stack) * sizeof(GstValue)); - } - /* Copy over most of stack frame */ - gst_frame_callee(stack) = gst_frame_callee(nextStack); - gst_frame_size(stack) = gst_frame_size(nextStack); - gst_frame_env(stack) = NULL; - gst_frame_errjmp(stack) = NULL; - /* Replace current stack frame with next */ - for (i = 0; i < nextSize; ++i) - stack[i] = nextStack[i]; - } - v1 = gst_frame_callee(stack); - if (v1.type == GST_FUNCTION) { - pc = v1.data.function->def->byteCode; - } else if (v1.type == GST_CFUNCTION) { - int status; - GST_STATE_WRITE(); - vm->ret.type = GST_NIL; - status = v1.data.cfunction(vm); - GST_STATE_SYNC(); - if (status == GST_RETURN_OK) - goto ret; - else - goto vm_error; - } else { - gst_error(vm, GST_EXPECTED_FUNCTION); - } - break; - - /* Macro for popping stack frame */ - #define pop_frame(onUnderflow) do { \ - if (gst_frame_env(stack) != NULL) { \ - gst_frame_env(stack)->thread = NULL; \ - gst_frame_env(stack)->stackOffset = gst_frame_size(stack); \ - gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack)); \ - gst_memcpy(gst_frame_env(stack)->values, \ - thread.data + thread.count, \ - gst_frame_size(stack) * sizeof(GstValue)); \ - } \ - if (thread.count <= stackBase) { \ - thread.count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE; \ - return (onUnderflow); \ - } \ - thread.count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE; \ - stack = thread.data + thread.count; \ - } while (0) - - /* Label for return */ - ret: - /* Check for closure */ - pop_frame(GST_RETURN_OK); - pc = gst_frame_pc(stack); - stack[gst_frame_ret(stack)] = vm->ret; - break; + { + GstValue *oldStack; + temp = stack[pc[1]]; + uint32_t i, arity, offset, size; + uint16_t ret = pc[2]; + offset = (*pc == GST_OP_CAL) ? 4 : 3; + arity = pc[offset - 1]; + /* Push new frame */ + stack = gst_thread_beginframe(vm, &thread, temp, arity); + oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack); + /* Write arguments */ + size = gst_frame_size(stack); + for (i = 0; i < arity; ++i) + stack[i + size - arity] = oldStack[pc[offset + i]]; + /* Finish new frame */ + gst_thread_endframe(vm, &thread); + /* Check tail call - if so, replace frame. */ + if (*pc == GST_OP_TCL) { + stack = gst_thread_tail(vm, &thread); + } else { + gst_frame_ret(oldStack) = ret; + } + /* Call function */ + temp = gst_frame_callee(stack); + if (temp.type == GST_FUNCTION) { + /* Save pc and set new pc */ + gst_frame_pc(oldStack) = pc + offset + arity; + pc = temp.data.function->def->byteCode; + } else { + int status; + GST_STATE_WRITE(); + vm->ret.type = GST_NIL; + status = temp.data.cfunction(vm); + GST_STATE_SYNC(); + stack = gst_thread_popframe(vm, &thread); + pc += offset + arity; + if (status == GST_RETURN_OK) + if (thread.count < stackBase) + return status; + else + stack[gst_frame_ret(stack)] = vm->ret; + else + goto vm_error; + } + } + break; /* Handle errors from c functions and vm opcodes */ vm_error: - while (gst_frame_errjmp(stack) == NULL) - pop_frame(GST_RETURN_ERROR); + while (gst_frame_errjmp(stack) == NULL) { + stack = gst_thread_popframe(vm, &thread); + if (thread.count < stackBase) + return GST_RETURN_ERROR; + } pc = gst_frame_errjmp(stack); stack[gst_frame_errloc(stack)] = vm->ret; break; - #undef pop_frame - } /* end switch */ /* TODO: Move collection only to places that allocate memory */ @@ -584,106 +433,34 @@ int gst_continue(Gst *vm) { } /* Run the vm with a given function */ -int gst_run(Gst *vm, GstValue func) { - gst_load(vm, func); +int gst_run(Gst *vm, GstValue callee) { + vm->thread = gst_thread(vm, callee, 64); return gst_continue(vm); } -/* Raw function call implementation for use from c code. Beware code - * duplication between this function and GST_OP_PSH and GST_OP_CAL/GST_OP_TCL */ +/* Call a gst function */ int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args) { - GstThread thread; GstValue *stack; - uint32_t expectedArity, normalArity, varArgs, i, locals, nextCount, size; - - /* Initialize some state */ - GST_STATE_SYNC(); - - /* Get the size of next stack frame */ - if (callee.type == GST_FUNCTION) { - GstFunction *fn = callee.data.function; - locals = fn->def->locals; - varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG; - expectedArity = fn->def->arity; - gst_frame_pc(stack) = fn->def->byteCode; - if (arity > expectedArity) - normalArity = expectedArity; - else - normalArity = arity; - } else if (callee.type == GST_CFUNCTION) { - locals = normalArity = expectedArity = arity; - varArgs = 0; - } else { - gst_c_throwc(vm, GST_EXPECTED_FUNCTION); - } - - /* Get next frame size */ - nextCount = thread.count + gst_frame_size(stack) + GST_FRAME_SIZE; - - /* Ensure capacity */ - if (nextCount + locals > thread.capacity) { - uint32_t newCap = (nextCount + locals) * 2; - GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap); - gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue)); - thread.data = newData; - thread.capacity = newCap; - } - - /* Save modified thread object */ - thread.count = nextCount; - *vm->thread = thread; - - /* Set up the new stack frame */ - size = gst_frame_size(stack); - stack = thread.data + nextCount; - gst_frame_prevsize(stack) = size; - gst_frame_size(stack) = locals; - gst_frame_env(stack) = NULL; - gst_frame_callee(stack) = callee; - gst_frame_errjmp(stack) = NULL; - - /* Write arguments to new stack */ - for (i = 0; i < normalArity; ++i) + uint32_t i; + stack = gst_thread_beginframe(vm, vm->thread, callee, arity); + for (i = 0; i < arity; ++i) stack[i] = args[i]; - - /* Clear stack */ - for (; i < locals; ++i) - stack[i].type = GST_NIL; - - /* Check for varargs and put them in a tuple */ - if (varArgs) { - GstValue *tuple; - uint32_t j; - tuple = gst_tuple(vm, arity - expectedArity); - for (j = expectedArity; j < arity; ++j) - tuple[j - expectedArity] = args[j]; - stack[expectedArity].type = GST_TUPLE; - stack[expectedArity].data.tuple = tuple; - } - - /* Call the function */ - if (callee.type == GST_FUNCTION) { - return gst_continue_size(vm, thread.count); - } else { - int status = callee.data.cfunction(vm); - GST_STATE_SYNC(); - /* Check for closures */ - if (gst_frame_env(stack) != NULL) { - gst_frame_env(stack)->thread = NULL; - gst_frame_env(stack)->stackOffset = gst_frame_size(stack); - gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack)); - gst_memcpy(gst_frame_env(stack)->values, - thread.data + thread.count, - gst_frame_size(stack) * sizeof(GstValue)); - } - vm->thread->count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE; - return status; - } + gst_thread_endframe(vm, vm->thread); + callee = gst_frame_callee(stack); + if (callee.type == GST_FUNCTION) { + return gst_continue(vm); + } else { + int status; + vm->ret.type = GST_NIL; + status = callee.data.cfunction(vm); + gst_thread_popframe(vm, vm->thread); + return status; + } } /* Get an argument from the stack */ GstValue gst_arg(Gst *vm, uint16_t index) { - GstValue *stack = vm->thread->data + vm->thread->count; + GstValue *stack = gst_thread_stack(vm->thread); uint16_t frameSize = gst_frame_size(stack); if (frameSize <= index) { GstValue ret; @@ -695,7 +472,7 @@ GstValue gst_arg(Gst *vm, uint16_t index) { /* Put a value on the stack */ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) { - GstValue *stack = vm->thread->data + vm->thread->count; + GstValue *stack = gst_thread_stack(vm->thread); uint16_t frameSize = gst_frame_size(stack); if (frameSize <= index) return; stack[index] = x; @@ -703,7 +480,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) { /* Get the size of the VMStack */ uint16_t gst_count_args(Gst *vm) { - GstValue *stack = vm->thread->data + vm->thread->count; + GstValue *stack = gst_thread_stack(vm->thread); return gst_frame_size(stack); }