2008-01-24 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / liveness.c
index 84ca40f8ce74f348049cba163a9f8bfede43cee5..df4c6b07a9bd06242995be7890c57927902c833b 100644 (file)
@@ -9,16 +9,26 @@
 
 #include "mini.h"
 #include "inssel.h"
+#include "aliasing.h"
+
+#define SPILL_COST_INCREMENT (1 << (bb->nesting << 1))
 
 //#define DEBUG_LIVENESS
 
-extern guint8 mono_burg_arity [];
+#if SIZEOF_VOID_P == 8
+#define BITS_PER_CHUNK 64
+#else
+#define BITS_PER_CHUNK 32
+#endif
+
+static void
+optimize_initlocals (MonoCompile *cfg);
 
 /* mono_bitset_mp_new:
  * 
  * allocates a MonoBitSet inside a memory pool
  */
-static MonoBitSet* 
+static inline MonoBitSet* 
 mono_bitset_mp_new (MonoMemPool *mp,  guint32 max_size)
 {
        int size = mono_bitset_alloc_size (max_size, 0);
@@ -28,6 +38,16 @@ mono_bitset_mp_new (MonoMemPool *mp,  guint32 max_size)
        return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
 }
 
+static inline MonoBitSet* 
+mono_bitset_mp_new_noinit (MonoMemPool *mp,  guint32 max_size)
+{
+       int size = mono_bitset_alloc_size (max_size, 0);
+       gpointer mem;
+
+       mem = mono_mempool_alloc (mp, size);
+       return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
+}
+
 G_GNUC_UNUSED static void
 mono_bitset_print (MonoBitSet *set)
 {
@@ -68,37 +88,61 @@ update_gen_kill_set (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int i
        if (arity > 1)
                update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num);
 
-       if (inst->ssa_op == MONO_SSA_LOAD) {
-               int idx = inst->inst_i0->inst_c0;
-               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
-               g_assert (idx < max_vars);
-               if ((bb->region != -1) && !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
-                       /*
-                        * Variables used in exception regions can't be allocated to 
-                        * registers.
-                        */
-                       cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+       if ((inst->ssa_op & MONO_SSA_LOAD_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+               MonoLocalVariableList* affected_variables;
+               MonoLocalVariableList local_affected_variable;
+               
+               if (cfg->aliasing_info == NULL) {
+                       if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+                               local_affected_variable.variable_index = inst->inst_i0->inst_c0;
+                               local_affected_variable.next = NULL;
+                               affected_variables = &local_affected_variable;
+                       } else {
+                               affected_variables = NULL;
+                       }
+               } else {
+                       affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
                }
-               update_live_range (cfg, idx, bb->dfn, inst_num); 
-               if (!mono_bitset_test (bb->kill_set, idx))
-                       mono_bitset_set (bb->gen_set, idx);
-               vi->spill_costs += 1 + (bb->nesting * 2);
-       } else if ((inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
-               int idx = inst->inst_i0->inst_c0;
-               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
-               g_assert (idx < max_vars);
-               if (arity > 0)
-                       g_assert (inst->inst_i1->opcode != OP_PHI);
-               if ((bb->region != -1) && !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
-                       /*
-                        * Variables used in exception regions can't be allocated to 
-                        * registers.
-                        */
-                       cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+               
+               if (inst->ssa_op & MONO_SSA_LOAD) {
+                       MonoLocalVariableList* affected_variable = affected_variables;
+                       while (affected_variable != NULL) {
+                               int idx = affected_variable->variable_index;
+                               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+                               g_assert (idx < max_vars);
+                               update_live_range (cfg, idx, bb->dfn, inst_num); 
+                               if (!mono_bitset_test_fast (bb->kill_set, idx))
+                                       mono_bitset_set_fast (bb->gen_set, idx);
+                               if (inst->ssa_op == MONO_SSA_LOAD)
+                                       vi->spill_costs += SPILL_COST_INCREMENT;
+                               
+                               affected_variable = affected_variable->next;
+                       }
+               } else if ((inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+                       MonoLocalVariableList* affected_variable = affected_variables;
+                       while (affected_variable != NULL) {
+                               int idx = affected_variable->variable_index;
+                               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+                               g_assert (idx < max_vars);
+                               //if (arity > 0)
+                                       //g_assert (inst->inst_i1->opcode != OP_PHI);
+                               update_live_range (cfg, idx, bb->dfn, inst_num); 
+                               mono_bitset_set_fast (bb->kill_set, idx);
+                               if (inst->ssa_op == MONO_SSA_STORE)
+                                       vi->spill_costs += SPILL_COST_INCREMENT;
+                               
+                               affected_variable = affected_variable->next;
+                       }
+               }
+       } else if (inst->opcode == OP_JMP) {
+               /* Keep arguments live! */
+               int i;
+               for (i = 0; i < cfg->num_varinfo; i++) {
+                       if (cfg->varinfo [i]->opcode == OP_ARG) {
+                               if (!mono_bitset_test_fast (bb->kill_set, i))
+                                       mono_bitset_set_fast (bb->gen_set, i);
+                       }
                }
-               update_live_range (cfg, idx, bb->dfn, inst_num); 
-               mono_bitset_set (bb->kill_set, idx);
-               vi->spill_costs += 1 + (bb->nesting * 2);
        }
 } 
 
@@ -114,11 +158,30 @@ update_volatile (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int inst_
        if (arity > 1)
                update_volatile (cfg, bb, inst->inst_i1, inst_num);
 
-       if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE)) {
-               int idx = inst->inst_i0->inst_c0;
-               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
-               g_assert (idx < max_vars);
-               cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+       if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
+               MonoLocalVariableList* affected_variables;
+               MonoLocalVariableList local_affected_variable;
+               
+               if (cfg->aliasing_info == NULL) {
+                       if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE)) {
+                               local_affected_variable.variable_index = inst->inst_i0->inst_c0;
+                               local_affected_variable.next = NULL;
+                               affected_variables = &local_affected_variable;
+                       } else {
+                               affected_variables = NULL;
+                       }
+               } else {
+                       affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
+               }
+               
+               while (affected_variables != NULL) {
+                       int idx = affected_variables->variable_index;
+                       MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+                       g_assert (idx < max_vars);
+                       cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+                       
+                       affected_variables = affected_variables->next;
+               }
        }
 } 
 
@@ -131,8 +194,13 @@ visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited)
        if (g_slist_find (*visited, bb))
                return;
 
