Browse Source

VM progress

master
Martin Dørum 3 years ago
parent
commit
4bd819cda1
4 changed files with 270 additions and 73 deletions
  1. 9
    8
      include/lang2/bytecode.h
  2. 9
    4
      include/lang2/vm/vm.h
  3. 70
    32
      src/main.c
  4. 182
    29
      src/vm/vm.c

+ 9
- 8
include/lang2/bytecode.h View File

@@ -6,21 +6,22 @@
typedef uint32_t l2_word;

enum l2_opcode {
L2_OP_NOP,
L2_OP_PUSH,
L2_OP_POP,
L2_OP_DUP,
L2_OP_ADD,
L2_OP_JUMP,
L2_OP_CALL,
L2_OP_RET,
L2_OP_ALLOC_INTEGER,
L2_OP_ALLOC_REAL,
L2_OP_ALLOC_STRING,
L2_OP_ALLOC_INTEGER_32,
L2_OP_ALLOC_INTEGER_64,
L2_OP_ALLOC_REAL_32,
L2_OP_ALLOC_REAL_64,
L2_OP_ALLOC_ARRAY,
L2_OP_ALLOC_BUFFER,
L2_OP_ALLOC_BUFFER_CONST,
L2_OP_HALT,
};

struct l2_op {
enum l2_opcode code;
l2_word val;
};

#endif

+ 9
- 4
include/lang2/vm/vm.h View File

@@ -8,11 +8,13 @@

