OSDN Git Service

* gcc.c (do_spec_1): Accept numeric characters in file name
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index 98a2843..9064b00 100644 (file)
@@ -1,6 +1,6 @@
 /* Analyze RTL for C-Compiler
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -16,8 +16,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 
 #include "config.h"
@@ -58,6 +58,10 @@ static unsigned int cached_num_sign_bit_copies (rtx, enum machine_mode, rtx,
 static unsigned int num_sign_bit_copies1 (rtx, enum machine_mode, rtx,
                                           enum machine_mode, unsigned int);
 
+/* Offset of the first 'e', 'E' or 'V' operand for each rtx code, or
+   -1 if a code has no such operand.  */
+static int non_rtx_starting_operands[NUM_RTX_CODE];
+
 /* Bit flags that specify the machine subtype we are compiling for.
    Bits are tested using macros TARGET_... defined in the tm.h file
    and set by `-m...' switches.  Must be defined in rtlanal.c.  */
@@ -662,9 +666,7 @@ reg_used_between_p (rtx reg, rtx from_insn, rtx to_insn)
   for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
     if (INSN_P (insn)
        && (reg_overlap_mentioned_p (reg, PATTERN (insn))
-          || (CALL_P (insn)
-             && (find_reg_fusage (insn, USE, reg)
-                 || find_reg_fusage (insn, CLOBBER, reg)))))
+          || (CALL_P (insn) && find_reg_fusage (insn, USE, reg))))
       return 1;
   return 0;
 }
@@ -784,51 +786,6 @@ reg_set_p (rtx reg, rtx insn)
 }
 
 /* Similar to reg_set_between_p, but check all registers in X.  Return 0
-   only if none of them are modified between START and END.  Do not
-   consider non-registers one way or the other.  */
-
-int
-regs_set_between_p (rtx x, rtx start, rtx end)
-{
-  enum rtx_code code = GET_CODE (x);
-  const char *fmt;
-  int i, j;
-
-  switch (code)
-    {
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_VECTOR:
-    case CONST:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    case PC:
-    case CC0:
-      return 0;
-
-    case REG:
-      return reg_set_between_p (x, start, end);
-
-    default:
-      break;
-    }
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'e' && regs_set_between_p (XEXP (x, i), start, end))
-       return 1;
-
-      else if (fmt[i] == 'E')
-       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         if (regs_set_between_p (XVECEXP (x, i, j), start, end))
-           return 1;
-    }
-
-  return 0;
-}
-
-/* Similar to reg_set_between_p, but check all registers in X.  Return 0
    only if none of them are modified between START and END.  Return 1 if
    X contains a MEM; this routine does usememory aliasing.  */
 
@@ -858,10 +815,10 @@ modified_between_p (rtx x, rtx start, rtx end)
       return 1;
 
     case MEM:
-      if (MEM_READONLY_P (x))
-       return 0;
       if (modified_between_p (XEXP (x, 0), start, end))
        return 1;
+      if (MEM_READONLY_P (x))
+       return 0;
       for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn))
        if (memory_modified_in_insn_p (x, insn))
          return 1;
@@ -916,10 +873,10 @@ modified_in_p (rtx x, rtx insn)
       return 1;
 
     case MEM:
-      if (MEM_READONLY_P (x))
-       return 0;
       if (modified_in_p (XEXP (x, 0), insn))
        return 1;
+      if (MEM_READONLY_P (x))
+       return 0;
       if (memory_modified_in_insn_p (x, insn))
        return 1;
       return 0;
@@ -1350,8 +1307,18 @@ reg_overlap_mentioned_p (rtx x, rtx in)
 
        fmt = GET_RTX_FORMAT (GET_CODE (in));
        for (i = GET_RTX_LENGTH (GET_CODE (in)) - 1; i >= 0; i--)
-         if (fmt[i] == 'e' && reg_overlap_mentioned_p (x, XEXP (in, i)))
-           return 1;
+         if (fmt[i] == 'e')
+           {
+             if (reg_overlap_mentioned_p (x, XEXP (in, i)))
+               return 1;
+           }
+         else if (fmt[i] == 'E')
+           {
+             int j;
+             for (j = XVECLEN (in, i) - 1; j >= 0; --j)
+               if (reg_overlap_mentioned_p (x, XVECEXP (in, i, j)))
+                 return 1;
+           }
 
        return 0;
       }
@@ -2146,11 +2113,9 @@ may_trap_p (rtx x)
     case UMOD:
       if (HONOR_SNANS (GET_MODE (x)))
        return 1;
-      if (! CONSTANT_P (XEXP (x, 1))
-         || (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
-             && flag_trapping_math))
-       return 1;
-      if (XEXP (x, 1) == const0_rtx)
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       return flag_trapping_math;
+      if (!CONSTANT_P (XEXP (x, 1)) || (XEXP (x, 1) == const0_rtx))
        return 1;
       break;
 
@@ -2199,6 +2164,7 @@ may_trap_p (rtx x)
 
     case NEG:
     case ABS:
