Add concurrency

This commit is contained in:
Mid 2025-09-03 01:03:45 +03:00
parent 64c21ca43a
commit 0165980111
7 changed files with 475 additions and 104 deletions

10
lexer.c
View File

@ -148,3 +148,13 @@ vec_Token ltokenize(const char *buf, size_t len) {
return tokens; 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);
}

View File

@ -84,3 +84,5 @@ typedef struct Token {
#undef i_header #undef i_header
vec_Token ltokenize(const char *buf, size_t len); vec_Token ltokenize(const char *buf, size_t len);
void lfreetoks(vec_Token*);

54
main.c
View File

@ -6,19 +6,31 @@
#include"str.h" #include"str.h"
#include"dump.h" #include"dump.h"
static size_t native_print(LVM *lvm, void *ud, size_t argn, LValue *values) { static size_t native_print(LVM *lvm, void *ud, size_t argn, LRegSet *regset) {
if(lvalue_tag(values[0]) == LTAG_STRING) { if(lvalue_tag(regset->regs[0]) == LTAG_STRING) {
LString *lstr = (void*) (values[0].u & ~LTAG_MASK); LString *lstr = (void*) (regset->regs[0].u & ~LTAG_MASK);
printf("%.*s\n", (int) lstr->length, lstr->data); printf("%.*s\n", (int) lstr->length, lstr->data);
} else if(lvalue_tag(values[0]) == LTAG_I32) { } else if(lvalue_tag(regset->regs[0]) == LTAG_I32) {
printf("%i\n", lvalue_to_int32(values[0])); printf("%i\n", lvalue_to_int32(regset->regs[0]));
} else if(values[0].u == LTAG_NIL) { } else if(regset->regs[0].u == LTAG_NIL) {
printf("nil\n"); printf("nil\n");
} }
return 0; 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); LTable *env = ltable_new(128);
LString *key = lstring_newz("print"); LString *key = lstring_newz("print");
@ -26,19 +38,33 @@ int main() {
ltable_set(env, lvalue_from_string(key), lvalue_from_func(func)); 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 = "local t = {a = 9} print(t.a)";
//const char *bufs = "z = 5 print(z)"; //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 = "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)); vec_Token toks = ltokenize(bufs, strlen(bufs));
LUnit *unit = lparse(toks.size, toks.data, env); free(bufs);
dump(unit->funcs[0].lua_instrs);
LValue regs[256]; LUnit *unit = lparse(toks.size, toks.data, env);
lvm_reset_regs(regs);
lfreetoks(&toks);
//dump(unit->funcs[0].lua_instrs);
LVM lvm = {}; 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(&regset);
lvm_run(&lvm, &unit->funcs[0], 0, &regset);
}
lvm_destroy(&lvm);
} }

63
parse.c
View File

