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
*/ | */ | ||||
L2_OP_NOP, | 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. | * Discard the top element from the stack. | ||||
* Pop <word> | * Pop <word> | ||||
L2_OP_ADD, | L2_OP_ADD, | ||||
/* | /* | ||||
* Call a function. | |||||
* Pop <argc> | |||||
* Pop argc times | |||||
* Call a function; func_call <argc> | |||||
* Pop <argc> times | |||||
* Pop <func> | * Pop <func> | ||||
* Push <iptr> + 1 | |||||
* Push array with args | * Push array with args | ||||
* Call <func> | * Call <func> | ||||
* (Before returning, the function will push a return value onto the stack) | |||||
*/ | */ | ||||
L2_OP_FUNC_CALL, | L2_OP_FUNC_CALL, | ||||
/* | /* | ||||
* Jump relative. | |||||
* Pop <word> | |||||
* Jump <word> words forwards | |||||
* Jump relative; rjmp <count> | |||||
* Jump <count> words forwards | |||||
*/ | */ | ||||
L2_OP_RJMP, | 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> | * Push <val> | ||||
*/ | */ | ||||
L2_OP_STACK_FRAME_LOOKUP, | 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> | * Read <val> | ||||
* Assign <val> to stack frame | |||||
* Assign <val> to stack frame at <key> | |||||
*/ | */ | ||||
L2_OP_STACK_FRAME_SET, | 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> | * Read <val> | ||||
* Assign <val> to stack frame | |||||
* Assign <val> to stack frame at <key> | |||||
*/ | */ | ||||
L2_OP_STACK_FRAME_REPLACE, | L2_OP_STACK_FRAME_REPLACE, | ||||
/* | /* | ||||
* Return from a function. | * Return from a function. | ||||
* NSPop | |||||
* FSPop | |||||
* Pop (discard args array) | * Pop (discard args array) | ||||
* Pop <word> | |||||
* Jump to <word> | |||||
* Jump to <return address> | |||||
*/ | */ | ||||
L2_OP_RET, | 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> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_ATOM, | 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> | * Alloc real <var> from <high> << 32 | <low> | ||||
* Push <var> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_REAL, | 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> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_BUFFER_STATIC, | 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> | * Alloc array <var> | ||||
* Push <var> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_NAMESPACE, | L2_OP_ALLOC_NAMESPACE, | ||||
/* | /* | ||||
* Allocate a function. | |||||
* Pop <word> | |||||
* Allocate a function; alloc_function <pos> | |||||
* Alloc function <var> pointing to location <word> | * Alloc function <var> pointing to location <word> | ||||
* Push <var> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_FUNCTION, | 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 <val> | ||||
* Read <ns> | * Read <ns> | ||||
* Assign <val> to <ns[<key>]> | * Assign <val> to <ns[<key>]> | ||||
L2_OP_NAMESPACE_SET, | L2_OP_NAMESPACE_SET, | ||||
/* | /* | ||||
* Lookup a value from a namespace. | |||||
* Pop <key> | |||||
* Lookup a value from a namespace; namespace_lookup <key> | |||||
* Pop <ns> | * Pop <ns> | ||||
* Push <ns[<key>]> | * Push <ns[<key>]> | ||||
*/ | */ | ||||
L2_OP_NAMESPACE_LOOKUP, | 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>]> | * 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> | * Read <arr> | ||||
* Push <arr[<key>]> | * Push <arr[<key>]> | ||||
*/ | */ | ||||
L2_OP_DIRECT_ARRAY_SET, | |||||
L2_OP_ARRAY_SET, | |||||
/* | /* | ||||
* Halt execution. | * Halt execution. |
void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | ||||
void l2_gen_pop(struct l2_generator *gen); | void l2_gen_pop(struct l2_generator *gen); | ||||
void l2_gen_swap_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_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_number(struct l2_generator *gen, double num); | ||||
void l2_gen_string(struct l2_generator *gen, char **str); | void l2_gen_string(struct l2_generator *gen, char **str); | ||||
void l2_gen_atom(struct l2_generator *gen, char **ident); | void l2_gen_atom(struct l2_generator *gen, char **ident); | ||||
void l2_gen_namespace(struct l2_generator *gen); | void l2_gen_namespace(struct l2_generator *gen); | ||||
void l2_gen_namespace_set(struct l2_generator *gen, char **ident); | 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_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_lookup(struct l2_generator *gen, char **ident); | ||||
void l2_gen_stack_frame_set(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_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 | #endif |
void l2_vm_print_state(struct l2_vm *vm); | void l2_vm_print_state(struct l2_vm *vm); | ||||
void l2_vm_print_heap(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_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_op(l2_word *ops, size_t opcount, size_t *ptr); | ||||
void l2_vm_print_bytecode(l2_word *ops, size_t opcount); | void l2_vm_print_bytecode(l2_word *ops, size_t opcount); |
l2_word data[]; | 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); | 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); | 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); | int l2_vm_namespace_replace(struct l2_vm *vm, struct l2_vm_value *ns, l2_word key, l2_word val); | ||||
l2_word stack[1024]; | l2_word stack[1024]; | ||||
l2_word sptr; | 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); | void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); |
} | } | ||||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | 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, L2_OP_RJMP); | ||||
put(gen, len); | |||||
} | } | ||||
void l2_gen_pop(struct l2_generator *gen) { | void l2_gen_pop(struct l2_generator *gen) { | ||||
put(gen, L2_OP_SWAP_POP); | 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) { | void l2_gen_ret(struct l2_generator *gen) { | ||||
put(gen, L2_OP_RET); | 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) { | void l2_gen_number(struct l2_generator *gen, double num) { | ||||
uint64_t n; | uint64_t n; | ||||
memcpy(&n, &num, sizeof(num)); | 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, L2_OP_ALLOC_REAL); | ||||
put(gen, n >> 32); | |||||
put(gen, n); | |||||
} | } | ||||
void l2_gen_atom(struct l2_generator *gen, char **str) { | void l2_gen_atom(struct l2_generator *gen, char **str) { | ||||
size_t id = l2_strset_put(&gen->atomset, 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, L2_OP_ALLOC_ATOM); | ||||
put(gen, id); | |||||
} | } | ||||
void l2_gen_string(struct l2_generator *gen, char **str) { | void l2_gen_string(struct l2_generator *gen, char **str) { | ||||
aligned += sizeof(l2_word) - (aligned % sizeof(l2_word)); | 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, L2_OP_RJMP); | ||||
put(gen, aligned / sizeof(l2_word)); | |||||
l2_word pos = gen->pos; | l2_word pos = gen->pos; | ||||
gen->pos += aligned / sizeof(l2_word); | gen->pos += aligned / sizeof(l2_word); | ||||
gen->strings[id - 1].length = len; | gen->strings[id - 1].length = len; | ||||
gen->strings[id - 1].pos = pos; | 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, L2_OP_ALLOC_BUFFER_STATIC); | ||||
put(gen, len); | |||||
put(gen, pos); | |||||
} else { | } else { | ||||
free(*str); | free(*str); | ||||
struct l2_generator_string *s = &gen->strings[id - 1]; | 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, L2_OP_ALLOC_BUFFER_STATIC); | ||||
put(gen, s->length); | |||||
put(gen, s->pos); | |||||
} | } | ||||
} | } | ||||
void l2_gen_function(struct l2_generator *gen, l2_word 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, L2_OP_ALLOC_FUNCTION); | ||||
put(gen, pos); | |||||
} | } | ||||
void l2_gen_array(struct l2_generator *gen, l2_word count) { | 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, L2_OP_ALLOC_ARRAY); | ||||
put(gen, count); | |||||
} | } | ||||
void l2_gen_namespace(struct l2_generator *gen) { | void l2_gen_namespace(struct l2_generator *gen) { | ||||
void l2_gen_namespace_set(struct l2_generator *gen, char **ident) { | void l2_gen_namespace_set(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atomset, 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, L2_OP_NAMESPACE_SET); | ||||
put(gen, atom_id); | |||||
} | } | ||||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atomset, 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, 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, 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, number); | ||||
put(gen, L2_OP_DIRECT_ARRAY_SET); | |||||
} | } | ||||
void l2_gen_stack_frame_lookup(struct l2_generator *gen, char **ident) { | void l2_gen_stack_frame_lookup(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atomset, 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, L2_OP_STACK_FRAME_LOOKUP); | ||||
put(gen, atom_id); | |||||
} | } | ||||
void l2_gen_stack_frame_set(struct l2_generator *gen, char **ident) { | void l2_gen_stack_frame_set(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atomset, 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, L2_OP_STACK_FRAME_SET); | ||||
put(gen, atom_id); | |||||
} | } | ||||
void l2_gen_stack_frame_replace(struct l2_generator *gen, char **ident) { | void l2_gen_stack_frame_replace(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atomset, 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, 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, L2_OP_FUNC_CALL); | ||||
put(gen, argc); | |||||
} | } |
// All functions must put _something_ on the stack | // All functions must put _something_ on the stack | ||||
if (first) { | if (first) { | ||||
l2_gen_push(gen, 0); | |||||
l2_gen_none(gen); | |||||
} | } | ||||
l2_gen_ret(gen); | l2_gen_ret(gen); | ||||
w.w.write = l2_io_mem_write; | w.w.write = l2_io_mem_write; | ||||
gen->writer.w = &w.w; | gen->writer.w = &w.w; | ||||
// Generates three words; PUSH, 0, RJMP | |||||
// Generates two words; RJMP, 0 | |||||
l2_gen_rjmp(gen, 0); | l2_gen_rjmp(gen, 0); | ||||
l2_word pos = gen->pos; | l2_word pos = gen->pos; | ||||
// Due to the earlier gen_rjmp, the second word will be the argument to RJMP. | // 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. | // 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)); | l2_bufio_put_n(&gen->writer, ops, opcount * sizeof(l2_word)); | ||||
free(w.mem); | free(w.mem); | ||||
l2_lexer_consume(lexer); // '(' | l2_lexer_consume(lexer); // '(' | ||||
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 ( | } else if ( | ||||
tok->kind == L2_TOK_PERIOD && tok2->kind == L2_TOK_IDENT && | tok->kind == L2_TOK_PERIOD && tok2->kind == L2_TOK_IDENT && | ||||
tok3->kind == L2_TOK_EQUALS) { | tok3->kind == L2_TOK_EQUALS) { | ||||
return -1; | return -1; | ||||
} | } | ||||
l2_gen_direct_array_set(gen, number); | |||||
l2_gen_array_set(gen, number); | |||||
l2_gen_swap_pop(gen); | l2_gen_swap_pop(gen); | ||||
} else if (tok->kind == L2_TOK_DOT_NUMBER) { | } else if (tok->kind == L2_TOK_DOT_NUMBER) { | ||||
l2_trace_scope("direct array lookup"); | l2_trace_scope("direct array lookup"); | ||||
int number = tok->v.integer; | int number = tok->v.integer; | ||||
l2_lexer_consume(lexer); // dot-number | l2_lexer_consume(lexer); // dot-number | ||||
l2_gen_direct_array_lookup(gen, number); | |||||
l2_gen_array_lookup(gen, number); | |||||
} else { | } else { | ||||
break; | break; | ||||
} | } | ||||
} while (!tok_is_end(l2_lexer_peek(lexer, 1))); | } while (!tok_is_end(l2_lexer_peek(lexer, 1))); | ||||
// The 'argc' previous expressions were arguments, the one before that was the function | // 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; | return 0; | ||||
} | } |
l2_vm_print_stack(vm); | l2_vm_print_stack(vm); | ||||
printf("Heap:\n"); | printf("Heap:\n"); | ||||
l2_vm_print_heap(vm); | 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) { | void l2_vm_print_heap(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); | |||||
} | } | ||||
} | } | ||||
printf("NOP\n"); | printf("NOP\n"); | ||||
return; | 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: | case L2_OP_POP: | ||||
printf("POP\n"); | printf("POP\n"); | ||||
return; | return; | ||||
return; | return; | ||||
case L2_OP_FUNC_CALL: | case L2_OP_FUNC_CALL: | ||||
printf("FUNC_CALL\n"); | |||||
printf("FUNC_CALL %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_RJMP: | case L2_OP_RJMP: | ||||
printf("RJMP\n"); | |||||
printf("RJMP %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_STACK_FRAME_LOOKUP: | case L2_OP_STACK_FRAME_LOOKUP: | ||||
printf("STACK_FRAME_LOOKUP\n"); | |||||
printf("STACK_FRAME_LOOKUP %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_STACK_FRAME_SET: | case L2_OP_STACK_FRAME_SET: | ||||
printf("STACK_FRAME_SET\n"); | |||||
printf("STACK_FRAME_SET %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_STACK_FRAME_REPLACE: | case L2_OP_STACK_FRAME_REPLACE: | ||||
printf("STACK_FRAME_REPLACE\n"); | |||||
printf("STACK_FRAME_REPLACE %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_RET: | case L2_OP_RET: | ||||
printf("RET\n"); | printf("RET\n"); | ||||
return; | return; | ||||
case L2_OP_ALLOC_NONE: | |||||
printf("ALLOC_NONE\n"); | |||||
return; | |||||
case L2_OP_ALLOC_ATOM: | case L2_OP_ALLOC_ATOM: | ||||
printf("ALLOC_ATOM\n"); | |||||
printf("ALLOC_ATOM %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_ALLOC_REAL: | 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; | return; | ||||
case L2_OP_ALLOC_BUFFER_STATIC: | 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; | return; | ||||
case L2_OP_ALLOC_ARRAY: | case L2_OP_ALLOC_ARRAY: | ||||
printf("ALLOC_ARRAY\n"); | |||||
printf("ALLOC_ARRAY %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_ALLOC_NAMESPACE: | case L2_OP_ALLOC_NAMESPACE: | ||||
return; | return; | ||||
case L2_OP_ALLOC_FUNCTION: | case L2_OP_ALLOC_FUNCTION: | ||||
printf("ALLOC_FUNCTION\n"); | |||||
printf("ALLOC_FUNCTION %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_NAMESPACE_SET: | case L2_OP_NAMESPACE_SET: | ||||
printf("NAMESPACE_SET\n"); | |||||
printf("NAMESPACE_SET %08x\n", ops[(*ptr)++]); | |||||
return; | return; | ||||
case L2_OP_NAMESPACE_LOOKUP: | case L2_OP_NAMESPACE_LOOKUP: | ||||
printf("NAMESPACE_LOOKUP\n"); | |||||
printf("NAMESPACE_LOOKUP %08x\n", ops[(*ptr)++]); | |||||
return; | 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; | 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; | return; | ||||
case L2_OP_HALT: | case L2_OP_HALT: |
} | } | ||||
static void gc_mark_namespace(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) { | if (val->ns == NULL) { | ||||
return; | return; | ||||
} | } | ||||
gc_mark(vm, val->ns->data[val->ns->size + i]); | 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) { | static void gc_free(struct l2_vm *vm, l2_word id) { | ||||
vm->opcount = opcount; | vm->opcount = opcount; | ||||
vm->iptr = 0; | vm->iptr = 0; | ||||
vm->sptr = 0; | vm->sptr = 0; | ||||
vm->nsptr = 0; | |||||
vm->fsptr = 0; | |||||
vm->values = NULL; | vm->values = NULL; | ||||
vm->valuessize = 0; | vm->valuessize = 0; | ||||
vm->values[builtins].extra.ns_parent = 0; | vm->values[builtins].extra.ns_parent = 0; | ||||
vm->values[builtins].ns = NULL; // Will be allocated on first insert | vm->values[builtins].ns = NULL; // Will be allocated on first insert | ||||
vm->values[builtins].flags = L2_VAL_TYPE_NAMESPACE; | 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 | // Need to allocate a root namespace | ||||
l2_word root = alloc_val(vm); | l2_word root = alloc_val(vm); | ||||
vm->values[root].extra.ns_parent = builtins; | vm->values[root].extra.ns_parent = builtins; | ||||
vm->values[root].ns = NULL; | vm->values[root].ns = NULL; | ||||
vm->values[root].flags = L2_VAL_TYPE_NAMESPACE; | 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 | // Define a C function variable for every builtin | ||||
l2_word id; | l2_word id; | ||||
} | } | ||||
size_t l2_vm_gc(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); | return gc_sweep(vm); | ||||
case L2_OP_NOP: | case L2_OP_NOP: | ||||
break; | 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: | case L2_OP_POP: | ||||
vm->sptr -= 1; | vm->sptr -= 1; | ||||
break; | break; | ||||
case L2_OP_FUNC_CALL: | 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); | l2_word arr_id = alloc_val(vm); | ||||
vm->values[arr_id].flags = L2_VAL_TYPE_ARRAY; | vm->values[arr_id].flags = L2_VAL_TYPE_ARRAY; | ||||
break; | break; | ||||
} | } | ||||
vm->stack[vm->sptr++] = vm->iptr; | |||||
vm->stack[vm->sptr++] = arr_id; | vm->stack[vm->sptr++] = arr_id; | ||||
l2_word ns_id = alloc_val(vm); | l2_word ns_id = alloc_val(vm); | ||||
vm->values[ns_id].extra.ns_parent = func->func.namespace; | vm->values[ns_id].extra.ns_parent = func->func.namespace; | ||||
vm->values[ns_id].ns = NULL; | vm->values[ns_id].ns = NULL; | ||||
vm->values[ns_id].flags = L2_VAL_TYPE_NAMESPACE; | 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; | vm->iptr = func->func.pos; | ||||
} | } | ||||
break; | break; | ||||
case L2_OP_RJMP: | case L2_OP_RJMP: | ||||
word = vm->stack[vm->sptr - 1]; | |||||
vm->sptr -= 1; | |||||
vm->iptr += word; | |||||
vm->iptr += vm->ops[vm->iptr++]; | |||||
break; | break; | ||||
case L2_OP_STACK_FRAME_LOOKUP: | 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; | break; | ||||
case L2_OP_STACK_FRAME_SET: | 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]; | 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); | l2_vm_namespace_set(ns, key, val); | ||||
} | } | ||||
break; | break; | ||||
case L2_OP_STACK_FRAME_REPLACE: | 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]; | 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 | l2_vm_namespace_replace(vm, ns, key, val); // TODO: error if returns -1 | ||||
} | } | ||||
break; | break; | ||||
{ | { | ||||
l2_word retval = vm->stack[--vm->sptr]; | l2_word retval = vm->stack[--vm->sptr]; | ||||
vm->sptr -= 1; // Discard arguments array | 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->stack[vm->sptr++] = retval; | ||||
vm->nsptr -= 1; // Pop function stack frame | |||||
vm->iptr = retaddr; | |||||
vm->iptr = retptr; | |||||
} | } | ||||
break; | break; | ||||
case L2_OP_ALLOC_NONE: | |||||
vm->stack[vm->sptr++] = 0; | |||||
break; | |||||
case L2_OP_ALLOC_ATOM: | case L2_OP_ALLOC_ATOM: | ||||
word = alloc_val(vm); | word = alloc_val(vm); | ||||
vm->values[word].flags = L2_VAL_TYPE_ATOM; | 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; | vm->stack[vm->sptr++] = word; | ||||
break; | break; | ||||
case L2_OP_ALLOC_REAL: | 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; | break; | ||||
case L2_OP_ALLOC_BUFFER_STATIC: | case L2_OP_ALLOC_BUFFER_STATIC: | ||||
{ | { | ||||
word = alloc_val(vm); | 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].flags = L2_VAL_TYPE_BUFFER; | ||||
vm->values[word].buffer = malloc(sizeof(struct l2_vm_buffer) + length); | vm->values[word].buffer = malloc(sizeof(struct l2_vm_buffer) + length); | ||||
vm->values[word].buffer->len = length; | vm->values[word].buffer->len = length; | ||||
} | } | ||||
break; | 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: | 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); | l2_word arr_id = alloc_val(vm); | ||||
struct l2_vm_value *arr = &vm->values[arr_id]; | struct l2_vm_value *arr = &vm->values[arr_id]; | ||||
arr->flags = L2_VAL_TYPE_ARRAY; | arr->flags = L2_VAL_TYPE_ARRAY; | ||||
case L2_OP_ALLOC_FUNCTION: | case L2_OP_ALLOC_FUNCTION: | ||||
word = alloc_val(vm); | word = alloc_val(vm); | ||||
vm->values[word].flags = L2_VAL_TYPE_FUNCTION; | 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->stack[vm->sptr] = word; | ||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
case L2_OP_NAMESPACE_SET: | 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 val = vm->stack[vm->sptr - 1]; | ||||
l2_word ns = vm->stack[vm->sptr - 2]; | l2_word ns = vm->stack[vm->sptr - 2]; | ||||
l2_vm_namespace_set(&vm->values[ns], key, val); | l2_vm_namespace_set(&vm->values[ns], key, val); | ||||
case L2_OP_NAMESPACE_LOOKUP: | 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]; | l2_word ns = vm->stack[--vm->sptr]; | ||||
vm->stack[vm->sptr++] = l2_vm_namespace_get(vm, &vm->values[ns], key); | vm->stack[vm->sptr++] = l2_vm_namespace_get(vm, &vm->values[ns], key); | ||||
} | } | ||||
break; | 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]; | l2_word arr = vm->stack[--vm->sptr]; | ||||
// TODO: Error if out of bounds or incorrect type | // TODO: Error if out of bounds or incorrect type | ||||
vm->stack[vm->sptr++] = vm->values[arr].array->data[key]; | vm->stack[vm->sptr++] = vm->values[arr].array->data[key]; | ||||
} | } | ||||
break; | 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 val = vm->stack[vm->sptr - 1]; | ||||
l2_word arr = vm->stack[vm->sptr - 2]; | l2_word arr = vm->stack[vm->sptr - 2]; | ||||
// TODO: Error if out of bounds or incorrect type | // TODO: Error if out of bounds or incorrect type |
static struct l2_vm_value *var_lookup(const char *name) { | static struct l2_vm_value *var_lookup(const char *name) { | ||||
l2_word atom_id = l2_strset_get(&gen.atomset, 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]; | return &vm.values[id]; | ||||
} | } | ||||
l2_vm_init(&vm, (l2_word *)bytecode.mem, bytecode.len / sizeof(l2_word)); | l2_vm_init(&vm, (l2_word *)bytecode.mem, bytecode.len / sizeof(l2_word)); | ||||
vm.std_output = &output.w; | 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); | l2_vm_free(&vm); | ||||
free(bytecode.mem); | free(bytecode.mem); | ||||
#define check(name) do { \ | #define check(name) do { \ | ||||
snow_fail_update(); \ | snow_fail_update(); \ | ||||
check_impl(name); \ | |||||
test(name) { check_impl(name); } \ | |||||
} while (0) | } while (0) | ||||
describe(exaples) { | 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) { | if (error_message != NULL) { | ||||
free(error_message); | free(error_message); |