diff options
Diffstat (limited to 'src/lower.c')
| -rw-r--r-- | src/lower.c | 365 |
1 files changed, 185 insertions, 180 deletions
diff --git a/src/lower.c b/src/lower.c index c78ac96..bf36fae 100644 --- a/src/lower.c +++ b/src/lower.c @@ -108,12 +108,17 @@ static bool retval_is_const(struct retval r) return r.kind == CONST_I9 || r.kind == CONST_I27; } -static bool is_i9(struct ast_node *n) +static bool is_small_type(struct type *type) { - if (AST_TYPE(n->type).kind != AST_TYPE_PRIMITIVE) - return false; + switch (type->k) { + case TYPE_I9: + case TYPE_BOOL: + return true; - return AST_PRIMITIVE_TYPE(n->type).type == AST_I9; + default: + } + + return false; } #define retval_create() \ @@ -157,26 +162,27 @@ char *build_str(const char *fmt, ...) { return buf; } -static size_t get_scope_number(struct ast_node *id) +static size_t get_scope_number(struct ast *id) { /** @todo this mirrors what's in actualize.c:actualize_id, same comments * apply */ - struct ast_node *def = file_scope_find_var(id->scope, id); + struct ast *def = file_scope_find_var(id->scope, id->s); if (def) return def->scope->number; - def = file_scope_find_proc(id->scope, id); + def = file_scope_find_proc(id->scope, id->s); if (def) return def->scope->number; return 0; } -static char *mangle_idx(struct ast_node *id, size_t idx) +static char *mangle_idx(struct ast *id, size_t idx) { - assert(id->node_type == AST_ID); assert(id->scope); - const char *name = AST_ID(id).id; + assert(id->s); + + const char *name = id->s; /* oh wait, I need to do a variable lookup on the ID, not use the ID's * scope number, duh */ size_t number = get_scope_number(id); @@ -187,89 +193,74 @@ static char *mangle_idx(struct ast_node *id, size_t idx) return build_str("%s_s%zif%zi", name, number, idx); } -static char *mangle(struct ast_node *id) +static char *mangle(struct ast *id) { return mangle_idx(id, 0); } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval); -static int lower_statement(struct lower_state *s, struct ast_node *n); +static int lower_statement(struct lower_state *s, struct ast *n); -static void output_id(struct ast_node *id) +static void output_ast_id(struct ast *id) { + assert(id->s); char *name = mangle(id); printf("%s", name); free(name); } -static int lower_global_var(struct ast_node *n) +static int lower_global_var(struct ast *n) { /* trivial types are reasonably easy, but stuff like compound types need * a lot of work */ - struct ast_node *type = AST_VAR(n).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + struct type *type = var_type(n); + if (is_primitive(type)) { semantic_error(n->scope->fctx, n, "only primitive globals currently implemented"); return -1; } - struct ast_node *id = AST_VAR(n).id; - struct ast_node *init = AST_VAR(n).init; - if (init->node_type != AST_CONST) { + struct ast *init = var_init(n); + if (init->k != AST_CONST_INT) { semantic_error(n->scope->fctx, n, - "constant expressions currently not implemented"); + "only constant expressions currently implemented"); return -1; } - output_id(id); + output_ast_id(n); printf(" = "); /* hmm, this might be useful elsewhere as well */ - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 %lli", AST_CONST(init).integer); break; - case AST_I9: printf("i9 %lli", AST_CONST(init).integer); break; - default: - semantic_error(n->scope->fctx, n, - "unhandled primitive type"); - return -1; - } - + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s %lli", t, int_val(init)); printf(";\n"); return 0; } -static int lower_param(struct lower_state *s, struct ast_node *p) +static int lower_param(struct lower_state *s, struct ast *p) { UNUSED(s); - assert(p->node_type == AST_VAR); - struct ast_node *type = AST_VAR(p).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + assert(p->k == AST_VAR_DEF); + struct type *type = var_type(p); + if (!is_primitive(type)) { semantic_error(p->scope->fctx, p, "only primitive params currently implemented"); return -1; } - assert(AST_VAR(p).init == NULL); - - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 "); break; - case AST_I9: printf("i9 "); break; - default: - semantic_error(p->scope->fctx, p, - "unhandled primitive type"); - return -1; - } + assert(var_init(p) == NULL); - struct ast_node *id = AST_VAR(p).id; - output_id(id); + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s ", t); + output_ast_id(p); printf(","); return 0; } -static int lower_params(struct lower_state *s, struct ast_node *params) +static int lower_params(struct lower_state *s, struct ast *params) { - for (struct ast_node *p = params; p; p = p->next) { + foreach_node(p, params) { if (lower_param(s, p)) return -1; } @@ -277,20 +268,19 @@ static int lower_params(struct lower_state *s, struct ast_node *params) return 0; } -static int lower_var(struct lower_state *s, struct ast_node *v, +static int lower_var(struct lower_state *s, struct ast *v, struct vec *retval) { - assert(v->node_type == AST_VAR); + assert(v->k == AST_VAR_DEF); struct vec input = retval_create(); - if (lower_expr(s, AST_VAR(v).init, &input)) + if (lower_expr(s, var_init(v), &input)) return -1; - struct ast_node *id = AST_VAR(v).id; /* 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(id, 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); @@ -355,27 +345,24 @@ static void do_store(struct lower_state *s, struct vec *from, struct vec *to, } } -static int lower_cast(struct lower_state *s, struct ast_node *e, +static int lower_cast(struct lower_state *s, struct ast *e, struct vec *retval) { - assert(e->node_type == AST_CAST); + assert(e->k == AST_CAST); /** @todo make sure actualize removes casts that aren't of these types * */ - assert(AST_TYPE(e->type).kind == AST_TYPE_PRIMITIVE - || AST_TYPE(e->type).kind == AST_TYPE_POINTER); + assert(is_primitive(e->t)); - if (lower_expr(s, AST_CAST(e).expr, retval)) + if (lower_expr(s, cast_expr(e), retval)) return -1; - enum retval_kind kind = REG_I27; - if (is_i9(e)) - kind = REG_I9; + enum retval_kind kind = is_small_type(e->t) ? REG_I9 : REG_I27; foreach_retval(ri, *retval) { struct retval r = retval_at(*retval, ri); /* build new temporary cast result and replace the previous * retval */ - char *s = build_str("%s%s", "cast_", r.s); + char *s = build_str("%s%s", "cast", r.s); printf("%s %s = %s;\n", retval_kind_str(kind), s, r.s); free(r.s); @@ -388,49 +375,35 @@ static int lower_cast(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_const(struct lower_state *s, struct ast_node *c, +static int lower_const(struct lower_state *s, struct ast *c, struct vec *retval) { UNUSED(s); - assert(c->node_type == AST_CONST); - if (AST_CONST(c).kind == AST_CONST_STRING) { - /* requires pushing strings as variables and replacing them with - * references */ - semantic_error(c->scope->fctx, c, - "string constant lowering not yet implemented"); - return -1; - } - - enum retval_kind type = CONST_I27; - if (AST_PRIMITIVE_TYPE(c->type).type == AST_I9) - type = CONST_I9; - - char *str = build_str("%lli", (long long int)AST_CONST(c).integer); - struct retval r = build_retval(type, str); + assert(is_const(c)); + char *str = build_str("%lli", (long long)c->v); + enum retval_kind kind = is_small_type(c->t) ? CONST_I9: CONST_I27; + struct retval r = build_retval(kind, str); vec_append(retval, &r); return 0; } -static int lower_assign(struct lower_state *s, struct ast_node *a, +static int lower_assign(struct lower_state *s, struct ast *a, struct vec *retval) { -#define IS_DEREF(t) (t->node_type == AST_UNOP && AST_UNOP(t).op == AST_DEREF) -#define IS_ARR(t) (t->node_type == AST_ARR_ACCESS) - - assert(a->node_type == AST_ASSIGN); - if (lower_expr(s, AST_ASSIGN(a).from, retval)) + assert(a->k == AST_ASSIGN); + if (lower_expr(s, assign_from(a), retval)) return -1; struct vec loc = retval_create(); struct vec off = retval_create(); - struct ast_node *to = AST_ASSIGN(a).to; - struct ast_node *base = to; - if (IS_DEREF(to)) - base = AST_UNOP(to).expr; - else if (IS_ARR(to)) { - base = AST_ARR_ACCESS(to).base; - if (lower_expr(s, AST_ARR_ACCESS(to).idx, &off)) { + struct ast *to = assign_to(a); + struct ast *base = to; + if (to->k == AST_DEREF) + base = unop_expr(to); + else if (to->k == AST_ARR) { + base = arr_base(to); + if (lower_expr(s, arr_idx(to), &off)) { retval_destroy(&loc); retval_destroy(&off); return -1; @@ -443,10 +416,10 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, return -1; } - if (IS_DEREF(to)) { + if (to->k == AST_DEREF) { do_store(s, retval, &loc, NULL); } - else if (IS_ARR(to)) { + else if (to->k == AST_ARR) { do_store(s, retval, &loc, &off); } else { assert(vec_len(retval) == vec_len(&loc)); @@ -464,29 +437,25 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, #undef IS_ARR } -static int lower_id(struct lower_state *s, struct ast_node *id, +static int lower_id(struct lower_state *s, struct ast *id, struct vec *retval) { UNUSED(s); - assert(id->node_type == AST_ID); + assert(id->k == AST_ID); char *m = mangle(id); - struct ast_node *type = id->type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE - && AST_TYPE(type).kind != AST_TYPE_POINTER - && AST_TYPE(type).kind != AST_TYPE_SIGN) { + 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 = REG_I27; - if (is_i9(id)) - kind = REG_I9; + 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 * want to take the address of */ - if (AST_TYPE(type).kind == AST_TYPE_SIGN) { + if (type->k == TYPE_CALLABLE) { char *o = m; m = build_str("&%s", m); free(o); @@ -497,11 +466,12 @@ static int lower_id(struct lower_state *s, struct ast_node *id, return 0; } -static int lower_return(struct lower_state *s, struct ast_node *r, +static int lower_return(struct lower_state *s, struct ast *r, struct vec *retval) { - assert(r->node_type == AST_RETURN); - if (lower_expr(s, AST_RETURN(r).expr, retval)) + assert(r->k == AST_RETURN); + /** @todo defers, should they be handled here or in ast? */ + if (lower_expr(s, return_expr(r), retval)) return -1; printf("=> ( "); @@ -515,11 +485,11 @@ static int lower_return(struct lower_state *s, struct ast_node *r, return 0; } -static int lower_if(struct lower_state *s, struct ast_node *i, +static int lower_if(struct lower_state *s, struct ast *i, struct vec *retval) { - assert(i->node_type == AST_IF); - if (lower_expr(s, AST_IF(i).cond, retval)) + assert(i->k == AST_IF); + if (lower_expr(s, if_cond(i), retval)) return -1; assert(vec_len(retval) == 1); @@ -533,7 +503,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("! %s -> %s;\n", (retval_at(*retval, 0)).s, bottom); /* a block counts as a statement in this case */ - if (lower_statement(s, AST_IF(i).body)) { + if (lower_statement(s, if_body(i))) { free(bottom); return -1; } @@ -542,7 +512,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("%s:\n", bottom); free(bottom); - if (AST_IF(i).els && lower_statement(s, AST_IF(i).els)) { + if (if_else(i) && lower_statement(s, if_else(i))) { free(out); return -1; } @@ -552,11 +522,11 @@ static int lower_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_for(struct lower_state *s, struct ast_node *f, +static int lower_for(struct lower_state *s, struct ast *f, struct vec *retval) { - assert(f->node_type == AST_FOR); - if (lower_statement(s, AST_FOR(f).pre)) + assert(f->k == AST_FOR); + if (lower_statement(s, for_pre(f))) return -1; long long uniq = s->uniq++; @@ -570,19 +540,19 @@ static int lower_for(struct lower_state *s, struct ast_node *f, printf("-> %s;\n", out); printf("%s:\n", top); - if (lower_statement(s, AST_FOR(f).body)) { + if (lower_statement(s, for_body(f))) { pop_loop(s); return -1; } printf("%s:\n", bottom); - if (lower_statement(s, AST_FOR(f).post)) { + if (lower_statement(s, for_post(f))) { pop_loop(s); return -1; } printf("%s:\n", out); - if (lower_expr(s, AST_FOR(f).cond, retval)) { + if (lower_expr(s, for_cond(f), retval)) { pop_loop(s); return -1; } @@ -594,7 +564,7 @@ static int lower_for(struct lower_state *s, struct ast_node *f, return 0; } -static int lower_expr_if(struct lower_state *s, struct ast_node *i, +static int lower_expr_if(struct lower_state *s, struct ast *i, struct vec *retval) { semantic_error(i->scope->fctx, i, @@ -602,19 +572,19 @@ static int lower_expr_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_binop(struct lower_state *s, struct ast_node *i, +static int lower_binop(struct lower_state *s, struct ast *i, struct vec *retval) { struct vec l = retval_create(); struct vec r = retval_create(); - if (lower_expr(s, AST_BINOP(i).left, &l)) { + if (lower_expr(s, binop_left(i), &l)) { retval_destroy(&l); retval_destroy(&r); return -1; } - if (lower_expr(s, AST_BINOP(i).right, &r)) { + if (lower_expr(s, binop_right(i), &r)) { retval_destroy(&l); retval_destroy(&r); return -1; @@ -623,12 +593,12 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, assert(vec_len(&l) == 1); assert(vec_len(&r) == 1); - char *name = build_str("tmp%lli", (long long)s->uniq++); + char *name = build_str("binop%lli", (long long)s->uniq++); struct retval ret = build_retval(REG_I27, name); vec_append(retval, &ret); char *op = ""; - switch (AST_BINOP(i).op) { + switch (i->k) { case AST_ADD: op = "+"; break; case AST_SUB: op = "-"; break; case AST_MUL: op = "*"; break; @@ -636,6 +606,50 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_REM: op = "%"; break; case AST_LSHIFT: op = "<<"; break; case AST_RSHIFT: op = ">>"; break; + default: semantic_error(i->scope->fctx, i, + "unimplemented binary operation"); + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + printf("i27 %s = %s %s %s;\n", name, + (retval_at(l, 0)).s, + op, + (retval_at(r, 0)).s); + + retval_destroy(&l); + retval_destroy(&r); + return 0; +} + +static int lower_comparison(struct lower_state *s, struct ast *i, struct vec *retval) +{ + /* very similar to lower_binop, hmm */ + struct vec l = retval_create(); + struct vec r = retval_create(); + + if (lower_expr(s, comparison_left(i), &l)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + if (lower_expr(s, comparison_right(i), &r)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + assert(vec_len(&l) == 1); + assert(vec_len(&r) == 1); + + char *name = build_str("comp%lli", (long long)s->uniq++); + struct retval ret = build_retval(REG_I27, name); + vec_append(retval, &ret); + + char *op = ""; + switch (i->k) { case AST_LT: op = "<"; break; case AST_GT: op = ">"; break; case AST_LE: op = "<="; break; @@ -643,13 +657,13 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_NE: op = "!="; break; case AST_EQ: op = "=="; break; default: semantic_error(i->scope->fctx, i, - "unimplemented binary operation"); + "unimplemented comparison operation"); retval_destroy(&l); retval_destroy(&r); return -1; } - printf("i27 %s = %s %s %s;\n", name, + printf("i9 %s = %s %s %s;\n", name, (retval_at(l, 0)).s, op, (retval_at(r, 0)).s); @@ -659,20 +673,20 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_call(struct lower_state *s, struct ast_node *c, +static int lower_call(struct lower_state *s, struct ast *c, struct vec *retval) { - assert(c->node_type == AST_CALL); + assert(c->k == AST_CALL); struct vec call = retval_create(); - if (lower_expr(s, AST_CALL(c).expr, &call)) { + if (lower_expr(s, call_expr(c), &call)) { retval_destroy(&call); return -1; } /* collect all args */ struct vec args = retval_create(); - foreach_node(a, AST_CALL(c).args) { + foreach_node(a, call_args(c)) { struct vec arg = retval_create(); if (lower_expr(s, a, &arg)) { retval_destroy(&arg); @@ -699,37 +713,51 @@ static int lower_call(struct lower_state *s, struct ast_node *c, printf("%s, ", r.s); } -#define IS_VOID(t) \ - (t->node_type == AST_TYPE && AST_TYPE(t).kind == AST_TYPE_PRIMITIVE && \ - AST_PRIMITIVE_TYPE(t).type == AST_VOID) - - if (!IS_VOID(c->type)) { + if (!is_primitive(c->t) && c->t->k != TYPE_VOID) { semantic_error(c->scope->fctx, c, - "only void return type implemented"); + "only primitive return types implemented"); retval_destroy(&args); return -1; } - printf(") => ();\n"); + printf(") => ( "); + + 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++; + } + + printf(" );\n"); retval_destroy(&args); return 0; } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval) { if (!e) return 0; - switch (e->node_type) { + if (is_const(e)) + return lower_const(s, e, retval); + + if (is_binop(e)) + return lower_binop(s, e, retval); + + if (is_comparison(e)) + return lower_comparison(s, e, retval); + + switch (e->k) { + case AST_VAR_DEF: return lower_var(s, e, retval); case AST_ID: return lower_id(s, e, retval); /* var is considered an expression in this case */ - case AST_VAR: return lower_var(s, e, retval); case AST_CAST: return lower_cast(s, e, retval); - case AST_CONST: return lower_const(s, e, retval); case AST_RETURN: return lower_return(s, e, retval); case AST_ASSIGN: return lower_assign(s, e, retval); - case AST_BINOP: return lower_binop(s, e, retval); case AST_CALL: return lower_call(s, e, retval); case AST_IF: return lower_expr_if(s, e, retval); default: @@ -741,25 +769,24 @@ static int lower_expr(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_block(struct lower_state *s, struct ast_node *body) +static int lower_block(struct lower_state *s, struct ast *block) { - assert(body->node_type == AST_BLOCK); - assert(!ast_flags(body, AST_FLAG_DOEXPR)); - struct ast_node *stmt = AST_BLOCK(body).body; - for (; stmt; stmt = stmt->next) { - if (lower_statement(s, stmt)) + assert(block->k == AST_BLOCK); + assert(!ast_flags(block, AST_FLAG_DOEXPR)); + foreach_node(n, block_body(block)) { + if (lower_statement(s, n)) return -1; } return 0; } -static int lower_statement(struct lower_state *s, struct ast_node *n) +static int lower_statement(struct lower_state *s, struct ast *n) { struct vec retval = retval_create(); int ret = 0; - switch (n->node_type) { + switch (n->k) { case AST_RETURN: ret = lower_return(s, n, &retval); break; case AST_IF: ret = lower_if(s, n, &retval); break; case AST_FOR: ret = lower_for(s, n, &retval); break; @@ -771,24 +798,22 @@ static int lower_statement(struct lower_state *s, struct ast_node *n) return ret; } -static int lower_proc(struct ast_node *n) +static int lower_proc(struct ast *n) { - assert(n->node_type == AST_PROC); + assert(n->k == AST_PROC_DEF); /* nobody uses the proc, so no need to do anything */ - if (n->uses == 0 && !ast_flags(AST_PROC(n).id, AST_FLAG_NOMANGLE)) + if (n->uses == 0 && !ast_flags(n, AST_FLAG_NOMANGLE)) return 0; struct lower_state state = create_state(); /* name */ - struct ast_node *id = AST_PROC(n).id; - output_id(id); + output_ast_id(n); /* args */ printf("("); - struct ast_node *sign = AST_PROC(n).sign; - if (lower_params(&state, AST_SIGN_TYPE(sign).params)) { + if (lower_params(&state, proc_params(n))) { destroy_state(&state); return -1; } @@ -799,7 +824,7 @@ static int lower_proc(struct ast_node *n) /* body */ printf("{\n"); - if (lower_block(&state, AST_PROC(n).body)) { + if (lower_block(&state, proc_body(n))) { destroy_state(&state); return -1; } @@ -809,19 +834,13 @@ static int lower_proc(struct ast_node *n) return 0; } -static int lower_actual(struct ast_node *n) -{ - assert(AST_TYPE(n).kind == AST_TYPE_CONSTRUCT); - return 0; -} - -static int _lower_actuals(struct scope *root) +int lower(struct scope *root) { /* go through all child scopes but only do actual work on file-scope * includes are allowed inside procs etc to make something only locally * visible */ for (struct scope *c = root->children; c; c = c->next) { - if (_lower_actuals(c)) + if (lower(c)) return -1; } @@ -842,17 +861,3 @@ static int _lower_actuals(struct scope *root) return 0; } - -int lower_actuals(struct scope *root) -{ - int ret = _lower_actuals(root); - /* actuals are currently global, would it make more sense for them to be - * scope-local? */ - for (struct actual *a = root->actuals; a; a = a->next) { - assert(a->node); - if (lower_actual(a->node)) - return -1; - } - - return ret; -} |
