summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c19
-rw-r--r--ast.h23
-rw-r--r--codegen.c167
-rw-r--r--parser.c87
-rw-r--r--test/manycalls.c24
-rw-r--r--test/math.c3
6 files changed, 284 insertions, 39 deletions
diff --git a/ast.c b/ast.c
index 7837cf8..7f97948 100644
--- a/ast.c
+++ b/ast.c
@@ -75,6 +75,19 @@ static void call_destroy(struct call_node* node) {
}
}
+static void unary_destroy(struct unary_node* node) {
+ expr_destroy(node->expr);
+ free(node->expr);
+}
+
+static void binary_destroy(struct binary_node* node) {
+ expr_destroy(node->lhs);
+ free(node->lhs);
+
+ expr_destroy(node->rhs);
+ free(node->rhs);
+}
+
static void expr_destroy(struct expr_node* node) {
switch (node->type) {
case EXPR_INT_LIT:
@@ -93,6 +106,12 @@ static void expr_destroy(struct expr_node* node) {
case EXPR_CALL:
call_destroy(&node->as._call);
break;
+ case EXPR_UNARY:
+ unary_destroy(&node->as._unary);
+ break;
+ case EXPR_BINARY:
+ binary_destroy(&node->as._binary);
+ break;
}
}
diff --git a/ast.h b/ast.h
index 2eabb02..4837c2b 100644
--- a/ast.h
+++ b/ast.h
@@ -52,7 +52,6 @@ struct lval_node {
} as;
};
-/* TODO: add to expression, parse */
struct assign_node {
struct lval_node lval;
struct expr_node* rval;
@@ -64,6 +63,24 @@ struct call_node {
struct expr_node* args_head;
};
+struct unary_node {
+ enum {
+ UNARY_NEG,
+ } op;
+ struct expr_node* expr;
+};
+
+struct binary_node {
+ enum {
+ BINARY_ADD,
+ BINARY_SUB,
+ BINARY_MUL,
+ BINARY_DIV,
+ } op;
+ struct expr_node* lhs;
+ struct expr_node* rhs;
+};
+
struct expr_node {
enum {
EXPR_INT_LIT,
@@ -73,6 +90,8 @@ struct expr_node {
EXPR_VAR_REF,
EXPR_ASSIGN,
EXPR_CALL,
+ EXPR_UNARY,
+ EXPR_BINARY,
} type;
union {
struct int_lit_node _int_lit;
@@ -82,6 +101,8 @@ struct expr_node {
struct var_ref_node _var_ref;
struct assign_node _assign;
struct call_node _call;
+ struct unary_node _unary;
+ struct binary_node _binary;
} as;
struct expr_node* next;
diff --git a/codegen.c b/codegen.c
index 216d41a..662097e 100644
--- a/codegen.c
+++ b/codegen.c
@@ -23,6 +23,11 @@ static const struct storage_location RV_LOC = {
.type = STO_REG,
.reg = &RAX,
};
+static const struct storage_location MULDIV_LOC = RV_LOC;
+static const struct storage_location MULDIV_OVERFLOW_LOC = {
+ .type = STO_REG,
+ .reg = &RDX,
+};
#define RETURN_LABEL_FMT "%s@coda"
#define FULL_REG_SZ 8
@@ -30,6 +35,42 @@ static const struct storage_location RV_LOC = {
static struct scope* scope;
static const struct fn_decl_node* active_fn;
+static struct lval_def allocate_register(unsigned long long sz) {
+ return (struct lval_def) {
+ .loc = {
+ .type = STO_REG,
+ .reg = &RAX, /* TODO: no real register coloring happening LOL */
+ },
+ .sz = sz,
+ };
+}
+
+static struct lval_def allocate_stack(FILE* outfile, unsigned long long sz) {
+ fprintf(outfile, "\tsub rsp, %llu\n", sz);
+ scope->bp_offset += sz;
+ return (struct lval_def) {
+ .loc = {
+ .type = STO_STACK,
+ .bp_offset = scope->bp_offset,
+ },
+ .sz = sz,
+ };
+}
+
+static struct lval_def allocate_temporary(
+ FILE* outfile,
+ unsigned long long sz
+) {
+ return allocate_stack(outfile, sz);
+}
+
+static void deallocate_temporary(FILE* outfile, const struct lval_def* tmp) {
+ if (tmp->loc.type == STO_STACK) {
+ fprintf(outfile, "\tadd rsp, %llu\n", tmp->sz);
+ scope->bp_offset -= tmp->sz;
+ }
+}
+
static void emit_storage_loc(
FILE* outfile,
const struct storage_location* loc,
@@ -83,6 +124,13 @@ static bool locs_equal(
CGEN_PANIC("unhandled storage type case");
}
+static void emit_size_const(FILE* outfile, unsigned long long sz) {
+ if (sz > 4) fprintf(outfile, "qword ");
+ else if (sz > 2) fprintf(outfile, "dword ");
+ else if (sz > 1) fprintf(outfile, "word ");
+ else fprintf(outfile, "byte ");
+}
+
static void emit_mov(
FILE* outfile,
const struct lval_def* dst,
@@ -107,25 +155,14 @@ static void emit_mov(
case STO_STACK:
if (src->type == STO_STACK) {
/* `mov mem, mem` is illegal in x86_64 */
- emit_mov(
- outfile,
- &(struct lval_def) {
- .loc = RV_LOC,
- .sz = dst->sz,
- },
- src);
- emit_mov(outfile, dst, &RV_LOC);
+ struct lval_def tmp = allocate_register(dst->sz);
+ emit_mov(outfile, &tmp, src);
+ emit_mov(outfile, dst, &tmp.loc);
return;
}
fprintf(outfile, "\tmov ");
- if (src->type == STO_IMM) {
- /* must specify size to move immediates into memory*/
- if (dst->sz > 4) fprintf(outfile, "qword ");
- else if (dst->sz > 2) fprintf(outfile, "dword ");
- else if (dst->sz > 1) fprintf(outfile, "word ");
- else fprintf(outfile, "byte ");
- }
+ if (src->type == STO_IMM) emit_size_const(outfile, dst->sz);
emit_storage_loc(outfile, &dst->loc, dst->sz);
fprintf(outfile, ", ");
@@ -148,20 +185,11 @@ static unsigned long long get_type_size(const struct type_node* type) {
return type->def.sz;
}
-static struct lval_def make_stack_lval(
+static inline struct lval_def make_stack_lval(
FILE* outfile,
const struct type_node* type
) {
- unsigned long long type_sz = get_type_size(type);
- fprintf(outfile, "\tsub rsp, %llu\n", type_sz);
- scope->bp_offset += type_sz;
- return (struct lval_def) {
- .loc = {
- .type = STO_STACK,
- .bp_offset = scope->bp_offset,
- },
- .sz = type_sz,
- };
+ return allocate_stack(outfile, get_type_size(type));
}
static void emit_expr(
@@ -336,7 +364,7 @@ static void emit_call(
emit_mov(outfile, dst, &RV_LOC);
}
- /* pop our argument temporaries off the stack */
+ /* mass-pop our argument temporaries off the stack */
scope->bp_offset = orig_bp_offset;
if (orig_bp_offset > 0)
fprintf(outfile, "\tlea rsp, [rbp - %llu]\n", orig_bp_offset);
@@ -344,6 +372,85 @@ static void emit_call(
fprintf(outfile, "\tmov rsp, rbp\n");
}
+static void emit_unary(
+ FILE* outfile,
+ const struct unary_node* node,
+ const struct lval_def* dst
+) {
+ emit_expr(outfile, node->expr, dst);
+ if (dst == NULL) return;
+
+ switch (node->op) {
+ case UNARY_NEG:
+ fprintf(outfile, "\tneg ");
+ if (dst->loc.type == STO_STACK) emit_size_const(outfile, dst->sz);
+ emit_storage_loc(outfile, &dst->loc, dst->sz);
+ fprintf(outfile, "\n");
+ break;
+ }
+}
+
+/* TODO: tighten up/enforce with regard to up-casting smaller operands */
+static void emit_binary(
+ FILE* outfile,
+ const struct binary_node* node,
+ const struct lval_def* dst
+) {
+ if (dst == NULL) {
+ emit_expr(outfile, node->lhs, NULL);
+ emit_expr(outfile, node->rhs, NULL);
+ return;
+ }
+
+ /* LHS goes in RAX explicitly because imul and idiv are weird */
+ struct lval_def rhs_dst = allocate_temporary(outfile, dst->sz);
+ emit_expr(outfile, node->rhs, &rhs_dst);
+ struct lval_def lhs_dst = (struct lval_def) {
+ .loc = MULDIV_LOC,
+ .sz = dst->sz,
+ };
+ emit_expr(outfile, node->lhs, &lhs_dst);
+
+ switch (node->op) {
+ case BINARY_ADD:
+ fprintf(outfile, "\tadd ");
+ emit_storage_loc(outfile, &lhs_dst.loc, dst->sz);
+ fprintf(outfile, ", ");
+ break;
+ case BINARY_SUB:
+ fprintf(outfile, "\tsub ");
+ emit_storage_loc(outfile, &lhs_dst.loc, dst->sz);
+ fprintf(outfile, ", ");
+ break;
+ case BINARY_MUL:
+ fprintf(outfile, "\timul ");
+ if (rhs_dst.loc.type == STO_STACK)
+ emit_size_const(outfile, dst->sz);
+ break;
+ case BINARY_DIV:
+ /* nothing in the top half reg */
+ fprintf(outfile, "\txor ");
+ emit_storage_loc(outfile, &MULDIV_OVERFLOW_LOC, FULL_REG_SZ);
+ fprintf(outfile, ", ");
+ emit_storage_loc(outfile, &MULDIV_OVERFLOW_LOC, FULL_REG_SZ);
+ fprintf(outfile, "\n");
+
+ fprintf(outfile, "\tidiv ");
+ if (rhs_dst.loc.type == STO_STACK)
+ emit_size_const(outfile, dst->sz);
+ break;
+ }
+
+ emit_storage_loc(outfile, &rhs_dst.loc, dst->sz);
+ fprintf(outfile, "\n");
+
+ /* TODO: deal with RDX overflow shit for imul and idiv */
+ emit_mov(outfile, dst, &lhs_dst.loc);
+
+ deallocate_temporary(outfile, &lhs_dst);
+ deallocate_temporary(outfile, &rhs_dst);
+}
+
static void emit_expr(
FILE* outfile,
const struct expr_node* node,
@@ -371,6 +478,12 @@ static void emit_expr(
case EXPR_CALL:
emit_call(outfile, &node->as._call, dst);
break;
+ case EXPR_UNARY:
+ emit_unary(outfile, &node->as._unary, dst);
+ break;
+ case EXPR_BINARY:
+ emit_binary(outfile, &node->as._binary, dst);
+ break;
}
}
diff --git a/parser.c b/parser.c
index f9c7047..6a8ad31 100644
--- a/parser.c
+++ b/parser.c
@@ -107,21 +107,18 @@ static void parse_var_ref(struct var_ref_node* p_node) {
p_node->ident = tok.data.ident;
}
-static void expr_to_lval(struct lval_node* l_node, struct expr_node* e_node) {
- switch (e_node->type) {
+static void parse_expr_assign(struct expr_node* p_node) {
+ switch (p_node->type) {
case EXPR_VAR_REF:
- *l_node = (struct lval_node) {
+ p_node->as._assign.lval = (struct lval_node) {
.type = LVAL_VAR_REF,
- .as._var_ref = e_node->as._var_ref,
+ .as._var_ref = p_node->as._var_ref,
};
return;
default:
PARSER_PANIC("expression is not assignable");
}
-}
-static void parse_expr_assign(struct expr_node* p_node) {
- expr_to_lval(&p_node->as._assign.lval, p_node);
p_node->type = EXPR_ASSIGN;
p_node->as._assign.rval = protected_alloc(sizeof(struct expr_node));
@@ -167,6 +164,59 @@ static void parse_expr_call(struct expr_node* p_node) {
expect(TK_RPAREN);
}
+static void parse_unary(struct unary_node* p_node) {
+ peek_or_panic();
+ switch (tok.type) {
+ case TK_NEG:
+ expect(TK_NEG);
+ p_node->op = UNARY_NEG;
+ break;
+ default:
+ PARSER_PANIC("expected unary operator");
+ }
+
+ p_node->expr = protected_alloc(sizeof(struct expr_node));
+ parse_expr(p_node->expr);
+}
+
+static void parse_expr_binary(struct expr_node* p_node) {
+ struct expr_node* lhs = protected_alloc(sizeof(struct expr_node));
+ struct expr_node* rhs = protected_alloc(sizeof(struct expr_node));
+
+ *lhs = *p_node;
+ *p_node = (struct expr_node) {
+ .type = EXPR_BINARY,
+ .as._binary = {
+ .lhs = lhs,
+ .rhs = rhs,
+ },
+ };
+
+ peek_or_panic();
+ switch (tok.type) {
+ case TK_PLUS:
+ expect(TK_PLUS);
+ p_node->as._binary.op = BINARY_ADD;
+ break;
+ case TK_NEG:
+ expect(TK_NEG);
+ p_node->as._binary.op = BINARY_SUB;
+ break;
+ case TK_STAR:
+ expect(TK_STAR);
+ p_node->as._binary.op = BINARY_MUL;
+ break;
+ case TK_DIV:
+ expect(TK_DIV);
+ p_node->as._binary.op = BINARY_DIV;
+ break;
+ default:
+ PARSER_PANIC("expected binary operator");
+ }
+
+ parse_expr(rhs);
+}
+
static void parse_expr(struct expr_node* p_node) {
peek_or_panic();
switch (tok.type) {
@@ -175,6 +225,10 @@ static void parse_expr(struct expr_node* p_node) {
parse_expr(p_node);
expect(TK_RPAREN);
break;
+ case TK_NEG:
+ p_node->type = EXPR_UNARY;
+ parse_unary(&p_node->as._unary);
+ break;
case TK_INT_LIT:
case TK_CHAR_LIT:
case TK_FLOAT_LIT:
@@ -190,10 +244,21 @@ static void parse_expr(struct expr_node* p_node) {
}
peek_or_panic();
- if (tok.type == TK_ASSIGN)
- parse_expr_assign(p_node);
- else if (tok.type == TK_LPAREN)
- parse_expr_call(p_node);
+ switch (tok.type) {
+ case TK_ASSIGN:
+ parse_expr_assign(p_node);
+ break;
+ case TK_LPAREN:
+ parse_expr_call(p_node);
+ break;
+ case TK_PLUS:
+ case TK_NEG:
+ case TK_STAR:
+ case TK_DIV:
+ parse_expr_binary(p_node);
+ break;
+ default:
+ }
}
static void parse_var_decl(struct var_decl_node* p_node) {
diff --git a/test/manycalls.c b/test/manycalls.c
new file mode 100644
index 0000000..bfcc775
--- /dev/null
+++ b/test/manycalls.c
@@ -0,0 +1,24 @@
+int one() {
+ return 1;
+}
+
+int two() {
+ return 2;
+}
+
+int three() {
+ return 3;
+}
+
+int four() {
+ return 4;
+}
+
+int main() {
+ int d;
+ int a = one();
+ int b = two();
+ int c = three();
+ d = four();
+ return d;
+}
diff --git a/test/math.c b/test/math.c
new file mode 100644
index 0000000..2131546
--- /dev/null
+++ b/test/math.c
@@ -0,0 +1,3 @@
+int main(int argc, char** argv) {
+ return argc * -5;
+}