+    case SUBREG:
       /* These operations don't trap even with floating point.  */
       break;
 
@@ -2669,6 +2635,82 @@ computed_jump_p (rtx insn)
   return 0;
 }
 
+/* Optimized loop of for_each_rtx, trying to avoid useless recursive
+   calls.  Processes the subexpressions of EXP and passes them to F.  */
+static int
+for_each_rtx_1 (rtx exp, int n, rtx_function f, void *data)
+{
+  int result, i, j;
+  const char *format = GET_RTX_FORMAT (GET_CODE (exp));
+  rtx *x;
+
+  for (; format[n] != '\0'; n++)
+    {
+      switch (format[n])
+       {
+       case 'e':
+         /* Call F on X.  */
+         x = &XEXP (exp, n);
+         result = (*f) (x, data);
+         if (result == -1)
+           /* Do not traverse sub-expressions.  */
+           continue;
+         else if (result != 0)
+           /* Stop the traversal.  */
+           return result;
+       
+         if (*x == NULL_RTX)
+           /* There are no sub-expressions.  */
+           continue;
+       
+         i = non_rtx_starting_operands[GET_CODE (*x)];
+         if (i >= 0)
+           {
+             result = for_each_rtx_1 (*x, i, f, data);
+             if (result != 0)
+               return result;
+           }
+         break;
+
+       case 'V':
+       case 'E':
+         if (XVEC (exp, n) == 0)
+           continue;
+         for (j = 0; j < XVECLEN (exp, n); ++j)
+           {
+             /* Call F on X.  */
+             x = &XVECEXP (exp, n, j);
+             result = (*f) (x, data);
+             if (result == -1)
+               /* Do not traverse sub-expressions.  */
+               continue;
+             else if (result != 0)
+               /* Stop the traversal.  */
+               return result;
+       
+             if (*x == NULL_RTX)
+               /* There are no sub-expressions.  */
+               continue;
+       
+             i = non_rtx_starting_operands[GET_CODE (*x)];
+             if (i >= 0)
+               {
+                 result = for_each_rtx_1 (*x, i, f, data);
+                 if (result != 0)
+                   return result;
+               }
+           }
+         break;
+
+       default:
+         /* Nothing to do.  */
+         break;
+       }
+    }
+
+  return 0;
+}
+
 /* Traverse X via depth-first search, calling F for each
    sub-expression (including X itself).  F is also passed the DATA.
    If F returns -1, do not traverse sub-expressions, but continue
@@ -2686,8 +2728,6 @@ int
 for_each_rtx (rtx *x, rtx_function f, void *data)
 {
   int result;
-  int length;
-  const char *format;
   int i;
 
   /* Call F on X.  */
@@ -2703,43 +2743,14 @@ for_each_rtx (rtx *x, rtx_function f, void *data)
     /* There are no sub-expressions.  */
     return 0;
 
-  length = GET_RTX_LENGTH (GET_CODE (*x));
-  format = GET_RTX_FORMAT (GET_CODE (*x));
-
-  for (i = 0; i < length; ++i)
-    {
-      switch (format[i])
-       {
-       case 'e':
-         result = for_each_rtx (&XEXP (*x, i), f, data);
-         if (result != 0)
-           return result;
-         break;
-
-       case 'V':
-       case 'E':
-         if (XVEC (*x, i) != 0)
-           {
-             int j;
-             for (j = 0; j < XVECLEN (*x, i); ++j)
-               {
-                 result = for_each_rtx (&XVECEXP (*x, i, j), f, data);
-                 if (result != 0)
-                   return result;
-               }
-           }
-         break;
-
-       default:
-         /* Nothing to do.  */
-         break;
-       }
-
-    }
+  i = non_rtx_starting_operands[GET_CODE (*x)];
+  if (i < 0)
+    return 0;
 
-  return 0;
+  return for_each_rtx_1 (*x, i, f, data);
 }
 
+
 /* Searches X for any reference to REGNO, returning the rtx of the
    reference found if any.  Otherwise, returns NULL_RTX.  */
 
@@ -3031,13 +3042,37 @@ unsigned int
 subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
                     unsigned int offset, enum machine_mode ymode)
 {
-  int nregs_xmode, nregs_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);
 
-  nregs_xmode = hard_regno_nregs[xregno][xmode];
+  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
@@ -3052,7 +3087,7 @@ subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
   if (offset == 0 || nregs_xmode == nregs_ymode)
     return 0;
 
-  /* size of ymode must not be greater than the size of xmode.  */
+  /* 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);
 
@@ -3067,36 +3102,82 @@ subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
    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.  */
+   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)
 {
-  int nregs_xmode, nregs_ymode;
+  int nregs_xmode, nregs_ymode, nregs_xmode_unit, 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);
 
