diff options
author | Ting-Wei Lan <lantw44@gmail.com> | 2016-01-02 04:18:06 +0800 |
---|---|---|
committer | Ting-Wei Lan <lantw44@gmail.com> | 2016-01-02 04:18:32 +0800 |
commit | 11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a (patch) | |
tree | 57888028d1e8e6ba0db4d2221e50c9fc39271094 /src/code-generation.c | |
parent | a3f2020bf16150c32939e894ab5fa49dafc6fc48 (diff) | |
download | compiler2015-11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a.tar.gz compiler2015-11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a.tar.zst compiler2015-11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a.zip |
Generate code for expressions and assignments
Diffstat (limited to 'src/code-generation.c')
-rw-r--r-- | src/code-generation.c | 387 |
1 files changed, 385 insertions, 2 deletions
diff --git a/src/code-generation.c b/src/code-generation.c index d569dd3..9d6bd7e 100644 --- a/src/code-generation.c +++ b/src/code-generation.c @@ -82,6 +82,354 @@ static inline bool safe_immediate(uint64_t imm) { return imm <= 4096; } +static inline void load_variable(CcmmcAst *id, CcmmcState *state, const char *r) { + fprintf(state->asm_output, "\t/* var load, line %zu */\n", id->line_number); + const char *var_name = id->value_id.name; + CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_name); + // TODO: array + if (ccmmc_symbol_attr_is_global(&var_sym->attr)) { + fprintf(state->asm_output, + "\tldr\t%s, %s\n", r, var_name); + } else { + if (safe_immediate(var_sym->attr.addr)) { + fprintf(state->asm_output, + "\tldr\t%s, [fp, #-%" PRIu64 "]\n", r, var_sym->attr.addr); + } else { + // TODO: large offset + assert(false); + } + } +} + +static inline void store_variable(CcmmcAst *id, CcmmcState *state, const char *r) { + fprintf(state->asm_output, "\t/* var store, line %zu */\n", id->line_number); + const char *var_name = id->value_id.name; + CcmmcSymbol *var_sym = ccmmc_symbol_table_retrieve(state->table, var_name); + // TODO: array + if (ccmmc_symbol_attr_is_global(&var_sym->attr)) { + fprintf(state->asm_output, + "\tstr\t%s, %s\n", r, var_name); + } else { + if (safe_immediate(var_sym->attr.addr)) { + fprintf(state->asm_output, + "\tstr\t%s, [fp, #-%" PRIu64 "]\n", r, var_sym->attr.addr); + } else { + // TODO: large offset + assert(false); + } + } +} + +static void generate_expression(CcmmcAst *expr, CcmmcState *state, + const char *result, const char *op1, const char *op2) +{ + if (expr->type_node == CCMMC_AST_NODE_CONST_VALUE) { + fprintf(state->asm_output, + "\t/* const value, line %zu */\n", expr->line_number); + if (expr->type_value == CCMMC_AST_VALUE_INT) { + fprintf(state->asm_output, + "\tldr\t%s, =%d\n", result, 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" + ".LC%zu:\n" + "\t.float\t%.9g\n" + "\t.text\n", + result, state->label_number, + state->label_number, + expr->value_const.const_float); + state->label_number++; + } else { + assert(false); + } + return; + } + + if (expr->type_node == CCMMC_AST_NODE_STMT && + expr->value_stmt.kind == CCMMC_KIND_STMT_FUNCTION_CALL) { + // TODO: function call + assert(false); + } + + if (expr->type_node == CCMMC_AST_NODE_ID) { + load_variable(expr, state, result); + 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, op1, op2, result); + if (expr->value_expr.op_binary == CCMMC_KIND_OP_BINARY_AND) { + // logical AND needs short-curcuit evaluation + label_short = state->label_number++; + 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, + FPREG_OP1, + label_short); + else + fprintf(state->asm_output, + "\tcbz\t%s, .LC%zu\n", + op1, label_short); + } else if (expr->value_expr.op_binary == CCMMC_KIND_OP_BINARY_OR) { + // logical OR needs short-curcuit evaluation + label_short = state->label_number++; + 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, + FPREG_OP1, + label_short); + else + fprintf(state->asm_output, + "\tcbnz\t%s, .LC%zu\n", + op1, label_short); + } + + // evaluate the right expression + fprintf(state->asm_output, + "\tsub\tsp, sp, #4 /* save left op */\n" + "\tstr\t%s, [sp] /* save left op */\n", + op1); + generate_expression(right, state, op2, result, op1); + fprintf(state->asm_output, + "\tldr\t%s, [sp] /* restore left op */\n" + "\tadd\tsp, sp, #4 /* restore left op */\n", + op1); + + if (left->type_value == CCMMC_AST_VALUE_FLOAT || + right->type_value == CCMMC_AST_VALUE_FLOAT) { + if (left->type_value == CCMMC_AST_VALUE_INT) + fprintf(state->asm_output, "\tscvtf\t%s, %s\n", FPREG_OP1, op1); + else if (left->type_value == CCMMC_AST_VALUE_FLOAT) + fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP1, op1); + else + assert(false); + if (right->type_value == CCMMC_AST_VALUE_INT) + fprintf(state->asm_output, "\tscvtf\t%s, %s\n", FPREG_OP2, op2); + else if (right->type_value == CCMMC_AST_VALUE_FLOAT) + fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP2, op2); + else + assert(false); + } + + 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, op1, op2); + 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, op1, op2); + 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, op1, op2); + 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, "\tdiv\t%s, %s, %s\n", + result, op1, op2); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, eq\n", result); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, ge\n", result); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, le\n", result); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, ne\n", result); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, gt\n", result); + 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, op2); + fprintf(state->asm_output, "\tcset\t%s, lt\n", result); + 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, + label_exit, + label_short, + result, + 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, label_short, + result, + label_exit, + label_short, + result, + 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, + label_exit, + label_short, + result, + 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, label_short, + result, + label_exit, + label_short, + result, + label_exit); + } break; + default: + assert(false); + } + + if (expr->type_value == CCMMC_AST_VALUE_FLOAT) + fprintf(state->asm_output, "\tfmov\t%s, %s\n", result, FPREG_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, op1, op2, result); + + if (arg->type_value == CCMMC_AST_VALUE_FLOAT) + fprintf(state->asm_output, "\tfmov\t%s, %s\n", FPREG_OP1, 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, op1); + else + fprintf(state->asm_output, "\tneg\t%s, %s\n", result, op1); + 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); + fprintf(state->asm_output, "\tcset\t%s, eq\n", result); + break; + default: + assert(false); + } + + if (expr->type_value == CCMMC_AST_VALUE_FLOAT) + fprintf(state->asm_output, "\tfmov\t%s, %s\n", result, FPREG_RESULT); + return; + } + +#undef FPREG_RESULT +#undef FPREG_OP1 +#undef FPREG_OP2 + + assert(false); +} + static void generate_block( CcmmcAst *block, CcmmcState *state, uint64_t current_offset); static void generate_statement( @@ -102,8 +450,42 @@ static void generate_statement( break; case CCMMC_KIND_STMT_FOR: break; - case CCMMC_KIND_STMT_ASSIGN: - break; + case CCMMC_KIND_STMT_ASSIGN: { +#define FPREG_TMP "s16" + CcmmcTmp *tmp1 = ccmmc_register_alloc(state->reg_pool, ¤t_offset); + CcmmcTmp *tmp2 = ccmmc_register_alloc(state->reg_pool, ¤t_offset); + CcmmcTmp *tmp3 = ccmmc_register_alloc(state->reg_pool, ¤t_offset); + const char *result = ccmmc_register_lock(state->reg_pool, tmp1); + const char *op1 = ccmmc_register_lock(state->reg_pool, tmp2); + const char *op2 = ccmmc_register_lock(state->reg_pool, tmp3); + CcmmcAst *lvar = stmt->child; + CcmmcAst *expr = stmt->child->right_sibling; + generate_expression(expr, state, result, op1, op2); + if (lvar->type_value == CCMMC_AST_VALUE_INT && + expr->type_value == CCMMC_AST_VALUE_FLOAT) { + fprintf(state->asm_output, + "\tfmov\t%s, %s\n" + "\tfcvtas\t%s, %s\n", + FPREG_TMP, result, + result, FPREG_TMP); + } else if ( + lvar->type_value == CCMMC_AST_VALUE_FLOAT && + expr->type_value == CCMMC_AST_VALUE_INT) { + fprintf(state->asm_output, + "\tscvtf\t%s, %s\n" + "\tfmov\t%s, %s\n", + FPREG_TMP, result, + result, FPREG_TMP); + } + store_variable(lvar, state, result); + ccmmc_register_unlock(state->reg_pool, tmp1); + ccmmc_register_unlock(state->reg_pool, tmp2); + ccmmc_register_unlock(state->reg_pool, tmp3); + ccmmc_register_free(state->reg_pool, tmp1, ¤t_offset); + ccmmc_register_free(state->reg_pool, tmp2, ¤t_offset); + ccmmc_register_free(state->reg_pool, tmp3, ¤t_offset); +#undef FPREG_TMP + } break; case CCMMC_KIND_STMT_IF: generate_statement(stmt->child->right_sibling, state, current_offset); @@ -144,6 +526,7 @@ static uint64_t generate_local_variable( case CCMMC_KIND_ID_WITH_INIT: { current_offset += 4; var_sym->attr.addr = current_offset; + // TODO: initializer } break; default: assert(false); |