diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | codegen.c | 139 | ||||
| -rw-r--r-- | codegen.h | 8 | ||||
| -rw-r--r-- | main.c | 6 | ||||
| -rw-r--r-- | scope.c | 42 | ||||
| -rw-r--r-- | scope.h | 46 | ||||
| -rw-r--r-- | test/simple.c | 2 |
7 files changed, 225 insertions, 19 deletions
@@ -1,4 +1,5 @@ ccc +*.s *.o *.out build/** @@ -1,42 +1,95 @@ #include "ccc.h" -#include "ast.h" +#include "codegen.h" +#include "scope.h" #include <stdlib.h> #include <stdio.h> -static void emit_stmt(FILE* outfile, const struct stmt_node* node); -static void emit_expr( +#define CGEN_PANIC(format, ...) {\ + fprintf(\ + stderr,\ + "ccc: code gen error: " format "\n" __VA_OPT__(,)\ + __VA_ARGS__);\ + exit(1);\ +} + +static const struct storage_location RV_LOC = { + .type = REGISTER, + .label = "rax", +}; + +static struct scope* scope; + +static void emit_storage_loc( FILE* outfile, - const struct expr_node* node, - const char* reg); + const struct storage_location* loc +) { + switch (loc->type) { + case REGISTER: + case JMP_LABEL: + fprintf(outfile, "%s", loc->label); + break; + case BP_OFFSET: + if (loc->offset < 0) + fprintf(outfile, "[rbp + %lld]", -loc->offset); + else if (loc->offset > 0) + fprintf(outfile, "[rbp - %lld]", loc->offset); + else + fprintf(outfile, "[rbp]"); + } +} static void emit_int_lit( FILE* outfile, const struct int_lit_node* node, - const char* reg + const struct storage_location* storage ) { - fprintf(outfile, "\tmov %s, %lld\n", reg, node->val); + if (storage != NULL) { + fprintf(outfile, "\tmov "); + emit_storage_loc(outfile, storage); + fprintf(outfile, ", %lld\n", node->val); + } } static void emit_expr( FILE* outfile, const struct expr_node* node, - const char* reg + const struct storage_location* storage ) { switch (node->type) { case EXPR_EMPTY: break; case EXPR_INT_LIT: - emit_int_lit(outfile, &node->as._int_lit, reg); + emit_int_lit(outfile, &node->as._int_lit, storage); break; } } +static void emit_stmt(FILE* outfile, const struct stmt_node* node); + +static struct type_def get_type_def(const char* type_name) { + struct type_def type_def; + if (!scope_get_type(scope, &type_def, type_name)) + CGEN_PANIC("size of type %s is not known", type_name); + return type_def; +} + static void emit_var_decl(FILE* outfile, const struct var_decl_node* node) { - /* TODO: make do smth */ + struct type_def type_def = get_type_def(node->type.type); + fprintf(outfile, "\tsub rsp, %llu\n", type_def.size); + scope->bp_offset += type_def.size; + scope_define_var(scope, (struct var_def) { + .name = node->ident, + .loc = { + .type = BP_OFFSET, + .offset = scope->bp_offset, + }, + }); } static void emit_return(FILE* outfile, const struct return_node* node) { - if (node->ret_val != NULL) emit_expr(outfile, node->ret_val, "rax"); + if (node->ret_val != NULL) emit_expr(outfile, node->ret_val, &RV_LOC); + fprintf(outfile, "\tmov rsp, rbp\n"); + fprintf(outfile, "\tpop rbp\n"); fprintf(outfile, "\tret\n"); } @@ -48,6 +101,25 @@ static void emit_group(FILE* outfile, const struct group_node* node) { } } +static void emit_stmt_group(FILE* outfile, const struct group_node* node) { + scope_push(&scope); + scope->bp_offset = scope->next_out->bp_offset; /* don't reset bp */ + + emit_group(outfile, node); + + scope_pop(&scope); + if (scope->bp_offset == 0) + fprintf(outfile, "\tmov rsp, rbp\n"); + else { + fprintf(outfile, "\tlea rsp, "); + emit_storage_loc(outfile, &(struct storage_location) { + .type = BP_OFFSET, + .offset = scope->bp_offset, + }); + fprintf(outfile, "\n"); + } +} + static void emit_stmt(FILE* outfile, const struct stmt_node* node) { switch (node->type) { case STMT_VAR_DECL: @@ -60,15 +132,41 @@ static void emit_stmt(FILE* outfile, const struct stmt_node* node) { emit_expr(outfile, &node->as._expr, NULL); break; case STMT_GROUP: - emit_group(outfile, &node->as._group); + emit_stmt_group(outfile, &node->as._group); break; } } static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) { fprintf(outfile, "%s:\n", node->name); - /* TODO: probably something to map the args to temporaries lol */ + fprintf(outfile, "\tpush rbp\n"); + fprintf(outfile, "\tmov rbp, rsp\n"); + + scope_push(&scope); + scope->bp_offset = 0; + + struct var_decl_node* args_node = node->args_head; + long long arg_bp_offset = -8; + while (args_node != NULL) { + struct type_def type_def = get_type_def(args_node->type.type); + scope_define_var(scope, (struct var_def) { + .name = args_node->ident, + .loc = { + .type = BP_OFFSET, + .offset = arg_bp_offset, + }, + }); + arg_bp_offset -= type_def.size; + args_node = args_node->next; + } + emit_group(outfile, &node->body); + + scope_pop(&scope); + + fprintf(outfile, "\tmov rsp, rbp\n"); + fprintf(outfile, "\tpop rbp\n"); + fprintf(outfile, "\tret\n"); } static void emit_root_node(FILE* outfile, const struct root_node* node) { @@ -84,12 +182,22 @@ void emit_code(const struct root_node* ast, const char* path) { if (outfile == NULL) CCC_PANIC; fprintf(outfile, "section .text\n"); + scope_push(&scope); /* output all non-static function declarations as globals */ const struct root_node* node = ast; while (node != NULL) { - if (node->type == ROOT_FN_DECL) - fprintf(outfile, "global %s\n", node->as._fn_decl.name); + if (node->type == ROOT_FN_DECL) { + const char* fn_name = node->as._fn_decl.name; + scope_define_var(scope, (struct var_def) { + .name = fn_name, + .loc = { + .type = JMP_LABEL, + .label = fn_name, + }, + }); + fprintf(outfile, "global %s\n", fn_name); + } node = node->next; } @@ -104,5 +212,6 @@ void emit_code(const struct root_node* ast, const char* path) { node = node->next; } + scope_pop(&scope); fclose(outfile); } diff --git a/codegen.h b/codegen.h new file mode 100644 index 0000000..50bb105 --- /dev/null +++ b/codegen.h @@ -0,0 +1,8 @@ +#ifndef CODEGEN_H +#define CODEGEN_H + +#include "ast.h" + +void emit_code(const struct root_node* ast, const char* path); + +#endif @@ -1,5 +1,6 @@ #include "lexer.h" #include "parser.h" +#include "codegen.h" #include <stdlib.h> #include <stdio.h> @@ -35,8 +36,6 @@ void test_lexer(int argc, char** argv) { } } -void gdb_break_here() {} - void test_parser(int argc, char** argv) { struct root_node* root; struct root_node** p_cur = &root; @@ -44,7 +43,8 @@ void test_parser(int argc, char** argv) { *p_cur = parse(argv[i]); p_cur = &((*p_cur)->next); } - gdb_break_here(); + + emit_code(root, "test/simple.s"); ast_destroy(root); } @@ -0,0 +1,42 @@ +#include "scope.h" +#include <stdlib.h> + +static void scope_init(struct scope* scope) {} + +static void scope_destroy(struct scope* scope) {} + +unsigned long long hash_name(const char* name) { + unsigned long long hash = 0, i = 0; + while (name[i] != 0) hash = (hash << 5) - hash + name[i++]; + return hash; +} + +void scope_push(struct scope** p_scope) { + struct scope* inner_scope = calloc(1, sizeof(struct scope)); + scope_init(inner_scope); + inner_scope->next_out = *p_scope; + *p_scope = inner_scope; +} + +void scope_pop(struct scope** p_scope) { + struct scope* discarded_scope = *p_scope; + *p_scope = (*p_scope)->next_out; + scope_destroy(discarded_scope); + free(discarded_scope); +} + +bool scope_get_type( + const struct scope* scope, + struct type_def* p_entry, + const char* name +) {} + +void scope_define_type(struct scope* scope, struct type_def type) {} + +bool scope_get_var( + const struct scope* scope, + struct var_def* p_entry, + const char* name +) {} + +void scope_define_var(struct scope* scope, struct var_def var) {} @@ -0,0 +1,46 @@ +#ifndef VARS_H +#define VARS_H + +struct storage_location { + enum { + REGISTER, + JMP_LABEL, + BP_OFFSET, + } type; + union { + long long offset; + const char* label; + }; +}; + +struct type_def { + const char* name; + unsigned long long size; +}; + +struct var_def { + const char* name; + struct storage_location loc; +}; + +struct scope { + struct type_def* types; + struct var_def* vars; + struct scope* next_out; + unsigned long long bp_offset; +}; + +void scope_push(struct scope** p_scope); +void scope_pop(struct scope** p_scope); +bool scope_get_type( + const struct scope* scope, + struct type_def* p_entry, + const char* name); +void scope_define_type(struct scope* scope, struct type_def type); +bool scope_get_var( + const struct scope* scope, + struct var_def* p_entry, + const char* name); +void scope_define_var(struct scope* scope, struct var_def var); + +#endif
\ No newline at end of file diff --git a/test/simple.c b/test/simple.c index 33c14ce..99d86c4 100644 --- a/test/simple.c +++ b/test/simple.c @@ -1,3 +1,3 @@ int main() { - return 0; + return 16; } |
