From 9911f9a73c7dc46069199e52f2bc54082d10366c Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Wed, 3 Jan 2018 23:10:59 +0100 Subject: Initial --- compiler.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 compiler.c (limited to 'compiler.c') diff --git a/compiler.c b/compiler.c new file mode 100644 index 0000000..42ad25c --- /dev/null +++ b/compiler.c @@ -0,0 +1,279 @@ +#include +#include +#include +#include "compiler.h" +#include "symtab.h" + + +static void write_func_params(struct pair_tn *params, struct node *list) { + if (list->type == N_LIST_END) return; + assert(list->child1->type == N_VAR_DECL); + params[0].type = list->child1->rtype; + params[0].name = strdup(list->child1->name); + write_func_params(params + 1, list->child2); +} + +static bool oper_is_basic_arith(enum operator oper) { + return oper == OP_ADD || oper == OP_SUB || oper == OP_MUL || oper == OP_DIV || oper == OP_MOD; +} + +static bool oper_is_comparison(enum operator oper) { + return oper == OP_EQ || oper == OP_NEQ || + oper == OP_LT || oper == OP_GT || + oper == OP_LEQ || oper == OP_GEQ; +} + +static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node *node); + +static void compile_boolexpr( + struct ir *ir, struct symtab *symtab, struct node *node, char *truedest, 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); + ir_append(ir, irins_make_12(INS_CMP, r1, r2)); + enum condcode condcode; + switch (node->oper) { + case OP_EQ: condcode = CCNZ; break; + case OP_NEQ: condcode = CCZ; break; + case OP_LT: condcode = CCGE; break; + case OP_GT: condcode = CCLE; break; + case OP_LEQ: condcode = CCG; break; + case OP_GEQ: condcode = CCL; break; + default: assert(false); + } + ir_append(ir, irins_make_jcc(falsedest, condcode)); + ir_append(ir, irins_make_name(INS_JMP, truedest)); + } else if (node->type == N_BINOP && node->oper == OP_AND) { + char *try2lbl = gen_label_name(); + compile_boolexpr(ir, symtab, node->child1, try2lbl, falsedest); + ir_append(ir, irins_make_name(INS_LBL, try2lbl)); + compile_boolexpr(ir, symtab, node->child2, truedest, falsedest); + } else if (node->type == N_BINOP && node->oper == OP_OR) { + char *try2lbl = gen_label_name(); + compile_boolexpr(ir, symtab, node->child1, truedest, try2lbl); + ir_append(ir, irins_make_name(INS_LBL, try2lbl)); + compile_boolexpr(ir, symtab, node->child2, truedest, falsedest); + } else if (node->type == N_UNOP && node->oper == OP_NOT) { + compile_boolexpr(ir, symtab, node->child1, falsedest, truedest); + } else { + struct ref ref = compile_expr(ir, symtab, 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) { + 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))); + return ref; + } + + case N_VAR: { + struct symbol *sym = symtab_find(symtab, node->name); + if (!sym) { + fprintf(stderr, "Use of undeclared variable '%s'\n", node->name); + exit(1); + } + 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); + enum instype instype; + switch (node->oper) { + case OP_ADD: instype = INS_ADD; break; + case OP_SUB: instype = INS_SUB; break; + case OP_MUL: instype = INS_MUL; break; + case OP_DIV: instype = INS_DIV; break; + case OP_MOD: instype = INS_MOD; break; + default: assert(false); + } + ir_append(ir, irins_make_012(instype, r0, r1, r2)); + return r0; + } else if (oper_is_comparison(node->oper) || node->oper == OP_AND || node->oper == OP_OR) { + char *truelbl = gen_label_name(); + char *falselbl = gen_label_name(); + char *afterlbl = gen_label_name(); + compile_boolexpr(ir, symtab, 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))); + ir_append(ir, irins_make_name(INS_JMP, afterlbl)); + ir_append(ir, irins_make_name(INS_LBL, falselbl)); + ir_append(ir, irins_make_01(INS_MOV, cval, ref_imm(0))); + ir_append(ir, irins_make_name(INS_LBL, afterlbl)); + return cval; + } 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); + ir_append(ir, irins_make_01(INS_MOV, r0, r1)); + return r0; + } else { + fprintf(stderr, "Invalid left-hand side in assignment.\n"); + exit(1); + } + } else { + assert(false); + } + } + + case N_UNOP: { + switch (node->oper) { + case OP_NEG: { + struct ref r0 = ref_next_register(); + struct ref r1 = compile_expr(ir, symtab, node->child1); + ir_append(ir, irins_make_01(INS_NEG, r0, r1)); + return r0; + } + + case OP_NOT: { + struct ref r0 = ref_next_register(); + struct ref r1 = compile_expr(ir, symtab, node->child1); + ir_append(ir, irins_make_01(INS_NOT, r0, r1)); + return r0; + } + + case OP_DEREF: { + struct ref addr = compile_expr(ir, symtab, node->child1); + struct ref r1; + switch (addr.type) { + case REF_REG: r1 = ref_mem(addr.reg, 0, false); break; + case REF_IMM: r1 = ref_mem(-1, addr.imm, false); break; + case REF_MEM: { + r1 = ref_next_register(); + ir_append(ir, irins_make_01(INS_MOV, r1, addr)); + r1 = ref_mem(r1.reg, 0, false); break; + break; + } + } + struct ref r0 = ref_next_register(); + ir_append(ir, irins_make_01(INS_MOV, r0, r1)); + return r0; + } + + case OP_ADDROF: + fprintf(stderr, "OP_ADDROF not implemented\n"); + exit(1); + + default: + assert(false); + } + } + + case N_CALL: { + fprintf(stderr, "function calls not implemented\n"); + exit(1); + /*struct symbol *sym = symtab_find(symtab, node->name); + if (!sym) { + fprintf(stderr, "Call of undeclared function '%s'\n", node->name); + exit(1); + } + ;*/ + } + + default: + fprintf(stderr, "Invalid node type %d in expression\n", node->type); + exit(1); + } +} + +static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node) { + switch (node->type) { + case N_LIST: + compile_node(ir, symtab, node->child1); + compile_node(ir, symtab, node->child2); + break; + + case N_LIST_END: + break; + + case N_BLOCK: { + symtab = symtab_sub(symtab); + compile_node(ir, symtab, node->child1); + symtab = symtab_delete_get_parent(symtab); + break; + } + + case N_VAR_DECL_INIT: + case N_VAR_DECL: { + if (symtab_find_local(symtab, node->name)) { + fprintf(stderr, "Redeclaration of variable '%s'\n", node->name); + exit(1); + } + struct symbol *sym = symbol_make_var(node->name, node->rtype); + sym->ref = ref_next_register(); + symtab_insert(symtab, sym); + ir_append(ir, irins_make_01(INS_MOV, sym->ref, ref_imm(node->value))); + break; + } + + case N_FUNC_DECL: { + if (symtab_find(symtab, node->name)) { + fprintf(stderr, "Redeclaration of function '%s'\n", node->name); + exit(1); + } + int numparams = node_list_length(node->child1); + struct symbol *sym = symbol_make_func(node->name, node->rtype, numparams); + write_func_params(sym->params, node->child1); + symtab_insert(symtab_root(symtab), sym); + ir_append(ir, irins_make_name(INS_LBL, strdup(node->name))); + symtab = symtab_sub(symtab); + compile_node(ir, symtab, node->child2); + symtab = symtab_delete_get_parent(symtab); + break; + } + + case N_IF: { + char *elselbl = gen_label_name(); + char *afterlbl = gen_label_name(); + struct ref condref = compile_expr(ir, symtab, node->child1); + ir_append(ir, irins_make_12(INS_TEST, condref, condref)); + ir_append(ir, irins_make_jcc(elselbl, CCZ)); + compile_node(ir, symtab, 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); + ir_append(ir, irins_make_name(INS_LBL, afterlbl)); + break; + } + + case N_WHILE: { + char *startlbl = gen_label_name(); + char *afterlbl = gen_label_name(); + ir_append(ir, irins_make_name(INS_LBL, startlbl)); + struct ref condref = compile_expr(ir, symtab, node->child1); + ir_append(ir, irins_make_12(INS_TEST, condref, condref)); + ir_append(ir, irins_make_jcc(afterlbl, CCZ)); + compile_node(ir, symtab, node->child2); + ir_append(ir, irins_make_name(INS_JMP, startlbl)); + ir_append(ir, irins_make_name(INS_LBL, afterlbl)); + break; + } + + case N_RETURN: { + struct ref ref = compile_expr(ir, symtab, node->child1); + ir_append(ir, irins_make_1(INS_RETV, ref)); + break; + } + + default: // hmm, maybe it's an expression statement? + compile_expr(ir, symtab, node); + break; + } +} + +struct ir* compile(struct node *node) { + struct ir *ir = ir_make(); + struct symtab *symtab = symtab_make(); + compile_node(ir, symtab, node); + return ir; +} -- cgit v1.2.3-54-g00ecf