mirror of
https://github.com/janet-lang/janet
synced 2024-12-25 07:50:27 +00:00
Work on speeding up interpreter by moving state inside
interpreter loop.
This commit is contained in:
parent
fd34837265
commit
33d09f98b1
6
Makefile
6
Makefile
@ -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)
|
||||||
|
42
compile.c
42
compile.c
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
datatypes.h
11
datatypes.h
@ -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 */
|
||||||
|
336
disasm.c
336
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_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_literal(FILE * out, uint16_t index) { fprintf(out, "(%d) ", index); }
|
||||||
static void dasm_print_upvalue(FILE * out, uint16_t level, uint16_t 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 */
|
/* Print the name of the argument but pad it */
|
||||||
static void dasm_print_arg(FILE * out, const char * name) {
|
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)
|
||||||
fputc(' ', out);
|
fputc(' ', out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print instructions that take a fixed number of arguments */
|
/* Print instructions that take a fixed number of arguments */
|
||||||
static uint32_t dasm_fixed_op(FILE * out, const uint16_t * current,
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print instructions that take a variable number of arguments */
|
/* Print instructions that take a variable number of arguments */
|
||||||
static uint32_t dasm_varg_op(FILE * out, const uint16_t * current,
|
static uint32_t dasm_varg_op(FILE * out, const uint16_t * current,
|
||||||
const char * name, uint32_t extra) {
|
const char * name, uint32_t extra) {
|
||||||
uint32_t i, argCount;
|
uint32_t i, argCount;
|
||||||
dasm_print_arg(out, name);
|
dasm_print_arg(out, name);
|
||||||
for (i = 0; i < extra; ++i) {
|
for (i = 0; i < extra; ++i) {
|
||||||
dasm_print_slot(out, current[i + 1]);
|
dasm_print_slot(out, current[i + 1]);
|
||||||
}
|
}
|
||||||
argCount = current[extra + 1];
|
argCount = current[extra + 1];
|
||||||
fprintf(out, ": "); /* Argument separator */
|
fprintf(out, ": "); /* Argument separator */
|
||||||
for (i = 0; i < argCount; ++i) {
|
for (i = 0; i < argCount; ++i) {
|
||||||
dasm_print_slot(out, current[i + extra + 2]);
|
dasm_print_slot(out, current[i + extra + 2]);
|
||||||
}
|
}
|
||||||
return argCount + extra + 2;
|
return argCount + extra + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print the disassembly for a function definition */
|
/* 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 *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:
|
||||||
current += dasm_fixed_op(out, current, "add", 3);
|
current += dasm_fixed_op(out, current, "add", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_SUB:
|
case GST_OP_SUB:
|
||||||
current += dasm_fixed_op(out, current, "sub", 3);
|
current += dasm_fixed_op(out, current, "sub", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_MUL:
|
case GST_OP_MUL:
|
||||||
current += dasm_fixed_op(out, current, "mul", 3);
|
current += dasm_fixed_op(out, current, "mul", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_DIV:
|
case GST_OP_DIV:
|
||||||
current += dasm_fixed_op(out, current, "div", 3);
|
current += dasm_fixed_op(out, current, "div", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_NOT:
|
case GST_OP_NOT:
|
||||||
current += dasm_fixed_op(out, current, "not", 2);
|
current += dasm_fixed_op(out, current, "not", 2);
|
||||||
break;
|
break;
|
||||||
case GST_OP_LD0:
|
case GST_OP_LD0:
|
||||||
current += dasm_fixed_op(out, current, "load0", 1);
|
current += dasm_fixed_op(out, current, "load0", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_LD1:
|
case GST_OP_LD1:
|
||||||
current += dasm_fixed_op(out, current, "load1", 1);
|
current += dasm_fixed_op(out, current, "load1", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_FLS:
|
case GST_OP_FLS:
|
||||||
current += dasm_fixed_op(out, current, "loadFalse", 1);
|
current += dasm_fixed_op(out, current, "loadFalse", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_TRU:
|
case GST_OP_TRU:
|
||||||
current += dasm_fixed_op(out, current, "loadTrue", 1);
|
current += dasm_fixed_op(out, current, "loadTrue", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_NIL:
|
case GST_OP_NIL:
|
||||||
current += dasm_fixed_op(out, current, "loadNil", 1);
|
current += dasm_fixed_op(out, current, "loadNil", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_I16:
|
case GST_OP_I16:
|
||||||
dasm_print_arg(out, "loadInt16");
|
dasm_print_arg(out, "loadInt16");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_i16(out, ((int16_t *)current)[2]);
|
dasm_print_i16(out, ((int16_t *)current)[2]);
|
||||||
current += 3;
|
current += 3;
|
||||||
break;
|
break;
|
||||||
case GST_OP_UPV:
|
case GST_OP_UPV:
|
||||||
dasm_print_arg(out, "loadUpValue");
|
dasm_print_arg(out, "loadUpValue");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_upvalue(out, current[2], current[3]);
|
dasm_print_upvalue(out, current[2], current[3]);
|
||||||
current += 4;
|
current += 4;
|
||||||
break;
|
break;
|
||||||
case GST_OP_JIF:
|
case GST_OP_JIF:
|
||||||
dasm_print_arg(out, "jumpIf");
|
dasm_print_arg(out, "jumpIf");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||||
current += 4;
|
current += 4;
|
||||||
break;
|
break;
|
||||||
case GST_OP_JMP:
|
case GST_OP_JMP:
|
||||||
dasm_print_arg(out, "jump");
|
dasm_print_arg(out, "jump");
|
||||||
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
||||||
current += 3;
|
current += 3;
|
||||||
break;
|
break;
|
||||||
case GST_OP_CAL:
|
case GST_OP_CAL:
|
||||||
current += dasm_varg_op(out, current, "call", 2);
|
current += dasm_varg_op(out, current, "call", 2);
|
||||||
break;
|
break;
|
||||||
case GST_OP_RET:
|
case GST_OP_RET:
|
||||||
current += dasm_fixed_op(out, current, "return", 1);
|
current += dasm_fixed_op(out, current, "return", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_SUV:
|
case GST_OP_SUV:
|
||||||
dasm_print_arg(out, "setUpValue");
|
dasm_print_arg(out, "setUpValue");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_upvalue(out, current[2], current[3]);
|
dasm_print_upvalue(out, current[2], current[3]);
|
||||||
current += 4;
|
current += 4;
|
||||||
break;
|
break;
|
||||||
case GST_OP_CST:
|
case GST_OP_CST:
|
||||||
dasm_print_arg(out, "loadLiteral");
|
dasm_print_arg(out, "loadLiteral");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_literal(out, current[2]);
|
dasm_print_literal(out, current[2]);
|
||||||
current += 3;
|
current += 3;
|
||||||
break;
|
break;
|
||||||
case GST_OP_I32:
|
case GST_OP_I32:
|
||||||
dasm_print_arg(out, "loadInt32");
|
dasm_print_arg(out, "loadInt32");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||||
current += 4;
|
current += 4;
|
||||||
break;
|
break;
|
||||||
case GST_OP_F64:
|
case GST_OP_F64:
|
||||||
dasm_print_arg(out, "loadFloat64");
|
dasm_print_arg(out, "loadFloat64");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_f64(out, ((double *)(current + 2))[0]);
|
dasm_print_f64(out, ((double *)(current + 2))[0]);
|
||||||
current += 6;
|
current += 6;
|
||||||
break;
|
break;
|
||||||
case GST_OP_MOV:
|
case GST_OP_MOV:
|
||||||
current += dasm_fixed_op(out, current, "move", 2);
|
current += dasm_fixed_op(out, current, "move", 2);
|
||||||
break;
|
break;
|
||||||
case GST_OP_CLN:
|
case GST_OP_CLN:
|
||||||
dasm_print_arg(out, "makeClosure");
|
dasm_print_arg(out, "makeClosure");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
dasm_print_literal(out, current[2]);
|
dasm_print_literal(out, current[2]);
|
||||||
current += 3;
|
current += 3;
|
||||||
break;
|
break;
|
||||||
case GST_OP_EQL:
|
case GST_OP_EQL:
|
||||||
current += dasm_fixed_op(out, current, "equals", 3);
|
current += dasm_fixed_op(out, current, "equals", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_LTN:
|
case GST_OP_LTN:
|
||||||
current += dasm_fixed_op(out, current, "lessThan", 3);
|
current += dasm_fixed_op(out, current, "lessThan", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_LTE:
|
case GST_OP_LTE:
|
||||||
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
|
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_ARR:
|
case GST_OP_ARR:
|
||||||
current += dasm_varg_op(out, current, "array", 1);
|
current += dasm_varg_op(out, current, "array", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_DIC:
|
case GST_OP_DIC:
|
||||||
current += dasm_varg_op(out, current, "object", 1);
|
current += dasm_varg_op(out, current, "object", 1);
|
||||||
break;
|
break;
|
||||||
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:
|
case GST_OP_RTN:
|
||||||
current += dasm_varg_op(out, current, "addMultiple", 1);
|
current += dasm_fixed_op(out, current, "returnNil", 0);
|
||||||
break;
|
break;
|
||||||
case GST_OP_SBM:
|
case GST_OP_GET:
|
||||||
current += dasm_varg_op(out, current, "subMultiple", 1);
|
current += dasm_fixed_op(out, current, "get", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_MUM:
|
case GST_OP_SET:
|
||||||
current += dasm_varg_op(out, current, "mulMultiple", 1);
|
current += dasm_fixed_op(out, current, "set", 3);
|
||||||
break;
|
break;
|
||||||
case GST_OP_DVM:
|
case GST_OP_ERR:
|
||||||
current += dasm_varg_op(out, current, "divMultiple", 1);
|
current += dasm_fixed_op(out, current, "error", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_RTN:
|
case GST_OP_TRY:
|
||||||
current += dasm_fixed_op(out, current, "returnNil", 0);
|
dasm_print_arg(out, "try");
|
||||||
break;
|
dasm_print_slot(out, current[1]);
|
||||||
case GST_OP_GET:
|
dasm_print_i32(out, *(int32_t *)(current + 2));
|
||||||
current += dasm_fixed_op(out, current, "get", 3);
|
current += 4;
|
||||||
break;
|
break;
|
||||||
case GST_OP_SET:
|
case GST_OP_UTY:
|
||||||
current += dasm_fixed_op(out, current, "set", 3);
|
current += dasm_fixed_op(out, current, "untry", 0);
|
||||||
break;
|
break;
|
||||||
case GST_OP_ERR:
|
}
|
||||||
current += dasm_fixed_op(out, current, "error", 1);
|
fprintf(out, "\n");
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
7
gc.c
7
gc.c
@ -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;
|
||||||
|
78
thread.c
78
thread.c
@ -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);
|
|
||||||
}
|
|
25
thread.h
25
thread.h
@ -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
637
vm.c
@ -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));
|
||||||
|
}
|
||||||
|
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;
|
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 (*pc) {
|
||||||
|
|
||||||
switch (opcode) {
|
|
||||||
|
|
||||||
#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);
|
{
|
||||||
|
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;
|
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 */
|
case GST_OP_RTN: /* Return nil */
|
||||||
temp.type = GST_NIL;
|
v2.type = GST_NIL;
|
||||||
gst_vm_return(vm, temp);
|
goto ret;
|
||||||
break;
|
|
||||||
|
|
||||||
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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user