summaryrefslogtreecommitdiffstats
path: root/src/code-generation.c
diff options
context:
space:
mode:
authorTing-Wei Lan <lantw44@gmail.com>2016-01-02 04:18:06 +0800
committerTing-Wei Lan <lantw44@gmail.com>2016-01-02 04:18:32 +0800
commit11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a (patch)
tree57888028d1e8e6ba0db4d2221e50c9fc39271094 /src/code-generation.c
parenta3f2020bf16150c32939e894ab5fa49dafc6fc48 (diff)
downloadcompiler2015-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.c387
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, &current_offset);
+ CcmmcTmp *tmp2 = ccmmc_register_alloc(state->reg_pool, &current_offset);
+ CcmmcTmp *tmp3 = ccmmc_register_alloc(state->reg_pool, &current_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, &current_offset);
+ ccmmc_register_free(state->reg_pool, tmp2, &current_offset);
+ ccmmc_register_free(state->reg_pool, tmp3, &current_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);