-       for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) {
+       if (cfg->aliasing_info != NULL)
+               mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
+       
+       tree_num = 0;
+       MONO_BB_FOR_EACH_INS (bb, inst) {
                update_volatile (cfg, bb, inst, tree_num);
+               tree_num++;
        }
 
        *visited = g_slist_append (*visited, bb);
@@ -146,8 +214,8 @@ visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited)
        }
 }
 
-static void
-handle_exception_clauses (MonoCompile *cfg)
+void
+mono_liveness_handle_exception_clauses (MonoCompile *cfg)
 {
        MonoBasicBlock *bb;
        GSList *visited = NULL;
@@ -176,14 +244,14 @@ handle_exception_clauses (MonoCompile *cfg)
 void
 mono_analyze_liveness (MonoCompile *cfg)
 {
-       MonoBitSet *old_live_in_set, *old_live_out_set, *tmp_in_set;
-       gboolean changes;
+       MonoBitSet *old_live_out_set;
        int i, j, max_vars = cfg->num_varinfo;
-       int iterations, out_iter, in_iter;
-       gboolean *changed_in, *changed_out, *new_changed_in, *in_worklist;
+       int out_iter;
+       gboolean *in_worklist;
        MonoBasicBlock **worklist;
-       guint32 l_begin, l_end;
-       static int count = 0;
+       guint32 l_end;
+       int bitsize;
+       guint8 *mem;
 
 #ifdef DEBUG_LIVENESS
        printf ("LIVENESS %s\n", mono_method_full_name (cfg->method, TRUE));
@@ -196,13 +264,20 @@ mono_analyze_liveness (MonoCompile *cfg)
        if (max_vars == 0)
                return;
 
+       bitsize = mono_bitset_alloc_size (max_vars, 0);
+       mem = mono_mempool_alloc0 (cfg->mempool, cfg->num_bblocks * bitsize * 4);
+
        for (i = 0; i < cfg->num_bblocks; ++i) {
                MonoBasicBlock *bb = cfg->bblocks [i];
 
-               bb->gen_set = mono_bitset_mp_new (cfg->mempool, max_vars);
-               bb->kill_set = mono_bitset_mp_new (cfg->mempool, max_vars);
-               bb->live_in_set = mono_bitset_mp_new (cfg->mempool, max_vars);
-               bb->live_out_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+               bb->gen_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+               mem += bitsize;
+               bb->kill_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+               mem += bitsize;
+               /* Initialized later */
+               bb->live_in_set = NULL;
+               bb->live_out_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+               mem += bitsize;
        }
        for (i = 0; i < max_vars; i ++) {
                MONO_VARINFO (cfg, i)->range.first_use.abs_pos = ~ 0;
@@ -215,11 +290,16 @@ mono_analyze_liveness (MonoCompile *cfg)
                MonoInst *inst;
                int tree_num;
 
-               for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) {
+               if (cfg->aliasing_info != NULL)
+                       mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
+               
+               tree_num = 0;
+               MONO_BB_FOR_EACH_INS (bb, inst) {
 #ifdef DEBUG_LIVENESS
                        mono_print_tree (inst); printf ("\n");
 #endif
                        update_gen_kill_set (cfg, bb, inst, tree_num);
+                       tree_num++;
                }
 
 #ifdef DEBUG_LIVENESS
@@ -233,103 +313,120 @@ mono_analyze_liveness (MonoCompile *cfg)
 #endif
        }
 
-       old_live_in_set = mono_bitset_new (max_vars, 0);
        old_live_out_set = mono_bitset_new (max_vars, 0);
-       tmp_in_set = mono_bitset_new (max_vars, 0);
-       changed_in = g_new0 (gboolean, cfg->num_bblocks + 1);
-       changed_out = g_new0 (gboolean, cfg->num_bblocks + 1);
        in_worklist = g_new0 (gboolean, cfg->num_bblocks + 1);
-       new_changed_in = g_new0 (gboolean, cfg->num_bblocks + 1);
-
-       for (i = 0; i < cfg->num_bblocks + 1; ++i) {
-               changed_in [i] = TRUE;
-               changed_out [i] = TRUE;
-       }
 
-       count ++;
-
-       worklist = g_new0 (MonoBasicBlock *, cfg->num_bblocks + 1);
-       l_begin = 0;
+       worklist = g_new (MonoBasicBlock *, cfg->num_bblocks + 1);
        l_end = 0;
 
-       for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+       /*
+        * This is a backward dataflow analysis problem, so we process blocks in
+        * decreasing dfn order, this speeds up the iteration.
+        */
+       for (i = 0; i < cfg->num_bblocks; i ++) {
                MonoBasicBlock *bb = cfg->bblocks [i];
 
                worklist [l_end ++] = bb;
                in_worklist [bb->dfn] = TRUE;
        }
 
-       iterations = 0;
        out_iter = 0;
-       in_iter = 0;
-       do {
-               changes = FALSE;
-               iterations ++;
 
-               while (l_begin != l_end) {
-                       MonoBasicBlock *bb = worklist [l_begin ++];
+       while (l_end != 0) {
+               MonoBasicBlock *bb = worklist [--l_end];
+               MonoBasicBlock *out_bb;
+               gboolean changed;
+
+               in_worklist [bb->dfn] = FALSE;
 
-                       in_worklist [bb->dfn] = FALSE;
+#ifdef DEBUG_LIVENESS
+               printf ("P: %d(%d): IN: ", bb->block_num, bb->dfn);
+               for (j = 0; j < bb->in_count; ++j) 
+                       printf ("BB%d ", bb->in_bb [j]->block_num);
+               printf ("OUT:");
+               for (j = 0; j < bb->out_count; ++j) 
+                       printf ("BB%d ", bb->out_bb [j]->block_num);
+               printf ("\n");
+#endif
 
-                       if (l_begin == cfg->num_bblocks + 1)
-                               l_begin = 0;
+               if (bb->out_count == 0)
+                       continue;
 
-                       if (bb->out_count > 0) {
-                               out_iter ++;
-                               mono_bitset_copyto (bb->live_out_set, old_live_out_set);
+               out_iter ++;
 
-                               for (j = 0; j < bb->out_count; j++) {
-                                       MonoBasicBlock *out_bb = bb->out_bb [j];
+               if (!bb->live_in_set) {
+                       /* First pass over this bblock */
+                       changed = TRUE;
+               }
+               else {
+                       changed = FALSE;
+                       mono_bitset_copyto (bb->live_out_set, old_live_out_set);
+               }
+               for (j = 0; j < bb->out_count; j++) {
+                       out_bb = bb->out_bb [j];
 
-                                       mono_bitset_copyto (out_bb->live_out_set, tmp_in_set);
-                                       mono_bitset_sub (tmp_in_set, out_bb->kill_set);
-                                       mono_bitset_union (tmp_in_set, out_bb->gen_set);
+                       if (!out_bb->live_in_set) {
+                               out_bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+                               mem += bitsize;
 
-                                       mono_bitset_union (bb->live_out_set, tmp_in_set);
+                               mono_bitset_copyto (out_bb->live_out_set, out_bb->live_in_set);
+                               mono_bitset_sub (out_bb->live_in_set, out_bb->kill_set);
+                               mono_bitset_union (out_bb->live_in_set, out_bb->gen_set);
+                       }
 
-                               }
+                       mono_bitset_union (bb->live_out_set, out_bb->live_in_set);
+               }
                                
-                               changed_out [bb->dfn] = !mono_bitset_equal (old_live_out_set, bb->live_out_set);
-                               if (changed_out [bb->dfn]) {
-                                       for (j = 0; j < bb->in_count; j++) {
-                                               MonoBasicBlock *in_bb = bb->in_bb [j];
-                                               /* 
-                                                * Some basic blocks do not seem to be in the 
-                                                * cfg->bblocks array...
-                                                */
-                                               if (in_bb->live_in_set)
-                                                       if (!in_worklist [in_bb->dfn]) {
-                                                               worklist [l_end ++] = in_bb;
-                                                               if (l_end == cfg->num_bblocks + 1)
-                                                                       l_end = 0;
-                                                               in_worklist [in_bb->dfn] = TRUE;
-                                                       }
-                                       }
-
-                                       changes = TRUE;
+               if (changed || !mono_bitset_equal (old_live_out_set, bb->live_out_set)) {
+                       if (!bb->live_in_set) {
+                               bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+                               mem += bitsize;
+                       }
+                       mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
+                       mono_bitset_sub (bb->live_in_set, bb->kill_set);
+                       mono_bitset_union (bb->live_in_set, bb->gen_set);
+
+                       for (j = 0; j < bb->in_count; j++) {
+                               MonoBasicBlock *in_bb = bb->in_bb [j];
+                               /* 
+                                * Some basic blocks do not seem to be in the 
+                                * cfg->bblocks array...
+                                */
+                               if (in_bb->gen_set && !in_worklist [in_bb->dfn]) {
+#ifdef DEBUG_LIVENESS
+                                       printf ("\tADD: %d\n", in_bb->block_num);
+#endif
+                                       /*
+                                        * Put the block at the top of the stack, so it
+                                        * will be processed right away.
+                                        */
+                                       worklist [l_end ++] = in_bb;
+                                       in_worklist [in_bb->dfn] = TRUE;
                                }
                        }
                }
-       } while (changes);
+       }
 
        //printf ("IT: %d %d %d.\n", iterations, in_iter, out_iter);
 
-       mono_bitset_free (old_live_in_set);
        mono_bitset_free (old_live_out_set);
-       mono_bitset_free (tmp_in_set);
 
-       g_free (changed_in);
-       g_free (changed_out);
-       g_free (new_changed_in);
        g_free (worklist);
        g_free (in_worklist);
 
-       for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+       /* Compute live_in_set for bblocks skipped earlier */
+       for (i = 0; i < cfg->num_bblocks; ++i) {
                MonoBasicBlock *bb = cfg->bblocks [i];
 
-               mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
-               mono_bitset_sub (bb->live_in_set, bb->kill_set);
-               mono_bitset_union (bb->live_in_set, bb->gen_set);
+               if (!bb->live_in_set) {
+                       bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+                       mem += bitsize;
+
+                       mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
+                       mono_bitset_sub (bb->live_in_set, bb->kill_set);
+                       mono_bitset_union (bb->live_in_set, bb->gen_set);
+               }
        }
 
        /*
@@ -338,35 +435,39 @@ mono_analyze_liveness (MonoCompile *cfg)
         */
        for (i = 0; i < cfg->num_bblocks; ++i) {
                MonoBasicBlock *bb = cfg->bblocks [i];
-               guint32 rem;
+               guint32 rem, max;
+
+               if (!bb->live_out_set)
+                       continue;
 
-               rem = max_vars % 32;
-               for (j = 0; j < (max_vars / 32) + 1; ++j) {
-                       guint32 bits_in;
-                       guint32 bits_out;
-                       int k, nbits;
+               rem = max_vars % BITS_PER_CHUNK;
+               max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK);
+               for (j = 0; j < max; ++j) {
+                       gsize bits_in;
+                       gsize bits_out;
+                       int k, end;
 
-                       if (j > (max_vars / 32))
-                               break;
+                       bits_in = mono_bitset_get_fast (bb->live_in_set, j);
+                       bits_out = mono_bitset_get_fast (bb->live_out_set, j);
+
+                       if (j == max)
+                               end = (j * BITS_PER_CHUNK) + rem;
                        else
-                               if (j == (max_vars / 32))
-                                       nbits = rem;
-                               else
-                                       nbits = 32;
-
-                       bits_in = mono_bitset_test_bulk (bb->live_in_set, j * 32);
-                       bits_out = mono_bitset_test_bulk (bb->live_out_set, j * 32);
-                       for (k = 0; k < nbits; ++k) {
-                               if (bits_in & (1 << k))
-                                       update_live_range (cfg, (j * 32) + k, bb->dfn, 0);
-                               if (bits_out & (1 << k))
-                                       update_live_range (cfg, (j * 32) + k, bb->dfn, 0xffff);
+                               end = (j * BITS_PER_CHUNK) + BITS_PER_CHUNK;
+
+                       k = (j * BITS_PER_CHUNK);
+                       while ((bits_in || bits_out)) {
+                               if (bits_in & 1)
+                                       update_live_range (cfg, k, bb->dfn, 0);
+                               if (bits_out & 1)
+                                       update_live_range (cfg, k, bb->dfn, 0xffff);
+                               bits_in >>= 1;
+                               bits_out >>= 1;
+                               k ++;
                        }
                }
        }
 
-       handle_exception_clauses (cfg);
-
        /*
         * Arguments need to have their live ranges extended to the beginning of
         * the method to account for the arg reg/memory -> global register copies
@@ -389,5 +490,64 @@ mono_analyze_liveness (MonoCompile *cfg)
                mono_bitset_print (bb->live_out_set); 
        }
 #endif
+
+       optimize_initlocals (cfg);
 }
 
+static void
+update_used (MonoCompile *cfg, MonoInst *inst, MonoBitSet *used)
+{
+       int arity = mono_burg_arity [inst->opcode];
+
+       if (arity)
+               update_used (cfg, inst->inst_i0, used);
+
+       if (arity > 1)
+               update_used (cfg, inst->inst_i1, used);
+
+       if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
+               if (inst->ssa_op == MONO_SSA_LOAD) {
+                       int idx = inst->inst_i0->inst_c0;
+
+                       mono_bitset_set_fast (used, idx);
+               }
+       }
+} 
+
+/**
+ * optimize_initlocals:
+ *
+ * Try to optimize away some of the redundant initialization code inserted because of
+ * 'locals init' using the liveness information.
+ */
+static void
+optimize_initlocals (MonoCompile *cfg)
+{
+       MonoBitSet *used;
+       MonoInst *ins;
+       MonoBasicBlock *initlocals_bb;
+
+       used = mono_bitset_new (cfg->num_varinfo, 0);
+
+       mono_bitset_clear_all (used);
+       initlocals_bb = cfg->bb_entry->next_bb;
+       MONO_BB_FOR_EACH_INS (initlocals_bb, ins)
+               update_used (cfg, ins, used);
+
+       MONO_BB_FOR_EACH_INS (initlocals_bb, ins) {
+               if (ins->ssa_op == MONO_SSA_STORE) {
+                       int idx = ins->inst_i0->inst_c0;
+                       MonoInst *var = cfg->varinfo [idx];
+
+                       if (var && !mono_bitset_test_fast (used, idx) && !mono_bitset_test_fast (initlocals_bb->live_out_set, var->inst_c0) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
+                               if (ins->inst_i1 && ((ins->inst_i1->opcode == OP_ICONST) || (ins->inst_i1->opcode == OP_I8CONST))) {
+                                       NULLIFY_INS (ins);
+                                       ins->ssa_op = MONO_SSA_NOP;
+                                       MONO_VARINFO (cfg, var->inst_c0)->spill_costs -= 1;                                     
+                               }
+                       }
+               }
+       }
+
+       g_free (used);
+}