OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index de54788..6948851 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"
@@ -33,7 +33,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "output.h"
 #include "tm_p.h"
 #include "flags.h"
-#include "basic-block.h"
 #include "real.h"
 #include "regs.h"
 #include "function.h"
@@ -59,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.  */
@@ -220,10 +223,13 @@ rtx_varies_p (rtx x, int for_alias)
   return 0;
 }
 
-/* Return 0 if the use of X as an address in a MEM can cause a trap.  */
+/* Return nonzero if the use of X as an address in a MEM can cause a trap.
+   MODE is the mode of the MEM (not that of X) and UNALIGNED_MEMS controls
+   whether nonzero is returned for unaligned memory accesses on strict
+   alignment machines.  */
 
-int
-rtx_addr_can_trap_p (rtx x)
+static int
+rtx_addr_can_trap_p_1 (rtx x, enum machine_mode mode, bool unaligned_mems)
 {
   enum rtx_code code = GET_CODE (x);
 
@@ -249,27 +255,54 @@ rtx_addr_can_trap_p (rtx x)
       return 1;
 
     case CONST:
-      return rtx_addr_can_trap_p (XEXP (x, 0));
+      return rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems);
 
     case PLUS:
-      /* An address is assumed not to trap if it is an address that can't
-        trap plus a constant integer or it is the pic register plus a
-        constant.  */
-      return ! ((! rtx_addr_can_trap_p (XEXP (x, 0))
-                && GET_CODE (XEXP (x, 1)) == CONST_INT)
-               || (XEXP (x, 0) == pic_offset_table_rtx
-                   && CONSTANT_P (XEXP (x, 1))));
+      /* An address is assumed not to trap if:
+        - it is an address that can't trap plus a constant integer,
+          with the proper remainder modulo the mode size if we are
+          considering unaligned memory references.  */
+      if (!rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems)
+         && GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         HOST_WIDE_INT offset;
+
+         if (!STRICT_ALIGNMENT
+             || !unaligned_mems
+             || GET_MODE_SIZE (mode) == 0)
+           return 0;
+
+         offset = INTVAL (XEXP (x, 1));
+
+#ifdef SPARC_STACK_BOUNDARY_HACK
+         /* ??? The SPARC port may claim a STACK_BOUNDARY higher than
+            the real alignment of %sp.  However, when it does this, the
+            alignment of %sp+STACK_POINTER_OFFSET is STACK_BOUNDARY.  */
+         if (SPARC_STACK_BOUNDARY_HACK
+             && (XEXP (x, 0) == stack_pointer_rtx
+                 || XEXP (x, 0) == hard_frame_pointer_rtx))
+           offset -= STACK_POINTER_OFFSET;
+#endif
+
+         return offset % GET_MODE_SIZE (mode) != 0;
+       }
+
+      /* - or it is the pic register plus a constant.  */
+      if (XEXP (x, 0) == pic_offset_table_rtx && CONSTANT_P (XEXP (x, 1)))
+       return 0;
+
+      return 1;
 
     case LO_SUM:
     case PRE_MODIFY:
-      return rtx_addr_can_trap_p (XEXP (x, 1));
+      return rtx_addr_can_trap_p_1 (XEXP (x, 1), mode, unaligned_mems);
 
     case PRE_DEC:
     case PRE_INC:
     case POST_DEC:
     case POST_INC:
     case POST_MODIFY:
-      return rtx_addr_can_trap_p (XEXP (x, 0));
+      return rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems);
 
     default:
       break;
@@ -279,6 +312,14 @@ rtx_addr_can_trap_p (rtx x)
   return 1;
 }
 
+/* Return nonzero if the use of X as an address in a MEM can cause a trap.  */
+
+int
+rtx_addr_can_trap_p (rtx x)
+{
+  return rtx_addr_can_trap_p_1 (x, VOIDmode, false);
+}
+
 /* Return true if X is an address that is known to not be zero.  */
 
 bool
@@ -663,9 +704,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;
 }
@@ -785,51 +824,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.  */
 
@@ -859,10 +853,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;
@@ -917,10 +911,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;
@@ -1076,8 +1070,7 @@ set_noop_p (rtx set)
   if (MEM_P (dst) && MEM_P (src))
     return rtx_equal_p (dst, src) && !side_effects_p (dst);
 
-  if (GET_CODE (dst) == SIGN_EXTRACT
-      || GET_CODE (dst) == ZERO_EXTRACT)
+  if (GET_CODE (dst) == ZERO_EXTRACT)
     return rtx_equal_p (XEXP (dst, 0), src)
           && ! BYTES_BIG_ENDIAN && XEXP (dst, 2) == const0_rtx
           && !side_effects_p (src);
@@ -1352,8 +1345,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;
       }
@@ -1406,7 +1409,6 @@ note_stores (rtx x, void (*fun) (rtx, rtx, void *), void *data)
              && (!REG_P (SUBREG_REG (dest))
                  || REGNO (SUBREG_REG (dest)) >= FIRST_PSEUDO_REGISTER))
             || GET_CODE (dest) == ZERO_EXTRACT
-            || GET_CODE (dest) == SIGN_EXTRACT
             || GET_CODE (dest) == STRICT_LOW_PART)
        dest = XEXP (dest, 0);
 
