1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-16 10:19:55 +00:00

Work on simplifying calling procedure

This commit is contained in:
Calvin Rose 2017-03-12 18:23:27 -04:00
parent 15dd15278c
commit 3274e87a45
12 changed files with 382 additions and 345 deletions

View File

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

View File

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

View File

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

View File

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

1
gst.h
View File

@ -8,5 +8,6 @@
#include "compile.h"
#include "value.h"
#include "stl.h"
#include "thread.h"
#endif // gst_h_INCLUDED

7
main.c
View File

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

View File

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

4
stl.c
View File

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

233
thread.c Normal file
View File

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

41
thread.h Normal file
View File

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

2
util.h
View File

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

393
vm.c
View File

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