From 33d09f98b106d6da2403ee1198f02f5bbc166544 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 26 Feb 2017 11:47:50 -0500 Subject: [PATCH] Work on speeding up interpreter by moving state inside interpreter loop. --- Makefile | 6 +- compile.c | 42 ++-- datatypes.h | 11 +- disasm.c | 336 +++++++++++++-------------- gc.c | 7 +- thread.c | 78 ------- thread.h | 25 --- vm.c | 637 ++++++++++++++++++++++++++++------------------------ 8 files changed, 538 insertions(+), 604 deletions(-) delete mode 100644 thread.c delete mode 100644 thread.h diff --git a/Makefile b/Makefile index b607f724..78b672a7 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/compile.c b/compile.c index 723d5755..e4446a2c 100644 --- a/compile.c +++ b/compile.c @@ -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]); } diff --git a/datatypes.h b/datatypes.h index 914d7df2..d01caf53 100644 --- a/datatypes.h +++ b/datatypes.h @@ -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 */ diff --git a/disasm.c b/disasm.c index 898d6b38..d66bed2b 100644 --- a/disasm.c +++ b/disasm.c @@ -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"); + } } diff --git a/gc.c b/gc.c index 64ba6aba..2546861a 100644 --- a/gc.c +++ b/gc.c @@ -1,7 +1,6 @@ #include "datatypes.h" #include "gc.h" #include "vm.h" -#include "thread.h" #include /* 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; diff --git a/thread.c b/thread.c deleted file mode 100644 index 3311d798..00000000 --- a/thread.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "thread.h" -#include "vm.h" -#include - -/* 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); -} \ No newline at end of file diff --git a/thread.h b/thread.h deleted file mode 100644 index 86736e21..00000000 --- a/thread.h +++ /dev/null @@ -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 diff --git a/vm.c b/vm.c index 8b5bee12..7588ebc2 100644 --- a/vm.c +++ b/vm.c @@ -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 */