@@ -1515,8 +1517,8 @@ note_uses (rtx *pbody, void (*fun) (rtx *, void *), void *data)
    This will be true if X is (cc0) or if X is a register and
    X dies in INSN or because INSN entirely sets X.
 
-   "Entirely set" means set directly and not through a SUBREG,
-   ZERO_EXTRACT or SIGN_EXTRACT, so no trace of the old contents remains.
+   "Entirely set" means set directly and not through a SUBREG, or
+   ZERO_EXTRACT, so no trace of the old contents remains.
    Likewise, REG_INC does not count.
 
    REG may be a hard or pseudo reg.  Renumbering is not taken into account,
@@ -2101,10 +2103,12 @@ side_effects_p (rtx x)
   return 0;
 }
 \f
-/* Return nonzero if evaluating rtx X might cause a trap.  */
+/* Return nonzero if evaluating rtx X might cause a trap.  UNALIGNED_MEMS
+   controls whether nonzero is returned for unaligned memory accesses on
+   strict alignment machines.  */
 
-int
-may_trap_p (rtx x)
+static int
+may_trap_p_1 (rtx x, bool unaligned_mems)
 {
   int i;
   enum rtx_code code;
@@ -2138,9 +2142,11 @@ may_trap_p (rtx x)
 
       /* Memory ref can trap unless it's a static var or a stack slot.  */
     case MEM:
-      if (MEM_NOTRAP_P (x))
+      if (MEM_NOTRAP_P (x)
+         && (!STRICT_ALIGNMENT || !unaligned_mems))
        return 0;
-      return rtx_addr_can_trap_p (XEXP (x, 0));
+      return
+       rtx_addr_can_trap_p_1 (XEXP (x, 0), GET_MODE (x), unaligned_mems);
 
       /* Division by a non-constant might trap.  */
     case DIV:
@@ -2149,11 +2155,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;
 
@@ -2202,6 +2206,7 @@ may_trap_p (rtx x)
 
     case NEG:
     case ABS:
+    case SUBREG:
       /* These operations don't trap even with floating point.  */
       break;
 
@@ -2217,19 +2222,73 @@ may_trap_p (rtx x)
     {
       if (fmt[i] == 'e')
        {
-         if (may_trap_p (XEXP (x, i)))
+         if (may_trap_p_1 (XEXP (x, i), unaligned_mems))
            return 1;
        }
       else if (fmt[i] == 'E')
        {
          int j;
          for (j = 0; j < XVECLEN (x, i); j++)
-           if (may_trap_p (XVECEXP (x, i, j)))
+           if (may_trap_p_1 (XVECEXP (x, i, j), unaligned_mems))
              return 1;
        }
     }
   return 0;
 }
+
+/* Return nonzero if evaluating rtx X might cause a trap.  */
+
+int
+may_trap_p (rtx x)
+{
+  return may_trap_p_1 (x, false);
+}
+
+/* Same as above, but additionally return non-zero if evaluating rtx X might
+   cause a fault.  We define a fault for the purpose of this function as a
+   erroneous execution condition that cannot be encountered during the normal
+   execution of a valid program; the typical example is an unaligned memory
+   access on a strict alignment machine.  The compiler guarantees that it
+   doesn't generate code that will fault from a valid program, but this
+   guarantee doesn't mean anything for individual instructions.  Consider
+   the following example:
+
+      struct S { int d; union { char *cp; int *ip; }; };
+
+      int foo(struct S *s)
+      {
+       if (s->d == 1)
+         return *s->ip;
+       else
+         return *s->cp;
+      }
+
+   on a strict alignment machine.  In a valid program, foo will never be
+   invoked on a structure for which d is equal to 1 and the underlying
+   unique field of the union not aligned on a 4-byte boundary, but the
+   expression *s->ip might cause a fault if considered individually.
+
+   At the RTL level, potentially problematic expressions will almost always
+   verify may_trap_p; for example, the above dereference can be emitted as
+   (mem:SI (reg:P)) and this expression is may_trap_p for a generic register.
+   However, suppose that foo is inlined in a caller that causes s->cp to
+   point to a local character variable and guarantees that s->d is not set
+   to 1; foo may have been effectively translated into pseudo-RTL as:
+
+      if ((reg:SI) == 1)
+       (set (reg:SI) (mem:SI (%fp - 7)))
+      else
+       (set (reg:QI) (mem:QI (%fp - 7)))
+
+   Now (mem:SI (%fp - 7)) is considered as not may_trap_p since it is a
+   memory reference to a stack slot, but it will certainly cause a fault
+   on a strict alignment machine.  */
+
+int
+may_trap_or_fault_p (rtx x)
+{
+  return may_trap_p_1 (x, true);
+}
 \f
 /* Return nonzero if X contains a comparison that is not either EQ or NE,
    i.e., an inequality.  */
@@ -2672,6 +2731,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
@@ -2689,8 +2824,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.  */
@@ -2706,43 +2839,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.  */
 
@@ -3034,13 +3138,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
@@ -3055,7 +3183,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);
 
@@ -3070,36 +3198,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
@@ -3110,7 +3284,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);
 
@@ -3158,12 +3332,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
@@ -3185,6 +3362,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)
@@ -3207,9 +3385,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
@@ -3318,6 +3507,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))))
@@ -3562,12 +3752,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;
@@ -4659,3 +4851,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;
+    }
+}