Instructions now take their static arguments in the bytecode directly instead of from the stack. The return address is now moved into a separate frame stack. That means the only thing in the main stack is now variable references. That means we can mark every variable which is currently on the stack during a GC; even variables which aren't referred to from a namespace. As a result, a GC can happen before or after any instruction without having to worry. The VM should also be faster, and the bytecode should be smaller, due to fewer instructions.master
@@ -11,19 +11,6 @@ enum l2_opcode { | |||
*/ | |||
L2_OP_NOP, | |||
/* | |||
* Push a value to the stack. | |||
* Push <word> | |||
*/ | |||
L2_OP_PUSH, | |||
/* | |||
* Push a value to the stack. | |||
* Push <word1> | |||
* Push <word2> | |||
*/ | |||
L2_OP_PUSH_2, | |||
/* | |||
* Discard the top element from the stack. | |||
* Pop <word> | |||
@@ -53,94 +40,80 @@ enum l2_opcode { | |||
L2_OP_ADD, | |||
/* | |||
* Call a function. | |||
* Pop <argc> | |||
* Pop argc times | |||
* Call a function; func_call <argc> | |||
* Pop <argc> times | |||
* Pop <func> | |||
* Push <iptr> + 1 | |||
* Push array with args | |||
* Call <func> | |||
* (Before returning, the function will push a return value onto the stack) | |||
*/ | |||
L2_OP_FUNC_CALL, | |||
/* | |||
* Jump relative. | |||
* Pop <word> | |||
* Jump <word> words forwards | |||
* Jump relative; rjmp <count> | |||
* Jump <count> words forwards | |||
*/ | |||
L2_OP_RJMP, | |||
/* | |||
* Look up a value from the current stack frame. | |||
* Pop <word> | |||
* Find <val> in stack frame using <word> | |||
* Look up a value from the current stack frame; stack_frame_lookup <key> | |||
* Find <val> in stack frame using <key> | |||
* Push <val> | |||
*/ | |||
L2_OP_STACK_FRAME_LOOKUP, | |||
/* | |||
* Set a value in the current stack frame. | |||
* Pop <key> | |||
* Set a value in the current stack frame; stack_frame_set <key> | |||
* Read <val> | |||
* Assign <val> to stack frame | |||
* Assign <val> to stack frame at <key> | |||
*/ | |||
L2_OP_STACK_FRAME_SET, | |||
/* | |||
* Replace a value on the stack. | |||
* Pop <key> | |||
* Replace a value on the stack; stack_frame_replace <key> | |||
* Read <val> | |||
* Assign <val> to stack frame | |||
* Assign <val> to stack frame at <key> | |||
*/ | |||
L2_OP_STACK_FRAME_REPLACE, | |||
/* | |||
* Return from a function. | |||
* NSPop | |||
* FSPop | |||
* Pop (discard args array) | |||
* Pop <word> | |||
* Jump to <word> | |||
* Jump to <return address> | |||
*/ | |||
L2_OP_RET, | |||
/* | |||
* Allocate an atom from one word. | |||
* Pop <word> | |||
* Alloc integer <var> from <word> | |||
* Put a reference to none at the top of the stack. | |||
* Push 0 | |||
*/ | |||
L2_OP_ALLOC_NONE, | |||
/* | |||
* Allocate an atom from one word; alloc_atom <word> | |||
* Alloc atom <var> from <word> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_ATOM, | |||
/* | |||
* Allocate a real from two words. | |||
* Pop <high> | |||
* Pop <low> | |||
* Allocate a real from two words; alloc_real <high> <low> | |||
* Alloc real <var> from <high> << 32 | <low> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_REAL, | |||
/* | |||
* Allocate a buffer from static data. | |||
* Pop <word1> | |||
* Pop <word2> | |||
* Alloc buffer <var> with length=<word1>, offset=<word2> | |||
* Allocate a buffer from static data; alloc_buffer_static <length> <offset> | |||
* Alloc buffer <var> with <length> and <offset> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_BUFFER_STATIC, | |||
/* | |||
* Allocate a zeroed buffer. | |||
* Pop <word> | |||
* Alloc buffer <var> with length=<word> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_BUFFER_ZERO, | |||
/* | |||
* Allocate an array. | |||
* Pop <count> | |||
* Pop count times | |||
* Allocate an array; <count> | |||
* Pop <count> times | |||
* Alloc array <var> | |||
* Push <var> | |||
*/ | |||
@@ -154,16 +127,14 @@ enum l2_opcode { | |||
L2_OP_ALLOC_NAMESPACE, | |||
/* | |||
* Allocate a function. | |||
* Pop <word> | |||
* Allocate a function; alloc_function <pos> | |||
* Alloc function <var> pointing to location <word> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_FUNCTION, | |||
/* | |||
* Set a namespace's name to a value. | |||
* Pop <key> | |||
* Set a namespace's name to a value; namespace_set <key> | |||
* Read <val> | |||
* Read <ns> | |||
* Assign <val> to <ns[<key>]> | |||
@@ -171,29 +142,26 @@ enum l2_opcode { | |||
L2_OP_NAMESPACE_SET, | |||
/* | |||
* Lookup a value from a namespace. | |||
* Pop <key> | |||
* Lookup a value from a namespace; namespace_lookup <key> | |||
* Pop <ns> | |||
* Push <ns[<key>]> | |||
*/ | |||
L2_OP_NAMESPACE_LOOKUP, | |||
/* | |||
* Look up a value from an array. | |||
* Pop <key> | |||
* Read <val> | |||
* Read <arr> | |||
* Look up a value from an array; direct_array_lookup <key> | |||
* Pop <val> | |||
* Pop <arr> | |||
* Assign <val> to <arr[<key>]> | |||
*/ | |||
L2_OP_DIRECT_ARRAY_LOOKUP, | |||
L2_OP_ARRAY_LOOKUP, | |||
/* | |||
* Set a value in an array. | |||
* Pop <key> | |||
* Set a value in an array; direct_array_set <key> | |||
* Read <arr> | |||
* Push <arr[<key>]> | |||
*/ | |||
L2_OP_DIRECT_ARRAY_SET, | |||
L2_OP_ARRAY_SET, | |||
/* | |||
* Halt execution. |
@@ -26,8 +26,8 @@ void l2_gen_halt(struct l2_generator *gen); | |||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | |||
void l2_gen_pop(struct l2_generator *gen); | |||
void l2_gen_swap_pop(struct l2_generator *gen); | |||
void l2_gen_push(struct l2_generator *gen, l2_word word); | |||
void l2_gen_ret(struct l2_generator *gen); | |||
void l2_gen_none(struct l2_generator *gen); | |||
void l2_gen_number(struct l2_generator *gen, double num); | |||
void l2_gen_string(struct l2_generator *gen, char **str); | |||
void l2_gen_atom(struct l2_generator *gen, char **ident); | |||
@@ -36,11 +36,11 @@ void l2_gen_array(struct l2_generator *gen, l2_word count); | |||
void l2_gen_namespace(struct l2_generator *gen); | |||
void l2_gen_namespace_set(struct l2_generator *gen, char **ident); | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | |||
void l2_gen_direct_array_lookup(struct l2_generator *gen, int number); | |||
void l2_gen_direct_array_set(struct l2_generator *gen, int number); | |||
void l2_gen_array_lookup(struct l2_generator *gen, int number); | |||
void l2_gen_array_set(struct l2_generator *gen, int number); | |||
void l2_gen_stack_frame_lookup(struct l2_generator *gen, char **ident); | |||
void l2_gen_stack_frame_set(struct l2_generator *gen, char **ident); | |||
void l2_gen_stack_frame_replace(struct l2_generator *gen, char **ident); | |||
void l2_gen_func_call(struct l2_generator *gen); | |||
void l2_gen_func_call(struct l2_generator *gen, l2_word argc); | |||
#endif |
@@ -7,7 +7,7 @@ void l2_vm_print_val(struct l2_vm_value *val); | |||
void l2_vm_print_state(struct l2_vm *vm); | |||
void l2_vm_print_heap(struct l2_vm *vm); | |||
void l2_vm_print_stack(struct l2_vm *vm); | |||
void l2_vm_print_nstack(struct l2_vm *vm); | |||
void l2_vm_print_fstack(struct l2_vm *vm); | |||
void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr); | |||
void l2_vm_print_bytecode(l2_word *ops, size_t opcount); |
@@ -75,6 +75,11 @@ struct l2_vm_namespace { | |||
l2_word data[]; | |||
}; | |||
struct l2_vm_stack_frame { | |||
l2_word namespace; | |||
l2_word retptr; | |||
}; | |||
l2_word l2_vm_namespace_get(struct l2_vm *vm, struct l2_vm_value *ns, l2_word key); | |||
void l2_vm_namespace_set(struct l2_vm_value *ns, l2_word key, l2_word val); | |||
int l2_vm_namespace_replace(struct l2_vm *vm, struct l2_vm_value *ns, l2_word key, l2_word val); | |||
@@ -94,8 +99,8 @@ struct l2_vm { | |||
l2_word stack[1024]; | |||
l2_word sptr; | |||
l2_word nstack[1024]; | |||
l2_word nsptr; | |||
struct l2_vm_stack_frame fstack[1024]; | |||
l2_word fsptr; | |||
}; | |||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); |
@@ -36,9 +36,8 @@ void l2_gen_halt(struct l2_generator *gen) { | |||
} | |||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, len); | |||
put(gen, L2_OP_RJMP); | |||
put(gen, len); | |||
} | |||
void l2_gen_pop(struct l2_generator *gen) { | |||
@@ -49,29 +48,26 @@ void l2_gen_swap_pop(struct l2_generator *gen) { | |||
put(gen, L2_OP_SWAP_POP); | |||
} | |||
void l2_gen_push(struct l2_generator *gen, l2_word word) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, word); | |||
} | |||
void l2_gen_ret(struct l2_generator *gen) { | |||
put(gen, L2_OP_RET); | |||
} | |||
void l2_gen_none(struct l2_generator *gen) { | |||
put(gen, L2_OP_ALLOC_NONE); | |||
} | |||
void l2_gen_number(struct l2_generator *gen, double num) { | |||
uint64_t n; | |||
memcpy(&n, &num, sizeof(num)); | |||
put(gen, L2_OP_PUSH_2); | |||
put(gen, n); | |||
put(gen, n >> 32); | |||
put(gen, L2_OP_ALLOC_REAL); | |||
put(gen, n >> 32); | |||
put(gen, n); | |||
} | |||
void l2_gen_atom(struct l2_generator *gen, char **str) { | |||
size_t id = l2_strset_put(&gen->atomset, str); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, id); | |||
put(gen, L2_OP_ALLOC_ATOM); | |||
put(gen, id); | |||
} | |||
void l2_gen_string(struct l2_generator *gen, char **str) { | |||
@@ -83,9 +79,8 @@ void l2_gen_string(struct l2_generator *gen, char **str) { | |||
aligned += sizeof(l2_word) - (aligned % sizeof(l2_word)); | |||
} | |||
put(gen, L2_OP_PUSH); | |||
put(gen, aligned / sizeof(l2_word)); | |||
put(gen, L2_OP_RJMP); | |||
put(gen, aligned / sizeof(l2_word)); | |||
l2_word pos = gen->pos; | |||
gen->pos += aligned / sizeof(l2_word); | |||
@@ -99,30 +94,26 @@ void l2_gen_string(struct l2_generator *gen, char **str) { | |||
gen->strings[id - 1].length = len; | |||
gen->strings[id - 1].pos = pos; | |||
put(gen, L2_OP_PUSH_2); | |||
put(gen, pos); | |||
put(gen, len); | |||
put(gen, L2_OP_ALLOC_BUFFER_STATIC); | |||
put(gen, len); | |||
put(gen, pos); | |||
} else { | |||
free(*str); | |||
struct l2_generator_string *s = &gen->strings[id - 1]; | |||
put(gen, L2_OP_PUSH_2); | |||
put(gen, s->pos); | |||
put(gen, s->length); | |||
put(gen, L2_OP_ALLOC_BUFFER_STATIC); | |||
put(gen, s->length); | |||
put(gen, s->pos); | |||
} | |||
} | |||
void l2_gen_function(struct l2_generator *gen, l2_word pos) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, pos); | |||
put(gen, L2_OP_ALLOC_FUNCTION); | |||
put(gen, pos); | |||
} | |||
void l2_gen_array(struct l2_generator *gen, l2_word count) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, count); | |||
put(gen, L2_OP_ALLOC_ARRAY); | |||
put(gen, count); | |||
} | |||
void l2_gen_namespace(struct l2_generator *gen) { | |||
@@ -131,51 +122,45 @@ void l2_gen_namespace(struct l2_generator *gen) { | |||
void l2_gen_namespace_set(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_NAMESPACE_SET); | |||
put(gen, atom_id); | |||
} | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_NAMESPACE_LOOKUP); | |||
put(gen, atom_id); | |||
} | |||
void l2_gen_direct_array_lookup(struct l2_generator *gen, int number) { | |||
put(gen, L2_OP_PUSH); | |||
void l2_gen_array_lookup(struct l2_generator *gen, int number) { | |||
put(gen, L2_OP_ARRAY_LOOKUP); | |||
put(gen, number); | |||
put(gen, L2_OP_DIRECT_ARRAY_LOOKUP); | |||
} | |||
void l2_gen_direct_array_set(struct l2_generator *gen, int number) { | |||
put(gen, L2_OP_PUSH); | |||
void l2_gen_array_set(struct l2_generator *gen, int number) { | |||
put(gen, L2_OP_ARRAY_SET); | |||
put(gen, number); | |||
put(gen, L2_OP_DIRECT_ARRAY_SET); | |||
} | |||
void l2_gen_stack_frame_lookup(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_STACK_FRAME_LOOKUP); | |||
put(gen, atom_id); | |||
} | |||
void l2_gen_stack_frame_set(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_STACK_FRAME_SET); | |||
put(gen, atom_id); | |||
} | |||
void l2_gen_stack_frame_replace(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_STACK_FRAME_REPLACE); | |||
put(gen, atom_id); | |||
} | |||
void l2_gen_func_call(struct l2_generator *gen) { | |||
void l2_gen_func_call(struct l2_generator *gen, l2_word argc) { | |||
put(gen, L2_OP_FUNC_CALL); | |||
put(gen, argc); | |||
} |
@@ -101,7 +101,7 @@ static int parse_function_literal_impl( | |||
// All functions must put _something_ on the stack | |||
if (first) { | |||
l2_gen_push(gen, 0); | |||
l2_gen_none(gen); | |||
} | |||
l2_gen_ret(gen); | |||
@@ -119,7 +119,7 @@ static int parse_function_literal( | |||
w.w.write = l2_io_mem_write; | |||
gen->writer.w = &w.w; | |||
// Generates three words; PUSH, 0, RJMP | |||
// Generates two words; RJMP, 0 | |||
l2_gen_rjmp(gen, 0); | |||
l2_word pos = gen->pos; | |||
@@ -138,8 +138,8 @@ static int parse_function_literal( | |||
// Due to the earlier gen_rjmp, the second word will be the argument to RJMP. | |||
// Need to set it properly to skip the function body. | |||
// The '- 3' is because we don't skip the PUSH, <count>, RJMP sequence. | |||
ops[1] = opcount - 3; | |||
// The '- 2' is because we don't skip the RJMP, <count> sequence. | |||
ops[1] = opcount - 2; | |||
l2_bufio_put_n(&gen->writer, ops, opcount * sizeof(l2_word)); | |||
free(w.mem); | |||
@@ -284,8 +284,7 @@ static int parse_arg_level_expression( | |||
l2_lexer_consume(lexer); // '(' | |||
l2_lexer_consume(lexer); // ')' | |||
l2_gen_push(gen, 0); | |||
l2_gen_func_call(gen); | |||
l2_gen_func_call(gen, 0); | |||
} else if ( | |||
tok->kind == L2_TOK_PERIOD && tok2->kind == L2_TOK_IDENT && | |||
tok3->kind == L2_TOK_EQUALS) { | |||
@@ -320,14 +319,14 @@ static int parse_arg_level_expression( | |||
return -1; | |||
} | |||
l2_gen_direct_array_set(gen, number); | |||
l2_gen_array_set(gen, number); | |||
l2_gen_swap_pop(gen); | |||
} else if (tok->kind == L2_TOK_DOT_NUMBER) { | |||
l2_trace_scope("direct array lookup"); | |||
int number = tok->v.integer; | |||
l2_lexer_consume(lexer); // dot-number | |||
l2_gen_direct_array_lookup(gen, number); | |||
l2_gen_array_lookup(gen, number); | |||
} else { | |||
break; | |||
} | |||
@@ -351,8 +350,7 @@ static int parse_func_call_after_base( | |||
} while (!tok_is_end(l2_lexer_peek(lexer, 1))); | |||
// The 'argc' previous expressions were arguments, the one before that was the function | |||
l2_gen_push(gen, argc); | |||
l2_gen_func_call(gen); | |||
l2_gen_func_call(gen, argc); | |||
return 0; | |||
} |
@@ -78,8 +78,8 @@ void l2_vm_print_state(struct l2_vm *vm) { | |||
l2_vm_print_stack(vm); | |||
printf("Heap:\n"); | |||
l2_vm_print_heap(vm); | |||
printf("Namespace Stack:\n"); | |||
l2_vm_print_nstack(vm); | |||
printf("Frame Stack:\n"); | |||
l2_vm_print_fstack(vm); | |||
} | |||
void l2_vm_print_heap(struct l2_vm *vm) { | |||
@@ -97,9 +97,9 @@ void l2_vm_print_stack(struct l2_vm *vm) { | |||
} | |||
} | |||
void l2_vm_print_nstack(struct l2_vm *vm) { | |||
for (l2_word i = 0; i < vm->nsptr; ++i) { | |||
printf(" %i: %i\n", i, vm->nstack[i]); | |||
void l2_vm_print_fstack(struct l2_vm *vm) { | |||
for (l2_word i = 0; i < vm->fsptr; ++i) { | |||
printf(" %i: %i, ret %i\n", i, vm->fstack[i].namespace, vm->fstack[i].retptr); | |||
} | |||
} | |||
@@ -119,20 +119,6 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
printf("NOP\n"); | |||
return; | |||
case L2_OP_PUSH: | |||
printf("PUSH "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf("\n"); | |||
return; | |||
case L2_OP_PUSH_2: | |||
printf("PUSH2 "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf(" "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf("\n"); | |||
return; | |||
case L2_OP_POP: | |||
printf("POP\n"); | |||
return; | |||
@@ -150,47 +136,55 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
return; | |||
case L2_OP_FUNC_CALL: | |||
printf("FUNC_CALL\n"); | |||
printf("FUNC_CALL %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_RJMP: | |||
printf("RJMP\n"); | |||
printf("RJMP %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_STACK_FRAME_LOOKUP: | |||
printf("STACK_FRAME_LOOKUP\n"); | |||
printf("STACK_FRAME_LOOKUP %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_STACK_FRAME_SET: | |||
printf("STACK_FRAME_SET\n"); | |||
printf("STACK_FRAME_SET %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_STACK_FRAME_REPLACE: | |||
printf("STACK_FRAME_REPLACE\n"); | |||
printf("STACK_FRAME_REPLACE %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_RET: | |||
printf("RET\n"); | |||
return; | |||
case L2_OP_ALLOC_NONE: | |||
printf("ALLOC_NONE\n"); | |||
return; | |||
case L2_OP_ALLOC_ATOM: | |||
printf("ALLOC_ATOM\n"); | |||
printf("ALLOC_ATOM %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_ALLOC_REAL: | |||
printf("ALLOC_REAL\n"); | |||
{ | |||
l2_word w1 = ops[(*ptr)++]; | |||
l2_word w2 = ops[(*ptr)++]; | |||
printf("ALLOC_REAL %08x %08x\n", w1, w2); | |||
} | |||
return; | |||
case L2_OP_ALLOC_BUFFER_STATIC: | |||
printf("ALLOC_BUFFER_STATIC\n"); | |||
return; | |||
case L2_OP_ALLOC_BUFFER_ZERO: | |||
printf("ALLOC_BUFFER_ZERO\n"); | |||
{ | |||
l2_word w1 = ops[(*ptr)++]; | |||
l2_word w2 = ops[(*ptr)++]; | |||
printf("ALLOC_BUFFER_STATIC %08x %08x\n", w1, w2); | |||
} | |||
return; | |||
case L2_OP_ALLOC_ARRAY: | |||
printf("ALLOC_ARRAY\n"); | |||
printf("ALLOC_ARRAY %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_ALLOC_NAMESPACE: | |||
@@ -198,23 +192,23 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
return; | |||
case L2_OP_ALLOC_FUNCTION: | |||
printf("ALLOC_FUNCTION\n"); | |||
printf("ALLOC_FUNCTION %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_NAMESPACE_SET: | |||
printf("NAMESPACE_SET\n"); | |||
printf("NAMESPACE_SET %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_NAMESPACE_LOOKUP: | |||
printf("NAMESPACE_LOOKUP\n"); | |||
printf("NAMESPACE_LOOKUP %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_DIRECT_ARRAY_LOOKUP: | |||
printf("DIRECT_ARRAY_LOOKUP\n"); | |||
case L2_OP_ARRAY_LOOKUP: | |||
printf("ARRAY_LOOKUP %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_DIRECT_ARRAY_SET: | |||
printf("DIRECT_ARRAY_SET\n"); | |||
case L2_OP_ARRAY_SET: | |||
printf("ARRAY_SET %08x\n", ops[(*ptr)++]); | |||
return; | |||
case L2_OP_HALT: |
@@ -71,6 +71,10 @@ static void gc_mark_array(struct l2_vm *vm, struct l2_vm_value *val) { | |||
} | |||
static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val) { | |||
if (val->extra.ns_parent != 0) { | |||
gc_mark(vm, val->extra.ns_parent); | |||
} | |||
if (val->ns == NULL) { | |||
return; | |||
} | |||
@@ -83,10 +87,6 @@ static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val) { | |||
gc_mark(vm, val->ns->data[val->ns->size + i]); | |||
} | |||
if (val->extra.ns_parent != 0) { | |||
gc_mark(vm, val->extra.ns_parent); | |||
} | |||
} | |||
static void gc_free(struct l2_vm *vm, l2_word id) { | |||
@@ -140,7 +140,7 @@ void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
vm->opcount = opcount; | |||
vm->iptr = 0; | |||
vm->sptr = 0; | |||
vm->nsptr = 0; | |||
vm->fsptr = 0; | |||
vm->values = NULL; | |||
vm->valuessize = 0; | |||
@@ -156,14 +156,18 @@ void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
vm->values[builtins].extra.ns_parent = 0; | |||
vm->values[builtins].ns = NULL; // Will be allocated on first insert | |||
vm->values[builtins].flags = L2_VAL_TYPE_NAMESPACE; | |||
vm->nstack[vm->nsptr++] = builtins; | |||
vm->fstack[vm->fsptr].namespace = builtins; | |||
vm->fstack[vm->fsptr].retptr = 0; | |||
vm->fsptr += 1; | |||
// Need to allocate a root namespace | |||
l2_word root = alloc_val(vm); | |||
vm->values[root].extra.ns_parent = builtins; | |||
vm->values[root].ns = NULL; | |||
vm->values[root].flags = L2_VAL_TYPE_NAMESPACE; | |||
vm->nstack[vm->nsptr++] = root; | |||
vm->fstack[vm->fsptr].namespace = root; | |||
vm->fstack[vm->fsptr].retptr = 0; | |||
vm->fsptr += 1; | |||
// Define a C function variable for every builtin | |||
l2_word id; | |||
@@ -199,10 +203,12 @@ void l2_vm_free(struct l2_vm *vm) { | |||
} | |||
size_t l2_vm_gc(struct l2_vm *vm) { | |||
for (l2_word nsptr = 0; nsptr < vm->nsptr; ++nsptr) { | |||
struct l2_vm_value *val = &vm->values[vm->nstack[nsptr]]; | |||
val->flags |= L2_VAL_MARKED; | |||
gc_mark_namespace(vm, val); | |||
for (l2_word sptr = 0; sptr < vm->sptr; ++sptr) { | |||
gc_mark(vm, vm->stack[sptr]); | |||
} | |||
for (l2_word fsptr = 0; fsptr < vm->fsptr; ++fsptr) { | |||
gc_mark(vm, vm->fstack[fsptr].namespace); | |||
} | |||
return gc_sweep(vm); | |||
@@ -222,19 +228,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_NOP: | |||
break; | |||
case L2_OP_PUSH: | |||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | |||
vm->sptr += 1; | |||
vm->iptr += 1; | |||
break; | |||
case L2_OP_PUSH_2: | |||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | |||
vm->stack[vm->sptr + 1] = vm->ops[vm->iptr + 1]; | |||
vm->sptr += 2; | |||
vm->iptr += 2; | |||
break; | |||
case L2_OP_POP: | |||
vm->sptr -= 1; | |||
break; | |||
@@ -256,7 +249,7 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_FUNC_CALL: | |||
{ | |||
l2_word argc = vm->stack[--vm->sptr]; | |||
l2_word argc = vm->ops[vm->iptr++]; | |||
l2_word arr_id = alloc_val(vm); | |||
vm->values[arr_id].flags = L2_VAL_TYPE_ARRAY; | |||
@@ -288,7 +281,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
break; | |||
} | |||
vm->stack[vm->sptr++] = vm->iptr; | |||
vm->stack[vm->sptr++] = arr_id; | |||
l2_word ns_id = alloc_val(vm); | |||
@@ -296,40 +288,40 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[ns_id].extra.ns_parent = func->func.namespace; | |||
vm->values[ns_id].ns = NULL; | |||
vm->values[ns_id].flags = L2_VAL_TYPE_NAMESPACE; | |||
vm->nstack[vm->nsptr++] = ns_id; | |||
vm->fstack[vm->fsptr].namespace = ns_id; | |||
vm->fstack[vm->fsptr].retptr = vm->iptr; | |||
vm->fsptr += 1; | |||
vm->iptr = func->func.pos; | |||
} | |||
break; | |||
case L2_OP_RJMP: | |||
word = vm->stack[vm->sptr - 1]; | |||
vm->sptr -= 1; | |||
vm->iptr += word; | |||
vm->iptr += vm->ops[vm->iptr++]; | |||
break; | |||
case L2_OP_STACK_FRAME_LOOKUP: | |||
{ | |||
l2_word key = vm->stack[vm->sptr - 1]; | |||
struct l2_vm_value *ns = &vm->values[vm->nstack[vm->nsptr - 1]]; | |||
vm->stack[vm->sptr - 1] = l2_vm_namespace_get(vm, ns, key); | |||
l2_word key = vm->ops[vm->iptr++]; | |||
struct l2_vm_value *ns = &vm->values[vm->fstack[vm->fsptr - 1].namespace]; | |||
vm->stack[vm->sptr++] = l2_vm_namespace_get(vm, ns, key); | |||
} | |||
break; | |||
case L2_OP_STACK_FRAME_SET: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word val = vm->stack[vm->sptr - 1]; | |||
struct l2_vm_value *ns = &vm->values[vm->nstack[vm->nsptr - 1]]; | |||
struct l2_vm_value *ns = &vm->values[vm->fstack[vm->fsptr - 1].namespace]; | |||
l2_vm_namespace_set(ns, key, val); | |||
} | |||
break; | |||
case L2_OP_STACK_FRAME_REPLACE: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word val = vm->stack[vm->sptr - 1]; | |||
struct l2_vm_value *ns = &vm->values[vm->nstack[vm->nsptr - 1]]; | |||
struct l2_vm_value *ns = &vm->values[vm->fstack[vm->fsptr - 1].namespace]; | |||
l2_vm_namespace_replace(vm, ns, key, val); // TODO: error if returns -1 | |||
} | |||
break; | |||
@@ -338,34 +330,39 @@ void l2_vm_step(struct l2_vm *vm) { | |||
{ | |||
l2_word retval = vm->stack[--vm->sptr]; | |||
vm->sptr -= 1; // Discard arguments array | |||
l2_word retaddr = vm->stack[--vm->sptr]; | |||
l2_word retptr = vm->fstack[--vm->fsptr].retptr; | |||
vm->stack[vm->sptr++] = retval; | |||
vm->nsptr -= 1; // Pop function stack frame | |||
vm->iptr = retaddr; | |||
vm->iptr = retptr; | |||
} | |||
break; | |||
case L2_OP_ALLOC_NONE: | |||
vm->stack[vm->sptr++] = 0; | |||
break; | |||
case L2_OP_ALLOC_ATOM: | |||
word = alloc_val(vm); | |||
vm->values[word].flags = L2_VAL_TYPE_ATOM; | |||
vm->values[word].atom= vm->stack[--vm->sptr]; | |||
vm->values[word].atom = vm->ops[vm->iptr++]; | |||
vm->stack[vm->sptr++] = word; | |||
break; | |||
case L2_OP_ALLOC_REAL: | |||
word = alloc_val(vm); | |||
vm->values[word].flags = L2_VAL_TYPE_REAL; | |||
vm->values[word].real = u32s_to_double(vm->stack[vm->sptr - 1], vm->stack[vm->sptr - 2]); | |||
vm->sptr -= 2; | |||
vm->stack[vm->sptr] = word; | |||
vm->sptr += 1; | |||
{ | |||
word = alloc_val(vm); | |||
l2_word high = vm->ops[vm->iptr++]; | |||
l2_word low = vm->ops[vm->iptr++]; | |||
vm->values[word].flags = L2_VAL_TYPE_REAL; | |||
vm->values[word].real = u32s_to_double(high, low); | |||
vm->stack[vm->sptr++] = word; | |||
} | |||
break; | |||
case L2_OP_ALLOC_BUFFER_STATIC: | |||
{ | |||
word = alloc_val(vm); | |||
l2_word length = vm->stack[--vm->sptr]; | |||
l2_word offset = vm->stack[--vm->sptr]; | |||
l2_word length = vm->ops[vm->iptr++]; | |||
l2_word offset = vm->ops[vm->iptr++]; | |||
vm->values[word].flags = L2_VAL_TYPE_BUFFER; | |||
vm->values[word].buffer = malloc(sizeof(struct l2_vm_buffer) + length); | |||
vm->values[word].buffer->len = length; | |||
@@ -377,21 +374,9 @@ void l2_vm_step(struct l2_vm *vm) { | |||
} | |||
break; | |||
case L2_OP_ALLOC_BUFFER_ZERO: | |||
{ | |||
word = alloc_val(vm); | |||
l2_word length = vm->stack[--vm->sptr]; | |||
vm->values[word].flags = L2_VAL_TYPE_BUFFER; | |||
vm->values[word].buffer = calloc(1, sizeof(struct l2_vm_buffer) + length); | |||
vm->values[word].buffer->len = length; | |||
vm->stack[vm->sptr] = word; | |||
vm->sptr += 1; | |||
} | |||
break; | |||
case L2_OP_ALLOC_ARRAY: | |||
{ | |||
l2_word count = vm->stack[--vm->sptr]; | |||
l2_word count = vm->ops[vm->iptr++]; | |||
l2_word arr_id = alloc_val(vm); | |||
struct l2_vm_value *arr = &vm->values[arr_id]; | |||
arr->flags = L2_VAL_TYPE_ARRAY; | |||
@@ -424,15 +409,15 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_ALLOC_FUNCTION: | |||
word = alloc_val(vm); | |||
vm->values[word].flags = L2_VAL_TYPE_FUNCTION; | |||
vm->values[word].func.pos = vm->stack[--vm->sptr]; | |||
vm->values[word].func.namespace = vm->nstack[vm->nsptr - 1]; | |||
vm->values[word].func.pos = vm->ops[vm->iptr++]; | |||
vm->values[word].func.namespace = vm->fstack[vm->fsptr - 1].namespace; | |||
vm->stack[vm->sptr] = word; | |||
vm->sptr += 1; | |||
break; | |||
case L2_OP_NAMESPACE_SET: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word val = vm->stack[vm->sptr - 1]; | |||
l2_word ns = vm->stack[vm->sptr - 2]; | |||
l2_vm_namespace_set(&vm->values[ns], key, val); | |||
@@ -441,24 +426,24 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_NAMESPACE_LOOKUP: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word ns = vm->stack[--vm->sptr]; | |||
vm->stack[vm->sptr++] = l2_vm_namespace_get(vm, &vm->values[ns], key); | |||
} | |||
break; | |||
case L2_OP_DIRECT_ARRAY_LOOKUP: | |||
case L2_OP_ARRAY_LOOKUP: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word arr = vm->stack[--vm->sptr]; | |||
// TODO: Error if out of bounds or incorrect type | |||
vm->stack[vm->sptr++] = vm->values[arr].array->data[key]; | |||
} | |||
break; | |||
case L2_OP_DIRECT_ARRAY_SET: | |||
case L2_OP_ARRAY_SET: | |||
{ | |||
l2_word key = vm->stack[--vm->sptr]; | |||
l2_word key = vm->ops[vm->iptr++]; | |||
l2_word val = vm->stack[vm->sptr - 1]; | |||
l2_word arr = vm->stack[vm->sptr - 2]; | |||
// TODO: Error if out of bounds or incorrect type |
@@ -13,7 +13,7 @@ static struct l2_vm vm; | |||
static struct l2_vm_value *var_lookup(const char *name) { | |||
l2_word atom_id = l2_strset_get(&gen.atomset, name); | |||
l2_word id = l2_vm_namespace_get(&vm, &vm.values[vm.nstack[1]], atom_id); | |||
l2_word id = l2_vm_namespace_get(&vm, &vm.values[vm.fstack[1].namespace], atom_id); | |||
return &vm.values[id]; | |||
} | |||
@@ -110,8 +110,11 @@ static void check_impl(const char *name) { | |||
l2_vm_init(&vm, (l2_word *)bytecode.mem, bytecode.len / sizeof(l2_word)); | |||
vm.std_output = &output.w; | |||
l2_vm_run(&vm); | |||
l2_vm_gc(&vm); | |||
// Run a GC after every instruction to uncover potential GC issues | |||
while (vm.ops[vm.iptr] != L2_OP_HALT) { | |||
l2_vm_step(&vm); | |||
l2_vm_gc(&vm); | |||
} | |||
l2_vm_free(&vm); | |||
free(bytecode.mem); | |||
@@ -122,15 +125,13 @@ static void check_impl(const char *name) { | |||
#define check(name) do { \ | |||
snow_fail_update(); \ | |||
check_impl(name); \ | |||
test(name) { check_impl(name); } \ | |||
} while (0) | |||
describe(exaples) { | |||
test("examples") { | |||
check("namespaces.l2"); | |||
check("arrays.l2"); | |||
check("functions.l2"); | |||
} | |||
check("namespaces.l2"); | |||
check("arrays.l2"); | |||
check("functions.l2"); | |||
if (error_message != NULL) { | |||
free(error_message); |