2009-12-04 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / regalloc2.c
index ad0f1e1a702da9a4501ad7ec82197192f1bce5b6..c0308e8c30303b8bf3285230d6c3bd6c9e7cbbab 100644 (file)
@@ -13,7 +13,7 @@
 #include <mono/metadata/mempool-internals.h>
 
 /* Disable for now to save space */
-#undef MONO_ARCH_ENABLE_GLOBAL_RA
+//#undef MONO_ARCH_ENABLE_GLOBAL_RA
 
 #ifdef MONO_ARCH_ENABLE_GLOBAL_RA
 
@@ -135,12 +135,7 @@ typedef struct MonoRegallocContext {
  * MACROS
  */
 
-#if SIZEOF_VOID_P == 8
-#define BITS_PER_CHUNK 64
-#else
-#define BITS_PER_CHUNK 32
-#endif
-
+#define BITS_PER_CHUNK MONO_BITSET_BITS_PER_CHUNK
 #define MONO_FIRST_VREG (MONO_MAX_IREGS+MONO_MAX_FREGS)
 
 /* 
@@ -177,23 +172,14 @@ typedef struct MonoRegallocContext {
 #define dreg_is_fp(spec)  (spec [MONO_INST_DEST] == 'f')
 #endif
 
-static inline GSList*
-g_slist_append_mempool (MonoMemPool *mp, GSList   *list,
-                                               gpointer  data)
-{
-       GSList *new_list, *last;
-
-       last = g_slist_last (list);
-       new_list = mono_mempool_alloc (mp, sizeof (GSList));
-       new_list->data = data;
-       new_list->next = NULL;
-       if (last) {
-               last->next = new_list;
-               return list;
-       } else {
-               return new_list;
-       }
-}
+/* 
+ * Get the base ins position from an ins pos.
+ * FIXME: This shouldn't be required but some parts of the code can't seem to
+ * handle use positions which have an INS_POS_DEF added.
+ */
+#define USE_POS_BASE(ins_pos) ((ins_pos) & ~(INS_POS_INTERVAL - 1))
+
+#define USE_POS_IS_DEF(ins_pos) ((ins_pos) & INS_POS_DEF)
 
 static MonoInst*
 create_move (MonoCompile *cfg, int dreg, int sreg)
@@ -761,9 +747,13 @@ update_liveness (MonoCompile *cfg, MonoRegallocContext *ctx, MonoInst *ins, int
                                }
                        }
                }
