[jit] Don't allocate memory for unused vreg
[mono.git] / mono / mini / local-propagation.c
index 3c76408f3e44ce31d0e4869c3e51aec8ba7549be..0282810f86210fae8fbd1153c45548b46279a4b6 100644 (file)
@@ -9,8 +9,11 @@
  *   Massimiliano Mantione (massi@ximian.com)
  *
  * (C) 2006 Novell, Inc.  http://www.novell.com
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
  */
 
+#include <config.h>
+#ifndef DISABLE_JIT
 
 #include <string.h>
 #include <stdio.h>
@@ -38,6 +41,119 @@ mono_bitset_mp_new_noinit (MonoMemPool *mp,  guint32 max_size)
        return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
 }
 
+/*
+ * Replaces ins with optimized opcodes.
+ * Returns TRUE if additional vregs were allocated.
+ */
+static gboolean
+mono_strength_reduction_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, const char **spec)
+{
+       gboolean allocated_vregs = FALSE;
+
+       /* FIXME: Add long/float */
+       switch (ins->opcode) {
+       case OP_MOVE:
+       case OP_XMOVE:
+               if (ins->dreg == ins->sreg1) {
+                       MONO_DELETE_INS (bb, ins);
+               }
+               break;
+       case OP_ADD_IMM:
+       case OP_IADD_IMM:
+       case OP_SUB_IMM:
+       case OP_ISUB_IMM:
+#if SIZEOF_REGISTER == 8
+       case OP_LADD_IMM:
+       case OP_LSUB_IMM:
+#endif
+               if (ins->inst_imm == 0) {
+                       ins->opcode = OP_MOVE;
+               }
+               break;
+       case OP_MUL_IMM:
+       case OP_IMUL_IMM:
+#if SIZEOF_REGISTER == 8
+       case OP_LMUL_IMM:
+#endif
+               if (ins->inst_imm == 0) {
+                       ins->opcode = (ins->opcode == OP_LMUL_IMM) ? OP_I8CONST : OP_ICONST;
+                       ins->inst_c0 = 0;
+                       ins->sreg1 = -1;
+               } else if (ins->inst_imm == 1) {
+                       ins->opcode = OP_MOVE;
+               } else if ((ins->opcode == OP_IMUL_IMM) && (ins->inst_imm == -1)) {
+                       ins->opcode = OP_INEG;
+               } else if ((ins->opcode == OP_LMUL_IMM) && (ins->inst_imm == -1)) {
+                       ins->opcode = OP_LNEG;
+               } else {
+                       int power2 = mono_is_power_of_two (ins->inst_imm);
+                       if (power2 >= 0) {
+                               ins->opcode = (ins->opcode == OP_MUL_IMM) ? OP_SHL_IMM : ((ins->opcode == OP_LMUL_IMM) ? OP_LSHL_IMM : OP_ISHL_IMM);
+                               ins->inst_imm = power2;
+                       }
+               }
+               break;
+       case OP_IREM_UN_IMM:
+       case OP_IDIV_UN_IMM: {
+               int c = ins->inst_imm;
+               int power2 = mono_is_power_of_two (c);
+
+               if (power2 >= 0) {
+                       if (ins->opcode == OP_IREM_UN_IMM) {
+                               ins->opcode = OP_IAND_IMM;
+                               ins->sreg2 = -1;
+                               ins->inst_imm = (1 << power2) - 1;
+                       } else if (ins->opcode == OP_IDIV_UN_IMM) {
+                               ins->opcode = OP_ISHR_UN_IMM;
+                               ins->sreg2 = -1;
+                               ins->inst_imm = power2;
+                       }
+               }
+               break;
+       }
+       case OP_IDIV_IMM: {
+               int c = ins->inst_imm;
+               int power2 = mono_is_power_of_two (c);
+               MonoInst *tmp1, *tmp2, *tmp3, *tmp4;
+
+               /* FIXME: Move this elsewhere cause its hard to implement it here */
+               if (power2 == 1) {
+                       int r1 = mono_alloc_ireg (cfg);
+
+                       NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_UN_IMM, r1, ins->sreg1, 31);
+                       mono_bblock_insert_after_ins (bb, ins, tmp1);
+                       NEW_BIALU (cfg, tmp2, OP_IADD, r1, r1, ins->sreg1);
+                       mono_bblock_insert_after_ins (bb, tmp1, tmp2);
+                       NEW_BIALU_IMM (cfg, tmp3, OP_ISHR_IMM, ins->dreg, r1, 1);
+                       mono_bblock_insert_after_ins (bb, tmp2, tmp3);
+
+                       NULLIFY_INS (ins);
+                       allocated_vregs = TRUE;
+               } else if (power2 > 0 && power2 < 31) {
+                       int r1 = mono_alloc_ireg (cfg);
+
+                       NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_IMM, r1, ins->sreg1, 31);
+                       mono_bblock_insert_after_ins (bb, ins, tmp1);
+                       NEW_BIALU_IMM (cfg, tmp2, OP_ISHR_UN_IMM, r1, r1, (32 - power2));
+                       mono_bblock_insert_after_ins (bb, tmp1, tmp2);
+                       NEW_BIALU (cfg, tmp3, OP_IADD, r1, r1, ins->sreg1);
+                       mono_bblock_insert_after_ins (bb, tmp2, tmp3);
+                       NEW_BIALU_IMM (cfg, tmp4, OP_ISHR_IMM, ins->dreg, r1, power2);
+                       mono_bblock_insert_after_ins (bb, tmp3, tmp4);
+
+                       NULLIFY_INS (ins);
+                       allocated_vregs = TRUE;
+               }
+               break;
+       }
+       default:
+               break;
+       }
+
+       *spec = INS_INFO (ins->opcode);
+       return allocated_vregs;
+}
+
 /*
  * mono_local_cprop:
  *
@@ -50,12 +166,13 @@ mono_local_cprop (MonoCompile *cfg)
        MonoInst **defs;
        gint32 *def_index;
        int max;
+       int filter = FILTER_IL_SEQ_POINT;
 
 restart:
 
        max = cfg->next_vreg;
-       defs = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * (cfg->next_vreg + 1));
-       def_index = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * (cfg->next_vreg + 1));
+       defs = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * cfg->next_vreg);
+       def_index = (gint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
 
        for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
                MonoInst *ins;
@@ -103,7 +220,7 @@ restart:
 
                        /* FIXME: Optimize this */
                        if (ins->opcode == OP_LDADDR) {
-                               MonoInst *var = ins->inst_p0;
+                               MonoInst *var = (MonoInst *)ins->inst_p0;
 
                                defs [var->dreg] = NULL;
                                /*
@@ -130,9 +247,8 @@ restart:
                        num_sregs = mono_inst_get_src_registers (ins, sregs);
                        for (srcindex = 0; srcindex < num_sregs; ++srcindex) {
                                MonoInst *def;
-                               int nsregs;
 
-                               nsregs = mono_inst_get_src_registers (ins, sregs);
+                               mono_inst_get_src_registers (ins, sregs);
 
                                regtype = spec [MONO_INST_SRC1 + srcindex];
                                sreg = sregs [srcindex];
@@ -165,7 +281,7 @@ restart:
                                        !vreg_is_volatile (cfg, def->sreg1) &&
                                        /* This avoids propagating local vregs across calls */
                                        ((get_vreg_to_inst (cfg, def->sreg1) || !defs [def->sreg1] || (def_index [def->sreg1] >= last_call_index) || (def->opcode == OP_VMOVE))) &&
-                                       !(defs [def->sreg1] && defs [def->sreg1]->next == def) &&
+                                       !(defs [def->sreg1] && mono_inst_next (defs [def->sreg1], filter) == def) &&
                                        (!MONO_ARCH_USE_FPSTACK || (def->opcode != OP_FMOVE)) &&
                                        (def->opcode != OP_FMOVE)) {
                                        int vreg = def->sreg1;
@@ -253,14 +369,14 @@ restart:
                                         * We have to guarantee that def->sreg1 haven't changed since def->dreg
                                         * was defined. cfg->frame_reg is assumed to remain constant.
                                         */
-                                       if ((def->sreg1 == cfg->frame_reg) || ((def->next == ins) && (def->dreg != def->sreg1))) {
+                                       if ((def->sreg1 == cfg->frame_reg) || ((mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1))) {
                                                ins->inst_basereg = def->sreg1;
                                                ins->inst_offset += def->inst_imm;
                                        }
-                               } else if ((ins->opcode == OP_ISUB_IMM) && (def->opcode == OP_IADD_IMM) && (def->next == ins) && (def->dreg != def->sreg1)) {
+                               } else if ((ins->opcode == OP_ISUB_IMM) && (def->opcode == OP_IADD_IMM) && (mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1)) {
                                        ins->sreg1 = def->sreg1;
                                        ins->inst_imm -= def->inst_imm;
-                               } else if ((ins->opcode == OP_IADD_IMM) && (def->opcode == OP_ISUB_IMM) && (def->next == ins) && (def->dreg != def->sreg1)) {
+                               } else if ((ins->opcode == OP_IADD_IMM) && (def->opcode == OP_ISUB_IMM) && (mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1)) {
                                        ins->sreg1 = def->sreg1;
                                        ins->inst_imm -= def->inst_imm;
                                } else if (ins->opcode == OP_STOREI1_MEMBASE_REG &&
@@ -277,112 +393,9 @@ restart:
                        }
 
                        /* Do strength reduction here */
-                       /* FIXME: Add long/float */
-                       switch (ins->opcode) {
-                       case OP_MOVE:
-                       case OP_XMOVE:
-                               if (ins->dreg == ins->sreg1) {
-                                       MONO_DELETE_INS (bb, ins);
-                                       spec = INS_INFO (ins->opcode);
-                               }
-                               break;
-                       case OP_ADD_IMM:
-                       case OP_IADD_IMM:
-                       case OP_SUB_IMM:
-                       case OP_ISUB_IMM:
-#if SIZEOF_REGISTER == 8
-                       case OP_LADD_IMM:
-                       case OP_LSUB_IMM:
-#endif
-                               if (ins->inst_imm == 0) {
-                                       ins->opcode = OP_MOVE;
-                                       spec = INS_INFO (ins->opcode);
-                               }
-                               break;
-                       case OP_MUL_IMM:
-                       case OP_IMUL_IMM:
-#if SIZEOF_REGISTER == 8
-                       case OP_LMUL_IMM:
-#endif
-                               if (ins->inst_imm == 0) {
-                                       ins->opcode = (ins->opcode == OP_LMUL_IMM) ? OP_I8CONST : OP_ICONST;
-                                       ins->inst_c0 = 0;
-                                       ins->sreg1 = -1;
-                               } else if (ins->inst_imm == 1) {
-                                       ins->opcode = OP_MOVE;
-                               } else if ((ins->opcode == OP_IMUL_IMM) && (ins->inst_imm == -1)) {
-                                       ins->opcode = OP_INEG;
-                               } else if ((ins->opcode == OP_LMUL_IMM) && (ins->inst_imm == -1)) {
-                                       ins->opcode = OP_LNEG;
-                               } else {
-                                       int power2 = mono_is_power_of_two (ins->inst_imm);
-                                       if (power2 >= 0) {
-                                               ins->opcode = (ins->opcode == OP_MUL_IMM) ? OP_SHL_IMM : ((ins->opcode == OP_LMUL_IMM) ? OP_LSHL_IMM : OP_ISHL_IMM);
-                                               ins->inst_imm = power2;
-                                       }
-                               }
-                               spec = INS_INFO (ins->opcode);
-                               break;
-                       case OP_IREM_UN_IMM:
-                       case OP_IDIV_UN_IMM: {
-                               int c = ins->inst_imm;
-                               int power2 = mono_is_power_of_two (c);
-
-                               if (power2 >= 0) {
-                                       if (ins->opcode == OP_IREM_UN_IMM) {
-                                               ins->opcode = OP_IAND_IMM;
-                                               ins->sreg2 = -1;
-                                               ins->inst_imm = (1 << power2) - 1;
-                                       } else if (ins->opcode == OP_IDIV_UN_IMM) {
-                                               ins->opcode = OP_ISHR_UN_IMM;
-                                               ins->sreg2 = -1;
-                                               ins->inst_imm = power2;
-                                       }
-                               }
-                               spec = INS_INFO (ins->opcode);
-                               break;
-                       }
-                       case OP_IDIV_IMM: {
-                               int c = ins->inst_imm;
-                               int power2 = mono_is_power_of_two (c);
-                               MonoInst *tmp1, *tmp2, *tmp3, *tmp4;
-
-                               /* FIXME: Move this elsewhere cause its hard to implement it here */
-                               if (power2 == 1) {
-                                       int r1 = mono_alloc_ireg (cfg);
-
-                                       NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_UN_IMM, r1, ins->sreg1, 31);
-                                       mono_bblock_insert_after_ins (bb, ins, tmp1);
-                                       NEW_BIALU (cfg, tmp2, OP_IADD, r1, r1, ins->sreg1);
-                                       mono_bblock_insert_after_ins (bb, tmp1, tmp2);
-                                       NEW_BIALU_IMM (cfg, tmp3, OP_ISHR_IMM, ins->dreg, r1, 1);
-                                       mono_bblock_insert_after_ins (bb, tmp2, tmp3);
-
-                                       NULLIFY_INS (ins);
-
-                                       // We allocated a new vreg, so need to restart
-                                       goto restart;
-                               } else if (power2 > 0) {
-                                       int r1 = mono_alloc_ireg (cfg);
-
-                                       NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_IMM, r1, ins->sreg1, 31);
-                                       mono_bblock_insert_after_ins (bb, ins, tmp1);
-                                       NEW_BIALU_IMM (cfg, tmp2, OP_ISHR_UN_IMM, r1, r1, (32 - power2));
-                                       mono_bblock_insert_after_ins (bb, tmp1, tmp2);
-                                       NEW_BIALU (cfg, tmp3, OP_IADD, r1, r1, ins->sreg1);
-                                       mono_bblock_insert_after_ins (bb, tmp2, tmp3);
-                                       NEW_BIALU_IMM (cfg, tmp4, OP_ISHR_IMM, ins->dreg, r1, power2);
-                                       mono_bblock_insert_after_ins (bb, tmp3, tmp4);
-
-                                       NULLIFY_INS (ins);
-
-                                       // We allocated a new vreg, so need to restart
-                                       goto restart;
-                               }
-                               break;
-                       }
-                       }
-                       
+                       if (mono_strength_reduction_ins (cfg, bb, ins, &spec))
+                               goto restart;
+
                        if (spec [MONO_INST_DEST] != ' ') {
                                MonoInst *def = defs [ins->dreg];
 
@@ -429,6 +442,30 @@ reg_is_softreg (int reg, const char spec)
                || (spec == 'v');
 }
 
