mirror of
https://github.com/janet-lang/janet
synced 2024-12-24 23:40:27 +00:00
Work on speeding up interpreter by moving state inside
interpreter loop.
This commit is contained in:
parent
fd34837265
commit
33d09f98b1
6
Makefile
6
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)
|
||||
|
42
compile.c
42
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]);
|
||||
}
|
||||
|
||||
|
11
datatypes.h
11
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 */
|
||||
|
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_literal(FILE * out, uint16_t index) { fprintf(out, "(%d) ", index); }
|
||||
static void dasm_print_upvalue(FILE * out, uint16_t level, uint16_t index) {
|
||||
fprintf(out, "<%d, %d> ", level, index);
|
||||
fprintf(out, "<%d, %d> ", level, index);
|
||||
}
|
||||
|
||||
/* Print the name of the argument but pad it */
|
||||
static void dasm_print_arg(FILE * out, const char * name) {
|
||||
uint32_t i = 0;
|
||||
char c;
|
||||
while ((c = *name++)) {
|
||||
fputc(c, out);
|
||||
++i;
|
||||
}
|
||||
for (; i < OP_WIDTH; ++i)
|
||||
fputc(' ', out);
|
||||
uint32_t i = 0;
|
||||
char c;
|
||||
while ((c = *name++)) {
|
||||
putc(c, out);
|
||||
++i;
|
||||
}
|
||||
for (; i < OP_WIDTH; ++i)
|
||||
fputc(' ', out);
|
||||
}
|
||||
|
||||
/* Print instructions that take a fixed number of arguments */
|
||||
static uint32_t dasm_fixed_op(FILE * out, const uint16_t * current,
|
||||
const char * name, uint32_t size) {
|
||||
uint32_t i;
|
||||
dasm_print_arg(out, name);
|
||||
for (i = 1; i <= size; ++i) {
|
||||
dasm_print_slot(out, current[i]);
|
||||
}
|
||||
return size + 1;
|
||||
const char * name, uint32_t size) {
|
||||
uint32_t i;
|
||||
dasm_print_arg(out, name);
|
||||
for (i = 1; i <= size; ++i)
|
||||
dasm_print_slot(out, current[i]);
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
/* Print instructions that take a variable number of arguments */
|
||||
static uint32_t dasm_varg_op(FILE * out, const uint16_t * current,
|
||||
const char * name, uint32_t extra) {
|
||||
uint32_t i, argCount;
|
||||
dasm_print_arg(out, name);
|
||||
for (i = 0; i < extra; ++i) {
|
||||
dasm_print_slot(out, current[i + 1]);
|
||||
}
|
||||
argCount = current[extra + 1];
|
||||
const char * name, uint32_t extra) {
|
||||
uint32_t i, argCount;
|
||||
dasm_print_arg(out, name);
|
||||
for (i = 0; i < extra; ++i) {
|
||||
dasm_print_slot(out, current[i + 1]);
|
||||
}
|
||||
argCount = current[extra + 1];
|
||||
fprintf(out, ": "); /* Argument separator */
|
||||
for (i = 0; i < argCount; ++i) {
|
||||
dasm_print_slot(out, current[i + extra + 2]);
|
||||
}
|
||||
return argCount + extra + 2;
|
||||
for (i = 0; i < argCount; ++i) {
|
||||
dasm_print_slot(out, current[i + extra + 2]);
|
||||
}
|
||||
return argCount + extra + 2;
|
||||
}
|
||||
|
||||
/* Print the disassembly for a function definition */
|
||||
@ -67,155 +66,140 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
||||
uint16_t *current = byteCode;
|
||||
uint16_t *end = byteCode + len;
|
||||
|
||||
fprintf(out, "----- ASM BYTECODE START -----\n");
|
||||
|
||||
while (current < end) {
|
||||
switch (*current) {
|
||||
case GST_OP_ADD:
|
||||
current += dasm_fixed_op(out, current, "add", 3);
|
||||
break;
|
||||
case GST_OP_SUB:
|
||||
current += dasm_fixed_op(out, current, "sub", 3);
|
||||
break;
|
||||
case GST_OP_MUL:
|
||||
current += dasm_fixed_op(out, current, "mul", 3);
|
||||
break;
|
||||
case GST_OP_DIV:
|
||||
current += dasm_fixed_op(out, current, "div", 3);
|
||||
break;
|
||||
case GST_OP_NOT:
|
||||
current += dasm_fixed_op(out, current, "not", 2);
|
||||
break;
|
||||
case GST_OP_LD0:
|
||||
current += dasm_fixed_op(out, current, "load0", 1);
|
||||
break;
|
||||
case GST_OP_LD1:
|
||||
current += dasm_fixed_op(out, current, "load1", 1);
|
||||
break;
|
||||
case GST_OP_FLS:
|
||||
current += dasm_fixed_op(out, current, "loadFalse", 1);
|
||||
break;
|
||||
case GST_OP_TRU:
|
||||
current += dasm_fixed_op(out, current, "loadTrue", 1);
|
||||
break;
|
||||
case GST_OP_NIL:
|
||||
current += dasm_fixed_op(out, current, "loadNil", 1);
|
||||
break;
|
||||
case GST_OP_I16:
|
||||
dasm_print_arg(out, "loadInt16");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i16(out, ((int16_t *)current)[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_UPV:
|
||||
dasm_print_arg(out, "loadUpValue");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_upvalue(out, current[2], current[3]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_JIF:
|
||||
dasm_print_arg(out, "jumpIf");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_JMP:
|
||||
dasm_print_arg(out, "jump");
|
||||
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_CAL:
|
||||
current += dasm_varg_op(out, current, "call", 2);
|
||||
break;
|
||||
case GST_OP_RET:
|
||||
current += dasm_fixed_op(out, current, "return", 1);
|
||||
break;
|
||||
case GST_OP_SUV:
|
||||
dasm_print_arg(out, "setUpValue");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_upvalue(out, current[2], current[3]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_CST:
|
||||
dasm_print_arg(out, "loadLiteral");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_literal(out, current[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_I32:
|
||||
dasm_print_arg(out, "loadInt32");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_F64:
|
||||
dasm_print_arg(out, "loadFloat64");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_f64(out, ((double *)(current + 2))[0]);
|
||||
current += 6;
|
||||
break;
|
||||
case GST_OP_MOV:
|
||||
current += dasm_fixed_op(out, current, "move", 2);
|
||||
break;
|
||||
case GST_OP_CLN:
|
||||
dasm_print_arg(out, "makeClosure");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_literal(out, current[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_EQL:
|
||||
current += dasm_fixed_op(out, current, "equals", 3);
|
||||
break;
|
||||
case GST_OP_LTN:
|
||||
current += dasm_fixed_op(out, current, "lessThan", 3);
|
||||
break;
|
||||
case GST_OP_LTE:
|
||||
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
|
||||
break;
|
||||
case GST_OP_ARR:
|
||||
current += dasm_varg_op(out, current, "array", 1);
|
||||
break;
|
||||
case GST_OP_DIC:
|
||||
current += dasm_varg_op(out, current, "object", 1);
|
||||
break;
|
||||
case GST_OP_TCL:
|
||||
current += dasm_varg_op(out, current, "tailCall", 1);
|
||||
break;
|
||||
case GST_OP_ADM:
|
||||
current += dasm_varg_op(out, current, "addMultiple", 1);
|
||||
break;
|
||||
case GST_OP_SBM:
|
||||
current += dasm_varg_op(out, current, "subMultiple", 1);
|
||||
break;
|
||||
case GST_OP_MUM:
|
||||
current += dasm_varg_op(out, current, "mulMultiple", 1);
|
||||
break;
|
||||
case GST_OP_DVM:
|
||||
current += dasm_varg_op(out, current, "divMultiple", 1);
|
||||
break;
|
||||
case GST_OP_RTN:
|
||||
current += dasm_fixed_op(out, current, "returnNil", 0);
|
||||
break;
|
||||
case GST_OP_GET:
|
||||
current += dasm_fixed_op(out, current, "get", 3);
|
||||
break;
|
||||
case GST_OP_SET:
|
||||
current += dasm_fixed_op(out, current, "set", 3);
|
||||
break;
|
||||
case GST_OP_ERR:
|
||||
current += dasm_fixed_op(out, current, "error", 1);
|
||||
break;
|
||||
case GST_OP_TRY:
|
||||
dasm_print_arg(out, "try");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, *(int32_t *)(current + 2));
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_UTY:
|
||||
current += dasm_fixed_op(out, current, "untry", 0);
|
||||
break;
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
fprintf(out, "----- END ASM BYTECODE -----\n");
|
||||
case GST_OP_ADD:
|
||||
current += dasm_fixed_op(out, current, "add", 3);
|
||||
break;
|
||||
case GST_OP_SUB:
|
||||
current += dasm_fixed_op(out, current, "sub", 3);
|
||||
break;
|
||||
case GST_OP_MUL:
|
||||
current += dasm_fixed_op(out, current, "mul", 3);
|
||||
break;
|
||||
case GST_OP_DIV:
|
||||
current += dasm_fixed_op(out, current, "div", 3);
|
||||
break;
|
||||
case GST_OP_NOT:
|
||||
current += dasm_fixed_op(out, current, "not", 2);
|
||||
break;
|
||||
case GST_OP_LD0:
|
||||
current += dasm_fixed_op(out, current, "load0", 1);
|
||||
break;
|
||||
case GST_OP_LD1:
|
||||
current += dasm_fixed_op(out, current, "load1", 1);
|
||||
break;
|
||||
case GST_OP_FLS:
|
||||
current += dasm_fixed_op(out, current, "loadFalse", 1);
|
||||
break;
|
||||
case GST_OP_TRU:
|
||||
current += dasm_fixed_op(out, current, "loadTrue", 1);
|
||||
break;
|
||||
case GST_OP_NIL:
|
||||
current += dasm_fixed_op(out, current, "loadNil", 1);
|
||||
break;
|
||||
case GST_OP_I16:
|
||||
dasm_print_arg(out, "loadInt16");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i16(out, ((int16_t *)current)[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_UPV:
|
||||
dasm_print_arg(out, "loadUpValue");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_upvalue(out, current[2], current[3]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_JIF:
|
||||
dasm_print_arg(out, "jumpIf");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_JMP:
|
||||
dasm_print_arg(out, "jump");
|
||||
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_CAL:
|
||||
current += dasm_varg_op(out, current, "call", 2);
|
||||
break;
|
||||
case GST_OP_RET:
|
||||
current += dasm_fixed_op(out, current, "return", 1);
|
||||
break;
|
||||
case GST_OP_SUV:
|
||||
dasm_print_arg(out, "setUpValue");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_upvalue(out, current[2], current[3]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_CST:
|
||||
dasm_print_arg(out, "loadLiteral");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_literal(out, current[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_I32:
|
||||
dasm_print_arg(out, "loadInt32");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, ((int32_t *)(current + 2))[0]);
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_F64:
|
||||
dasm_print_arg(out, "loadFloat64");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_f64(out, ((double *)(current + 2))[0]);
|
||||
current += 6;
|
||||
break;
|
||||
case GST_OP_MOV:
|
||||
current += dasm_fixed_op(out, current, "move", 2);
|
||||
break;
|
||||
case GST_OP_CLN:
|
||||
dasm_print_arg(out, "makeClosure");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_literal(out, current[2]);
|
||||
current += 3;
|
||||
break;
|
||||
case GST_OP_EQL:
|
||||
current += dasm_fixed_op(out, current, "equals", 3);
|
||||
break;
|
||||
case GST_OP_LTN:
|
||||
current += dasm_fixed_op(out, current, "lessThan", 3);
|
||||
break;
|
||||
case GST_OP_LTE:
|
||||
current += dasm_fixed_op(out, current, "lessThanEquals", 3);
|
||||
break;
|
||||
case GST_OP_ARR:
|
||||
current += dasm_varg_op(out, current, "array", 1);
|
||||
break;
|
||||
case GST_OP_DIC:
|
||||
current += dasm_varg_op(out, current, "object", 1);
|
||||
break;
|
||||
case GST_OP_TCL:
|
||||
current += dasm_varg_op(out, current, "tailCall", 1);
|
||||
break;
|
||||
case GST_OP_RTN:
|
||||
current += dasm_fixed_op(out, current, "returnNil", 0);
|
||||
break;
|
||||
case GST_OP_GET:
|
||||
current += dasm_fixed_op(out, current, "get", 3);
|
||||
break;
|
||||
case GST_OP_SET:
|
||||
current += dasm_fixed_op(out, current, "set", 3);
|
||||
break;
|
||||
case GST_OP_ERR:
|
||||
current += dasm_fixed_op(out, current, "error", 1);
|
||||
break;
|
||||
case GST_OP_TRY:
|
||||
dasm_print_arg(out, "try");
|
||||
dasm_print_slot(out, current[1]);
|
||||
dasm_print_i32(out, *(int32_t *)(current + 2));
|
||||
current += 4;
|
||||
break;
|
||||
case GST_OP_UTY:
|
||||
current += dasm_fixed_op(out, current, "untry", 0);
|
||||
break;
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
}
|
||||
|
7
gc.c
7
gc.c
@ -1,7 +1,6 @@
|
||||
#include "datatypes.h"
|
||||
#include "gc.h"
|
||||
#include "vm.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The metadata header associated with an allocated block of memory */
|
||||
@ -102,7 +101,8 @@ void gst_mark(Gst *vm, GstValue *x) {
|
||||
if (gc_header(x->data.thread)->color != vm->black) {
|
||||
GstThread *thread = x->data.thread;
|
||||
GstStackFrame *frame = (GstStackFrame *)thread->data;
|
||||
GstStackFrame *end = gst_thread_frame(thread);
|
||||
GstStackFrame *end = (GstStackFrame *)(thread->data +
|
||||
thread->count - GST_FRAME_SIZE);
|
||||
gc_header(thread)->color = vm->black;
|
||||
gc_header(thread->data)->color = vm->black;
|
||||
while (frame <= end)
|
||||
@ -143,9 +143,7 @@ void gst_mark(Gst *vm, GstValue *x) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Iterate over all allocated memory, and free memory that is not
|
||||
@ -200,7 +198,6 @@ void *gst_zalloc(Gst *vm, uint32_t size) {
|
||||
|
||||
/* Run garbage collection */
|
||||
void gst_collect(Gst *vm) {
|
||||
if (vm->lock > 0) return;
|
||||
/* Thread can be null */
|
||||
if (vm->thread) {
|
||||
GstValue thread;
|
||||
|
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 "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 */
|
||||
|
Loading…
Reference in New Issue
Block a user