1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-24 23:40: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
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 disasm.h datatypes.h gc.h thread.h
SOURCES=main.c parse.c value.c vm.c ds.c compile.c disasm.c gc.c thread.c
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
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET)

View File

@ -117,7 +117,7 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
}
if (sameFunction) {
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->literals = c->tail->literals;
@ -135,7 +135,7 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
static void compiler_pop_scope(GstCompiler *c) {
GstScope *last = c->tail;
if (last == NULL) {
c_error(c, "No scope to pop.");
c_error(c, "no scope to pop");
} else {
if (last->nextLocal > last->frameSize) {
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) {
if (scope->heapSize == 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++;
} else {
@ -266,7 +266,7 @@ static void compiler_tracker_write(GstCompiler *c, SlotTracker *tracker, int rev
else
s = tracker->slots[i];
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);
}
}
@ -320,7 +320,7 @@ static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValu
GstValue x;
uint16_t target;
if (sym.type != GST_STRING)
c_error(c, "Expected symbol");
c_error(c, "expected symbol");
target = compiler_get_local(c, scope);
x.type = GST_NUMBER;
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);
}
} else {
c_error(c, "Expected boolean, nil, or number type.");
c_error(c, "expected boolean, nil, or number type");
}
return ret;
}
@ -420,7 +420,7 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
Slot ret;
if (opts.resultUnused) return nil_slot();
if (!symbol_resolve(scope, sym, &level, &index))
c_error(c, "Undefined symbol");
c_error(c, "undefined symbol");
if (level > 0) {
/* We have an upvalue */
ret = compiler_get_target(c, opts);
@ -499,7 +499,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
/* Write the correct opcode */
if (form->count < 2) {
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;
} else {
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) {
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;
} else {
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) {
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;
} else {
gst_buffer_push_u16(c->vm, buffer, op2);
@ -523,7 +523,7 @@ static Slot compile_operator(GstCompiler *c, FormOptions opts, GstArray *form,
}
} else {
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, ret.index);
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 */
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) {
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) {
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) {
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) {
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) {
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();
} else {
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;
FormOptions subOpts = form_options_default();
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) {
ds = compiler_realize_slot(c, compile_value(c, subOpts, form->data[1]));
} else {
@ -680,7 +680,7 @@ static GstFuncDef *compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, uin
GstFuncDef *def = gst_alloc(c->vm, sizeof(GstFuncDef));
/* Create enough space for the new byteCode */
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);
def->byteCode = (uint16_t *)byteCode;
def->byteCodeLen = lastNBytes / 2;
@ -725,12 +725,12 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstArray *form) {
++current;
/* Define the function parameters */
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;
for (i = 0; i < params->count; ++i) {
GstValue param = params->data[i];
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
* in the right place by default - at the beginning
* of the stack frame. */
@ -982,7 +982,7 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstArray *form) {
/* Assignment special */
static Slot compile_var(GstCompiler *c, FormOptions opts, GstArray *form) {
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]);
}

View File