+static inline gboolean
+mono_is_simd_accessor (MonoInst *ins)
+{
+       switch (ins->opcode) {
+#ifdef MONO_ARCH_SIMD_INTRINSICS
+       case OP_INSERT_I1:
+       case OP_INSERT_I2:
+       case OP_INSERT_I4:
+       case OP_INSERT_I8:
+       case OP_INSERT_R4:
+       case OP_INSERT_R8:
+
+       case OP_INSERTX_U1_SLOW:
+       case OP_INSERTX_I4_SLOW:
+       case OP_INSERTX_R4_SLOW:
+       case OP_INSERTX_R8_SLOW:
+       case OP_INSERTX_I8_SLOW:
+               return TRUE;
+#endif
+       default:
+               return FALSE;
+       }
+}
+
 /**
  * mono_local_deadce:
  *
@@ -485,6 +522,7 @@ mono_local_deadce (MonoCompile *cfg)
                        const char *spec = INS_INFO (ins->opcode);
                        int sregs [MONO_MAX_SRC_REGS];
                        int num_sregs, i;
+                       MonoInst *prev_f = mono_inst_prev (ins, FILTER_NOP | FILTER_IL_SEQ_POINT);
 
                        if (ins->opcode == OP_NOP) {
                                MONO_DELETE_INS (bb, ins);
@@ -493,13 +531,11 @@ mono_local_deadce (MonoCompile *cfg)
 
                        g_assert (ins->opcode > MONO_CEE_LAST);
 
-                       if (MONO_IS_NON_FP_MOVE (ins) && ins->prev) {
+                       if (MONO_IS_NON_FP_MOVE (ins) && prev_f) {
                                MonoInst *def;
                                const char *spec2;
 
-                               def = ins->prev;
-                               while (def->prev && (def->opcode == OP_NOP))
-                                       def = def->prev;
+                               def = prev_f;
                                spec2 = INS_INFO (def->opcode);
 
                                /* 
@@ -508,7 +544,7 @@ mono_local_deadce (MonoCompile *cfg)
                                 * This isn't copyprop, not deadce, but it can only be performed
                                 * after handle_global_vregs () has run.
                                 */
