Browse Source

namespace (int->int map)

master
Martin Dørum 3 years ago
parent
commit
f71e87c213
5 changed files with 292 additions and 31 deletions
  1. 102
    4
      include/lang2/bytecode.h
  2. 10
    2
      include/lang2/vm/vm.h
  3. 27
    4
      src/main.c
  4. 117
    0
      src/vm/namespace.c
  5. 36
    21
      src/vm/vm.c

+ 102
- 4
include/lang2/bytecode.h View File

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

enum l2_opcode {
/*
* Do nothing.
*/
L2_OP_NOP,

/*
* Push a value to the stack.
* Push <word>
*/
L2_OP_PUSH,

/*
* Discard the top element from the stack.
* Pop <word>
*/
L2_OP_POP,

/*
* Duplicate the top element on the stack.
* Push <word at <sptr> - 1>
*/
L2_OP_DUP,

/*
* Add two words.
* Pop <word1>
* Pop <word2>
* Push <word1> + <word2>
*/
L2_OP_ADD,
L2_OP_JUMP,

/*
* Call a function.
* Pop <word>
* Push <iptr> + 1
* Jump to <word>
*/
L2_OP_CALL,

/*
* Return from a function.
* Pop <word>
* Jump to <word>
*/
L2_OP_RET,

/*
* Allocate an integer from one word.
* Pop <word>
* Alloc integer <var> from <word>
* Push <var>
*/
L2_OP_ALLOC_INTEGER_32,

/*
* Allocate an integer from two words.
* Pop <word1>
* Pop <word2>
* Alloc integer <var> from <word1> << 32 | <word2>
* Push <var>
*/
L2_OP_ALLOC_INTEGER_64,

/*
* Allocate a real from one word.
* Pop <word>
* Alloc real <var> from <word>
* Push <var>
*/
L2_OP_ALLOC_REAL_32,

/*
* Allocate a real from two words.
* Pop <word1>
* Pop <word2>
* Alloc real <var> from <word1> << 32 | <word2>
* Push <var>
*/
L2_OP_ALLOC_REAL_64,
L2_OP_ALLOC_BUFFER,
L2_OP_ALLOC_BUFFER_CONST,

/*
* Allocate a buffer from static data.
* Pop <word1>
* Pop <word2>
* Alloc buffer <var> with length=<word1>, offset=<word2>
* 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.
* Alloc array <var>
* Push <var>
*/
L2_OP_ALLOC_ARRAY,
L2_OP_ALLOC_MAP,

/*
* Allocate an integer->value map.
* Alloc namespace <var>
* Push <var>
*/
L2_OP_ALLOC_NAMESPACE,

/*
* Halt execution.
*/
L2_OP_HALT,
};


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

@@ -13,7 +13,7 @@ struct l2_vm_value {
L2_VAL_TYPE_REAL,
L2_VAL_TYPE_BUFFER,
L2_VAL_TYPE_ARRAY,
L2_VAL_TYPE_MAP,
L2_VAL_TYPE_NAMESPACE,
L2_VAL_MARKED = 1 << 7,
L2_VAL_CONST = 1 << 8,
} flags;
@@ -35,12 +35,20 @@ struct l2_vm_array {
l2_word data[];
};

