diff options
Diffstat (limited to 'codegen.c')
| -rw-r--r-- | codegen.c | 108 |
1 files changed, 94 insertions, 14 deletions
@@ -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"); |