@ -98,6 +98,7 @@ typedef enum ExprKind {
} ExprKind; } ExprKind;
typedef struct Expr { typedef struct Expr {
ExprKind kind; ExprKind kind;
struct Expr *next_to_die;
union { union {
struct { struct {
struct Expr *A; 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"); 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); void parse_chunk(Parser *P);
int parse_functiondef(Parser *P, bool can_be_local) { 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; size_t function_idx = P->unit_functions.size - 1;
P->current_chunk = old_chunk; P->current_chunk = old_chunk;
P->scope = P->scope->parent; scope_kill(P);
int vreg = find_vreg(P); int vreg = find_vreg(P);
assert(vreg != -1); assert(vreg != -1);
@ -315,6 +329,17 @@ vec_Token parse_namelist(Parser *P) {
return v; 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) { Expr *desc_subexp(Parser *P, int priority) {
if(priority == 0) { if(priority == 0) {
Expr *a = desc_subexp(P, priority + 1); 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 *b = desc_subexp(P, priority + 1);
Expr *opex = calloc(1, sizeof(*opex)); Expr *opex = new_expr(0);
opex->A = a; opex->A = a;
opex->B = b; opex->B = b;
@ -346,7 +371,7 @@ Expr *desc_subexp(Parser *P, int priority) {
Expr *b = desc_subexp(P, priority + 1); Expr *b = desc_subexp(P, priority + 1);
Expr *opex = calloc(1, sizeof(*opex)); Expr *opex = new_expr(0);
opex->A = a; opex->A = a;
opex->B = b; opex->B = b;
@ -368,7 +393,7 @@ Expr *desc_subexp(Parser *P, int priority) {
Expr *b = desc_subexp(P, priority + 1); Expr *b = desc_subexp(P, priority + 1);
Expr *opex = calloc(1, sizeof(*opex)); Expr *opex = new_expr(0);
opex->A = a; opex->A = a;
opex->B = b; opex->B = b;
@ -390,11 +415,11 @@ Expr *desc_subexp(Parser *P, int priority) {
Expr *e = NULL; Expr *e = NULL;
if(maybe(P, TOK_TRUE)) { if(maybe(P, TOK_TRUE)) {
e = calloc(1, sizeof(*e)); e = new_expr(0);
e->kind = EX_BOOL; e->kind = EX_BOOL;
e->b = true; e->b = true;
} else if(maybe(P, TOK_FALSE)) { } else if(maybe(P, TOK_FALSE)) {
e = calloc(1, sizeof(*e)); e = new_expr(0);
e->kind = EX_BOOL; e->kind = EX_BOOL;
e->b = false; e->b = false;
} else if(maybe(P, TOK_NUMBER)) { } else if(maybe(P, TOK_NUMBER)) {
@ -403,7 +428,7 @@ Expr *desc_subexp(Parser *P, int priority) {
Token num = expect(P, TOK_NUMBER); Token num = expect(P, TOK_NUMBER);
long i = strtol(num.text, NULL, 10); long i = strtol(num.text, NULL, 10);
e = calloc(1, sizeof(*e)); e = new_expr(0);
e->kind = EX_INT; e->kind = EX_INT;
e->i = i; e->i = i;
} else if(maybe(P, TOK_NAME)) { } 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); 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->kind = si ? EX_LOCAL : EX_GLOBAL;
e->name = name; e->name = name;
} else if(maybe(P, TOK_STRING)) { } else if(maybe(P, TOK_STRING)) {
@ -421,11 +446,11 @@ Expr *desc_subexp(Parser *P, int priority) {
Token str = expect(P, TOK_STRING); Token str = expect(P, TOK_STRING);
e = calloc(1, sizeof(*e)); e = new_expr(0);
e->kind = EX_STR; e->kind = EX_STR;
e->name = str; e->name = str;
} else if(maybe(P, TOK_SQUIGGLY_L)) { } else if(maybe(P, TOK_SQUIGGLY_L)) {
e = calloc(1, sizeof(*e)); e = new_expr(0);
e->kind = EX_TBL_LIT; e->kind = EX_TBL_LIT;
e->table_first_token = P->i - 1; e->table_first_token = P->i - 1;
@ -448,15 +473,14 @@ Expr *desc_subexp(Parser *P, int priority) {
if(e) { if(e) {
while(maybe(P, TOK_PAREN_L) || maybe(P, TOK_DOT)) { while(maybe(P, TOK_PAREN_L) || maybe(P, TOK_DOT)) {
if(peek(P, -1).type == TOK_PAREN_L) { 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->kind = EX_CALL;
call->sub_count = 1; call->sub_count = 1;
call->subs[0] = e; call->subs[0] = e;
if(!maybe(P, TOK_PAREN_R)) { if(!maybe(P, TOK_PAREN_R)) {
while(1) { while(1) {
call = realloc(call, sizeof(*call) + sizeof(Expr*) * (++call->sub_count)); call->subs[call->sub_count++] = desc_exp(P);
call->subs[call->sub_count - 1] = desc_exp(P);
if(maybe(P, TOK_PAREN_R)) { if(maybe(P, TOK_PAREN_R)) {
break; break;
@ -468,7 +492,7 @@ Expr *desc_subexp(Parser *P, int priority) {
e = call; e = call;
} else if(peek(P, -1).type == TOK_DOT) { } else if(peek(P, -1).type == TOK_DOT) {
Expr *dot = calloc(1, sizeof(*dot)); Expr *dot = new_expr(0);
dot->kind = EX_INDEX; dot->kind = EX_INDEX;
dot->A = e; dot->A = e;
dot->B_tok = expect(P, TOK_NAME); 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->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)) { } else if(maybe(P, TOK_FOR)) {
if(peek(P, 0).type == TOK_NAME && peek(P, 1).type == TOK_EQUAL) { if(peek(P, 0).type == TOK_NAME && peek(P, 1).type == TOK_EQUAL) {
// Range loop // 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->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); expect(P, TOK_END);
@ -1049,5 +1073,12 @@ LUnit *lparse(size_t sz, Token *tokens, LTable *environment) {
unit->func_count = 1; unit->func_count = 1;
unit->funcs = P.unit_functions.data; 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; return unit;
} }

32
table.h
View File

@ -59,7 +59,7 @@ static inline bool ltablebuckets_set(LTableBuckets *self, LValue key, LValue val
LValue prevKey = {.u = LTAG_NIL}; LValue prevKey = {.u = LTAG_NIL};
atomic_compare_exchange_strong(&current[idx].key.u, &prevKey.u, key.u); atomic_compare_exchange_strong(&current[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(&current[idx].val.u, val.u); atomic_store(&current[idx].val.u, val.u);
break; 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) { 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)) { if(!ltablebuckets_set(self->buckets, key, val)) {
assert(0 && "No table resizing"); 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(&current[idx].key.u, &prevKey.u, key.u);
if(prevKey.u == LTAG_NIL) {
atomic_store(&current[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) { static inline LValue ltablebuckets_get(LTableBuckets *self, LValue key) {
size_t idx = lvalue_hash(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) { 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); return ltablebuckets_get(self->buckets, key);
} }

377
vm.c
View File

@ -5,13 +5,14 @@
#include"str.h" #include"str.h"
#include<math.h> #include<math.h>
#include<malloc.h>
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) { 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_GETGLOBAL] = &&do_getglobal,
[L_SETGLOBAL] = &&do_setglobal, [L_SETGLOBAL] = &&do_setglobal,
[L_SETINT16] = &&do_setint16, [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; LInst *inst = func->lua_instrs;
#define DISPATCH() goto *dispatch_table[(++inst)->opcode] #define DISPATCH() goto *dispatch_table[(++inst)->opcode]
LThreadPrivates privates = {.regset = regset, .heap = heap};
inst--; inst--;
DISPATCH(); DISPATCH();
@ -53,9 +56,15 @@ do_getglobal:;
size_t len = *(uint16_t*) area; size_t len = *(uint16_t*) area;
area += 2; area += 2;
LString *str = lstring_new(len, area); LString *str = realloc(NULL, sizeof(*str) + len);
regs[inst->a] = ltable_get(func->env, lvalue_from_string(str)); str->length = len;
lstring_free(str); 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(); DISPATCH();
@ -65,18 +74,24 @@ do_setglobal:;
size_t len = *(uint16_t*) area; size_t len = *(uint16_t*) area;
area += 2; area += 2;
LString *str = lstring_new(len, area); LString *str = realloc(NULL, sizeof(*str) + len);
ltable_set(func->env, lvalue_from_string(str), regs[inst->a]); str->length = len;
lvm_gc_add(L, lvalue_from_string(str)); 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(); DISPATCH();
do_setint16:; do_setint16:;
regs[inst->a] = lvalue_from_int32((int16_t) inst->bc); regset->regs[inst->a] = lvalue_from_int32((int16_t) inst->bc);
DISPATCH(); DISPATCH();
do_setint32:; 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(); DISPATCH();
do_setfloat:; do_setfloat:;
@ -88,87 +103,94 @@ do_setstr:;
size_t len = *(uint16_t*) area; size_t len = *(uint16_t*) area;
area += 2; area += 2;
regs[inst->a] = lvalue_raw(LTAG_STRING, (uintptr_t) lstring_new(len, area)); LString *str = realloc(NULL, sizeof(*str) + len);
lvm_gc_add(L, regs[inst->a]); 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(); DISPATCH();
do_settable:; do_settable:;
{ {
LTable *tbl = ltable_new(inst->bc); LTable *tbl = ltable_new(inst->bc);
lvm_gc_add(L, lvalue_from_table(tbl)); regset->regs[inst->a] = lvalue_from_table(tbl);
regs[inst->a] = lvalue_from_table(tbl); set_LValueU_insert(heap, lvalue_from_table(tbl).u);
} }
DISPATCH(); DISPATCH();
do_setbool:; do_setbool:;
regs[inst->a] = lvalue_from_bool(inst->b); regset->regs[inst->a] = lvalue_from_bool(inst->b);
DISPATCH(); DISPATCH();
do_setnil:; do_setnil:;
regs[inst->a] = lvalue_from_nil(); regset->regs[inst->a] = lvalue_from_nil();
DISPATCH(); DISPATCH();
do_setfunc:; 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(); DISPATCH();
do_add:; do_add:;
{ {
LValue x = regs[inst->b]; LValue x = regset->regs[inst->b];
LValue y = regs[inst->c]; LValue y = regset->regs[inst->c];
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { 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) { } 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) { } 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; } else goto err;
} }
DISPATCH(); DISPATCH();
do_sub:; do_sub:;
{ {
LValue x = regs[inst->b]; LValue x = regset->regs[inst->b];
LValue y = regs[inst->c]; LValue y = regset->regs[inst->c];
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { 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) { } 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) { } 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; } else goto err;
} }
DISPATCH(); DISPATCH();
do_mul:; do_mul:;
{ {
LValue x = regs[inst->b]; LValue x = regset->regs[inst->b];
LValue y = regs[inst->c]; LValue y = regset->regs[inst->c];
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { 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) { } 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) { } 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; } else goto err;
} }
DISPATCH(); DISPATCH();
do_div:; do_div:;
{ {
LValue x = regs[inst->b]; LValue x = regset->regs[inst->b];
LValue y = regs[inst->c]; LValue y = regset->regs[inst->c];
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { 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) { } 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) { } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
int32_t yv = lvalue_to_int32(y); int32_t yv = lvalue_to_int32(y);
if(yv == 0) { if(yv == 0) {
regs[inst->a] = lvalue_from_nil(); regset->regs[inst->a] = lvalue_from_nil();
} else { } 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; } else goto err;
} }
@ -176,19 +198,19 @@ do_div:;
do_mod:; do_mod:;
{ {
LValue x = regs[inst->b]; LValue x = regset->regs[inst->b];
LValue y = regs[inst->c]; LValue y = regset->regs[inst->c];
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) { 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) { } else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
int32_t yv = lvalue_to_int32(y); 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) { } else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
int32_t yv = lvalue_to_int32(y); int32_t yv = lvalue_to_int32(y);
if(yv == 0) { if(yv == 0) {
goto err; goto err;
} else { } 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; } else goto err;
} }
@ -196,11 +218,13 @@ do_mod:;
do_jump:; do_jump:;
inst += (int16_t) inst->bc; inst += (int16_t) inst->bc;
L->safepoint_func(L, heap, regset);
DISPATCH(); DISPATCH();
do_jnotcond:; do_jnotcond:;
{ {
LValue v = regs[inst->a]; LValue v = regset->regs[inst->a];
if(v.u == LTAG_NIL || v.u == LTAG_FALSE) { if(v.u == LTAG_NIL || v.u == LTAG_FALSE) {
inst += (int16_t) inst->bc; inst += (int16_t) inst->bc;
} }
@ -209,7 +233,7 @@ do_jnotcond:;
do_call:; do_call:;
{ {
if(lvalue_tag(regs[inst->a]) != LTAG_FUNCTION) { if(lvalue_tag(regset->regs[inst->a]) != LTAG_FUNCTION) {
goto err; goto err;
} }
@ -218,30 +242,30 @@ do_call:;
uint8_t ret_vreg = abyss_data[0]; uint8_t ret_vreg = abyss_data[0];
uint8_t arg_count = abyss_data[1]; uint8_t arg_count = abyss_data[1];
uint8_t *args = &abyss_data[2]; uint8_t *args = &abyss_data[2];
LValue regs2[256]; LRegSet regset2 = {.parent = regset};
lvm_reset_regs(regs2); lvm_reset_regs(&regset2);
for(int i = 0; i < arg_count; i++) { 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, &regset2);
if(returned_count) { if(returned_count) {
// TODO: more than 1 return // TODO: more than 1 return
regs[ret_vreg] = regs2[0]; regset->regs[ret_vreg] = regset2.regs[0];
} }
} }
DISPATCH(); DISPATCH();
do_move:; do_move:;
regs[inst->a] = regs[inst->b]; regset->regs[inst->a] = regset->regs[inst->b];
DISPATCH(); DISPATCH();
do_advancetest:; do_advancetest:;
{ {
int64_t a = lvalue_to_int32(regs[inst->a]); int64_t a = lvalue_to_int32(regset->regs[inst->a]);
int64_t b = lvalue_to_int32(regs[inst->b]); int64_t b = lvalue_to_int32(regset->regs[inst->b]);
int64_t c = lvalue_to_int32(regs[inst->c]); int64_t c = lvalue_to_int32(regset->regs[inst->c]);
if(!((c >= 0 && a > b) || (c < 0 && a < b))) { if(!((c >= 0 && a > b) || (c < 0 && a < b))) {
inst++; inst++;
} }
@ -249,49 +273,65 @@ do_advancetest:;
DISPATCH(); DISPATCH();
do_cond_eq:; 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(); DISPATCH();
do_cond_neq:; 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(); DISPATCH();
do_setfield:; do_setfield:;
{ {
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) { if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) {
goto err; goto err;
} }
if(lvalue_tag(regs[inst->b]) == LTAG_NIL) { if(lvalue_tag(regset->regs[inst->b]) == LTAG_NIL) {
goto err; 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(); DISPATCH();
do_getfield:; do_getfield:;
{ {
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) { if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) {
goto err; 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(); DISPATCH();
err:; err:;
puts("Error");
do_ret:; do_ret:;
return 0; return 0;
} }
void lvm_gc_add(LVM *L, LValue lvalue) { size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LRegSet *regset) {
set_LValueU_insert(&L->gc_objects, lvalue.u); 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) { LFunc *lvm_func_from_native(LFuncCallback cb, void *ud) {
@ -324,3 +364,206 @@ bool lvalue_eq(LValue a, LValue b) {
return false; 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);
}

41
vm.h
View File

@ -7,6 +7,7 @@
#include<stdlib.h> #include<stdlib.h>
#include<stdio.h> #include<stdio.h>
#include<string.h> #include<string.h>
#include<threads.h>
#include"table.h" #include"table.h"
@ -68,7 +69,12 @@ typedef union __attribute__((packed)) {
struct LUnit; struct LUnit;
struct LVM; 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 { typedef struct LFunc {
struct LUnit *unit; struct LUnit *unit;
bool is_native; bool is_native;
@ -98,20 +104,43 @@ typedef struct LUnit {
#include"stc/hashset.h" #include"stc/hashset.h"
#undef i_header #undef i_header
typedef struct LThreadPrivates {
set_LValueU *heap;
LRegSet *regset;
} LThreadPrivates;
#define L_THREADS_MAX 32
typedef struct LVM { typedef struct LVM {
size_t unit_count; size_t unit_count;
LUnit *units; 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; } LVM;
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs); size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LRegSet *regset);
void lvm_gc_add(LVM *L, LValue lvalue);
LFunc *lvm_func_from_native(LFuncCallback, void *ud); 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++) { for(int i = 0; i < 256; i++) {
regs[i] = lvalue_from_nil(); regset->regs[i] = lvalue_from_nil();
} }
} }