-  nregs_xmode = hard_regno_nregs[xregno][xmode];
+  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];
+
+  /* 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)
+    {
+      gcc_assert (nregs_xmode_unit * GET_MODE_NUNITS (xmode)
+                 == hard_regno_nregs[xregno][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
+        picking a different register class, or doing it in memory if
+        necessary.)  An example of a value with holes is XCmode on 32-bit
+        x86 with -m128bit-long-double; it's represented in 6 32-bit registers,
+        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) 
+             != ((offset + GET_MODE_SIZE (ymode) - 1)
+                 / GET_MODE_SIZE (xmode_unit_int))))
+       return false;
+
+      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];
 
-  /* Paradoxical subregs are always valid.  */
+  /* 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;
 
-  /* Lowpart subregs are always valid.  */
+  /* Lowpart subregs are otherwise valid.  */
   if (offset == subreg_lowpart_offset (ymode, xmode))
     return true;
 
-  /* This should always pass, otherwise we don't know how to verify the
-     constraint.  These conditions may be relaxed but subreg_offset would
-     need to be redesigned.  */
+  /* This should always pass, otherwise we don't know how to verify
+     the constraint.  These conditions may be relaxed but
+     subreg_regno_offset would need to be redesigned.  */
   gcc_assert ((GET_MODE_SIZE (xmode) % GET_MODE_SIZE (ymode)) == 0);
-  gcc_assert ((GET_MODE_SIZE (ymode) % nregs_ymode) == 0);
   gcc_assert ((nregs_xmode % nregs_ymode) == 0);
 
   /* The XMODE value can be seen as a vector of NREGS_XMODE
@@ -3107,7 +3188,7 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
                                                  / nregs_xmode,
                                                  MODE_INT, 0));
 
-  /* size of ymode must not be greater than the size of xmode.  */
+  /* 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);
 
@@ -3155,12 +3236,15 @@ parms_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
 }
 
 /* Look backward for first parameter to be loaded.
+   Note that loads of all parameters will not necessarily be
+   found if CSE has eliminated some of them (e.g., an argument
+   to the outer function is passed down as a parameter).
    Do not skip BOUNDARY.  */
 rtx
 find_first_parameter_load (rtx call_insn, rtx boundary)
 {
   struct parms_set_data parm;
-  rtx p, before;
+  rtx p, before, first_set;
 
   /* Since different machines initialize their parameter registers
      in different orders, assume nothing.  Collect the set of all
@@ -3182,6 +3266,7 @@ find_first_parameter_load (rtx call_insn, rtx boundary)
        parm.nregs++;
       }
   before = call_insn;
+  first_set = call_insn;
 
   /* Search backward for the first set of a register in this set.  */
   while (parm.nregs && before != boundary)
@@ -3204,9 +3289,20 @@ find_first_parameter_load (rtx call_insn, rtx boundary)
        }
 
       if (INSN_P (before))
-       note_stores (PATTERN (before), parms_set, &parm);
+       {
+         int nregs_old = parm.nregs;
+         note_stores (PATTERN (before), parms_set, &parm);
+         /* If we found something that did not set a parameter reg,
+            we're done.  Do not keep going, as that might result
+            in hoisting an insn before the setting of a pseudo
+            that is used by the hoisted insn. */
+         if (nregs_old != parm.nregs)
+           first_set = before;
+         else
+           break;
+       }
     }
-  return before;
+  return first_set;
 }
 
 /* Return true if we should avoid inserting code between INSN and preceding
@@ -3315,6 +3411,7 @@ rtx_cost (rtx x, enum rtx_code outer_code ATTRIBUTE_UNUSED)
       return 0;
 
     case SUBREG:
+      total = 0;
       /* If we can't tie these modes, make this expensive.  The larger
         the mode, the more expensive it is.  */
       if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x))))
@@ -3559,12 +3656,14 @@ nonzero_bits1 (rtx x, enum machine_mode mode, rtx known_x,
     case GE:  case GEU:  case UNGE:
     case LE:  case LEU:  case UNLE:
     case UNORDERED: case ORDERED:
-
       /* If this produces an integer result, we know which bits are set.
         Code here used to clear bits outside the mode of X, but that is
         now done above.  */
-
-      if (GET_MODE_CLASS (mode) == MODE_INT
+      /* Mind that MODE is the mode the caller wants to look at this 
+        operation in, and not the actual operation mode.  We can wind 
+        up with (subreg:DI (gt:V4HI x y)), and we don't have anything
+        that describes the results of a vector compare.  */
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
          && mode_width <= HOST_BITS_PER_WIDE_INT)
        nonzero = STORE_FLAG_VALUE;
       break;
@@ -4656,3 +4755,17 @@ get_condition (rtx jump, rtx *earliest, int allow_cc_mode, int valid_at_insn_p)
                                 allow_cc_mode, valid_at_insn_p);
 }
 
+\f
+/* Initialize non_rtx_starting_operands, which is used to speed up
+   for_each_rtx.  */
+void
+init_rtlanal (void)
+{
+  int i;
+  for (i = 0; i < NUM_RTX_CODE; i++)
+    {
+      const char *format = GET_RTX_FORMAT (i);
+      const char *first = strpbrk (format, "eEV");
+      non_rtx_starting_operands[i] = first ? first - format : -1;
+    }
+}