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

Redo calling convetion for more code reuse. Allow calling gst functions from c.

This commit is contained in:
Calvin Rose 2017-03-10 00:09:42 -05:00
parent 169e3de5a7
commit b986e1b967
14 changed files with 470 additions and 367 deletions

View File

@ -1,13 +1,13 @@
# TIL
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -O3
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
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
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c
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
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET)

View File

@ -1246,21 +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);
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

@ -44,7 +44,6 @@ typedef struct GstFuncDef GstFuncDef;
typedef struct GstFuncEnv GstFuncEnv;
/* Definitely implementation details */
typedef struct GstStackFrame GstStackFrame;
typedef struct GstBucket GstBucket;
/* The general gst value type. Contains a large union and
@ -63,6 +62,11 @@ struct GstValue {
GstCFunction cfunction;
GstFunction *function;
uint8_t *string;
/* Indirectly used union members */
uint16_t *u16p;
GstFuncEnv *env;
uint16_t hws[4];
uint8_t bytes[8];
void *pointer;
} data;
};
@ -80,9 +84,6 @@ struct GstThread {
} status;
};
/* Size of stack frame */
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) + 1) / sizeof(GstValue))
/* A dynamic array type. Useful for implementing a stack. */
struct GstArray {
uint32_t count;
@ -150,18 +151,6 @@ struct GstUserdataHeader {
GstObject *meta;
};
/* A stack frame in the VM */
struct GstStackFrame {
GstValue callee;
uint16_t size;
uint16_t prevSize;
uint16_t ret;
uint16_t errorSlot;
uint16_t *errorJump;
GstFuncEnv *env;
uint16_t *pc;
};
/* VM return status from c function */
#define GST_RETURN_OK 0
#define GST_RETURN_ERROR 1
@ -203,8 +192,6 @@ enum GstOpCode {
GST_OP_UPV, /* Load upvalue */
GST_OP_JIF, /* Jump if */
GST_OP_JMP, /* Jump */
GST_OP_CAL, /* Call function */
GST_OP_RET, /* Return from function */
GST_OP_SUV, /* Set upvalue */
GST_OP_CST, /* Load constant */
GST_OP_I32, /* Load 32 bit signed integer */
@ -217,13 +204,16 @@ enum GstOpCode {
GST_OP_ARR, /* Create array */
GST_OP_DIC, /* Create object */
GST_OP_TUP, /* Create tuple */
GST_OP_TCL, /* Tail call */
GST_OP_RTN, /* Return nil */
GST_OP_SET, /* Assocaitive set */
GST_OP_GET, /* Associative get */
GST_OP_ERR, /* Throw error */
GST_OP_TRY, /* Begin try block */
GST_OP_UTY /* End try block */
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 */
};
#endif

View File

@ -118,12 +118,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
current += 3;
break;
case GST_OP_CAL:
current += dasm_varg_op(out, current, "call", 2);
break;
case GST_OP_RET:
current += dasm_fixed_op(out, current, "return", 1);
break;
case GST_OP_SUV:
dasm_print_arg(out, "setUpValue");
dasm_print_slot(out, current[1]);
@ -175,12 +169,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
case GST_OP_TUP:
current += dasm_varg_op(out, current, "tuple", 1);
break;
case GST_OP_TCL:
current += dasm_varg_op(out, current, "tailCall", 1);
break;
case GST_OP_RTN:
current += dasm_fixed_op(out, current, "returnNil", 0);
break;
case GST_OP_GET:
current += dasm_fixed_op(out, current, "get", 3);
break;
@ -199,6 +187,21 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
case GST_OP_UTY:
current += dasm_fixed_op(out, current, "untry", 0);
break;
case GST_OP_RET:
current += dasm_fixed_op(out, current, "return", 1);
break;
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);
break;
case GST_OP_TCL:
current += dasm_fixed_op(out, current, "tailCall", 0);
break;
}
fprintf(out, "\n");
}

36
ds.c
View File

