Browse Source

builtin stuff, and reorganized l2_vm_value

master
Martin Dørum 3 years ago
parent
commit
2bf2b4a88f
8 changed files with 136 additions and 71 deletions
  1. 5
    0
      include/lang2/builtins.x.h
  2. 6
    0
      include/lang2/vm/builtins.h
  3. 33
    15
      include/lang2/vm/vm.h
  4. 6
    0
      lib/gen/gen.c
  5. 8
    0
      lib/vm/builtins.c
  6. 5
    6
      lib/vm/namespace.c
  7. 21
    19
      lib/vm/print.c
  8. 52
    31
      lib/vm/vm.c

+ 5
- 0
include/lang2/builtins.x.h View File

// X macro: Define a macro named X, then include this file, then undef X.

#ifdef X
X("print", l2_builtin_print);
#endif

+ 6
- 0
include/lang2/vm/builtins.h View File

#include "vm.h"

#define X(name, f) \
l2_word f(struct l2_vm *vm, struct l2_vm_array *args);
#include "../builtins.x.h"
#undef X

+ 33
- 15
include/lang2/vm/vm.h View File

struct l2_vm_array; struct l2_vm_array;
typedef l2_word (*l2_vm_cfunction)(struct l2_vm *vm, struct l2_vm_array *args); typedef l2_word (*l2_vm_cfunction)(struct l2_vm *vm, struct l2_vm_array *args);


enum l2_value_type {
L2_VAL_TYPE_NONE,
L2_VAL_TYPE_INTEGER,
L2_VAL_TYPE_REAL,
L2_VAL_TYPE_BUFFER,
L2_VAL_TYPE_ARRAY,
L2_VAL_TYPE_NAMESPACE,
L2_VAL_TYPE_FUNCTION,
L2_VAL_TYPE_CFUNCTION,
};

enum l2_value_flags {
L2_VAL_MARKED = 1 << 6,
L2_VAL_CONST = 1 << 7,
};

// The smallest size an l2_vm_value can be is 16 bytes on common platforms.
// Pointers are 8 bytes, and since we need to store _at least_ 1 pointer +
// 1 byte for flags, it's going to be padded up to 16 bytes anyways.
// Might as well store some useful extra info in here.
struct l2_vm_value { struct l2_vm_value {
enum l2_value_flags {
L2_VAL_TYPE_NONE,
L2_VAL_TYPE_INTEGER,
L2_VAL_TYPE_REAL,
L2_VAL_TYPE_BUFFER,
L2_VAL_TYPE_ARRAY,
L2_VAL_TYPE_NAMESPACE,
L2_VAL_TYPE_FUNCTION,
L2_VAL_TYPE_CFUNCTION,
L2_VAL_MARKED = 1 << 7,
L2_VAL_CONST = 1 << 8,
} flags;
// Byte 0: 4 bytes
union {
l2_word ns_parent;
} extra;

// Byte 4: 1 byte, 3 bytes padding
uint8_t flags;

// Byte 8: 8 bytes
union { union {
int64_t integer; int64_t integer;
double real; double real;
struct l2_vm_buffer *buffer;
struct l2_vm_array *array;
struct l2_vm_namespace *ns;
struct { struct {
l2_word pos; l2_word pos;
l2_word namespace; l2_word namespace;
} func; } func;
l2_vm_cfunction cfunc; l2_vm_cfunction cfunc;
void *data;
}; };
}; };


#define l2_vm_value_type(val) ((val)->flags & 0x0f)
#define l2_vm_value_type(val) ((enum l2_value_type)((val)->flags & 0x0f))


struct l2_vm_buffer { struct l2_vm_buffer {
size_t len; size_t len;
}; };