@ -77,6 +77,9 @@ struct GstThread {
} status;
};
/* Size of stack frame */
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) + 1) / sizeof(GstValue))
/* A dynamic array type */
struct GstArray {
uint32_t count;
@ -153,12 +156,8 @@ struct Gst {
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
uint32_t lock : 31;
/* Thread */
uint16_t *pc;
GstThread *thread;
GstValue *base;
GstStackFrame *frame;
/* Return state */
const char *crash;
jmp_buf jump;
@ -226,10 +225,6 @@ enum GstOpCode {
GST_OP_ARR, /* 0x0019 */
GST_OP_DIC, /* 0x001a */
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_SET, /* 0x0021 */
GST_OP_GET, /* 0x0022 */

336
disasm.c
View File

@ -10,46 +10,45 @@ static void dasm_print_i32(FILE * out, int32_t number) { fprintf(out, "#%d ", nu
static void dasm_print_f64(FILE * out, double number) { fprintf(out, "#%f ", number); }
static void dasm_print_literal(FILE * out, uint16_t index) { fprintf(out, "(%d) ", index); }
static void dasm_print_upvalue(FILE * out, uint16_t level, uint16_t index) {
fprintf(out, "<%d, %d> ", level, index);
fprintf(out, "<%d, %d> ", level, index);
}
/* Print the name of the argument but pad it */
static void dasm_print_arg(FILE * out, const char * name) {
uint32_t i = 0;
char c;
while ((c = *name++)) {
fputc(c, out);
++i;
}
for (; i < OP_WIDTH; ++i)
fputc(' ', out);
uint32_t i = 0;
char c;
while ((c = *name++)) {
putc(c, out);
++i;
}
for (; i < OP_WIDTH; ++i)
fputc(' ', out);
}
/* Print instructions that take a fixed number of arguments */
static uint32_t dasm_fixed_op(FILE * out, const uint16_t * current,
const char * name, uint32_t size) {
uint32_t i;
dasm_print_arg(out, name);
for (i = 1; i <= size; ++i) {
dasm_print_slot(out, current[i]);
}
return size + 1;
const char * name, uint32_t size) {
uint32_t i;
dasm_print_arg(out, name);
for (i = 1; i <= size; ++i)
dasm_print_slot(out, current[i]);
return size + 1;
}
/* Print instructions that take a variable number of arguments */
static uint32_t dasm_varg_op(FILE * out, const uint16_t * current,
const char * name, uint32_t extra) {
uint32_t i, argCount;
dasm_print_arg(out, name);
for (i = 0; i < extra; ++i) {
dasm_print_slot(out, current[i + 1]);
}
argCount = current[extra + 1];
const char * name, uint32_t extra) {
uint32_t i, argCount;
dasm_print_arg(out, name);
for (i = 0; i < extra; ++i) {
dasm_print_slot(out, current[i + 1]);
}
argCount = current[extra + 1];
fprintf(out, ": "); /* Argument separator */
for (i = 0; i < argCount; ++i) {
dasm_print_slot(out, current[i + extra + 2]);
}
return argCount + extra + 2;
for (i = 0; i < argCount; ++i) {
dasm_print_slot(out, current[i + extra + 2]);
}
return argCount + extra + 2;
}
/* Print the disassembly for a function definition */
@ -67,155 +66,140 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
uint16_t *current = byteCode;
uint16_t *end = byteCode + len;
fprintf(out, "----- ASM BYTECODE START -----\n");
while (current < end) {
switch (*current) {
case GST_OP_ADD:
current += dasm_fixed_op(out, current, "add", 3);
break;
case GST_OP_SUB:
current += dasm_fixed_op(out, current, "sub", 3);
break;
case GST_OP_MUL:
current += dasm_fixed_op(out, current, "mul", 3);
break;
case GST_OP_DIV:
current += dasm_fixed_op(out, current, "div", 3);
break;
case GST_OP_NOT:
current += dasm_fixed_op(out, current, "not", 2);
break;
case GST_OP_LD0:
current += dasm_fixed_op(out, current, "load0", 1);
break;
case GST_OP_LD1:
current += dasm_fixed_op(out, current, "load1", 1);
break;
case GST_OP_FLS:
current += dasm_fixed_op(out, current, "loadFalse", 1);
break;
case GST_OP_TRU:
current += dasm_fixed_op(out, current, "loadTrue", 1);
break;
case GST_OP_NIL:
current += dasm_fixed_op(out, current, "loadNil", 1);
break;
case GST_OP_I16:
dasm_print_arg(out, "loadInt16");
dasm_print_slot(out, current[1]);
dasm_print_i16(out, ((int16_t *)current)[2]);
current += 3;
break;
case GST_OP_UPV:
dasm_print_arg(out, "loadUpValue");
dasm_print_slot(out, current[1]);
dasm_print_upvalue(out, current[2], current[3]);
current += 4;
break;
case GST_OP_JIF:
dasm_print_arg(out, "jumpIf");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
current += 4;
break;
case GST_OP_JMP:
dasm_print_arg(out, "jump");
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]);
dasm_print_upvalue(out, current[2], current[3]);
current += 4;
break;
case GST_OP_CST:
dasm_print_arg(out, "loadLiteral");
dasm_print_slot(out, current[1]);
dasm_print_literal(out, current[2]);
current += 3;
break;
case GST_OP_I32:
dasm_print_arg(out, "loadInt32");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
current += 4;
break;
case GST_OP_F64:
dasm_print_arg(out, "loadFloat64");
dasm_print_slot(out, current[1]);
dasm_print_f64(out, ((double *)(current + 2))[0]);
current += 6;
break;
case GST_OP_MOV:
current += dasm_fixed_op(out, current, "move", 2);
break;
case GST_OP_CLN:
dasm_print_arg(out, "makeClosure");
dasm_print_slot(out, current[1]);
dasm_print_literal(out, current[2]);
current += 3;
break;
case GST_OP_EQL:
current += dasm_fixed_op(out, current, "equals", 3);
break;
case GST_OP_LTN:
current += dasm_fixed_op(out, current, "lessThan", 3);
break;
case GST_OP_LTE:
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
break;
case GST_OP_ARR:
current += dasm_varg_op(out, current, "array", 1);
break;
case GST_OP_DIC:
current += dasm_varg_op(out, current, "object", 1);
break;
case GST_OP_TCL:
current += dasm_varg_op(out, current, "tailCall", 1);
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:
current += dasm_fixed_op(out, current, "returnNil", 0);
break;
case GST_OP_GET:
current += dasm_fixed_op(out, current, "get", 3);
break;
case GST_OP_SET:
current += dasm_fixed_op(out, current, "set", 3);
break;
case GST_OP_ERR:
current += dasm_fixed_op(out, current, "error", 1);
break;
case GST_OP_TRY:
dasm_print_arg(out, "try");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, *(int32_t *)(current + 2));
current += 4;
break;
case GST_OP_UTY:
current += dasm_fixed_op(out, current, "untry", 0);
break;
}
fprintf(out, "\n");
}
fprintf(out, "----- END ASM BYTECODE -----\n");
case GST_OP_ADD:
current += dasm_fixed_op(out, current, "add", 3);
break;
case GST_OP_SUB:
current += dasm_fixed_op(out, current, "sub", 3);
break;
case GST_OP_MUL:
current += dasm_fixed_op(out, current, "mul", 3);
break;
case GST_OP_DIV:
current += dasm_fixed_op(out, current, "div", 3);
break;
case GST_OP_NOT:
current += dasm_fixed_op(out, current, "not", 2);
break;
case GST_OP_LD0:
current += dasm_fixed_op(out, current, "load0", 1);
break;
case GST_OP_LD1:
current += dasm_fixed_op(out, current, "load1", 1);
break;
case GST_OP_FLS:
current += dasm_fixed_op(out, current, "loadFalse", 1);
break;
case GST_OP_TRU:
current += dasm_fixed_op(out, current, "loadTrue", 1);
break;
case GST_OP_NIL:
current += dasm_fixed_op(out, current, "loadNil", 1);
break;
case GST_OP_I16:
dasm_print_arg(out, "loadInt16");
dasm_print_slot(out, current[1]);
dasm_print_i16(out, ((int16_t *)current)[2]);
current += 3;
break;
case GST_OP_UPV:
dasm_print_arg(out, "loadUpValue");
dasm_print_slot(out, current[1]);
dasm_print_upvalue(out, current[2], current[3]);
current += 4;
break;
case GST_OP_JIF:
dasm_print_arg(out, "jumpIf");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
current += 4;
break;
case GST_OP_JMP:
dasm_print_arg(out, "jump");
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]);
dasm_print_upvalue(out, current[2], current[3]);
current += 4;
break;
case GST_OP_CST:
dasm_print_arg(out, "loadLiteral");
dasm_print_slot(out, current[1]);
dasm_print_literal(out, current[2]);
current += 3;
break;
case GST_OP_I32:
dasm_print_arg(out, "loadInt32");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
current += 4;
break;
case GST_OP_F64:
dasm_print_arg(out, "loadFloat64");
dasm_print_slot(out, current[1]);
dasm_print_f64(out, ((double *)(current + 2))[0]);
current += 6;
break;
case GST_OP_MOV:
current += dasm_fixed_op(out, current, "move", 2);
break;
case GST_OP_CLN:
dasm_print_arg(out, "makeClosure");
dasm_print_slot(out, current[1]);
dasm_print_literal(out, current[2]);
current += 3;
break;
case GST_OP_EQL:
current += dasm_fixed_op(out, current, "equals", 3);
break;
case GST_OP_LTN:
current += dasm_fixed_op(out, current, "lessThan", 3);
break;
case GST_OP_LTE:
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
break;
case GST_OP_ARR:
current += dasm_varg_op(out, current, "array", 1);
break;
case GST_OP_DIC:
current += dasm_varg_op(out, current, "object", 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;
case GST_OP_SET:
current += dasm_fixed_op(out, current, "set", 3);
break;
case GST_OP_ERR:
current += dasm_fixed_op(out, current, "error", 1);
break;
case GST_OP_TRY:
dasm_print_arg(out, "try");
dasm_print_slot(out, current[1]);
dasm_print_i32(out, *(int32_t *)(current + 2));
current += 4;
break;
case GST_OP_UTY:
current += dasm_fixed_op(out, current, "untry", 0);
break;
}
fprintf(out, "\n");
}
}

