From 11a1e0ab1d2abb7e66030cab5ad976dd9557ca0a Mon Sep 17 00:00:00 2001
From: Ting-Wei Lan <lantw44@gmail.com>
Date: Sat, 2 Jan 2016 04:18:06 +0800
Subject: Generate code for expressions and assignments

---
 src/code-generation.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 385 insertions(+), 2 deletions(-)

(limited to 'src/code-generation.c')

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);
-- 
cgit