struct l2_vm_namespace { struct l2_vm_namespace {
l2_word parent;
size_t len; size_t len;
size_t size; size_t size;
l2_word mask; l2_word mask;

+ 6
- 0
lib/gen/gen.c View File

gen->strings = NULL; gen->strings = NULL;
gen->pos = 0; gen->pos = 0;
l2_bufio_writer_init(&gen->writer, w); l2_bufio_writer_init(&gen->writer, w);

// Register atoms for all builtins
#define X(name, f) \
l2_strset_put_copy(&gen->atomset, name);
#include "builtins.x.h"
#undef X
} }


void l2_gen_flush(struct l2_generator *gen) { void l2_gen_flush(struct l2_generator *gen) {

+ 8
- 0
lib/vm/builtins.c View File

#include "vm/builtins.h"

#include <stdio.h>

l2_word l2_builtin_print(struct l2_vm *vm, struct l2_vm_array *args) {
printf("hey this is test\n");
return 0;
}

+ 5
- 6
lib/vm/namespace.c View File



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


l2_word l2_vm_namespace_get(struct l2_vm *vm, struct l2_vm_value *v, l2_word key) { l2_word l2_vm_namespace_get(struct l2_vm *vm, struct l2_vm_value *v, l2_word key) {
struct l2_vm_namespace *ns = v->data;
l2_word ret = get(ns, key);
if (ret == 0 && ns != NULL && ns->parent != 0) {
return l2_vm_namespace_get(vm, &vm->values[ns->parent], key);
l2_word ret = get(v->ns, key);
if (ret == 0 && v->extra.ns_parent != 0) {
return l2_vm_namespace_get(vm, &vm->values[v->extra.ns_parent], key);
} }


return ret; return ret;

+ 21
- 19
lib/vm/print.c View File

#include <string.h> #include <string.h>


void l2_vm_print_val(struct l2_vm_value *val) { void l2_vm_print_val(struct l2_vm_value *val) {
switch (val->flags & 0x0f) {

switch (l2_vm_value_type(val)) {
case L2_VAL_TYPE_NONE: case L2_VAL_TYPE_NONE:
printf("NONE\n"); printf("NONE\n");
break; break;


case L2_VAL_TYPE_ARRAY: case L2_VAL_TYPE_ARRAY:
{ {
if (val->data == NULL) {
if (val->array == NULL) {
printf("ARRAY, empty\n"); printf("ARRAY, empty\n");
return; 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) {
printf(" %zu: %u\n", i, arr->data[i]);
printf("ARRAY, len %zu\n", val->array->len);
for (size_t i = 0; i < val->array->len; ++i) {
printf(" %zu: %u\n", i, val->array->data[i]);
} }
} }
break; break;


case L2_VAL_TYPE_BUFFER: case L2_VAL_TYPE_BUFFER:
{ {
if (val->data == NULL) {
if (val->buffer == NULL) {
printf("BUFFER, empty\n"); printf("BUFFER, empty\n");
return; 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) {
printf(" %zu: %c\n", i, buf->data[i]);
printf("BUFFER, len %zu\n", val->buffer->len);
for (size_t i = 0; i < val->buffer->len; ++i) {
printf(" %zu: %c\n", i, val->buffer->data[i]);
} }
} }
break; break;


case L2_VAL_TYPE_NAMESPACE: case L2_VAL_TYPE_NAMESPACE:
{ {
if (val->data == NULL) {
printf("NAMESPACE, empty\n");
if (val->ns == NULL) {
printf("NAMESPACE, empty, parent %u\n", val->extra.ns_parent);
return; return;
} }


struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data;
printf("NAMESPACE, len %zu, parent %u\n", ns->len, ns->parent);
for (size_t i = 0; i < ns->size; ++i) {
l2_word key = ns->data[i];
l2_word val = ns->data[ns->size + i];
printf("NAMESPACE, len %zu, parent %u\n", val->ns->len, val->extra.ns_parent);
for (size_t i = 0; i < val->ns->size; ++i) {
l2_word key = val->ns->data[i];
l2_word v = val->ns->data[val->ns->size + i];
if (key == 0 || key == ~(l2_word)0) continue; if (key == 0 || key == ~(l2_word)0) continue;
printf(" %u: %u\n", key, val);
printf(" %u: %u\n", key, v);
} }
} }
break; break;
case L2_VAL_TYPE_FUNCTION: case L2_VAL_TYPE_FUNCTION:
printf("FUNCTION, pos %u, ns %u\n", val->func.pos, val->func.namespace); printf("FUNCTION, pos %u, ns %u\n", val->func.pos, val->func.namespace);
break; break;

case L2_VAL_TYPE_CFUNCTION:
printf("C FUNCTION, %p\n", val->cfunc);
break;
} }
} }



+ 52
- 31
lib/vm/vm.c View File

#include "vm/vm.h" #include "vm/vm.h"


#include <string.h> #include <string.h>

#include <stdio.h> #include <stdio.h>


#include "vm/builtins.h"

static l2_word alloc_val(struct l2_vm *vm) { static l2_word alloc_val(struct l2_vm *vm) {
size_t id = l2_bitset_set_next(&vm->valueset); size_t id = l2_bitset_set_next(&vm->valueset);
if (id >= vm->valuessize) { if (id >= vm->valuessize) {
} }


static void gc_mark_array(struct l2_vm *vm, struct l2_vm_value *val) { static void gc_mark_array(struct l2_vm *vm, struct l2_vm_value *val) {
if (val->data == NULL) {
if (val->array == NULL) {
return; 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]);
for (size_t i = 0; i < val->array->len; ++i) {
gc_mark(vm, val->array->data[i]);
} }
} }


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->data == NULL) {
if (val->ns == NULL) {
return; 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];
for (size_t i = 0; i < val->ns->size; ++i) {
l2_word key = val->ns->data[i];
if (key == 0 || key == ~(l2_word)0) { if (key == 0 || key == ~(l2_word)0) {
continue; continue;
} }


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


if (ns->parent != 0) {
gc_mark(vm, ns->parent);
if (val->extra.ns_parent != 0) {
gc_mark(vm, val->extra.ns_parent);
} }
} }


struct l2_vm_value *val = &vm->values[id]; struct l2_vm_value *val = &vm->values[id];
l2_bitset_unset(&vm->valueset, id); l2_bitset_unset(&vm->valueset, id);


// Don't need to do anything more; the next round of GC will free
// whichever values were only referenced by the array
int typ = l2_vm_value_type(val); int typ = l2_vm_value_type(val);
if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER || typ == L2_VAL_TYPE_NAMESPACE) { 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
free(val->array);
} else if (typ == L2_VAL_TYPE_BUFFER) {
free(val->buffer);
} else if (typ == L2_VAL_TYPE_NAMESPACE) {
free(val->ns);
} }
} }


l2_word none_id = alloc_val(vm); l2_word none_id = alloc_val(vm);
vm->values[none_id].flags = L2_VAL_TYPE_NONE | L2_VAL_CONST; vm->values[none_id].flags = L2_VAL_TYPE_NONE | L2_VAL_CONST;


// Need to allocate a builtins namespace
l2_word builtins = alloc_val(vm);
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;

// 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].ns = NULL;
vm->values[root].flags = L2_VAL_TYPE_NAMESPACE; vm->values[root].flags = L2_VAL_TYPE_NAMESPACE;
vm->values[root].data = NULL; // Will be allocated on first insert
vm->nstack[vm->nsptr] = root;
vm->nsptr += 1;
vm->nstack[vm->nsptr++] = root;

// Define a C function variable for every builtin
l2_word id;
l2_word key = 1;
#define X(name, f) \
id = alloc_val(vm); \
vm->values[id].flags = L2_VAL_TYPE_CFUNCTION; \
vm->values[id].cfunc = f; \
l2_vm_namespace_set(&vm->values[builtins], key++, id);
#include "builtins.x.h"
#undef X
} }


