#include "ccc.h" #include "codegen.h" #include "scope.h" #include #include #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 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 struct storage_location* storage ) { 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 struct storage_location* storage ) { switch (node->type) { case EXPR_EMPTY: break; case EXPR_INT_LIT: 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) { 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, &RV_LOC); fprintf(outfile, "\tmov rsp, rbp\n"); fprintf(outfile, "\tpop rbp\n"); fprintf(outfile, "\tret\n"); } static void emit_group(FILE* outfile, const struct group_node* node) { const struct stmt_node* body_node = node->body_head; while (body_node != NULL) { emit_stmt(outfile, body_node); body_node = body_node->next; } } 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: emit_var_decl(outfile, &node->as._var_decl); break; case STMT_RETURN: emit_return(outfile, &node->as._return); break; case STMT_EXPR: emit_expr(outfile, &node->as._expr, NULL); break; case STMT_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); 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) { switch (node->type) { case ROOT_FN_DECL: emit_fn_decl(outfile, &node->as._fn_decl); break; } } void emit_code(const struct root_node* ast, const char* path) { FILE* outfile = fopen(path, "w"); if (outfile == NULL) CCC_PANIC; fprintf(outfile, "section .text\n"); scope_push(&scope); /* TODO: register all basic types */ /* output all non-static function declarations as globals */ const struct root_node* node = ast; while (node != NULL) { 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; } fprintf(outfile, "\n"); /* actual code body */ node = ast; while (node != NULL) { emit_root_node(outfile, node); fprintf(outfile, "\n"); node = node->next; } scope_pop(&scope); fclose(outfile); }