OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / regcprop.c
index 199ff29..ceb4635 100644 (file)
@@ -1,6 +1,6 @@
 /* Copy propagation on hard registers for the GNU compiler.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+   2010  Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -34,7 +34,7 @@
 #include "function.h"
 #include "recog.h"
 #include "flags.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "obstack.h"
 #include "timevar.h"
 #include "tree-pass.h"
    up some silly register allocation decisions made by reload.  This
    code may be obsoleted by a new register allocator.  */
 
+/* DEBUG_INSNs aren't changed right away, as doing so might extend the
+   lifetime of a register and get the DEBUG_INSN subsequently reset.
+   So they are queued instead, and updated only when the register is
+   used in some subsequent real insn before it is set.  */
+struct queued_debug_insn_change
+{
+  struct queued_debug_insn_change *next;
+  rtx insn;
+  rtx *loc;
+  rtx new_rtx;
+};
+
 /* For each register, we have a list of registers that contain the same
    value.  The OLDEST_REGNO field points to the head of the list, and
    the NEXT_REGNO field runs through the list.  The MODE field indicates
@@ -57,14 +69,18 @@ struct value_data_entry
   enum machine_mode mode;
   unsigned int oldest_regno;
   unsigned int next_regno;
+  struct queued_debug_insn_change *debug_insn_changes;
 };
 
 struct value_data
 {
   struct value_data_entry e[FIRST_PSEUDO_REGISTER];
   unsigned int max_value_regs;
+  unsigned int n_debug_insn_changes;
 };
 
+static alloc_pool debug_insn_changes_pool;
+
 static void kill_value_one_regno (unsigned, struct value_data *);
 static void kill_value_regno (unsigned, unsigned, struct value_data *);
 static void kill_value (rtx, struct value_data *);
@@ -82,7 +98,7 @@ static rtx find_oldest_value_reg (enum reg_class, rtx, struct value_data *);
 static bool replace_oldest_value_reg (rtx *, enum reg_class, rtx,
                                      struct value_data *);
 static bool replace_oldest_value_addr (rtx *, enum reg_class,
-                                      enum machine_mode, rtx,
+                                      enum machine_mode, addr_space_t, rtx,
                                       struct value_data *);
 static bool replace_oldest_value_mem (rtx, rtx, struct value_data *);
 static bool copyprop_hardreg_forward_1 (basic_block, struct value_data *);
@@ -91,6 +107,22 @@ extern void debug_value_data (struct value_data *);
 static void validate_value_data (struct value_data *);
 #endif
 
+/* Free all queued updates for DEBUG_INSNs that change some reg to
+   register REGNO.  */
+
+static void
+free_debug_insn_changes (struct value_data *vd, unsigned int regno)
+{
+  struct queued_debug_insn_change *cur, *next;
+  for (cur = vd->e[regno].debug_insn_changes; cur; cur = next)
+    {
+      next = cur->next;
+      --vd->n_debug_insn_changes;
+      pool_free (debug_insn_changes_pool, cur);
+    }
+  vd->e[regno].debug_insn_changes = NULL;
+}
+
 /* Kill register REGNO.  This involves removing it from any value
    lists, and resetting the value mode to VOIDmode.  This is only a
    helper function; it does not handle any hard registers overlapping
@@ -118,6 +150,8 @@ kill_value_one_regno (unsigned int regno, struct value_data *vd)
   vd->e[regno].mode = VOIDmode;
   vd->e[regno].oldest_regno = regno;
   vd->e[regno].next_regno = INVALID_REGNUM;
+  if (vd->e[regno].debug_insn_changes)
+    free_debug_insn_changes (vd, regno);
 
 #ifdef ENABLE_CHECKING
   validate_value_data (vd);
@@ -204,8 +238,10 @@ init_value_data (struct value_data *vd)
       vd->e[i].mode = VOIDmode;
       vd->e[i].oldest_regno = i;
       vd->e[i].next_regno = INVALID_REGNUM;
+      vd->e[i].debug_insn_changes = NULL;
     }
   vd->max_value_regs = 0;
+  vd->n_debug_insn_changes = 0;
 }
 
 /* Called through note_stores.  If X is clobbered, kill its value.  */
