1
0
mirror of https://github.com/janet-lang/janet synced 2025-02-02 10:19:10 +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 # TIL
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -O3 CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
TARGET=interp TARGET=interp
PREFIX=/usr/local PREFIX=/usr/local
# C sources # C sources
HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h stl.h 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 SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c disasm.c
OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET) all: $(TARGET)

View File

@ -1246,21 +1246,22 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, GstValue *form) {
/* Free up some slots */ /* Free up some slots */
compiler_drop_slot(c, scope, callee); compiler_drop_slot(c, scope, callee);
compiler_tracker_free(c, scope, &tracker); 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 this is in tail position do a tail call. */
if (opts.isTail) { if (opts.isTail) {
gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL); gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL);
gst_buffer_push_u16(c->vm, buffer, callee.index);
ret.hasReturned = 1; ret.hasReturned = 1;
ret.isNil = 1; ret.isNil = 1;
} else { } else {
ret = compiler_get_target(c, opts); ret = compiler_get_target(c, opts);
gst_buffer_push_u16(c->vm, buffer, GST_OP_CAL); 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, 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; return ret;
} }
} }

View File

@ -44,7 +44,6 @@ typedef struct GstFuncDef GstFuncDef;
typedef struct GstFuncEnv GstFuncEnv; typedef struct GstFuncEnv GstFuncEnv;
/* Definitely implementation details */ /* Definitely implementation details */
typedef struct GstStackFrame GstStackFrame;
typedef struct GstBucket GstBucket; typedef struct GstBucket GstBucket;
/* The general gst value type. Contains a large union and /* The general gst value type. Contains a large union and
@ -63,6 +62,11 @@ struct GstValue {
GstCFunction cfunction; GstCFunction cfunction;
GstFunction *function; GstFunction *function;
uint8_t *string; uint8_t *string;
/* Indirectly used union members */
uint16_t *u16p;
GstFuncEnv *env;
uint16_t hws[4];
uint8_t bytes[8];
void *pointer; void *pointer;
} data; } data;
}; };
@ -80,9 +84,6 @@ struct GstThread {
} status; } 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. */ /* A dynamic array type. Useful for implementing a stack. */
struct GstArray { struct GstArray {
uint32_t count; uint32_t count;
@ -150,18 +151,6 @@ struct GstUserdataHeader {
GstObject *meta; 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 */ /* VM return status from c function */
#define GST_RETURN_OK 0 #define GST_RETURN_OK 0
#define GST_RETURN_ERROR 1 #define GST_RETURN_ERROR 1
@ -203,8 +192,6 @@ enum GstOpCode {
GST_OP_UPV, /* Load upvalue */ GST_OP_UPV, /* Load upvalue */
GST_OP_JIF, /* Jump if */ GST_OP_JIF, /* Jump if */
GST_OP_JMP, /* Jump */ GST_OP_JMP, /* Jump */
GST_OP_CAL, /* Call function */
GST_OP_RET, /* Return from function */
GST_OP_SUV, /* Set upvalue */ GST_OP_SUV, /* Set upvalue */
GST_OP_CST, /* Load constant */ GST_OP_CST, /* Load constant */
GST_OP_I32, /* Load 32 bit signed integer */ GST_OP_I32, /* Load 32 bit signed integer */
@ -217,13 +204,16 @@ enum GstOpCode {
GST_OP_ARR, /* Create array */ GST_OP_ARR, /* Create array */
GST_OP_DIC, /* Create object */ GST_OP_DIC, /* Create object */
GST_OP_TUP, /* Create tuple */ GST_OP_TUP, /* Create tuple */
GST_OP_TCL, /* Tail call */
GST_OP_RTN, /* Return nil */
GST_OP_SET, /* Assocaitive set */ GST_OP_SET, /* Assocaitive set */
GST_OP_GET, /* Associative get */ GST_OP_GET, /* Associative get */
GST_OP_ERR, /* Throw error */ GST_OP_ERR, /* Throw error */
GST_OP_TRY, /* Begin try block */ 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 #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]); dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
current += 3; current += 3;
break; 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: case GST_OP_SUV:
dasm_print_arg(out, "setUpValue"); dasm_print_arg(out, "setUpValue");
dasm_print_slot(out, current[1]); 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: case GST_OP_TUP:
current += dasm_varg_op(out, current, "tuple", 1); current += dasm_varg_op(out, current, "tuple", 1);
break; 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: case GST_OP_GET:
current += dasm_fixed_op(out, current, "get", 3); current += dasm_fixed_op(out, current, "get", 3);
break; break;
@ -199,6 +187,21 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
case GST_OP_UTY: case GST_OP_UTY:
current += dasm_fixed_op(out, current, "untry", 0); current += dasm_fixed_op(out, current, "untry", 0);
break; 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"); fprintf(out, "\n");
} }

36
ds.c
View File

@ -216,7 +216,7 @@ static GstBucket *gst_object_find(GstObject *o, GstValue key) {
return (GstBucket *)0; 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) { GstValue gst_object_get(GstObject *o, GstValue key) {
GstBucket *bucket = gst_object_find(o, key); GstBucket *bucket = gst_object_find(o, key);
if (bucket) { 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 */ /* Remove an entry from the dictionary */
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) { GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
GstBucket *bucket, *previous; 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 */ /* Create a new object */
GstObject *gst_object(Gst *vm, uint32_t capacity); 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); 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 /* Get a Value from the dictionary, but remove it at the same
* time. */ * time. */
GstValue gst_object_remove(Gst *vm, GstObject *obj, GstValue key); 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. */ /* Helper to mark a stack frame. Returns the next stackframe. */
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) { static GstValue *gst_mark_stackframe(Gst *vm, GstValue *stack) {
uint32_t i; uint32_t i;
GstValue *stack = (GstValue *)frame + GST_FRAME_SIZE; gst_mark(vm, &gst_frame_callee(stack));
gst_mark(vm, &frame->callee); if (gst_frame_env(stack) != NULL)
if (frame->env) gst_mark_funcenv(vm, gst_frame_env(stack));
gst_mark_funcenv(vm, frame->env); for (i = 0; i < gst_frame_size(stack); ++i)
for (i = 0; i < frame->size; ++i)
gst_mark(vm, 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 /* Mark allocated memory associated with a value. This is
@ -110,9 +109,8 @@ void gst_mark(Gst *vm, GstValue *x) {
case GST_THREAD: case GST_THREAD:
if (gc_header(x->data.thread)->color != vm->black) { if (gc_header(x->data.thread)->color != vm->black) {
GstThread *thread = x->data.thread; GstThread *thread = x->data.thread;
GstStackFrame *frame = (GstStackFrame *)thread->data; GstValue *frame = thread->data + GST_FRAME_SIZE;
GstStackFrame *end = (GstStackFrame *)(thread->data + GstValue *end = thread->data + thread->count;
thread->count - GST_FRAME_SIZE);
gc_header(thread)->color = vm->black; gc_header(thread)->color = vm->black;
gc_header(thread->data)->color = vm->black; gc_header(thread->data)->color = vm->black;
while (frame <= end) while (frame <= end)

6
main.c
View File

@ -1,6 +1,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "gst.h" #include "gst.h"
#include "disasm.h"
/* Simple printer for gst strings */ /* Simple printer for gst strings */
static void string_put(FILE *out, uint8_t * string) { static void string_put(FILE *out, uint8_t * string) {
@ -76,8 +77,11 @@ void debug_repl(FILE *in, FILE *out) {
continue; continue;
} }
/* Print asm */
/* gst_dasm_function(stdout, func.data.function); */
/* Execute function */ /* Execute function */
if (gst_start(&vm, func)) { if (gst_run(&vm, func)) {
if (out) { if (out) {
if (vm.crash) { if (vm.crash) {
fprintf(out, "VM crash: %s\n", 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 */ * will eventually be ported over to gst if possible */
#include "stl.h" #include "stl.h"
#include "gst.h" #include "gst.h"
@ -38,11 +38,36 @@ int gst_stl_setclass(Gst *vm) {
gst_c_return(vm, x); 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 */ /* Load core */
void gst_stl_load_core(GstCompiler *c) { void gst_stl_load_core(GstCompiler *c) {
gst_compiler_add_global_cfunction(c, "print", gst_stl_print); 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, "get-class", gst_stl_getclass);
gst_compiler_add_global_cfunction(c, "set-class", gst_stl_setclass); 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) #define NULL ((void *)0)
#endif #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 */ /* C function helpers */
/* Return in a c function */ /* 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; return NULL;
} }
/* Simple hash function */ /* GST string version */
static uint32_t djb2(const uint8_t * str) { uint32_t gst_string_calchash(const uint8_t *str) {
const uint8_t * end = str + gst_string_length(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; uint32_t hash = 5381;
while (str < end) while (str < end)
hash = (hash << 5) + hash + *str++; hash = (hash << 5) + hash + *str++;
@ -165,7 +170,7 @@ static uint32_t djb2(const uint8_t * str) {
} }
/* Simple hash function to get tuple hash */ /* 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 i;
uint32_t count = gst_tuple_length(tuple); uint32_t count = gst_tuple_length(tuple);
uint32_t hash = 5387; uint32_t hash = 5387;
@ -264,13 +269,13 @@ uint32_t gst_hash(GstValue x) {
if (gst_string_hash(x.data.string)) if (gst_string_hash(x.data.string))
hash = gst_string_hash(x.data.string); hash = gst_string_hash(x.data.string);
else 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; break;
case GST_TUPLE: case GST_TUPLE:
if (gst_tuple_hash(x.data.tuple)) if (gst_tuple_hash(x.data.tuple))
hash = gst_tuple_hash(x.data.tuple); hash = gst_tuple_hash(x.data.tuple);
else 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; break;
default: default:
/* Cast the pointer */ /* 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) */ /* Load a c style string into a gst value (copies data) */
GstValue gst_load_cstring(Gst *vm, const char *string); 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 */ /* Convert any gst value into a string */
uint8_t *gst_to_string(Gst *vm, GstValue x); 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) #define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
/* Bail from the VM with an error string. */ /* 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. */ /* Crash. Not catchable, unlike error. */
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH) #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 startCapacity;
uint32_t locals, i; uint32_t locals, i;
uint16_t *pc; uint16_t *pc;
GstStackFrame *frame; GstValue *stack;
GstThread *thread = gst_alloc(vm, sizeof(GstThread)); GstThread *thread = gst_alloc(vm, sizeof(GstThread));
if (callee.type == GST_FUNCTION) { if (callee.type == GST_FUNCTION) {
locals = callee.data.function->def->locals; locals = callee.data.function->def->locals;
@ -45,43 +45,51 @@ static void gst_load(Gst *vm, GstValue callee) {
thread->capacity = startCapacity; thread->capacity = startCapacity;
thread->count = GST_FRAME_SIZE; thread->count = GST_FRAME_SIZE;
vm->thread = thread; vm->thread = thread;
frame = (GstStackFrame *)thread->data; stack = thread->data + GST_FRAME_SIZE;
frame->prevSize = 0; gst_frame_prevsize(stack) = 0;
frame->size = locals; gst_frame_size(stack) = locals;
frame->callee = callee; gst_frame_callee(stack) = callee;
frame->errorJump = NULL; gst_frame_env(stack) = NULL;
frame->env = NULL; gst_frame_errjmp(stack) = NULL;
frame->pc = pc; gst_frame_pc(stack) = pc;
/* Nil arguments */ /* Nil arguments */
for (i = 0; i < locals; ++i) 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 */ /* Write local state back to VM */
int gst_start(Gst *vm, GstValue func) { #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 */ /* VM state */
GstThread thread; GstThread thread;
GstValue *stack; GstValue *stack;
GstStackFrame frame;
GstValue temp, v1, v2; GstValue temp, v1, v2;
uint16_t *pc; uint16_t *pc;
/* Load the callee */
gst_load(vm, func);
/* Intialize local state */ /* Intialize local state */
thread = *vm->thread; GST_STATE_SYNC();
stack = thread.data + thread.count; pc = gst_frame_pc(stack);
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
pc = frame.pc;
/* Main interpreter loop */ /* Main interpreter loop */
mainloop:
for (;;) { for (;;) {
switch (*pc) { switch (*pc) {
default:
gst_error(vm, "unknown opcode");
break;
#define OP_BINARY_MATH(op) \ #define OP_BINARY_MATH(op) \
v1 = stack[pc[2]]; \ v1 = stack[pc[2]]; \
v2 = stack[pc[3]]; \ 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_UPV: /* Load Up Value */
case GST_OP_SUV: /* Set Up Value */ case GST_OP_SUV: /* Set Up Value */
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
{ {
GstValue *upv; GstValue *upv;
GstFunction *fn = frame.callee.data.function; GstFunction *fn;
GstFuncEnv *env; GstFuncEnv *env;
uint16_t level = pc[2]; 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) if (level == 0)
upv = stack + pc[3]; upv = stack + pc[3];
else { else {
@ -200,116 +210,12 @@ int gst_start(Gst *vm, GstValue func) {
pc += *((int32_t *)(pc + 1)); pc += *((int32_t *)(pc + 1));
break; 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 */ case GST_OP_CST: /* Load constant value */
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION); v1 = gst_frame_callee(stack);
if (pc[2] > frame.callee.data.function->def->literalsLen) gst_assert(vm, v1.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
if (pc[2] > v1.data.function->def->literalsLen)
gst_error(vm, GST_NO_UPVALUE); 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; pc += 3;
break; break;
@ -335,24 +241,25 @@ int gst_start(Gst *vm, GstValue func) {
case GST_OP_CLN: /* Create closure from constant FuncDef */ case GST_OP_CLN: /* Create closure from constant FuncDef */
{ {
GstFunction *fn; GstFunction *fn;
if (frame.callee.type != GST_FUNCTION) v1 = gst_frame_callee(stack);
if (v1.type != GST_FUNCTION)
gst_error(vm, GST_EXPECTED_FUNCTION); gst_error(vm, GST_EXPECTED_FUNCTION);
if (!frame.env) { if (gst_frame_env(stack) == NULL) {
frame.env = gst_alloc(vm, sizeof(GstFuncEnv)); gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv));
*vm->thread = thread; *vm->thread = thread;
frame.env->thread = vm->thread; gst_frame_env(stack)->thread = vm->thread;
frame.env->stackOffset = thread.count; gst_frame_env(stack)->stackOffset = thread.count;
frame.env->values = NULL; 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); 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) if (temp.type != GST_NIL)
gst_error(vm, "cannot create closure"); gst_error(vm, "cannot create closure");
fn = gst_alloc(vm, sizeof(GstFunction)); fn = gst_alloc(vm, sizeof(GstFunction));
fn->def = (GstFuncDef *) temp.data.pointer; fn->def = (GstFuncDef *) temp.data.pointer;
fn->parent = frame.callee.data.function; fn->parent = v1.data.function;
fn->env = frame.env; fn->env = gst_frame_env(stack);
temp.type = GST_FUNCTION; temp.type = GST_FUNCTION;
temp.data.function = fn; temp.data.function = fn;
stack[pc[1]] = temp; stack[pc[1]] = temp;
@ -428,110 +335,6 @@ int gst_start(Gst *vm, GstValue func) {
} }
break; 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 */ case GST_OP_GET: /* Associative get */
{ {
const char *err; const char *err;
@ -558,84 +361,296 @@ int gst_start(Gst *vm, GstValue func) {
break; break;
case GST_OP_TRY: /* Begin try block */ case GST_OP_TRY: /* Begin try block */
frame.errorSlot = pc[1]; gst_frame_errloc(stack) = pc[1];
frame.errorJump = pc + *(uint32_t *)(pc + 2); gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2);
pc += 4; pc += 4;
break; break;
case GST_OP_UTY: /* End try block */ case GST_OP_UTY: /* End try block */
frame.errorJump = NULL; gst_frame_errjmp(stack) = NULL;
pc++; pc++;
break; break;
default: case GST_OP_RTN: /* Return nil */
gst_error(vm, "unknown opcode"); 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; 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 */ /* Label for return */
ret: ret:
{ /* Check for closure */
/* Check for closure */ pop_frame(GST_RETURN_OK);
if (frame.env) { pc = gst_frame_pc(stack);
frame.env->thread = NULL; stack[gst_frame_ret(stack)] = vm->ret;
frame.env->stackOffset = frame.size; break;
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
gst_memcpy(frame.env->values, /* Handle errors from c functions and vm opcodes */
thread.data + thread.count, vm_error:
frame.size * sizeof(GstValue)); while (gst_frame_errjmp(stack) == NULL)
} pop_frame(GST_RETURN_ERROR);
if (thread.count <= GST_FRAME_SIZE) pc = gst_frame_errjmp(stack);
gst_exit(vm, v2); stack[gst_frame_errloc(stack)] = vm->ret;
stack -= frame.prevSize + GST_FRAME_SIZE; break;
thread.count -= frame.prevSize + GST_FRAME_SIZE;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE)); #undef pop_frame
pc = frame.pc;
stack[frame.ret] = v2; } /* end switch */
break;
}
}
/* TODO: Move collection only to places that allocate memory */ /* TODO: Move collection only to places that allocate memory */
/* This, however, is good for testing to ensure no memory leaks */ /* This, however, is good for testing to ensure no memory leaks */
*vm->thread = thread; *vm->thread = thread;
gst_maybe_collect(vm); 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 */ /* Get next frame size */
vm_error: nextCount = thread.count + gst_frame_size(stack) + GST_FRAME_SIZE;
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;
/* 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 */ /* Get an argument from the stack */
GstValue gst_arg(Gst *vm, uint16_t index) { GstValue gst_arg(Gst *vm, uint16_t index) {
GstValue *stack = vm->thread->data + vm->thread->count; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE); uint16_t frameSize = gst_frame_size(stack);
uint16_t frameSize = frame->size;
if (frameSize <= index) { if (frameSize <= index) {
GstValue ret; GstValue ret;
ret.type = GST_NIL; ret.type = GST_NIL;
@ -647,8 +662,7 @@ GstValue gst_arg(Gst *vm, uint16_t index) {
/* Put a value on the stack */ /* Put a value on the stack */
void gst_set_arg(Gst* vm, uint16_t index, GstValue x) { void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
GstValue *stack = vm->thread->data + vm->thread->count; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE); uint16_t frameSize = gst_frame_size(stack);
uint16_t frameSize = frame->size;
if (frameSize <= index) return; if (frameSize <= index) return;
stack[index] = x; stack[index] = x;
} }
@ -656,8 +670,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
/* Get the size of the VMStack */ /* Get the size of the VMStack */
uint16_t gst_count_args(Gst *vm) { uint16_t gst_count_args(Gst *vm) {
GstValue *stack = vm->thread->data + vm->thread->count; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE); return gst_frame_size(stack);
return frame->size;
} }
/* Initialize the VM */ /* Initialize the VM */

8
vm.h
View File

@ -11,7 +11,13 @@ void gst_init(Gst * vm);
void gst_deinit(Gst * vm); void gst_deinit(Gst * vm);
/* Start running the VM with a given entry point */ /* 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 */ /* Run garbage collection */
void gst_collect(Gst * vm); void gst_collect(Gst * vm);