next: *
This commit is contained in:
parent
2ac2f85512
commit
90175b7e26
12 changed files with 300 additions and 77 deletions
|
|
@ -45,6 +45,7 @@ growl = executable(
|
||||||
)
|
)
|
||||||
|
|
||||||
growlnext_sources = [
|
growlnext_sources = [
|
||||||
|
'next/core/alien.c',
|
||||||
'next/core/arena.c',
|
'next/core/arena.c',
|
||||||
'next/core/callable.c',
|
'next/core/callable.c',
|
||||||
'next/core/compiler.c',
|
'next/core/compiler.c',
|
||||||
|
|
@ -53,6 +54,7 @@ growlnext_sources = [
|
||||||
'next/core/sleb128.c',
|
'next/core/sleb128.c',
|
||||||
'next/core/string.c',
|
'next/core/string.c',
|
||||||
'next/core/tuple.c',
|
'next/core/tuple.c',
|
||||||
|
'next/core/value.c',
|
||||||
'next/core/vm.c',
|
'next/core/vm.c',
|
||||||
'next/main.c',
|
'next/main.c',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
23
next/core/alien.c
Normal file
23
next/core/alien.c
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <growl.h>
|
||||||
|
|
||||||
|
Growl growl_make_alien(GrowlVM *vm, GrowlAlienType *type, void *data) {
|
||||||
|
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlAlien);
|
||||||
|
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
||||||
|
hdr->type = GROWL_TYPE_ALIEN;
|
||||||
|
GrowlAlien *alien = (GrowlAlien *)(hdr + 1);
|
||||||
|
alien->type = type;
|
||||||
|
alien->data = data;
|
||||||
|
return GROWL_BOX(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type) {
|
||||||
|
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
||||||
|
return NULL;
|
||||||
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
|
if (hdr->type != GROWL_TYPE_ALIEN)
|
||||||
|
return NULL;
|
||||||
|
GrowlAlien *alien = (GrowlAlien *)(hdr + 1);
|
||||||
|
if (alien->type != type)
|
||||||
|
return NULL;
|
||||||
|
return alien;
|
||||||
|
}
|
||||||
|
|
@ -16,11 +16,11 @@ void growl_arena_free(GrowlGCArena *arena) {
|
||||||
|
|
||||||
void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align,
|
void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align,
|
||||||
size_t count) {
|
size_t count) {
|
||||||
ptrdiff_t padding = -(uintptr_t)arena->start & (align - 1);
|
ptrdiff_t padding = -(uintptr_t)arena->free & (align - 1);
|
||||||
ptrdiff_t available = arena->end - arena->start - padding;
|
ptrdiff_t available = arena->end - arena->free - padding;
|
||||||
if (available < 0 || count > available / size)
|
if (available < 0 || count > available / size)
|
||||||
abort();
|
abort();
|
||||||
void *p = arena->start + padding;
|
void *p = arena->free + padding;
|
||||||
arena->start += padding + count * size;
|
arena->free += padding + count * size;
|
||||||
return memset(p, 0, count * size);
|
return memset(p, 0, count * size);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ int growl_callable(Growl obj) {
|
||||||
return 0;
|
return 0;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
switch (hdr->type) {
|
switch (hdr->type) {
|
||||||
case GROWL_QUOTATION:
|
case GROWL_TYPE_QUOTATION:
|
||||||
case GROWL_COMPOSE:
|
case GROWL_TYPE_COMPOSE:
|
||||||
case GROWL_CURRY:
|
case GROWL_TYPE_CURRY:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -21,7 +21,7 @@ Growl growl_make_quotation(GrowlVM *vm, const uint8_t *code, size_t code_size,
|
||||||
constants_size * sizeof(Growl);
|
constants_size * sizeof(Growl);
|
||||||
GrowlObjectHeader *constants_hdr =
|
GrowlObjectHeader *constants_hdr =
|
||||||
growl_gc_alloc_tenured(vm, constants_obj_size);
|
growl_gc_alloc_tenured(vm, constants_obj_size);
|
||||||
constants_hdr->type = GROWL_TUPLE;
|
constants_hdr->type = GROWL_TYPE_TUPLE;
|
||||||
GrowlTuple *constants_tuple = (GrowlTuple *)(constants_hdr + 1);
|
GrowlTuple *constants_tuple = (GrowlTuple *)(constants_hdr + 1);
|
||||||
|
|
||||||
constants_tuple->count = constants_size;
|
constants_tuple->count = constants_size;
|
||||||
|
|
@ -33,7 +33,7 @@ Growl growl_make_quotation(GrowlVM *vm, const uint8_t *code, size_t code_size,
|
||||||
sizeof(GrowlObjectHeader) + sizeof(GrowlQuotation) + code_size;
|
sizeof(GrowlObjectHeader) + sizeof(GrowlQuotation) + code_size;
|
||||||
GrowlObjectHeader *quotation_hdr =
|
GrowlObjectHeader *quotation_hdr =
|
||||||
growl_gc_alloc_tenured(vm, quotation_obj_size);
|
growl_gc_alloc_tenured(vm, quotation_obj_size);
|
||||||
quotation_hdr->type = GROWL_QUOTATION;
|
quotation_hdr->type = GROWL_TYPE_QUOTATION;
|
||||||
GrowlQuotation *quotation = (GrowlQuotation *)(quotation_hdr + 1);
|
GrowlQuotation *quotation = (GrowlQuotation *)(quotation_hdr + 1);
|
||||||
|
|
||||||
quotation->constants = GROWL_BOX(constants_hdr);
|
quotation->constants = GROWL_BOX(constants_hdr);
|
||||||
|
|
@ -47,7 +47,7 @@ GrowlQuotation *growl_unwrap_quotation(Growl obj) {
|
||||||
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
if (hdr->type != GROWL_QUOTATION)
|
if (hdr->type != GROWL_TYPE_QUOTATION)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (GrowlQuotation *)(hdr + 1);
|
return (GrowlQuotation *)(hdr + 1);
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ Growl growl_compose(GrowlVM *vm, Growl first, Growl second) {
|
||||||
return GROWL_NIL;
|
return GROWL_NIL;
|
||||||
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCompose);
|
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCompose);
|
||||||
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
||||||
hdr->type = GROWL_COMPOSE;
|
hdr->type = GROWL_TYPE_COMPOSE;
|
||||||
GrowlCompose *comp = (GrowlCompose *)(hdr + 1);
|
GrowlCompose *comp = (GrowlCompose *)(hdr + 1);
|
||||||
comp->first = first;
|
comp->first = first;
|
||||||
comp->second = second;
|
comp->second = second;
|
||||||
|
|
@ -70,7 +70,7 @@ GrowlCompose *growl_unwrap_compose(Growl obj) {
|
||||||
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
if (hdr->type != GROWL_COMPOSE)
|
if (hdr->type != GROWL_TYPE_COMPOSE)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (GrowlCompose *)(hdr + 1);
|
return (GrowlCompose *)(hdr + 1);
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ Growl growl_curry(GrowlVM *vm, Growl value, Growl callable) {
|
||||||
return GROWL_NIL;
|
return GROWL_NIL;
|
||||||
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCurry);
|
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCurry);
|
||||||
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
||||||
hdr->type = GROWL_CURRY;
|
hdr->type = GROWL_TYPE_CURRY;
|
||||||
GrowlCurry *comp = (GrowlCurry *)(hdr + 1);
|
GrowlCurry *comp = (GrowlCurry *)(hdr + 1);
|
||||||
comp->value = value;
|
comp->value = value;
|
||||||
comp->callable = callable;
|
comp->callable = callable;
|
||||||
|
|
@ -91,7 +91,7 @@ GrowlCurry *growl_unwrap_curry(Growl obj) {
|
||||||
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
if (obj == GROWL_NIL || GROWL_IMM(obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
if (hdr->type != GROWL_CURRY)
|
if (hdr->type != GROWL_TYPE_CURRY)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (GrowlCurry *)(hdr + 1);
|
return (GrowlCurry *)(hdr + 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,38 +67,40 @@ GrowlObjectHeader *growl_gc_alloc_tenured(GrowlVM *vm, size_t size) {
|
||||||
|
|
||||||
static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) {
|
static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) {
|
||||||
switch (hdr->type) {
|
switch (hdr->type) {
|
||||||
case GROWL_STRING:
|
case GROWL_TYPE_STRING:
|
||||||
break;
|
break;
|
||||||
case GROWL_LIST: {
|
case GROWL_TYPE_LIST: {
|
||||||
GrowlList *list = (GrowlList *)(hdr + 1);
|
GrowlList *list = (GrowlList *)(hdr + 1);
|
||||||
list->head = forward(vm, list->head);
|
list->head = forward(vm, list->head);
|
||||||
list->tail = forward(vm, list->tail);
|
list->tail = forward(vm, list->tail);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GROWL_TUPLE: {
|
case GROWL_TYPE_TUPLE: {
|
||||||
GrowlTuple *tuple = (GrowlTuple *)(hdr + 1);
|
GrowlTuple *tuple = (GrowlTuple *)(hdr + 1);
|
||||||
for (size_t i = 0; i < tuple->count; ++i) {
|
for (size_t i = 0; i < tuple->count; ++i) {
|
||||||
tuple->data[i] = forward(vm, tuple->data[i]);
|
tuple->data[i] = forward(vm, tuple->data[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GROWL_QUOTATION: {
|
case GROWL_TYPE_QUOTATION: {
|
||||||
GrowlQuotation *quot = (GrowlQuotation *)(hdr + 1);
|
GrowlQuotation *quot = (GrowlQuotation *)(hdr + 1);
|
||||||
quot->constants = forward(vm, quot->constants);
|
quot->constants = forward(vm, quot->constants);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GROWL_COMPOSE: {
|
case GROWL_TYPE_COMPOSE: {
|
||||||
GrowlCompose *comp = (GrowlCompose *)(hdr + 1);
|
GrowlCompose *comp = (GrowlCompose *)(hdr + 1);
|
||||||
comp->first = forward(vm, comp->first);
|
comp->first = forward(vm, comp->first);
|
||||||
comp->second = forward(vm, comp->second);
|
comp->second = forward(vm, comp->second);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GROWL_CURRY: {
|
case GROWL_TYPE_CURRY: {
|
||||||
GrowlCurry *comp = (GrowlCurry *)(hdr + 1);
|
GrowlCurry *comp = (GrowlCurry *)(hdr + 1);
|
||||||
comp->value = forward(vm, comp->value);
|
comp->value = forward(vm, comp->value);
|
||||||
comp->callable = forward(vm, comp->callable);
|
comp->callable = forward(vm, comp->callable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case GROWL_TYPE_ALIEN:
|
||||||
|
break;
|
||||||
case UINT32_MAX:
|
case UINT32_MAX:
|
||||||
fprintf(stderr, "gc: fwd pointer during scan\n");
|
fprintf(stderr, "gc: fwd pointer during scan\n");
|
||||||
abort();
|
abort();
|
||||||
|
|
@ -109,15 +111,22 @@ static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gc_print_stats(GrowlVM *vm, const char *label) {
|
static void gc_print_stats(GrowlVM *vm, const char *label) {
|
||||||
size_t used = vm->from.free - vm->from.start;
|
size_t nursery_used = vm->from.free - vm->from.start;
|
||||||
size_t total = vm->from.end - vm->from.start;
|
size_t nursery_total = vm->from.end - vm->from.start;
|
||||||
fprintf(stderr, "[%s] used=%zu/%zu bytes (%.1f%%)\n", label, used, total,
|
size_t tenured_used = vm->arena.free - vm->arena.start;
|
||||||
(double)used / (double)total * 100.0);
|
size_t tenured_total = vm->arena.end - vm->arena.start;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s:\n", label);
|
||||||
|
fprintf(stderr, " tenured: %zu/%zu bytes (%.1f%%)\n", tenured_used,
|
||||||
|
nursery_total, (double)tenured_used / (double)tenured_total * 100.0);
|
||||||
|
fprintf(stderr, " nursery: %zu/%zu bytes (%.1f%%)\n", nursery_used,
|
||||||
|
nursery_total, (double)nursery_used / (double)nursery_total * 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void growl_gc_collect(GrowlVM *vm) {
|
void growl_gc_collect(GrowlVM *vm) {
|
||||||
uint8_t *gc_scan = vm->to.free;
|
uint8_t *gc_scan = vm->to.free;
|
||||||
|
|
||||||
|
fprintf(stderr, ">>> starting garbage collection\n");
|
||||||
gc_print_stats(vm, "before GC");
|
gc_print_stats(vm, "before GC");
|
||||||
|
|
||||||
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) {
|
for (size_t i = 0; i < GROWL_STACK_SIZE; ++i) {
|
||||||
|
|
@ -141,6 +150,26 @@ void growl_gc_collect(GrowlVM *vm) {
|
||||||
gc_scan += ALIGN(hdr->size);
|
gc_scan += ALIGN(hdr->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gc_scan = vm->from.start;
|
||||||
|
while (gc_scan < vm->from.free) {
|
||||||
|
GrowlObjectHeader *hdr = (GrowlObjectHeader *)gc_scan;
|
||||||
|
if (hdr->type != UINT32_MAX) {
|
||||||
|
switch (hdr->type) {
|
||||||
|
case GROWL_TYPE_ALIEN: {
|
||||||
|
GrowlAlien *alien = (GrowlAlien *)(hdr + 1);
|
||||||
|
if (alien->type->finalizer != NULL) {
|
||||||
|
alien->type->finalizer(alien->data);
|
||||||
|
alien->data = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gc_scan += ALIGN(hdr->size);
|
||||||
|
}
|
||||||
|
|
||||||
GrowlGCArena tmp = vm->from;
|
GrowlGCArena tmp = vm->from;
|
||||||
vm->from = vm->to;
|
vm->from = vm->to;
|
||||||
vm->to = tmp;
|
vm->to = tmp;
|
||||||
|
|
@ -148,6 +177,7 @@ void growl_gc_collect(GrowlVM *vm) {
|
||||||
vm->scratch.free = vm->scratch.start;
|
vm->scratch.free = vm->scratch.start;
|
||||||
|
|
||||||
gc_print_stats(vm, "after GC");
|
gc_print_stats(vm, "after GC");
|
||||||
|
fprintf(stderr, ">>> garbage collection finished\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void growl_gc_root(GrowlVM *vm, Growl *ptr) {
|
void growl_gc_root(GrowlVM *vm, Growl *ptr) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,25 @@
|
||||||
#ifndef GROWL_OPCODES_H
|
#ifndef GROWL_OPCODES_H
|
||||||
#define GROWL_OPCODES_H
|
#define GROWL_OPCODES_H
|
||||||
|
|
||||||
enum {
|
enum GrowlOpcode {
|
||||||
GOP_NOP = 0,
|
GOP_NOP = 0,
|
||||||
GOP_PUSH_NIL,
|
GOP_PUSH_NIL,
|
||||||
GOP_PUSH_CONSTANT,
|
GOP_PUSH_CONSTANT,
|
||||||
|
GOP_DROP,
|
||||||
|
GOP_DUP,
|
||||||
|
GOP_SWAP,
|
||||||
|
GOP_2DROP,
|
||||||
|
GOP_2DUP,
|
||||||
|
GOP_2SWAP,
|
||||||
|
GOP_NIP,
|
||||||
|
GOP_OVER,
|
||||||
|
GOP_BURY,
|
||||||
|
GOP_DIG,
|
||||||
|
GOP_TO_RETAIN,
|
||||||
|
GOP_FROM_RETAIN,
|
||||||
GOP_CALL,
|
GOP_CALL,
|
||||||
|
GOP_CALL_NEXT,
|
||||||
|
GOP_TAIL_CALL,
|
||||||
GOP_RETURN,
|
GOP_RETURN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ intptr_t growl_sleb128_decode(uint8_t **ptr) {
|
||||||
shift += 7;
|
shift += 7;
|
||||||
} while (byte & 0x80);
|
} while (byte & 0x80);
|
||||||
|
|
||||||
if ((shift < 64) && (byte & 0x40)) {
|
if (shift < 64 && byte & 0x40) {
|
||||||
result |= -(1LL << shift);
|
result |= -(1LL << shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
Growl growl_make_string(GrowlVM *vm, size_t len) {
|
Growl growl_make_string(GrowlVM *vm, size_t len) {
|
||||||
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len;
|
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len;
|
||||||
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
||||||
hdr->type = GROWL_STRING;
|
hdr->type = GROWL_TYPE_STRING;
|
||||||
GrowlString *str = (GrowlString *)(hdr + 1);
|
GrowlString *str = (GrowlString *)(hdr + 1);
|
||||||
str->len = len;
|
str->len = len;
|
||||||
memset(str->data, 0, len);
|
memset(str->data, 0, len);
|
||||||
|
|
@ -15,7 +15,7 @@ Growl growl_wrap_string(GrowlVM *vm, const char *cstr) {
|
||||||
size_t len = strlen(cstr);
|
size_t len = strlen(cstr);
|
||||||
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len + 1;
|
size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len + 1;
|
||||||
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
GrowlObjectHeader *hdr = growl_gc_alloc(vm, size);
|
||||||
hdr->type = GROWL_STRING;
|
hdr->type = GROWL_TYPE_STRING;
|
||||||
GrowlString *str = (GrowlString *)(hdr + 1);
|
GrowlString *str = (GrowlString *)(hdr + 1);
|
||||||
str->len = len;
|
str->len = len;
|
||||||
memcpy(str->data, cstr, len);
|
memcpy(str->data, cstr, len);
|
||||||
|
|
@ -27,7 +27,7 @@ GrowlString *growl_unwrap_string(Growl obj) {
|
||||||
if (obj == 0 || GROWL_IMM(obj))
|
if (obj == 0 || GROWL_IMM(obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
if (hdr->type != GROWL_STRING)
|
if (hdr->type != GROWL_TYPE_STRING)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (GrowlString *)(hdr + 1);
|
return (GrowlString *)(hdr + 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ GrowlTuple *growl_unwrap_tuple(Growl obj) {
|
||||||
if (obj == 0 || GROWL_IMM(obj))
|
if (obj == 0 || GROWL_IMM(obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
if (hdr->type != GROWL_TUPLE)
|
if (hdr->type != GROWL_TYPE_TUPLE)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (GrowlTuple *)(hdr + 1);
|
return (GrowlTuple *)(hdr + 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
next/core/value.c
Normal file
11
next/core/value.c
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <growl.h>
|
||||||
|
|
||||||
|
uint32_t growl_type(Growl obj) {
|
||||||
|
if (obj == GROWL_NIL)
|
||||||
|
return GROWL_TYPE_NIL;
|
||||||
|
if (GROWL_IMM(obj))
|
||||||
|
return GROWL_TYPE_NUMBER;
|
||||||
|
GrowlObjectHeader *hdr = GROWL_UNBOX(obj);
|
||||||
|
return hdr->type;
|
||||||
|
}
|
||||||
|
|
||||||
201
next/core/vm.c
201
next/core/vm.c
|
|
@ -33,6 +33,8 @@ GrowlVM *growl_vm_init(void) {
|
||||||
mem->root_count = 0;
|
mem->root_count = 0;
|
||||||
mem->root_capacity = 0;
|
mem->root_capacity = 0;
|
||||||
|
|
||||||
|
// TODO: initialize compose trampoline
|
||||||
|
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +65,12 @@ void growl_push(GrowlVM *vm, Growl obj) {
|
||||||
*vm->sp++ = obj;
|
*vm->sp++ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Growl growl_peek(GrowlVM *vm, size_t depth) {
|
||||||
|
if (vm->sp <= vm->wst + depth)
|
||||||
|
vm_error(vm, "work stack underflow");
|
||||||
|
return vm->sp[-(depth + 1)];
|
||||||
|
}
|
||||||
|
|
||||||
Growl growl_pop(GrowlVM *vm) {
|
Growl growl_pop(GrowlVM *vm) {
|
||||||
if (vm->sp <= vm->wst)
|
if (vm->sp <= vm->wst)
|
||||||
vm_error(vm, "work stack underflow");
|
vm_error(vm, "work stack underflow");
|
||||||
|
|
@ -85,19 +93,47 @@ Growl growl_rpop(GrowlVM *vm) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void push_call(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) {
|
static void callstack_push(GrowlVM *vm, GrowlQuotation *q, uint8_t *ip) {
|
||||||
if (vm->csp >= vm->cst + GROWL_CALL_STACK_SIZE)
|
if (vm->csp >= vm->cst + GROWL_CALL_STACK_SIZE)
|
||||||
vm_error(vm, "call stack overflow");
|
vm_error(vm, "call stack overflow");
|
||||||
vm->csp->quot = q;
|
vm->csp->quot = q;
|
||||||
vm->csp->ip = ip;
|
vm->csp->ip = ip;
|
||||||
vm->csp++;
|
vm->csp++;
|
||||||
}
|
}
|
||||||
static GrowlFrame pop_call(GrowlVM *vm) {
|
|
||||||
|
static GrowlFrame callstack_pop(GrowlVM *vm) {
|
||||||
if (vm->csp <= vm->cst)
|
if (vm->csp <= vm->cst)
|
||||||
vm_error(vm, "call stack underflow");
|
vm_error(vm, "call stack underflow");
|
||||||
return *--vm->csp;
|
return *--vm->csp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void dispatch(GrowlVM *vm, Growl obj) {
|
||||||
|
for (;;) {
|
||||||
|
switch (growl_type(obj)) {
|
||||||
|
case GROWL_TYPE_QUOTATION: {
|
||||||
|
GrowlQuotation *q = (GrowlQuotation *)(GROWL_UNBOX(obj) + 1);
|
||||||
|
vm->quotation = q;
|
||||||
|
vm->ip = q->data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case GROWL_TYPE_COMPOSE: {
|
||||||
|
GrowlCompose *c = (GrowlCompose *)(GROWL_UNBOX(obj) + 1);
|
||||||
|
callstack_push(vm, vm->compose_trampoline, vm->compose_trampoline->data);
|
||||||
|
vm->csp[-1].next = c->second;
|
||||||
|
obj = c->first;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case GROWL_TYPE_CURRY: {
|
||||||
|
GrowlCurry *c = (GrowlCurry *)(GROWL_UNBOX(obj) + 1);
|
||||||
|
growl_push(vm, c->value);
|
||||||
|
obj = c->callable;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
vm_error(vm, "attempt to call non-callable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
int vm_doquot(GrowlVM *vm, GrowlQuotation *quot) {
|
int vm_doquot(GrowlVM *vm, GrowlQuotation *quot) {
|
||||||
size_t gc_mark = growl_gc_mark(vm);
|
size_t gc_mark = growl_gc_mark(vm);
|
||||||
int result = setjmp(vm->error);
|
int result = setjmp(vm->error);
|
||||||
|
|
@ -117,46 +153,129 @@ int vm_doquot(GrowlVM *vm, GrowlQuotation *quot) {
|
||||||
vm->ip = quot->data;
|
vm->ip = quot->data;
|
||||||
vm->quotation = quot;
|
vm->quotation = quot;
|
||||||
|
|
||||||
for (;;) {
|
// clang-format off
|
||||||
uint8_t opcode;
|
#define VM_START() for (;;) { uint8_t opcode; switch(opcode = *vm->ip++) {
|
||||||
switch (opcode = *vm->ip++) {
|
#define VM_END() }}
|
||||||
case GOP_NOP:
|
#define VM_DEFAULT() default:
|
||||||
break;
|
#define VM_OP(op) case GOP_## op:
|
||||||
case GOP_PUSH_NIL:
|
#define VM_NEXT() break
|
||||||
growl_push(vm, GROWL_NIL);
|
// clang-format on
|
||||||
break;
|
|
||||||
case GOP_PUSH_CONSTANT: {
|
VM_START()
|
||||||
intptr_t idx = growl_sleb128_decode(&vm->ip);
|
VM_OP(NOP) VM_NEXT();
|
||||||
if (constants != NULL) {
|
VM_OP(PUSH_NIL) {
|
||||||
growl_push(vm, constants->data[idx]);
|
growl_push(vm, GROWL_NIL);
|
||||||
} else {
|
VM_NEXT();
|
||||||
vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GOP_CALL: { // TODO: compose and curry
|
|
||||||
Growl obj = growl_pop(vm);
|
|
||||||
push_call(vm, vm->quotation, vm->ip);
|
|
||||||
GrowlQuotation *obj_quot = growl_unwrap_quotation(obj);
|
|
||||||
if (obj_quot == NULL)
|
|
||||||
vm_error(vm, "attempt to call non-callable");
|
|
||||||
vm->quotation = obj_quot;
|
|
||||||
vm->ip = obj_quot->data;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GOP_RETURN:
|
|
||||||
if (vm->csp != vm->cst) {
|
|
||||||
GrowlFrame frame = pop_call(vm);
|
|
||||||
vm->quotation = frame.quot;
|
|
||||||
vm->ip = frame.ip;
|
|
||||||
} else {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
vm_error(vm, "unknown opcode %d", opcode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
VM_OP(PUSH_CONSTANT) {
|
||||||
|
intptr_t idx = growl_sleb128_decode(&vm->ip);
|
||||||
|
if (constants != NULL) {
|
||||||
|
growl_push(vm, constants->data[idx]);
|
||||||
|
} else {
|
||||||
|
vm_error(vm, "constant index %" PRIdPTR " out of bounds", idx);
|
||||||
|
}
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(DROP) {
|
||||||
|
(void)growl_pop(vm);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(DUP) {
|
||||||
|
growl_push(vm, growl_peek(vm, 0));
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(SWAP) {
|
||||||
|
Growl b = growl_pop(vm);
|
||||||
|
Growl a = growl_pop(vm);
|
||||||
|
growl_push(vm, b);
|
||||||
|
growl_push(vm, a);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(2DROP) {
|
||||||
|
(void)growl_pop(vm);
|
||||||
|
(void)growl_pop(vm);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(2DUP) {
|
||||||
|
growl_push(vm, growl_peek(vm, 1));
|
||||||
|
growl_push(vm, growl_peek(vm, 1));
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(2SWAP) {
|
||||||
|
Growl d = growl_pop(vm);
|
||||||
|
Growl c = growl_pop(vm);
|
||||||
|
Growl b = growl_pop(vm);
|
||||||
|
Growl a = growl_pop(vm);
|
||||||
|
growl_push(vm, c);
|
||||||
|
growl_push(vm, d);
|
||||||
|
growl_push(vm, a);
|
||||||
|
growl_push(vm, b);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(NIP) {
|
||||||
|
Growl b = growl_pop(vm);
|
||||||
|
(void)growl_pop(vm);
|
||||||
|
growl_push(vm, b);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(OVER) {
|
||||||
|
growl_push(vm, growl_peek(vm, 1));
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(BURY) {
|
||||||
|
Growl c = growl_pop(vm);
|
||||||
|
Growl b = growl_pop(vm);
|
||||||
|
Growl a = growl_pop(vm);
|
||||||
|
growl_push(vm, c);
|
||||||
|
growl_push(vm, a);
|
||||||
|
growl_push(vm, b);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(TO_RETAIN) {
|
||||||
|
growl_rpush(vm, growl_pop(vm));
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(FROM_RETAIN) {
|
||||||
|
growl_push(vm, growl_rpop(vm));
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(DIG) {
|
||||||
|
Growl c = growl_pop(vm);
|
||||||
|
Growl b = growl_pop(vm);
|
||||||
|
Growl a = growl_pop(vm);
|
||||||
|
growl_push(vm, b);
|
||||||
|
growl_push(vm, c);
|
||||||
|
growl_push(vm, a);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(CALL) { // TODO: compose and curry
|
||||||
|
Growl obj = growl_pop(vm);
|
||||||
|
callstack_push(vm, vm->quotation, vm->ip);
|
||||||
|
dispatch(vm, obj);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(CALL_NEXT) {
|
||||||
|
growl_push(vm, vm->next);
|
||||||
|
vm->next = GROWL_NIL;
|
||||||
|
__attribute__((__fallthrough__));
|
||||||
|
}
|
||||||
|
VM_OP(TAIL_CALL) {
|
||||||
|
Growl obj = growl_pop(vm);
|
||||||
|
dispatch(vm, obj);
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_OP(RETURN) {
|
||||||
|
if (vm->csp != vm->cst) {
|
||||||
|
GrowlFrame frame = callstack_pop(vm);
|
||||||
|
vm->quotation = frame.quot;
|
||||||
|
vm->ip = frame.ip;
|
||||||
|
} else {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
VM_NEXT();
|
||||||
|
}
|
||||||
|
VM_DEFAULT() { vm_error(vm, "unknown opcode %d", opcode); }
|
||||||
|
VM_END()
|
||||||
|
|
||||||
done:
|
done:
|
||||||
growl_gc_reset(vm, gc_mark);
|
growl_gc_reset(vm, gc_mark);
|
||||||
|
|
|
||||||
|
|
@ -21,19 +21,26 @@ typedef struct GrowlTuple GrowlTuple;
|
||||||
typedef struct GrowlQuotation GrowlQuotation;
|
typedef struct GrowlQuotation GrowlQuotation;
|
||||||
typedef struct GrowlCompose GrowlCompose;
|
typedef struct GrowlCompose GrowlCompose;
|
||||||
typedef struct GrowlCurry GrowlCurry;
|
typedef struct GrowlCurry GrowlCurry;
|
||||||
|
typedef struct GrowlAlienType GrowlAlienType;
|
||||||
|
typedef struct GrowlAlien GrowlAlien;
|
||||||
typedef struct GrowlGCArena GrowlGCArena;
|
typedef struct GrowlGCArena GrowlGCArena;
|
||||||
typedef struct GrowlFrame GrowlFrame;
|
typedef struct GrowlFrame GrowlFrame;
|
||||||
typedef struct GrowlVM GrowlVM;
|
typedef struct GrowlVM GrowlVM;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GROWL_STRING,
|
GROWL_TYPE_NIL,
|
||||||
GROWL_LIST,
|
GROWL_TYPE_NUMBER,
|
||||||
GROWL_TUPLE,
|
GROWL_TYPE_STRING,
|
||||||
GROWL_QUOTATION,
|
GROWL_TYPE_LIST,
|
||||||
GROWL_COMPOSE,
|
GROWL_TYPE_TUPLE,
|
||||||
GROWL_CURRY,
|
GROWL_TYPE_QUOTATION,
|
||||||
|
GROWL_TYPE_COMPOSE,
|
||||||
|
GROWL_TYPE_CURRY,
|
||||||
|
GROWL_TYPE_ALIEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint32_t growl_type(Growl obj);
|
||||||
|
|
||||||
struct GrowlObjectHeader {
|
struct GrowlObjectHeader {
|
||||||
size_t size;
|
size_t size;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
|
|
@ -82,6 +89,19 @@ GrowlCompose *growl_unwrap_compose(Growl obj);
|
||||||
Growl growl_curry(GrowlVM *vm, Growl value, Growl callable);
|
Growl growl_curry(GrowlVM *vm, Growl value, Growl callable);
|
||||||
GrowlCurry *growl_unwrap_curry(Growl obj);
|
GrowlCurry *growl_unwrap_curry(Growl obj);
|
||||||
|
|
||||||
|
struct GrowlAlienType {
|
||||||
|
const char *name;
|
||||||
|
void (*finalizer)(void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrowlAlien {
|
||||||
|
GrowlAlienType *type;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
Growl growl_make_alien(GrowlVM *vm, GrowlAlienType *type, void *data);
|
||||||
|
GrowlAlien *growl_unwrap_alien(Growl obj, GrowlAlienType *type);
|
||||||
|
|
||||||
struct GrowlGCArena {
|
struct GrowlGCArena {
|
||||||
uint8_t *start, *end;
|
uint8_t *start, *end;
|
||||||
uint8_t *free;
|
uint8_t *free;
|
||||||
|
|
@ -103,6 +123,7 @@ void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align,
|
||||||
struct GrowlFrame {
|
struct GrowlFrame {
|
||||||
GrowlQuotation *quot;
|
GrowlQuotation *quot;
|
||||||
uint8_t *ip;
|
uint8_t *ip;
|
||||||
|
Growl next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GrowlVM {
|
struct GrowlVM {
|
||||||
|
|
@ -116,6 +137,9 @@ struct GrowlVM {
|
||||||
Growl rst[GROWL_STACK_SIZE], *rsp;
|
Growl rst[GROWL_STACK_SIZE], *rsp;
|
||||||
GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp;
|
GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp;
|
||||||
|
|
||||||
|
GrowlQuotation *compose_trampoline;
|
||||||
|
Growl next;
|
||||||
|
|
||||||
Growl **roots;
|
Growl **roots;
|
||||||
size_t root_count, root_capacity;
|
size_t root_count, root_capacity;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue