From e1f17535a043b55ce34de4d7583928d8c94bb71a Mon Sep 17 00:00:00 2001 From: Kimplul Date: Fri, 12 Apr 2024 02:19:23 +0300 Subject: start work on struct handling --- src/actualize.c | 268 ++++++++++++++++++++++++++++++---------- src/ast.c | 70 +++++++++++ src/lower.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++------- src/parser.y | 4 +- 4 files changed, 603 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/actualize.c b/src/actualize.c index 7f17fbc..71f54a4 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -119,6 +119,10 @@ static int is_lvalue(struct ast *n) if (n->k == AST_DEREF) return 1; + /* at least generally speaking */ + if (n->k == AST_DOT) + return 1; + return 0; } @@ -330,17 +334,21 @@ static int eval_const_if(struct scope *scope, struct ast *node) return -1; } +static int actualize_proc_sign(struct scope *scope, struct ast *proc); + static int analyze_visibility(struct scope *scope, struct ast *node) { if (!node) return 0; - node->scope = scope; - switch (node->k) { - case AST_PROC_DEF: return scope_add_proc(scope, node); - case AST_MACRO_DEF: return scope_add_macro(scope, node); case AST_VAR_DEF: return scope_add_var(scope, node); + case AST_PROC_DEF: { + node->scope = create_scope(); + scope_add_scope(scope, node->scope); + return scope_add_proc(scope, node); + } + case AST_MACRO_DEF: return scope_add_macro(scope, node); case AST_IMPORT: { const char *file = import_file(node); @@ -360,14 +368,15 @@ static int analyze_visibility(struct scope *scope, struct ast *node) } case AST_STRUCT_DEF: { - /* we shouldn't get any anonymous structs at this stage */ - char *id = struct_id(node); - return scope_add_type(scope, id, node); + node->scope = create_scope(); + scope_add_scope(scope, node->scope); + return scope_add_type(scope, struct_id(node), node); } case AST_ENUM_DEF: { - char *id = enum_id(node); - return scope_add_type(scope, id, node); + node->scope = create_scope(); + scope_add_scope(scope, node->scope); + return scope_add_type(scope, enum_id(node), node); } case AST_ALIAS_DEF: { @@ -376,8 +385,9 @@ static int analyze_visibility(struct scope *scope, struct ast *node) } case AST_TRAIT_DEF: { - char *id = trait_id(node); - return scope_add_type(scope, id, node); + node->scope = create_scope(); + scope_add_scope(scope, node->scope); + return scope_add_type(scope, trait_id(node), node); } case AST_EMPTY: { @@ -475,7 +485,8 @@ static struct ast *analyze_type_expand(struct scope *scope, struct ast *n) { assert(n->k == AST_TYPE_EXPAND); - struct ast *trait = file_scope_find_type(scope, type_expand_id(n)); + struct act_state state = {0}; + struct ast *trait = actualized_file_scope_find_type(&state, scope, type_expand_id(n)); if (!trait) { semantic_error(scope->fctx, n, "no such type"); return NULL; @@ -486,9 +497,7 @@ static struct ast *analyze_type_expand(struct scope *scope, return NULL; } - semantic_info(scope->fctx, n, - "FIXME: skipping type param check for now"); - struct ast *body = trait_raw_body(trait); + struct ast *body = trait_body(trait); body = clone_ast(body); struct type *pa = type_expand_args(n); @@ -617,6 +626,12 @@ static int analyze_trait(struct scope *scope, struct ast *node) static int analyze(struct scope *scope, struct ast *tree) { + foreach_node(node, tree) { + /* make all defs visible */ + if (analyze_visibility(scope, node)) + return -1; + } + foreach_node(node, tree){ struct act_state state = {0}; if (actualize(&state, scope, node)) @@ -803,14 +818,22 @@ static int maybe_ufcs(struct act_state *state, struct scope *scope, return 0; struct ast *expr = dot_expr(dot); - call_expr(call) = gen_fetch(dot_id(dot), clone_type(expr->t), dot->loc); + char *id = strdup(dot_id(dot)); + call_expr(call) = gen_fetch(id, clone_type(expr->t), dot->loc); (call_expr(call))->t = clone_type(dot->t); (call_expr(call))->scope = scope; - /* hard core type */ - struct ast *ref = gen_unop(AST_REF, expr, dot->loc); - ref->t = tgen_ptr(clone_type(expr->t), dot->loc); - ref->scope = scope; + struct ast *ref = NULL; + struct type *ptypes = callable_ptypes(dot->t); + if (ptypes->k == TYPE_PTR) { + /* is ufcs expects reference to member, try to take address */ + ref = gen_unop(AST_REF, expr, dot->loc); + ref->t = tgen_ptr(clone_type(expr->t), dot->loc); + ref->scope = scope; + } else { + /* otherwise, try to pass expr as is */ + ref = expr; + } if (simplify_refderef(state, scope, ref)) return -1; @@ -904,16 +927,14 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) return ret; } -static int actualize_proc(struct act_state *state, - struct scope *scope, struct ast *proc) +static int actualize_proc_sign(struct scope *scope, struct ast *proc) { - UNUSED(state); - /* actualize_proc is called on trait procs as well, but I believe - * that's fine? */ assert(proc && proc->k == AST_PROC_DEF); struct act_state new_state = {0}; - /* params should already have been actualized, should maybe check */ + proc->scope = create_scope(); + scope_add_scope(scope, proc->scope); + if (actualize_list(&new_state, proc->scope, proc_params(proc))) { destroy_act_state(&new_state); return -1; @@ -940,9 +961,22 @@ static int actualize_proc(struct act_state *state, } set_type(proc, callable); - scope_add_proc(scope, proc); + return 0; +} + +static int actualize_proc(struct act_state *state, + struct scope *scope, struct ast *proc) +{ + UNUSED(state); + if (actualize_proc_sign(scope, proc)) + return -1; + + /* external functions and so on */ + if (!proc_body(proc)) + return 0; /* actualize body */ + struct act_state new_state = {0}; new_state.cur_proc = proc; if (actualize(&new_state, proc->scope, proc_body(proc))) { destroy_act_state(&new_state); @@ -1149,33 +1183,18 @@ static int actualize_tid(struct act_state *state, struct scope *scope, return 0; } - struct ast *def = file_scope_find_type(scope, t->id); + struct ast *def = actualized_file_scope_find_type(state, scope, t->id); if (!def) { type_error(scope->fctx, t, "no such type"); return -1; } - if (!def->t && actualize(state, def->scope, def)) - return -1; - assert(t->n == NULL); - assert(def->k != AST_TRAIT_DEF); - - if (def->k == AST_ALIAS_DEF) { - replace_type(t, clone_type_list(def->t)); + replace_type(t, clone_type_list(def->t)); + if (def->k == AST_ALIAS_DEF) t->a = def; - return 0; - } - else if (def->k == AST_STRUCT_DEF) { - replace_type(t, clone_type_list(def->t)); - return 0; - } - else if (def->k == AST_ENUM_DEF) { - replace_type(t, clone_type_list(def->t)); - return 0; - } - return -1; + return 0; } static int actualize_ptr(struct act_state *state, struct scope *scope, @@ -1901,24 +1920,93 @@ static int replace_type_id(struct ast *nodes, char *id, static int actualize_trait(struct act_state *state, struct scope *scope, struct ast *node) { - UNUSED(scope); assert(node->k == AST_TRAIT_DEF); + struct ast *params = trait_params(node); + struct scope *trait_scope = create_scope(); + if (!trait_scope) + return -1; + + scope_add_scope(node->scope, trait_scope); + node->scope = trait_scope; + + /** @todo should probably add in aliases for the traits in scope? */ + if (params) + ast_set_flags(node, AST_FLAG_GENERIC); + + node->t = tgen_trait(trait_id(node), node, node->loc); + + /* copy body */ + node->a2 = clone_ast(trait_raw_body(node)); + + /* do type expansions */ foreach_node(n, trait_body(node)) { - /* there's really only prodcedure body actualization left I - * guess, as type stuff was taken care of in the analysis phase + if (n->k != AST_TYPE_EXPAND) + continue; + + /* don't re-expand already implemented traits */ + if (implements_trait(trait_body(node), type_expand_id(n))) { + /* not sure about this, but at least we don't have stray + * type expands everywhere */ + n->k = AST_EMPTY; + continue; + } + + if (same_id(trait_id(node), type_expand_id(n))) { + semantic_error(scope->fctx, n, + "recursive trait implementations not allowed"); + return -1; + } + + struct ast *body = analyze_type_expand(scope, n); + if (!body) { + n->k = AST_EMPTY; + continue; + } + + replace_type_id(body, type_expand_id(n), node->t); + ast_last(body)->n = n->n; + n->n = body; + } + + + /** @todo I should really check that there's just one prototype and one + * implementation of that prototype, not sure what the best approach + * would be. Add a prototypes -list to scopes? */ + + /* add all prototypes that don't have matching definition to scope */ + foreach_node(n, trait_body(node)) { + if (n->k == AST_TYPE_EXPAND) + continue; + + if (n->k == AST_PROC_DEF && !proc_body(n)) + continue; + + if (analyze_visibility(trait_scope, n)) + return -1; + + struct act_state state = {0}; + if (actualize(&state, trait_scope, n)) + return -1; + } + + foreach_node(n, trait_body(node)) { + /* 'actualize' prototypes to make them appear in scope searches * */ if (n->k != AST_PROC_DEF) continue; - /* don't actualize prototypes, duh */ - if (!proc_body(n)) + if (proc_body(n)) continue; - if (actualize(state, node->scope, n)) + struct ast *exists = scope_find_proc(trait_scope, proc_id(n)); + if (exists) + continue; + + if (actualize_proc_sign(trait_scope, n)) return -1; } - return 0; + return node->t == NULL; } static int actualize_struct(struct act_state *state, @@ -1930,9 +2018,7 @@ static int actualize_struct(struct act_state *state, if (!struct_scope) return -1; - scope_add_type(scope, struct_id(node), node); - - scope_add_scope(node->scope, struct_scope); + scope_add_scope(scope, struct_scope); node->scope = struct_scope; if (params) ast_set_flags(node, AST_FLAG_GENERIC); @@ -1990,6 +2076,29 @@ static int actualize_struct(struct act_state *state, */ foreach_node(n, struct_body(node)) { + /* don't actually actualize type expansion for now, it's just + * sticking around to make it easier to check if a type + * implements a trait */ + /* should maybe rename to trait_expand or something, huh */ + if (n->k == AST_TYPE_EXPAND) + continue; + + /* prototypes are handled separately */ + if (n->k == AST_PROC_DEF && !proc_body(n)) + continue; + + if (analyze_visibility(struct_scope, n)) + return -1; + } + + foreach_node(n, struct_body(node)) { + if (n->k == AST_TYPE_EXPAND) + continue; + + /* prototypes are (still) handled separately */ + if (n->k == AST_PROC_DEF && !proc_body(n)) + continue; + struct act_state state = {0}; if (actualize(&state, struct_scope, n)) return -1; @@ -2003,6 +2112,9 @@ static int actualize_struct(struct act_state *state, if (proc_body(n)) continue; + if (actualize_proc_sign(struct_scope, n)) + return -1; + struct ast *proc = scope_find_proc(struct_scope, proc_id(n)); if (!proc) { semantic_error(scope->fctx, n, @@ -2180,6 +2292,27 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return 0; } +static int actualize_enum_fetch(struct act_state *state, struct scope *scope, + struct ast *fetch) +{ + char *id = fetch_id(fetch); + struct type *type = fetch_type(fetch); + struct ast *def = type->d; + assert(def); + + struct ast *member = lookup_enum_member(def, id); + if (!member) { + char *estr = type_str(type); + semantic_error(scope->fctx, fetch, "no such member in enum %s"); + free(estr); + return -1; + } + + replace_ast(fetch, val_val(member)); + set_type(fetch, def->t); + return 0; +} + static int actualize_fetch(struct act_state *state, struct scope *scope, struct ast *fetch) { @@ -2188,24 +2321,28 @@ static int actualize_fetch(struct act_state *state, struct scope *scope, if (actualize_type(state, scope, type)) return -1; - if (type->k != TYPE_ENUM) { - type_error(scope->fctx, type, "type is not an enum"); + if (type->k == TYPE_ENUM) + return actualize_enum_fetch(state, scope, fetch); + + if (type->k != TYPE_STRUCT && type->k != TYPE_TRAIT && !is_primitive(type)) { + char *tstr = type_str(type); + semantic_error(scope->fctx, fetch, + "illegal fetch type %s", tstr); + free(tstr); return -1; } - char *id = fetch_id(fetch); struct ast *def = type->d; assert(def); - struct ast *member = lookup_enum_member(def, id); + struct ast *member = scope_find_proc(def->scope, fetch_id(fetch)); if (!member) { - char *estr = type_str(type); - semantic_error(scope->fctx, fetch, "no such member in enum %s"); - free(estr); + semantic_error(scope->fctx, fetch, + "no such proc"); return -1; } - set_type(fetch, def->t); + set_type(fetch, member->t); return 0; } @@ -2384,8 +2521,6 @@ static int actualize(struct act_state *state, struct scope *scope, case AST_TRAIT_DEF: ret = actualize_trait(state, scope, node); break; case AST_ALIAS_DEF: ret = actualize_alias(state, scope, node); break; case AST_ENUM_DEF: ret = actualize_enum(state, scope, node); break; - case AST_MACRO_DEF: ret = actualize_macro_def(state, scope, node); - break; case AST_STRUCT_DEF: ret = actualize_struct(state, scope, node); break; case AST_VAR_DEF: ret = actualize_var(state, scope, node); break; case AST_CALL: ret = actualize_call(state, scope, node); break; @@ -2406,6 +2541,7 @@ static int actualize(struct act_state *state, struct scope *scope, case AST_FOR: ret = actualize_for(state, scope, node); break; case AST_MACRO_EXPAND: ret = actualize_macro_expand(state, scope, node); break; + case AST_MACRO_DEF: ret = actualize_macro_def(state, scope, node); break; default: /* more like internal_error, maybe? */ semantic_error(scope->fctx, node, diff --git a/src/ast.c b/src/ast.c index b3cf408..df016a8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -91,6 +91,7 @@ static struct type *create_empty_type() struct type *n = calloc(1, sizeof(struct type)); /* just to be safe */ n->k = TYPE_VOID; + n->size = -1; vect_append(struct ast *, types, &n); return n; } @@ -379,6 +380,11 @@ struct ast *clone_ast(struct ast *n) new->v = n->v; new->f = n->f; + /* unsure if this should be a separate step maybe? Generally this is + * unwanted, but I might run into exceptions and then it's more + * difficult to rebuild the init/actual state... */ + ast_clear_flags(new, AST_FLAG_INIT | AST_FLAG_ACTUAL); + if (n->t) new->t = clone_type_list(n->t); @@ -658,3 +664,67 @@ int equiv_type_lists(struct type *t1, struct type *t2) return 1; } + +size_t align3k(size_t o) +{ + size_t rem = o % 3; + if (rem) + o += rem; + + return o; +} + +static size_t struct_size(struct type *t) +{ + if (t->size != -1) + return t->size; + + size_t size = 0; + foreach_node(n, struct_body(t->d)) { + if (n->k != AST_VAR_DEF) + continue; + + size_t sz = type_size(n->t); + if (sz > 2) + size = align3k(size); + + size += sz; + } + + t->size = size; + return size; +} + +size_t type_size(struct type *t) +{ + switch (t->k) { + case TYPE_I9: return 1; + case TYPE_I27: return 3; + case TYPE_PTR: return 3; + case TYPE_STRUCT: return struct_size(t); + default: + } + + assert(0 && "unhandled type to get size of"); + abort(); +} + +size_t type_offsetof(struct type *t, char *m) +{ + assert(t->k == TYPE_STRUCT); + + size_t offset = 0; + foreach_node(n, struct_body(t->d)) { + if (n->k != AST_VAR_DEF) + continue; + + if (same_id(var_id(n), m)) + break; + + size_t sz = type_size(n->t); + if (sz > 2) + offset = align3k(offset); + } + + return offset; +} diff --git a/src/lower.c b/src/lower.c index 50e7ef8..253293f 100644 --- a/src/lower.c +++ b/src/lower.c @@ -27,6 +27,9 @@ struct lower_state { struct vec bottom; struct vec out; int64_t uniq; + + struct vec dealloc; + size_t deallocs; }; static struct lower_state create_state() @@ -35,6 +38,8 @@ static struct lower_state create_state() state.top = vec_create(sizeof(char *)); state.bottom = vec_create(sizeof(char *)); state.out = vec_create(sizeof(char *)); + state.dealloc = vec_create(sizeof(char *)); + state.deallocs = 0; state.uniq = 0; return state; } @@ -44,10 +49,17 @@ static void destroy_state(struct lower_state *state) assert(vec_len(&state->top) == 0); assert(vec_len(&state->bottom) == 0); assert(vec_len(&state->out) == 0); + assert(vec_len(&state->dealloc) == 0); vec_destroy(&state->top); vec_destroy(&state->bottom); vec_destroy(&state->out); + vec_destroy(&state->dealloc); +} + +static void add_dealloc(struct lower_state *s, char *dealloc) +{ + vect_append(char *, s->dealloc, &dealloc); } static void push_loop(struct lower_state *s, char *top, char *bottom, char *out) @@ -254,12 +266,14 @@ static int lower_param(struct lower_state *s, struct ast *p) char *t = is_small_type(type) ? "i9" : "i27"; printf("%s ", t); output_ast_id(p); - printf(","); + printf(", "); return 0; } static int lower_params(struct lower_state *s, struct ast *params) { + /** @todo fix structs, struct arguments must be stored to some + * structures on the stack */ foreach_node(p, params) { if (lower_param(s, p)) return -1; @@ -278,17 +292,12 @@ static int lower_var(struct lower_state *s, struct ast *v, return -1; } - /* if we have a struct, we should add the member name to the base name - * */ - foreach_retval(ri, input) { - struct retval r = retval_at(input, ri); - char *name = mangle_idx(v, ri); - /* I assume we're always dealing with i27 for now */ - /** @todo qbt could maybe skip the type stuff except for casts */ - printf("i27 %s = %s;\n", name, r.s); - struct retval n = build_retval(REG_I27, name); - vec_append(retval, &n); - } + char *name = mangle(v); + /* if we're a struct, we can just reuse the newly built constant struct, + * yay, just rename it */ + printf("i27 %s = %s;\n", name, (retval_at(input, 0)).s); + struct retval n = build_retval(REG_I27, name); + vec_append(retval, &n); retval_destroy(&input); return 0; @@ -447,12 +456,6 @@ static int lower_id(struct lower_state *s, struct ast *id, char *m = mangle(id); struct type *type = id->t; - if (!is_primitive(type) && type->k != TYPE_CALLABLE) { - semantic_error(id->scope->fctx, id, - "only primitive ids currently implemented"); - return -1; - } - enum retval_kind kind = is_small_type(type) ? REG_I9 : REG_I27; /* this likely isn't enough and we need to add the & to most things we @@ -676,6 +679,122 @@ static int lower_comparison(struct lower_state *s, struct ast *i, return 0; } +static int collect_primitive_arg(struct lower_state *s, struct ast *c, struct vec *retval) +{ + struct vec arg = retval_create(); + if (lower_expr(s, c, &arg)) { + retval_destroy(&arg); + return -1; + } + + assert(vec_len(&arg) == 1); + struct retval r = retval_at(arg, 0); + /* very important! */ + r.s = strdup(r.s); + vec_append(retval, &r); + + retval_destroy(&arg); + return 0; +} + +static size_t collect_struct_tmps(struct lower_state *s, struct ast *def, char *name, size_t base, struct vec *retval) +{ + size_t offset = base; + foreach_node(n, struct_body(def)) { + if (n->k != AST_VAR_DEF) + continue; + + if (n->t->k == TYPE_STRUCT) { + offset += collect_struct_tmps(s, n->t->d, name, offset, retval); + continue; + } + + size_t sz = type_size(n->t); + assert(sz == 1 || sz == 3); + + char *type = sz == 1 ? "i9" : "i27"; + char *tmp = build_str("callstruct_%zd", s->uniq++); + printf("%s %s << %s %zd;\n", type, tmp, name, offset); + + struct retval r = build_retval(sz == 1 ? REG_I9 : REG_I27, tmp); + vec_append(retval, &r); + offset += sz; + } + + assert((offset - base) == type_size(def->t)); + return offset; +} + +static int collect_struct_arg(struct lower_state *s, struct ast *c, struct vec *retval) +{ + struct vec arg = retval_create(); + if (lower_expr(s, c, &arg)) { + retval_destroy(&arg); + return -1; + } + + struct ast *def = c->t->d; + collect_struct_tmps(s, def, (retval_at(arg, 0)).s, 0, retval); + retval_destroy(&arg); + return 0; +} + +static size_t collect_struct_retvals(struct lower_state *s, struct ast *def, char *rbuf, size_t base, struct vec *stores) +{ + size_t offset = base; + foreach_node(n, struct_body(def)) { + if (n->k != AST_VAR_DEF) + continue; + + if (n->t->k == TYPE_STRUCT) { + offset += collect_struct_retvals(s, n->t->d, rbuf, offset, stores); + continue; + } + + size_t sz = type_size(n->t); + assert(sz == 1 || sz == 3); + + char *type = sz == 1 ? "i9" : "i27"; + char *tmp = build_str("callret_%zd", s->uniq++); + printf("%s, ", tmp); + + char *store = build_str("%s >> %s %s %zd;\n", tmp, type, rbuf, offset); + vec_append(stores, &store); + offset += sz; + } + + assert(offset - base == type_size(def->t)); + return offset; +} + +static void collect_struct_rets(struct lower_state *s, struct type *rtype, char *rbuf, struct vec *retval) +{ + struct ast *def = rtype->d; + struct vec stores = vec_create(sizeof(char *)); + + printf("("); + collect_struct_retvals(s, def, rbuf, 0, &stores); + printf(");\n"); + + foreach_vec(si, stores) { + char *store = vect_at(char *, stores, si); + printf("%s", store); + free(store); + } + + vec_destroy(&stores); + struct retval r = build_retval(REG_I27, rbuf); + vec_append(retval, &r); +} + +static void collect_primitive_rets(struct lower_state *s, struct type *rtype, struct vec *retval) +{ + + char *name = build_str("(rv_%zd);\n", s->uniq++); + struct retval r = build_retval(is_small_type(rtype) ? REG_I9 : REG_I27, name); + vec_append(retval, &r); +} + static int lower_call(struct lower_state *s, struct ast *c, struct vec *retval) { @@ -687,24 +806,29 @@ static int lower_call(struct lower_state *s, struct ast *c, return -1; } + struct type *rtype = callable_rtype((call_expr(c))->t); + char *rbuf = NULL; + if (rtype->k == TYPE_STRUCT) { + rbuf = build_str("rbuf_%zd", s->uniq++); + printf("i27 %s = ^ %zd;\n", rbuf, type_size(rtype)); + } + /* collect all args */ struct vec args = retval_create(); foreach_node(a, call_args(c)) { - struct vec arg = retval_create(); - if (lower_expr(s, a, &arg)) { - retval_destroy(&arg); - retval_destroy(&args); - return -1; - } + if (a->t->k == TYPE_STRUCT) { + if (collect_struct_arg(s, a, &args)) { + retval_destroy(&args); + return -1; + } - foreach_retval(ri, arg) { - struct retval r = retval_at(arg, ri); - /* very important! */ - r.s = strdup(r.s); - vec_append(&args, &r); + continue; } - retval_destroy(&arg); + if (collect_primitive_arg(s, a, &args)) { + retval_destroy(&args); + return -1; + } } assert(vec_len(&call) == 1); @@ -715,27 +839,156 @@ static int lower_call(struct lower_state *s, struct ast *c, struct retval r = retval_at(args, ri); printf("%s, ", r.s); } + retval_destroy(&args); + + printf(") => "); + + if (rtype->k == TYPE_STRUCT) { + collect_struct_rets(s, rtype, rbuf, retval); + } + else if (rtype->k != TYPE_VOID) { + collect_primitive_rets(s, rtype, retval); + } + else { + printf("();\n"); + } + + return 0; +} + +static int lower_init(struct lower_state *s, struct ast *init, + struct vec *retval) +{ + assert(init->k == AST_INIT); + char *name = build_str("init_%zi", s->uniq++); + size_t size = type_size(init->t); + assert(size % 3 == 0); + + /* alloc stack space for struct */ + printf("i27 %s = ^ %zi;\n", name, size); + + char *dealloc = build_str("^^ %zi;\n", size); + add_dealloc(s, dealloc); + + size_t offset = 0; + foreach_node(n, init_body(init)) { + size_t sz = type_size(n->t); + + /* 2 is a special case where a struct consists of two i9s */ + if (sz > 2) + offset = align3k(offset); + + struct vec val = retval_create(); + if (lower_expr(s, var_init(n), &val)) { + retval_destroy(&val); + return -1; + } + + printf("i27 %soff = %s + %zi;\n", name, name, offset); + + char *type = is_small_type(n->t) ? "i9" : "i27"; + struct retval r = retval_at(val, 0); + if (n->t->k == TYPE_STRUCT) + printf("%soff <<* %zi %s;\n", name, sz, r.s); + else + printf("%s >> %s %soff;\n", r.s, type, name); + + offset += sz; + retval_destroy(&val); + } + + struct retval r = build_retval(REG_I27, name); + vec_reset(retval); + vect_append(struct retval, *retval, &r); + assert(offset == size); + + return 0; +} + +static int lower_fetch(struct lower_state *s, struct ast *f, struct vec *retval) +{ + /* at this point all fetches should exclusively be about fetching a + * specific function from a struct */ + assert(f->k == AST_FETCH); + assert(f->t->k == TYPE_CALLABLE); + char *name = mangle(f); + struct retval r = build_retval(REG_I27, name); + vec_append(retval, &r); + return 0; +} - if (!is_primitive(c->t) && c->t->k != TYPE_VOID) { - semantic_error(c->scope->fctx, c, - "only primitive return types implemented"); - retval_destroy(&args); +static int lower_ref(struct lower_state *s, struct ast *r, struct vec *retval) +{ + assert(r->k == AST_REF); + if (lower_expr(s, unop_expr(r), retval)) + return -1; + + /* structs are internally references anyway */ + if (r->t->k == TYPE_STRUCT) + return 0; + + struct retval ret = retval_at(*retval, 0); + char *ref = build_str("&%s", ret.s); + free(ret.s); + ret.s = ref; + retval_at(*retval, 0) = ret; + return 0; +} + +static int lower_deref(struct lower_state *s, struct ast *d, struct vec *retval) +{ + assert(d->k == AST_DEREF); + struct ast *expr = unop_expr(d); + struct vec input = retval_create(); + if (lower_expr(s, expr, &input)) { + retval_destroy(&input); return -1; } - printf(") => ( "); + assert(expr->t->k == TYPE_PTR); + char *name = build_str("deref_%zd", s->uniq++); + char *base = (retval_at(input, 0)).s; + char *type = is_small_type(expr->t) ? "i9" : "i27"; + printf("%s %s = << %s 0;\n", type, name, base); + retval_destroy(&input); + + struct retval r = build_retval(is_small_type(expr->t) ? REG_I9 : REG_I27, name); + vect_append(struct retval, *retval, &r); + return 0; +} - int i = 0; - foreach_type(t, c->t) { - char *s = build_str("r%i\n", i); - enum retval_kind k = is_small_type(t) ? REG_I9 : REG_I27; - struct retval r = build_retval(k, s); - vec_append(retval, &r); - i++; +static int lower_dot(struct lower_state *s, struct ast *d, struct vec *retval) +{ + assert(d->k == AST_DOT); + assert((dot_expr(d))->t->k == TYPE_STRUCT); + + struct vec input = retval_create(); + if (lower_expr(s, dot_expr(d), &input)) { + retval_destroy(&input); + return -1; } - printf(" );\n"); - retval_destroy(&args); + struct type *type = d->t; + size_t off = type_offsetof((dot_expr(d))->t, dot_id(d)); + char *name = build_str("dot_%zd", s->uniq++); + char *base = (retval_at(input, 0)).s; + + struct retval r; + if (type->k == TYPE_STRUCT) { + size_t size = type_size(type); + printf("i27 %s = ^ %zd;\n", name, type_size(type)); + printf("i27 %soff = %s + %zd;\n", name, base, off); + printf("%s <<* %zd %soff;\n", name, size, name); + r = build_retval(REG_I27, name); + } + else { + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s << %s %s %zd;\n", name, t, base, off); + r = build_retval(is_small_type(type) ? REG_I9 : REG_I27, name); + } + + vec_append(retval, &r); + retval_destroy(&input); return 0; } @@ -756,12 +1009,20 @@ static int lower_expr(struct lower_state *s, struct ast *e, switch (e->k) { case AST_VAR_DEF: return lower_var(s, e, retval); + case AST_INIT: return lower_init(s, e, retval); case AST_ID: return lower_id(s, e, retval); /* var is considered an expression in this case */ case AST_CAST: return lower_cast(s, e, retval); case AST_RETURN: return lower_return(s, e, retval); case AST_ASSIGN: return lower_assign(s, e, retval); case AST_CALL: return lower_call(s, e, retval); + case AST_FETCH: return lower_fetch(s, e, retval); + /* in the AST, it's useful to think of ref as just a + * unary operation, but here it's weird enough that it's + * not really worth it */ + case AST_REF: return lower_ref(s, e, retval); + case AST_DEREF: return lower_deref(s, e, retval); + case AST_DOT: return lower_dot(s, e, retval); case AST_IF: return lower_expr_if(s, e, retval); default: semantic_error(e->scope->fctx, e, @@ -776,11 +1037,34 @@ static int lower_block(struct lower_state *s, struct ast *block) { assert(block->k == AST_BLOCK); assert(!ast_flags(block, AST_FLAG_DOEXPR)); + + /* deallocs_top marks where the dealloc stack was when entering the + * block, and deallocs_bottom where the parent block's dealloc stack + * is. Stuff like continue and break will probably need this + * information, which is why it's attached to lower_state */ + size_t deallocs_top = vec_len(&s->dealloc); + size_t deallocs_parent = s->deallocs; + + s->deallocs = deallocs_top; foreach_node(n, block_body(block)) { if (lower_statement(s, n)) return -1; } + assert(deallocs_top <= vec_len(&s->dealloc)); + + /* heh, if this block contains a return, the deallocs get placed after + * the return. In the case of stack freeing, that's fine, but I'll have + * to be careful if I do something more fancy in the future. Qbt should + * be able to detect unreachable code, so this is not exactly an issue. */ + while (vec_len(&s->dealloc) > deallocs_top) { + /* perform all deallocs that were queued within this block */ + char *dealloc = vect_pop(char *, s->dealloc); + printf("%s", dealloc); + free(dealloc); + } + + s->deallocs = deallocs_parent; return 0; } @@ -794,6 +1078,7 @@ static int lower_statement(struct lower_state *s, struct ast *n) case AST_IF: ret = lower_if(s, n, &retval); break; case AST_FOR: ret = lower_for(s, n, &retval); break; case AST_BLOCK: ret = lower_block(s, n); break; + case AST_EMPTY: break; default: ret = lower_expr(s, n, &retval); break; } diff --git a/src/parser.y b/src/parser.y index 1bec7e2..4837a53 100644 --- a/src/parser.y +++ b/src/parser.y @@ -706,8 +706,8 @@ member ; members - : member members { $$ = $1; $1->n = $2; } - | member + : member members { $$ = $1; $1->n = $2; ast_set_flags($$, AST_FLAG_MEMBER); } + | member { $$ = $1; ast_set_flags($$, AST_FLAG_MEMBER); } opt_members : members -- cgit v1.3