1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-26 00:10:27 +00:00

Work on speeding up interpreter by moving state inside

interpreter loop.
This commit is contained in:
Calvin Rose 2017-02-26 11:47:50 -05:00
parent fd34837265
commit 33d09f98b1
8 changed files with 538 additions and 604 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 disasm.h datatypes.h gc.h thread.h HEADERS=vm.h ds.h compile.h parse.h value.h disasm.h datatypes.h gc.h
SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c gc.c thread.c SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c gc.c
OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET) all: $(TARGET)

View File

@ -117,7 +117,7 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
} }
if (sameFunction) { if (sameFunction) {
if (!c->tail) { if (!c->tail) {
c_error(c, "Cannot inherit scope when root scope"); c_error(c, "cannot inherit scope when root scope");
} }
scope->nextLocal = c->tail->nextLocal; scope->nextLocal = c->tail->nextLocal;
scope->literals = c->tail->literals; scope->literals = c->tail->literals;
@ -135,7 +135,7 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
static void compiler_pop_scope(GstCompiler *c) { static void compiler_pop_scope(GstCompiler *c) {
GstScope *last = c->tail; GstScope *last = c->tail;
if (last == NULL) { if (last == NULL) {
c_error(c, "No scope to pop."); c_error(c, "no scope to pop");
} else { } else {
if (last->nextLocal > last->frameSize) { if (last->nextLocal > last->frameSize) {
last->frameSize = last->nextLocal; last->frameSize = last->nextLocal;
@ -154,7 +154,7 @@ static void compiler_pop_scope(GstCompiler *c) {
static uint16_t compiler_get_local(GstCompiler *c, GstScope *scope) { static uint16_t compiler_get_local(GstCompiler *c, GstScope *scope) {
if (scope->heapSize == 0) { if (scope->heapSize == 0) {
if (scope->nextLocal + 1 == 0) { if (scope->nextLocal + 1 == 0) {
c_error(c, "Too many local variables. Try splitting up your functions :)"); c_error(c, "too many local variables");
} }
return scope->nextLocal++; return scope->nextLocal++;
} else { } else {
@ -266,7 +266,7 @@ static void compiler_tracker_write(GstCompiler *c, SlotTracker *tracker, int rev
else else
s = tracker->slots[i]; s = tracker->slots[i];
if (s.isNil) if (s.isNil)
c_error(c, "Trying to write nil slot."); c_error(c, "trying to write nil slot");
gst_buffer_push_u16(c->vm, buffer, s.index); gst_buffer_push_u16(c->vm, buffer, s.index);
} }
} }
@ -320,7 +320,7 @@ static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValu
GstValue x; GstValue x;
uint16_t target; uint16_t target;
if (sym.type != GST_STRING) if (sym.type != GST_STRING)
c_error(c, "Expected symbol"); c_error(c, "expected symbol");
target = compiler_get_local(c, scope); target = compiler_get_local(c, scope);
x.type = GST_NUMBER; x.type = GST_NUMBER;
x.data.number = target; x.data.number = target;
@ -406,7 +406,7 @@ static Slot compile_nonref_type(GstCompiler *c, FormOptions opts, GstValue x) {
gst_buffer_push_number(c->vm, buffer, number); gst_buffer_push_number(c->vm, buffer, number);
} }
} else { } else {
c_error(c, "Expected boolean, nil, or number type."); c_error(c, "expected boolean, nil, or number type");
} }
return ret; return ret;
} }
@ -420,7 +420,7 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
Slot ret; Slot ret;
if (opts.resultUnused) return nil_slot(); if (opts.resultUnused) return nil_slot();
if (!symbol_resolve(scope, sym, &level, &index)) if (!symbol_resolve(scope, sym, &level, &index))
c_error(c, "Undefined symbol"); c_error(c, "undefined symbol");
if (level > 0) { if (level > 0) {
/* We have an upvalue */ /* We have an upvalue */
ret = compiler_get_target(c, opts); ret = compiler_get_target(c, opts);
@ -499,7 +499,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
/* Write the correct opcode */ /* Write the correct opcode */
if (form->count < 2) { if (form->count < 2) {
if (op0 < 0) { if (op0 < 0) {
if (opn < 0) c_error(c, "This operator does not take 0 arguments."); if (opn < 0) c_error(c, "this operator does not take 0 arguments");
goto opn; goto opn;
} else { } else {
gst_buffer_push_u16(c->vm, buffer, op0); gst_buffer_push_u16(c->vm, buffer, op0);
@ -507,7 +507,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
} }
} else if (form->count == 2) { } else if (form->count == 2) {
if (op1 < 0) { if (op1 < 0) {
if (opn < 0) c_error(c, "This operator does not take 1 argument."); if (opn < 0) c_error(c, "this operator does not take 1 argument");
goto opn; goto opn;
} else { } else {
gst_buffer_push_u16(c->vm, buffer, op1); gst_buffer_push_u16(c->vm, buffer, op1);
@ -515,7 +515,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
} }
} else if (form->count == 3) { } else if (form->count == 3) {
if (op2 < 0) { if (op2 < 0) {
if (opn < 0) c_error(c, "This operator does not take 2 arguments."); if (opn < 0) c_error(c, "this operator does not take 2 arguments");
goto opn; goto opn;
} else { } else {
gst_buffer_push_u16(c->vm, buffer, op2); gst_buffer_push_u16(c->vm, buffer, op2);
@ -523,7 +523,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
} }
} else { } else {
opn: opn:
if (opn < 0) c_error(c, "This operator does not take n arguments."); if (opn < 0) c_error(c, "this operator does not take n arguments");
gst_buffer_push_u16(c->vm, buffer, opn); gst_buffer_push_u16(c->vm, buffer, opn);
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, form->count - 1); gst_buffer_push_u16(c->vm, buffer, form->count - 1);
@ -536,16 +536,16 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
/* Math specials */ /* Math specials */
static Slot compile_addition(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_addition(GstCompiler *c, FormOptions opts, GstArray *form) {
return compile_operator(c, opts, form, GST_OP_LD0, -1, GST_OP_ADD, GST_OP_ADM, 0); return compile_operator(c, opts, form, GST_OP_LD0, -1, GST_OP_ADD, -1, 0);
} }
static Slot compile_subtraction(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_subtraction(GstCompiler *c, FormOptions opts, GstArray *form) {
return compile_operator(c, opts, form, GST_OP_LD0, -1, GST_OP_SUB, GST_OP_SBM, 0); return compile_operator(c, opts, form, GST_OP_LD0, -1, GST_OP_SUB, -1, 0);
} }
static Slot compile_multiplication(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_multiplication(GstCompiler *c, FormOptions opts, GstArray *form) {
return compile_operator(c, opts, form, GST_OP_LD1, -1, GST_OP_MUL, GST_OP_MUM, 0); return compile_operator(c, opts, form, GST_OP_LD1, -1, GST_OP_MUL, -1, 0);
} }
static Slot compile_division(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_division(GstCompiler *c, FormOptions opts, GstArray *form) {
return compile_operator(c, opts, form, GST_OP_LD1, -1, GST_OP_DIV, GST_OP_DVM, 0); return compile_operator(c, opts, form, GST_OP_LD1, -1, GST_OP_DIV, -1, 0);
} }
static Slot compile_equals(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_equals(GstCompiler *c, FormOptions opts, GstArray *form) {
return compile_operator(c, opts, form, GST_OP_TRU, GST_OP_TRU, GST_OP_EQL, -1, 0); return compile_operator(c, opts, form, GST_OP_TRU, GST_OP_TRU, GST_OP_EQL, -1, 0);
@ -573,7 +573,7 @@ static Slot compile_array(GstCompiler *c, FormOptions opts, GstArray *form) {
} }
static Slot compile_object(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_object(GstCompiler *c, FormOptions opts, GstArray *form) {
if ((form->count % 2) == 0) { if ((form->count % 2) == 0) {
c_error(c, "Dictionary literal requires an even number of arguments"); c_error(c, "dictionary literal requires an even number of arguments");
return nil_slot(); return nil_slot();
} else { } else {
return compile_operator(c, opts, form, -1, -1, -1, GST_OP_DIC, 0); return compile_operator(c, opts, form, -1, -1, -1, GST_OP_DIC, 0);
@ -585,7 +585,7 @@ static Slot compile_set(GstCompiler *c, FormOptions opts, GstArray *form) {
GstBuffer *buffer = c->buffer; GstBuffer *buffer = c->buffer;
FormOptions subOpts = form_options_default(); FormOptions subOpts = form_options_default();
Slot ds, key, val; Slot ds, key, val;
if (form->count != 4) c_error(c, "Set expects 4 arguments"); if (form->count != 4) c_error(c, "set expects 4 arguments");
if (opts.resultUnused) { if (opts.resultUnused) {
ds = compiler_realize_slot(c, compile_value(c, subOpts, form->data[1])); ds = compiler_realize_slot(c, compile_value(c, subOpts, form->data[1]));
} else { } else {
@ -680,7 +680,7 @@ static GstFuncDef *compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, uin
GstFuncDef *def = gst_alloc(c->vm, sizeof(GstFuncDef)); GstFuncDef *def = gst_alloc(c->vm, sizeof(GstFuncDef));
/* Create enough space for the new byteCode */ /* Create enough space for the new byteCode */
if (lastNBytes > buffer->count) if (lastNBytes > buffer->count)
c_error(c, "Trying to extract more bytes from buffer than in buffer."); c_error(c, "trying to extract more bytes from buffer than in buffer");
uint8_t * byteCode = gst_alloc(c->vm, lastNBytes); uint8_t * byteCode = gst_alloc(c->vm, lastNBytes);
def->byteCode = (uint16_t *)byteCode; def->byteCode = (uint16_t *)byteCode;
def->byteCodeLen = lastNBytes / 2; def->byteCodeLen = lastNBytes / 2;
@ -725,12 +725,12 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) {
++current; ++current;
/* Define the function parameters */ /* Define the function parameters */
if (form->data[current].type != GST_ARRAY) if (form->data[current].type != GST_ARRAY)
c_error(c, "Expected function arguments"); c_error(c, "expected function arguments");
params = form->data[current++].data.array; params = form->data[current++].data.array;
for (i = 0; i < params->count; ++i) { for (i = 0; i < params->count; ++i) {
GstValue param = params->data[i]; GstValue param = params->data[i];
if (param.type != GST_STRING) if (param.type != GST_STRING)
c_error(c, "Function parameters should be symbols"); c_error(c, "function parameters should be symbols");
/* The compiler puts the parameter locals /* The compiler puts the parameter locals
* in the right place by default - at the beginning * in the right place by default - at the beginning
* of the stack frame. */ * of the stack frame. */
@ -982,7 +982,7 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstArray *form) {
/* Assignment special */ /* Assignment special */
static Slot compile_var(GstCompiler *c, FormOptions opts, GstArray *form) { static Slot compile_var(GstCompiler *c, FormOptions opts, GstArray *form) {
if (form->count != 3) if (form->count != 3)
c_error(c, "Assignment expects 2 arguments"); c_error(c, "assignment expects 2 arguments");
return compile_assign(c, opts, form->data[1], form->data[2]); return compile_assign(c, opts, form->data[1], form->data[2]);
} }

View File

@ -77,6 +77,9 @@ struct GstThread {
} status; } status;
}; };
/* Size of stack frame */
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) + 1) / sizeof(GstValue))
/* A dynamic array type */ /* A dynamic array type */
struct GstArray { struct GstArray {
uint32_t count; uint32_t count;
@ -153,12 +156,8 @@ struct Gst {
uint32_t memoryInterval; uint32_t memoryInterval;
uint32_t nextCollection; uint32_t nextCollection;
uint32_t black : 1; uint32_t black : 1;
uint32_t lock : 31;
/* Thread */ /* Thread */
uint16_t *pc;
GstThread *thread; GstThread *thread;
GstValue *base;
GstStackFrame *frame;
/* Return state */ /* Return state */
const char *crash; const char *crash;
jmp_buf jump; jmp_buf jump;
@ -226,10 +225,6 @@ enum GstOpCode {
GST_OP_ARR, /* 0x0019 */ GST_OP_ARR, /* 0x0019 */
GST_OP_DIC, /* 0x001a */ GST_OP_DIC, /* 0x001a */
GST_OP_TCL, /* 0x001b */ GST_OP_TCL, /* 0x001b */
GST_OP_ADM, /* 0x001c */
GST_OP_SBM, /* 0x001d */
GST_OP_MUM, /* 0x001e */
GST_OP_DVM, /* 0x001f */
GST_OP_RTN, /* 0x0020 */ GST_OP_RTN, /* 0x0020 */
GST_OP_SET, /* 0x0021 */ GST_OP_SET, /* 0x0021 */
GST_OP_GET, /* 0x0022 */ GST_OP_GET, /* 0x0022 */

View File

@ -18,7 +18,7 @@ static void dasm_print_arg(FILE * out, const char * name) {
uint32_t i = 0; uint32_t i = 0;
char c; char c;
while ((c = *name++)) { while ((c = *name++)) {
fputc(c, out); putc(c, out);
++i; ++i;
} }
for (; i < OP_WIDTH; ++i) for (; i < OP_WIDTH; ++i)
@ -30,9 +30,8 @@ static uint32_t dasm_fixed_op(FILE * out, const uint16_t * current,
const char * name, uint32_t size) { const char * name, uint32_t size) {
uint32_t i; uint32_t i;
dasm_print_arg(out, name); dasm_print_arg(out, name);
for (i = 1; i <= size; ++i) { for (i = 1; i <= size; ++i)
dasm_print_slot(out, current[i]); dasm_print_slot(out, current[i]);
}
return size + 1; return size + 1;
} }
@ -67,8 +66,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
uint16_t *current = byteCode; uint16_t *current = byteCode;
uint16_t *end = byteCode + len; uint16_t *end = byteCode + len;
fprintf(out, "----- ASM BYTECODE START -----\n");
while (current < end) { while (current < end) {
switch (*current) { switch (*current) {
case GST_OP_ADD: case GST_OP_ADD:
@ -181,18 +178,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
case GST_OP_TCL: case GST_OP_TCL:
current += dasm_varg_op(out, current, "tailCall", 1); current += dasm_varg_op(out, current, "tailCall", 1);
break; break;
case GST_OP_ADM:
current += dasm_varg_op(out, current, "addMultiple", 1);
break;
case GST_OP_SBM:
current += dasm_varg_op(out, current, "subMultiple", 1);
break;
case GST_OP_MUM:
current += dasm_varg_op(out, current, "mulMultiple", 1);
break;
case GST_OP_DVM:
current += dasm_varg_op(out, current, "divMultiple", 1);
break;
case GST_OP_RTN: case GST_OP_RTN:
current += dasm_fixed_op(out, current, "returnNil", 0); current += dasm_fixed_op(out, current, "returnNil", 0);
break; break;
@ -217,5 +202,4 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
} }
fprintf(out, "\n"); fprintf(out, "\n");
} }
fprintf(out, "----- END ASM BYTECODE -----\n");
} }

7
gc.c
View File

@ -1,7 +1,6 @@
#include "datatypes.h" #include "datatypes.h"
#include "gc.h" #include "gc.h"
#include "vm.h" #include "vm.h"
#include "thread.h"
#include <stdlib.h> #include <stdlib.h>
/* The metadata header associated with an allocated block of memory */ /* The metadata header associated with an allocated block of memory */
@ -102,7 +101,8 @@ void gst_mark(Gst *vm, GstValue *x) {
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; GstStackFrame *frame = (GstStackFrame *)thread->data;
GstStackFrame *end = gst_thread_frame(thread); GstStackFrame *end = (GstStackFrame *)(thread->data +
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)
@ -143,9 +143,7 @@ void gst_mark(Gst *vm, GstValue *x) {
} }
} }
break; break;
} }
} }
/* Iterate over all allocated memory, and free memory that is not /* Iterate over all allocated memory, and free memory that is not
@ -200,7 +198,6 @@ void *gst_zalloc(Gst *vm, uint32_t size) {
/* Run garbage collection */ /* Run garbage collection */
void gst_collect(Gst *vm) { void gst_collect(Gst *vm) {
if (vm->lock > 0) return;
/* Thread can be null */ /* Thread can be null */
if (vm->thread) { if (vm->thread) {
GstValue thread; GstValue thread;

View File

@ -1,78 +0,0 @@
#include "thread.h"
#include "vm.h"
#include <string.h>
/* Get the stack frame pointer for a thread */
GstStackFrame *gst_thread_frame(GstThread * thread) {
return (GstStackFrame *)(thread->data + thread->count - GST_FRAME_SIZE);
}
/* Ensure that a thread has enough space in it */
void gst_thread_ensure(Gst *vm, GstThread *thread, uint32_t size) {
if (size > thread->capacity) {
uint32_t newCap = size * 2;
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
memcpy(newData, thread->data, thread->capacity * sizeof(GstValue));
thread->data = newData;
thread->capacity = newCap;
}
}
/* Push a stack frame onto a thread */
void gst_thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size) {
uint16_t oldSize;
uint32_t nextCount, i;
GstStackFrame *frame;
if (thread->count) {
frame = gst_thread_frame(thread);
oldSize = frame->size;
} else {
oldSize = 0;
}
nextCount = thread->count + oldSize + GST_FRAME_SIZE;
gst_thread_ensure(vm, thread, nextCount + size);
thread->count = nextCount;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i)
thread->data[i].type = GST_NIL;
vm->base = thread->data + thread->count;
vm->frame = frame = (GstStackFrame *)(vm->base - GST_FRAME_SIZE);
/* Set up the new stack frame */
frame->prevSize = oldSize;
frame->size = size;
frame->env = NULL;
frame->callee = callee;
frame->errorJump = NULL;
}
/* Copy the current function stack to the current closure
environment. Call when exiting function with closures. */
void gst_thread_split_env(Gst *vm) {
GstStackFrame *frame = vm->frame;
GstFuncEnv *env = frame->env;
/* Check for closures */
if (env) {
GstThread *thread = vm->thread;
uint32_t size = frame->size;
env->thread = NULL;
env->stackOffset = size;
env->values = gst_alloc(vm, sizeof(GstValue) * size);
memcpy(env->values, thread->data + thread->count, size * sizeof(GstValue));
}
}
/* Pop the top-most stack frame from stack */
void gst_thread_pop(Gst *vm) {
GstThread *thread = vm->thread;
GstStackFrame *frame = vm->frame;
uint32_t delta = GST_FRAME_SIZE + frame->prevSize;
if (thread->count) {
gst_thread_split_env(vm);
} else {
gst_crash(vm, "stack underflow");
}
thread->count -= delta;
vm->base -= delta;
vm->frame = (GstStackFrame *)(vm->base - GST_FRAME_SIZE);
}

View File

@ -1,25 +0,0 @@
#ifndef THREAD_H
#define THREAD_H
#include "datatypes.h"
/* The size of a StackFrame in units of Values. */
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) - 1) / sizeof(GstValue))
/* Get the stack frame pointer for a thread */
GstStackFrame *gst_thread_frame(GstThread * thread);
/* Ensure that a thread has enough space in it */
void gst_thread_ensure(Gst *vm, GstThread *thread, uint32_t size);
/* Push a stack frame onto a thread */
void gst_thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size);
/* Copy the current function stack to the current closure
environment. Call when exiting function with closures. */
void gst_thread_split_env(Gst *vm);
/* Pop the top-most stack frame from stack */
void gst_thread_pop(Gst *vm);
#endif

623
vm.c
View File

@ -5,30 +5,12 @@
#include "value.h" #include "value.h"
#include "ds.h" #include "ds.h"
#include "gc.h" #include "gc.h"
#include "thread.h"
static const char GST_NO_UPVALUE[] = "no upvalue"; static const char GST_NO_UPVALUE[] = "no upvalue";
static const char GST_EXPECTED_FUNCTION[] = "expected function"; 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_ROP[] = "expected right operand to be number";
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number"; static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
/* Get an upvalue */
static GstValue *gst_vm_upvalue_location(Gst *vm, GstFunction *fn, uint16_t level, uint16_t index) {
GstFuncEnv *env;
GstValue *stack;
if (!level)
return vm->base + index;
while (fn && --level)
fn = fn->parent;
gst_assert(vm, fn, GST_NO_UPVALUE);
env = fn->env;
if (env->thread)
stack = env->thread->data + env->stackOffset;
else
stack = env->values;
return stack + index;
}
/* Get a literal */ /* Get a literal */
static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) { static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
if (index > fn->def->literalsLen) { if (index > fn->def->literalsLen) {
@ -37,128 +19,22 @@ static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
return fn->def->literals[index]; return fn->def->literals[index];
} }
/* Return from the vm */
static void gst_vm_return(Gst *vm, GstValue ret) {
gst_thread_pop(vm);
if (vm->thread->count == 0) {
gst_exit(vm, ret);
}
vm->pc = vm->frame->pc;
vm->base[vm->frame->ret] = ret;
}
/* Implementation of the opcode for function calls */
static void gst_vm_call(Gst *vm) {
GstThread *thread = vm->thread;
GstValue callee = vm->base[vm->pc[1]];
uint32_t arity = vm->pc[3];
uint32_t oldCount = thread->count;
uint32_t i;
GstValue *oldBase;
vm->frame->pc = vm->pc + 4 + arity;
vm->frame->ret = vm->pc[2];
if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function;
gst_thread_push(vm, thread, callee, fn->def->locals);
} else if (callee.type == GST_CFUNCTION) {
gst_thread_push(vm, thread, callee, arity);
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
oldBase = thread->data + oldCount;
if (callee.type == GST_CFUNCTION) {
for (i = 0; i < arity; ++i)
vm->base[i] = oldBase[vm->pc[4 + i]];
++vm->lock;
gst_vm_return(vm, callee.data.cfunction(vm));
--vm->lock;
} else {
GstFunction *f = callee.data.function;
uint32_t locals = f->def->locals;
for (i = 0; i < arity; ++i)
vm->base[i] = oldBase[vm->pc[4 + i]];
for (; i < locals; ++i)
vm->base[i].type = GST_NIL;
vm->pc = f->def->byteCode;
}
}
/* Implementation of the opcode for tail calls */
static void gst_vm_tailcall(Gst *vm) {
GstThread *thread = vm->thread;
GstValue callee = vm->base[vm->pc[1]];
uint32_t arity = vm->pc[2];
uint16_t newFrameSize, currentFrameSize;
uint32_t i;
/* Check for closures */
gst_thread_split_env(vm);
if (callee.type == GST_CFUNCTION) {
newFrameSize = arity;
} else if (callee.type == GST_FUNCTION) {
GstFunction * f = callee.data.function;
newFrameSize = f->def->locals;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
/* Ensure stack has enough space for copies of arguments */
currentFrameSize = vm->frame->size;
gst_thread_ensure(vm, thread, thread->count + currentFrameSize + arity);
vm->base = thread->data + thread->count;
/* Copy the arguments into the extra space */
for (i = 0; i < arity; ++i)
vm->base[currentFrameSize + i] = vm->base[vm->pc[3 + i]];
/* Copy the end of the stack to the parameter position */
memcpy(vm->base, vm->base + currentFrameSize, arity * sizeof(GstValue));
/* nil the non argument part of the stack for gc */
for (i = arity; i < newFrameSize; ++i)
vm->base[i].type = GST_NIL;
/* Update the stack frame */
vm->frame->size = newFrameSize;
vm->frame->callee = callee;
vm->frame->env = NULL;
if (callee.type == GST_CFUNCTION) {
++vm->lock;
gst_vm_return(vm, callee.data.cfunction(vm));
--vm->lock;
} else {
GstFunction *f = callee.data.function;
vm->pc = f->def->byteCode;
}
}
/* Instantiate a closure */
static GstValue gst_vm_closure(Gst *vm, uint16_t literal) {
GstThread *thread = vm->thread;
if (vm->frame->callee.type != GST_FUNCTION) {
gst_error(vm, GST_EXPECTED_FUNCTION);
} else {
GstValue constant, ret;
GstFunction *fn, *current;
GstFuncEnv *env = vm->frame->env;
if (!env) {
env = gst_alloc(vm, sizeof(GstFuncEnv));
env->thread = thread;
env->stackOffset = thread->count;
env->values = NULL;
vm->frame->env = env;
}
current = vm->frame->callee.data.function;
constant = gst_vm_literal(vm, current, literal);
if (constant.type != GST_NIL) {
gst_error(vm, "cannot create closure");
}
fn = gst_alloc(vm, sizeof(GstFunction));
fn->def = (GstFuncDef *) constant.data.pointer;
fn->parent = current;
fn->env = env;
ret.type = GST_FUNCTION;
ret.data.function = fn;
return ret;
}
}
/* Start running the VM */ /* Start running the VM */
int gst_start(Gst *vm) { int gst_start(Gst *vm) {
/* VM state */
GstThread thread = *vm->thread;
GstValue *stack;
GstStackFrame frame;
GstValue temp, v1, v2;
uint16_t *pc;
/* Check for proper initialization */
if (thread.count == 0) {
gst_error(vm, "need thread in vm state");
}
stack = thread.data + thread.count;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
pc = frame.pc;
/* Set jmp_buf to jump back to for return. */ /* Set jmp_buf to jump back to for return. */
{ {
@ -166,43 +42,51 @@ int gst_start(Gst *vm) {
if ((n = setjmp(vm->jump))) { if ((n = setjmp(vm->jump))) {
/* Good return */ /* Good return */
if (n == 1) { if (n == 1) {
vm->lock = 0;
return 0; return 0;
} else if (n == 2) { } else if (n == 2) {
/* Error. */ /* Error. */
while (vm->thread->count && !vm->frame->errorJump) { while (!frame.errorJump) {
gst_thread_pop(vm); /* 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);
memcpy(frame.env->values,
thread.data + thread.count,
frame.size * sizeof(GstValue));
} }
if (vm->thread->count == 0) stack -= frame.prevSize + GST_FRAME_SIZE;
if (stack <= thread.data)
break;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
}
if (thread.count < GST_FRAME_SIZE)
return n; return n;
/* Jump to the error location */ /* Jump to the error location */
vm->pc = vm->frame->errorJump; pc = frame.errorJump;
/* Set error */ /* Set error */
vm->base[vm->frame->errorSlot] = vm->error; stack[frame.errorSlot] = vm->error;
vm->lock = 0;
} else { } else {
/* Crash. just return */ /* Crash. just return */
vm->lock = 0;
return n; return n;
} }
} }
} }
/* Main interpreter loop */
for (;;) { for (;;) {
GstValue temp, v1, v2;
uint16_t opcode = *vm->pc;
switch (opcode) { switch (*pc) {
#define DO_BINARY_MATH(op) \ #define DO_BINARY_MATH(op) \
v1 = vm->base[vm->pc[2]]; \ v1 = stack[pc[2]]; \
v2 = vm->base[vm->pc[3]]; \ v2 = stack[pc[3]]; \
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP); \ gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP); \
gst_assert(vm, v2.type == GST_NUMBER, GST_EXPECTED_NUMBER_ROP); \ gst_assert(vm, v2.type == GST_NUMBER, GST_EXPECTED_NUMBER_ROP); \
temp.type = GST_NUMBER; \ temp.type = GST_NUMBER; \
temp.data.number = v1.data.number op v2.data.number; \ temp.data.number = v1.data.number op v2.data.number; \
vm->base[vm->pc[1]] = temp; \ stack[pc[1]] = temp; \
vm->pc += 4; \ pc += 4; \
break; break;
case GST_OP_ADD: /* Addition */ case GST_OP_ADD: /* Addition */
@ -221,276 +105,441 @@ int gst_start(Gst *vm) {
case GST_OP_NOT: /* Boolean unary (Boolean not) */ case GST_OP_NOT: /* Boolean unary (Boolean not) */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = !gst_truthy(vm->base[vm->pc[2]]); temp.data.boolean = !gst_truthy(stack[pc[2]]);
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 3; pc += 3;
break; break;
case GST_OP_LD0: /* Load 0 */ case GST_OP_LD0: /* Load 0 */
temp.type = GST_NUMBER; temp.type = GST_NUMBER;
temp.data.number = 0; temp.data.number = 0;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 2; pc += 2;
break; break;
case GST_OP_LD1: /* Load 1 */ case GST_OP_LD1: /* Load 1 */
temp.type = GST_NUMBER; temp.type = GST_NUMBER;
temp.data.number = 1; temp.data.number = 1;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 2; pc += 2;
break; break;
case GST_OP_FLS: /* Load False */ case GST_OP_FLS: /* Load False */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = 0; temp.data.boolean = 0;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 2; pc += 2;
break; break;
case GST_OP_TRU: /* Load True */ case GST_OP_TRU: /* Load True */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = 1; temp.data.boolean = 1;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 2; pc += 2;
break; break;
case GST_OP_NIL: /* Load Nil */ case GST_OP_NIL: /* Load Nil */
temp.type = GST_NIL; temp.type = GST_NIL;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 2; pc += 2;
break; break;
case GST_OP_I16: /* Load Small Integer */ case GST_OP_I16: /* Load Small Integer */
temp.type = GST_NUMBER; temp.type = GST_NUMBER;
temp.data.number = ((int16_t *)(vm->pc))[2]; temp.data.number = ((int16_t *)(pc))[2];
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 3; pc += 3;
break; break;
case GST_OP_UPV: /* Load Up Value */ case GST_OP_UPV: /* Load Up Value */
temp = vm->frame->callee; case GST_OP_SUV: /* Set Up Value */
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION); gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
vm->base[vm->pc[1]] = *gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]); {
vm->pc += 4; GstValue *upv;
GstFunction *fn = frame.callee.data.function;
GstFuncEnv *env;
uint16_t level = pc[2];
if (level == 0)
upv = stack + pc[3];
else {
while (fn && --level)
fn = fn->parent;
gst_assert(vm, fn, GST_NO_UPVALUE);
env = fn->env;
if (env->thread)
upv = env->thread->data + env->stackOffset + pc[3];
else
upv = env->values + pc[3];
}
if (pc[0] == GST_OP_UPV) {
stack[pc[1]] = *upv;
} else {
*upv = stack[pc[1]];
}
pc += 4;
}
break; break;
case GST_OP_JIF: /* Jump If */ case GST_OP_JIF: /* Jump If */
if (gst_truthy(vm->base[vm->pc[1]])) { if (gst_truthy(stack[pc[1]])) {
vm->pc += 4; pc += 4;
} else { } else {
vm->pc += *((int32_t *)(vm->pc + 2)); pc += *((int32_t *)(pc + 2));
} }
break; break;
case GST_OP_JMP: /* Jump */ case GST_OP_JMP: /* Jump */
vm->pc += *((int32_t *)(vm->pc + 1)); pc += *((int32_t *)(pc + 1));
break; break;
case GST_OP_CAL: /* Call */ case GST_OP_CAL: /* Call */
gst_vm_call(vm); {
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;
} else if (temp.type == GST_CFUNCTION) {
locals = arity;
} 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);
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 < arity; ++i)
stack[i] = oldBase[pc[4 + i]];
/* Call the function */
if (temp.type == GST_CFUNCTION) {
/* Save current state to vm thread */
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
*vm->thread = thread;
v2 = temp.data.cfunction(vm);
goto ret;
} else {
for (; i < locals; ++i)
stack[i].type = GST_NIL;
pc = temp.data.function->def->byteCode;
}
}
break; break;
case GST_OP_RET: /* Return */ case GST_OP_RET: /* Return */
gst_vm_return(vm, vm->base[vm->pc[1]]); v2 = stack[pc[1]];
break; goto ret;
case GST_OP_SUV: /* Set Up Value */
temp = vm->frame->callee;
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
*gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
vm->pc += 4;
break;
case GST_OP_CST: /* Load constant value */ case GST_OP_CST: /* Load constant value */
temp = vm->frame->callee; gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION); stack[pc[1]] = gst_vm_literal(vm, frame.callee.data.function, pc[2]);
vm->base[vm->pc[1]] = gst_vm_literal(vm, temp.data.function, vm->pc[2]); pc += 3;
vm->pc += 3;
break; break;
case GST_OP_I32: /* Load 32 bit integer */ case GST_OP_I32: /* Load 32 bit integer */
temp.type = GST_NUMBER; temp.type = GST_NUMBER;
temp.data.number = *((int32_t *)(vm->pc + 2)); temp.data.number = *((int32_t *)(pc + 2));
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 4; pc += 4;
break; break;
case GST_OP_F64: /* Load 64 bit float */ case GST_OP_F64: /* Load 64 bit float */
temp.type = GST_NUMBER; temp.type = GST_NUMBER;
temp.data.number = (GstNumber) *((double *)(vm->pc + 2)); temp.data.number = (GstNumber) *((double *)(pc + 2));
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 6; pc += 6;
break; break;
case GST_OP_MOV: /* Move Values */ case GST_OP_MOV: /* Move Values */
vm->base[vm->pc[1]] = vm->base[vm->pc[2]]; stack[pc[1]] = stack[pc[2]];
vm->pc += 3; pc += 3;
break; break;
case GST_OP_CLN: /* Create closure from constant FuncDef */ case GST_OP_CLN: /* Create closure from constant FuncDef */
vm->base[vm->pc[1]] = gst_vm_closure(vm, vm->pc[2]); {
vm->pc += 3; GstFunction *fn, *current;
if (frame.callee.type != GST_FUNCTION)
gst_error(vm, GST_EXPECTED_FUNCTION);
if (!frame.env) {
frame.env = gst_alloc(vm, sizeof(GstFuncEnv));
*vm->thread = thread;
frame.env->thread = vm->thread;
frame.env->stackOffset = thread.count;
frame.env->values = NULL;
}
current = frame.callee.data.function;
temp = gst_vm_literal(vm, current, 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 = current;
fn->env = frame.env;
temp.type = GST_FUNCTION;
temp.data.function = fn;
stack[pc[1]] = temp;
pc += 3;
}
break; break;
case GST_OP_EQL: /* Equality */ case GST_OP_EQL: /* Equality */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = gst_equals(vm->base[vm->pc[2]], vm->base[vm->pc[3]]); temp.data.boolean = gst_equals(stack[pc[2]], stack[pc[3]]);
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 4; pc += 4;
break; break;
case GST_OP_LTN: /* Less Than */ case GST_OP_LTN: /* Less Than */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) == -1); temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) == -1);
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 4; pc += 4;
break; break;
case GST_OP_LTE: /* Less Than or Equal to */ case GST_OP_LTE: /* Less Than or Equal to */
temp.type = GST_BOOLEAN; temp.type = GST_BOOLEAN;
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) != 1); temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) != 1);
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 4; pc += 4;
break; break;
case GST_OP_ARR: /* Array literal */ case GST_OP_ARR: /* Array literal */
{ {
uint32_t i; uint32_t i;
uint32_t arrayLen = vm->pc[2]; uint32_t arrayLen = pc[2];
GstArray *array = gst_array(vm, arrayLen); GstArray *array = gst_array(vm, arrayLen);
array->count = arrayLen; array->count = arrayLen;
for (i = 0; i < arrayLen; ++i) for (i = 0; i < arrayLen; ++i)
array->data[i] = vm->base[vm->pc[3 + i]]; array->data[i] = stack[pc[3 + i]];
temp.type = GST_ARRAY; temp.type = GST_ARRAY;
temp.data.array = array; temp.data.array = array;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 3 + arrayLen; pc += 3 + arrayLen;
} }
break; break;
case GST_OP_DIC: /* Object literal */ case GST_OP_DIC: /* Object literal */
{ {
uint32_t i = 3; uint32_t i = 3;
uint32_t kvs = vm->pc[2]; uint32_t kvs = pc[2];
GstObject *o = gst_object(vm, kvs + 2); GstObject *o = gst_object(vm, kvs + 2);
kvs = kvs + 3; kvs = kvs + 3;
while (i < kvs) { while (i < kvs) {
v1 = vm->base[vm->pc[i++]]; v1 = stack[pc[i++]];
v2 = vm->base[vm->pc[i++]]; v2 = stack[pc[i++]];
gst_object_put(vm, o, v1, v2); gst_object_put(vm, o, v1, v2);
} }
temp.type = GST_OBJECT; temp.type = GST_OBJECT;
temp.data.object = o; temp.data.object = o;
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += kvs; pc += kvs;
} }
break; break;
case GST_OP_TCL: /* Tail call */ case GST_OP_TCL: /* Tail call */
gst_vm_tailcall(vm); {
break; temp = stack[pc[1]];
uint32_t arity = pc[2];
uint16_t locals;
uint32_t i, workspace, totalCapacity;
/* Macro for generating some math operators */ /* Check for closures */
#define DO_MULTI_MATH(op, start) { \ if (frame.env) {
uint16_t count = vm->pc[2]; \ frame.env->thread = NULL;
uint16_t i; \ frame.env->stackOffset = frame.size;
GstNumber accum = start; \ frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
for (i = 0; i < count; ++i) { \ memcpy(frame.env->values,
v1 = vm->base[vm->pc[3 + i]]; \ thread.data + thread.count,
gst_assert(vm, v1.type == GST_NUMBER, "expected number"); \ frame.size * sizeof(GstValue));
accum = accum op v1.data.number; \ frame.env = NULL;
} \
temp.type = GST_NUMBER; \
temp.data.number = accum; \
vm->base[vm->pc[1]] = temp; \
vm->pc += 3 + count; \
break; \
} }
/* Vectorized math */ /* Get size of new stack frame */
case GST_OP_ADM: if (temp.type == GST_CFUNCTION) {
DO_MULTI_MATH(+, 0) locals = arity;
} else if (temp.type == GST_FUNCTION) {
locals = temp.data.function->def->locals;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
case GST_OP_SBM: /* Get enough space for manipulating args */
DO_MULTI_MATH(-, 0) if (arity > frame.size) {
workspace = arity;
} else {
workspace = frame.size;
}
case GST_OP_MUM: /* Ensure stack has enough space for copies of arguments */
DO_MULTI_MATH(*, 1) totalCapacity = thread.count + arity + workspace + locals;
if (totalCapacity > thread.capacity) {
uint32_t newCap = totalCapacity * 2;
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
memcpy(newData, thread.data, thread.capacity * sizeof(GstValue));
thread.data = newData;
thread.capacity = newCap;
stack = thread.data + thread.count;
}
case GST_OP_DVM: /* Copy the arguments into the extra space */
DO_MULTI_MATH(/, 1) for (i = 0; i < arity; ++i)
stack[workspace + i] = stack[pc[3 + i]];
#undef DO_MULTI_MATH /* Copy the end of the stack to the parameter position */
memcpy(stack, stack + workspace, arity * sizeof(GstValue));
case GST_OP_RTN: /* Return nil */ /* Update the stack frame */
temp.type = GST_NIL; frame.callee = temp;
gst_vm_return(vm, temp); frame.size = locals;
/* Nil the non argument part of the stack for gc */
for (i = arity; i < frame.size; ++i)
stack[i].type = GST_NIL;
/* Call the function */
if (temp.type == GST_CFUNCTION) {
/* Save current state to vm thread */
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
*vm->thread = thread;
v2 = temp.data.cfunction(vm);
goto ret;
} else {
pc = temp.data.function->def->byteCode;
}
}
break; break;
case GST_OP_RTN: /* Return nil */
v2.type = GST_NIL;
goto ret;
case GST_OP_GET: case GST_OP_GET:
temp = gst_get(vm, vm->base[vm->pc[2]], vm->base[vm->pc[3]]); temp = gst_get(vm, stack[pc[2]], stack[pc[3]]);
vm->base[vm->pc[1]] = temp; stack[pc[1]] = temp;
vm->pc += 4; pc += 4;
break; break;
case GST_OP_SET: case GST_OP_SET:
gst_set(vm, vm->base[vm->pc[1]], vm->base[vm->pc[2]], vm->base[vm->pc[3]]); gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
vm->pc += 4; pc += 4;
break; break;
case GST_OP_ERR: case GST_OP_ERR:
vm->error = vm->base[vm->pc[1]]; vm->error = stack[pc[1]];
longjmp(vm->jump, 2); longjmp(vm->jump, 2);
break; break;
case GST_OP_TRY: case GST_OP_TRY:
vm->frame->errorSlot = vm->pc[1]; frame.errorSlot = pc[1];
vm->frame->errorJump = vm->pc + *(uint32_t *)(vm->pc + 2); frame.errorJump = pc + *(uint32_t *)(pc + 2);
vm->pc += 4; pc += 4;
break; break;
case GST_OP_UTY: case GST_OP_UTY:
vm->frame->errorJump = NULL; frame.errorJump = NULL;
vm->pc++; pc++;
break; break;
default: default:
gst_error(vm, "unknown opcode"); gst_error(vm, "unknown opcode");
break; break;
}
/* Move collection only to places that allocate memory */ /* 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);
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;
}
}
/* 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;
gst_maybe_collect(vm); gst_maybe_collect(vm);
} }
} }
/* 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) {
uint16_t frameSize = vm->frame->size; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
uint16_t frameSize = frame->size;
gst_assert(vm, frameSize > index, "cannot get arg out of stack bounds"); gst_assert(vm, frameSize > index, "cannot get arg out of stack bounds");
return vm->base[index]; return stack[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) {
uint16_t frameSize = vm->frame->size; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
uint16_t frameSize = frame->size;
gst_assert(vm, frameSize > index, "cannot set arg out of stack bounds"); gst_assert(vm, frameSize > index, "cannot set arg out of stack bounds");
vm->base[index] = x; stack[index] = 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) {
return vm->frame->size; GstValue *stack = vm->thread->data + vm->thread->count;
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
return frame->size;
} }
/* Initialize the VM */ /* Initialize the VM */
void gst_init(Gst *vm) { void gst_init(Gst *vm) {
vm->ret.type = GST_NIL; vm->ret.type = GST_NIL;
vm->error.type = GST_NIL; vm->error.type = GST_NIL;
vm->base = NULL;
vm->frame = NULL;
vm->pc = NULL;
vm->crash = NULL; vm->crash = NULL;
/* Garbage collection */ /* Garbage collection */
vm->blocks = NULL; vm->blocks = NULL;
@ -501,7 +550,6 @@ void gst_init(Gst *vm) {
* there are no memory bugs during dev */ * there are no memory bugs during dev */
vm->memoryInterval = 2000; vm->memoryInterval = 2000;
vm->black = 0; vm->black = 0;
vm->lock = 0;
/* Add thread */ /* Add thread */
vm->thread = NULL; vm->thread = NULL;
} }
@ -509,22 +557,35 @@ void gst_init(Gst *vm) {
/* Load a function into the VM. The function will be called with /* Load a function into the VM. The function will be called with
* no arguments when run */ * no arguments when run */
void gst_load(Gst *vm, GstValue callee) { void gst_load(Gst *vm, GstValue callee) {
uint32_t startCapacity = 100; uint32_t startCapacity;
uint32_t locals, i;
uint16_t *pc;
GstStackFrame *frame;
GstThread *thread = gst_alloc(vm, sizeof(GstThread)); GstThread *thread = gst_alloc(vm, sizeof(GstThread));
thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity);
thread->capacity = startCapacity;
thread->count = 0;
vm->thread = thread;
if (callee.type == GST_FUNCTION) { if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function; locals = callee.data.function->def->locals;
gst_thread_push(vm, thread, callee, fn->def->locals); pc = callee.data.function->def->byteCode;
vm->pc = fn->def->byteCode;
} else if (callee.type == GST_CFUNCTION) { } else if (callee.type == GST_CFUNCTION) {
gst_thread_push(vm, thread, callee, 0); locals = 0;
vm->pc = NULL; pc = NULL;
} else { } else {
return; 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;
frame = (GstStackFrame *)thread->data;
frame->prevSize = 0;
frame->size = locals;
frame->callee = callee;
frame->errorJump = NULL;
frame->env = NULL;
frame->pc = pc;
/* Nil arguments */
for (i = 0; i < locals; ++i)
thread->data[GST_FRAME_SIZE + i].type = GST_NIL;
} }
/* Clear all memory associated with the VM */ /* Clear all memory associated with the VM */