void l2_vm_free(struct l2_vm *vm) { void l2_vm_free(struct l2_vm *vm) {


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;
vm->values[arr_id].data = malloc(
vm->values[arr_id].array = malloc(
sizeof(struct l2_vm_array) + sizeof(l2_word) * argc); sizeof(struct l2_vm_array) + sizeof(l2_word) * argc);
struct l2_vm_array *arr = vm->values[arr_id].data;
struct l2_vm_array *arr = vm->values[arr_id].array;
arr->len = argc; arr->len = argc;
arr->size = argc; arr->size = argc;


arr->data[i] = vm->stack[vm->sptr + i]; arr->data[i] = vm->stack[vm->sptr + i];
} }


enum l2_value_flags typ = l2_vm_value_type(func);
enum l2_value_type typ = l2_vm_value_type(func);


// C functions are called differently from language functions // C functions are called differently from language functions
if (typ == L2_VAL_TYPE_CFUNCTION) { if (typ == L2_VAL_TYPE_CFUNCTION) {
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 = ns_id;
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->values[ns_id].data = calloc(1, sizeof(struct l2_vm_namespace));
struct l2_vm_namespace *ns = vm->values[ns_id].data;
ns->parent = func->func.namespace;
vm->nstack[vm->nsptr++] = ns_id; vm->nstack[vm->nsptr++] = ns_id;


vm->iptr = func->func.pos; vm->iptr = func->func.pos;
l2_word length = vm->stack[--vm->sptr]; l2_word length = vm->stack[--vm->sptr];
l2_word offset = vm->stack[--vm->sptr]; l2_word offset = vm->stack[--vm->sptr];
vm->values[word].flags = L2_VAL_TYPE_BUFFER; 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;
vm->values[word].buffer = malloc(sizeof(struct l2_vm_buffer) + length);
vm->values[word].buffer->len = length;
memcpy( memcpy(
(unsigned char *)vm->values[word].data + sizeof(struct l2_vm_buffer),
(unsigned char *)vm->values[word].buffer + sizeof(struct l2_vm_buffer),
vm->ops + offset, length); vm->ops + offset, length);
vm->stack[vm->sptr] = word; vm->stack[vm->sptr] = word;
vm->sptr += 1; vm->sptr += 1;
word = alloc_val(vm); word = alloc_val(vm);
l2_word length = vm->stack[--vm->sptr]; l2_word length = vm->stack[--vm->sptr];
vm->values[word].flags = L2_VAL_TYPE_BUFFER; 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->values[word].buffer = calloc(1, sizeof(struct l2_vm_buffer) + length);
vm->values[word].buffer->len = length;
vm->stack[vm->sptr] = word; vm->stack[vm->sptr] = word;
vm->sptr += 1; vm->sptr += 1;
} }
case L2_OP_ALLOC_ARRAY: case L2_OP_ALLOC_ARRAY:
word = alloc_val(vm); word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_ARRAY; vm->values[word].flags = L2_VAL_TYPE_ARRAY;
vm->values[word].data = NULL; // Will be allocated on first insert
vm->values[word].array = NULL; // Will be allocated on first insert
vm->stack[vm->sptr] = word; vm->stack[vm->sptr] = word;
vm->sptr += 1; vm->sptr += 1;
break; break;
case L2_OP_ALLOC_NAMESPACE: case L2_OP_ALLOC_NAMESPACE:
word = alloc_val(vm); word = alloc_val(vm);
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; vm->values[word].flags = L2_VAL_TYPE_NAMESPACE;
vm->values[word].data = NULL; // Will be allocated on first insert
vm->values[word].extra.ns_parent = 0;
vm->values[word].ns = NULL; // Will be allocated on first insert
vm->stack[vm->sptr] = word; vm->stack[vm->sptr] = word;
vm->sptr += 1; vm->sptr += 1;
break; break;

Loading…
Cancel
Save