#ifdef HAVE_CONFIG_H # include "config.h" #endif #include "code-generation.h" #include "register.h" #include #include #include #include #include static void generate_global_variable(CcmmcAst *global_decl, CcmmcState *state) { fputs("\t.data\n\t.align\t2\n", state->asm_output); for (CcmmcAst *var_decl = global_decl->child->right_sibling; var_decl != NULL; var_decl = var_decl->right_sibling) { CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_decl->value_id.name); switch (var_decl->value_id.kind) { case CCMMC_KIND_ID_NORMAL: fprintf(state->asm_output, "\t.comm\t%s, 4\n", var_sym->name); break; case CCMMC_KIND_ID_ARRAY: { size_t total_elements = 1; assert(var_sym->type.array_dimension > 0); for (size_t i = 0; i < var_sym->type.array_dimension; i++) total_elements *= var_sym->type.array_size[i]; fprintf(state->asm_output, "\t.comm\t%s, %zu\n", var_sym->name, total_elements * 4); } break; case CCMMC_KIND_ID_WITH_INIT: { CcmmcAst *init_value = var_decl->child; fprintf(state->asm_output, "\t.type\t%s, %%object\n" "\t.size\t%s, 4\n" "\t.global\t%s\n", var_sym->name, var_sym->name, var_sym->name); if (var_sym->type.type_base == CCMMC_AST_VALUE_INT) { int int_value; if (init_value->type_node == CCMMC_AST_NODE_CONST_VALUE) { assert(init_value->value_const.kind == CCMMC_KIND_CONST_INT); int_value = init_value->value_const.const_int; } else if (init_value->type_node == CCMMC_AST_NODE_EXPR) { assert(ccmmc_ast_expr_get_is_constant(init_value)); assert(ccmmc_ast_expr_get_is_int(init_value)); int_value = ccmmc_ast_expr_get_int(init_value); } else { assert(false); } fprintf(state->asm_output, "%s:\n" "\t.word\t%d\n", var_sym->name, int_value); } else if (var_sym->type.type_base == CCMMC_AST_VALUE_FLOAT) { float float_value; if (init_value->type_node == CCMMC_AST_NODE_CONST_VALUE) { assert(init_value->value_const.kind == CCMMC_KIND_CONST_FLOAT); float_value = init_value->value_const.const_float; } else if (init_value->type_node == CCMMC_AST_NODE_EXPR) { assert(ccmmc_ast_expr_get_is_constant(init_value)); assert(ccmmc_ast_expr_get_is_float(init_value)); float_value = ccmmc_ast_expr_get_float(init_value); } else { assert(false); } fprintf(state->asm_output, "%s:\n" "\t.float\t%.9g\n", var_sym->name, float_value); } else { assert(false); } } break; default: assert(false); } } } static inline bool safe_immediate(uint64_t imm) { return false; // imm <= 4096; } static void generate_expression(CcmmcAst *expr, CcmmcState *state, CcmmcTmp *result, uint64_t *current_offset); static void calc_array_offset(CcmmcAst *ref, CcmmcSymbolType *type, CcmmcState *state, CcmmcTmp *result, uint64_t *current_offset) { size_t i; CcmmcAst *index_node; CcmmcTmp *index, *mul; const char *result_reg, *index_reg, *mul_reg; generate_expression(ref->child, state, result, current_offset); index = ccmmc_register_alloc(state->reg_pool, current_offset); mul = ccmmc_register_alloc(state->reg_pool, current_offset); for (i = 1, index_node = ref->child->right_sibling; index_node != NULL || i < type->array_dimension; i++, index_node = index_node == NULL ? NULL : index_node->right_sibling) { if (index_node != NULL) { generate_expression(index_node, state, index, current_offset); index_reg = ccmmc_register_lock(state->reg_pool, index); } result_reg = ccmmc_register_lock(state->reg_pool, result); mul_reg = ccmmc_register_lock(state->reg_pool, mul); fprintf(state->asm_output, "\tldr\t%s, =%zu\n" "\tmul\t%s, %s, %s\n", mul_reg, type->array_size[i], result_reg, result_reg, mul_reg); if (index_node != NULL) { fprintf(state->asm_output, "\tadd\t%s, %s, %s\n", result_reg, result_reg, index_reg); ccmmc_register_unlock(state->reg_pool, index); } ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, mul); } ccmmc_register_free(state->reg_pool, index, current_offset); ccmmc_register_free(state->reg_pool, mul, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); fprintf(state->asm_output, "\tlsl\t%s, %s, #2\n", result_reg, result_reg); ccmmc_register_unlock(state->reg_pool, result); } static inline int calc_arg_offset(int arg_num) { return 16 + (arg_num - 8) * 8; } #define REG_TMP "x9" static void load_variable(CcmmcAst *id, CcmmcState *state, CcmmcTmp *dist, uint64_t *current_offset) { const char *dist_reg, *var_name = id->value_id.name; CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_name); fprintf(state->asm_output, "\t/* var load, line %zu */\n", id->line_number); if (ccmmc_symbol_attr_is_global(&var_sym->attr)) { if (id->value_id.kind != CCMMC_KIND_ID_ARRAY) { dist_reg = ccmmc_register_lock(state->reg_pool, dist); fprintf(state->asm_output, "\tadrp\t" REG_TMP ", %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", #:lo12:%s\n" "\tldr\t%s, [" REG_TMP "]\n", var_name, var_name, dist_reg); ccmmc_register_unlock(state->reg_pool, dist); } else { CcmmcTmp *offset; const char *offset_reg; char extend[8]; offset = ccmmc_register_alloc(state->reg_pool, current_offset); calc_array_offset(id, &var_sym->type, state, offset, current_offset); dist_reg = ccmmc_register_lock(state->reg_pool, dist); offset_reg = ccmmc_register_lock(state->reg_pool, offset); ccmmc_register_extend_name(offset, extend); fprintf(state->asm_output, "\tadrp\t" REG_TMP ", %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", #:lo12:%s\n" "\tsxtw\t%s, %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", %s\n" "\tldr\t%s, [" REG_TMP "]\n", var_name, var_name, extend, offset_reg, extend, dist_reg); ccmmc_register_unlock(state->reg_pool, dist); ccmmc_register_unlock(state->reg_pool, offset); ccmmc_register_free(state->reg_pool, offset, current_offset); } } else { if (id->value_id.kind != CCMMC_KIND_ID_ARRAY) { dist_reg = ccmmc_register_lock(state->reg_pool, dist); if (var_sym->attr.is_arg) { if (var_sym->attr.arg_num < 8) fprintf(state->asm_output, "\tmov\t%s, w%d\n", dist_reg, var_sym->attr.arg_num); else fprintf(state->asm_output, "\tldr\t%s, [fp, #%d]\n", dist_reg, calc_arg_offset(var_sym->attr.arg_num)); } else if (safe_immediate(var_sym->attr.addr)) { fprintf(state->asm_output, "\tldr\t%s, [fp, #-%" PRIu64 "]\n", dist_reg, var_sym->attr.addr); } else { fprintf(state->asm_output, "\tldr\t" REG_TMP ", =%" PRIu64 "\n" "\tsub\t" REG_TMP ", fp, " REG_TMP "\n" "\tldr\t%s, [" REG_TMP "]\n", var_sym->attr.addr, dist_reg); } ccmmc_register_unlock(state->reg_pool, dist); } else { CcmmcTmp *offset; const char *offset_reg; char extend[8]; offset = ccmmc_register_alloc(state->reg_pool, current_offset); calc_array_offset(id, &var_sym->type, state, offset, current_offset); dist_reg = ccmmc_register_lock(state->reg_pool, dist); offset_reg = ccmmc_register_lock(state->reg_pool, offset); ccmmc_register_extend_name(offset, extend); if (var_sym->attr.is_arg) { if (var_sym->attr.arg_num < 8) fprintf(state->asm_output, "\tmov\t" REG_TMP ", x%d\n", var_sym->attr.arg_num); else fprintf(state->asm_output, "\tldr\t" REG_TMP ", [fp, #%d]\n", calc_arg_offset(var_sym->attr.arg_num)); } else { fprintf(state->asm_output, "\tldr\t" REG_TMP ", =%" PRIu64 "\n" "\tsub\t" REG_TMP ", fp, " REG_TMP "\n" "\tsxtw\t%s, %s\n", var_sym->attr.addr, extend, offset_reg); } fprintf(state->asm_output, "\tadd\t" REG_TMP ", " REG_TMP ", %s\n" "\tldr\t%s, [" REG_TMP "]\n", extend, dist_reg); ccmmc_register_unlock(state->reg_pool, dist); ccmmc_register_unlock(state->reg_pool, offset); ccmmc_register_free(state->reg_pool, offset, current_offset); } } } static void store_variable(CcmmcAst *id, CcmmcState *state, CcmmcTmp *src, uint64_t *current_offset) { const char *src_reg, *var_name = id->value_id.name; CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_name); fprintf(state->asm_output, "\t/* var store, line %zu */\n", id->line_number); if (ccmmc_symbol_attr_is_global(&var_sym->attr)) { if (id->value_id.kind != CCMMC_KIND_ID_ARRAY) { src_reg = ccmmc_register_lock(state->reg_pool, src); fprintf(state->asm_output, "\tadrp\t" REG_TMP ", %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", #:lo12:%s\n" "\tstr\t%s, [" REG_TMP "]\n", var_name, var_name, src_reg); ccmmc_register_unlock(state->reg_pool, src); } else { CcmmcTmp *offset; const char *offset_reg; char extend[8]; offset = ccmmc_register_alloc(state->reg_pool, current_offset); calc_array_offset(id, &var_sym->type, state, offset, current_offset); src_reg = ccmmc_register_lock(state->reg_pool, src); offset_reg = ccmmc_register_lock(state->reg_pool, offset); ccmmc_register_extend_name(offset, extend); fprintf(state->asm_output, "\tadrp\t" REG_TMP ", %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", #:lo12:%s\n" "\tsxtw\t%s, %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", %s\n" "\tstr\t%s, [" REG_TMP "]\n", var_name, var_name, extend, offset_reg, extend, src_reg); ccmmc_register_unlock(state->reg_pool, src); ccmmc_register_unlock(state->reg_pool, offset); ccmmc_register_free(state->reg_pool, offset, current_offset); } } else { if (id->value_id.kind != CCMMC_KIND_ID_ARRAY) { src_reg = ccmmc_register_lock(state->reg_pool, src); if (var_sym->attr.is_arg) { if (var_sym->attr.arg_num < 8) fprintf(state->asm_output, "\tmov\tw%d, %s\n", var_sym->attr.arg_num, src_reg); else fprintf(state->asm_output, "\tstr\t%s, [fp, #%d]\n", src_reg, calc_arg_offset(var_sym->attr.arg_num)); } else if (safe_immediate(var_sym->attr.addr)) { fprintf(state->asm_output, "\tstr\t%s, [fp, #-%" PRIu64 "]\n", src_reg, var_sym->attr.addr); } else { fprintf(state->asm_output, "\tldr\t" REG_TMP ", =%" PRIu64 "\n" "\tsub\t" REG_TMP ", fp, " REG_TMP "\n" "\tstr\t%s, [" REG_TMP "]\n", var_sym->attr.addr, src_reg); } ccmmc_register_unlock(state->reg_pool, src); } else { CcmmcTmp *offset; const char *offset_reg; char extend[8]; offset = ccmmc_register_alloc(state->reg_pool, current_offset); calc_array_offset(id, &var_sym->type, state, offset, current_offset); src_reg = ccmmc_register_lock(state->reg_pool, src); offset_reg = ccmmc_register_lock(state->reg_pool, offset); ccmmc_register_extend_name(offset, extend); if (var_sym->attr.is_arg) { if (var_sym->attr.arg_num < 8) fprintf(state->asm_output, "\tmov\t" REG_TMP ", x%d\n", var_sym->attr.arg_num); else fprintf(state->asm_output, "\tldr\t" REG_TMP ", [fp, #%d]\n", calc_arg_offset(var_sym->attr.arg_num)); } else { fprintf(state->asm_output, "\tldr\t" REG_TMP ", =%" PRIu64 "\n" "\tsub\t" REG_TMP ", fp, " REG_TMP "\n" "\tsxtw\t%s, %s\n", var_sym->attr.addr, extend, offset_reg); } fprintf(state->asm_output, "\tadd\t" REG_TMP ", " REG_TMP ", %s\n" "\tstr\t%s, [" REG_TMP "]\n", extend, src_reg); ccmmc_register_unlock(state->reg_pool, src); ccmmc_register_unlock(state->reg_pool, offset); ccmmc_register_free(state->reg_pool, offset, current_offset); } } } #undef REG_TMP static const char *call_write(CcmmcAst *id, CcmmcState *state, uint64_t *current_offset) { CcmmcAst *arg = id->right_sibling->child; CcmmcTmp *dist; const char *dist_reg; if (arg->type_value == CCMMC_AST_VALUE_CONST_STRING) { size_t label_str = state->label_number++; fprintf(state->asm_output, "\t.section .rodata\n" "\t.align 2\n" ".LC%zu:\n" "\t.ascii \"%s\\000\"\n" "\t.text\n" "\tadr\tx0, .LC%zu\n", label_str, arg->value_const.const_string, label_str); return "_write_str"; } else if (arg->type_value == CCMMC_AST_VALUE_INT) { dist = ccmmc_register_alloc(state->reg_pool, current_offset); generate_expression(arg, state, dist, current_offset); dist_reg = ccmmc_register_lock(state->reg_pool, dist); fprintf(state->asm_output, "\tmov\tw0, %s\n", dist_reg); ccmmc_register_unlock(state->reg_pool, dist); ccmmc_register_free(state->reg_pool, dist, current_offset); return "_write_int"; } else if (arg->type_value == CCMMC_AST_VALUE_FLOAT) { dist = ccmmc_register_alloc(state->reg_pool, current_offset); generate_expression(arg, state, dist, current_offset); dist_reg = ccmmc_register_lock(state->reg_pool, dist); fprintf(state->asm_output, "\tfmov\ts0, %s\n", dist_reg); ccmmc_register_unlock(state->reg_pool, dist); ccmmc_register_free(state->reg_pool, dist, current_offset); return "_write_float"; } abort(); } static void call_function(CcmmcAst *id, CcmmcState *state, uint64_t *current_offset) { // XXX: We should have a better way to find the function in which we are CcmmcAst *in_func = id->parent; for (; in_func->type_node != CCMMC_AST_NODE_DECL || in_func->value_decl.kind != CCMMC_KIND_DECL_FUNCTION; in_func = in_func->parent); // XXX: We should not search scopes other than the global scope CcmmcSymbol *in_func_sym = ccmmc_symbol_table_retrieve( state->table, in_func->child->right_sibling->value_id.name); size_t stored_param_count = in_func_sym->type.param_count; const char *func_name = id->value_id.name; if (strcmp(func_name, "write") == 0) { ccmmc_register_save_arguments(state->reg_pool, stored_param_count); ccmmc_register_caller_save(state->reg_pool); func_name = call_write(id, state, current_offset); } else if (strcmp(func_name, "read") == 0) { ccmmc_register_save_arguments(state->reg_pool, stored_param_count); ccmmc_register_caller_save(state->reg_pool); func_name = "_read_int"; } else if (strcmp(func_name, "fread") == 0) { ccmmc_register_save_arguments(state->reg_pool, stored_param_count); ccmmc_register_caller_save(state->reg_pool); func_name = "_read_float"; } else if (id->right_sibling->child != NULL) { CcmmcSymbol *func_sym = ccmmc_symbol_table_retrieve( state->table, func_name); size_t call_param_count = func_sym->type.param_count; CcmmcAst *arg; CcmmcTmp *dists[call_param_count]; size_t i; for (i = 0; i < call_param_count; i++) dists[i] = ccmmc_register_alloc(state->reg_pool, current_offset); for (i = 0, arg = id->right_sibling->child; i < call_param_count; i++, arg = arg->right_sibling) { if (ccmmc_symbol_type_is_array(func_sym->type.param_list[i])) { assert(arg->type_node == CCMMC_AST_NODE_ID); CcmmcSymbolType var_type = ccmmc_symbol_table_retrieve( state->table, arg->value_id.name)->type; if (arg->child != NULL) calc_array_offset(arg, &var_type, state, dists[i], current_offset); } else { generate_expression(arg, state, dists[i], current_offset); } } ccmmc_register_save_arguments(state->reg_pool, stored_param_count); ccmmc_register_caller_save(state->reg_pool); if (call_param_count > 8) fprintf(state->asm_output, "\tsub\tsp, sp, %zu\n", (call_param_count - 8) * 8); for (i = 0, arg = id->right_sibling->child; i < call_param_count; i++, arg = arg->right_sibling) { if (ccmmc_symbol_type_is_array(func_sym->type.param_list[i])) { CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve( state->table, arg->value_id.name); const char *offset_reg = ccmmc_register_lock( state->reg_pool, dists[i]); char offset_extend[8]; ccmmc_register_extend_name(dists[i], offset_extend); #define REG_TMP "x9" if (var_sym->attr.is_arg) { if (var_sym->attr.arg_num < 8) fprintf(state->asm_output, "\tmov\t" REG_TMP ", x%d\n", var_sym->attr.arg_num); else fprintf(state->asm_output, "\tldr\t" REG_TMP ", [fp, #%d]\n", calc_arg_offset(var_sym->attr.arg_num)); } else { fprintf(state->asm_output, "\tldr\t" REG_TMP ", =%" PRIu64 "\n" "\tsub\t" REG_TMP ", fp, " REG_TMP "\n", var_sym->attr.addr); } if (arg->child != NULL) fprintf(state->asm_output, "\tsxtw\t%s, %s\n" "\tadd\t" REG_TMP ", " REG_TMP ", %s\n", offset_extend, offset_reg, offset_extend); if (i < 8) fprintf(state->asm_output, "\tmov\tx%zu, " REG_TMP "\n", i); else fprintf(state->asm_output, "\tstr\t" REG_TMP ", [sp, %zu]\n", (i - 8) * 8); #undef REG_TMP ccmmc_register_unlock(state->reg_pool, dists[i]); } else { CcmmcAstValueType arg_type = arg->type_value; CcmmcAstValueType expected_type = func_sym->type.param_list[i].type_base; const char *dist_reg = ccmmc_register_lock( state->reg_pool, dists[i]); char dist_extend[8]; ccmmc_register_extend_name(dists[i], dist_extend); #define FPREG_TMP "s16" if (arg_type == CCMMC_AST_VALUE_FLOAT && expected_type == CCMMC_AST_VALUE_INT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcvtas\t%s, %s\n", FPREG_TMP, dist_reg, dist_reg, FPREG_TMP); else if (arg_type == CCMMC_AST_VALUE_INT && expected_type == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tscvtf\t%s, %s\n" "\tfmov\t%s, %s\n", FPREG_TMP, dist_reg, dist_reg, FPREG_TMP); #undef FPREG_TMP if (i < 8) fprintf(state->asm_output, "\tsxtw\tx%zu, %s\n", i, dist_reg); else fprintf(state->asm_output, "\tsxtw\t%s, %s\n" "\tstr\t%s, [sp, %zu]\n", dist_extend, dist_reg, dist_extend, (i - 8) * 8); ccmmc_register_unlock(state->reg_pool, dists[i]); } } fprintf(state->asm_output, "\tbl\t%s\n", func_name); if (call_param_count > 8) fprintf(state->asm_output, "\tadd\tsp, sp, %zu\n", (call_param_count - 8) * 8); ccmmc_register_caller_load(state->reg_pool); ccmmc_register_load_arguments(state->reg_pool, stored_param_count); for (i = 0; i < call_param_count; i++) ccmmc_register_free(state->reg_pool, dists[i], current_offset); return; } else { ccmmc_register_save_arguments(state->reg_pool, stored_param_count); ccmmc_register_caller_save(state->reg_pool); } fprintf(state->asm_output, "\tbl\t%s\n", func_name); ccmmc_register_caller_load(state->reg_pool); ccmmc_register_load_arguments(state->reg_pool, stored_param_count); } static void generate_expression(CcmmcAst *expr, CcmmcState *state, CcmmcTmp *result, uint64_t *current_offset) { const char *result_reg, *op1_reg, *op2_reg; if (expr->type_node == CCMMC_AST_NODE_CONST_VALUE) { fprintf(state->asm_output, "\t/* const value, line %zu */\n", expr->line_number); result_reg = ccmmc_register_lock(state->reg_pool, result); if (expr->type_value == CCMMC_AST_VALUE_INT) { fprintf(state->asm_output, "\tldr\t%s, =%d\n", result_reg, expr->value_const.const_int); } else if (expr->type_value == CCMMC_AST_VALUE_FLOAT) { fprintf(state->asm_output, "\tldr\t%s, .LC%zu\n" "\t.section .rodata\n" "\t.align 2\n" ".LC%zu:\n" "\t.float\t%.9g\n" "\t.text\n", result_reg, state->label_number, state->label_number, expr->value_const.const_float); state->label_number++; } else { assert(false); } ccmmc_register_unlock(state->reg_pool, result); return; } if (expr->type_node == CCMMC_AST_NODE_STMT && expr->value_stmt.kind == CCMMC_KIND_STMT_FUNCTION_CALL) { const char *func_name = expr->child->value_id.name; CcmmcSymbol *func_sym = ccmmc_symbol_table_retrieve( state->table, func_name); CcmmcAstValueType func_type = func_sym->type.type_base; call_function(expr->child, state, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); if (func_type == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, s0\n", result_reg); else fprintf(state->asm_output, "\tmov\t%s, w0\n", result_reg); ccmmc_register_unlock(state->reg_pool, result); return ; } if (expr->type_node == CCMMC_AST_NODE_ID) { load_variable(expr, state, result, current_offset); return; } assert(expr->type_node == CCMMC_AST_NODE_EXPR); #define FPREG_RESULT "s16" #define FPREG_OP1 "s17" #define FPREG_OP2 "s18" if (expr->value_expr.kind == CCMMC_KIND_EXPR_BINARY_OP) { CcmmcAst *left = expr->child; CcmmcAst *right = expr->child->right_sibling; size_t label_short = state->label_number; // evaluate the left expression fprintf(state->asm_output, "\t/* expr binary op, line %zu */\n", expr->line_number); generate_expression(left, state, result, current_offset); CcmmcTmp *op1 = ccmmc_register_alloc(state->reg_pool, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); op1_reg = ccmmc_register_lock(state->reg_pool, op1); fprintf(state->asm_output, "\t/* mov op1_reg, result_reg */\n" "\tmov\t%s, %s\n", op1_reg, result_reg); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, op1); if (expr->value_expr.op_binary == CCMMC_KIND_OP_BINARY_AND) { // logical AND needs short-curcuit evaluation label_short = state->label_number++; op1_reg = ccmmc_register_lock(state->reg_pool, op1); if (left->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcmp\t%s, #0.0\n" "\tb.eq\t.LC%zu\n", FPREG_OP1, op1_reg, FPREG_OP1, label_short); else fprintf(state->asm_output, "\tcbz\t%s, .LC%zu\n", op1_reg, label_short); ccmmc_register_unlock(state->reg_pool, op1); } else if (expr->value_expr.op_binary == CCMMC_KIND_OP_BINARY_OR) { // logical OR needs short-curcuit evaluation label_short = state->label_number++; op1_reg = ccmmc_register_lock(state->reg_pool, op1); if (left->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcmp\t%s, #0.0\n" "\tb.ne\t.LC%zu\n", FPREG_OP1, op1_reg, FPREG_OP1, label_short); else fprintf(state->asm_output, "\tcbnz\t%s, .LC%zu\n", op1_reg, label_short); ccmmc_register_unlock(state->reg_pool, op1); } // evaluate the right expression generate_expression(right, state, result, current_offset); CcmmcTmp *op2 = ccmmc_register_alloc(state->reg_pool, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); op2_reg = ccmmc_register_lock(state->reg_pool, op2); fprintf(state->asm_output, "\t/* mov op2_reg, result_reg */\n" "\tmov\t%s, %s\n", op2_reg, result_reg); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, op2); if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) { op1_reg = ccmmc_register_lock(state->reg_pool, op1); if (left->type_value == CCMMC_AST_VALUE_INT) fprintf(state->asm_output, "\tscvtf\t%s, %s\n", FPREG_OP1, op1_reg); else if (left->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP1, op1_reg); else assert(false); ccmmc_register_unlock(state->reg_pool, op1); op2_reg = ccmmc_register_lock(state->reg_pool, op2); if (right->type_value == CCMMC_AST_VALUE_INT) fprintf(state->asm_output, "\tscvtf\t%s, %s\n", FPREG_OP2, op2_reg); else if (right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP2, op2_reg); else assert(false); ccmmc_register_unlock(state->reg_pool, op2); } result_reg = ccmmc_register_lock(state->reg_pool, result); op1_reg = ccmmc_register_lock(state->reg_pool, op1); op2_reg = ccmmc_register_lock(state->reg_pool, op2); switch (expr->value_expr.op_binary) { case CCMMC_KIND_OP_BINARY_ADD: if (expr->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfadd\t%s, %s, %s\n", FPREG_RESULT, FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tadd\t%s, %s, %s\n", result_reg, op1_reg, op2_reg); break; case CCMMC_KIND_OP_BINARY_SUB: if (expr->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfsub\t%s, %s, %s\n", FPREG_RESULT, FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tsub\t%s, %s, %s\n", result_reg, op1_reg, op2_reg); break; case CCMMC_KIND_OP_BINARY_MUL: if (expr->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmul\t%s, %s, %s\n", FPREG_RESULT, FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tmul\t%s, %s, %s\n", result_reg, op1_reg, op2_reg); break; case CCMMC_KIND_OP_BINARY_DIV: if (expr->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfdiv\t%s, %s, %s\n", FPREG_RESULT, FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tsdiv\t%s, %s, %s\n", result_reg, op1_reg, op2_reg); break; case CCMMC_KIND_OP_BINARY_EQ: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, eq\n", result_reg); break; case CCMMC_KIND_OP_BINARY_GE: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, ge\n", result_reg); break; case CCMMC_KIND_OP_BINARY_LE: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, le\n", result_reg); break; case CCMMC_KIND_OP_BINARY_NE: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, ne\n", result_reg); break; case CCMMC_KIND_OP_BINARY_GT: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, gt\n", result_reg); break; case CCMMC_KIND_OP_BINARY_LT: if (left->type_value == CCMMC_AST_VALUE_FLOAT || right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, %s\n", FPREG_OP1, FPREG_OP2); else fprintf(state->asm_output, "\tcmp\t%s, %s\n", op1_reg, op2_reg); fprintf(state->asm_output, "\tcset\t%s, lt\n", result_reg); break; case CCMMC_KIND_OP_BINARY_AND: { size_t label_exit = state->label_number++; if (right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, #0.0\n" "\tb.eq\t.LC%zu\n" "\tmov\t%s, #1\n" "\tb\t.LC%zu\n" ".LC%zu:\n" "\tmov\t%s, #0\n" ".LC%zu:\n", FPREG_OP2, label_short, result_reg, label_exit, label_short, result_reg, label_exit); else fprintf(state->asm_output, "\tcbz\t%s, .LC%zu\n" "\tmov\t%s, #1\n" "\tb\t.LC%zu\n" ".LC%zu:\n" "\tmov\t%s, #0\n" ".LC%zu:\n", op2_reg, label_short, result_reg, label_exit, label_short, result_reg, label_exit); } break; case CCMMC_KIND_OP_BINARY_OR: { size_t label_exit = state->label_number++; if (right->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, #0.0\n" "\tb.ne\t.LC%zu\n" "\tmov\t%s, #0\n" "\tb\t.LC%zu\n" ".LC%zu:\n" "\tmov\t%s, #1\n" ".LC%zu:\n", FPREG_OP2, label_short, result_reg, label_exit, label_short, result_reg, label_exit); else fprintf(state->asm_output, "\tcbnz\t%s, .LC%zu\n" "\tmov\t%s, #0\n" "\tb\t.LC%zu\n" ".LC%zu:\n" "\tmov\t%s, #1\n" ".LC%zu:\n", op2_reg, label_short, result_reg, label_exit, label_short, result_reg, label_exit); } break; default: assert(false); } ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, op1); ccmmc_register_unlock(state->reg_pool, op2); ccmmc_register_free(state->reg_pool, op1, current_offset); ccmmc_register_free(state->reg_pool, op2, current_offset); if (expr->type_value == CCMMC_AST_VALUE_FLOAT) { result_reg = ccmmc_register_lock(state->reg_pool, result); fprintf(state->asm_output, "\tfmov\t%s, %s\n", result_reg, FPREG_RESULT); ccmmc_register_unlock(state->reg_pool, result); } return; } if (expr->value_expr.kind == CCMMC_KIND_EXPR_UNARY_OP) { CcmmcAst *arg = expr->child; fprintf(state->asm_output, "\t/* expr unary op, line %zu */\n", expr->line_number); generate_expression(arg, state, result, current_offset); CcmmcTmp *op1 = ccmmc_register_alloc(state->reg_pool, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); op1_reg = ccmmc_register_lock(state->reg_pool, op1); fprintf(state->asm_output, "\t/* mov op1_reg, result_reg */\n" "\tmov\t%s, %s\n", op1_reg, result_reg); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, op1); if (arg->type_value == CCMMC_AST_VALUE_FLOAT) { op1_reg = ccmmc_register_lock(state->reg_pool, op1); fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP1, op1_reg); ccmmc_register_unlock(state->reg_pool, op1); } result_reg = ccmmc_register_lock(state->reg_pool, result); op1_reg = ccmmc_register_lock(state->reg_pool, op1); switch (expr->value_expr.op_unary) { case CCMMC_KIND_OP_UNARY_POSITIVE: fputs("\t/* nop */\n", state->asm_output); break; case CCMMC_KIND_OP_UNARY_NEGATIVE: if (arg->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfneg\t%s, %s\n", FPREG_RESULT, FPREG_OP1); else fprintf(state->asm_output, "\tneg\t%s, %s\n", result_reg, op1_reg); break; case CCMMC_KIND_OP_UNARY_LOGICAL_NEGATION: if (arg->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfcmp\t%s, #0.0\n", FPREG_OP1); else fprintf(state->asm_output, "\tcmp\t%s, wzr\n", op1_reg); fprintf(state->asm_output, "\tcset\t%s, eq\n", result_reg); break; default: assert(false); } ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_unlock(state->reg_pool, op1); ccmmc_register_free(state->reg_pool, op1, current_offset); if (expr->type_value == CCMMC_AST_VALUE_FLOAT) { result_reg = ccmmc_register_lock(state->reg_pool, result); fprintf(state->asm_output, "\tfmov\t%s, %s\n", result_reg, FPREG_RESULT); ccmmc_register_unlock(state->reg_pool, result); } return; } #undef FPREG_RESULT #undef FPREG_OP1 #undef FPREG_OP2 assert(false); } static void calc_expression_result(CcmmcAst *expr, CcmmcAstValueType type, CcmmcState *state, CcmmcTmp *result, uint64_t *current_offset) { #define FPREG_TMP "s16" const char *result_reg; generate_expression(expr, state, result, current_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); if (expr->type_value == CCMMC_AST_VALUE_FLOAT && type == CCMMC_AST_VALUE_INT) { fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcvtas\t%s, %s\n", FPREG_TMP, result_reg, result_reg, FPREG_TMP); } else if (expr->type_value == CCMMC_AST_VALUE_INT && type == CCMMC_AST_VALUE_FLOAT) { fprintf(state->asm_output, "\tscvtf\t%s, %s\n" "\tfmov\t%s, %s\n", FPREG_TMP, result_reg, result_reg, FPREG_TMP); } ccmmc_register_unlock(state->reg_pool, result); #undef FPREG_TMP } static void calc_and_save_expression_result(CcmmcAst *lvar, CcmmcAst *expr, CcmmcState *state, uint64_t *current_offset) { CcmmcTmp *result; result = ccmmc_register_alloc(state->reg_pool, current_offset); calc_expression_result(expr, lvar->type_value, state, result, current_offset); store_variable(lvar, state, result, current_offset); ccmmc_register_free(state->reg_pool, result, current_offset); } static void generate_block(CcmmcAst *block, CcmmcState *state, uint64_t current_offset, bool scope_already_open); static void generate_statement( CcmmcAst *stmt, CcmmcState *state, uint64_t current_offset) { if (stmt->type_node == CCMMC_AST_NODE_NUL) return; if (stmt->type_node == CCMMC_AST_NODE_BLOCK) { generate_block(stmt, state, current_offset, false); return; } assert(stmt->type_node == CCMMC_AST_NODE_STMT); switch(stmt->value_stmt.kind) { case CCMMC_KIND_STMT_WHILE: { #define FPREG_TMP "s16" size_t label_cmp = state->label_number++; size_t label_exit = state->label_number++; const char *result_reg; CcmmcTmp *result = ccmmc_register_alloc(state->reg_pool, ¤t_offset); // while condition fprintf(state->asm_output, ".LC%zu:\n", label_cmp); generate_expression(stmt->child, state, result, ¤t_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); if (stmt->child->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcmp\t%s, #0.0\n" "\tb.e\t.LC%zu\n", FPREG_TMP, result_reg, FPREG_TMP, label_exit); else fprintf(state->asm_output, "\tcbz\t%s, .LC%zu\n", result_reg, label_exit); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_free(state->reg_pool, result, ¤t_offset); // while body generate_statement(stmt->child->right_sibling, state, current_offset); fprintf(state->asm_output, "\tb\t.LC%zu\n" ".LC%zu:\n", label_cmp, label_exit); #undef FPREG_TMP } break; case CCMMC_KIND_STMT_FOR: { #define FPREG_TMP "s16" size_t label_cmp = state->label_number++; size_t label_exit = state->label_number++; const char *result_reg; CcmmcTmp *result = ccmmc_register_alloc(state->reg_pool, ¤t_offset); for (CcmmcAst *assign = stmt->child->child; assign != NULL; assign = assign->right_sibling) { generate_statement(assign, state, current_offset); } // for condition fprintf(state->asm_output, ".LC%zu:\n", label_cmp); for (CcmmcAst *expr = stmt->child->right_sibling->child; expr != NULL; expr = expr->right_sibling) { generate_expression(expr, state, result, ¤t_offset); } result_reg = ccmmc_register_lock(state->reg_pool, result); if (stmt->child->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcmp\t%s, #0.0\n" "\tb.e\t.LC%zu\n", FPREG_TMP, result_reg, FPREG_TMP, label_exit); else fprintf(state->asm_output, "\tcbz\t%s, .LC%zu\n", result_reg, label_exit); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_free(state->reg_pool, result, ¤t_offset); // for body generate_statement( stmt->child->right_sibling->right_sibling->right_sibling, state, current_offset); for (CcmmcAst *assign = stmt->child->right_sibling->right_sibling->child; assign != NULL; assign = assign->right_sibling) { generate_statement(assign, state, current_offset); } fprintf(state->asm_output, "\tb\t.LC%zu\n" ".LC%zu:\n", label_cmp, label_exit); #undef FPREG_TMP } break; case CCMMC_KIND_STMT_ASSIGN: calc_and_save_expression_result(stmt->child, stmt->child->right_sibling, state, ¤t_offset); break; case CCMMC_KIND_STMT_IF: { #define FPREG_TMP "s16" size_t label_cross_if = state->label_number++; const char *result_reg; CcmmcTmp *result = ccmmc_register_alloc(state->reg_pool, ¤t_offset); // if condition generate_expression(stmt->child, state, result, ¤t_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); if (stmt->child->type_value == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\t%s, %s\n" "\tfcmp\t%s, #0.0\n" "\tb.e\t.LC%zu\n", FPREG_TMP, result_reg, FPREG_TMP, label_cross_if); else fprintf(state->asm_output, "\tcbz\t%s, .LC%zu\n", result_reg, label_cross_if); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_free(state->reg_pool, result, ¤t_offset); // if body generate_statement(stmt->child->right_sibling, state, current_offset); if (stmt->child->right_sibling->right_sibling->type_node == CCMMC_AST_NODE_NUL) { // no else fprintf(state->asm_output, ".LC%zu:\n", label_cross_if); } else { // jump across else size_t label_exit = state->label_number++; fprintf(state->asm_output, "\tb\t.LC%zu\n" ".LC%zu:\n", label_exit, label_cross_if); // else body generate_statement(stmt->child->right_sibling->right_sibling, state, current_offset); fprintf(state->asm_output, ".LC%zu:\n", label_exit); #undef FPREG_TMP } } break; case CCMMC_KIND_STMT_FUNCTION_CALL: call_function(stmt->child, state, ¤t_offset); break; case CCMMC_KIND_STMT_RETURN: for (CcmmcAst *func = stmt->parent; ; func = func->parent) { if (func->type_node == CCMMC_AST_NODE_DECL && func->value_decl.kind == CCMMC_KIND_DECL_FUNCTION) { const char *func_name = func->child->right_sibling->value_id.name; CcmmcSymbol *func_sym = ccmmc_symbol_table_retrieve(state->table, func_name); CcmmcAstValueType func_type = func_sym->type.type_base; const char *result_reg; CcmmcTmp *result; result = ccmmc_register_alloc(state->reg_pool, ¤t_offset); calc_expression_result(stmt->child, func_sym->type.type_base, state, result, ¤t_offset); result_reg = ccmmc_register_lock(state->reg_pool, result); if (func_type == CCMMC_AST_VALUE_FLOAT) fprintf(state->asm_output, "\tfmov\ts0, %s\n", result_reg); else fprintf(state->asm_output, "\tmov\tw0, %s\n", result_reg); ccmmc_register_unlock(state->reg_pool, result); ccmmc_register_free(state->reg_pool, result, ¤t_offset); // XXX: We should fix the location of the return label // instead of copying code and modifying sp here. if (safe_immediate(current_offset)) { fprintf(state->asm_output, "\tadd\tsp, sp, #%" PRIu64 "\n", current_offset); } else { #define REG_TMP "x9" fprintf(state->asm_output, "\tldr\t%s, =%" PRIu64 "\n" "\tadd\tsp, sp, %s\n", REG_TMP, current_offset, REG_TMP); #undef REG_TMP } fprintf(state->asm_output, "\tb\t.LR_%s\n", func_name); break; } } break; default: assert(false); } } static uint64_t generate_local_variable( CcmmcAst *local_decl, CcmmcState *state, uint64_t current_offset) { for (CcmmcAst *var_decl = local_decl->child->right_sibling; var_decl != NULL; var_decl = var_decl->right_sibling) { CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_decl->value_id.name); switch (var_decl->value_id.kind) { case CCMMC_KIND_ID_ARRAY: { size_t total_elements = 1; assert(var_sym->type.array_dimension > 0); for (size_t i = 0; i < var_sym->type.array_dimension; i++) total_elements *= var_sym->type.array_size[i]; current_offset += total_elements * 4; var_sym->attr.addr = current_offset; } break; case CCMMC_KIND_ID_NORMAL: case CCMMC_KIND_ID_WITH_INIT: current_offset += 4; var_sym->attr.addr = current_offset; break; default: assert(false); } } return current_offset; } static void init_local_variable( CcmmcAst *local_decl, CcmmcState *state, uint64_t current_offset) { for (CcmmcAst *var_decl = local_decl->child->right_sibling; var_decl != NULL; var_decl = var_decl->right_sibling) { if (var_decl->value_id.kind == CCMMC_KIND_ID_WITH_INIT) calc_and_save_expression_result(var_decl, var_decl->child, state, ¤t_offset); } } static void generate_block(CcmmcAst *block, CcmmcState *state, uint64_t current_offset, bool scope_already_open) { if (!scope_already_open) ccmmc_symbol_table_reopen_scope(state->table); CcmmcAst *child = block->child; uint64_t orig_offset = current_offset; uint64_t offset_diff = 0; if (child != NULL && child->type_node == CCMMC_AST_NODE_VARIABLE_DECL_LIST) { for (CcmmcAst *local = child->child; local != NULL; local = local->right_sibling) current_offset = generate_local_variable(local, state, current_offset); offset_diff = current_offset - orig_offset; if (offset_diff > 0) { if (safe_immediate(offset_diff)) { fprintf(state->asm_output, "\tsub\tsp, sp, #%" PRIu64 "\n", offset_diff); } else { #define REG_TMP "x9" fprintf(state->asm_output, "\tldr\t%s, =%" PRIu64 "\n" "\tsub\tsp, sp, %s\n", REG_TMP, offset_diff, REG_TMP); #undef REG_TMP } } for (CcmmcAst *local = child->child; local != NULL; local = local->right_sibling) init_local_variable(local, state, current_offset); child = child->right_sibling; } if (child != NULL && child->type_node == CCMMC_AST_NODE_STMT_LIST) { for (CcmmcAst *stmt = child->child; stmt != NULL; stmt = stmt->right_sibling) generate_statement(stmt, state, current_offset); } if (offset_diff > 0) { if (safe_immediate(offset_diff)) { fprintf(state->asm_output, "\tadd\tsp, sp, #%" PRIu64 "\n", offset_diff); } else { #define REG_TMP "x9" fprintf(state->asm_output, "\tldr\t%s, =%" PRIu64 "\n" "\tadd\tsp, sp, %s\n", REG_TMP, offset_diff, REG_TMP); #undef REG_TMP } } ccmmc_symbol_table_close_scope(state->table); } static void generate_function(CcmmcAst *function, CcmmcState *state) { fputs("\t.text\n\t.align\t2\n", state->asm_output); const char *func_name = function->child->right_sibling->value_id.name; const char *symbol_name = func_name; // XXX: We have to rewrite some names to workaround TA's broken toolchain if (strcmp(func_name, "MAIN") == 0) symbol_name = "_start_MAIN"; fprintf(state->asm_output, "\t.type\t%s, %%function\n" "\t.global\t%s\n" "%s:\n" "\tstp\tlr, fp, [sp, -16]!\n" "\tmov\tfp, sp\n", symbol_name, symbol_name, symbol_name); CcmmcAst *param_node = function->child->right_sibling->right_sibling; ccmmc_symbol_table_reopen_scope(state->table); CcmmcAst *arg_node = param_node->child; for (int i = 0; arg_node != NULL; arg_node = arg_node->right_sibling, i++) { CcmmcSymbol *arg_sym = ccmmc_symbol_table_retrieve(state->table, arg_node->child->right_sibling->value_id.name); arg_sym->attr.is_arg = true; arg_sym->attr.arg_num = i; } CcmmcAst *block_node = param_node->right_sibling; generate_block(block_node, state, 0, true); fprintf(state->asm_output, ".LR_%s:\n" "\tldp\tlr, fp, [sp], 16\n" "\tret\tlr\n" "\t.size\t%s, .-%s\n", func_name, symbol_name, symbol_name); } static void generate_program(CcmmcState *state) { for (CcmmcAst *global_decl = state->ast->child; global_decl != NULL; global_decl = global_decl->right_sibling) { switch (global_decl->value_decl.kind) { case CCMMC_KIND_DECL_VARIABLE: generate_global_variable(global_decl, state); break; case CCMMC_KIND_DECL_FUNCTION: generate_function(global_decl, state); break; case CCMMC_KIND_DECL_TYPE: break; case CCMMC_KIND_DECL_FUNCTION_PARAMETER: default: assert(false); } } } void ccmmc_code_generation(CcmmcState *state) { state->table->this_scope = NULL; state->table->current = NULL; ccmmc_symbol_table_reopen_scope(state->table); state->reg_pool = ccmmc_register_init(state->asm_output); fputs( "fp\t.req\tx29\n" "lr\t.req\tx30\n", state->asm_output); generate_program(state); ccmmc_register_fini(state->reg_pool); state->reg_pool = NULL; } // vim: set sw=4 ts=4 sts=4 et: