diff options
| -rw-r--r-- | ast.c | 5 | ||||
| -rw-r--r-- | ast.h | 2 | ||||
| -rw-r--r-- | codegen.c | 108 | ||||
| -rw-r--r-- | parser.c | 23 | ||||
| -rw-r--r-- | test/hello-world.c | 6 | ||||
| -rw-r--r-- | type_checker.c | 4 |
6 files changed, 122 insertions, 26 deletions
@@ -65,7 +65,10 @@ static void fn_decl_destroy(struct fn_decl_node* node) { free(node->args); } - group_destroy(&node->body); + if (node->body != NULL) { + group_destroy(node->body); + free(node->body); + } scope_destroy(node->scope); free(node->scope); @@ -129,7 +129,7 @@ struct fn_decl_node { struct type return_type; const char* name; struct arg_decl_node* args; - struct group_node body; + struct group_node* body; struct scope* scope; }; @@ -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"); @@ -576,14 +576,14 @@ static void parse_fn_decl(struct fn_decl_node* p_node) { expect(TK_IDENT); - if (scope_define_var(scope, (struct var_def) { - .name = tok.data.ident, - .loc = { - .type = STO_FN, - .decl = p_node, - }, - }) == NULL) - PARSER_PANIC("redefinition of '%s'", tok.data.ident) + /* redefinition is allowed */ + scope_define_var(scope, (struct var_def) { + .name = tok.data.ident, + .loc = { + .type = STO_FN, + .decl = p_node, + }, + }); p_node->name = tok.data.ident; @@ -600,7 +600,12 @@ static void parse_fn_decl(struct fn_decl_node* p_node) { expect(TK_RPAREN); - parse_group(&p_node->body); + peek_or_panic(); + if (tok.type == TK_SEMI) expect(TK_SEMI); + else { + p_node->body = ccc_alloc(sizeof(struct group_node)); + parse_group(p_node->body); + } scope_pop(&scope); } diff --git a/test/hello-world.c b/test/hello-world.c new file mode 100644 index 0000000..ec2e87c --- /dev/null +++ b/test/hello-world.c @@ -0,0 +1,6 @@ +void printf(char* str); + +int main (int argc, char** argv) { + printf("hello world\n"); + return 0; +} diff --git a/type_checker.c b/type_checker.c index 0aaa584..00f57d0 100644 --- a/type_checker.c +++ b/type_checker.c @@ -281,6 +281,8 @@ static void type_check_group(struct group_node* node) { exit_scope(node->scope); } +/* TODO: type check redefinition arguments against each other */ +/* TODO: wire the body to the definition either here or in the parser */ static void type_check_fn_decl(struct fn_decl_node* node) { enter_scope(node->scope); @@ -289,7 +291,7 @@ static void type_check_fn_decl(struct fn_decl_node* node) { arg_decl = arg_decl->next) arg_decl->def_ref->type = &arg_decl->type; - type_check_group(&node->body); + if (node->body != NULL) type_check_group(node->body); exit_scope(node->scope); } |