struct l2_vm_value {
enum l2_value_flags {
L2_VAL_TYPE_NONE,
L2_VAL_TYPE_INTEGER,
L2_VAL_TYPE_REAL,
L2_VAL_TYPE_STRING,
L2_VAL_TYPE_ARRAY,
L2_VAL_TYPE_BUFFER,
L2_VAL_MARKED = 1 << 7,
L2_VAL_CONST = 1 << 8,
} flags;
union {
int64_t integer;
@@ -21,7 +23,7 @@ struct l2_vm_value {
};
};

struct l2_vm_string {
struct l2_vm_buffer {
struct l2_vm_value val;
size_t len;
};
@@ -33,7 +35,7 @@ struct l2_vm_array {
};

struct l2_vm {
struct l2_op *ops;
l2_word *ops;
size_t opcount;

struct l2_vm_value *values;
@@ -41,11 +43,14 @@ struct l2_vm {
struct l2_bitset valueset;

l2_word stack[1024];
unsigned char stackflags[1024];
l2_word iptr;
l2_word sptr;
};

void l2_vm_init(struct l2_vm *vm, struct l2_op *ops, size_t opcount);
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount);
void l2_vm_step(struct l2_vm *vm);
void l2_vm_run(struct l2_vm *vm);
size_t l2_vm_gc(struct l2_vm *vm);

#endif

+ 70
- 32
src/main.c View File

@@ -2,53 +2,91 @@
#include "bitset.h"

#include <stdio.h>
#include <assert.h>
#include <string.h>

int main() {
struct l2_bitset bs;
l2_bitset_init(&bs);
void print_var(struct l2_vm_value *val) {
switch (val->flags & 0x0f) {
case L2_VAL_TYPE_NONE:
printf("NONE\n");
break;

for (size_t i = 0; i < 8191; ++i) {
size_t id = l2_bitset_set_next(&bs);
assert(id == i);
assert(l2_bitset_get(&bs, i));
}
case L2_VAL_TYPE_INTEGER:
printf("INTEGER %zi\n", val->integer);
break;

for (size_t i = 0; i < 10000; ++i) {
if (i < 8191) {
assert(l2_bitset_get(&bs, i));
} else {
assert(!l2_bitset_get(&bs, i));
}
}
case L2_VAL_TYPE_REAL:
printf("REAL %f\n", val->real);
break;

l2_bitset_unset(&bs, 100);
assert(l2_bitset_set_next(&bs) == 8191);
assert(l2_bitset_set_next(&bs) == 100);
case L2_VAL_TYPE_ARRAY:
{
struct l2_vm_array *arr = (struct l2_vm_array *)val->data;
l2_word *data = (l2_word *)((char *)arr + sizeof(struct l2_vm_array));
printf("ARRAY, len %zu\n", arr->len);
for (size_t i = 0; i < arr->len; ++i) {
printf(" %zu: %u\n", i, data[i]);
}
}
break;

l2_bitset_free(&bs);
case L2_VAL_TYPE_BUFFER:
{
struct l2_vm_buffer *buf = (struct l2_vm_buffer *)val->data;
char *data = (char *)buf + sizeof(struct l2_vm_buffer);
printf("BUFFER, len %zu\n", buf->len);
for (size_t i = 0; i < buf->len; ++i) {
printf(" %zu: %c\n", i, data[i]);
}
}
break;
}
}

/*
int main() {
struct l2_op ops[] = {
{ L2_OP_PUSH, 100 },
{ L2_OP_PUSH, 100 },
{ L2_OP_ADD },
{ L2_OP_HALT },
l2_word ops[] = {
L2_OP_PUSH, 100,
L2_OP_PUSH, 100,
L2_OP_ADD,
L2_OP_ALLOC_INTEGER_32,
L2_OP_PUSH, 20 /* offset */,
L2_OP_PUSH, 5 /* length */,
L2_OP_ALLOC_BUFFER_CONST,
L2_OP_POP,
L2_OP_PUSH, 16,
L2_OP_JUMP,
L2_OP_HALT,
L2_OP_PUSH, 53,
L2_OP_ALLOC_INTEGER_32,
L2_OP_HALT,
0, 0,
};
memcpy(&ops[20], "Hello", 5);

struct l2_vm vm;
l2_vm_init(&vm, ops, sizeof(ops) / sizeof(*ops));

while (vm.ops[vm.iptr].code != L2_OP_HALT) {
printf("Exec %i\n", vm.ops[vm.iptr].code);
l2_vm_step(&vm);
}
l2_vm_run(&vm);

printf("Done. Stack:\n");
printf("Stack:\n");
for (l2_word i = 0; i < vm.sptr; ++i) {
printf(" %i: %i\n", i, vm.stack[i]);
}

printf("Heap:\n");
for (l2_word i = 0; i < vm.valuessize; ++i) {
if (l2_bitset_get(&vm.valueset, i)) {
printf(" %u: ", i);
print_var(&vm.values[i]);
}
}

l2_vm_gc(&vm);

printf("Heap:\n");
for (l2_word i = 0; i < vm.valuessize; ++i) {
if (l2_bitset_get(&vm.valueset, i)) {
printf(" %u: ", i);
print_var(&vm.values[i]);
}
}
}
*/

+ 182
- 29
src/vm/vm.c View File

@@ -1,19 +1,12 @@
#include "vm/vm.h"

void l2_vm_init(struct l2_vm *vm, struct l2_op *ops, size_t opcount) {
vm->ops = ops;
vm->opcount = opcount;
vm->iptr = 0;
vm->sptr = 0;
#include <string.h>

vm->values = NULL;
vm->valuessize = 0;
l2_bitset_init(&vm->valueset);
}
#include <stdio.h>

static l2_word alloc_val(struct l2_vm *vm) {
size_t id = l2_bitset_set_next(&vm->valueset);
if (id > vm->valuessize) {
if (id >= vm->valuessize) {
if (vm->valuessize == 0) {
vm->valuessize = 16;
}
@@ -28,60 +21,220 @@ static l2_word alloc_val(struct l2_vm *vm) {
return (l2_word)id;
}

static float u32_to_float(uint32_t num) {
float f;
memcpy(&f, &num, sizeof(num));
return f;
}

static double u32s_to_double(uint32_t high, uint32_t low) {
double d;
uint64_t num = (uint64_t)high << 32 | (uint64_t)low;
memcpy(&d, &num, sizeof(num));
return d;
}

static void gc_mark(struct l2_vm *vm, l2_word id) {
printf("GC MARK %i\n", id);
struct l2_vm_value *val = &vm->values[id];
val->flags |= L2_VAL_MARKED;

int typ = val->flags & 0x0f;
if (typ == L2_VAL_TYPE_ARRAY) {
struct l2_vm_array *arr = (struct l2_vm_array *)val->data;
l2_word *ids = (l2_word *)((char *)arr + sizeof(struct l2_vm_array));
for (size_t i = 0; i < arr->len; ++i) {
gc_mark(vm, ids[i]);
}
}
}

static void gc_free(struct l2_vm *vm, l2_word id) {
printf("GC FREE %i\n", id);
struct l2_vm_value *val = &vm->values[id];
l2_bitset_unset(&vm->valueset, id);

int typ = val->flags & 0x0f;
if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER) {
free(val->data);
// Don't need to do anything more; the next round of GC will free
// whichever values were only referenced by the array
}
}

static size_t gc_sweep(struct l2_vm *vm) {
size_t freed = 0;
// Skip ID 0, because that should always exist
for (size_t i = 1; i < vm->valuessize; ++i) {
if (!l2_bitset_get(&vm->valueset, i)) {
continue;
}

struct l2_vm_value *val = &vm->values[i];
if (!(val->flags & L2_VAL_MARKED)) {
gc_free(vm, i);
freed += 1;
} else {
val->flags &= ~L2_VAL_MARKED;
}
}
return freed;
}

void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) {
vm->ops = ops;
vm->opcount = opcount;
vm->iptr = 0;
vm->sptr = 0;

vm->values = NULL;
vm->valuessize = 0;
l2_bitset_init(&vm->valueset);

// It's wasteful to allocate new 'none' variables all the time,
// variable ID 0 should be the only 'none' variable in the system
l2_word none_id = alloc_val(vm);
vm->values[none_id].flags = L2_VAL_TYPE_NONE | L2_VAL_CONST;
}

size_t l2_vm_gc(struct l2_vm *vm) {
for (l2_word sptr = 0; sptr < vm->sptr; ++sptr) {
if (vm->stackflags[sptr]) {
gc_mark(vm, vm->stack[sptr]);
}
}

return gc_sweep(vm);
}

void l2_vm_run(struct l2_vm *vm) {
while ((enum l2_opcode)vm->ops[vm->iptr] != L2_OP_HALT) {
l2_vm_step(vm);
}
}

void l2_vm_step(struct l2_vm *vm) {
struct l2_op op = vm->ops[vm->iptr++];
enum l2_opcode opcode = (enum l2_opcode)vm->ops[vm->iptr++];

l2_word word;
switch (op.code) {
switch (opcode) {
case L2_OP_NOP:
break;

case L2_OP_PUSH:
vm->stack[vm->sptr++] = op.val;
vm->stack[vm->sptr] = vm->ops[vm->iptr];
vm->stackflags[vm->sptr] = 0;
vm->sptr += 1;
vm->iptr += 1;
break;

case L2_OP_POP:
vm->sptr -= 1;
break;

case L2_OP_DUP:
vm->stack[vm->sptr] = vm->ops[vm->sptr - 1];
vm->stackflags[vm->sptr] = 0;
vm->sptr += 1;
break;

case L2_OP_ADD:
vm->stack[vm->sptr - 2] += vm->stack[vm->sptr - 1];
vm->stackflags[vm->sptr - 2] = 0;
vm->sptr -= 1;
vm->stack[vm->sptr - 1] += vm->stack[vm->sptr];
break;

case L2_OP_JUMP:
vm->iptr = vm->stack[--vm->sptr];
vm->iptr = vm->stack[vm->sptr - 1];
vm->stackflags[vm->sptr - 1] = 0;
vm->sptr -= 1;
break;

case L2_OP_CALL:
word = vm->stack[--vm->sptr];
vm->stack[vm->sptr++] = vm->iptr + 1;
word = vm->stack[vm->sptr - 1];
vm->stack[vm->sptr - 1] = vm->iptr + 1;
vm->stackflags[vm->sptr - 1] = 0;
vm->iptr = word;
break;

case L2_OP_RET:
vm->iptr = vm->stack[--vm->sptr];
vm->iptr = vm->stack[vm->sptr - 1];
vm->sptr -= 1;
break;

case L2_OP_ALLOC_INTEGER_32:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_INTEGER;
vm->values[word].integer = vm->stack[--vm->sptr];
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_INTEGER:
case L2_OP_ALLOC_INTEGER_64:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_INTEGER;
vm->values[word].integer = 0;
vm->stack[vm->sptr++] = word;
vm->values[word].integer = (int64_t)(
(uint64_t)vm->stack[vm->sptr - 1] << 32 |
(uint64_t)vm->stack[vm->sptr - 2]);
vm->sptr -= 2;
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_REAL:
case L2_OP_ALLOC_REAL_32:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_REAL;
vm->values[word].real = 0;
vm->stack[vm->sptr++] = word;
vm->values[word].real = u32_to_float(vm->stack[--vm->sptr]);
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_STRING:
case L2_OP_ALLOC_REAL_64:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_STRING;
vm->values[word].data = calloc(1, sizeof(struct l2_vm_string));
vm->stack[vm->sptr++] = word;
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->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_ARRAY:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_ARRAY;
vm->values[word].data = calloc(1, sizeof(struct l2_vm_array));
vm->stack[vm->sptr++] = word;
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_BUFFER:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_BUFFER;
vm->values[word].data = calloc(1, sizeof(struct l2_vm_buffer));
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_BUFFER_CONST:
{
word = alloc_val(vm);
l2_word length = vm->stack[--vm->sptr];
l2_word offset = vm->stack[--vm->sptr];
vm->values[word].flags = L2_VAL_TYPE_BUFFER;
vm->values[word].data = malloc(sizeof(struct l2_vm_buffer) + length);
((struct l2_vm_buffer *)vm->values[word].data)->len = length;
memcpy(
(unsigned char *)vm->values[word].data + sizeof(struct l2_vm_buffer),
vm->ops + offset, length);
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
}
break;

case L2_OP_HALT:

Loading…
Cancel
Save