diff options
Diffstat (limited to 'src/lower.c')
-rw-r--r-- | src/lower.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/src/lower.c b/src/lower.c new file mode 100644 index 0000000..f4ca28e --- /dev/null +++ b/src/lower.c @@ -0,0 +1,730 @@ +#include <stdlib.h> + +#include <posthaste/lower.h> +#include <posthaste/scope.h> + +#define UNUSED(x) (void)x + +static struct vec fns = {0}; +/* zero is unintialized, global 1 reserved as null, so skip two first globals */ +static size_t globals = 2; + +static void lower(struct fn *f, struct ast *n); +static void lower_list(struct fn *f, struct ast *l) +{ + foreach_node(n, l) { + lower(f, n); + } +} + +static size_t loc_as_int(struct loc l) +{ + size_t a = l.o; + a |= (size_t)(l.l) << (sizeof(size_t) * 8 - 1); + return a; +} + +static struct loc build_local_loc(size_t idx) +{ + return (struct loc){.l = 1, .o = idx}; +} + +static struct loc build_global_loc(size_t idx) +{ + return (struct loc){.l = 0, .o = idx}; +} + +static struct loc null_loc() +{ + /* don't exactly love this but I guess it works */ + return build_global_loc(1); +} + +static void output_insn(struct fn *f, enum insn_kind k, struct loc o, struct loc i0, struct loc i1, int64_t v) +{ + struct insn i = {.k = k, .o = o, .i0 = i0, .i1 = i1, .v = v}; + vect_append(struct insn, f->insns, &i); +} + +static void lower_var(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp++); + struct ast *expr = var_expr(n); + lower(f, expr); + output_insn(f, MOVE, n->l, expr->l, null_loc(), 0); +} + +static void lower_formal(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp++); +} + +static void lower_date(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + output_insn(f, CONST, n->l, null_loc(), null_loc(), n->v); +} + +static void lower_string(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + output_insn(f, CONST, n->l, null_loc(), null_loc(), (int64_t)n->id); +} + +static void lower_int(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + output_insn(f, CONST, n->l, null_loc(), null_loc(), n->v); +} + +static void lower_add(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = add_l(n); + struct ast *r = add_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, ADD, n->l, l->l, r->l, 0); +} + +static void lower_sub(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = sub_l(n); + struct ast *r = sub_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, SUB, n->l, l->l, r->l, 0); +} + +static void lower_mul(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = mul_l(n); + struct ast *r = mul_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, MUL, n->l, l->l, r->l, 0); +} + +static void lower_div(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = div_l(n); + struct ast *r = div_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, DIV, n->l, l->l, r->l, 0); +} + +static void lower_dot_assign(struct fn *f, struct ast *n) +{ + struct ast *r = assign_r(n); + struct ast *l = assign_l(n); + + lower(f, r); + + struct ast *base = dot_base(l); + lower(f, base); + + enum insn_kind store = STORE_DAY; + if (same_id(l->id, "day")) + store = STORE_DAY; + + else if (same_id(l->id, "month")) + store = STORE_MONTH; + + else if (same_id(l->id, "year")) + store = STORE_YEAR; + else + abort(); + + output_insn(f, store, base->l, base->l, r->l, 0); + n->l = base->l; +} + +static void lower_assign(struct fn *f, struct ast *n) +{ + struct ast *l = assign_l(n); + if (l->k == AST_DOT) + return lower_dot_assign(f, n); + + struct ast *r = assign_r(n); + + lower(f, r); + lower(f, l); + output_insn(f, MOVE, l->l, r->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_id(struct fn *f, struct ast *n) +{ + UNUSED(f); + struct ast *exists = file_scope_find(n->scope, n->id); + assert(exists); + + n->l = exists->l; +} + +static void lower_return(struct fn *f, struct ast *n) +{ + struct ast *expr = return_expr(n); + lower(f, expr); + output_insn(f, RET, null_loc(), expr->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_attr(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + + struct ast *base = attr_base(n); + lower(f, base); + + enum insn_kind load = LOAD_DAY; + if (same_id(n->id, "day")) + load = LOAD_DAY; + + else if (same_id(n->id, "month")) + load = LOAD_MONTH; + + else if (same_id(n->id, "year")) + load = LOAD_YEAR; + + else if (same_id(n->id, "weekday")) + load = LOAD_WEEKDAY; + + else if (same_id(n->id, "weeknum")) + load = LOAD_WEEKNUM; + + else + abort(); + + output_insn(f, load, n->l, base->l, null_loc(), 0); +} + +static void lower_print(struct fn *f, struct ast *p) +{ + foreach_node(n, print_items(p)) { + lower(f, n); + + /* don't print space on last element */ + if (n->n) + output_insn(f, PRINT_SPACE, null_loc(), null_loc(), null_loc(), 0); + } + + output_insn(f, PRINT_NEWLINE, null_loc(), null_loc(), null_loc(), 0); + p->l = null_loc(); +} + +static void lower_proc_call(struct fn *f, struct ast *c) +{ + size_t count = 0; + foreach_node(n, proc_call_args(c)) { + f->sp += 1; lower(f, n); count += 1; + } + + size_t i = 0; + foreach_node(n, proc_call_args(c)) { + output_insn(f, ARG, null_loc(), n->l, null_loc(), i); + i++; + } + + struct ast *def = file_scope_find(c->scope, proc_call_id(c)); + output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l)); + + c->l = build_local_loc(f->sp); + if (c->t != TYPE_VOID) + output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0); + + f->sp -= count; +} + +static void lower_func_call(struct fn *f, struct ast *c) +{ + size_t count = 0; + foreach_node(n, func_call_args(c)) { + f->sp += 1; lower(f, n); count += 1; + } + + size_t i = 0; + foreach_node(n, func_call_args(c)) { + output_insn(f, ARG, null_loc(), n->l, null_loc(), i); + i++; + } + + struct ast *def = file_scope_find(c->scope, func_call_id(c)); + output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l)); + c->l = build_local_loc(f->sp); + if (c->t != TYPE_VOID) + output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0); + + f->sp -= count; +} + +static void lower_eq(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = eq_l(n); + struct ast *r = eq_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, EQ, n->l, l->l, r->l, 0); +} + +static void lower_lt(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *l = lt_l(n); + struct ast *r = lt_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, LT, n->l, l->l, r->l, 0); +} + +static void lower_pos(struct fn *f, struct ast *n) +{ + struct ast *expr = pos_expr(n); + lower(f, expr); + n->l = expr->l; +} + +static void lower_neg(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + struct ast *expr = neg_expr(n); + f->sp += 1; lower(f, expr); + f->sp -= 1; + + output_insn(f, NEG, n->l, expr->l, null_loc(), 0); +} + +static void lower_print_date(struct fn *f, struct ast *n) +{ + struct ast *expr = print_date_expr(n); + lower(f, expr); + output_insn(f, PRINT_DATE, null_loc(), expr->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_print_int(struct fn *f, struct ast *n) +{ + struct ast *expr = print_int_expr(n); + lower(f, expr); + output_insn(f, PRINT_INT, null_loc(), expr->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_print_string(struct fn *f, struct ast *n) +{ + struct ast *expr = print_string_expr(n); + lower(f, expr); + output_insn(f, PRINT_STRING, null_loc(), expr->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_print_bool(struct fn *f, struct ast *n) +{ + struct ast *expr = print_bool_expr(n); + lower(f, expr); + output_insn(f, PRINT_BOOL, null_loc(), expr->l, null_loc(), 0); + n->l = null_loc(); +} + +static void lower_date_add(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + + struct ast *l = date_add_l(n); + struct ast *r = date_add_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, DATE_ADD, n->l, l->l, r->l, 0); +} + +static void lower_date_sub(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + + struct ast *l = date_sub_l(n); + struct ast *r = date_sub_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, DATE_SUB, n->l, l->l, r->l, 0); +} + +static void lower_date_diff(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + + struct ast *l = date_diff_l(n); + struct ast *r = date_diff_r(n); + + f->sp += 1; lower(f, l); + f->sp += 1; lower(f, r); + f->sp -= 2; + + output_insn(f, DATE_DIFF, n->l, l->l, r->l, 0); +} + +static void lower_until(struct fn *f, struct ast *n) +{ + size_t off = vec_len(&f->insns); + output_insn(f, LABEL, null_loc(), null_loc(), null_loc(), 0); + + lower_list(f, until_body(n)); + + struct ast *cond = until_cond(n); + lower(f, cond); + + output_insn(f, BZ, null_loc(), cond->l, null_loc(), off); + n->l = null_loc(); +} + +static void patch_branch(struct fn *f, size_t branch, size_t off) +{ + struct insn i = vect_at(struct insn, f->insns, branch); + i.v = off; + vect_at(struct insn, f->insns, branch) = i; +} + +static void lower_unless(struct fn *f, struct ast *n) +{ + struct ast *cond = unless_cond(n); + lower(f, cond); + + size_t branch = vec_len(&f->insns); + /* placeholder */ + output_insn(f, B, null_loc(), cond->l, null_loc(), 0); + + struct ast *body = unless_body(n); + lower_list(f, body); + + size_t jump = vec_len(&f->insns); + /* placeholder */ + output_insn(f, J, null_loc(), null_loc(), null_loc(), 0); + + size_t off = vec_len(&f->insns); + patch_branch(f, branch, off); + + struct ast *otherwise = unless_otherwise(n); + lower_list(f, otherwise); + + off = vec_len(&f->insns); + patch_branch(f, jump, off); + + n->l = null_loc(); +} + +static void lower_unless_expr(struct fn *f, struct ast *n) +{ + n->l = build_local_loc(f->sp); + + struct ast *cond = unless_expr_cond(n); + lower(f, cond); + + size_t branch = vec_len(&f->insns); + /* placeholder */ + output_insn(f, B, null_loc(), cond->l, null_loc(), 0); + + struct ast *body = unless_expr_body(n); + lower(f, body); + + output_insn(f, MOVE, n->l, body->l, null_loc(), 0); + + size_t jump = vec_len(&f->insns); + /* placeholder */ + output_insn(f, J, null_loc(), null_loc(), null_loc(), 0); + + size_t off = vec_len(&f->insns); + patch_branch(f, branch, off); + + struct ast *otherwise = unless_expr_otherwise(n); + lower(f, otherwise); + output_insn(f, MOVE, n->l, otherwise->l, null_loc(), 0); + + off = vec_len(&f->insns); + patch_branch(f, jump, off); +} + +static void lower_builtin_call(struct fn *f, struct ast *n) +{ + /* for now we only support Today(), which doesn't have any args */ + assert(same_id(builtin_call_id(n), "Today")); + + n->l = build_local_loc(f->sp); + output_insn(f, TODAY, n->l, null_loc(), null_loc(), 0); +} + +static void lower(struct fn *f, struct ast *n) +{ + if (f->max_sp < f->sp) + f->max_sp = f->sp; + + switch (n->k) { + case AST_PROC_DEF: break; + case AST_FUNC_DEF: break; + case AST_VAR_DEF: lower_var(f, n); break; + case AST_FORMAL_DEF: lower_formal(f, n); break; + case AST_CONST_STRING: lower_string(f, n); break; + case AST_CONST_DATE: lower_date(f, n); break; + case AST_CONST_INT: lower_int(f, n); break; + case AST_ASSIGN: lower_assign(f, n); break; + case AST_ADD: lower_add(f, n); break; + case AST_SUB: lower_sub(f, n); break; + case AST_MUL: lower_mul(f, n); break; + case AST_DIV: lower_div(f, n); break; + case AST_ID: lower_id(f, n); break; + case AST_RETURN: lower_return(f, n); break; + case AST_ATTR: lower_attr(f, n); break; + case AST_PRINT: lower_print(f, n); break; + case AST_PROC_CALL: lower_proc_call(f, n); break; + case AST_FUNC_CALL: lower_func_call(f, n); break; + case AST_BUILTIN_CALL: lower_builtin_call(f, n); break; + case AST_EQ: lower_eq(f, n); break; + case AST_LT: lower_lt(f, n); break; + case AST_POS: lower_pos(f, n); break; + case AST_NEG: lower_neg(f, n); break; + case AST_PRINT_DATE: lower_print_date(f, n); break; + case AST_PRINT_STRING: lower_print_string(f, n); break; + case AST_PRINT_BOOL: lower_print_bool(f, n); break; + case AST_PRINT_INT: lower_print_int(f, n); break; + case AST_DATE_ADD: lower_date_add(f, n); break; + case AST_DATE_SUB: lower_date_sub(f, n); break; + case AST_DATE_DIFF: lower_date_diff(f, n); break; + case AST_UNTIL: lower_until(f, n); break; + case AST_UNLESS: lower_unless(f, n); break; + case AST_UNLESS_EXPR: lower_unless_expr(f, n); break; + + /* handled by assign */ + case AST_DOT: break; + } + + assert(loc_as_int(n->l) > 0); +} + +static void lower_global_var(struct fn *f, struct ast *n) { + n->l = build_global_loc(globals++); + struct ast *expr = var_expr(n); + lower(f, expr); + output_insn(f, MOVE, n->l, expr->l, null_loc(), 0); +} + +static void add_proc(struct ast *n) { + size_t idx = vec_len(&fns); + n->l = build_global_loc(idx); + struct fn f = {.name = proc_id(n), + .idx = idx, + .sp = 0, + .insns = vec_create(sizeof(struct insn))}; + + vect_append(struct fn, fns, &f); +} + +static void add_func(struct ast *n) { + size_t idx = vec_len(&fns); + n->l = build_global_loc(idx); + struct fn f = {.name = func_id(n), + .idx = idx, + .sp = 0, + .insns = vec_create(sizeof(struct insn))}; + + vect_append(struct fn, fns, &f); +} + +static void lower_proc_def(struct ast *d) +{ + struct fn *f = vec_at(&fns, d->l.o); + assert(f); + + lower_list(f, proc_formals(d)); + lower_list(f, proc_vars(d)); + lower_list(f, proc_body(d)); + + if (d->t == TYPE_VOID) + output_insn(f, STOP, null_loc(), null_loc(), null_loc(), 0); +} + +static void lower_func_def(struct ast *d) +{ + struct fn *f = vec_at(&fns, d->l.o); + assert(f); + + lower_list(f, func_formals(d)); + lower_list(f, func_vars(d)); + lower_list(f, func_body(d)); +} + +#ifdef DEBUG +static void dump_loc(struct loc l) +{ + if (is_null_loc(l)) { + printf("null, "); + return; + } + + bool local = l.l; + if (local) { + printf("l"); + } else { + printf("g"); + } + + size_t val = l.o; + printf("%zd, ", val); +} + +static void dump_val(int64_t v) +{ + printf("%lli", (long long)v); +} + +static void dump_insn(struct insn i, size_t addr) +{ + printf("//%8zd: ", addr); +#define DUMP(x) case x: printf(#x); break; + switch (i.k) { + DUMP(TODAY); + DUMP(CALL); + DUMP(MOVE); + DUMP(ADD); + DUMP(SUB); + DUMP(MUL); + DUMP(DIV); + DUMP(ARG); + DUMP(STOP); + DUMP(RETVAL); + DUMP(PRINT_DATE); + DUMP(PRINT_INT); + DUMP(PRINT_STRING); + DUMP(PRINT_NEWLINE); + DUMP(PRINT_SPACE); + DUMP(PRINT_BOOL); + DUMP(DATE_ADD); + DUMP(DATE_SUB); + DUMP(DATE_DIFF); + DUMP(STORE_DAY); + DUMP(STORE_MONTH); + DUMP(STORE_YEAR); + DUMP(LOAD_DAY); + DUMP(LOAD_MONTH); + DUMP(LOAD_YEAR); + DUMP(LOAD_WEEKDAY); + DUMP(LOAD_WEEKNUM); + DUMP(RET); + DUMP(CONST); + DUMP(EQ); + DUMP(LT); + DUMP(NEG); + DUMP(B); + DUMP(BZ); + DUMP(J); + DUMP(LABEL); + } +#undef DUMP + + printf(" "); + + dump_loc(i.o); + dump_loc(i.i0); + dump_loc(i.i1); + dump_val(i.v); + + printf("\n"); +} + +static void dump_insns(struct fn *f) +{ + size_t addr = 0; + foreach_vec(ii, f->insns) { + struct insn i = vect_at(struct insn, f->insns, ii); + dump_insn(i, addr); + addr++; + } +} +#endif /* DEBUG */ + +struct fn *find_fn(size_t idx) +{ + return vec_at(&fns, idx); +} + +int lower_ast(struct ast *tree) +{ + fns = vec_create(sizeof(struct fn)); + + struct fn main = {.name = "main", + .idx = 0, + .sp = 0, + .insns = vec_create(sizeof(struct insn))}; + + vect_append(struct fn, fns, &main); + + foreach_node(n, tree) { + switch (n->k) { + case AST_PROC_DEF: add_proc(n); break; + case AST_FUNC_DEF: add_func(n); break; + default: + } + } + + struct fn *f = &vect_at(struct fn, fns, 0); + foreach_node(n, tree) { + switch (n->k) { + case AST_VAR_DEF: lower_global_var(f, n); break; + case AST_PROC_DEF: lower_proc_def(n); break; + case AST_FUNC_DEF: lower_func_def(n); break; + default: lower(f, n); + } + } + + output_insn(f, STOP, null_loc(), null_loc(), null_loc(), 0); + +#ifdef DEBUG + foreach_vec(fi, fns) { + struct fn *f = vec_at(&fns, fi); + printf("// %s (%zd):\n", f->name, f->idx); + dump_insns(f); + } +#endif + return 0; +} + +static void destroy_fn(struct fn *f) +{ + vec_destroy(&f->insns); +} + +void destroy_lowering() +{ + foreach_vec(fi, fns) { + struct fn *f = vec_at(&fns, fi); + destroy_fn(f); + } + + vec_destroy(&fns); +} |