summaryrefslogtreecommitdiff
path: root/codegen.c
diff options
context:
space:
mode:
Diffstat (limited to 'codegen.c')
-rw-r--r--codegen.c108
1 files changed, 94 insertions, 14 deletions
diff --git a/codegen.c b/codegen.c
index bca0411..71350e4 100644
--- a/codegen.c
+++ b/codegen.c
@@ -28,6 +28,7 @@ static struct reg* MULDIV_OVERFLOW_REG = &RDX;
#define RETURN_LABEL_FMT "%s@ret"
#define FULL_REG_SZ 8
#define WORD_SZ 2
+#define STACK_ALIGNMENT 16
static struct hash_map dseg;
static struct scope* scope;
@@ -206,10 +207,70 @@ static void emit_size_const(FILE* outfile, integral_t sz) {
static void emit_mov(
FILE* outfile,
const struct lval_def* dst,
+ const struct storage_location* src);
+
+static void emit_label_lea(
+ FILE* outfile,
+ const struct lval_def* dst,
+ const char* label
+) {
+ integral_t dst_sz = get_effective_data_type(dst->type)->sz;
+ if (dst_sz < FULL_REG_SZ)
+ CGEN_PANIC("can't load label address into an undersized type");
+
+ switch (dst->loc.type) {
+ case STO_REG:
+ fprintf(outfile, "\tlea ");
+ emit_storage_loc(outfile, &dst->loc, FULL_REG_SZ);
+ fprintf(outfile, ", [rel %s]\n", label);
+ return;
+ case STO_STACK:
+ struct reg* tmp_reg = allocate_register();
+ bool spill_reg = tmp_reg == NULL;
+ if (spill_reg) {
+ tmp_reg = &RBX;
+ spill_register(outfile, tmp_reg);
+ }
+
+ emit_label_lea(
+ outfile,
+ &(struct lval_def) {
+ .type = dst->type,
+ .loc = {
+ .type = STO_REG,
+ .reg = tmp_reg,
+ }
+ },
+ label);
+ emit_mov(
+ outfile,
+ dst,
+ &(struct storage_location) {
+ .type = STO_REG,
+ .reg = tmp_reg,
+ });
+
+ if (spill_reg) unspill_register(outfile, tmp_reg);
+ else release_register(tmp_reg);
+ return;
+ case STO_FN:
+ case STO_IMM:
+ case STO_LABEL:
+ case STO_UNRESOLVED:
+ CGEN_PANIC("can't load label address into non-value storage");
+ }
+}
+
+static void emit_mov(
+ FILE* outfile,
+ const struct lval_def* dst,
const struct storage_location* src
) {
/* first optimization: if dst == src, emit nothing */
if (locs_equal(&dst->loc, src)) return;
+ /* use lea's on label sources to support relative addressing */
+ if (src->type == STO_LABEL)
+ return emit_label_lea(outfile, dst, src->label);
integral_t dst_sz = get_effective_data_type(dst->type)->sz;
switch (dst->loc.type) {
@@ -242,8 +303,8 @@ static void emit_mov(
struct reg* tmp_reg = allocate_register();
bool spill_reg = tmp_reg == NULL;
if (spill_reg) {
- spill_register(outfile, &RCX);
tmp_reg = &RCX;
+ spill_register(outfile, tmp_reg);
}
struct lval_def tmp = {
@@ -256,7 +317,7 @@ static void emit_mov(
emit_mov(outfile, &tmp, src);
emit_mov(outfile, dst, &tmp.loc);
- if (spill_reg) unspill_register(outfile, &RCX);
+ if (spill_reg) unspill_register(outfile, tmp_reg);
else release_register(tmp_reg);
return;
}
@@ -417,6 +478,7 @@ static void emit_assignment(
if (dst != NULL) emit_mov(outfile, dst, &lval_def.loc);
}
+/* TODO: could eventually allocate all space ahead of time instead of repeated sub's */
static integral_t push_stack_args(FILE* outfile, struct expr_list_node* arg) {
if (arg == NULL) return 0;
@@ -435,8 +497,9 @@ static void emit_call(
const struct call_node* node,
const struct lval_def* dst
) {
- /* 1. spill all existing data registers besides dst */
- const struct reg* dst_reg = dst->loc.type == STO_REG ? dst->loc.reg : NULL;
+ /* 1. spill all existing data registers besides dst if applicable */
+ const struct reg* dst_reg = (dst != NULL && dst->loc.type == STO_REG)
+ ? dst->loc.reg : NULL;
integral_t reg_occupied = 0, n_data_regs = 0;
for (; DATA_REGS[n_data_regs] != NULL; n_data_regs++) {
if (!DATA_REGS[n_data_regs]->is_occupied) continue;
@@ -461,19 +524,30 @@ static void emit_call(
});
}
integral_t arg_stack_space = push_stack_args(outfile, arg);
+ integral_t stack_misalignment =
+ STACK_ALIGNMENT - (scope->bp_offset % STACK_ALIGNMENT);
+ if (stack_misalignment > 0) {
+ fprintf(outfile, "\tsub rsp, 0x%llx\n", stack_misalignment);
+ arg_stack_space += stack_misalignment;
+ }
/* 3. `call <label>` */
- fprintf(outfile, "\tcall %s\n", node->fn_ref->name);
+ fprintf(outfile, "\tcall %s", node->fn_ref->name);
+ // TODO: this is kinda fukt we should probably keep an
+ // internal/external flag in the storage loc instead
+ if (node->fn_ref->body == NULL) fprintf(outfile, " WRT ..plt");
+ fprintf(outfile, "\n");
/* 4. `mov dst, rax` */
- emit_mov(outfile, dst, &(struct storage_location) {
- .type = STO_REG,
- .reg = &RAX,
- });
+ if (dst != NULL)
+ emit_mov(outfile, dst, &(struct storage_location) {
+ .type = STO_REG,
+ .reg = &RAX,
+ });
/* 5. destroy stack-based arg holders */
if (arg_stack_space > 0)
- fprintf(outfile, "\tadd rsp, %llu\n", arg_stack_space);
+ fprintf(outfile, "\tadd rsp, 0x%llx\n", arg_stack_space);
/* 6. unspill all data registers in reverse order */
for (integral_t i = n_data_regs; i > 0; i--) {
@@ -775,6 +849,8 @@ static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
}
static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
+ if (node->body == NULL) return;
+
if (active_fn != NULL)
CGEN_PANIC(
"can't define function %s inside function %s",
@@ -815,9 +891,9 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
emit_mov(outfile, &arg_dst, &arg_src);
}
- enter_scope(node->body.scope, scope->bp_offset);
- emit_group_contents(outfile, &node->body);
- exit_scope(node->body.scope, true);
+ enter_scope(node->body->scope, scope->bp_offset);
+ emit_group_contents(outfile, node->body);
+ exit_scope(node->body->scope, true);
fprintf(outfile, RETURN_LABEL_FMT ":\n", node->name);
fprintf(outfile, "\tmov rsp, rbp\n");
@@ -847,7 +923,11 @@ void emit_code(struct ast* ast, const char* path) {
const struct root_node* node = ast->root_node;
for (; node != NULL; node = node->next) {
if (node->type != ROOT_FN_DECL) continue;
- fprintf(outfile, "global %s\n", node->inner.fn_decl.name);
+ const struct fn_decl_node* fn = &node->inner.fn_decl;
+ if (fn->body != NULL)
+ fprintf(outfile, "global %s\n", fn->name);
+ else
+ fprintf(outfile, "extern %s\n", fn->name);
}
fprintf(outfile, "\n");