diff --git a/ir.c b/ir.c index c7a8b17..64596de 100644 --- a/ir.c +++ b/ir.c @@ -4,23 +4,23 @@ #include const char *irinstr_str[IRInstrEnumSize] = { - [IRSet] = "set", - [IRNeg] = "neg", - [IRAdd] = "add", - [IRSub] = "sub", - [IRMul] = "mul", - [IRDiv] = "div", - [IREq] = "eq", - [IRNeq] = "neq", - [IRLt] = "lt", - [IRLe] = "le", - [IRNot] = "not", - [IRAnd] = "and", - [IROr] = "or", - [IRJmp] = "jmp", - [IRJnz] = "jnz", + [IRSet] = "set", + [IRNeg] = "neg", + [IRAdd] = "add", + [IRSub] = "sub", + [IRMul] = "mul", + [IRDiv] = "div", + [IREq] = "eq", + [IRNeq] = "neq", + [IRLt] = "lt", + [IRLe] = "le", + [IRNot] = "not", + [IRAnd] = "and", + [IROr] = "or", + [IRJmp] = "jmp", + [IRJnz] = "jnz", [IRCallInternal] = "calli", - [IRAddrOf] = "addrof", + [IRAddrOf] = "addrof", }; #define IRLIST_INIT_CAP_LONG 4096 @@ -51,10 +51,51 @@ void irlist_init_short(IRList *v) { irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT); } +static void free_irparam(IRParam *v, bool purge); + +/* if purge is set, even statically allocated literals are freed */ +static void free_irparam(IRParam *v, bool purge) { + if (v->kind == IRParamLiteral) + free_value(&v->Literal, purge); +} + void irlist_term(IRList *v) { for (IRItem *i = v->begin; i; i = i->next) { - if (i->tok.instr == IRCallInternal && i->tok.CallI.args) - free(i->tok.CallI.args); + switch (i->tok.instr) { + case IRSet: + case IRNeg: + case IRNot: + case IRAddrOf: + free_irparam(&i->tok.Unary.val, true); + break; + case IRAdd: + case IRSub: + case IRDiv: + case IRMul: + case IREq: + case IRNeq: + case IRLt: + case IRLe: + case IRAnd: + case IROr: + free_irparam(&i->tok.Binary.lhs, true); + free_irparam(&i->tok.Binary.rhs, true); + break; + case IRJmp: + break; + case IRJnz: + free_irparam(&i->tok.CJmp.condition, true); + break; + case IRCallInternal: { + size_t n_args = i->tok.CallI.n_args; + for (size_t j = 0; j < n_args; j++) + free_irparam(&i->tok.CallI.args[j], true); + free(i->tok.CallI.args); + break; + } + default: + ASSERT_UNREACHED(); + } } pool_term(v->p); } @@ -94,7 +135,7 @@ void irlist_eat_irlist(IRList *v, IRList *other) { void irlist_update_index(IRList *v) { if (v->index) return; - v->index = pool_alloc(v->p, v->len); + v->index = pool_alloc(v->p, sizeof(size_t) * v->len); size_t num_idx = 0; for (IRItem *i = v->begin; i; i = i->next, num_idx++) v->index[num_idx] = i; diff --git a/lex.c b/lex.c index df1f524..89e6f82 100644 --- a/lex.c +++ b/lex.c @@ -58,7 +58,7 @@ static char get_esc_char(char c) { } } -TokList lex(const char *s, Pool *static_vars) { +TokList lex(const char *s) { TokList toks; toklist_init(&toks); Pos pos = { .ln = 1, .col = 1 }; @@ -329,7 +329,7 @@ TokList lex(const char *s, Pool *static_vars) { /* go through the actual string */ s = start; pos = start_pos; - char *str = pool_alloc(static_vars, type_size[TypeChar] * size); + char *str = xmalloc(size); for (size_t i = 0; i < size; i++) { char c = s[0]; if (c == '\\') { @@ -347,6 +347,7 @@ TokList lex(const char *s, Pool *static_vars) { .type = TypeArr, .Arr = { .is_string = true, + .dynamically_allocated = false, .type = TypeChar, .vals = str, .len = size, diff --git a/lex.h b/lex.h index bc60e54..b57ac35 100644 --- a/lex.h +++ b/lex.h @@ -3,6 +3,6 @@ #include "tok.h" -TokList lex(const char *s, Pool *static_vars); +TokList lex(const char *s); #endif /* LEX_H */ diff --git a/main.c b/main.c index f55da11..a8f1687 100644 --- a/main.c +++ b/main.c @@ -166,6 +166,7 @@ static Value fn_getln(Value *args) { .type = TypeArr, .Arr = { .is_string = true, + .dynamically_allocated = true, .type = TypeChar, .vals = line, .len = len, @@ -219,11 +220,9 @@ int main(int argc, const char **argv) { } fclose(fp); /* lex source file */ - Pool *static_vars = pool_new(4096); - TokList tokens = lex(file, static_vars); + TokList tokens = lex(file); if (err) { toklist_term(&tokens); - pool_term(static_vars); free(file); fprintf(stderr, C_IRED "Lexer error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); return 1; @@ -248,7 +247,6 @@ int main(int argc, const char **argv) { if (err) { irlist_term(&ir); toklist_term(&tokens); - pool_term(static_vars); fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); return 1; } @@ -261,11 +259,9 @@ int main(int argc, const char **argv) { run(&ir, funcs); if (err) { irlist_term(&ir); - pool_term(static_vars); fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); return 1; } } irlist_term(&ir); - pool_term(static_vars); } diff --git a/parse.c b/parse.c index 39331a3..ea1b918 100644 --- a/parse.c +++ b/parse.c @@ -5,8 +5,6 @@ #include "map.h" #include "runtime.h" -static BuiltinFunc *bf; - typedef struct Scope { struct Scope *parent; size_t mem_addr; @@ -750,8 +748,6 @@ static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt } IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { - bf = builtin_funcs; - Map funcs; map_init(&funcs, sizeof(BuiltinFunc)); for (size_t i = 0; i < n_builtin_funcs; i++) { diff --git a/tok.c b/tok.c index e62be06..f20d9be 100644 --- a/tok.c +++ b/tok.c @@ -25,6 +25,22 @@ const char *type_str[TypeEnumSize] = { [TypeArr] = "arr", }; +/* if purge is set, even statically allocated literals will be freed */ +void free_value(Value *v, bool purge) { + switch (v->type) { + case TypeArr: + if (v->Arr.vals && (purge || v->Arr.dynamically_allocated)) { + free(v->Arr.vals); + v->Arr.vals = NULL; + v->Arr.len = 0; + v->Arr.cap = 0; + } + break; + default: + break; + } +} + void print_value(const Value *v, bool raw) { switch (v->type) { case TypeVoid: diff --git a/tok.h b/tok.h index b782d29..e1b6a72 100644 --- a/tok.h +++ b/tok.h @@ -36,12 +36,14 @@ typedef struct Value { struct { Type type; bool is_string : 1; + bool dynamically_allocated : 1; void *vals; size_t len, cap; } Arr; }; } Value; +void free_value(Value *v, bool purge); void print_value(const Value *v, bool raw); enum Operator { diff --git a/vm.c b/vm.c index a9383a3..ac9ea68 100644 --- a/vm.c +++ b/vm.c @@ -3,33 +3,50 @@ #include "runtime.h" #include "util.h" -#define INIT_STACK_CAP 256 +#define INIT_STACK_CAP 128 typedef struct Stack { Value *mem; + bool *holds_value; size_t len, cap; } Stack; static Stack stack_make(void); static void stack_term(Stack *s); static void stack_fit(Stack *s, size_t idx); +static void stack_assign(Stack *s, size_t idx, const Value *v); static Stack stack_make(void) { Stack s; s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP); + s.holds_value = xmalloc(sizeof(bool) * INIT_STACK_CAP); s.cap = INIT_STACK_CAP; s.len = 0; + for (size_t i = 0; i < s.cap; i++) + s.holds_value[i] = false; return s; } static void stack_term(Stack *s) { + /* free any dynamically allocated objects still alive */ + for (size_t i = 0; i < s->cap; i++) { + if (s->holds_value[i]) + free_value(&s->mem[i], false); + } + /* free the stack memory itself */ free(s->mem); + free(s->holds_value); } static void stack_fit(Stack *s, size_t idx) { size_t size = idx+1; if (size > s->cap) { - s->mem = xrealloc(s->mem, sizeof(Value) * (size + (s->cap *= 2))); + size_t new_cap = size + s->cap * 2; + s->mem = xrealloc(s->mem, sizeof(Value) * new_cap); + s->holds_value = xrealloc(s->holds_value, sizeof(bool) * new_cap); + for (size_t i = s->cap; i < new_cap; i++) + s->holds_value[i] = false; + s->cap = new_cap; } } @@ -42,6 +59,14 @@ static Value *irparam_to_val(Stack *s, IRParam *v) { ASSERT_UNREACHED(); } +static void stack_assign(Stack *s, size_t idx, const Value *v) { + stack_fit(s, idx); + if (s->holds_value[idx]) + free_value(&s->mem[idx], false); /* free any overwritten heap-allocated values */ + s->mem[idx] = *v; + s->holds_value[idx] = true; +} + void run(IRList *ir, const BuiltinFunc *builtin_funcs) { /* so we don't have to call malloc on every function call */ size_t fn_args_cap = 16; @@ -58,25 +83,29 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) { switch (instr->instr) { case IRSet: case IRNeg: - case IRNot: - stack_fit(&s, instr->Unary.addr); - TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), + case IRNot: { + Value res; + TRY_ELSE(res = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), {free(fn_args); stack_term(&s);}); + stack_assign(&s, instr->Unary.addr, &res); break; - case IRAddrOf: + } + case IRAddrOf: { if (instr->Unary.val.kind != IRParamAddr) { set_err("Unable to take the address of a literal"); return; } Value *v = &s.mem[instr->Unary.val.Addr]; - s.mem[instr->Unary.addr] = (Value){ + Value res = { .type = TypePtr, .Ptr = { .type = v->type, .val = &v->Void, }, }; + stack_assign(&s, instr->Unary.addr, &res); break; + } case IRAdd: case IRSub: case IRDiv: @@ -86,19 +115,27 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) { case IRLt: case IRLe: case IRAnd: - case IROr: - stack_fit(&s, instr->Binary.addr); - TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr, + case IROr: { + Value res; + TRY_ELSE(res = eval_binary(instr->instr, irparam_to_val(&s, &instr->Binary.lhs), irparam_to_val(&s, &instr->Binary.rhs)), {free(fn_args); stack_term(&s);}); + stack_assign(&s, instr->Binary.addr, &res); break; + } case IRJmp: - i = ir->index[instr->Jmp.iaddr]; + if (instr->Jmp.iaddr < ir->len) + i = ir->index[instr->Jmp.iaddr]; + else + i = NULL; continue; case IRJnz: if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) { - i = ir->index[instr->Jmp.iaddr]; + if (instr->Jmp.iaddr < ir->len) + i = ir->index[instr->CJmp.iaddr]; + else + i = NULL; continue; } break; @@ -113,16 +150,17 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) { fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]); if (f->returns) { - stack_fit(&s, instr->CallI.ret_addr); + Value res; if (f->kind == FuncVarArgs) { size_t min_args = f->VarArgs.min_args; - TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->VarArgs.WithRet.func(n_args - min_args, fn_args), + TRY_ELSE(res = f->VarArgs.WithRet.func(n_args - min_args, fn_args), {free(fn_args); stack_term(&s);}); } else if (f->kind == FuncFixedArgs) { - TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->FixedArgs.WithRet.func(fn_args), + TRY_ELSE(res = f->FixedArgs.WithRet.func(fn_args), {free(fn_args); stack_term(&s);}); } else ASSERT_UNREACHED(); + stack_assign(&s, instr->CallI.ret_addr, &res); } else { if (f->kind == FuncVarArgs) { size_t min_args = f->VarArgs.min_args;