@@ -382,10 +418,9 @@ maybe_mode_change (enum machine_mode orig_mode, enum machine_mode copy_mode,
 
       offset = ((WORDS_BIG_ENDIAN ? wordoffset : 0)
                + (BYTES_BIG_ENDIAN ? byteoffset : 0));
-      return gen_rtx_raw_REG (new_mode,
-                             regno + subreg_regno_offset (regno, orig_mode,
-                                                          offset,
-                                                          new_mode));
+      regno += subreg_regno_offset (regno, orig_mode, offset, new_mode);
+      if (HARD_REGNO_MODE_OK (regno, new_mode))
+       return gen_rtx_raw_REG (new_mode, regno);
     }
   return NULL_RTX;
 }
@@ -421,7 +456,7 @@ find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd)
       rtx new_rtx;
 
       if (!in_hard_reg_set_p (reg_class_contents[cl], mode, i))
-       return NULL_RTX;
+       continue;
 
       new_rtx = maybe_mode_change (oldmode, vd->e[regno].mode, mode, i, regno);
       if (new_rtx)
@@ -446,6 +481,24 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn,
   rtx new_rtx = find_oldest_value_reg (cl, *loc, vd);
   if (new_rtx)
     {
+      if (DEBUG_INSN_P (insn))
+       {
+         struct queued_debug_insn_change *change;
+
+         if (dump_file)
+           fprintf (dump_file, "debug_insn %u: queued replacing reg %u with %u\n",
+                    INSN_UID (insn), REGNO (*loc), REGNO (new_rtx));
+
+         change = (struct queued_debug_insn_change *)
+                  pool_alloc (debug_insn_changes_pool);
+         change->next = vd->e[REGNO (new_rtx)].debug_insn_changes;
+         change->insn = insn;
+         change->loc = loc;
+         change->new_rtx = new_rtx;
+         vd->e[REGNO (new_rtx)].debug_insn_changes = change;
+         ++vd->n_debug_insn_changes;
+         return true;
+       }
       if (dump_file)
        fprintf (dump_file, "insn %u: replaced reg %u with %u\n",
                 INSN_UID (insn), REGNO (*loc), REGNO (new_rtx));
@@ -462,8 +515,8 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn,
 
 static bool
 replace_oldest_value_addr (rtx *loc, enum reg_class cl,
-                          enum machine_mode mode, rtx insn,
-                          struct value_data *vd)
+                          enum machine_mode mode, addr_space_t as,
+                          rtx insn, struct value_data *vd)
 {
   rtx x = *loc;
   RTX_CODE code = GET_CODE (x);
@@ -532,15 +585,15 @@ replace_oldest_value_addr (rtx *loc, enum reg_class cl,
            unsigned regno0 = REGNO (op0), regno1 = REGNO (op1);
 
            if (REGNO_OK_FOR_INDEX_P (regno1)
-               && regno_ok_for_base_p (regno0, mode, PLUS, REG))
+               && regno_ok_for_base_p (regno0, mode, as, PLUS, REG))
              index_op = 1;
            else if (REGNO_OK_FOR_INDEX_P (regno0)
-                    && regno_ok_for_base_p (regno1, mode, PLUS, REG))
+                    && regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
              index_op = 0;
-           else if (regno_ok_for_base_p (regno0, mode, PLUS, REG)
+           else if (regno_ok_for_base_p (regno0, mode, as, PLUS, REG)
                     || REGNO_OK_FOR_INDEX_P (regno1))
              index_op = 1;
-           else if (regno_ok_for_base_p (regno1, mode, PLUS, REG))
+           else if (regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
              index_op = 0;
            else
              index_op = 1;
@@ -563,13 +616,13 @@ replace_oldest_value_addr (rtx *loc, enum reg_class cl,
          }
 
        if (locI)
-         changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode,
-                                               insn, vd);
+         changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS,
+                                               mode, as, insn, vd);
        if (locB)
          changed |= replace_oldest_value_addr (locB,
-                                               base_reg_class (mode, PLUS,
+                                               base_reg_class (mode, as, PLUS,
                                                                index_code),
-                                               mode, insn, vd);
+                                               mode, as, insn, vd);
        return changed;
       }
 
@@ -595,12 +648,12 @@ replace_oldest_value_addr (rtx *loc, enum reg_class cl,
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode,
+       changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode, as,
                                              insn, vd);
       else if (fmt[i] == 'E')
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
          changed |= replace_oldest_value_addr (&XVECEXP (x, i, j), cl,
-                                               mode, insn, vd);
+                                               mode, as, insn, vd);
     }
 
   return changed;
