From 88b8cddabdaaa1bfd6d6f566bcebc38f516227df Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sat, 11 Jul 2020 16:05:49 -0400 Subject: improved the way value memory management works --- abbrevs.txt | 3 +- allocator.c | 2 +- eval.c | 206 +++++++++++++++++++++++++++++++++--------------------------- main.c | 2 +- runv | 2 +- test.toc | 27 ++------ types.h | 2 + 7 files changed, 124 insertions(+), 120 deletions(-) diff --git a/abbrevs.txt b/abbrevs.txt index d486c78..eefa7f5 100644 --- a/abbrevs.txt +++ b/abbrevs.txt @@ -1,6 +1,4 @@ Abbreviations used in this code -Some of them are very common -Others aren't allocr - allocator arg - argument @@ -29,6 +27,7 @@ op - operator param - parameter ptr - pointer ret - return +ret_decl - return declaration (named return values) stmt - statement str - string tokr - tokenizer diff --git a/allocator.c b/allocator.c index 98005ef..a731928 100644 --- a/allocator.c +++ b/allocator.c @@ -25,7 +25,7 @@ static void *err_malloc(size_t bytes); static void *err_calloc(size_t n, size_t sz); static void *err_realloc(void *prev, size_t new_size); #ifdef TOC_DEBUG -#define NO_ALLOCATOR 1 /* useful for debugging; valgrind checks writing past the end of a malloc, but that won't work with an allocator */ +//#define NO_ALLOCATOR 1 /* useful for debugging; valgrind checks writing past the end of a malloc, but that won't work with an allocator */ #endif /* number of bytes a page hold, not including the header */ #define PAGE_BYTES (16384 - sizeof(Page)) diff --git a/eval.c b/eval.c index a948da2..5ba5cdd 100644 --- a/eval.c +++ b/eval.c @@ -12,6 +12,8 @@ static void evalr_create(Evaluator *ev, Typer *tr, Allocator *allocr) { ev->returning = NULL; ev->typer = tr; ev->allocr = allocr; + ev->to_free = NULL; + ev->decls_given_values = NULL; ffmgr_create(&ev->ffmgr, ev->allocr); } @@ -265,12 +267,12 @@ static void print_val(Value v, Type *t) { printf("\n"); } -static void *val_ptr_to_free(Value *v, Type *t) { +static void *val_ptr_to_free(Value v, Type *t) { assert(t->flags & TYPE_IS_RESOLVED); switch (t->kind) { case TYPE_BUILTIN: if (t->builtin == BUILTIN_VARARGS) - return v->varargs ? arr_hdr(v->varargs) : NULL; + return v.varargs ? arr_hdr(v.varargs) : NULL; return NULL; case TYPE_FN: case TYPE_PTR: @@ -278,23 +280,23 @@ static void *val_ptr_to_free(Value *v, Type *t) { case TYPE_UNKNOWN: return NULL; case TYPE_ARR: - return v->arr; + return v.arr; case TYPE_TUPLE: - return v->tuple; + return v.tuple; case TYPE_STRUCT: - return v->struc; + return v.struc; case TYPE_EXPR: break; } assert(0); return NULL; } -static inline void val_free(Value *v, Type *t) { +static inline void val_free(Value v, Type *t) { free(val_ptr_to_free(v, t)); } static inline void val_free_ptr(Value *v, Type *t) { - val_free(v, t); + val_free(*v, t); free(v); } @@ -725,18 +727,19 @@ static Status eval_ptr_to_struct_field(Evaluator *ev, Expression *dot_expr, void struct_type = struct_type->ptr; } if (struct_type->kind == TYPE_STRUCT) { - Value struc; - if (!eval_expr(ev, lhs, &struc)) - return false; void *struc_data; if (is_ptr) { - struc_data = struc.ptr; - if (struc_data == NULL) { - err_print(dot_expr->where, "Attempt to dereference null pointer."); + Value struc_ptr; + if (!eval_expr(ev, lhs, &struc_ptr)) return false; - } + struc_data = struc_ptr.ptr; } else { - struc_data = struc.struc; + if (!eval_address_of(ev, lhs, &struc_data)) + return false; + } + if (struc_data == NULL) { + err_print(dot_expr->where, "Attempt to dereference null pointer."); + return false; } *p = (char *)struc_data + dot_expr->binary.field->offset; } else if (struct_type->kind == TYPE_SLICE) { @@ -1045,31 +1048,31 @@ static Status eval_ident(Evaluator *ev, Identifier ident, Value *v, Location whe } Value *ival = ident_val(ev, ident, where); if (!ival) return false; - *v = *ival; + copy_val(NULL, v, *ival, decl_type_at_index(d, decl_ident_index(d, ident))); return true; } -static Value *decl_add_val(Declaration *d) { - Value *valp = err_malloc(sizeof *valp); - arr_add(d->val_stack, valp); - if (arr_len(d->idents) > 1) { - valp->tuple = err_malloc(arr_len(d->idents) * sizeof *valp->tuple); - } - return valp; +static void evalr_add_val_on_stack(Evaluator *ev, Value v, Type *t) { + void *ptr = val_ptr_to_free(v, t); + arr_add(ev->to_free, ptr); } - + +/* remove and free the last value in d->val_stack */ static void decl_remove_val(Declaration *d) { - assert(arr_len(d->val_stack)); - Value *valp = arr_last(d->val_stack); - if (arr_len(d->idents) == 1 || d->type.kind == TYPE_TUPLE) { - val_free_ptr(valp, &d->type); + Type *t = &d->type; + Value *dval = arr_last(d->val_stack); + if (arr_len(d->idents) > 1) { + if (t->kind == TYPE_TUPLE) { + val_free(*dval, t); + } else { + arr_foreach(dval->tuple, Value, sub) + val_free(*sub, t); + free(dval->tuple); + } } else { - long idx = 0; - arr_foreach(d->idents, Identifier, i) - val_free(&valp->tuple[idx++], &d->type); - free(valp->tuple); - free(valp); + val_free(*dval, t); } + free(dval); arr_remove_last(d->val_stack); } @@ -1179,7 +1182,10 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { eval_numerical_bin_op(lhs, &e->binary.lhs->type, e->binary.op, rhs, &e->binary.rhs->type, v, &e->type); break; case BINARY_SET: - if (!eval_set(ev, e->binary.lhs, &rhs)) return false; + if (!eval_set(ev, lhs_expr, &rhs)) return false; + if (lhs_expr->kind == EXPR_TUPLE) { + free(rhs.tuple); + } break; case BINARY_SET_ADD: case BINARY_SET_SUB: @@ -1271,6 +1277,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { } size_t nargs = arr_len(e->call.arg_exprs); if (fn->flags & FN_EXPR_FOREIGN) { + /* evaluate foreign function */ Value *args = err_malloc(nargs * sizeof *args); for (size_t i = 0; i < nargs; ++i) { if (!eval_expr(ev, &e->call.arg_exprs[i], &args[i])) @@ -1284,14 +1291,15 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { break; } + Type *ret_type = &fn->ret_type; /* set parameter values */ - Declaration *params = fn->params; + Declaration *params = fn->params, *ret_decls = fn->ret_decls; Expression *arg = e->call.arg_exprs; /* @OPTIM: figure out how much memory parameters use, then allocate that much space (possibly with alloca)? */ arr_foreach(params, Declaration, p) { + /* give each parameter its value */ int idx = 0; bool multiple_idents = arr_len(p->idents) > 1; - bool is_tuple = p->type.kind == TYPE_TUPLE; Value *pval = err_malloc(sizeof *pval); if (type_is_builtin(&p->type, BUILTIN_VARARGS)) { Expression *args_end = e->call.arg_exprs + nargs; @@ -1308,9 +1316,8 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { Value arg_val; if (!eval_expr(ev, arg, &arg_val)) return false; - Type *type = is_tuple ? &p->type.tuple[idx] : &p->type; Value *ival = multiple_idents ? &pval->tuple[idx] : pval; - copy_val(NULL, ival, arg_val, type); + *ival = arg_val; ++arg; ++idx; } @@ -1318,33 +1325,41 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { arr_add(p->val_stack, pval); } - arr_foreach(fn->ret_decls, Declaration, d) { + arr_foreach(ret_decls, Declaration, d) { + /* give each return declaration its value */ int idx = 0; Value ret_decl_val; - if (d->flags & DECL_HAS_EXPR) + DeclFlags has_expr = d->flags & DECL_HAS_EXPR; + if (has_expr) { if (!eval_expr(ev, &d->expr, &ret_decl_val)) return false; - Value *dval = decl_add_val(d); + } + Value *dval = err_malloc(sizeof *dval); bool multiple_idents = arr_len(d->idents) > 1; bool is_tuple = d->type.kind == TYPE_TUPLE; arr_foreach(d->idents, Identifier, i) { Value *ival = multiple_idents ? &dval->tuple[idx] : dval; Type *type = is_tuple ? &d->type.tuple[idx] : &d->type; - if (d->flags & DECL_HAS_EXPR) { - *ival = d->type.kind == TYPE_TUPLE ? ret_decl_val.tuple[idx] : ret_decl_val; + if (has_expr) { + *ival = is_tuple ? ret_decl_val.tuple[idx] : ret_decl_val; } else { *ival = val_zero(NULL, type); + evalr_add_val_on_stack(ev, *ival, type); } ++idx; } + if (is_tuple && has_expr) + free(ret_decl_val.tuple); /* we extracted the individual elements of this */ + arr_add(d->val_stack, dval); } if (!eval_block(ev, &fn->body)) { return false; } - if (fn->ret_decls) { + if (ret_decls) { + /* extract return value from return declarations */ size_t nret_decls = 0; - arr_foreach(fn->ret_decls, Declaration, d) { + arr_foreach(ret_decls, Declaration, d) { nret_decls += arr_len(d->idents); } @@ -1353,20 +1368,18 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { tuple = err_malloc(nret_decls * sizeof *tuple); size_t tuple_idx = 0; - arr_foreach(fn->ret_decls, Declaration, d) { - int i = 0; - arr_foreach(d->idents, Identifier, ident) { - Value this_one; - Expression expr; - expr.flags = EXPR_FOUND_TYPE; - expr.kind = EXPR_IDENT; - expr.ident = *ident; - if (!eval_expr(ev, &expr, &this_one)) - return false; - Value *element = tuple ? &tuple[tuple_idx++] : v; - Type *type = decl_type_at_index(d, i); - copy_val(NULL, element, this_one, type); - ++i; + arr_foreach(ret_decls, Declaration, d) { + Type *t = &d->type; + Value dval = *arr_last(d->val_stack); + size_t nidents = arr_len(d->idents); + if (nidents > 1) { + bool is_tuple = d->type.kind == TYPE_TUPLE; + for (size_t i = 0; i < nidents; ++i, ++tuple_idx) + copy_val(NULL, &tuple[tuple_idx], dval.tuple[i], is_tuple ? &t->tuple[tuple_idx] : t); + } else if (tuple) { + copy_val(NULL, &tuple[tuple_idx++], dval, t); + } else { + copy_val(NULL, v, dval, t); } } if (tuple) { @@ -1374,13 +1387,16 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { } } if (ev->returning) { - if (!type_is_void(&fn->ret_type) && !fn->ret_decls) + if (!type_is_void(ret_type) && !ret_decls) *v = ev->ret_val; ev->returning = NULL; } - arr_foreach(fn->params, Declaration, p) + + /* remove parameter values */ + arr_foreach(params, Declaration, p) decl_remove_val(p); - arr_foreach(fn->ret_decls, Declaration, d) + /* remove ret decl values */ + arr_foreach(ret_decls, Declaration, d) decl_remove_val(d); } break; case EXPR_SLICE: { @@ -1439,8 +1455,8 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) { } static Status eval_decl(Evaluator *ev, Declaration *d) { - unsigned has_expr = d->flags & DECL_HAS_EXPR; - unsigned is_const = d->flags & DECL_IS_CONST; + DeclFlags has_expr = d->flags & DECL_HAS_EXPR; + DeclFlags is_const = d->flags & DECL_IS_CONST; Value val = {0}; if (has_expr) { @@ -1458,10 +1474,18 @@ static Status eval_decl(Evaluator *ev, Declaration *d) { if (!is_const) { int index = 0; - Value *dval = decl_add_val(d); + + Value *dval = err_malloc(sizeof *dval); + arr_add(ev->to_free, dval); + arr_add(ev->decls_given_values, d); + arr_add(d->val_stack, dval); + if (arr_len(d->idents) > 1) { + dval->tuple = err_malloc(arr_len(d->idents) * sizeof *dval->tuple); + arr_add(ev->to_free, dval->tuple); + } + bool is_tuple = d->type.kind == TYPE_TUPLE; bool multiple_idents = arr_len(d->idents) > 1; - bool need_to_copy = d->expr.kind != EXPR_CALL; /* don't copy if it's a return value */ arr_foreach(d->idents, Identifier, ip) { Value *ival = multiple_idents ? &dval->tuple[index] : dval; @@ -1469,14 +1493,11 @@ static Status eval_decl(Evaluator *ev, Declaration *d) { if (!is_const) { if (has_expr) { Value v = is_tuple ? val.tuple[index] : val; - if (need_to_copy) { - copy_val(NULL, ival, v, type); - } else { - *ival = v; - } + *ival = v; } else { *ival = val_zero(NULL, type); } + evalr_add_val_on_stack(ev, *ival, type); } ++index; } @@ -1487,18 +1508,6 @@ static Status eval_decl(Evaluator *ev, Declaration *d) { } -static void eval_exit_stmts(Statement *stmts, Statement *last_reached) { - if (stmts) { - for (Statement *s = stmts; s <= last_reached; ++s) { - if (s->kind == STMT_DECL && !(s->decl->flags & DECL_IS_CONST)) { - Declaration *d = s->decl; - decl_remove_val(d); - } - /* inline blocks are handled by eval_stmt; don't worry */ - } - } -} - static Status eval_stmt(Evaluator *ev, Statement *stmt) { switch (stmt->kind) { case STMT_DECL: @@ -1513,10 +1522,8 @@ static Status eval_stmt(Evaluator *ev, Statement *stmt) { Return *r = stmt->ret; if (r->flags & RET_HAS_EXPR) { - Value v; - if (!eval_expr(ev, &r->expr, &v)) + if (!eval_expr(ev, &r->expr, &ev->ret_val)) return false; - copy_val(NULL, &ev->ret_val, v, &r->expr.type); } ev->returning = r->referring_to; } break; @@ -1537,16 +1544,13 @@ static Status eval_stmt(Evaluator *ev, Statement *stmt) { break; case STMT_INLINE_BLOCK: { Statement *stmts = stmt->inline_block; - Statement *last_reached = arr_last_ptr(stmts); arr_foreach(stmts, Statement, s) { if (!eval_stmt(ev, s)) return false; if (ev->returning) { - last_reached = s; break; } } - eval_exit_stmts(stmts, last_reached); } break; case STMT_IF: { for (If *i = stmt->if_; i; i = i->next_elif) { @@ -1730,17 +1734,19 @@ static Status eval_stmt(Evaluator *ev, Statement *stmt) { static Status eval_block(Evaluator *ev, Block *b) { assert(b->flags & BLOCK_FOUND_TYPES); Block *prev = ev->typer->block; + void **prev_to_free = ev->to_free; + Declaration **prev_dgv = ev->decls_given_values; + ev->to_free = NULL; + ev->decls_given_values = NULL; ev->typer->block = b; b->deferred = NULL; bool success = true; - Statement *last_reached = arr_last_ptr(b->stmts); arr_foreach(b->stmts, Statement, stmt) { if (!eval_stmt(ev, stmt)) { success = false; goto ret; } if (ev->returning) { - last_reached = stmt; break; } } @@ -1752,15 +1758,27 @@ static Status eval_block(Evaluator *ev, Block *b) { ev->returning = NULL; /* if we didn't set this, the deferred stmts would immediately return */ arr_foreach(b->deferred, StatementPtr, stmtp) { Statement *stmt = *stmtp; - if (!eval_stmt(ev, stmt)) - return false; + if (!eval_stmt(ev, stmt)) { + success = false; + goto ret; + } } arr_clear(b->deferred); ev->returning = return_block; ev->ret_val = return_val; } - eval_exit_stmts(b->stmts, last_reached); + typedef void *voidptr; + arr_foreach(ev->to_free, voidptr, pp) { + free(*pp); + } + arr_clear(ev->to_free); + arr_foreach(ev->decls_given_values, DeclarationPtr, dp) { + arr_remove_last((*dp)->val_stack); + } + arr_clear(ev->decls_given_values); ret: + ev->to_free = prev_to_free; + ev->decls_given_values = prev_dgv; ev->typer->block = prev; return success; } diff --git a/main.c b/main.c index 4515bc9..a612db5 100644 --- a/main.c +++ b/main.c @@ -8,7 +8,7 @@ /* @TODO: -error when a template is used after it's defined +error when a template is used before it's defined if we do #include "foo.toc", bar; and foo.toc fails, bar should be declared as TYPE_UNKNOWN (right now it's undeclared) fix #foreign not at global scope - right now the cgen'd definition doesn't use the proper type figure out how printf is gonna work diff --git a/runv b/runv index 8cba96a..b40ca55 100755 --- a/runv +++ b/runv @@ -5,7 +5,7 @@ else tocf="$2" fi if [ "$1" = "noq" ]; then - FLAGS= + FLAGS="--leak-check=full" else FLAGS="-q" fi diff --git a/test.toc b/test.toc index 86c3855..52399cb 100644 --- a/test.toc +++ b/test.toc @@ -1,24 +1,9 @@ #include "std/io.toc", io; -Point ::= struct { - x, y: int; -} -main ::= fn(){ - a, b: Point; - b.x = 15; - b.y = 12; - a = b; - a = a; - c, d: [5]int; - c[0] = 39; - - d = c; - d = d; - c = d; - io.puti(a.x); - io.puti(a.y); - io.puti(b.x); - io.puti(b.y); - io.puti(c[0]); - io.puti(d[0]); +main ::= fn() { + file, err := io.fopen_write("test.txt"); + for i := 0.,1000000 { + io.fputs(file, "!"); + } + io.fclose(file); } main(); diff --git a/types.h b/types.h index 2f6946d..811f927 100644 --- a/types.h +++ b/types.h @@ -1037,6 +1037,8 @@ typedef struct Evaluator { struct Typer *typer; Block *returning; /* function body from which we are returning OR loop body in which we are continuing/breaking */ bool is_break; /* is returning because of a break, as opposed to a continue? */ + void **to_free; /* array of pointers to free once block is exited */ + Declaration **decls_given_values; /* array of declarations whose last value in their val stacks should be removed when the block is exited */ Value ret_val; ForeignFnManager ffmgr; } Evaluator; -- cgit v1.2.3