From 90175b7e266bbc00d40a0e3d39cb3e16b08706f0 Mon Sep 17 00:00:00 2001 From: "Javier B. Torres" Date: Fri, 6 Feb 2026 11:52:53 -0300 Subject: [PATCH] next: * --- meson.build | 2 + next/core/alien.c | 23 +++++ next/core/arena.c | 8 +- next/core/callable.c | 20 ++--- next/core/gc.c | 50 ++++++++--- next/core/opcodes.h | 16 +++- next/core/sleb128.c | 2 +- next/core/string.c | 6 +- next/core/tuple.c | 2 +- next/core/value.c | 11 +++ next/core/vm.c | 201 ++++++++++++++++++++++++++++++++++--------- next/include/growl.h | 36 ++++++-- 12 files changed, 300 insertions(+), 77 deletions(-) create mode 100644 next/core/alien.c create mode 100644 next/core/value.c diff --git a/meson.build b/meson.build index 90cb202..89491b3 100644 --- a/meson.build +++ b/meson.build @@ -45,6 +45,7 @@ growl = executable( ) growlnext_sources = [ + 'next/core/alien.c', 'next/core/arena.c', 'next/core/callable.c', 'next/core/compiler.c', @@ -53,6 +54,7 @@ growlnext_sources = [ 'next/core/sleb128.c', 'next/core/string.c', 'next/core/tuple.c', + 'next/core/value.c', 'next/core/vm.c', 'next/main.c', ] diff --git a/next/core/alien.c b/next/core/alien.c new file mode 100644 index 0000000..3281c6f --- /dev/null +++ b/next/core/alien.c @@ -0,0 +1,23 @@ +#include + +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; +} diff --git a/next/core/arena.c b/next/core/arena.c index c7b6247..f0359cb 100644 --- a/next/core/arena.c +++ b/next/core/arena.c @@ -16,11 +16,11 @@ void growl_arena_free(GrowlGCArena *arena) { void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align, size_t count) { - ptrdiff_t padding = -(uintptr_t)arena->start & (align - 1); - ptrdiff_t available = arena->end - arena->start - padding; + ptrdiff_t padding = -(uintptr_t)arena->free & (align - 1); + ptrdiff_t available = arena->end - arena->free - padding; if (available < 0 || count > available / size) abort(); - void *p = arena->start + padding; - arena->start += padding + count * size; + void *p = arena->free + padding; + arena->free += padding + count * size; return memset(p, 0, count * size); } diff --git a/next/core/callable.c b/next/core/callable.c index 71052ba..e9bae62 100644 --- a/next/core/callable.c +++ b/next/core/callable.c @@ -6,9 +6,9 @@ int growl_callable(Growl obj) { return 0; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); switch (hdr->type) { - case GROWL_QUOTATION: - case GROWL_COMPOSE: - case GROWL_CURRY: + case GROWL_TYPE_QUOTATION: + case GROWL_TYPE_COMPOSE: + case GROWL_TYPE_CURRY: return 1; default: return 0; @@ -21,7 +21,7 @@ Growl growl_make_quotation(GrowlVM *vm, const uint8_t *code, size_t code_size, constants_size * sizeof(Growl); GrowlObjectHeader *constants_hdr = 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); 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; GrowlObjectHeader *quotation_hdr = 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); quotation->constants = GROWL_BOX(constants_hdr); @@ -47,7 +47,7 @@ GrowlQuotation *growl_unwrap_quotation(Growl obj) { if (obj == GROWL_NIL || GROWL_IMM(obj)) return NULL; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); - if (hdr->type != GROWL_QUOTATION) + if (hdr->type != GROWL_TYPE_QUOTATION) return NULL; return (GrowlQuotation *)(hdr + 1); } @@ -59,7 +59,7 @@ Growl growl_compose(GrowlVM *vm, Growl first, Growl second) { return GROWL_NIL; size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCompose); GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); - hdr->type = GROWL_COMPOSE; + hdr->type = GROWL_TYPE_COMPOSE; GrowlCompose *comp = (GrowlCompose *)(hdr + 1); comp->first = first; comp->second = second; @@ -70,7 +70,7 @@ GrowlCompose *growl_unwrap_compose(Growl obj) { if (obj == GROWL_NIL || GROWL_IMM(obj)) return NULL; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); - if (hdr->type != GROWL_COMPOSE) + if (hdr->type != GROWL_TYPE_COMPOSE) return NULL; return (GrowlCompose *)(hdr + 1); } @@ -80,7 +80,7 @@ Growl growl_curry(GrowlVM *vm, Growl value, Growl callable) { return GROWL_NIL; size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlCurry); GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); - hdr->type = GROWL_CURRY; + hdr->type = GROWL_TYPE_CURRY; GrowlCurry *comp = (GrowlCurry *)(hdr + 1); comp->value = value; comp->callable = callable; @@ -91,7 +91,7 @@ GrowlCurry *growl_unwrap_curry(Growl obj) { if (obj == GROWL_NIL || GROWL_IMM(obj)) return NULL; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); - if (hdr->type != GROWL_CURRY) + if (hdr->type != GROWL_TYPE_CURRY) return NULL; return (GrowlCurry *)(hdr + 1); } diff --git a/next/core/gc.c b/next/core/gc.c index 9e17c1f..b683494 100644 --- a/next/core/gc.c +++ b/next/core/gc.c @@ -67,38 +67,40 @@ GrowlObjectHeader *growl_gc_alloc_tenured(GrowlVM *vm, size_t size) { static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) { switch (hdr->type) { - case GROWL_STRING: + case GROWL_TYPE_STRING: break; - case GROWL_LIST: { + case GROWL_TYPE_LIST: { GrowlList *list = (GrowlList *)(hdr + 1); list->head = forward(vm, list->head); list->tail = forward(vm, list->tail); break; } - case GROWL_TUPLE: { + case GROWL_TYPE_TUPLE: { GrowlTuple *tuple = (GrowlTuple *)(hdr + 1); for (size_t i = 0; i < tuple->count; ++i) { tuple->data[i] = forward(vm, tuple->data[i]); } break; } - case GROWL_QUOTATION: { + case GROWL_TYPE_QUOTATION: { GrowlQuotation *quot = (GrowlQuotation *)(hdr + 1); quot->constants = forward(vm, quot->constants); break; } - case GROWL_COMPOSE: { + case GROWL_TYPE_COMPOSE: { GrowlCompose *comp = (GrowlCompose *)(hdr + 1); comp->first = forward(vm, comp->first); comp->second = forward(vm, comp->second); break; } - case GROWL_CURRY: { + case GROWL_TYPE_CURRY: { GrowlCurry *comp = (GrowlCurry *)(hdr + 1); comp->value = forward(vm, comp->value); comp->callable = forward(vm, comp->callable); break; } + case GROWL_TYPE_ALIEN: + break; case UINT32_MAX: fprintf(stderr, "gc: fwd pointer during scan\n"); abort(); @@ -109,15 +111,22 @@ static void scan(GrowlVM *vm, GrowlObjectHeader *hdr) { } static void gc_print_stats(GrowlVM *vm, const char *label) { - size_t used = vm->from.free - vm->from.start; - size_t total = vm->from.end - vm->from.start; - fprintf(stderr, "[%s] used=%zu/%zu bytes (%.1f%%)\n", label, used, total, - (double)used / (double)total * 100.0); + size_t nursery_used = vm->from.free - vm->from.start; + size_t nursery_total = vm->from.end - vm->from.start; + size_t tenured_used = vm->arena.free - vm->arena.start; + 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) { uint8_t *gc_scan = vm->to.free; + fprintf(stderr, ">>> starting garbage collection\n"); gc_print_stats(vm, "before GC"); 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 = 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; vm->from = vm->to; vm->to = tmp; @@ -148,6 +177,7 @@ void growl_gc_collect(GrowlVM *vm) { vm->scratch.free = vm->scratch.start; gc_print_stats(vm, "after GC"); + fprintf(stderr, ">>> garbage collection finished\n"); } void growl_gc_root(GrowlVM *vm, Growl *ptr) { diff --git a/next/core/opcodes.h b/next/core/opcodes.h index 0d3eca1..c392c31 100644 --- a/next/core/opcodes.h +++ b/next/core/opcodes.h @@ -1,11 +1,25 @@ #ifndef GROWL_OPCODES_H #define GROWL_OPCODES_H -enum { +enum GrowlOpcode { GOP_NOP = 0, GOP_PUSH_NIL, 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_NEXT, + GOP_TAIL_CALL, GOP_RETURN, }; diff --git a/next/core/sleb128.c b/next/core/sleb128.c index 0f41aad..15dadbe 100644 --- a/next/core/sleb128.c +++ b/next/core/sleb128.c @@ -16,7 +16,7 @@ intptr_t growl_sleb128_decode(uint8_t **ptr) { shift += 7; } while (byte & 0x80); - if ((shift < 64) && (byte & 0x40)) { + if (shift < 64 && byte & 0x40) { result |= -(1LL << shift); } diff --git a/next/core/string.c b/next/core/string.c index 41bf42a..bf913bd 100644 --- a/next/core/string.c +++ b/next/core/string.c @@ -4,7 +4,7 @@ Growl growl_make_string(GrowlVM *vm, size_t len) { size_t size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len; GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); - hdr->type = GROWL_STRING; + hdr->type = GROWL_TYPE_STRING; GrowlString *str = (GrowlString *)(hdr + 1); str->len = 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 size = sizeof(GrowlObjectHeader) + sizeof(GrowlString) + len + 1; GrowlObjectHeader *hdr = growl_gc_alloc(vm, size); - hdr->type = GROWL_STRING; + hdr->type = GROWL_TYPE_STRING; GrowlString *str = (GrowlString *)(hdr + 1); str->len = len; memcpy(str->data, cstr, len); @@ -27,7 +27,7 @@ GrowlString *growl_unwrap_string(Growl obj) { if (obj == 0 || GROWL_IMM(obj)) return NULL; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); - if (hdr->type != GROWL_STRING) + if (hdr->type != GROWL_TYPE_STRING) return NULL; return (GrowlString *)(hdr + 1); } diff --git a/next/core/tuple.c b/next/core/tuple.c index f38e0b5..b417d5e 100644 --- a/next/core/tuple.c +++ b/next/core/tuple.c @@ -4,7 +4,7 @@ GrowlTuple *growl_unwrap_tuple(Growl obj) { if (obj == 0 || GROWL_IMM(obj)) return NULL; GrowlObjectHeader *hdr = GROWL_UNBOX(obj); - if (hdr->type != GROWL_TUPLE) + if (hdr->type != GROWL_TYPE_TUPLE) return NULL; return (GrowlTuple *)(hdr + 1); } diff --git a/next/core/value.c b/next/core/value.c new file mode 100644 index 0000000..08c9c77 --- /dev/null +++ b/next/core/value.c @@ -0,0 +1,11 @@ +#include + +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; +} + diff --git a/next/core/vm.c b/next/core/vm.c index 49d94d7..fd3b8ba 100644 --- a/next/core/vm.c +++ b/next/core/vm.c @@ -33,6 +33,8 @@ GrowlVM *growl_vm_init(void) { mem->root_count = 0; mem->root_capacity = 0; + // TODO: initialize compose trampoline + return mem; } @@ -63,6 +65,12 @@ void growl_push(GrowlVM *vm, Growl 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) { if (vm->sp <= vm->wst) vm_error(vm, "work stack underflow"); @@ -85,19 +93,47 @@ Growl growl_rpop(GrowlVM *vm) { 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) vm_error(vm, "call stack overflow"); vm->csp->quot = q; vm->csp->ip = ip; vm->csp++; } -static GrowlFrame pop_call(GrowlVM *vm) { + +static GrowlFrame callstack_pop(GrowlVM *vm) { if (vm->csp <= vm->cst) vm_error(vm, "call stack underflow"); 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) { size_t gc_mark = growl_gc_mark(vm); int result = setjmp(vm->error); @@ -117,46 +153,129 @@ int vm_doquot(GrowlVM *vm, GrowlQuotation *quot) { vm->ip = quot->data; vm->quotation = quot; - for (;;) { - uint8_t opcode; - switch (opcode = *vm->ip++) { - case GOP_NOP: - break; - case GOP_PUSH_NIL: - growl_push(vm, GROWL_NIL); - break; - case GOP_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); - } - 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); - } + // clang-format off +#define VM_START() for (;;) { uint8_t opcode; switch(opcode = *vm->ip++) { +#define VM_END() }} +#define VM_DEFAULT() default: +#define VM_OP(op) case GOP_## op: +#define VM_NEXT() break + // clang-format on + + VM_START() + VM_OP(NOP) VM_NEXT(); + VM_OP(PUSH_NIL) { + growl_push(vm, GROWL_NIL); + VM_NEXT(); } + 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: growl_gc_reset(vm, gc_mark); diff --git a/next/include/growl.h b/next/include/growl.h index 0bda72a..8af5b26 100644 --- a/next/include/growl.h +++ b/next/include/growl.h @@ -21,19 +21,26 @@ typedef struct GrowlTuple GrowlTuple; typedef struct GrowlQuotation GrowlQuotation; typedef struct GrowlCompose GrowlCompose; typedef struct GrowlCurry GrowlCurry; +typedef struct GrowlAlienType GrowlAlienType; +typedef struct GrowlAlien GrowlAlien; typedef struct GrowlGCArena GrowlGCArena; typedef struct GrowlFrame GrowlFrame; typedef struct GrowlVM GrowlVM; enum { - GROWL_STRING, - GROWL_LIST, - GROWL_TUPLE, - GROWL_QUOTATION, - GROWL_COMPOSE, - GROWL_CURRY, + GROWL_TYPE_NIL, + GROWL_TYPE_NUMBER, + GROWL_TYPE_STRING, + GROWL_TYPE_LIST, + GROWL_TYPE_TUPLE, + GROWL_TYPE_QUOTATION, + GROWL_TYPE_COMPOSE, + GROWL_TYPE_CURRY, + GROWL_TYPE_ALIEN, }; +uint32_t growl_type(Growl obj); + struct GrowlObjectHeader { size_t size; uint32_t type; @@ -82,6 +89,19 @@ GrowlCompose *growl_unwrap_compose(Growl obj); Growl growl_curry(GrowlVM *vm, Growl value, Growl callable); 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 { uint8_t *start, *end; uint8_t *free; @@ -103,6 +123,7 @@ void *growl_arena_alloc(GrowlGCArena *arena, size_t size, size_t align, struct GrowlFrame { GrowlQuotation *quot; uint8_t *ip; + Growl next; }; struct GrowlVM { @@ -116,6 +137,9 @@ struct GrowlVM { Growl rst[GROWL_STACK_SIZE], *rsp; GrowlFrame cst[GROWL_CALL_STACK_SIZE], *csp; + GrowlQuotation *compose_trampoline; + Growl next; + Growl **roots; size_t root_count, root_capacity;