From 0165980111529caad8596f3248b8b511a7f793f1 Mon Sep 17 00:00:00 2001 From: Mid <> Date: Wed, 3 Sep 2025 01:03:45 +0300 Subject: [PATCH] Add concurrency --- lexer.c | 10 ++ lexer.h | 2 + main.c | 54 +++++--- parse.c | 63 +++++++--- table.h | 32 ++++- vm.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++---------- vm.h | 41 +++++- 7 files changed, 475 insertions(+), 104 deletions(-) diff --git a/lexer.c b/lexer.c index c4c60de..c8695e8 100644 --- a/lexer.c +++ b/lexer.c @@ -148,3 +148,13 @@ vec_Token ltokenize(const char *buf, size_t len) { return tokens; } + +void lfreetoks(vec_Token *toks) { + for(size_t i = 0; i < toks->size; i++) { + Token *tok = &toks->data[i]; + if(tok->text) { + free(tok->text); + } + } + vec_Token_drop(toks); +} \ No newline at end of file diff --git a/lexer.h b/lexer.h index c21d2e8..982629d 100644 --- a/lexer.h +++ b/lexer.h @@ -84,3 +84,5 @@ typedef struct Token { #undef i_header vec_Token ltokenize(const char *buf, size_t len); + +void lfreetoks(vec_Token*); \ No newline at end of file diff --git a/main.c b/main.c index 835f6b4..0d57d52 100644 --- a/main.c +++ b/main.c @@ -6,19 +6,31 @@ #include"str.h" #include"dump.h" -static size_t native_print(LVM *lvm, void *ud, size_t argn, LValue *values) { - if(lvalue_tag(values[0]) == LTAG_STRING) { - LString *lstr = (void*) (values[0].u & ~LTAG_MASK); +static size_t native_print(LVM *lvm, void *ud, size_t argn, LRegSet *regset) { + if(lvalue_tag(regset->regs[0]) == LTAG_STRING) { + LString *lstr = (void*) (regset->regs[0].u & ~LTAG_MASK); printf("%.*s\n", (int) lstr->length, lstr->data); - } else if(lvalue_tag(values[0]) == LTAG_I32) { - printf("%i\n", lvalue_to_int32(values[0])); - } else if(values[0].u == LTAG_NIL) { + } else if(lvalue_tag(regset->regs[0]) == LTAG_I32) { + printf("%i\n", lvalue_to_int32(regset->regs[0])); + } else if(regset->regs[0].u == LTAG_NIL) { printf("nil\n"); } return 0; } -int main() { +static char* read_full_file(const char *fn) { + FILE *f = fopen(fn, "rb"); + fseek(f, 0, SEEK_END); + size_t filesize = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = malloc(filesize + 1); + fread(buf, 1, filesize, f); + buf[filesize] = '\0'; + fclose(f); + return buf; +} + +int main(int argc, char **argv) { LTable *env = ltable_new(128); LString *key = lstring_newz("print"); @@ -26,19 +38,33 @@ int main() { ltable_set(env, lvalue_from_string(key), lvalue_from_func(func)); - const char *bufs = "for i = 1, 1000000 do print(i) if i % 3 == 0 then print(\"Fizz\") end if i % 5 == 0 then print(\"Buzz\") end end"; + //const char *bufs = "for i = 1, 10000 do print(i) if i % 3 == 0 then print(\"Fizz\") end if i % 5 == 0 then print(\"Buzz\") end end"; //const char *bufs = "local t = {a = 9} print(t.a)"; //const char *bufs = "z = 5 print(z)"; //const char *bufs = "local i = 0 while i ~= 1500000 do print(i) i = i + 1 end"; + //const char *bufs = "for i = 1, 1000 do print(print) end"; + + char *bufs = read_full_file(argv[1]); + vec_Token toks = ltokenize(bufs, strlen(bufs)); - LUnit *unit = lparse(toks.size, toks.data, env); - - dump(unit->funcs[0].lua_instrs); + free(bufs); - LValue regs[256]; - lvm_reset_regs(regs); + LUnit *unit = lparse(toks.size, toks.data, env); + + lfreetoks(&toks); + + //dump(unit->funcs[0].lua_instrs); LVM lvm = {}; - lvm_run(&lvm, &unit->funcs[0], 0, regs); + lvm_init(&lvm); + + //#pragma omp parallel for + for(int i = 0; i < 1; i++) { + LRegSet regset = {.parent = NULL}; + lvm_reset_regs(®set); + lvm_run(&lvm, &unit->funcs[0], 0, ®set); + } + + lvm_destroy(&lvm); } diff --git a/parse.c b/parse.c index 1eba0aa..6a040e3 100644 --- a/parse.c +++ b/parse.c @@ -98,6 +98,7 @@ typedef enum ExprKind { } ExprKind; typedef struct Expr { ExprKind kind; + struct Expr *next_to_die; union { struct { struct Expr *A; @@ -181,6 +182,19 @@ void free_vreg(Parser *P, int vreg) { assert(P->current_chunk.used_vregs[vreg] >= 0 && "Cannot free unused vreg"); } +static void scope_kill(Parser *P) { + Scope *parent = P->scope->parent; + + for(ScopeItem *si = P->scope->items; si;) { + ScopeItem *next = si->next; + free(si); + si = next; + } + free(P->scope); + + P->scope = parent; +} + void parse_chunk(Parser *P); int parse_functiondef(Parser *P, bool can_be_local) { @@ -251,7 +265,7 @@ int parse_functiondef(Parser *P, bool can_be_local) { size_t function_idx = P->unit_functions.size - 1; P->current_chunk = old_chunk; - P->scope = P->scope->parent; + scope_kill(P); int vreg = find_vreg(P); assert(vreg != -1); @@ -315,6 +329,17 @@ vec_Token parse_namelist(Parser *P) { return v; } +static Expr *last_desc = NULL; +static Expr *mark_for_death(Expr *e) { + e->next_to_die = last_desc; + last_desc = e; + return e; +} +static Expr *new_expr(size_t space) { + Expr *e = calloc(1, sizeof(*e) + space); + mark_for_death(e); + return e; +} Expr *desc_subexp(Parser *P, int priority) { if(priority == 0) { Expr *a = desc_subexp(P, priority + 1); @@ -324,7 +349,7 @@ Expr *desc_subexp(Parser *P, int priority) { Expr *b = desc_subexp(P, priority + 1); - Expr *opex = calloc(1, sizeof(*opex)); + Expr *opex = new_expr(0); opex->A = a; opex->B = b; @@ -346,7 +371,7 @@ Expr *desc_subexp(Parser *P, int priority) { Expr *b = desc_subexp(P, priority + 1); - Expr *opex = calloc(1, sizeof(*opex)); + Expr *opex = new_expr(0); opex->A = a; opex->B = b; @@ -368,7 +393,7 @@ Expr *desc_subexp(Parser *P, int priority) { Expr *b = desc_subexp(P, priority + 1); - Expr *opex = calloc(1, sizeof(*opex)); + Expr *opex = new_expr(0); opex->A = a; opex->B = b; @@ -390,11 +415,11 @@ Expr *desc_subexp(Parser *P, int priority) { Expr *e = NULL; if(maybe(P, TOK_TRUE)) { - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = EX_BOOL; e->b = true; } else if(maybe(P, TOK_FALSE)) { - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = EX_BOOL; e->b = false; } else if(maybe(P, TOK_NUMBER)) { @@ -403,7 +428,7 @@ Expr *desc_subexp(Parser *P, int priority) { Token num = expect(P, TOK_NUMBER); long i = strtol(num.text, NULL, 10); - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = EX_INT; e->i = i; } else if(maybe(P, TOK_NAME)) { @@ -413,7 +438,7 @@ Expr *desc_subexp(Parser *P, int priority) { ScopeItem *si = scope_find(P->scope, name.text); - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = si ? EX_LOCAL : EX_GLOBAL; e->name = name; } else if(maybe(P, TOK_STRING)) { @@ -421,11 +446,11 @@ Expr *desc_subexp(Parser *P, int priority) { Token str = expect(P, TOK_STRING); - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = EX_STR; e->name = str; } else if(maybe(P, TOK_SQUIGGLY_L)) { - e = calloc(1, sizeof(*e)); + e = new_expr(0); e->kind = EX_TBL_LIT; e->table_first_token = P->i - 1; @@ -448,15 +473,14 @@ Expr *desc_subexp(Parser *P, int priority) { if(e) { while(maybe(P, TOK_PAREN_L) || maybe(P, TOK_DOT)) { if(peek(P, -1).type == TOK_PAREN_L) { - Expr *call = calloc(1, sizeof(*call) + sizeof(Expr*)); + Expr *call = new_expr(sizeof(Expr*) + sizeof(Expr*) * 32); call->kind = EX_CALL; call->sub_count = 1; call->subs[0] = e; if(!maybe(P, TOK_PAREN_R)) { while(1) { - call = realloc(call, sizeof(*call) + sizeof(Expr*) * (++call->sub_count)); - call->subs[call->sub_count - 1] = desc_exp(P); + call->subs[call->sub_count++] = desc_exp(P); if(maybe(P, TOK_PAREN_R)) { break; @@ -468,7 +492,7 @@ Expr *desc_subexp(Parser *P, int priority) { e = call; } else if(peek(P, -1).type == TOK_DOT) { - Expr *dot = calloc(1, sizeof(*dot)); + Expr *dot = new_expr(0); dot->kind = EX_INDEX; dot->A = e; dot->B_tok = expect(P, TOK_NAME); @@ -932,7 +956,7 @@ bool parse_stat(Parser *P) { P->current_chunk.instrs.data[jump2end].bc = P->current_chunk.instrs.size - 1 - jump2end; - P->scope = P->scope->parent; + scope_kill(P); } else if(maybe(P, TOK_FOR)) { if(peek(P, 0).type == TOK_NAME && peek(P, 1).type == TOK_EQUAL) { // Range loop @@ -985,7 +1009,7 @@ bool parse_stat(Parser *P) { P->current_chunk.instrs.data[jump2end].bc = P->current_chunk.instrs.size - 1 - jump2end; - P->scope = P->scope->parent; + scope_kill(P); expect(P, TOK_END); @@ -1049,5 +1073,12 @@ LUnit *lparse(size_t sz, Token *tokens, LTable *environment) { unit->func_count = 1; unit->funcs = P.unit_functions.data; + for(Expr *e = last_desc; e;) { + Expr *n = e->next_to_die; + free(e); + e = n; + } + last_desc = NULL; + return unit; } diff --git a/table.h b/table.h index 6ed90e7..939b480 100644 --- a/table.h +++ b/table.h @@ -59,7 +59,7 @@ static inline bool ltablebuckets_set(LTableBuckets *self, LValue key, LValue val LValue prevKey = {.u = LTAG_NIL}; atomic_compare_exchange_strong(¤t[idx].key.u, &prevKey.u, key.u); - if(prevKey.u == LTAG_NIL || prevKey.u == key.u) { + if(prevKey.u == LTAG_NIL || lvalue_eq(prevKey, key)) { atomic_store(¤t[idx].val.u, val.u); break; } @@ -76,11 +76,37 @@ static inline bool ltablebuckets_set(LTableBuckets *self, LValue key, LValue val } static inline void ltable_set(LTable *self, LValue key, LValue val) { + if(lvalue_tag(key) == LTAG_I32 && lvalue_to_int32(key) < 9007199254740993UL) { + key = lvalue_from_double(lvalue_to_int32(key)); + } + if(!ltablebuckets_set(self->buckets, key, val)) { assert(0 && "No table resizing"); } } +static inline bool ltable_set_no_overwrite(LTable *tbl, LValue key, LValue val) { + LTableBuckets *self = tbl->buckets; + + size_t idx = lvalue_hash(key); + LTableEntry *current = self->data; + while(1) { + idx &= self->capacity - 1; + + LValue prevKey = {.u = LTAG_NIL}; + atomic_compare_exchange_strong(¤t[idx].key.u, &prevKey.u, key.u); + + if(prevKey.u == LTAG_NIL) { + atomic_store(¤t[idx].val.u, val.u); + return true; + } else if(lvalue_eq(prevKey, key)) { + return false; + } + + idx++; + } +} + static inline LValue ltablebuckets_get(LTableBuckets *self, LValue key) { size_t idx = lvalue_hash(key); @@ -103,5 +129,9 @@ static inline LValue ltablebuckets_get(LTableBuckets *self, LValue key) { } static inline LValue ltable_get(LTable *self, LValue key) { + if(lvalue_tag(key) == LTAG_I32 && lvalue_to_int32(key) < 9007199254740993UL) { + key = lvalue_from_double(lvalue_to_int32(key)); + } + return ltablebuckets_get(self->buckets, key); } diff --git a/vm.c b/vm.c index 2c24aef..cdf4483 100644 --- a/vm.c +++ b/vm.c @@ -5,13 +5,14 @@ #include"str.h" #include +#include -size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) { +static size_t lvm_run_internal(LVM *L, LFunc *func, size_t arg_count, set_LValueU *heap, LRegSet *regset) { if(func->is_native) { - return func->native_func(L, func->ud, arg_count, regs); + return func->native_func(L, func->ud, arg_count, regset); } - static void *dispatch_table[] = { + static const void *dispatch_table[] = { [L_GETGLOBAL] = &&do_getglobal, [L_SETGLOBAL] = &&do_setglobal, [L_SETINT16] = &&do_setint16, @@ -44,6 +45,8 @@ size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) { LInst *inst = func->lua_instrs; #define DISPATCH() goto *dispatch_table[(++inst)->opcode] + LThreadPrivates privates = {.regset = regset, .heap = heap}; + inst--; DISPATCH(); @@ -53,9 +56,15 @@ do_getglobal:; size_t len = *(uint16_t*) area; area += 2; - LString *str = lstring_new(len, area); - regs[inst->a] = ltable_get(func->env, lvalue_from_string(str)); - lstring_free(str); + LString *str = realloc(NULL, sizeof(*str) + len); + str->length = len; + memcpy(str->data, area, len); + + regset->regs[inst->a] = ltable_get(func->env, lvalue_from_string(str)); + + set_LValueU_insert(heap, lvalue_from_string(str).u); + + lvm_gc_alert(L, &privates, sizeof(*str) + len); } DISPATCH(); @@ -65,18 +74,24 @@ do_setglobal:; size_t len = *(uint16_t*) area; area += 2; - LString *str = lstring_new(len, area); - ltable_set(func->env, lvalue_from_string(str), regs[inst->a]); - lvm_gc_add(L, lvalue_from_string(str)); + LString *str = realloc(NULL, sizeof(*str) + len); + str->length = len; + memcpy(str->data, area, len); + + ltable_set(func->env, lvalue_from_string(str), regset->regs[inst->a]); + + set_LValueU_insert(heap, lvalue_from_string(str).u); + + lvm_gc_alert(L, &privates, sizeof(*str) + len); } DISPATCH(); do_setint16:; - regs[inst->a] = lvalue_from_int32((int16_t) inst->bc); + regset->regs[inst->a] = lvalue_from_int32((int16_t) inst->bc); DISPATCH(); do_setint32:; - regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]); + regset->regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]); DISPATCH(); do_setfloat:; @@ -88,87 +103,94 @@ do_setstr:; size_t len = *(uint16_t*) area; area += 2; - regs[inst->a] = lvalue_raw(LTAG_STRING, (uintptr_t) lstring_new(len, area)); - lvm_gc_add(L, regs[inst->a]); + LString *str = realloc(NULL, sizeof(*str) + len); + str->length = len; + memcpy(str->data, area, len); + + regset->regs[inst->a] = lvalue_from_string(str); + + set_LValueU_insert(heap, lvalue_from_string(str).u); + + lvm_gc_alert(L, &privates, sizeof(*str) + len); } DISPATCH(); do_settable:; { LTable *tbl = ltable_new(inst->bc); - lvm_gc_add(L, lvalue_from_table(tbl)); - regs[inst->a] = lvalue_from_table(tbl); + regset->regs[inst->a] = lvalue_from_table(tbl); + set_LValueU_insert(heap, lvalue_from_table(tbl).u); } DISPATCH(); do_setbool:; - regs[inst->a] = lvalue_from_bool(inst->b); + regset->regs[inst->a] = lvalue_from_bool(inst->b); DISPATCH(); do_setnil:; - regs[inst->a] = lvalue_from_nil(); + regset->regs[inst->a] = lvalue_from_nil(); DISPATCH(); do_setfunc:; - regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]); + regset->regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]); DISPATCH(); do_add:; { - LValue x = regs[inst->b]; - LValue y = regs[inst->c]; + LValue x = regset->regs[inst->b]; + LValue y = regset->regs[inst->c]; if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { - regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f); + regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f); } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y)); } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y)); } else goto err; } DISPATCH(); do_sub:; { - LValue x = regs[inst->b]; - LValue y = regs[inst->c]; + LValue x = regset->regs[inst->b]; + LValue y = regset->regs[inst->c]; if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { - regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f); + regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f); } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y)); } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y)); } else goto err; } DISPATCH(); do_mul:; { - LValue x = regs[inst->b]; - LValue y = regs[inst->c]; + LValue x = regset->regs[inst->b]; + LValue y = regset->regs[inst->c]; if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { - regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f); + regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f); } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y)); } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y)); } else goto err; } DISPATCH(); do_div:; { - LValue x = regs[inst->b]; - LValue y = regs[inst->c]; + LValue x = regset->regs[inst->b]; + LValue y = regset->regs[inst->c]; if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { - regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f); + regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f); } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) { - regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y)); + regset->regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y)); } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) { int32_t yv = lvalue_to_int32(y); if(yv == 0) { - regs[inst->a] = lvalue_from_nil(); + regset->regs[inst->a] = lvalue_from_nil(); } else { - regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv); + regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv); } } else goto err; } @@ -176,19 +198,19 @@ do_div:; do_mod:; { - LValue x = regs[inst->b]; - LValue y = regs[inst->c]; + LValue x = regset->regs[inst->b]; + LValue y = regset->regs[inst->c]; if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { - regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f)); + regset->regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f)); } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) { int32_t yv = lvalue_to_int32(y); - regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv)); + regset->regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv)); } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) { int32_t yv = lvalue_to_int32(y); if(yv == 0) { goto err; } else { - regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv); + regset->regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv); } } else goto err; } @@ -196,11 +218,13 @@ do_mod:; do_jump:; inst += (int16_t) inst->bc; + + L->safepoint_func(L, heap, regset); DISPATCH(); do_jnotcond:; { - LValue v = regs[inst->a]; + LValue v = regset->regs[inst->a]; if(v.u == LTAG_NIL || v.u == LTAG_FALSE) { inst += (int16_t) inst->bc; } @@ -209,7 +233,7 @@ do_jnotcond:; do_call:; { - if(lvalue_tag(regs[inst->a]) != LTAG_FUNCTION) { + if(lvalue_tag(regset->regs[inst->a]) != LTAG_FUNCTION) { goto err; } @@ -218,30 +242,30 @@ do_call:; uint8_t ret_vreg = abyss_data[0]; uint8_t arg_count = abyss_data[1]; uint8_t *args = &abyss_data[2]; - - LValue regs2[256]; - lvm_reset_regs(regs2); + + LRegSet regset2 = {.parent = regset}; + lvm_reset_regs(®set2); for(int i = 0; i < arg_count; i++) { - regs2[i] = regs[args[i]]; + regset2.regs[i] = regset->regs[args[i]]; } - size_t returned_count = lvm_run(L, (LFunc*) (regs[inst->a].u & ~LTAG_MASK), arg_count, regs2); + size_t returned_count = lvm_run_internal(L, (LFunc*) (regset->regs[inst->a].u & ~LTAG_MASK), arg_count, heap, ®set2); if(returned_count) { // TODO: more than 1 return - regs[ret_vreg] = regs2[0]; + regset->regs[ret_vreg] = regset2.regs[0]; } } DISPATCH(); do_move:; - regs[inst->a] = regs[inst->b]; + regset->regs[inst->a] = regset->regs[inst->b]; DISPATCH(); do_advancetest:; { - int64_t a = lvalue_to_int32(regs[inst->a]); - int64_t b = lvalue_to_int32(regs[inst->b]); - int64_t c = lvalue_to_int32(regs[inst->c]); + int64_t a = lvalue_to_int32(regset->regs[inst->a]); + int64_t b = lvalue_to_int32(regset->regs[inst->b]); + int64_t c = lvalue_to_int32(regset->regs[inst->c]); if(!((c >= 0 && a > b) || (c < 0 && a < b))) { inst++; } @@ -249,49 +273,65 @@ do_advancetest:; DISPATCH(); do_cond_eq:; - regs[inst->a] = lvalue_from_bool(lvalue_eq(regs[inst->b], regs[inst->c])); + regset->regs[inst->a] = lvalue_from_bool(lvalue_eq(regset->regs[inst->b], regset->regs[inst->c])); DISPATCH(); do_cond_neq:; - regs[inst->a] = lvalue_from_bool(!lvalue_eq(regs[inst->b], regs[inst->c])); + regset->regs[inst->a] = lvalue_from_bool(!lvalue_eq(regset->regs[inst->b], regset->regs[inst->c])); DISPATCH(); do_setfield:; { - if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) { + if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) { goto err; } - if(lvalue_tag(regs[inst->b]) == LTAG_NIL) { + if(lvalue_tag(regset->regs[inst->b]) == LTAG_NIL) { goto err; } - LTable *tbl = (void*) (regs[inst->a].u & ~LTAG_MASK); + LTable *tbl = (void*) (regset->regs[inst->a].u & ~LTAG_MASK); - ltable_set(tbl, regs[inst->b], regs[inst->c]); + ltable_set(tbl, regset->regs[inst->b], regset->regs[inst->c]); } DISPATCH(); do_getfield:; { - if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) { + if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) { goto err; } - LTable *tbl = (void*) (regs[inst->b].u & ~LTAG_MASK); + LTable *tbl = (void*) (regset->regs[inst->b].u & ~LTAG_MASK); - regs[inst->a] = ltable_get(tbl, regs[inst->c]); + regset->regs[inst->a] = ltable_get(tbl, regset->regs[inst->c]); } DISPATCH(); err:; + puts("Error"); do_ret:; return 0; } -void lvm_gc_add(LVM *L, LValue lvalue) { - set_LValueU_insert(&L->gc_objects, lvalue.u); +size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LRegSet *regset) { + set_LValueU heap = {}; + + atomic_fetch_add(&L->active_thread_count, 1); + + size_t ret = lvm_run_internal(L, func, arg_count, &heap, regset); + + mtx_lock(&L->dead_heap_mut); + for(c_each(i, set_LValueU, heap)) { + set_LValueU_insert(&L->dead_heap, *i.ref); + } + atomic_fetch_sub(&L->active_thread_count, 1); + mtx_unlock(&L->dead_heap_mut); + + set_LValueU_drop(&heap); + + return ret; } LFunc *lvm_func_from_native(LFuncCallback cb, void *ud) { @@ -324,3 +364,206 @@ bool lvalue_eq(LValue a, LValue b) { return false; } + +static void gc_unmark_heap(set_LValueU *heap) { + for(c_each(i, set_LValueU, *heap)) { + LValue v = (LValue) {.u = *i.ref}; + + assert(lvalue_tag(v) == LTAG_TABLE || lvalue_tag(v) == LTAG_STRING); + + void *gco = (void*) (v.u & ~LTAG_MASK); + + if(lvalue_tag(v) == LTAG_TABLE) { + LTable *tbl = gco; + tbl->ref = false; + } else if(lvalue_tag(v) == LTAG_STRING) { + LString *str = gco; + str->ref = false; + } + } +} +static void gc_unmark_all(LVM *L, size_t thread_count) { + for(size_t thrd = 0; thrd < thread_count; thrd++) { + LThreadPrivates *privates = &L->privates[thrd]; + gc_unmark_heap(privates->heap); + } + gc_unmark_heap(&L->dead_heap); +} +static void gc_mark(LValue v) { + if(lvalue_tag(v) != LTAG_TABLE && lvalue_tag(v) != LTAG_STRING) { + return; + } + + void *gco = (void*) (v.u & ~LTAG_MASK); + + if(lvalue_tag(v) == LTAG_TABLE) { + LTable *tbl = gco; + tbl->ref = true; + + for(size_t i = 0; tbl->buckets->capacity; i++) { + LTableEntry e = tbl->buckets->data[i]; + gc_mark(e.key); + gc_mark(e.val); + } + } else if(lvalue_tag(v) == LTAG_STRING) { + LString *str = gco; + str->ref = true; + } +} +static void gc_mark_units(LVM *L) { + for(size_t u = 0; u < L->unit_count; u++) { + LUnit *unit = &L->units[u]; + for(size_t f = 0; f < unit->func_count; f++) { + LFunc *func = &unit->funcs[f]; + gc_mark(lvalue_from_table(func->env)); + for(size_t upv = 0; upv < func->upvalue_count; upv++) { + gc_mark(func->upvalues[upv]); + } + } + } +} +static void safepoint_active(LVM *L, set_LValueU *heap, LRegSet *regset) { + size_t my_privates_index = atomic_fetch_add(&L->privates_index, 1); + + L->privates[my_privates_index].heap = heap; + L->privates[my_privates_index].regset = regset; + + atomic_fetch_add(&L->privates_ready, 1); + + // Wait until GC finishes + while(atomic_load(&L->safepoint_func) == safepoint_active); + + atomic_fetch_sub(&L->privates_ready, 1); +} +static void gc_mark_stacks(LVM *L, size_t thread_count) { + for(size_t thrd = 0; thrd < thread_count; thrd++) { + LThreadPrivates *privates = &L->privates[thrd]; + + LRegSet *rset = privates->regset; + while(rset) { + for(size_t r = 0; r < 256; r++) { + gc_mark(rset->regs[r]); + } + rset = rset->parent; + } + } +} +static void safepoint_inactive(LVM *L, set_LValueU *heap, LRegSet *regset) { +} +static void gc_delete_unmarked_in_heap(LVM *L, set_LValueU *heap) { + for(set_LValueU_iter i = set_LValueU_begin(heap); i.ref;) { + LValue v = (LValue) {.u = *i.ref}; + + void *gco = (void*) (v.u & ~LTAG_MASK); + + if(lvalue_tag(v) == LTAG_TABLE) { + LTable *tbl = gco; + + if(tbl->ref == false) { + free(tbl->buckets); + free(tbl); + + i = set_LValueU_erase_at(heap, i); + continue; + } + } else if(lvalue_tag(v) == LTAG_STRING) { + LString *str = gco; + + if(str->ref == false) { + lvm_gc_alert(L, NULL, -sizeof(*str) - str->length); + + free(str); + + i = set_LValueU_erase_at(heap, i); + continue; + } + } + + set_LValueU_next(&i); + } +} +static void gc_delete_unmarked(LVM *L, size_t thread_count) { + for(size_t thrd = 0; thrd < thread_count; thrd++) { + LThreadPrivates *privates = &L->privates[thrd]; + gc_delete_unmarked_in_heap(L, privates->heap); + } + gc_delete_unmarked_in_heap(L, &L->dead_heap); +} +static void lvm_gc_force(LVM *L, LThreadPrivates *callerPrivates) { + // At most one thread can force GC, while others must behave as usual and enter a safepoint instead + if(atomic_compare_exchange_strong(&L->gcInProgress, &(bool) {false}, true)) { + + //static size_t gcidx = 0; + //fprintf(stderr, "GC %i (%lu bytes)\n", atomic_fetch_add(&gcidx, 1), L->memUsage); + + if(callerPrivates) { + // Called from within VM + atomic_store(&L->privates_index, 1); + atomic_store(&L->privates_ready, 1); + + L->privates[0] = *callerPrivates; + } else { + // Called outside of VM, probably by lvm_destroy + atomic_store(&L->privates_index, 0); + atomic_store(&L->privates_ready, 0); + } + + L->safepoint_func = safepoint_active; + + // Wait until other threads have entered GC stage + while(atomic_load(&L->privates_ready) < atomic_load(&L->active_thread_count)); + + size_t thread_count = atomic_load(&L->privates_ready); + + mtx_lock(&L->dead_heap_mut); + gc_unmark_all(L, thread_count); + gc_mark_stacks(L, thread_count); + gc_mark_units(L); + gc_delete_unmarked(L, thread_count); + mtx_unlock(&L->dead_heap_mut); + + while(L->memUsage > L->nextGCThreshold) { + L->nextGCThreshold <<= 1; + } + + L->safepoint_func = safepoint_inactive; + + if(callerPrivates) { + // Called from within VM + atomic_fetch_sub(&L->privates_ready, 1); + } + + // Wait until other threads have left GC stage + while(atomic_load(&L->privates_ready) > 0); + + atomic_store(&L->gcInProgress, false); + + } +} + +// `privates` can be NULL but ONLY IF diff < 0 +void lvm_gc_alert(LVM *L, LThreadPrivates *privates, intmax_t diff) { + L->memUsage += diff; + + assert(L->memUsage >= 0); + + if(L->memUsage > L->nextGCThreshold) { + lvm_gc_force(L, privates); + } +} + +void lvm_init(LVM *L) { + memset(L, 0, sizeof(*L)); + + L->safepoint_func = safepoint_inactive; + L->nextGCThreshold = 16384; + mtx_init(&L->dead_heap_mut, mtx_plain); +} + +void lvm_destroy(LVM *L) { + mtx_destroy(&L->dead_heap_mut); + + lvm_gc_force(L, NULL); + + set_LValueU_drop(&L->dead_heap); +} diff --git a/vm.h b/vm.h index 60c6520..cd9a19c 100644 --- a/vm.h +++ b/vm.h @@ -7,6 +7,7 @@ #include #include #include +#include #include"table.h" @@ -68,7 +69,12 @@ typedef union __attribute__((packed)) { struct LUnit; struct LVM; -typedef size_t(*LFuncCallback)(struct LVM*, void *ud, size_t argn, LValue *args); +typedef struct LRegSet { + struct LRegSet *parent; + LValue regs[256]; +} LRegSet; + +typedef size_t(*LFuncCallback)(struct LVM*, void *ud, size_t argn, LRegSet *regset); typedef struct LFunc { struct LUnit *unit; bool is_native; @@ -98,20 +104,43 @@ typedef struct LUnit { #include"stc/hashset.h" #undef i_header +typedef struct LThreadPrivates { + set_LValueU *heap; + LRegSet *regset; +} LThreadPrivates; + +#define L_THREADS_MAX 32 typedef struct LVM { size_t unit_count; LUnit *units; - set_LValueU gc_objects; + // The following is all used for GC + _Atomic bool gcInProgress; + _Atomic size_t nextGCThreshold; + _Atomic intmax_t memUsage; + _Atomic size_t active_thread_count; + _Atomic size_t privates_index; + _Atomic size_t privates_ready; + LThreadPrivates privates[L_THREADS_MAX]; + void(*safepoint_func)(struct LVM*, set_LValueU*, LRegSet*); + + // The dead heap stores the heap of threads that have exited + // This is rare enough that it's done with a lock + mtx_t dead_heap_mut; + set_LValueU dead_heap; } LVM; -size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs); -void lvm_gc_add(LVM *L, LValue lvalue); +size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LRegSet *regset); LFunc *lvm_func_from_native(LFuncCallback, void *ud); -static inline void lvm_reset_regs(LValue *regs) { +void lvm_init(LVM *L); +void lvm_destroy(LVM *L); + +void lvm_gc_alert(LVM *L, LThreadPrivates*, intmax_t diff); + +static inline void lvm_reset_regs(LRegSet *regset) { for(int i = 0; i < 256; i++) { - regs[i] = lvalue_from_nil(); + regset->regs[i] = lvalue_from_nil(); } }