OSDN Git Service

* rtl.h (remove_reg_equal_equiv_notes): New prototype.
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index 8a7c914..00a996e 100644 (file)
@@ -1,6 +1,6 @@
 /* Analyze RTL for GNU compiler.
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
    Foundation, Inc.
 
 This file is part of GCC.
@@ -38,6 +38,18 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "regs.h"
 #include "function.h"
 
+/* Information about a subreg of a hard register.  */
+struct subreg_info
+{
+  /* Offset of first hard register involved in the subreg.  */
+  int offset;
+  /* Number of hard registers involved in the subreg.  */
+  int nregs;
+  /* Whether this subreg can be represented as a hard reg with the new
+     mode.  */
+  bool representable_p;
+};
+
 /* Forward declarations */
 static void set_of_1 (rtx, rtx, void *);
 static bool covers_regno_p (rtx, unsigned int);
@@ -45,6 +57,9 @@ static bool covers_regno_no_parallel_p (rtx, unsigned int);
 static int rtx_referenced_p_1 (rtx *, void *);
 static int computed_jump_p_1 (rtx);
 static void parms_set (rtx, rtx, void *);
+static void subreg_get_info (unsigned int, enum machine_mode,
+                            unsigned int, enum machine_mode,
+                            struct subreg_info *);
 
 static unsigned HOST_WIDE_INT cached_nonzero_bits (rtx, enum machine_mode,
                                                    rtx, enum machine_mode,
@@ -509,6 +524,12 @@ count_occurrences (rtx x, rtx find, int count_dest)
     case CC0:
       return 0;
 
+    case EXPR_LIST:
+      count = count_occurrences (XEXP (x, 0), find, count_dest);
+      if (XEXP (x, 1))
+       count += count_occurrences (XEXP (x, 1), find, count_dest);
+      return count;
+       
     case MEM:
       if (MEM_P (find) && rtx_equal_p (x, find))
        return 1;
@@ -1170,7 +1191,7 @@ refers_to_regno_p (unsigned int regno, unsigned int endregno, rtx x,
          unsigned int inner_regno = subreg_regno (x);
          unsigned int inner_endregno
            = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER
-                            ? hard_regno_nregs[inner_regno][GET_MODE (x)] : 1);
+                            ? subreg_nregs (x) : 1);
 
          return endregno > inner_regno && regno < inner_endregno;
        }
@@ -1260,13 +1281,15 @@ reg_overlap_mentioned_p (rtx x, rtx in)
       regno = REGNO (SUBREG_REG (x));
       if (regno < FIRST_PSEUDO_REGISTER)
        regno = subreg_regno (x);
+      endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+                         ? subreg_nregs (x) : 1);
       goto do_reg;
 
     case REG:
       regno = REGNO (x);
-    do_reg:
       endregno = regno + (regno < FIRST_PSEUDO_REGISTER
                          ? hard_regno_nregs[regno][GET_MODE (x)] : 1);
+    do_reg:
       return refers_to_regno_p (regno, endregno, in, (rtx*) 0);
 
     case MEM:
@@ -1390,6 +1413,11 @@ note_uses (rtx *pbody, void (*fun) (rtx *, void *), void *data)
        note_uses (&XVECEXP (body, 0, i), fun, data);
       return;
 
+    case SEQUENCE:
+      for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+       note_uses (&PATTERN (XVECEXP (body, 0, i)), fun, data);
+      return;
+
     case USE:
       (*fun) (&XEXP (body, 0), data);
       return;
@@ -1648,11 +1676,18 @@ find_reg_equal_equiv_note (rtx insn)
 
   if (!INSN_P (insn))
     return 0;
+
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     if (REG_NOTE_KIND (link) == REG_EQUAL
        || REG_NOTE_KIND (link) == REG_EQUIV)
       {
-       if (single_set (insn) == 0)
+       /* FIXME: We should never have REG_EQUAL/REG_EQUIV notes on
+          insns that have multiple sets.  Checking single_set to
+          make sure of this is not the proper check, as explained
+          in the comment in set_unique_reg_note.
+
+          This should be changed into an assert.  */
+       if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn))
          return 0;
        return link;
       }
@@ -1785,6 +1820,24 @@ remove_note (rtx insn, rtx note)
   gcc_unreachable ();
 }
 
+/* Remove REG_EQUAL and/or REG_EQUIV notes if INSN has such notes.  */
+
+void
+remove_reg_equal_equiv_notes (rtx insn)
+{
+  rtx *loc;
+
+  loc = &REG_NOTES (insn);
+  while (*loc)
+    {
+      enum reg_note kind = REG_NOTE_KIND (*loc);
+      if (kind == REG_EQUAL || kind == REG_EQUIV)
+       *loc = XEXP (*loc, 1);
+      else
+       loc = &XEXP (*loc, 1);
+    }
+}
+
 /* Search LISTP (an EXPR_LIST) for an entry whose first operand is NODE and
    return 1 if it is found.  A simple equality test is used to determine if
    NODE matches.  */
@@ -2915,113 +2968,45 @@ subreg_lsb (rtx x)
                       SUBREG_BYTE (x));
 }
 
-/* This function returns the regno offset of a subreg expression.
-   xregno - A regno of an inner hard subreg_reg (or what will become one).
-   xmode  - The mode of xregno.
-   offset - The byte offset.
-   ymode  - The mode of a top level SUBREG (or what may become one).
-   RETURN - The regno offset which would be used.  */
-unsigned int
-subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
-                    unsigned int offset, enum machine_mode ymode)
-{
-  int nregs_xmode, nregs_ymode, nregs_xmode_unit_int;
-  int mode_multiple, nregs_multiple;
-  int y_offset;
-  enum machine_mode xmode_unit, xmode_unit_int;
-
-  gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
-
-  if (GET_MODE_INNER (xmode) == VOIDmode)
-    xmode_unit = xmode;
-  else
-    xmode_unit = GET_MODE_INNER (xmode);
-  
-  if (FLOAT_MODE_P (xmode_unit))
-    {
-      xmode_unit_int = int_mode_for_mode (xmode_unit);
-      if (xmode_unit_int == BLKmode)
-       /* It's probably bad to be here; a port should have an integer mode
-          that's the same size as anything of which it takes a SUBREG.  */
-       xmode_unit_int = xmode_unit;
-    }
-  else
-    xmode_unit_int = xmode_unit;
-
-  nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int];
-
-  /* Adjust nregs_xmode to allow for 'holes'.  */
-  if (nregs_xmode_unit_int != hard_regno_nregs[xregno][xmode_unit])
-    nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode);
-  else
-    nregs_xmode = hard_regno_nregs[xregno][xmode];
-    
-  nregs_ymode = hard_regno_nregs[xregno][ymode];
-
-  /* If this is a big endian paradoxical subreg, which uses more actual
-     hard registers than the original register, we must return a negative
-     offset so that we find the proper highpart of the register.  */
-  if (offset == 0
-      && nregs_ymode > nregs_xmode
-      && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
-         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
-    return nregs_xmode - nregs_ymode;
-
-  if (offset == 0 || nregs_xmode == nregs_ymode)
-    return 0;
-
-  /* Size of ymode must not be greater than the size of xmode.  */
-  mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
-  gcc_assert (mode_multiple != 0);
-
-  y_offset = offset / GET_MODE_SIZE (ymode);
-  nregs_multiple =  nregs_xmode / nregs_ymode;
-  return (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
-}
-
-/* This function returns true when the offset is representable via
-   subreg_offset in the given regno.
+/* Fill in information about a subreg of a hard register.
    xregno - A regno of an inner hard subreg_reg (or what will become one).
    xmode  - The mode of xregno.
    offset - The byte offset.
    ymode  - The mode of a top level SUBREG (or what may become one).
-   RETURN - Whether the offset is representable.  */
-bool
-subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
-                              unsigned int offset, enum machine_mode ymode)
+   info   - Pointer to structure to fill in.  */
+static void
+subreg_get_info (unsigned int xregno, enum machine_mode xmode,
+                unsigned int offset, enum machine_mode ymode,
+                struct subreg_info *info)
 {
-  int nregs_xmode, nregs_ymode, nregs_xmode_unit, nregs_xmode_unit_int;
+  int nregs_xmode, nregs_ymode;
   int mode_multiple, nregs_multiple;
-  int y_offset;
-  enum machine_mode xmode_unit, xmode_unit_int;
+  int offset_adj, y_offset, y_offset_adj;
+  int regsize_xmode, regsize_ymode;
+  bool rknown;
 
   gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
 
-  if (GET_MODE_INNER (xmode) == VOIDmode)
-    xmode_unit = xmode;
-  else
-    xmode_unit = GET_MODE_INNER (xmode);
-  
-  if (FLOAT_MODE_P (xmode_unit))
-    {
-      xmode_unit_int = int_mode_for_mode (xmode_unit);
-      if (xmode_unit_int == BLKmode)
-       /* It's probably bad to be here; a port should have an integer mode
-          that's the same size as anything of which it takes a SUBREG.  */
-       xmode_unit_int = xmode_unit;
-    }
-  else
-    xmode_unit_int = xmode_unit;
-
-  nregs_xmode_unit = hard_regno_nregs[xregno][xmode_unit];
-  nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int];
+  rknown = false;
 
   /* If there are holes in a non-scalar mode in registers, we expect
      that it is made up of its units concatenated together.  */
-  if (nregs_xmode_unit != nregs_xmode_unit_int)
+  if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode))
     {
-      gcc_assert (nregs_xmode_unit * GET_MODE_NUNITS (xmode)
-                 == hard_regno_nregs[xregno][xmode]);
+      enum machine_mode xmode_unit;
+
+      nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode);
+      if (GET_MODE_INNER (xmode) == VOIDmode)
+       xmode_unit = xmode;
+      else
+       xmode_unit = GET_MODE_INNER (xmode);
+      gcc_assert (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode_unit));
+      gcc_assert (nregs_xmode
+                 == (GET_MODE_NUNITS (xmode)
+                     * HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode_unit)));
+      gcc_assert (hard_regno_nregs[xregno][xmode]
+                 == (hard_regno_nregs[xregno][xmode_unit]
+                     * GET_MODE_NUNITS (xmode)));
 
       /* You can only ask for a SUBREG of a value with holes in the middle
         if you don't cross the holes.  (Such a SUBREG should be done by
@@ -3031,15 +3016,15 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
         3 for each part, but in memory it's two 128-bit parts.  
         Padding is assumed to be at the end (not necessarily the 'high part')
         of each unit.  */
-      if (nregs_xmode_unit != nregs_xmode_unit_int
-         && (offset / GET_MODE_SIZE (xmode_unit_int) + 1 
-             < GET_MODE_NUNITS (xmode))
-         && (offset / GET_MODE_SIZE (xmode_unit_int) 
+      if ((offset / GET_MODE_SIZE (xmode_unit) + 1 
+          < GET_MODE_NUNITS (xmode))
+         && (offset / GET_MODE_SIZE (xmode_unit)
              != ((offset + GET_MODE_SIZE (ymode) - 1)
-                 / GET_MODE_SIZE (xmode_unit_int))))
-       return false;
-
-      nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode);
+                 / GET_MODE_SIZE (xmode_unit))))
+       {
+         info->representable_p = false;
+         rknown = true;
+       }
     }
   else
     nregs_xmode = hard_regno_nregs[xregno][xmode];
@@ -3047,15 +3032,64 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
   nregs_ymode = hard_regno_nregs[xregno][ymode];
 
   /* Paradoxical subregs are otherwise valid.  */
-  if (offset == 0
-      && nregs_ymode > nregs_xmode
-      && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
-         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
-    return true;
+  if (!rknown
+      && offset == 0
+      && GET_MODE_SIZE (ymode) > GET_MODE_SIZE (xmode))
+    {
+      info->representable_p = true;
+      /* If this is a big endian paradoxical subreg, which uses more
+        actual hard registers than the original register, we must
+        return a negative offset so that we find the proper highpart
+        of the register.  */
+      if (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
+         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+       info->offset = nregs_xmode - nregs_ymode;
+      else
+       info->offset = 0;
+      info->nregs = nregs_ymode;
+      return;
+    }
+
+  /* If registers store different numbers of bits in the different
+     modes, we cannot generally form this subreg.  */
+  if (!HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode)
+      && !HARD_REGNO_NREGS_HAS_PADDING (xregno, ymode)
+      && (GET_MODE_SIZE (xmode) % nregs_xmode) == 0
+      && (GET_MODE_SIZE (ymode) % nregs_ymode) == 0)
+    {
+      regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode;
+      regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode;
+      if (!rknown && regsize_xmode > regsize_ymode && nregs_ymode > 1)
+       {
+         info->representable_p = false;
+         info->nregs
+           = (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
+         info->offset = offset / regsize_xmode;
+         return;
+       }
+      if (!rknown && regsize_ymode > regsize_xmode && nregs_xmode > 1)
+       {
+         info->representable_p = false;
+         info->nregs
+           = (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
+         info->offset = offset / regsize_xmode;
+         return;
+       }
+    }
 
   /* Lowpart subregs are otherwise valid.  */
-  if (offset == subreg_lowpart_offset (ymode, xmode))
-    return true;
+  if (!rknown && offset == subreg_lowpart_offset (ymode, xmode))
+    {
+      info->representable_p = true;
+      rknown = true;
+
+      if (offset == 0 || nregs_xmode == nregs_ymode)
+       {
+         info->offset = 0;
+         info->nregs = nregs_ymode;
+         return;
+       }
+    }
 
   /* This should always pass, otherwise we don't know how to verify
      the constraint.  These conditions may be relaxed but
@@ -3066,22 +3100,61 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
   /* The XMODE value can be seen as a vector of NREGS_XMODE
      values.  The subreg must represent a lowpart of given field.
      Compute what field it is.  */
-  offset -= subreg_lowpart_offset (ymode,
-                                  mode_for_size (GET_MODE_BITSIZE (xmode)
-                                                 / nregs_xmode,
-                                                 MODE_INT, 0));
+  offset_adj = offset;
+  offset_adj -= subreg_lowpart_offset (ymode,
+                                      mode_for_size (GET_MODE_BITSIZE (xmode)
+                                                     / nregs_xmode,
+                                                     MODE_INT, 0));
 
   /* Size of ymode must not be greater than the size of xmode.  */
   mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
   gcc_assert (mode_multiple != 0);
 
   y_offset = offset / GET_MODE_SIZE (ymode);
-  nregs_multiple =  nregs_xmode / nregs_ymode;
+  y_offset_adj = offset_adj / GET_MODE_SIZE (ymode);
+  nregs_multiple = nregs_xmode / nregs_ymode;
 
-  gcc_assert ((offset % GET_MODE_SIZE (ymode)) == 0);
+  gcc_assert ((offset_adj % GET_MODE_SIZE (ymode)) == 0);
   gcc_assert ((mode_multiple % nregs_multiple) == 0);
 
-  return (!(y_offset % (mode_multiple / nregs_multiple)));
+  if (!rknown)
+    {
+      info->representable_p = (!(y_offset_adj % (mode_multiple / nregs_multiple)));
+      rknown = true;
+    }
+  info->offset = (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
+  info->nregs = nregs_ymode;
+}
+
+/* This function returns the regno offset of a subreg expression.
+   xregno - A regno of an inner hard subreg_reg (or what will become one).
+   xmode  - The mode of xregno.
+   offset - The byte offset.
+   ymode  - The mode of a top level SUBREG (or what may become one).
+   RETURN - The regno offset which would be used.  */
+unsigned int
+subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
+                    unsigned int offset, enum machine_mode ymode)
+{
+  struct subreg_info info;
+  subreg_get_info (xregno, xmode, offset, ymode, &info);
+  return info.offset;
+}
+
+/* This function returns true when the offset is representable via
+   subreg_offset in the given regno.
+   xregno - A regno of an inner hard subreg_reg (or what will become one).
+   xmode  - The mode of xregno.
+   offset - The byte offset.
+   ymode  - The mode of a top level SUBREG (or what may become one).
+   RETURN - Whether the offset is representable.  */
+bool
+subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
+                              unsigned int offset, enum machine_mode ymode)
+{
+  struct subreg_info info;
+  subreg_get_info (xregno, xmode, offset, ymode, &info);
+  return info.representable_p;
 }
 
 /* Return the final regno that a subreg expression refers to.  */
@@ -3099,6 +3172,21 @@ subreg_regno (rtx x)
   return ret;
 
 }
+
+/* Return the number of registers that a subreg expression refers
+   to.  */
+unsigned int
+subreg_nregs (rtx x)
+{
+  struct subreg_info info;
+  rtx subreg = SUBREG_REG (x);
+  int regno = REGNO (subreg);
+
+  subreg_get_info (regno, GET_MODE (subreg), SUBREG_BYTE (x), GET_MODE (x),
+                  &info);
+  return info.nregs;
+}
+
 struct parms_set_data
 {
   int nregs;