struct l2_vm_map {
void l2_vm_array_mark(struct l2_vm_value *arr);

struct l2_vm_namespace {
struct l2_vm_value *parent;
size_t len;
size_t size;
l2_word mask;
l2_word data[];
};

void l2_vm_namespace_mark(struct l2_vm_value *ns);
void l2_vm_namespace_set(struct l2_vm_value *ns, l2_word key, l2_word val);
l2_word l2_vm_namespace_get(struct l2_vm_value *ns, l2_word key);

struct l2_vm {
l2_word *ops;
size_t opcount;

+ 27
- 4
src/main.c View File

@@ -20,6 +20,11 @@ void print_var(struct l2_vm_value *val) {

case L2_VAL_TYPE_ARRAY:
{
if (val->data == NULL) {
printf("ARRAY, empty\n");
return;
}

struct l2_vm_array *arr = (struct l2_vm_array *)val->data;
printf("ARRAY, len %zu\n", arr->len);
for (size_t i = 0; i < arr->len; ++i) {
@@ -30,6 +35,11 @@ void print_var(struct l2_vm_value *val) {

case L2_VAL_TYPE_BUFFER:
{
if (val->data == NULL) {
printf("BUFFER, empty\n");
return;
}

struct l2_vm_buffer *buf = (struct l2_vm_buffer *)val->data;
printf("BUFFER, len %zu\n", buf->len);
for (size_t i = 0; i < buf->len; ++i) {
@@ -37,6 +47,18 @@ void print_var(struct l2_vm_value *val) {
}
}
break;

case L2_VAL_TYPE_NAMESPACE:
{
if (val->data == NULL) {
printf("NAMESPACE, empty\n");
return;
}

struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data;
printf("NAMESPACE, len %zu\n", ns->len);
}
break;
}
}

@@ -46,19 +68,20 @@ int main() {
L2_OP_PUSH, 100,
L2_OP_ADD,
L2_OP_ALLOC_INTEGER_32,
L2_OP_PUSH, 20 /* offset */,
L2_OP_PUSH, 21 /* offset */,
L2_OP_PUSH, 5 /* length */,
L2_OP_ALLOC_BUFFER_CONST,
L2_OP_ALLOC_BUFFER_STATIC,
L2_OP_POP,
L2_OP_PUSH, 16,
L2_OP_JUMP,
L2_OP_CALL,
L2_OP_HALT,
L2_OP_PUSH, 53,
L2_OP_ALLOC_INTEGER_32,
L2_OP_ALLOC_NAMESPACE,
L2_OP_HALT,
0, 0,
};
memcpy(&ops[20], "Hello", 5);
memcpy(&ops[21], "Hello", 5);

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

+ 117
- 0
src/vm/namespace.c View File

@@ -0,0 +1,117 @@
#include "vm/vm.h"

#include <stdio.h>

static const l2_word tombstone = ~(l2_word)0;

static struct l2_vm_namespace *set(struct l2_vm_namespace *ns, l2_word key, l2_word val);

static struct l2_vm_namespace *alloc(size_t size, l2_word mask) {
printf("alloc, size %zu, mask allows up to [%u]\n", size, mask);
struct l2_vm_namespace *ns = calloc(
1, sizeof(struct l2_vm_namespace) + sizeof(l2_word) * size * 2);
ns->size = size;
ns->mask = mask;
return ns;
}

static struct l2_vm_namespace *grow(struct l2_vm_namespace *ns) {
struct l2_vm_namespace *newns = alloc(ns->size * 2, (ns->mask << 1) | ns->mask);

for (size_t i = 0; i < ns->size; ++i) {
l2_word key = ns->data[i];
if (key == 0 || key == tombstone) {
continue;
}

l2_word val = ns->data[ns->size + i];
for (l2_word i = 0; ; ++i) {
l2_word hash = (key + i) & newns->mask;
if (newns->data[hash] == 0) {
newns->data[hash] = key;
newns->data[newns->size + hash] = val;
newns->len += 1;
break;
}
}
}

free(ns);
return newns;
}

static void del(struct l2_vm_namespace *ns, l2_word key) {
if (ns == NULL) {
return;
}

for (l2_word i = 0; ; ++i) {
l2_word hash = (key + i) & ns->mask;
l2_word k = ns->data[hash];
if (k == 0) {
return;
} else if (k == key) {
ns->data[hash] = tombstone;
return;
}
}
}

static struct l2_vm_namespace *set(struct l2_vm_namespace *ns, l2_word key, l2_word val) {
if (ns == NULL) {
ns = alloc(16, 0x0f);
printf("alloc to %zu\n", ns->size);
} else if (ns->len >= ns->size / 2) {
ns = grow(ns);
printf("grew to %zu\n", ns->size);
}

for (l2_word i = 0; ; ++i) {
l2_word hash = (key + i) & ns->mask;
l2_word k = ns->data[hash];
if (k == tombstone) {
ns->data[hash] = key;
ns->data[ns->size + hash] = val;
break;
} else if (k == key) {
ns->data[ns->size + hash] = val;
break;
} else if (k == 0) {
ns->len += 1;
ns->data[hash] = key;
ns->data[ns->size + hash] = val;
break;
}
}

return ns;
}

static l2_word get(struct l2_vm_namespace *ns, l2_word key) {
if (ns == NULL) {
return 0;
}

for (l2_word i = 0; ; ++i) {
l2_word hash = (key + i) & ns->mask;
l2_word k = ns->data[hash];
if (k == 0) {
return 0;
} else if (k == key) {
printf("found, %i collisions\n", i);
return ns->data[ns->size + hash];
}
}
}

void l2_vm_namespace_set(struct l2_vm_value *ns, l2_word key, l2_word val) {
if (val == 0) {
del(ns->data, key);
} else {
ns->data = set(ns->data, key, val);
}
}

l2_word l2_vm_namespace_get(struct l2_vm_value *ns, l2_word key) {
return get(ns->data, key);
}

+ 36
- 21
src/vm/vm.c View File

@@ -41,10 +41,28 @@ static void gc_mark(struct l2_vm *vm, l2_word id) {

int typ = val->flags & 0x0f;
if (typ == L2_VAL_TYPE_ARRAY) {
if (val->data == NULL) {
return;
}

struct l2_vm_array *arr = (struct l2_vm_array *)val->data;
for (size_t i = 0; i < arr->len; ++i) {
gc_mark(vm, arr->data[i]);
}
} else if (typ == L2_VAL_TYPE_NAMESPACE) {
if (val->data == NULL) {
return;
}

struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data;
for (size_t i = 0; i < ns->size; ++i) {
l2_word key = ns->data[i];
if (key == 0 || key == ~(l2_word)0) {
continue;
}

gc_mark(vm, ns->data[ns->size + i]);
}
}
}

@@ -54,7 +72,7 @@ static void gc_free(struct l2_vm *vm, l2_word id) {
l2_bitset_unset(&vm->valueset, id);

int typ = val->flags & 0x0f;
if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER) {
if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER || typ == L2_VAL_TYPE_NAMESPACE) {
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
@@ -157,12 +175,6 @@ void l2_vm_step(struct l2_vm *vm) {
vm->sptr -= 1;
break;

case L2_OP_JUMP:
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 - 1];
vm->stack[vm->sptr - 1] = vm->iptr + 1;
@@ -215,16 +227,7 @@ void l2_vm_step(struct l2_vm *vm) {
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:
case L2_OP_ALLOC_BUFFER_STATIC:
{
word = alloc_val(vm);
l2_word length = vm->stack[--vm->sptr];
@@ -241,19 +244,31 @@ 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].data = calloc(1, sizeof(struct l2_vm_buffer) + length);
((struct l2_vm_buffer *)vm->values[word].data)->len = length;
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
}

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->values[word].data = NULL; // Will be allocated on first insert
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;
break;

case L2_OP_ALLOC_MAP:
case L2_OP_ALLOC_NAMESPACE:
word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_MAP;
vm->values[word].data = calloc(1, sizeof(struct l2_vm_map));
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE;
vm->values[word].data = NULL; // Will be allocated on first insert
vm->stack[vm->sptr] = word;
vm->stackflags[vm->sptr] = 1;
vm->sptr += 1;

Loading…
Cancel
Save