@ -216,7 +216,7 @@ static GstBucket *gst_object_find(GstObject *o, GstValue key) {
return (GstBucket *)0;
}
/* Get a value out of the dictionary */
/* Get a value out of the object */
GstValue gst_object_get(GstObject *o, GstValue key) {
GstBucket *bucket = gst_object_find(o, key);
if (bucket) {
@ -228,6 +228,40 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
}
}
/* Get a value of the object with a cstring key */
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
const char *end = key;
while (*end++);
uint32_t len = end - key;
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
uint32_t index = hash % obj->capacity;
GstBucket *bucket = obj->buckets[index];
while (bucket) {
if (bucket->key.type == GST_STRING) {
uint8_t *s = bucket->key.data.string;
if (gst_string_length(s) == len) {
if (!gst_string_hash(s))
gst_string_hash(s) = gst_string_calchash(s);
if (gst_string_hash(s) == hash) {
uint32_t i;
for (i = 0; i < len; ++i)
if (s[i] != key[i])
goto notequal;
return bucket->value;
}
}
}
notequal:
bucket = bucket->next;
}
/* Return nil */
{
GstValue ret;
ret.type = GST_NIL;
return ret;
}
}
/* Remove an entry from the dictionary */
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
GstBucket *bucket, *previous;

5
ds.h
View File

@ -87,9 +87,12 @@ void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta);
/* Create a new object */
GstObject *gst_object(Gst *vm, uint32_t capacity);
/* Get a value out of the dictionary */
/* Get a value out of the object */
GstValue gst_object_get(GstObject *obj, GstValue key);
/* Get a value of of an object with a string key */
GstValue gst_object_get_cstring(GstObject *obj, const char *key);
/* Get a Value from the dictionary, but remove it at the same
* time. */
GstValue gst_object_remove(Gst *vm, GstObject *obj, GstValue key);

20
gc.c
View File

