summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c5
-rw-r--r--ast.h2
-rw-r--r--codegen.c108
-rw-r--r--parser.c23
-rw-r--r--test/hello-world.c6
-rw-r--r--type_checker.c4
6 files changed, 122 insertions, 26 deletions
diff --git a/ast.c b/ast.c
index 9fa4f82..c491c42 100644
--- a/ast.c
+++ b/ast.c
@@ -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);
diff --git a/ast.h b/ast.h
index 8611aaa..af35d45 100644
--- a/ast.h
+++ b/ast.h
@@ -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;
};
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");
diff --git a/parser.c b/parser.c
index bdf266d..c4fa8b7 100644
--- a/parser.c
+++ b/parser.c
@@ -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);
}