summaryrefslogtreecommitdiff
path: root/codegen.c
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-03-31 16:30:29 -1000
committerCarson Fleming <cflems@cflems.net>2026-03-31 16:30:29 -1000
commit1dfa2971999b4adf36a33866b0b07af07188da08 (patch)
tree7d65b9a5780a95bf519fed6d435bdade264aac62 /codegen.c
parent7cf2065be92855b5b1db31a4bb7afbb4af29a817 (diff)
downloadccc-1dfa2971999b4adf36a33866b0b07af07188da08.tar.gz
math n shi
Diffstat (limited to 'codegen.c')
-rw-r--r--codegen.c167
1 files changed, 140 insertions, 27 deletions
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;
}
}