diff --git a/src/runtime.c b/src/runtime.c index 18a543c..230c139 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -28,11 +28,13 @@ #include #include -static struct u6a_vm_ins* text; -static uint32_t text_len; -static char* rodata; -static uint32_t rodata_len; -static bool force_exec; +static struct u6a_vm_ins* text; +static uint32_t text_len; +static char* rodata; +static uint32_t rodata_len; +static bool force_exec; +static struct u6a_vm_stack_ctx stack_ctx; +static struct u6a_vm_pool_ctx pool_ctx; static const struct u6a_vm_ins text_subst[] = { { .opcode = u6a_vo_la }, @@ -66,39 +68,50 @@ static const char* info_runtime = "runtime"; #define VM_JMP(dest) \ ins = text + (dest); \ continue -#define VM_VAR_JMP \ - U6A_VM_VAR_FN_REF(u6a_vf_j, ins - text) -#define VM_VAR_FINALIZE \ - U6A_VM_VAR_FN_REF(u6a_vf_f, ins - text) #define CHECK_FORCE(log_func, err_val) \ if (!force_exec) { \ log_func(err_runtime, err_val); \ goto runtime_error; \ } -#define STACK_PUSH1(fn_0) \ - vm_var_fn_addref(fn_0); \ - if (UNLIKELY(!u6a_vm_stack_push1(fn_0))) { \ - goto runtime_error; \ +#define VM_VAR_JMP U6A_VM_VAR_FN_REF(u6a_vf_j, ins - text) +#define VM_VAR_FINALIZE U6A_VM_VAR_FN_REF(u6a_vf_f, ins - text) + +#define STACK_PUSH1(fn_0) \ + vm_var_fn_addref(fn_0); \ + if (UNLIKELY(!u6a_vm_stack_push1(&stack_ctx, fn_0))) { \ + goto runtime_error; \ } -#define STACK_PUSH2(fn_0, fn_1) \ - if (UNLIKELY(!u6a_vm_stack_push2(fn_0, fn_1))) { \ - goto runtime_error; \ +#define STACK_PUSH2(fn_0, fn_1) \ + if (UNLIKELY(!u6a_vm_stack_push2(&stack_ctx, fn_0, fn_1))) { \ + goto runtime_error; \ } -#define STACK_PUSH3(fn_0, fn_1, fn_2) \ - if (UNLIKELY(!u6a_vm_stack_push3(fn_0, fn_1, fn_2))) { \ - goto runtime_error; \ +#define STACK_PUSH3(fn_0, fn_1, fn_2) \ + if (UNLIKELY(!u6a_vm_stack_push3(&stack_ctx, fn_0, fn_1, fn_2))) { \ + goto runtime_error; \ } -#define STACK_PUSH4(fn_0, fn_1, fn_2, fn_3) \ - if (UNLIKELY(!u6a_vm_stack_push4(fn_0, fn_1, fn_2, fn_3))) { \ - goto runtime_error; \ +#define STACK_PUSH4(fn_0, fn_1, fn_2, fn_3) \ + if (UNLIKELY(!u6a_vm_stack_push4(&stack_ctx, fn_0, fn_1, fn_2, fn_3))) { \ + goto runtime_error; \ } -#define STACK_POP() \ - vm_var_fn_free(top); \ - top = u6a_vm_stack_top(); \ - if (UNLIKELY(!u6a_vm_stack_pop())) { \ - goto runtime_error; \ +#define STACK_POP() \ + vm_var_fn_free(top); \ + top = u6a_vm_stack_top(&stack_ctx); \ + if (UNLIKELY(!u6a_vm_stack_pop(&stack_ctx))) { \ + goto runtime_error; \ } +#define STACK_XCH(fn_0) \ + fn_0 = u6a_vm_stack_xch(&stack_ctx, fn_0); \ + if (UNLIKELY(U6A_VM_VAR_FN_IS_EMPTY(fn_0))) { \ + goto runtime_error; \ + } + +#define POOL_ALLOC1(v1) u6a_vm_pool_alloc1(&pool_ctx, v1) +#define POOL_ALLOC2(v1, v2) u6a_vm_pool_alloc2(&pool_ctx, v1, v2) +#define POOL_ALLOC2_PTR(v1, v2) u6a_vm_pool_alloc2_ptr(&pool_ctx, v1, v2) +#define POOL_GET1(offset) u6a_vm_pool_get1(pool_ctx.active_pool, offset) +#define POOL_GET2(offset) u6a_vm_pool_get2(pool_ctx.active_pool, offset) +#define POOL_GET2_SEPARATE(offset) u6a_vm_pool_get2_separate(&pool_ctx, offset) static inline bool read_bc_header(struct u6a_bc_header* restrict header, FILE* restrict input_stream) { @@ -124,7 +137,7 @@ read_bc_header(struct u6a_bc_header* restrict header, FILE* restrict input_strea static inline struct u6a_vm_var_fn vm_var_fn_addref(struct u6a_vm_var_fn var) { if (var.token.fn & U6A_VM_FN_REF) { - u6a_vm_pool_addref(var.ref); + u6a_vm_pool_addref(pool_ctx.active_pool, var.ref); } return var; } @@ -132,7 +145,7 @@ vm_var_fn_addref(struct u6a_vm_var_fn var) { static inline void vm_var_fn_free(struct u6a_vm_var_fn var) { if (var.token.fn & U6A_VM_FN_REF) { - u6a_vm_pool_free(var.ref); + //u6a_vm_pool_free(pool_ctx.active_pool, var.ref); } } @@ -190,12 +203,14 @@ u6a_runtime_init(struct u6a_runtime_options* options) { if (UNLIKELY(rodata_len != fread(rodata, sizeof(char), rodata_len, options->istream))) { goto runtime_init_failed; } - if (UNLIKELY(!u6a_vm_stack_init(options->stack_segment_size, err_runtime))) { + if (UNLIKELY(!u6a_vm_stack_init(&stack_ctx, options->stack_segment_size, err_runtime))) { goto runtime_init_failed; } - if (UNLIKELY(!u6a_vm_pool_init(options->pool_size, text_len, err_runtime))) { + if (UNLIKELY(!u6a_vm_pool_init(&pool_ctx, options->pool_size, text_len, err_runtime))) { goto runtime_init_failed; } + stack_ctx.pool_ctx = &pool_ctx; + pool_ctx.stack_ctx = &stack_ctx; for (struct u6a_vm_ins* ins = text + text_subst_len; ins < text + text_len; ++ins) { if (ins->opcode & U6A_VM_OP_OFFSET) { ins->operand.offset = ntohl(ins->operand.offset); @@ -240,15 +255,15 @@ u6a_runtime_execute(FILE* restrict istream, FILE* restrict ostream) { switch (func.token.fn) { case u6a_vf_s: vm_var_fn_addref(arg); - ACC_FN_REF(u6a_vf_s1, u6a_vm_pool_alloc1(arg)); + ACC_FN_REF(u6a_vf_s1, POOL_ALLOC1(arg)); break; case u6a_vf_s1: vm_var_fn_addref(arg); - vm_var_fn_addref(u6a_vm_pool_get1(func.ref).fn); - ACC_FN_REF(u6a_vf_s2, u6a_vm_pool_alloc2(u6a_vm_pool_get1(func.ref).fn, arg)); + vm_var_fn_addref(POOL_GET1(func.ref).fn); + ACC_FN_REF(u6a_vf_s2, POOL_ALLOC2(POOL_GET1(func.ref).fn, arg)); break; case u6a_vf_s2: - tuple = u6a_vm_pool_get2(func.ref); + tuple = POOL_GET2(func.ref); vm_var_fn_addref(tuple.v1.fn); vm_var_fn_addref(tuple.v2.fn); vm_var_fn_addref(arg); @@ -261,10 +276,10 @@ u6a_runtime_execute(FILE* restrict istream, FILE* restrict ostream) { VM_JMP(0x00); case u6a_vf_k: vm_var_fn_addref(arg); - ACC_FN_REF(u6a_vf_k1, u6a_vm_pool_alloc1(arg)); + ACC_FN_REF(u6a_vf_k1, POOL_ALLOC1(arg)); break; case u6a_vf_k1: - ACC_FN(u6a_vm_pool_get1(func.ref).fn); + ACC_FN(POOL_GET1(func.ref).fn); break; case u6a_vf_i: ACC_FN(arg); @@ -284,29 +299,29 @@ u6a_runtime_execute(FILE* restrict istream, FILE* restrict ostream) { ACC_FN(top); VM_JMP(0x03); case u6a_vf_c: - cont = u6a_vm_stack_save(); + cont = u6a_vm_stack_save(&stack_ctx); if (UNLIKELY(cont == NULL)) { goto runtime_error; } STACK_PUSH2(VM_VAR_JMP, vm_var_fn_addref(arg)); - ACC_FN_REF(u6a_vf_c1, u6a_vm_pool_alloc2_ptr(cont, ins)); + ACC_FN_REF(u6a_vf_c1, POOL_ALLOC2_PTR(cont, ins)); VM_JMP(0x03); case u6a_vf_d: vm_var_fn_addref(arg); - ACC_FN_REF(u6a_vf_d1_c, u6a_vm_pool_alloc1(arg)); + ACC_FN_REF(u6a_vf_d1_c, POOL_ALLOC1(arg)); break; case u6a_vf_c1: - tuple = u6a_vm_pool_get2_separate(func.ref); - u6a_vm_stack_resume(tuple.v1.ptr); + tuple = POOL_GET2_SEPARATE(func.ref); + u6a_vm_stack_resume(&stack_ctx, tuple.v1.ptr); ins = tuple.v2.ptr; ACC_FN(arg); break; case u6a_vf_d1_c: - STACK_PUSH2(VM_VAR_JMP, vm_var_fn_addref(u6a_vm_pool_get1(func.ref).fn)); + STACK_PUSH2(VM_VAR_JMP, vm_var_fn_addref(POOL_GET1(func.ref).fn)); ACC_FN(arg); VM_JMP(0x03); case u6a_vf_d1_s: - tuple = u6a_vm_pool_get2(func.ref); + tuple = POOL_GET2(func.ref); STACK_PUSH3(vm_var_fn_addref(arg), VM_VAR_FINALIZE, tuple.v1.fn); ACC_FN(tuple.v2.fn); VM_JMP(0x03); @@ -364,12 +379,9 @@ u6a_runtime_execute(FILE* restrict istream, FILE* restrict ostream) { func = top; STACK_POP(); arg = top; - ACC_FN_REF(u6a_vf_d1_s, u6a_vm_pool_alloc2(func, arg)); + ACC_FN_REF(u6a_vf_d1_s, POOL_ALLOC2(func, arg)); } else { - acc = u6a_vm_stack_xch(acc); - if (UNLIKELY(U6A_VM_VAR_FN_IS_EMPTY(acc))) { - goto runtime_error; - } + STACK_XCH(acc); } break; case u6a_vo_del: diff --git a/src/vm_pool.c b/src/vm_pool.c index eb2a88e..ce7ee91 100644 --- a/src/vm_pool.c +++ b/src/vm_pool.c @@ -17,190 +17,46 @@ * along with this program. If not, see . */ -#include "vm_pool.h" #include "vm_stack.h" +#include "vm_pool.h" #include "logging.h" #include #include -struct vm_pool_elem { - struct u6a_vm_var_tuple values; - uint32_t refcnt; - uint32_t flags; -}; - -#define POOL_ELEM_HOLDS_PTR ( 1 << 0 ) - -struct vm_pool { - uint32_t pos; - struct vm_pool_elem elems[]; -}; - -struct vm_pool_elem_ptrs { - uint32_t pos; - struct vm_pool_elem* elems[]; -}; - -static struct vm_pool* active_pool; -static struct vm_pool_elem_ptrs* holes; -static uint32_t pool_len; -static struct vm_pool_elem** fstack; -static uint32_t fstack_top; - -const char* err_stage; - -static inline struct vm_pool_elem* -vm_pool_elem_alloc() { - struct vm_pool_elem* new_elem; - if (holes->pos == UINT32_MAX) { - if (UNLIKELY(++active_pool->pos == pool_len)) { - u6a_err_vm_pool_oom(err_stage); - return NULL; - } - new_elem = active_pool->elems + active_pool->pos; - } else { - new_elem = holes->elems[holes->pos--]; - } - new_elem->refcnt = 1; - return new_elem; -} - -static inline struct vm_pool_elem* -vm_pool_elem_dup(struct vm_pool_elem* elem) { - struct vm_pool_elem* new_elem = vm_pool_elem_alloc(); - if (UNLIKELY(new_elem == NULL)) { - return NULL; - } - *new_elem = *elem; - return new_elem; -} - -static inline void -free_stack_push(struct u6a_vm_var_fn fn) { - if (fn.token.fn & U6A_VM_FN_REF) { - fstack[++fstack_top] = active_pool->elems + fn.ref; - } -} - -static inline struct vm_pool_elem* -free_stack_pop() { - if (fstack_top == UINT32_MAX) { - return NULL; - } - return fstack[fstack_top--]; -} - bool -u6a_vm_pool_init(uint32_t pool_len_, uint32_t ins_len, const char* err_stage_) { - const uint32_t pool_size = sizeof(struct vm_pool) + pool_len_ * sizeof(struct vm_pool_elem); - active_pool = malloc(pool_size); - if (UNLIKELY(active_pool == NULL)) { - u6a_err_bad_alloc(err_stage_, pool_size); +u6a_vm_pool_init(struct u6a_vm_pool_ctx* ctx, uint32_t pool_len, uint32_t ins_len, const char* err_stage) { + const uint32_t pool_size = sizeof(struct u6a_vm_pool) + pool_len * sizeof(struct u6a_vm_pool_elem); + ctx->active_pool = malloc(pool_size); + if (UNLIKELY(ctx->active_pool == NULL)) { + u6a_err_bad_alloc(err_stage, pool_size); return false; } - const uint32_t holes_size = sizeof(struct vm_pool_elem_ptrs) + pool_len_ * sizeof(struct vm_pool_elem*); - holes = malloc(holes_size); - if (UNLIKELY(holes == NULL)) { - u6a_err_bad_alloc(err_stage_, holes_size); - free(holes); + const uint32_t holes_size = sizeof(struct u6a_vm_pool_elem_ptrs) + pool_len * sizeof(struct u6a_vm_pool_elem*); + ctx->holes = malloc(holes_size); + if (UNLIKELY(ctx->holes == NULL)) { + u6a_err_bad_alloc(err_stage, holes_size); + free(ctx->active_pool); return false; } const uint32_t free_stack_size = ins_len * sizeof(struct vm_pool_elem*); - fstack = malloc(free_stack_size); - if (UNLIKELY(fstack == NULL)) { - u6a_err_bad_alloc(err_stage_, free_stack_size); - free(active_pool); - free(holes); + ctx->fstack = malloc(free_stack_size); + if (UNLIKELY(ctx->fstack == NULL)) { + u6a_err_bad_alloc(err_stage, free_stack_size); + free(ctx->active_pool); + free(ctx->holes); return false; } - active_pool->pos = UINT32_MAX; - holes->pos = UINT32_MAX; - pool_len = pool_len_; - err_stage = err_stage_; + ctx->active_pool->pos = UINT32_MAX; + ctx->holes->pos = UINT32_MAX; + ctx->pool_len = pool_len; + ctx->err_stage = err_stage; return true; } -U6A_HOT uint32_t -u6a_vm_pool_alloc1(struct u6a_vm_var_fn v1) { - struct vm_pool_elem* elem = vm_pool_elem_alloc(); - if (UNLIKELY(elem == NULL)) { - return UINT32_MAX; - } - elem->values = (struct u6a_vm_var_tuple) { .v1.fn = v1, .v2.ptr = NULL }; - elem->flags = 0; - return elem - active_pool->elems; -} - -U6A_HOT uint32_t -u6a_vm_pool_alloc2(struct u6a_vm_var_fn v1, struct u6a_vm_var_fn v2) { - struct vm_pool_elem* elem = vm_pool_elem_alloc(); - if (UNLIKELY(elem == NULL)) { - return UINT32_MAX; - } - elem->values = (struct u6a_vm_var_tuple) { .v1.fn = v1, .v2.fn = v2 }; - elem->flags = 0; - return elem - active_pool->elems; -} - -U6A_HOT uint32_t -u6a_vm_pool_alloc2_ptr(void* v1, void* v2) { - struct vm_pool_elem* elem = vm_pool_elem_alloc(); - if (UNLIKELY(elem == NULL)) { - return UINT32_MAX; - } - elem->values = (struct u6a_vm_var_tuple) { .v1.ptr = v1, .v2.ptr = v2 }; - elem->flags = POOL_ELEM_HOLDS_PTR; - return elem - active_pool->elems; -} - -U6A_HOT union u6a_vm_var -u6a_vm_pool_get1(uint32_t offset) { - return active_pool->elems[offset].values.v1; -} - -U6A_HOT struct u6a_vm_var_tuple -u6a_vm_pool_get2(uint32_t offset) { - return active_pool->elems[offset].values; -} - -U6A_HOT struct u6a_vm_var_tuple -u6a_vm_pool_get2_separate(uint32_t offset) { - struct vm_pool_elem* elem = active_pool->elems + offset; - struct u6a_vm_var_tuple values = elem->values; - if (elem->refcnt > 1) { - // Continuation having more than 1 reference should be separated before reinstatement - values.v1.ptr = u6a_vm_stack_dup(values.v1.ptr); - } - return values; -} - -U6A_HOT void -u6a_vm_pool_addref(uint32_t offset) { - ++active_pool->elems[offset].refcnt; -} - -U6A_HOT void -u6a_vm_pool_free(uint32_t offset) { - struct vm_pool_elem* elem = active_pool->elems + offset; - fstack_top = UINT32_MAX; - do { - if (--elem->refcnt == 0) { - holes->elems[++holes->pos] = elem; - if (elem->flags & POOL_ELEM_HOLDS_PTR) { - // Continuation destroyed before used - u6a_vm_stack_discard(elem->values.v1.ptr); - } else { - free_stack_push(elem->values.v2.fn); - free_stack_push(elem->values.v1.fn); - } - } - } while ((elem = free_stack_pop())); -} - void -u6a_vm_pool_destroy() { - free(active_pool); - free(holes); - free(fstack); +u6a_vm_pool_destroy(struct u6a_vm_pool_ctx* ctx) { + free(ctx->active_pool); + free(ctx->holes); + free(ctx->fstack); } diff --git a/src/vm_pool.h b/src/vm_pool.h index 881ee2b..5f672f9 100644 --- a/src/vm_pool.h +++ b/src/vm_pool.h @@ -22,38 +22,164 @@ #include "common.h" #include "vm_defs.h" +#include "logging.h" #include #include +struct u6a_vm_pool_elem { + struct u6a_vm_var_tuple values; + uint32_t refcnt; + uint32_t flags; +}; + +#define U6A_VM_POOL_ELEM_HOLDS_PTR ( 1 << 0 ) + +struct u6a_vm_pool { + uint32_t pos; + struct u6a_vm_pool_elem elems[]; +}; + +struct u6a_vm_pool_elem_ptrs { + uint32_t pos; + struct u6a_vm_pool_elem* elems[]; +}; + +struct u6a_vm_pool_ctx { + struct u6a_vm_pool* active_pool; + struct u6a_vm_pool_elem_ptrs* holes; + struct u6a_vm_pool_elem** fstack; + struct u6a_vm_stack_ctx* stack_ctx; + uint32_t pool_len; + uint32_t fstack_top; + const char* err_stage; +}; + +static inline void +free_stack_push(struct u6a_vm_pool_ctx* ctx, struct u6a_vm_var_fn fn) { + if (fn.token.fn & U6A_VM_FN_REF) { + ctx->fstack[++ctx->fstack_top] = ctx->active_pool->elems + fn.ref; + } +} + +static inline struct u6a_vm_pool_elem* +free_stack_pop(struct u6a_vm_pool_ctx* ctx) { + if (ctx->fstack_top == UINT32_MAX) { + return NULL; + } + return ctx->fstack[ctx->fstack_top--]; +} + +static inline struct u6a_vm_pool_elem* +vm_pool_elem_alloc(struct u6a_vm_pool_ctx* ctx) { + struct u6a_vm_pool* pool = ctx->active_pool; + struct u6a_vm_pool_elem_ptrs* holes = ctx->holes; + struct u6a_vm_pool_elem* new_elem; + if (ctx->holes->pos == UINT32_MAX) { + if (UNLIKELY(++pool->pos == ctx->pool_len)) { + u6a_err_vm_pool_oom(ctx->err_stage); + return NULL; + } + new_elem = pool->elems + pool->pos; + } else { + new_elem = holes->elems[holes->pos--]; + } + new_elem->refcnt = 1; + return new_elem; +} + +static inline struct u6a_vm_pool_elem* +vm_pool_elem_dup(struct u6a_vm_pool_ctx* ctx, struct u6a_vm_pool_elem* elem) { + struct u6a_vm_pool_elem* new_elem = vm_pool_elem_alloc(ctx); + if (UNLIKELY(new_elem == NULL)) { + return NULL; + } + *new_elem = *elem; + return new_elem; +} + bool -u6a_vm_pool_init(uint32_t pool_len, uint32_t ins_len, const char* err_stage); +u6a_vm_pool_init(struct u6a_vm_pool_ctx* ctx, uint32_t pool_len, uint32_t ins_len, const char* err_stage); -uint32_t -u6a_vm_pool_alloc1(struct u6a_vm_var_fn v1); +static inline uint32_t +u6a_vm_pool_alloc1(struct u6a_vm_pool_ctx* ctx, struct u6a_vm_var_fn v1) { + struct u6a_vm_pool_elem* elem = vm_pool_elem_alloc(ctx); + if (UNLIKELY(elem == NULL)) { + return UINT32_MAX; + } + elem->values = (struct u6a_vm_var_tuple) { .v1.fn = v1, .v2.ptr = NULL }; + elem->flags = 0; + return elem - ctx->active_pool->elems; +} -uint32_t -u6a_vm_pool_alloc2(struct u6a_vm_var_fn v1, struct u6a_vm_var_fn v2); +static inline uint32_t +u6a_vm_pool_alloc2(struct u6a_vm_pool_ctx* ctx, struct u6a_vm_var_fn v1, struct u6a_vm_var_fn v2) { + struct u6a_vm_pool_elem* elem = vm_pool_elem_alloc(ctx); + if (UNLIKELY(elem == NULL)) { + return UINT32_MAX; + } + elem->values = (struct u6a_vm_var_tuple) { .v1.fn = v1, .v2.fn = v2 }; + elem->flags = 0; + return elem - ctx->active_pool->elems; +} -uint32_t -u6a_vm_pool_alloc2_ptr(void* v1, void* v2); +static inline uint32_t +u6a_vm_pool_alloc2_ptr(struct u6a_vm_pool_ctx* ctx, void* v1, void* v2) { + struct u6a_vm_pool_elem* elem = vm_pool_elem_alloc(ctx); + if (UNLIKELY(elem == NULL)) { + return UINT32_MAX; + } + elem->values = (struct u6a_vm_var_tuple) { .v1.ptr = v1, .v2.ptr = v2 }; + elem->flags = U6A_VM_POOL_ELEM_HOLDS_PTR; + return elem - ctx->active_pool->elems; +} -union u6a_vm_var -u6a_vm_pool_get1(uint32_t offset); +static inline union u6a_vm_var +u6a_vm_pool_get1(struct u6a_vm_pool* pool, uint32_t offset) { + return pool->elems[offset].values.v1; +} -struct u6a_vm_var_tuple -u6a_vm_pool_get2(uint32_t offset); +static inline struct u6a_vm_var_tuple +u6a_vm_pool_get2(struct u6a_vm_pool* pool, uint32_t offset) { + return pool->elems[offset].values; +} -struct u6a_vm_var_tuple -u6a_vm_pool_get2_separate(uint32_t offset); +static inline struct u6a_vm_var_tuple +u6a_vm_pool_get2_separate(struct u6a_vm_pool_ctx* ctx, uint32_t offset) { + struct u6a_vm_pool_elem* elem = ctx->active_pool->elems + offset; + struct u6a_vm_var_tuple values = elem->values; + if (elem->refcnt > 1) { + // Continuation having more than 1 reference should be separated before reinstatement + values.v1.ptr = u6a_vm_stack_dup(ctx->stack_ctx, values.v1.ptr); + } + return values; +} + +static inline void +u6a_vm_pool_addref(struct u6a_vm_pool* pool, uint32_t offset) { + ++pool->elems[offset].refcnt; +} + +static inline void +u6a_vm_pool_free(struct u6a_vm_pool_ctx* ctx, uint32_t offset) { + struct u6a_vm_pool_elem* elem = ctx->active_pool->elems + offset; + struct u6a_vm_pool_elem_ptrs* holes = ctx->holes; + ctx->fstack_top = UINT32_MAX; + do { + if (--elem->refcnt == 0) { + holes->elems[++holes->pos] = elem; + if (elem->flags & U6A_VM_POOL_ELEM_HOLDS_PTR) { + // Continuation destroyed before used + u6a_vm_stack_discard(ctx->stack_ctx, elem->values.v1.ptr); + } else { + free_stack_push(ctx, elem->values.v2.fn); + free_stack_push(ctx, elem->values.v1.fn); + } + } + } while ((elem = free_stack_pop(ctx))); +} void -u6a_vm_pool_addref(uint32_t offset); - -void -u6a_vm_pool_free(uint32_t offset); - -void -u6a_vm_pool_destroy(); +u6a_vm_pool_destroy(struct u6a_vm_pool_ctx* ctx); #endif diff --git a/src/vm_stack.c b/src/vm_stack.c index a049965..55b2340 100644 --- a/src/vm_stack.c +++ b/src/vm_stack.c @@ -25,24 +25,12 @@ #include #include -struct vm_stack { - struct vm_stack* prev; - uint32_t top; - uint32_t refcnt; - struct u6a_vm_var_fn elems[]; -}; - -static struct vm_stack* active_stack; -static uint32_t stack_seg_len; - -const char* err_stage; - -static inline struct vm_stack* -vm_stack_create(struct vm_stack* prev, uint32_t top) { - const uint32_t size = sizeof(struct vm_stack) + stack_seg_len * sizeof(struct u6a_vm_var_fn); - struct vm_stack* vs = malloc(size); +static inline struct u6a_vm_stack* +vm_stack_create(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* prev, uint32_t top) { + const uint32_t size = sizeof(struct u6a_vm_stack) + ctx->stack_seg_len * sizeof(struct u6a_vm_var_fn); + struct u6a_vm_stack* vs = malloc(size); if (UNLIKELY(vs == NULL)) { - u6a_err_bad_alloc(err_stage, size); + u6a_err_bad_alloc(ctx->err_stage, size); return NULL; } vs->prev = prev; @@ -51,20 +39,20 @@ vm_stack_create(struct vm_stack* prev, uint32_t top) { return vs; } -static inline struct vm_stack* -vm_stack_dup(struct vm_stack* vs) { - const uint32_t size = sizeof(struct vm_stack) + stack_seg_len * sizeof(struct u6a_vm_var_fn); - struct vm_stack* dup_stack = malloc(size); +static inline struct u6a_vm_stack* +vm_stack_dup(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs) { + const uint32_t size = sizeof(struct u6a_vm_stack) + ctx->stack_seg_len * sizeof(struct u6a_vm_var_fn); + struct u6a_vm_stack* dup_stack = malloc(size); if (UNLIKELY(dup_stack == NULL)) { - u6a_err_bad_alloc(err_stage, size); + u6a_err_bad_alloc(ctx->err_stage, size); return NULL; } - memcpy(dup_stack, vs, sizeof(struct vm_stack) + (vs->top + 1) * sizeof(struct u6a_vm_var_fn)); + memcpy(dup_stack, vs, sizeof(struct u6a_vm_stack) + (vs->top + 1) * sizeof(struct u6a_vm_var_fn)); dup_stack->refcnt = 0; for (uint32_t idx = vs->top; idx < UINT32_MAX; --idx) { struct u6a_vm_var_fn elem = vs->elems[idx]; if (elem.token.fn & U6A_VM_FN_REF) { - u6a_vm_pool_addref(elem.ref); + u6a_vm_pool_addref(ctx->pool_ctx->active_pool, elem.ref); } } if (vs->prev) { @@ -74,8 +62,8 @@ vm_stack_dup(struct vm_stack* vs) { } static inline void -vm_stack_free(struct vm_stack* vs) { - struct vm_stack* prev; +vm_stack_free(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs) { + struct u6a_vm_stack* prev; vs->refcnt = 1; do { prev = vs->prev; @@ -83,7 +71,7 @@ vm_stack_free(struct vm_stack* vs) { for (uint32_t idx = vs->top; idx < UINT32_MAX; --idx) { struct u6a_vm_var_fn elem = vs->elems[idx]; if (elem.token.fn & U6A_VM_FN_REF) { - u6a_vm_pool_free(elem.ref); + u6a_vm_pool_free(ctx->pool_ctx, elem.ref); } } free(vs); @@ -95,190 +83,139 @@ vm_stack_free(struct vm_stack* vs) { } bool -u6a_vm_stack_init(uint32_t stack_seg_len_, const char* err_stage_) { - stack_seg_len = stack_seg_len_; - err_stage = err_stage_; - active_stack = vm_stack_create(NULL, UINT32_MAX); - return active_stack != NULL; -} - -U6A_HOT struct u6a_vm_var_fn -u6a_vm_stack_top() { - struct vm_stack* vs = active_stack; - if (UNLIKELY(vs->top == UINT32_MAX)) { - vs = vs->prev; - if (UNLIKELY(vs == NULL)) { - return U6A_VM_VAR_FN_EMPTY; - } - active_stack = vs; - } - return vs->elems[vs->top]; +u6a_vm_stack_init(struct u6a_vm_stack_ctx* ctx, uint32_t stack_seg_len, const char* err_stage) { + ctx->stack_seg_len = stack_seg_len; + ctx->err_stage = err_stage; + ctx->active_stack = vm_stack_create(ctx, NULL, UINT32_MAX); + return ctx->active_stack != NULL; } // Boilerplates below. If only we have C++ templates here... (macros just make things nastier) U6A_HOT bool -u6a_vm_stack_push1(struct u6a_vm_var_fn v0) { - struct vm_stack* vs = active_stack; - if (LIKELY(vs->top + 1 < stack_seg_len)) { - vs->elems[++vs->top] = v0; - return true; - } - active_stack = vm_stack_create(vs, 0); - if (UNLIKELY(active_stack == NULL)) { - active_stack = vs; +u6a_vm_stack_push1_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0) { + struct u6a_vm_stack* vs = ctx->active_stack; + ctx->active_stack = vm_stack_create(ctx, vs, 0); + if (UNLIKELY(ctx->active_stack == NULL)) { + ctx->active_stack = vs; return false; } ++vs->refcnt; - active_stack->elems[0] = v0; + ctx->active_stack->elems[0] = v0; return true; } U6A_HOT bool -u6a_vm_stack_push2(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1) { - struct vm_stack* vs = active_stack; - if (LIKELY(vs->top + 2 < stack_seg_len)) { - vs->elems[++vs->top] = v0; - vs->elems[++vs->top] = v1; - return true; - } - active_stack = vm_stack_create(vs, 1); - if (UNLIKELY(active_stack == NULL)) { - active_stack = vs; +u6a_vm_stack_push2_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1) { + struct u6a_vm_stack* vs = ctx->active_stack; + ctx->active_stack = vm_stack_create(ctx, vs, 1); + if (UNLIKELY(ctx->active_stack == NULL)) { + ctx->active_stack = vs; return false; } ++vs->refcnt; - active_stack->elems[0] = v0; - active_stack->elems[1] = v1; + ctx->active_stack->elems[0] = v0; + ctx->active_stack->elems[1] = v1; return true; } U6A_HOT bool -u6a_vm_stack_push3(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, struct u6a_vm_var_fn v2) { - struct vm_stack* vs = active_stack; - if (LIKELY(vs->top + 3 < stack_seg_len)) { - vs->elems[++vs->top] = v0; - vs->elems[++vs->top] = v1; - vs->elems[++vs->top] = v2; - return true; - } - active_stack = vm_stack_create(vs, 2); - if (UNLIKELY(active_stack == NULL)) { - active_stack = vs; +u6a_vm_stack_push3_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2) +{ + struct u6a_vm_stack* vs = ctx->active_stack; + ctx->active_stack = vm_stack_create(ctx, vs, 2); + if (UNLIKELY(ctx->active_stack == NULL)) { + ctx->active_stack = vs; return false; } ++vs->refcnt; - active_stack->elems[0] = v0; - active_stack->elems[1] = v1; - active_stack->elems[2] = v2; + ctx->active_stack->elems[0] = v0; + ctx->active_stack->elems[1] = v1; + ctx->active_stack->elems[2] = v2; return true; } U6A_HOT bool -u6a_vm_stack_push4(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, - struct u6a_vm_var_fn v2, struct u6a_vm_var_fn v3) { - struct vm_stack* vs = active_stack; - if (LIKELY(vs->top + 4 < stack_seg_len)) { - vs->elems[++vs->top] = v0; - vs->elems[++vs->top] = v1; - vs->elems[++vs->top] = v2; - vs->elems[++vs->top] = v3; - return true; - } - active_stack = vm_stack_create(vs, 3); - if (UNLIKELY(active_stack == NULL)) { - active_stack = vs; +u6a_vm_stack_push4_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2, struct u6a_vm_var_fn v3) +{ + struct u6a_vm_stack* vs = ctx->active_stack; + ctx->active_stack = vm_stack_create(ctx, vs, 3); + if (UNLIKELY(ctx->active_stack == NULL)) { + ctx->active_stack = vs; return false; } ++vs->refcnt; - active_stack->elems[0] = v0; - active_stack->elems[1] = v1; - active_stack->elems[2] = v2; - active_stack->elems[3] = v3; + ctx->active_stack->elems[0] = v0; + ctx->active_stack->elems[1] = v1; + ctx->active_stack->elems[2] = v2; + ctx->active_stack->elems[3] = v3; return true; } U6A_HOT bool -u6a_vm_stack_pop() { - struct vm_stack* vs = active_stack; - if (LIKELY(vs->top-- != UINT32_MAX)) { - return true; - } - active_stack = vs->prev; - if (UNLIKELY(active_stack == NULL)) { - u6a_err_stack_underflow(err_stage); - active_stack = vs; +u6a_vm_stack_pop_split_(struct u6a_vm_stack_ctx* ctx) { + struct u6a_vm_stack* vs = ctx->active_stack; + ctx->active_stack = vs->prev; + if (UNLIKELY(ctx->active_stack == NULL)) { + u6a_err_stack_underflow(ctx->err_stage); + ctx->active_stack = vs; return false; } - if (--active_stack->refcnt > 0) { - active_stack = vm_stack_dup(active_stack); + if (--ctx->active_stack->refcnt > 0) { + ctx->active_stack = vm_stack_dup(ctx, ctx->active_stack); } - if (UNLIKELY(active_stack == NULL)) { - active_stack = vs; + if (UNLIKELY(ctx->active_stack == NULL)) { + ctx->active_stack = vs; return false; } free(vs); - --active_stack->top; + --ctx->active_stack->top; return true; } -struct u6a_vm_var_fn -u6a_vm_stack_xch(struct u6a_vm_var_fn v0) { - struct vm_stack* vs = active_stack; +U6A_HOT struct u6a_vm_var_fn +u6a_vm_stack_xch_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0) { + struct u6a_vm_stack* vs = ctx->active_stack; struct u6a_vm_var_fn elem; // XCH on segmented stacks is inefficient, perhaps there's a better solution? - if (LIKELY(vs->top != 0 && vs->top != UINT32_MAX)) { - elem = vs->elems[vs->top - 1]; - vs->elems[vs->top - 1] = v0; - } else { - struct vm_stack* prev = vs->prev; + struct u6a_vm_stack* prev = vs->prev; + if (UNLIKELY(prev == NULL)) { + u6a_err_stack_underflow(ctx->err_stage); + return U6A_VM_VAR_FN_EMPTY; + } + if (--prev->refcnt > 0) { + prev = vm_stack_dup(ctx, prev); if (UNLIKELY(prev == NULL)) { - u6a_err_stack_underflow(err_stage); return U6A_VM_VAR_FN_EMPTY; } - if (--prev->refcnt > 0) { - prev = vm_stack_dup(prev); - if (UNLIKELY(prev == NULL)) { - return U6A_VM_VAR_FN_EMPTY; - } - } - if (vs->top == 0) { - ++prev->refcnt; - vs->prev = prev; - elem = prev->elems[prev->top]; - prev->elems[prev->top] = v0; - } else { - free(vs); - active_stack = prev; - elem = prev->elems[prev->top - 1]; - prev->elems[prev->top - 1] = v0; - } + } + if (vs->top == 0) { + ++prev->refcnt; + vs->prev = prev; + elem = prev->elems[prev->top]; + prev->elems[prev->top] = v0; + } else { + free(vs); + ctx->active_stack = prev; + elem = prev->elems[prev->top - 1]; + prev->elems[prev->top - 1] = v0; } return elem; } -void* -u6a_vm_stack_save() { - return vm_stack_dup(active_stack); -} - -void* -u6a_vm_stack_dup(void* ptr) { - return vm_stack_dup(ptr); +struct u6a_vm_stack* +u6a_vm_stack_dup(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs) { + return vm_stack_dup(ctx, vs); } void -u6a_vm_stack_resume(void* ptr) { - u6a_vm_stack_destroy(); - active_stack = ptr; +u6a_vm_stack_discard(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs) { + vm_stack_free(ctx, vs); } void -u6a_vm_stack_discard(void* ptr) { - vm_stack_free(ptr); -} - -void -u6a_vm_stack_destroy() { - vm_stack_free(active_stack); +u6a_vm_stack_destroy(struct u6a_vm_stack_ctx* ctx) { + vm_stack_free(ctx, ctx->active_stack); } diff --git a/src/vm_stack.h b/src/vm_stack.h index c7b3755..d4234e2 100644 --- a/src/vm_stack.h +++ b/src/vm_stack.h @@ -26,46 +26,147 @@ #include #include +struct u6a_vm_stack { + struct u6a_vm_stack* prev; + uint32_t top; + uint32_t refcnt; + struct u6a_vm_var_fn elems[]; +}; + +struct u6a_vm_stack_ctx { + struct u6a_vm_stack* active_stack; + uint32_t stack_seg_len; + struct u6a_vm_pool_ctx* pool_ctx; + const char* err_stage; +}; + bool -u6a_vm_stack_init(uint32_t stack_seg_len, const char* err_stage); +u6a_vm_stack_init(struct u6a_vm_stack_ctx* ctx, uint32_t stack_seg_len, const char* err_stage); + +static inline struct u6a_vm_var_fn +u6a_vm_stack_top(struct u6a_vm_stack_ctx* ctx) { + struct u6a_vm_stack* vs = ctx->active_stack; + if (UNLIKELY(vs->top == UINT32_MAX)) { + vs = vs->prev; + if (UNLIKELY(vs == NULL)) { + return U6A_VM_VAR_FN_EMPTY; + } + ctx->active_stack = vs; + } + return vs->elems[vs->top]; +} + +bool +u6a_vm_stack_push1_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0); + +static inline bool +u6a_vm_stack_push1(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0) { + struct u6a_vm_stack* vs = ctx->active_stack; + if (LIKELY(vs->top + 1 < ctx->stack_seg_len)) { + vs->elems[++vs->top] = v0; + return true; + } + return u6a_vm_stack_push1_split_(ctx, v0); +} + +bool +u6a_vm_stack_push2_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1); + +static inline bool +u6a_vm_stack_push2(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1) { + struct u6a_vm_stack* vs = ctx->active_stack; + if (LIKELY(vs->top + 2 < ctx->stack_seg_len)) { + vs->elems[++vs->top] = v0; + vs->elems[++vs->top] = v1; + return true; + } + return u6a_vm_stack_push2_split_(ctx, v0, v1); +} + +bool +u6a_vm_stack_push3_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2); + +static inline bool +u6a_vm_stack_push3(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2) +{ + struct u6a_vm_stack* vs = ctx->active_stack; + if (LIKELY(vs->top + 3 < ctx->stack_seg_len)) { + vs->elems[++vs->top] = v0; + vs->elems[++vs->top] = v1; + vs->elems[++vs->top] = v2; + return true; + } + return u6a_vm_stack_push3_split_(ctx, v0, v1, v2); +} + +bool +u6a_vm_stack_push4_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2, struct u6a_vm_var_fn v3); + +static inline bool +u6a_vm_stack_push4(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, + struct u6a_vm_var_fn v2, struct u6a_vm_var_fn v3) +{ + struct u6a_vm_stack* vs = ctx->active_stack; + if (LIKELY(vs->top + 4 < ctx->stack_seg_len)) { + vs->elems[++vs->top] = v0; + vs->elems[++vs->top] = v1; + vs->elems[++vs->top] = v2; + vs->elems[++vs->top] = v3; + return true; + } + return u6a_vm_stack_push4_split_(ctx, v0, v1, v2, v3); +} + +bool +u6a_vm_stack_pop_split_(struct u6a_vm_stack_ctx* ctx); + +static inline bool +u6a_vm_stack_pop(struct u6a_vm_stack_ctx* ctx) { + struct u6a_vm_stack* vs = ctx->active_stack; + if (LIKELY(vs->top-- != UINT32_MAX)) { + return true; + } + return u6a_vm_stack_pop_split_(ctx); +} struct u6a_vm_var_fn -u6a_vm_stack_top(); +u6a_vm_stack_xch_split_(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0); -bool -u6a_vm_stack_push1(struct u6a_vm_var_fn v0); +static inline struct u6a_vm_var_fn +u6a_vm_stack_xch(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_var_fn v0) { + struct u6a_vm_stack* vs = ctx->active_stack; + struct u6a_vm_var_fn elem; + // XCH on segmented stacks is inefficient, perhaps there's a better solution? + if (LIKELY(vs->top != 0 && vs->top != UINT32_MAX)) { + elem = vs->elems[vs->top - 1]; + vs->elems[vs->top - 1] = v0; + } else { + elem = u6a_vm_stack_xch_split_(ctx, v0); + } + return elem; +} -bool -u6a_vm_stack_push2(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1); +struct u6a_vm_stack* +u6a_vm_stack_dup(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs); -// Functions push3 and push4 are made for the s2 function to alleviate overhead caused by hot split - -bool -u6a_vm_stack_push3(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, struct u6a_vm_var_fn v2); - -bool -u6a_vm_stack_push4(struct u6a_vm_var_fn v0, struct u6a_vm_var_fn v1, - struct u6a_vm_var_fn v2, struct u6a_vm_var_fn v3); - -bool -u6a_vm_stack_pop(); - -struct u6a_vm_var_fn -u6a_vm_stack_xch(struct u6a_vm_var_fn v1); - -void* -u6a_vm_stack_save(); - -void* -u6a_vm_stack_dup(void* ptr); +static inline struct u6a_vm_stack* +u6a_vm_stack_save(struct u6a_vm_stack_ctx* ctx) { + return u6a_vm_stack_dup(ctx, ctx->active_stack); +} void -u6a_vm_stack_resume(void* ptr); +u6a_vm_stack_discard(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs); void -u6a_vm_stack_discard(void* ptr); +u6a_vm_stack_destroy(struct u6a_vm_stack_ctx* ctx); -void -u6a_vm_stack_destroy(); +static inline void +u6a_vm_stack_resume(struct u6a_vm_stack_ctx* ctx, struct u6a_vm_stack* vs) { + u6a_vm_stack_destroy(ctx); + ctx->active_stack = vs; +} #endif