@ -55,16 +55,15 @@ static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
}
}
/* Helper to mark a stack frame. Returns the next frame. */
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) {
/* Helper to mark a stack frame. Returns the next stackframe. */
static GstValue *gst_mark_stackframe(Gst *vm, GstValue *stack) {
uint32_t i;
GstValue *stack = (GstValue *)frame + GST_FRAME_SIZE;
gst_mark(vm, &frame->callee);
if (frame->env)
gst_mark_funcenv(vm, frame->env);
for (i = 0; i < frame->size; ++i)
gst_mark(vm, &gst_frame_callee(stack));
if (gst_frame_env(stack) != NULL)
gst_mark_funcenv(vm, gst_frame_env(stack));
for (i = 0; i < gst_frame_size(stack); ++i)
gst_mark(vm, stack + i);
return (GstStackFrame *)(stack + frame->size);
return stack + gst_frame_size(stack) + GST_FRAME_SIZE;
}
/* Mark allocated memory associated with a value. This is
@ -110,9 +109,8 @@ void gst_mark(Gst *vm, GstValue *x) {
case GST_THREAD:
if (gc_header(x->data.thread)->color != vm->black) {
GstThread *thread = x->data.thread;
GstStackFrame *frame = (GstStackFrame *)thread->data;
GstStackFrame *end = (GstStackFrame *)(thread->data +
thread->count - GST_FRAME_SIZE);
GstValue *frame = thread->data + GST_FRAME_SIZE;
GstValue *end = thread->data + thread->count;
gc_header(thread)->color = vm->black;
gc_header(thread->data)->color = vm->black;
while (frame <= end)

6
main.c
View File

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "gst.h"
#include "disasm.h"
/* Simple printer for gst strings */
static void string_put(FILE *out, uint8_t * string) {
@ -76,8 +77,11 @@ void debug_repl(FILE *in, FILE *out) {
continue;
}
/* Print asm */
/* gst_dasm_function(stdout, func.data.function); */
/* Execute function */
if (gst_start(&vm, func)) {
if (gst_run(&vm, func)) {
if (out) {
if (vm.crash) {
fprintf(out, "VM crash: %s\n", vm.crash);

27
stl.c
View File

@ -1,4 +1,4 @@
/* This implemets a standard library in gst. Some of this
/* This implements a standard library in gst. Some of this
* will eventually be ported over to gst if possible */
#include "stl.h"
#include "gst.h"
@ -38,11 +38,36 @@ int gst_stl_setclass(Gst *vm) {
gst_c_return(vm, x);
}
/* Call a function */
int gst_stl_callforeach(Gst *vm) {
GstValue func = gst_arg(vm, 0);
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);
return GST_RETURN_OK;
} else {
gst_c_throwc(vm, "expected at least one argument");
}
}
/* Exit */
int gst_stl_exit(Gst *vm) {
int ret;
GstValue exitValue = gst_arg(vm, 0);
ret = (exitValue.type == GST_NUMBER) ? exitValue.data.number : 0;
exit(ret);
return GST_RETURN_OK;
}
/* Load core */
void gst_stl_load_core(GstCompiler *c) {
gst_compiler_add_global_cfunction(c, "print", gst_stl_print);
gst_compiler_add_global_cfunction(c, "get-class", gst_stl_getclass);
gst_compiler_add_global_cfunction(c, "set-class", gst_stl_setclass);
gst_compiler_add_global_cfunction(c, "call-for-each", gst_stl_callforeach);
gst_compiler_add_global_cfunction(c, "exit", gst_stl_exit);
}
/****/

15
util.h
View File

@ -46,6 +46,21 @@
#define NULL ((void *)0)
#endif
/* Stack frame manipulation */
/* Size of stack frame in number of values */
#define GST_FRAME_SIZE 5
/* Macros for referencing that 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])
#define gst_frame_errloc(s) ((s - 2)->data.hws[2])
#define gst_frame_ret(s) ((s - 2)->data.hws[3])
#define gst_frame_pc(s) ((s - 3)->data.u16p)
#define gst_frame_errjmp(s) ((s - 4)->data.u16p)
#define gst_frame_env(s) ((s - 5)->data.env)
/* C function helpers */
/* Return in a c function */

17
value.c
View File

@ -155,9 +155,14 @@ uint8_t *gst_to_string(Gst *vm, GstValue x) {
return NULL;
}
/* Simple hash function */
static uint32_t djb2(const uint8_t * str) {
const uint8_t * end = str + gst_string_length(str);
/* GST string version */
uint32_t gst_string_calchash(const uint8_t *str) {
return gst_cstring_calchash(str, gst_string_length(str));
}
/* Simple hash function (djb2) */
uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len) {
const uint8_t *end = str + len;
uint32_t hash = 5381;
while (str < end)
hash = (hash << 5) + hash + *str++;
@ -165,7 +170,7 @@ static uint32_t djb2(const uint8_t * str) {
}
/* Simple hash function to get tuple hash */
static uint32_t tuple_hash(GstValue *tuple) {
static uint32_t tuple_calchash(GstValue *tuple) {
uint32_t i;
uint32_t count = gst_tuple_length(tuple);
uint32_t hash = 5387;
@ -264,13 +269,13 @@ uint32_t gst_hash(GstValue x) {
if (gst_string_hash(x.data.string))
hash = gst_string_hash(x.data.string);
else
hash = gst_string_hash(x.data.string) = djb2(x.data.string);
hash = gst_string_hash(x.data.string) = gst_string_calchash(x.data.string);
break;
case GST_TUPLE:
if (gst_tuple_hash(x.data.tuple))
hash = gst_tuple_hash(x.data.tuple);
else
hash = gst_tuple_hash(x.data.tuple) = tuple_hash(x.data.tuple);
hash = gst_tuple_hash(x.data.tuple) = tuple_calchash(x.data.tuple);
break;
default:
/* Cast the pointer */

View File

@ -23,6 +23,12 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value);
/* Load a c style string into a gst value (copies data) */
GstValue gst_load_cstring(Gst *vm, const char *string);
/* Simple hash function (djb2) */
uint32_t gst_string_calchash(const uint8_t *str);
/* C string hash version */
uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len);
/* Convert any gst value into a string */
uint8_t *gst_to_string(Gst *vm, GstValue x);

621
vm.c
View File

@ -10,7 +10,7 @@
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
/* Bail from the VM with an error string. */
#define gst_error(vm, e) return ((vm)->ret = gst_load_cstring((vm), (e)), GST_RETURN_ERROR)
#define gst_error(vm, e) do { (vm)->ret = gst_load_cstring((vm), (e)); goto vm_error; } while (0)
/* Crash. Not catchable, unlike error. */
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
@ -29,7 +29,7 @@ static void gst_load(Gst *vm, GstValue callee) {
uint32_t startCapacity;
uint32_t locals, i;
uint16_t *pc;
GstStackFrame *frame;
GstValue *stack;
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
if (callee.type == GST_FUNCTION) {
locals = callee.data.function->def->locals;
@ -45,43 +45,51 @@ static void gst_load(Gst *vm, GstValue callee) {
thread->capacity = startCapacity;
thread->count = GST_FRAME_SIZE;
vm->thread = thread;
frame = (GstStackFrame *)thread->data;
frame->prevSize = 0;
frame->size = locals;
frame->callee = callee;
frame->errorJump = NULL;
frame->env = NULL;
frame->pc = pc;
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)
thread->data[GST_FRAME_SIZE + i].type = GST_NIL;
stack[i].type = GST_NIL;
}
/* Contextual macro to state in function with VM */
#define GST_STATE_SYNC() do { \
thread = *vm->thread; \
stack = thread.data + thread.count; \
} while (0)
/* Start running the VM */
int gst_start(Gst *vm, GstValue func) {
/* Write local state back to VM */
#define GST_STATE_WRITE() do { \
*vm->thread = thread; \
} while (0)
/* Start running the VM from where it left off. Continue running
* until the stack size is smaller than minStackSize. */
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
/* VM state */
GstThread thread;
GstValue *stack;
GstStackFrame frame;
GstValue temp, v1, v2;
uint16_t *pc;
/* Load the callee */
gst_load(vm, func);
/* Intialize local state */
thread = *vm->thread;
stack = thread.data + thread.count;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
pc = frame.pc;
GST_STATE_SYNC();
pc = gst_frame_pc(stack);
/* Main interpreter loop */
mainloop:
for (;;) {
switch (*pc) {
default:
gst_error(vm, "unknown opcode");
break;
#define OP_BINARY_MATH(op) \
v1 = stack[pc[2]]; \
v2 = stack[pc[3]]; \
@ -161,12 +169,14 @@ int gst_start(Gst *vm, GstValue func) {
case GST_OP_UPV: /* Load Up Value */
case GST_OP_SUV: /* Set Up Value */
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
{
GstValue *upv;
GstFunction *fn = frame.callee.data.function;
GstFunction *fn;
GstFuncEnv *env;
uint16_t level = pc[2];
temp = gst_frame_callee(stack);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
fn = temp.data.function;
if (level == 0)
upv = stack + pc[3];
else {
@ -200,116 +210,12 @@ int gst_start(Gst *vm, GstValue func) {
pc += *((int32_t *)(pc + 1));
break;
case GST_OP_CAL: /* Call */
{
int varArgs;
uint32_t expectedArity;
uint32_t normalArity;
temp = stack[pc[1]];
uint32_t arity = pc[3];
uint32_t oldCount = thread.count;
uint32_t i, locals;
GstValue *oldBase;
frame.pc = pc + 4 + arity;
frame.ret = pc[2];
/* Save current stack frame */
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
/* Get the size of next stack frame */
if (temp.type == GST_FUNCTION) {
GstFunction *fn = temp.data.function;
locals = fn->def->locals;
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
expectedArity = fn->def->arity;
if (arity > expectedArity)
normalArity = expectedArity;
else
normalArity = arity;
} else if (temp.type == GST_CFUNCTION) {
locals = normalArity = expectedArity = arity;
varArgs = 0;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
/* Push next stack frame */
{
uint32_t nextCount = thread.count + frame.size + 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;
}
thread.count = nextCount;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + locals; ++i)
thread.data[i].type = GST_NIL;
/* Set up the new stack frame */
stack = thread.data + thread.count;
frame.prevSize = frame.size;
frame.size = locals;
frame.env = NULL;
frame.callee = temp;
frame.errorJump = NULL;
}
/* Prepare to copy arguments to next stack frame */
oldBase = thread.data + oldCount;
/* Write arguments to new stack */
for (i = 0; i < normalArity; ++i)
stack[i] = oldBase[pc[4 + 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] = stack[j];
stack[expectedArity].type = GST_TUPLE;
stack[expectedArity].data.tuple = tuple;
}
/* Call the function */
if (temp.type == GST_CFUNCTION) {
/* Save current state to vm thread */
int status;
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
*vm->thread = thread;
vm->ret.type = GST_NIL;
status = temp.data.cfunction(vm);
v2 = vm->ret;
if (status == GST_RETURN_OK)
goto ret;
else
goto vm_error;
} else {
pc = temp.data.function->def->byteCode;
}
}
break;
case GST_OP_RET: /* Return */
v2 = stack[pc[1]];
goto ret;
case GST_OP_CST: /* Load constant value */
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
if (pc[2] > frame.callee.data.function->def->literalsLen)
v1 = gst_frame_callee(stack);
gst_assert(vm, v1.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
if (pc[2] > v1.data.function->def->literalsLen)
gst_error(vm, GST_NO_UPVALUE);
stack[pc[1]] = frame.callee.data.function->def->literals[pc[2]];
stack[pc[1]] = v1.data.function->def->literals[pc[2]];
pc += 3;
break;
@ -335,24 +241,25 @@ int gst_start(Gst *vm, GstValue func) {
case GST_OP_CLN: /* Create closure from constant FuncDef */
{
GstFunction *fn;
if (frame.callee.type != GST_FUNCTION)
v1 = gst_frame_callee(stack);
if (v1.type != GST_FUNCTION)
gst_error(vm, GST_EXPECTED_FUNCTION);
if (!frame.env) {
frame.env = gst_alloc(vm, sizeof(GstFuncEnv));
if (gst_frame_env(stack) == NULL) {
gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv));
*vm->thread = thread;
frame.env->thread = vm->thread;
frame.env->stackOffset = thread.count;
frame.env->values = NULL;
gst_frame_env(stack)->thread = vm->thread;
gst_frame_env(stack)->stackOffset = thread.count;
gst_frame_env(stack)->values = NULL;
}
if (pc[2] > frame.callee.data.function->def->literalsLen)
if (pc[2] > v1.data.function->def->literalsLen)
gst_error(vm, GST_NO_UPVALUE);
temp = frame.callee.data.function->def->literals[pc[2]];
temp = v1.data.function->def->literals[pc[2]];
if (temp.type != GST_NIL)
gst_error(vm, "cannot create closure");
fn = gst_alloc(vm, sizeof(GstFunction));
fn->def = (GstFuncDef *) temp.data.pointer;
fn->parent = frame.callee.data.function;
fn->env = frame.env;
fn->parent = v1.data.function;
fn->env = gst_frame_env(stack);
temp.type = GST_FUNCTION;
temp.data.function = fn;
stack[pc[1]] = temp;
@ -428,110 +335,6 @@ int gst_start(Gst *vm, GstValue func) {
}
break;
case GST_OP_TCL: /* Tail call */
{
int varArgs;
uint32_t expectedArity;
uint32_t normalArity;
temp = stack[pc[1]];
uint32_t arity = pc[2];
uint16_t locals;
uint32_t i, workspace, totalCapacity;
/* Check for closures */
if (frame.env) {
frame.env->thread = NULL;
frame.env->stackOffset = frame.size;
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
gst_memcpy(frame.env->values,
thread.data + thread.count,
frame.size * sizeof(GstValue));
frame.env = NULL;
}
/* Get size of new stack frame */
if (temp.type == GST_CFUNCTION) {
locals = normalArity = expectedArity = arity;
varArgs = 0;
} else if (temp.type == GST_FUNCTION) {
locals = temp.data.function->def->locals;
varArgs = temp.data.function->def->flags & GST_FUNCDEF_FLAG_VARARG;
expectedArity = temp.data.function->def->arity;
if (arity > expectedArity)
normalArity = expectedArity;
else
normalArity = arity;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
/* Get enough space for manipulating args - at least twice current frame size */
if (arity > frame.size) {
workspace = arity;
} else {
workspace = frame.size;
}
/* Ensure stack has enough space for copies of arguments */
totalCapacity = thread.count + arity + workspace + locals;
if (totalCapacity > thread.capacity) {
uint32_t newCap = totalCapacity * 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;
}
/* Copy the arguments into the extra space */
for (i = 0; i < normalArity; ++i)
stack[workspace + i] = stack[pc[3 + i]];
/* Copy the end of the stack to the parameter position */
gst_memcpy(stack, stack + workspace, normalArity * sizeof(GstValue));
/* Update the stack frame */
frame.callee = temp;
frame.size = locals;
/* Nil the non argument part of the stack for gc */
for (i = normalArity; i < frame.size; ++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] = stack[workspace + j];
stack[expectedArity].type = GST_TUPLE;
stack[expectedArity].data.tuple = tuple;
}
/* Call the function */
if (temp.type == GST_CFUNCTION) {
/* Save current state to vm thread */
int status;
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
*vm->thread = thread;
vm->ret.type = GST_NIL;
status = temp.data.cfunction(vm);
v2 = vm->ret;
if (status == GST_RETURN_OK)
goto ret;
else
goto vm_error;
} else {
pc = temp.data.function->def->byteCode;
}
}
break;
case GST_OP_RTN: /* Return nil */
v2.type = GST_NIL;
goto ret;
case GST_OP_GET: /* Associative get */
{
const char *err;
@ -558,84 +361,296 @@ int gst_start(Gst *vm, GstValue func) {
break;
case GST_OP_TRY: /* Begin try block */
frame.errorSlot = pc[1];
frame.errorJump = pc + *(uint32_t *)(pc + 2);
gst_frame_errloc(stack) = pc[1];
gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2);
pc += 4;
break;
case GST_OP_UTY: /* End try block */
frame.errorJump = NULL;
gst_frame_errjmp(stack) = NULL;
pc++;
break;
default:
gst_error(vm, "unknown opcode");
case GST_OP_RTN: /* Return nil */
vm->ret.type = GST_NIL;
goto ret;
case GST_OP_RET: /* Return */
vm->ret = stack[pc[1]];
goto ret;
case GST_OP_PSH: /* Push stack frame */
{
GstValue *nextStack;
uint32_t expectedArity, normalArity, arity, varArgs, i, locals, nextCount;
/* Get arguments to op */
temp = stack[pc[1]];
arity = pc[2];
/* Get the size of next stack frame */
if (temp.type == GST_FUNCTION) {
GstFunction *fn = temp.data.function;
locals = fn->def->locals;
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
expectedArity = fn->def->arity;
if (arity > expectedArity)
normalArity = expectedArity;
else
normalArity = arity;
} else if (temp.type == GST_CFUNCTION) {
locals = normalArity = expectedArity = arity;
varArgs = 0;
} else {
gst_error(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;
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 arguments to new stack */
for (i = 0; i < normalArity; ++i)
nextStack[i] = stack[pc[3 + i]];
/* Clear 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, arity - expectedArity);
for (j = expectedArity; j < arity; ++j)
tuple[j - expectedArity] = stack[pc[3 + j]];
nextStack[expectedArity].type = GST_TUPLE;
nextStack[expectedArity].data.tuple = tuple;
}
/* Increment pc */
pc += 3 + arity;
}
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();
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 */
if (frame.env) {
frame.env->thread = NULL;
frame.env->stackOffset = frame.size;
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
gst_memcpy(frame.env->values,
thread.data + thread.count,
frame.size * sizeof(GstValue));
}
if (thread.count <= GST_FRAME_SIZE)
gst_exit(vm, v2);
stack -= frame.prevSize + GST_FRAME_SIZE;
thread.count -= frame.prevSize + GST_FRAME_SIZE;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
pc = frame.pc;
stack[frame.ret] = v2;
break;
}
}
/* Check for closure */
pop_frame(GST_RETURN_OK);
pc = gst_frame_pc(stack);
stack[gst_frame_ret(stack)] = vm->ret;
break;
/* Handle errors from c functions and vm opcodes */
vm_error:
while (gst_frame_errjmp(stack) == NULL)
pop_frame(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 */
/* This, however, is good for testing to ensure no memory leaks */
*vm->thread = thread;
gst_maybe_collect(vm);
} /* end for */
}
/* Continue running the VM after it has stopped */
int gst_continue(Gst *vm) {
return gst_continue_size(vm, vm->thread->count);
}
/* Run the vm with a given function */
int gst_run(Gst *vm, GstValue func) {
gst_load(vm, func);
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 */
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);
}
/* Handle errors from c functions and vm opcodes */
vm_error:
while (!frame.errorJump) {
/* Check for closure */
if (frame.env) {
frame.env->thread = NULL;
frame.env->stackOffset = frame.size;
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
gst_memcpy(frame.env->values,
thread.data + thread.count,
frame.size * sizeof(GstValue));
}
stack -= frame.prevSize + GST_FRAME_SIZE;
if (stack <= thread.data) {
thread.count = 0;
break;
}
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
}
/* If we completely unwind the stack we just return */
if (thread.count < GST_FRAME_SIZE)
return GST_RETURN_ERROR;
/* Jump to the error location */
pc = frame.errorJump;
/* Set error */
stack[frame.errorSlot] = vm->ret;
/* Resume vm */
goto mainloop;
/* 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)
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;
}
}
/* Get an argument from the stack */
GstValue gst_arg(Gst *vm, uint16_t index) {
GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
uint16_t frameSize = frame->size;
uint16_t frameSize = gst_frame_size(stack);
if (frameSize <= index) {
GstValue ret;
ret.type = GST_NIL;
@ -647,8 +662,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;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
uint16_t frameSize = frame->size;
uint16_t frameSize = gst_frame_size(stack);
if (frameSize <= index) return;
stack[index] = x;
}
@ -656,8 +670,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;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
return frame->size;
return gst_frame_size(stack);
}
/* Initialize the VM */

8
vm.h
View File

@ -11,7 +11,13 @@ void gst_init(Gst * vm);
void gst_deinit(Gst * vm);
/* Start running the VM with a given entry point */
int gst_start(Gst * vm, GstValue func);
int gst_run(Gst *vm, GstValue func);
/* Start running the VM from where it left off. */
int gst_continue(Gst *vm);
/* Call a gst value */
int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args);
/* Run garbage collection */
void gst_collect(Gst * vm);