7
gc.c
View File

@ -1,7 +1,6 @@
#include "datatypes.h"
#include "gc.h"
#include "vm.h"
#include "thread.h"
#include <stdlib.h>
/* 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) {
GstThread *thread = x->data.thread;
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->data)->color = vm->black;
while (frame <= end)
@ -143,9 +143,7 @@ void gst_mark(Gst *vm, GstValue *x) {
}
}
break;
}
}
/* 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 */
void gst_collect(Gst *vm) {
if (vm->lock > 0) return;
/* Thread can be null */
if (vm->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

637
vm.c
View File

@ -5,30 +5,12 @@
#include "value.h"
#include "ds.h"
#include "gc.h"
#include "thread.h"
static const char GST_NO_UPVALUE[] = "no upvalue";
static const char GST_EXPECTED_FUNCTION[] = "expected function";
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
/* 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 */
static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
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 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 */
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. */
{
@ -166,43 +42,51 @@ int gst_start(Gst *vm) {
if ((n = setjmp(vm->jump))) {
/* Good return */
if (n == 1) {
vm->lock = 0;
return 0;
} else if (n == 2) {
/* Error. */
while (vm->thread->count && !vm->frame->errorJump) {
gst_thread_pop(vm);
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);
memcpy(frame.env->values,
thread.data + thread.count,
frame.size * sizeof(GstValue));
}
stack -= frame.prevSize + GST_FRAME_SIZE;
if (stack <= thread.data)
break;
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
}
if (vm->thread->count == 0)
if (thread.count < GST_FRAME_SIZE)
return n;
/* Jump to the error location */
vm->pc = vm->frame->errorJump;
pc = frame.errorJump;
/* Set error */
vm->base[vm->frame->errorSlot] = vm->error;
vm->lock = 0;
stack[frame.errorSlot] = vm->error;
} else {
/* Crash. just return */
vm->lock = 0;
return n;
}
}
}
/* Main interpreter loop */
for (;;) {
GstValue temp, v1, v2;
uint16_t opcode = *vm->pc;
switch (opcode) {
switch (*pc) {
#define DO_BINARY_MATH(op) \
v1 = vm->base[vm->pc[2]]; \
v2 = vm->base[vm->pc[3]]; \
v1 = stack[pc[2]]; \
v2 = stack[pc[3]]; \
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP); \
gst_assert(vm, v2.type == GST_NUMBER, GST_EXPECTED_NUMBER_ROP); \
temp.type = GST_NUMBER; \
temp.data.number = v1.data.number op v2.data.number; \
vm->base[vm->pc[1]] = temp; \
vm->pc += 4; \
stack[pc[1]] = temp; \
pc += 4; \
break;
case GST_OP_ADD: /* Addition */
@ -221,276 +105,441 @@ int gst_start(Gst *vm) {
case GST_OP_NOT: /* Boolean unary (Boolean not) */
temp.type = GST_BOOLEAN;
temp.data.boolean = !gst_truthy(vm->base[vm->pc[2]]);
vm->base[vm->pc[1]] = temp;
vm->pc += 3;
temp.data.boolean = !gst_truthy(stack[pc[2]]);
stack[pc[1]] = temp;
pc += 3;
break;
case GST_OP_LD0: /* Load 0 */
temp.type = GST_NUMBER;
temp.data.number = 0;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
stack[pc[1]] = temp;
pc += 2;
break;
case GST_OP_LD1: /* Load 1 */
temp.type = GST_NUMBER;
temp.data.number = 1;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
stack[pc[1]] = temp;
pc += 2;
break;
case GST_OP_FLS: /* Load False */
temp.type = GST_BOOLEAN;
temp.data.boolean = 0;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
stack[pc[1]] = temp;
pc += 2;
break;
case GST_OP_TRU: /* Load True */
temp.type = GST_BOOLEAN;
temp.data.boolean = 1;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
stack[pc[1]] = temp;
pc += 2;
break;
case GST_OP_NIL: /* Load Nil */
temp.type = GST_NIL;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
stack[pc[1]] = temp;
pc += 2;
break;
case GST_OP_I16: /* Load Small Integer */
temp.type = GST_NUMBER;
temp.data.number = ((int16_t *)(vm->pc))[2];
vm->base[vm->pc[1]] = temp;
vm->pc += 3;
temp.data.number = ((int16_t *)(pc))[2];
stack[pc[1]] = temp;
pc += 3;
break;
case GST_OP_UPV: /* Load Up Value */
temp = vm->frame->callee;
gst_assert(vm, temp.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;
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;
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;
case GST_OP_JIF: /* Jump If */
if (gst_truthy(vm->base[vm->pc[1]])) {
vm->pc += 4;
if (gst_truthy(stack[pc[1]])) {
pc += 4;
} else {
vm->pc += *((int32_t *)(vm->pc + 2));
pc += *((int32_t *)(pc + 2));
}
break;
case GST_OP_JMP: /* Jump */
vm->pc += *((int32_t *)(vm->pc + 1));
pc += *((int32_t *)(pc + 1));
break;
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;
case GST_OP_RET: /* Return */
gst_vm_return(vm, vm->base[vm->pc[1]]);
break;
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;
v2 = stack[pc[1]];
goto ret;
case GST_OP_CST: /* Load constant value */
temp = vm->frame->callee;
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
vm->base[vm->pc[1]] = gst_vm_literal(vm, temp.data.function, vm->pc[2]);
vm->pc += 3;
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
stack[pc[1]] = gst_vm_literal(vm, frame.callee.data.function, pc[2]);
pc += 3;
break;
case GST_OP_I32: /* Load 32 bit integer */
temp.type = GST_NUMBER;
temp.data.number = *((int32_t *)(vm->pc + 2));
vm->base[vm->pc[1]] = temp;
vm->pc += 4;
temp.data.number = *((int32_t *)(pc + 2));
stack[pc[1]] = temp;
pc += 4;
break;
case GST_OP_F64: /* Load 64 bit float */
temp.type = GST_NUMBER;
temp.data.number = (GstNumber) *((double *)(vm->pc + 2));
vm->base[vm->pc[1]] = temp;
vm->pc += 6;
temp.data.number = (GstNumber) *((double *)(pc + 2));
stack[pc[1]] = temp;
pc += 6;
break;
case GST_OP_MOV: /* Move Values */
vm->base[vm->pc[1]] = vm->base[vm->pc[2]];
vm->pc += 3;
stack[pc[1]] = stack[pc[2]];
pc += 3;
break;
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;
case GST_OP_EQL: /* Equality */
temp.type = GST_BOOLEAN;
temp.data.boolean = gst_equals(vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
vm->base[vm->pc[1]] = temp;
vm->pc += 4;
temp.data.boolean = gst_equals(stack[pc[2]], stack[pc[3]]);
stack[pc[1]] = temp;
pc += 4;
break;
case GST_OP_LTN: /* Less Than */
temp.type = GST_BOOLEAN;
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) == -1);
vm->base[vm->pc[1]] = temp;
vm->pc += 4;
temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) == -1);
stack[pc[1]] = temp;
pc += 4;
break;
case GST_OP_LTE: /* Less Than or Equal to */
temp.type = GST_BOOLEAN;
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) != 1);
vm->base[vm->pc[1]] = temp;
vm->pc += 4;
temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) != 1);
stack[pc[1]] = temp;
pc += 4;
break;
case GST_OP_ARR: /* Array literal */
{
uint32_t i;
uint32_t arrayLen = vm->pc[2];
uint32_t arrayLen = pc[2];
GstArray *array = gst_array(vm, arrayLen);
array->count = arrayLen;
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.data.array = array;
vm->base[vm->pc[1]] = temp;
vm->pc += 3 + arrayLen;
stack[pc[1]] = temp;
pc += 3 + arrayLen;
}
break;
case GST_OP_DIC: /* Object literal */
{
uint32_t i = 3;
uint32_t kvs = vm->pc[2];
uint32_t kvs = pc[2];
GstObject *o = gst_object(vm, kvs + 2);
kvs = kvs + 3;
while (i < kvs) {
v1 = vm->base[vm->pc[i++]];
v2 = vm->base[vm->pc[i++]];
v1 = stack[pc[i++]];
v2 = stack[pc[i++]];
gst_object_put(vm, o, v1, v2);
}
temp.type = GST_OBJECT;
temp.data.object = o;
vm->base[vm->pc[1]] = temp;
vm->pc += kvs;
stack[pc[1]] = temp;
pc += kvs;
}
break;
case GST_OP_TCL: /* Tail call */
gst_vm_tailcall(vm);
{
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);
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 = arity;
} else if (temp.type == GST_FUNCTION) {
locals = temp.data.function->def->locals;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
}
/* Get enough space for manipulating args */
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);
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 < arity; ++i)
stack[workspace + i] = stack[pc[3 + i]];
/* Copy the end of the stack to the parameter position */
memcpy(stack, stack + workspace, arity * sizeof(GstValue));
/* Update the stack frame */
frame.callee = 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;
/* Macro for generating some math operators */
#define DO_MULTI_MATH(op, start) { \
uint16_t count = vm->pc[2]; \
uint16_t i; \
GstNumber accum = start; \
for (i = 0; i < count; ++i) { \
v1 = vm->base[vm->pc[3 + i]]; \
gst_assert(vm, v1.type == GST_NUMBER, "expected number"); \
accum = accum op v1.data.number; \
} \
temp.type = GST_NUMBER; \
temp.data.number = accum; \
vm->base[vm->pc[1]] = temp; \
vm->pc += 3 + count; \
break; \
}
/* Vectorized math */
case GST_OP_ADM:
DO_MULTI_MATH(+, 0)
case GST_OP_SBM:
DO_MULTI_MATH(-, 0)
case GST_OP_MUM:
DO_MULTI_MATH(*, 1)
case GST_OP_DVM:
DO_MULTI_MATH(/, 1)
#undef DO_MULTI_MATH
case GST_OP_RTN: /* Return nil */
temp.type = GST_NIL;
gst_vm_return(vm, temp);
break;
v2.type = GST_NIL;
goto ret;
case GST_OP_GET:
temp = gst_get(vm, vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
vm->base[vm->pc[1]] = temp;
vm->pc += 4;
temp = gst_get(vm, stack[pc[2]], stack[pc[3]]);
stack[pc[1]] = temp;
pc += 4;
break;
case GST_OP_SET:
gst_set(vm, vm->base[vm->pc[1]], vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
vm->pc += 4;
gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
pc += 4;
break;
case GST_OP_ERR:
vm->error = vm->base[vm->pc[1]];
vm->error = stack[pc[1]];
longjmp(vm->jump, 2);
break;
case GST_OP_TRY:
vm->frame->errorSlot = vm->pc[1];
vm->frame->errorJump = vm->pc + *(uint32_t *)(vm->pc + 2);
vm->pc += 4;
frame.errorSlot = pc[1];
frame.errorJump = pc + *(uint32_t *)(pc + 2);
pc += 4;
break;
case GST_OP_UTY:
vm->frame->errorJump = NULL;
vm->pc++;
frame.errorJump = NULL;
pc++;
break;
default:
gst_error(vm, "unknown opcode");
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 */
*vm->thread = thread;
gst_maybe_collect(vm);
}
}
/* Get an argument from the stack */
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");
return vm->base[index];
return stack[index];
}
/* Put a value on the stack */
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");
vm->base[index] = x;
stack[index] = x;
}
/* Get the size of the VMStack */
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 */
void gst_init(Gst *vm) {
vm->ret.type = GST_NIL;
vm->error.type = GST_NIL;
vm->base = NULL;
vm->frame = NULL;
vm->pc = NULL;
vm->crash = NULL;
/* Garbage collection */
vm->blocks = NULL;
@ -501,7 +550,6 @@ void gst_init(Gst *vm) {
* there are no memory bugs during dev */
vm->memoryInterval = 2000;
vm->black = 0;
vm->lock = 0;
/* Add thread */
vm->thread = NULL;
}
@ -509,22 +557,35 @@ void gst_init(Gst *vm) {
/* Load a function into the VM. The function will be called with
* no arguments when run */
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));
thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity);
thread->capacity = startCapacity;
thread->count = 0;
vm->thread = thread;
if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function;
gst_thread_push(vm, thread, callee, fn->def->locals);
vm->pc = fn->def->byteCode;
locals = callee.data.function->def->locals;
pc = callee.data.function->def->byteCode;
} else if (callee.type == GST_CFUNCTION) {
gst_thread_push(vm, thread, callee, 0);
vm->pc = NULL;
locals = 0;
pc = NULL;
} else {
return;
}
startCapacity = locals + GST_FRAME_SIZE + 10;
thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity);
thread->capacity = startCapacity;
thread->count = GST_FRAME_SIZE;
vm->thread = thread;
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 */