aboutsummaryrefslogtreecommitdiff
path: root/compiler.c
diff options
context:
space:
mode:
Diffstat (limited to 'compiler.c')
-rw-r--r--compiler.c279
1 files changed, 279 insertions, 0 deletions
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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#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;
+}