diff options
author | tomsmeding <tom.smeding@gmail.com> | 2018-01-07 20:35:36 +0100 |
---|---|---|
committer | tomsmeding <tom.smeding@gmail.com> | 2018-01-07 20:35:36 +0100 |
commit | 20d898496b7371872bfd128a895d39465f13c970 (patch) | |
tree | 44ab01e31b408a1ff96f454445a6886b41c0f681 | |
parent | 6eb88017ce9a2e91d90634cbb77097dd821b8626 (diff) |
Now with type checking
-rw-r--r-- | compiler.c | 197 | ||||
-rw-r--r-- | node.c | 2 | ||||
-rw-r--r-- | node.h | 2 |
3 files changed, 156 insertions, 45 deletions
@@ -29,14 +29,31 @@ static bool oper_is_comparison(enum operator oper) { oper == OP_LEQ || oper == OP_GEQ; } -static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node *node); + +struct info { + const char *name; + struct type *rettype; +}; + +static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct info info, struct node *node); static void compile_boolexpr( - struct ir *ir, struct symtab *symtab, struct node *node, const char *truedest, const char *falsedest) { + struct ir *ir, struct symtab *symtab, struct info info, struct node *node, + const char *truedest, const char *falsedest) { if (node->type == N_BINOP && oper_is_comparison(node->oper)) { - struct ref r1 = compile_expr(ir, symtab, node->child1); - struct ref r2 = compile_expr(ir, symtab, node->child2); + struct ref r1 = compile_expr(ir, symtab, info, node->child1); + struct ref r2 = compile_expr(ir, symtab, info, node->child2); + if (node->child1->valuetype != node->child2->valuetype || + (node->child1->valuetype->tag != T_INT && node->child1->valuetype->tag != T_PTR) || + (node->child2->valuetype->tag != T_INT && node->child2->valuetype->tag != T_PTR)) { + fprintf(stderr, "Invalid types "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, " and "); + type_print(node->child2->valuetype, stderr); + fprintf(stderr, " to operator %s\n", oper_string(node->oper)); + exit(1); + } ir_append(ir, irins_make_12(INS_CMP, r1, r2)); enum condcode condcode; switch (node->oper) { @@ -52,29 +69,30 @@ static void compile_boolexpr( ir_append(ir, irins_make_name(INS_JMP, truedest)); } else if (node->type == N_BINOP && node->oper == OP_AND) { const char *try2lbl = gen_label_name(); - compile_boolexpr(ir, symtab, node->child1, try2lbl, falsedest); + compile_boolexpr(ir, symtab, info, node->child1, try2lbl, falsedest); ir_append(ir, irins_make_name(INS_LBL, try2lbl)); - compile_boolexpr(ir, symtab, node->child2, truedest, falsedest); + compile_boolexpr(ir, symtab, info, node->child2, truedest, falsedest); } else if (node->type == N_BINOP && node->oper == OP_OR) { const char *try2lbl = gen_label_name(); - compile_boolexpr(ir, symtab, node->child1, truedest, try2lbl); + compile_boolexpr(ir, symtab, info, node->child1, truedest, try2lbl); ir_append(ir, irins_make_name(INS_LBL, try2lbl)); - compile_boolexpr(ir, symtab, node->child2, truedest, falsedest); + compile_boolexpr(ir, symtab, info, node->child2, truedest, falsedest); } else if (node->type == N_UNOP && node->oper == OP_NOT) { - compile_boolexpr(ir, symtab, node->child1, falsedest, truedest); + compile_boolexpr(ir, symtab, info, node->child1, falsedest, truedest); } else { - struct ref ref = compile_expr(ir, symtab, node); + struct ref ref = compile_expr(ir, symtab, info, node); ir_append(ir, irins_make_12(INS_TEST, ref, ref)); ir_append(ir, irins_make_jcc(falsedest, CCZ)); ir_append(ir, irins_make_name(INS_JMP, truedest)); } } -static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node *node) { +static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) { switch (node->type) { case N_NUM: { struct ref ref = ref_next_register(); ir_append(ir, irins_make_01(INS_MOV, ref, ref_imm(node->value))); + node->valuetype = type_int(16); return ref; } @@ -88,14 +106,23 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node fprintf(stderr, "Cannot use function name in expression\n"); exit(1); } + node->valuetype = sym->rtype; return sym->ref; } case N_BINOP: { if (oper_is_basic_arith(node->oper)) { struct ref r0 = ref_next_register(); - struct ref r1 = compile_expr(ir, symtab, node->child1); - struct ref r2 = compile_expr(ir, symtab, node->child2); + struct ref r1 = compile_expr(ir, symtab, info, node->child1); + struct ref r2 = compile_expr(ir, symtab, info, node->child2); + if (node->child1->valuetype != type_int(16) || node->child2->valuetype != type_int(16)) { + fprintf(stderr, "Invalid operand types "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, " and "); + type_print(node->child2->valuetype, stderr); + fprintf(stderr, " to arithmetic operator %s\n", oper_string(node->oper)); + exit(1); + } enum instype instype; switch (node->oper) { case OP_ADD: instype = INS_ADD; break; @@ -106,12 +133,13 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node default: assert(false); } ir_append(ir, irins_make_012(instype, r0, r1, r2)); + node->valuetype = node->child1->valuetype; return r0; } else if (oper_is_comparison(node->oper) || node->oper == OP_AND || node->oper == OP_OR) { const char *truelbl = gen_label_name(); const char *falselbl = gen_label_name(); const char *afterlbl = gen_label_name(); - compile_boolexpr(ir, symtab, node, truelbl, falselbl); + compile_boolexpr(ir, symtab, info, node, truelbl, falselbl); struct ref cval = ref_next_register(); ir_append(ir, irins_make_name(INS_LBL, truelbl)); ir_append(ir, irins_make_01(INS_MOV, cval, ref_imm(1))); @@ -123,9 +151,18 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node } else if (node->oper == OP_ASSIGN) { if (node->child1->type == N_VAR || (node->child1->type == N_UNOP && node->child1->oper == OP_DEREF)) { - struct ref r0 = compile_expr(ir, symtab, node->child1); - struct ref r1 = compile_expr(ir, symtab, node->child2); + struct ref r0 = compile_expr(ir, symtab, info, node->child1); + struct ref r1 = compile_expr(ir, symtab, info, node->child2); + if (node->child1->valuetype != node->child2->valuetype) { + fprintf(stderr, "Cannot assign type "); + type_print(node->child2->valuetype, stderr); + fprintf(stderr, " to destination of type "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, "\n"); + exit(1); + } ir_append(ir, irins_make_01(INS_MOV, r0, r1)); + node->valuetype = node->child1->valuetype; return r0; } else { fprintf(stderr, "Invalid left-hand side in assignment.\n"); @@ -140,25 +177,45 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node switch (node->oper) { case OP_NEG: { struct ref r0 = ref_next_register(); - struct ref r1 = compile_expr(ir, symtab, node->child1); + struct ref r1 = compile_expr(ir, symtab, info, node->child1); + if (node->child1->valuetype != type_int(16)) { + fprintf(stderr, "Cannot negate value of type "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, "\n"); + exit(1); + } ir_append(ir, irins_make_01(INS_NEG, r0, r1)); + node->valuetype = node->child1->valuetype; return r0; } case OP_NOT: { struct ref r0 = ref_next_register(); - struct ref r1 = compile_expr(ir, symtab, node->child1); + struct ref r1 = compile_expr(ir, symtab, info, node->child1); + if (node->child1->valuetype->tag != T_INT && node->child1->valuetype->tag != T_PTR) { + fprintf(stderr, "Cannot apply OP_NOT to value of type "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, "\n"); + exit(1); + } const char *afterlbl = gen_label_name(); ir_append(ir, irins_make_12(INS_TEST, r1, r1)); ir_append(ir, irins_make_01(INS_MOV, r0, ref_imm(0))); ir_append(ir, irins_make_jcc(afterlbl, CCNZ)); ir_append(ir, irins_make_01(INS_MOV, r0, ref_imm(1))); ir_append(ir, irins_make_name(INS_LBL, afterlbl)); + node->valuetype = type_int(16); return r0; } case OP_DEREF: { - struct ref addr = compile_expr(ir, symtab, node->child1); + struct ref addr = compile_expr(ir, symtab, info, node->child1); + if (node->child1->valuetype->tag != T_PTR) { + fprintf(stderr, "Cannot dereference value of type "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, "\n"); + exit(1); + } struct ref r1; switch (addr.type) { case REF_REG: r1 = ref_mem(addr.reg, 0, REFREL_ZERO); break; @@ -172,6 +229,8 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node } struct ref r0 = ref_next_register(); ir_append(ir, irins_make_01(INS_MOV, r0, r1)); + assert(node->child1->valuetype->tag == T_PTR); + node->valuetype = node->child1->valuetype->target; return r0; } @@ -191,17 +250,27 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node exit(1); } if (sym->stype != ST_FUNC) { - fprintf(stderr, "Cannot call variable\n"); + fprintf(stderr, "Cannot call non-function\n"); exit(1); } - if (node_list_length(node->child1) != sym->numparams) { - fprintf(stderr, "Invalid number of parameters to function '%s' (typechecker, come on)\n", node->name); + int actual_numparams = node_list_length(node->child1); + if (actual_numparams != sym->numparams) { + fprintf(stderr, "Invalid number of parameters to function '%s' (expected %d, got %d)\n", + node->name, sym->numparams, actual_numparams); exit(1); } struct node **args = malloc(sym->numparams * sizeof(struct node*)); write_node_list(args, node->child1); for (int i = sym->numparams - 1; i >= 0; i--) { - struct ref r = compile_expr(ir, symtab, args[i]); + struct ref r = compile_expr(ir, symtab, info, args[i]); + if (args[i]->valuetype != sym->params[i].type) { + fprintf(stderr, "Invalid argument type "); + type_print(args[i]->valuetype, stderr); + fprintf(stderr, "to parameter %d (type ", i + 1); + type_print(sym->params[i].type, stderr); + fprintf(stderr, ") of function '%s'\n", node->name); + exit(1); + } ir_append(ir, irins_make_1(INS_PUSH, r)); } free(args); @@ -216,6 +285,7 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node ir_append(ir, ins); } ir_append(ir, irins_make_012(INS_ADD, ref_reg(REG_SP), ref_reg(REG_SP), ref_imm(sym->numparams))); + node->valuetype = sym->rtype; return retref; } @@ -225,11 +295,11 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node } } -static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node) { +static void compile_node(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) { switch (node->type) { case N_LIST: - compile_node(ir, symtab, node->child1); - compile_node(ir, symtab, node->child2); + compile_node(ir, symtab, info, node->child1); + compile_node(ir, symtab, info, node->child2); break; case N_LIST_END: @@ -237,7 +307,7 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node case N_BLOCK: { symtab = symtab_sub(symtab); - compile_node(ir, symtab, node->child1); + compile_node(ir, symtab, info, node->child1); symtab = symtab_delete_get_parent(symtab); break; } @@ -248,13 +318,29 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node fprintf(stderr, "Redeclaration of variable '%s'\n", node->name); exit(1); } + struct ref vref; - if (node->type == N_VAR_DECL_INIT) vref = compile_expr(ir, symtab, node->child1); - else vref = ref_imm(0); + if (node->type == N_VAR_DECL_INIT) { + struct info info2 = info; + info2.name = node->name; + vref = compile_expr(ir, symtab, info2, node->child1); + if (node->child1->valuetype != node->rtype) { + fprintf(stderr, "Invalid type in initialisation of variable '%s' (expected ", node->name); + type_print(node->rtype, stderr); + fprintf(stderr, ", got "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, ")\n"); + exit(1); + } + } else { + vref = ref_imm(0); + } + struct symbol *sym = symbol_make_var(strdup(node->name), node->rtype); if (symtab->parent == NULL) sym->ref = ir_reserve_global(ir, 1); else sym->ref = ref_next_register(); symtab_insert(symtab, sym); + ir_append(ir, irins_make_01(INS_MOV, sym->ref, vref)); break; } @@ -282,7 +368,11 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node symtab_insert(symtab, parsym); } - compile_node(ir, symtab, node->child2); + struct info info2 = info; + info2.name = node->name; + info2.rettype = node->rtype; + + compile_node(ir, symtab, info2, node->child2); symtab = symtab_delete_get_parent(symtab); @@ -295,12 +385,12 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node const char *thenlbl = gen_label_name(); const char *elselbl = gen_label_name(); const char *afterlbl = gen_label_name(); - compile_boolexpr(ir, symtab, node->child1, thenlbl, elselbl); + compile_boolexpr(ir, symtab, info, node->child1, thenlbl, elselbl); ir_append(ir, irins_make_name(INS_LBL, thenlbl)); - compile_node(ir, symtab, node->child2); + compile_node(ir, symtab, info, node->child2); ir_append(ir, irins_make_name(INS_JMP, afterlbl)); ir_append(ir, irins_make_name(INS_LBL, elselbl)); - compile_node(ir, symtab, node->child3); + compile_node(ir, symtab, info, node->child3); ir_append(ir, irins_make_name(INS_LBL, afterlbl)); break; } @@ -310,39 +400,51 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node const char *bodylbl = gen_label_name(); const char *afterlbl = gen_label_name(); ir_append(ir, irins_make_name(INS_LBL, startlbl)); - compile_boolexpr(ir, symtab, node->child1, bodylbl, afterlbl); + compile_boolexpr(ir, symtab, info, node->child1, bodylbl, afterlbl); ir_append(ir, irins_make_name(INS_LBL, bodylbl)); - compile_node(ir, symtab, node->child2); + compile_node(ir, symtab, info, node->child2); ir_append(ir, irins_make_name(INS_JMP, startlbl)); ir_append(ir, irins_make_name(INS_LBL, afterlbl)); break; } case N_RETURN: + if (info.rettype != type_void()) { + fprintf(stderr, "Return without value from '%s'\n", info.name); + exit(1); + } ir_append(ir, irins_make(INS_RET)); break; case N_RETURNV: { - struct ref ref = compile_expr(ir, symtab, node->child1); + struct ref ref = compile_expr(ir, symtab, info, node->child1); + if (node->child1->valuetype != info.rettype) { + fprintf(stderr, "Return with type "); + type_print(node->child1->valuetype, stderr); + fprintf(stderr, " from function '%s' with return type ", info.name); + type_print(info.rettype, stderr); + fprintf(stderr, "\n"); + exit(1); + } ir_append(ir, irins_make_1(INS_RETV, ref)); break; } default: // hmm, maybe it's an expression statement? - compile_expr(ir, symtab, node); + compile_expr(ir, symtab, info, node); break; } } -static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct node *node) { +static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) { switch (node->type) { case N_LIST: if (node->child1->type == N_VAR_DECL || node->child1->type == N_VAR_DECL_INIT) { - compile_node(ir, symtab, node->child1); + compile_node(ir, symtab, info, node->child1); node_delete_recursive(node->child1); node->child1 = node_make_0(N_LIST_END); } - compile_data_setup_node(ir, symtab, node->child2); + compile_data_setup_node(ir, symtab, info, node->child2); break; case N_LIST_END: @@ -350,7 +452,7 @@ static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct case N_VAR_DECL: case N_VAR_DECL_INIT: - compile_node(ir, symtab, node); + compile_node(ir, symtab, info, node); break; case N_FUNC_DECL: @@ -361,13 +463,13 @@ static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct } } -static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct node *root) { +static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct info info, struct node *root) { struct ref flag = ir_reserve_global(ir, 1); ir_append(ir, irins_make_12(INS_TEST, flag, flag)); const char *afterlbl = gen_label_name(); ir_append(ir, irins_make_jcc(afterlbl, CCNZ)); - compile_data_setup_node(ir, symtab, root); + compile_data_setup_node(ir, symtab, info, root); ir_append(ir, irins_make_01(INS_MOV, flag, ref_imm(1))); ir_append(ir, irins_make_name(INS_LBL, afterlbl)); @@ -378,7 +480,12 @@ static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct node struct ir* compile(struct node *node) { struct ir *ir = ir_make(); struct symtab *symtab = symtab_make(); - compile_data_setup(ir, symtab, node); - compile_node(ir, symtab, node); + + struct info info; + info.name = "<<root>>"; + info.rettype = type_void(); + + compile_data_setup(ir, symtab, info, node); + compile_node(ir, symtab, info, node); return ir; } @@ -12,6 +12,8 @@ struct node* node_make_0(enum node_type type) { node->rtype = NULL; node->name = NULL; node->value = 0; + node->oper = -1; + node->valuetype = NULL; return node; } @@ -38,6 +38,8 @@ struct node { char *name; int value; enum operator oper; + + struct type *valuetype; // filled in by type checker }; |