-                               if (!get_vreg_to_inst (cfg, ins->sreg1) && (spec2 [MONO_INST_DEST] != ' ') && (def->dreg == ins->sreg1) && !mono_bitset_test_fast (used, ins->sreg1) && !MONO_IS_STORE_MEMBASE (def) && reg_is_softreg (ins->sreg1, spec [MONO_INST_DEST])) {
+                               if (!get_vreg_to_inst (cfg, ins->sreg1) && (spec2 [MONO_INST_DEST] != ' ') && (def->dreg == ins->sreg1) && !mono_bitset_test_fast (used, ins->sreg1) && !MONO_IS_STORE_MEMBASE (def) && reg_is_softreg (ins->sreg1, spec [MONO_INST_DEST]) && !mono_is_simd_accessor (def)) {
                                        if (cfg->verbose_level > 2) {
                                                printf ("\tReverse copyprop in BB%d on ", bb->block_num);
                                                mono_print_ins (ins);
@@ -530,8 +566,8 @@ mono_local_deadce (MonoCompile *cfg)
                                        (!get_vreg_to_inst (cfg, ins->dreg) || (!bb->extended && !vreg_is_volatile (cfg, ins->dreg) && mono_bitset_test_fast (defined, ins->dreg))) &&
                                        MONO_INS_HAS_NO_SIDE_EFFECT (ins)) {
                                        /* Happens with CMOV instructions */
-                                       if (ins->prev && ins->prev->opcode == OP_ICOMPARE_IMM) {
-                                               MonoInst *prev = ins->prev;
+                                       if (prev_f && prev_f->opcode == OP_ICOMPARE_IMM) {
+                                               MonoInst *prev = prev_f;
                                                /* 
                                                 * Can't use DELETE_INS since that would interfere with the
                                                 * FOR_EACH_INS loop.
@@ -586,3 +622,5 @@ mono_local_deadce (MonoCompile *cfg)
 
        //mono_print_code (cfg, "AFTER LOCAL-DEADCE");
 }
+
+#endif /* DISABLE_JIT */