@@ -616,10 +669,63 @@ replace_oldest_value_mem (rtx x, rtx insn, struct value_data *vd)
   if (DEBUG_INSN_P (insn))
     cl = ALL_REGS;
   else
-    cl = base_reg_class (GET_MODE (x), MEM, SCRATCH);
+    cl = base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x), MEM, SCRATCH);
 
   return replace_oldest_value_addr (&XEXP (x, 0), cl,
-                                   GET_MODE (x), insn, vd);
+                                   GET_MODE (x), MEM_ADDR_SPACE (x),
+                                   insn, vd);
+}
+
+/* Apply all queued updates for DEBUG_INSNs that change some reg to
+   register REGNO.  */
+
+static void
+apply_debug_insn_changes (struct value_data *vd, unsigned int regno)
+{
+  struct queued_debug_insn_change *change;
+  rtx last_insn = vd->e[regno].debug_insn_changes->insn;
+
+  for (change = vd->e[regno].debug_insn_changes;
+       change;
+       change = change->next)
+    {
+      if (last_insn != change->insn)
+       {
+         apply_change_group ();
+         last_insn = change->insn;
+       }
+      validate_change (change->insn, change->loc, change->new_rtx, 1);
+    }
+  apply_change_group ();
+}
+
+/* Called via for_each_rtx, for all used registers in a real
+   insn apply DEBUG_INSN changes that change registers to the
+   used register.  */
+
+static int
+cprop_find_used_regs_1 (rtx *loc, void *data)
+{
+  if (REG_P (*loc))
+    {
+      struct value_data *vd = (struct value_data *) data;
+      if (vd->e[REGNO (*loc)].debug_insn_changes)
+       {
+         apply_debug_insn_changes (vd, REGNO (*loc));
+         free_debug_insn_changes (vd, REGNO (*loc));
+       }
+    }
+  return 0;
+}
+
+/* Called via note_uses, for all used registers in a real insn
+   apply DEBUG_INSN changes that change registers to the used
+   registers.  */
+
+static void
+cprop_find_used_regs (rtx *loc, void *vd)
+{
+  for_each_rtx (loc, cprop_find_used_regs_1, vd);
 }
 
 /* Perform the forward copy propagation on basic block BB.  */
@@ -643,16 +749,10 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
          if (DEBUG_INSN_P (insn))
            {
              rtx loc = INSN_VAR_LOCATION_LOC (insn);
-             if (!VAR_LOC_UNKNOWN_P (loc)
-                 && replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
-                                               ALL_REGS, GET_MODE (loc),
-                                               insn, vd))
-               {
-                 changed = apply_change_group ();
-                 gcc_assert (changed);
-                 df_insn_rescan (insn);
-                 anything_changed = true;
-               }
+             if (!VAR_LOC_UNKNOWN_P (loc))
+               replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
+                                          ALL_REGS, GET_MODE (loc),
+                                          ADDR_SPACE_GENERIC, insn, vd);
            }
 
          if (insn == BB_END (bb))
@@ -685,6 +785,10 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
            recog_data.operand_type[i] = OP_INOUT;
        }
 
+      /* Apply changes to earlier DEBUG_INSNs if possible.  */
+      if (vd->n_debug_insn_changes)
+       note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
+
       /* For each earlyclobber operand, zap the value data.  */
       for (i = 0; i < n_ops; i++)
        if (recog_op_alt[i][alt].earlyclobber)
@@ -721,6 +825,14 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
              if (hard_regno_nregs[regno][mode]
                  > hard_regno_nregs[regno][vd->e[regno].mode])
                goto no_move_special_case;
+
+             /* And likewise, if we are narrowing on big endian the transformation
+                is also invalid.  */
+             if (hard_regno_nregs[regno][mode]
+                 < hard_regno_nregs[regno][vd->e[regno].mode]
+                 && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD
+                     ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+               goto no_move_special_case;
            }
 
          /* If the destination is also a register, try to find a source
@@ -737,6 +849,12 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                  changed = true;
                  goto did_replacement;
                }
+             /* We need to re-extract as validate_change clobbers
+                recog_data.  */
+             extract_insn (insn);
+             if (! constrain_operands (1))
+               fatal_insn_not_found (insn);
+             preprocess_constraints ();
            }
 
          /* Otherwise, try all valid registers and see if its valid.  */