-               if (ins->opcode != OP_NOP)
+               if (ins->opcode != OP_NOP) {
                        /* Since we process instructions backwards, the list will be properly sorted */
-                       ctx->varinfo [ins->dreg].use_pos = g_slist_prepend_mempool (ctx->cfg->mempool, ctx->varinfo [ins->dreg].use_pos, GINT_TO_POINTER (inst_num));
+                       if (MONO_IS_STORE_MEMBASE (ins))
+                               ctx->varinfo [ins->dreg].use_pos = g_slist_prepend_mempool (ctx->cfg->mempool, ctx->varinfo [ins->dreg].use_pos, GINT_TO_POINTER (inst_num));
+                       else
+                               ctx->varinfo [ins->dreg].use_pos = g_slist_prepend_mempool (ctx->cfg->mempool, ctx->varinfo [ins->dreg].use_pos, GINT_TO_POINTER (inst_num + INS_POS_DEF));
+               }
 
                /* Set preferred vregs */
                if ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE)) {
@@ -1122,7 +1112,9 @@ decompose_volatile_intervals (MonoCompile *cfg, MonoRegallocContext *ctx, GList
        new_intervals = g_list_copy (intervals);
        for (l = intervals; l; l = l->next) {
                MonoRegallocInterval *current = l->data;
+               MonoLiveInterval *new;
                GSList *use_pos;
+               gboolean ends_with_def;
 
                if (!current->is_volatile)
                        continue;
@@ -1131,8 +1123,19 @@ decompose_volatile_intervals (MonoCompile *cfg, MonoRegallocContext *ctx, GList
                 * Instead of trying to split the arbitrary interval produced by the liveness
                 * analysis phase, just use one big interval.
                 */
-               current->interval = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
-               mono_linterval_add_range (cfg, current->interval, 0, (32767 << 16));
+               ends_with_def = FALSE;
+               use_pos = current->use_pos;
+               while (use_pos) {
+                       int pos = GPOINTER_TO_INT (use_pos->data);
+
+                       use_pos = use_pos->next;
+                       if (!use_pos && USE_POS_IS_DEF (pos))
+                               ends_with_def = TRUE;
+               }
+
+               new = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
+               mono_linterval_add_range (cfg, new, 0, current->interval->last_range->to + (ends_with_def ? INS_POS_INTERVAL : 0));
+               current->interval = new;
 
                LSCAN_DEBUG (printf ("R%d is volatile ", current->vreg));
                LSCAN_DEBUG (mono_linterval_print (current->interval));
@@ -1142,7 +1145,8 @@ decompose_volatile_intervals (MonoCompile *cfg, MonoRegallocContext *ctx, GList
 
                use_pos = current->use_pos;
                while (use_pos) {
-                       int pos = GPOINTER_TO_INT (use_pos->data);
+                       gboolean is_def = USE_POS_IS_DEF (GPOINTER_TO_INT (use_pos->data));
+                       int pos = USE_POS_BASE (GPOINTER_TO_INT (use_pos->data));
                        use_pos = use_pos->next;
 
                        LSCAN_DEBUG (printf ("\tUse pos: %x\n", pos));
@@ -1153,13 +1157,19 @@ decompose_volatile_intervals (MonoCompile *cfg, MonoRegallocContext *ctx, GList
                                current = current->child2;
                        }
 
+                       if (!is_def && pos == current->interval->last_range->to) {
+                               /* No need to split the last use */
+                               new_intervals = g_list_insert_sorted (new_intervals, current, compare_by_interval_start_pos_func);                              
+                               break;
+                       }
+
                        /* Split the use into its own interval */
                        split_interval (cfg, ctx, current, pos + INS_POS_INTERVAL);
                        new_intervals = g_list_insert_sorted (new_intervals, current->child1, compare_by_interval_start_pos_func);
                        current = current->child2;
 
                        /* No need to (and hard to) split between use positions at the same place */
-                       while (use_pos && GPOINTER_TO_INT (use_pos->data) == pos)
+                       while (use_pos && USE_POS_BASE (GPOINTER_TO_INT (use_pos->data)) == pos)
                                use_pos = use_pos->next;
                }
        }
@@ -1238,7 +1248,7 @@ linear_scan (MonoCompile *cfg, MonoRegallocContext *ctx)
 
                        /* Have to split at an use pos so a spill load can be inserted */
                        if (current->use_pos) {
-                               guint32 pos = GPOINTER_TO_INT (current->use_pos->data);
+                               guint32 pos = USE_POS_BASE (GPOINTER_TO_INT (current->use_pos->data));
 
                                split_interval (cfg, ctx, current, pos);
                                unhandled = g_list_remove (unhandled, current);
@@ -1395,7 +1405,7 @@ linear_scan (MonoCompile *cfg, MonoRegallocContext *ctx)
                g_assert (reg != -1);
 
                if (!(free_pos [reg] > 0 && free_pos [reg] >= current->interval->range->from) &&
-                       GPOINTER_TO_INT (current->use_pos->data) <= current->interval->range->from) {
+                       USE_POS_BASE (GPOINTER_TO_INT (current->use_pos->data)) <= current->interval->range->from) {
                        /*
                         * No register is available, and current is needed in a register right now.
                         * So free up a register by spilling an interval in active.
@@ -1420,7 +1430,7 @@ linear_scan (MonoCompile *cfg, MonoRegallocContext *ctx)
                        g_assert (to_spill);
 
                        LSCAN_DEBUG (printf ("\tNo free register found, splitting and spilling R%d\n", to_spill->vreg));
-                       split_pos = GPOINTER_TO_INT (current->use_pos->data);
+                       split_pos = USE_POS_BASE (GPOINTER_TO_INT (current->use_pos->data));
                        /* 
                         * Avoid splitting to_spill before the start of current, since
                         * its second child, which is added to unhandled would begin before 
@@ -1486,15 +1496,17 @@ linear_scan (MonoCompile *cfg, MonoRegallocContext *ctx)
 
                        unhandled = g_list_insert_sorted (unhandled, current->child2, compare_by_interval_start_pos_func);
                } else {
+                       guint32 use_pos = USE_POS_BASE (GPOINTER_TO_INT (current->use_pos->data));
+
                        /* No register is available */
-                       if (GPOINTER_TO_INT (current->use_pos->data) > current->interval->range->from) {
+                       if (use_pos > current->interval->range->from) {
                                /*
                                 * The interval is not currently needed in a register. So split it, and
                                 * spill the first part to memory, and save the second part for later
                                 * processing.
                                 */
-                               LSCAN_DEBUG (printf ("\tSplitting R%d(current) at first use pos %x, spilling the first part.\n", current->vreg, GPOINTER_TO_INT (current->use_pos->data)));
-                               split_interval (cfg, ctx, current, GPOINTER_TO_INT (current->use_pos->data));
+                               LSCAN_DEBUG (printf ("\tSplitting R%d(current) at first use pos %x, spilling the first part.\n", current->vreg, use_pos));
+                               split_interval (cfg, ctx, current, use_pos);
                                unhandled = g_list_insert_sorted (unhandled, current->child2, compare_by_interval_start_pos_func);
                        } else {
                                /* Handled previously */
@@ -2080,11 +2092,16 @@ rewrite_code (MonoCompile *cfg, MonoRegallocContext *ctx)
        MonoBasicBlock *bb;
        MonoInst *ins, *prev;
        int pos;
+       MonoInst **defs;
+
+       defs = g_new (MonoInst*, MONO_MAX_IREGS + MONO_MAX_FREGS);
 
        for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
                if (cfg->verbose_level > 1)
                        printf ("\nREGALLOC-REWRITE BLOCK %d:\n", bb->block_num);
 
+               memset (defs, 0, sizeof (MonoInst*) * (MONO_MAX_IREGS + MONO_MAX_FREGS));
+
                pos = (bb->dfn << 16);
                prev = NULL;
                MONO_BB_FOR_EACH_INS (bb, ins) {
@@ -2129,12 +2146,21 @@ rewrite_code (MonoCompile *cfg, MonoRegallocContext *ctx)
                                        ins->dreg = l->hreg;
                                        ins->sreg1 = l->hreg;
                                        ins->inst_imm = ctx->varinfo [var->dreg].offset;
+                                       defs [ins->dreg] = ins;
                                } else {
                                        ins->opcode = OP_MOVE;
                                        ins->dreg = l->hreg;
                                        ins->sreg1 = cfg->frame_reg;
+                                       defs [ins->dreg] = ins;
                                }
                                spec = INS_INFO (OP_NOP);
+
+                               /* 
+                                * We need to fold these instructions into the instructions which
+                                * use them, but we can't call mono_local_cprop () since that could
+                                * generate code which doesn't obey register constraints.
+                                * So we do it manually.
+                                */
                        }
 
                        if (spec [MONO_INST_DEST] != ' ') {
@@ -2142,16 +2168,38 @@ rewrite_code (MonoCompile *cfg, MonoRegallocContext *ctx)
                                        MonoRegallocInterval *l = child_at (&ctx->varinfo [ins->dreg], pos + INS_POS_USE);
                                        g_assert (l->hreg != -1);
                                        ins->dreg = l->hreg;
+
+                                       /* Fold the instruction computing the address */
+                                       /* FIXME: fails in generics-sharing.2.exe
+                                       def = defs [ins->dreg];
+                                       if (def && def->opcode == OP_MOVE && def->sreg1 == cfg->frame_reg) {
+                                               ins->dreg = cfg->frame_reg;
+                                       } else if (def && def->opcode == OP_ADD_IMM && def->sreg1 == cfg->frame_reg) {
+                                               ins->dreg = cfg->frame_reg;
+                                               ins->inst_destbasereg += def->inst_imm;
+                                       }
+                                       */
+                                       /*
+                                        * FIXME: Deadce the def. This is hard to do, since it could be
+                                        * accessed in other bblocks.
+                                        */
                                } else {
                                        MonoRegallocInterval *l = child_at (&ctx->varinfo [ins->dreg], pos + INS_POS_DEF);
                                        g_assert (l->hreg != -1);
                                        ins->dreg = l->hreg;
+                                       defs [ins->dreg] = NULL;
                                }
                        }
                        if (spec [MONO_INST_SRC1] != ' ') {
                                MonoRegallocInterval *l = child_at (&ctx->varinfo [ins->sreg1], pos + INS_POS_USE);
                                g_assert (l->hreg != -1);
                                ins->sreg1 = l->hreg;
+
+                               /*
+                               def = defs [ins->sreg1];
+                               if (def && def->opcode == OP_MOVE && def->sreg1 == cfg->frame_reg)
+                                       ins->sreg1 = cfg->frame_reg;
+                               */
                        }
                        if (spec [MONO_INST_SRC2] != ' ') {
                                MonoRegallocInterval *l = child_at (&ctx->varinfo [ins->sreg2], pos + INS_POS_USE);
@@ -2165,6 +2213,8 @@ rewrite_code (MonoCompile *cfg, MonoRegallocContext *ctx)
                        prev = ins;
                }
        }
+
+       g_free (defs);
 }
 
 static MonoRegallocContext*