Browse Source

rework bytecode and stacks

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
Martin Dørum 3 years ago
parent
commit
82c92b26b9
10 changed files with 179 additions and 243 deletions
  1. 35
    67
      include/lang2/bytecode.h
  2. 4
    4
      include/lang2/gen/gen.h
  3. 1
    1
      include/lang2/vm/print.h
  4. 7
    2
      include/lang2/vm/vm.h
  5. 26
    41
      lib/gen/gen.c
  6. 8
    10
      lib/parse/parse.c
  7. 33
    39
      lib/vm/print.c
  8. 55
    70
      lib/vm/vm.c
  9. 1
    1
      test/src/eval.t.c
  10. 9
    8
      test/src/examples.t.c

+ 35
- 67
include/lang2/bytecode.h View File

*/ */
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.

+ 4
- 4
include/lang2/gen/gen.h View File

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

+ 1
- 1
include/lang2/vm/print.h View File

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);

+ 7
- 2
include/lang2/vm/vm.h View File

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);

+ 26
- 41
lib/gen/gen.c View File

} }


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);
} }

+ 8
- 10
lib/parse/parse.c View File



// 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;
} }

+ 33
- 39
lib/vm/print.c View File

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:

+ 55
- 70
lib/vm/vm.c View File

} }


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

+ 1
- 1
test/src/eval.t.c View File



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];
} }



+ 9
- 8
test/src/examples.t.c View File

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);

Loading…
Cancel
Save