@@ -759,6 +877,12 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                      changed = true;
                      goto did_replacement;
                    }
+                 /* We need to re-extract as validate_change clobbers
+                    recog_data.  */
+                 extract_insn (insn);
+                 if (! constrain_operands (1))
+                   fatal_insn_not_found (insn);
+                 preprocess_constraints ();
                }
            }
        }
@@ -790,7 +914,8 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                replaced[i]
                  = replace_oldest_value_addr (recog_data.operand_loc[i],
                                               recog_op_alt[i][alt].cl,
-                                              VOIDmode, insn, vd);
+                                              VOIDmode, ADDR_SPACE_GENERIC,
+                                              insn, vd);
              else if (REG_P (recog_data.operand[i]))
                replaced[i]
                  = replace_oldest_value_reg (recog_data.operand_loc[i],
@@ -843,8 +968,12 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
     did_replacement:
       if (changed)
        {
-         df_insn_rescan (insn);
          anything_changed = true;
+
+         /* If something changed, perhaps further changes to earlier
+            DEBUG_INSNs can be applied.  */
+         if (vd->n_debug_insn_changes)
+           note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
        }
 
       /* Clobber call-clobbered registers.  */
@@ -875,12 +1004,18 @@ copyprop_hardreg_forward (void)
   struct value_data *all_vd;
   basic_block bb;
   sbitmap visited;
+  bool analyze_called = false;
 
   all_vd = XNEWVEC (struct value_data, last_basic_block);
 
   visited = sbitmap_alloc (last_basic_block);
   sbitmap_zero (visited);
 
+  if (MAY_HAVE_DEBUG_INSNS)
+    debug_insn_changes_pool
+      = create_alloc_pool ("debug insn changes pool",
+                          sizeof (struct queued_debug_insn_change), 256);
+
   FOR_EACH_BB (bb)
     {
       SET_BIT (visited, bb->index);
@@ -892,13 +1027,57 @@ copyprop_hardreg_forward (void)
       if (single_pred_p (bb)
          && TEST_BIT (visited, single_pred (bb)->index)
          && ! (single_pred_edge (bb)->flags & (EDGE_ABNORMAL_CALL | EDGE_EH)))
-       all_vd[bb->index] = all_vd[single_pred (bb)->index];
+       {
+         all_vd[bb->index] = all_vd[single_pred (bb)->index];
+         if (all_vd[bb->index].n_debug_insn_changes)
+           {
+             unsigned int regno;
+
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               {
+                 if (all_vd[bb->index].e[regno].debug_insn_changes)
+                   {
+                     all_vd[bb->index].e[regno].debug_insn_changes = NULL;
+                     if (--all_vd[bb->index].n_debug_insn_changes == 0)
+                       break;
+                   }
+               }
+           }
+       }
       else
        init_value_data (all_vd + bb->index);
 
       copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
     }
 
+  if (MAY_HAVE_DEBUG_INSNS)
+    {
+      FOR_EACH_BB (bb)
+       if (TEST_BIT (visited, bb->index)
+           && all_vd[bb->index].n_debug_insn_changes)
+         {
+           unsigned int regno;
+           bitmap live;
+
+           if (!analyze_called)
+             {
+               df_analyze ();
+               analyze_called = true;
+             }
+           live = df_get_live_out (bb);
+           for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+             if (all_vd[bb->index].e[regno].debug_insn_changes)
+               {
+                 if (REGNO_REG_SET_P (live, regno))
+                   apply_debug_insn_changes (all_vd + bb->index, regno);
+                 if (all_vd[bb->index].n_debug_insn_changes == 0)
+                   break;
+               }
+         }
+
+      free_alloc_pool (debug_insn_changes_pool);
+    }
+
   sbitmap_free (visited);
   free (all_vd);
   return 0;
@@ -906,7 +1085,7 @@ copyprop_hardreg_forward (void)
 
 /* Dump the value chain data to stderr.  */
 
-void
+DEBUG_FUNCTION void
 debug_value_data (struct value_data *vd)
 {
   HARD_REG_SET set;
@@ -1030,6 +1209,7 @@ struct rtl_opt_pass pass_cprop_hardreg =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
+  TODO_df_finish
+  | TODO_verify_rtl_sharing            /* todo_flags_finish */
  }
 };