OSDN Git Service

* c-common.c (shadow_warning): Delete.
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index 50c436f..7f6b549 100644 (file)
@@ -1,6 +1,6 @@
 /* RTL simplification functions for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -49,19 +49,19 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #define HWI_SIGN_EXTEND(low) \
  ((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
 
-static rtx neg_const_int PARAMS ((enum machine_mode, rtx));
-static int simplify_plus_minus_op_data_cmp PARAMS ((const void *,
-                                                   const void *));
-static rtx simplify_plus_minus         PARAMS ((enum rtx_code,
-                                                enum machine_mode, rtx,
-                                                rtx, int));
+static rtx neg_const_int (enum machine_mode, rtx);
+static int simplify_plus_minus_op_data_cmp (const void *, const void *);
+static rtx simplify_plus_minus (enum rtx_code, enum machine_mode, rtx,
+                               rtx, int);
+static rtx simplify_immed_subreg (enum machine_mode, rtx, enum machine_mode,
+                                 unsigned int);
+static rtx simplify_associative_operation (enum rtx_code, enum machine_mode,
+                                          rtx, rtx);
 \f
 /* Negate a CONST_INT rtx, truncating (because a conversion from a
    maximally negative number can overflow).  */
 static rtx
-neg_const_int (mode, i)
-     enum machine_mode mode;
-     rtx i;
+neg_const_int (enum machine_mode mode, rtx i)
 {
   return gen_int_mode (- INTVAL (i), mode);
 }
@@ -71,10 +71,8 @@ neg_const_int (mode, i)
    seeing if the expression folds.  */
 
 rtx
-simplify_gen_binary (code, mode, op0, op1)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op0, op1;
+simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
+                    rtx op1)
 {
   rtx tem;
 
@@ -104,8 +102,7 @@ simplify_gen_binary (code, mode, op0, op1)
 /* If X is a MEM referencing the constant pool, return the real value.
    Otherwise return X.  */
 rtx
-avoid_constant_pool_reference (x)
-     rtx x;
+avoid_constant_pool_reference (rtx x)
 {
   rtx c, tmp, addr;
   enum machine_mode cmode;
@@ -134,7 +131,7 @@ avoid_constant_pool_reference (x)
 
   addr = XEXP (x, 0);
 
-  /* Call target hook to avoid the effects of -fpic etc...  */
+  /* Call target hook to avoid the effects of -fpic etc....  */
   addr = (*targetm.delegitimize_address) (addr);
 
   if (GET_CODE (addr) == LO_SUM)
@@ -163,11 +160,8 @@ avoid_constant_pool_reference (x)
    the specified operation.  */
 
 rtx
-simplify_gen_unary (code, mode, op, op_mode)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op;
-     enum machine_mode op_mode;
+simplify_gen_unary (enum rtx_code code, enum machine_mode mode, rtx op,
+                   enum machine_mode op_mode)
 {
   rtx tem;
 
@@ -181,10 +175,8 @@ simplify_gen_unary (code, mode, op, op_mode)
 /* Likewise for ternary operations.  */
 
 rtx
-simplify_gen_ternary (code, mode, op0_mode, op0, op1, op2)
-     enum rtx_code code;
-     enum machine_mode mode, op0_mode;
-     rtx op0, op1, op2;
+simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
+                     enum machine_mode op0_mode, rtx op0, rtx op1, rtx op2)
 {
   rtx tem;
 
@@ -201,46 +193,67 @@ simplify_gen_ternary (code, mode, op0_mode, op0, op1, op2)
   */
 
 rtx
-simplify_gen_relational (code, mode, cmp_mode, op0, op1)
-     enum rtx_code code;
-     enum machine_mode mode;
-     enum machine_mode cmp_mode;
-     rtx op0, op1;
+simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
+                        enum machine_mode cmp_mode, rtx op0, rtx op1)
 {
   rtx tem;
 
-  if ((tem = simplify_relational_operation (code, cmp_mode, op0, op1)) != 0)
-    return tem;
+  if (cmp_mode == VOIDmode)
+    cmp_mode = GET_MODE (op0);
+  if (cmp_mode == VOIDmode)
+    cmp_mode = GET_MODE (op1);
+
+  if (cmp_mode != VOIDmode)
+    {
+      tem = simplify_relational_operation (code, cmp_mode, op0, op1);
+
+      if (tem)
+       {
+#ifdef FLOAT_STORE_FLAG_VALUE
+         if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+           {
+             REAL_VALUE_TYPE val;
+             if (tem == const0_rtx)
+               return CONST0_RTX (mode);
+             if (tem != const_true_rtx)
+               abort ();
+             val = FLOAT_STORE_FLAG_VALUE (mode);
+             return CONST_DOUBLE_FROM_REAL_VALUE (val, mode);
+           }
+#endif
+         return tem;
+       }
+    }
 
   /* For the following tests, ensure const0_rtx is op1.  */
-  if (op0 == const0_rtx && swap_commutative_operands_p (op0, op1))
+  if (swap_commutative_operands_p (op0, op1)
+      || (op0 == const0_rtx && op1 != const0_rtx))
     tem = op0, op0 = op1, op1 = tem, code = swap_condition (code);
 
   /* If op0 is a compare, extract the comparison arguments from it.  */
   if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
-    op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
+    return simplify_gen_relational (code, mode, VOIDmode,
+                                   XEXP (op0, 0), XEXP (op0, 1));
 
   /* If op0 is a comparison, extract the comparison arguments form it.  */
-  if (code == NE && op1 == const0_rtx
-      && GET_RTX_CLASS (GET_CODE (op0)) == '<')
-    return op0;
-  else if (code == EQ && op1 == const0_rtx)
+  if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && op1 == const0_rtx)
     {
-      /* The following tests GET_RTX_CLASS (GET_CODE (op0)) == '<'.  */
-      enum rtx_code new = reversed_comparison_code (op0, NULL_RTX);
-      if (new != UNKNOWN)
-        {
-         code = new;
-         mode = cmp_mode;
-         op1 = XEXP (op0, 1);
-         op0 = XEXP (op0, 0);
+      if (code == NE)
+       {
+         if (GET_MODE (op0) == mode)
+           return op0;
+         return simplify_gen_relational (GET_CODE (op0), mode, VOIDmode,
+                                         XEXP (op0, 0), XEXP (op0, 1));
+       }
+      else if (code == EQ)
+       {
+         enum rtx_code new = reversed_comparison_code (op0, NULL_RTX);
+         if (new != UNKNOWN)
+           return simplify_gen_relational (new, mode, VOIDmode,
+                                           XEXP (op0, 0), XEXP (op0, 1));
         }
     }
 
-  /* Put complex operands first and constants second.  */
-  if (swap_commutative_operands_p (op0, op1))
-    tem = op0, op0 = op1, op1 = tem, code = swap_condition (code);
-
   return gen_rtx_fmt_ee (code, mode, op0, op1);
 }
 \f
@@ -248,13 +261,12 @@ simplify_gen_relational (code, mode, cmp_mode, op0, op1)
    resulting RTX.  Return a new RTX which is as simplified as possible.  */
 
 rtx
-simplify_replace_rtx (x, old, new)
-     rtx x;
-     rtx old;
-     rtx new;
+simplify_replace_rtx (rtx x, rtx old, rtx new)
 {
   enum rtx_code code = GET_CODE (x);
   enum machine_mode mode = GET_MODE (x);
+  enum machine_mode op_mode;
+  rtx op0, op1, op2;
 
   /* If X is OLD, return NEW.  Otherwise, if this is an expression, try
      to build a new expression substituting recursively.  If we can't do
@@ -266,83 +278,77 @@ simplify_replace_rtx (x, old, new)
   switch (GET_RTX_CLASS (code))
     {
     case '1':
-      {
-       enum machine_mode op_mode = GET_MODE (XEXP (x, 0));
-       rtx op = (XEXP (x, 0) == old
-                 ? new : simplify_replace_rtx (XEXP (x, 0), old, new));
-
-       return simplify_gen_unary (code, mode, op, op_mode);
-      }
+      op0 = XEXP (x, 0);
+      op_mode = GET_MODE (op0);
+      op0 = simplify_replace_rtx (op0, old, new);
+      if (op0 == XEXP (x, 0))
+       return x;
+      return simplify_gen_unary (code, mode, op0, op_mode);
 
     case '2':
     case 'c':
-      return
-       simplify_gen_binary (code, mode,
-                            simplify_replace_rtx (XEXP (x, 0), old, new),
-                            simplify_replace_rtx (XEXP (x, 1), old, new));
+      op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
+      op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
+      if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
+       return x;
+      return simplify_gen_binary (code, mode, op0, op1);
+
     case '<':
-      {
-       enum machine_mode op_mode = (GET_MODE (XEXP (x, 0)) != VOIDmode
-                                    ? GET_MODE (XEXP (x, 0))
-                                    : GET_MODE (XEXP (x, 1)));
-       rtx op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
-       rtx op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
-
-       return
-         simplify_gen_relational (code, mode,
-                                  (op_mode != VOIDmode
-                                   ? op_mode
-                                   : GET_MODE (op0) != VOIDmode
-                                   ? GET_MODE (op0)
-                                   : GET_MODE (op1)),
-                                  op0, op1);
-      }
+      op0 = XEXP (x, 0);
+      op1 = XEXP (x, 1);
+      op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1);
+      op0 = simplify_replace_rtx (op0, old, new);
+      op1 = simplify_replace_rtx (op1, old, new);
+      if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
+       return x;
+      return simplify_gen_relational (code, mode, op_mode, op0, op1);
 
     case '3':
     case 'b':
-      {
-       enum machine_mode op_mode = GET_MODE (XEXP (x, 0));
-       rtx op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
-
-       return
-         simplify_gen_ternary (code, mode,
-                               (op_mode != VOIDmode
-                                ? op_mode
-                                : GET_MODE (op0)),
-                               op0,
-                               simplify_replace_rtx (XEXP (x, 1), old, new),
-                               simplify_replace_rtx (XEXP (x, 2), old, new));
-      }
+      op0 = XEXP (x, 0);
+      op_mode = GET_MODE (op0);
+      op0 = simplify_replace_rtx (op0, old, new);
+      op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
+      op2 = simplify_replace_rtx (XEXP (x, 2), old, new);
+      if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2))
+       return x;
+      if (op_mode == VOIDmode)
+       op_mode = GET_MODE (op0);
+      return simplify_gen_ternary (code, mode, op_mode, op0, op1, op2);
 
     case 'x':
       /* The only case we try to handle is a SUBREG.  */
       if (code == SUBREG)
        {
-         rtx exp;
-         exp = simplify_gen_subreg (GET_MODE (x),
-                                    simplify_replace_rtx (SUBREG_REG (x),
-                                                          old, new),
+         op0 = simplify_replace_rtx (SUBREG_REG (x), old, new);
+         if (op0 == SUBREG_REG (x))
+           return x;
+         op0 = simplify_gen_subreg (GET_MODE (x), op0,
                                     GET_MODE (SUBREG_REG (x)),
                                     SUBREG_BYTE (x));
-         if (exp)
-          x = exp;
+         return op0 ? op0 : x;
        }
-      return x;
+      break;
 
     case 'o':
       if (code == MEM)
-       return replace_equiv_address_nv (x,
-                                        simplify_replace_rtx (XEXP (x, 0),
-                                                              old, new));
+       {
+         op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
+         if (op0 == XEXP (x, 0))
+           return x;
+         return replace_equiv_address_nv (x, op0);
+       }
       else if (code == LO_SUM)
        {
-         rtx op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
-         rtx op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
+         op0 = simplify_replace_rtx (XEXP (x, 0), old, new);
+         op1 = simplify_replace_rtx (XEXP (x, 1), old, new);
 
          /* (lo_sum (high x) x) -> x  */
          if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1))
            return op1;
 
+         if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
+           return x;
          return gen_rtx_LO_SUM (mode, op0, op1);
        }
       else if (code == REG)
@@ -350,11 +356,10 @@ simplify_replace_rtx (x, old, new)
          if (REG_P (old) && REGNO (x) == REGNO (old))
            return new;
        }
-
-      return x;
+      break;
 
     default:
-      return x;
+      break;
     }
   return x;
 }
@@ -363,11 +368,8 @@ simplify_replace_rtx (x, old, new)
    MODE with input operand OP whose mode was originally OP_MODE.
    Return zero if no simplification can be made.  */
 rtx
-simplify_unary_operation (code, mode, op, op_mode)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op;
-     enum machine_mode op_mode;
+simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
+                         rtx op, enum machine_mode op_mode)
 {
   unsigned int width = GET_MODE_BITSIZE (mode);
   rtx trueop = avoid_constant_pool_reference (op);
@@ -409,6 +411,8 @@ simplify_unary_operation (code, mode, op, op_mode)
          return gen_rtx_CONST_VECTOR (mode, v);
        }
     }
+  else if (GET_CODE (op) == CONST)
+    return simplify_unary_operation (code, mode, XEXP (op, 0), op_mode);
 
   if (VECTOR_MODE_P (mode) && GET_CODE (trueop) == CONST_VECTOR)
     {
@@ -658,24 +662,23 @@ simplify_unary_operation (code, mode, op, op_mode)
 
        case CLZ:
          hv = 0;
-         if (h1 == 0)
-           lv = GET_MODE_BITSIZE (mode) - floor_log2 (l1) - 1;
-         else
+         if (h1 != 0)
            lv = GET_MODE_BITSIZE (mode) - floor_log2 (h1) - 1
              - HOST_BITS_PER_WIDE_INT;
+         else if (l1 != 0)
+           lv = GET_MODE_BITSIZE (mode) - floor_log2 (l1) - 1;
+         else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, lv))
+           lv = GET_MODE_BITSIZE (mode);
          break;
 
        case CTZ:
          hv = 0;
-         if (l1 == 0)
-           {
-             if (h1 == 0)
-               lv = GET_MODE_BITSIZE (mode);
-             else
-               lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & -h1);
-           }
-         else
+         if (l1 != 0)
            lv = exact_log2 (l1 & -l1);
+         else if (h1 != 0)
+           lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & -h1);
+         else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, lv))
+           lv = GET_MODE_BITSIZE (mode);
          break;
 
        case POPCOUNT:
@@ -778,19 +781,99 @@ simplify_unary_operation (code, mode, op, op_mode)
   else if (GET_CODE (trueop) == CONST_DOUBLE
           && GET_MODE_CLASS (GET_MODE (trueop)) == MODE_FLOAT
           && GET_MODE_CLASS (mode) == MODE_INT
-          && width <= HOST_BITS_PER_WIDE_INT && width > 0)
+          && width <= 2*HOST_BITS_PER_WIDE_INT && width > 0)
     {
-      HOST_WIDE_INT i;
-      REAL_VALUE_TYPE d;
-      REAL_VALUE_FROM_CONST_DOUBLE (d, trueop);
+      /* Although the overflow semantics of RTL's FIX and UNSIGNED_FIX
+        operators are intentionally left unspecified (to ease implementation
+        by target backends), for consistency, this routine implements the
+        same semantics for constant folding as used by the middle-end.  */
+
+      HOST_WIDE_INT xh, xl, th, tl;
+      REAL_VALUE_TYPE x, t;
+      REAL_VALUE_FROM_CONST_DOUBLE (x, trueop);
       switch (code)
        {
-       case FIX:               i = REAL_VALUE_FIX (d);           break;
-       case UNSIGNED_FIX:      i = REAL_VALUE_UNSIGNED_FIX (d);  break;
+       case FIX:
+         if (REAL_VALUE_ISNAN (x))
+           return const0_rtx;
+
+         /* Test against the signed upper bound.  */
+         if (width > HOST_BITS_PER_WIDE_INT)
+           {
+             th = ((unsigned HOST_WIDE_INT) 1
+                   << (width - HOST_BITS_PER_WIDE_INT - 1)) - 1;
+             tl = -1;
+           }
+         else
+           {
+             th = 0;
+             tl = ((unsigned HOST_WIDE_INT) 1 << (width - 1)) - 1;
+           }
+         real_from_integer (&t, VOIDmode, tl, th, 0);
+         if (REAL_VALUES_LESS (t, x))
+           {
+             xh = th;
+             xl = tl;
+             break;
+           }
+
+         /* Test against the signed lower bound.  */
+         if (width > HOST_BITS_PER_WIDE_INT)
+           {
+             th = (HOST_WIDE_INT) -1 << (width - HOST_BITS_PER_WIDE_INT - 1);
+             tl = 0;
+           }
+         else
+           {
+             th = -1;
+             tl = (HOST_WIDE_INT) -1 << (width - 1);
+           }
+         real_from_integer (&t, VOIDmode, tl, th, 0);
+         if (REAL_VALUES_LESS (x, t))
+           {
+             xh = th;
+             xl = tl;
+             break;
+           }
+         REAL_VALUE_TO_INT (&xl, &xh, x);
+         break;
+
+       case UNSIGNED_FIX:
+         if (REAL_VALUE_ISNAN (x) || REAL_VALUE_NEGATIVE (x))
+           return const0_rtx;
+
+         /* Test against the unsigned upper bound.  */
+         if (width == 2*HOST_BITS_PER_WIDE_INT)
+           {
+             th = -1;
+             tl = -1;
+           }
+         else if (width >= HOST_BITS_PER_WIDE_INT)
+           {
+             th = ((unsigned HOST_WIDE_INT) 1
+                   << (width - HOST_BITS_PER_WIDE_INT)) - 1;
+             tl = -1;
+           }
+         else
+           {
+             th = 0;
+             tl = ((unsigned HOST_WIDE_INT) 1 << width) - 1;
+           }
+         real_from_integer (&t, VOIDmode, tl, th, 1);
+         if (REAL_VALUES_LESS (t, x))
+           {
+             xh = th;
+             xl = tl;
+             break;
+           }
+
+         REAL_VALUE_TO_INT (&xl, &xh, x);
+         break;
+
        default:
          abort ();
        }
-      return gen_int_mode (i, mode);
+      return immed_double_const (xl, xh, mode);
     }
 
   /* This was formerly used only for non-IEEE float.
@@ -798,6 +881,8 @@ simplify_unary_operation (code, mode, op, op_mode)
   else
     {
       enum rtx_code reversed;
+      rtx temp;
+
       /* There are some simplifications we can do even if the operands
         aren't constant.  */
       switch (code)
@@ -808,17 +893,131 @@ simplify_unary_operation (code, mode, op, op_mode)
            return XEXP (op, 0);
 
          /* (not (eq X Y)) == (ne X Y), etc.  */
-         if (mode == BImode && GET_RTX_CLASS (GET_CODE (op)) == '<'
+         if (GET_RTX_CLASS (GET_CODE (op)) == '<'
+             && (mode == BImode || STORE_FLAG_VALUE == -1)
              && ((reversed = reversed_comparison_code (op, NULL_RTX))
                  != UNKNOWN))
-           return gen_rtx_fmt_ee (reversed,
-                                  op_mode, XEXP (op, 0), XEXP (op, 1));
+           return simplify_gen_relational (reversed, mode, VOIDmode,
+                                           XEXP (op, 0), XEXP (op, 1));
+
+          /* (not (plus X -1)) can become (neg X).  */
+          if (GET_CODE (op) == PLUS
+             && XEXP (op, 1) == constm1_rtx)
+           return simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
+
+         /* Similarly, (not (neg X)) is (plus X -1).  */
+         if (GET_CODE (op) == NEG)
+           return plus_constant (XEXP (op, 0), -1);
+
+         /* (not (xor X C)) for C constant is (xor X D) with D = ~C.  */
+         if (GET_CODE (op) == XOR
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+             && (temp = simplify_unary_operation (NOT, mode,
+                                                  XEXP (op, 1),
+                                                  mode)) != 0)
+           return simplify_gen_binary (XOR, mode, XEXP (op, 0), temp);
+
+
+         /* (not (ashift 1 X)) is (rotate ~1 X).  We used to do this for
+            operands other than 1, but that is not valid.  We could do a
+            similar simplification for (not (lshiftrt C X)) where C is
+            just the sign bit, but this doesn't seem common enough to
+            bother with.  */
+         if (GET_CODE (op) == ASHIFT
+             && XEXP (op, 0) == const1_rtx)
+           {
+             temp = simplify_gen_unary (NOT, mode, const1_rtx, mode);
+             return simplify_gen_binary (ROTATE, mode, temp, XEXP (op, 1));
+           }
+
+         /* If STORE_FLAG_VALUE is -1, (not (comparison X Y)) can be done
+            by reversing the comparison code if valid.  */
+         if (STORE_FLAG_VALUE == -1
+             && GET_RTX_CLASS (GET_CODE (op)) == '<'
+             && (reversed = reversed_comparison_code (op, NULL_RTX))
+                != UNKNOWN)
+           return simplify_gen_relational (reversed, mode, VOIDmode,
+                                           XEXP (op, 0), XEXP (op, 1));
+
+         /* (not (ashiftrt foo C)) where C is the number of bits in FOO
+            minus 1 is (ge foo (const_int 0)) if STORE_FLAG_VALUE is -1,
+            so we can perform the above simplification.  */
+
+         if (STORE_FLAG_VALUE == -1
+             && GET_CODE (op) == ASHIFTRT
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+             && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+           return simplify_gen_relational (GE, mode, VOIDmode,
+                                           XEXP (op, 0), const0_rtx);
+
          break;
 
        case NEG:
          /* (neg (neg X)) == X.  */
          if (GET_CODE (op) == NEG)
            return XEXP (op, 0);
+
+         /* (neg (plus X 1)) can become (not X).  */
+         if (GET_CODE (op) == PLUS
+             && XEXP (op, 1) == const1_rtx)
+           return simplify_gen_unary (NOT, mode, XEXP (op, 0), mode);
+
+         /* Similarly, (neg (not X)) is (plus X 1).  */
+         if (GET_CODE (op) == NOT)
+           return plus_constant (XEXP (op, 0), 1);
+
+         /* (neg (minus X Y)) can become (minus Y X).  This transformation
+            isn't safe for modes with signed zeros, since if X and Y are
+            both +0, (minus Y X) is the same as (minus X Y).  If the
+            rounding mode is towards +infinity (or -infinity) then the two
+            expressions will be rounded differently.  */
+         if (GET_CODE (op) == MINUS
+             && !HONOR_SIGNED_ZEROS (mode)
+             && !HONOR_SIGN_DEPENDENT_ROUNDING (mode))
+           return simplify_gen_binary (MINUS, mode, XEXP (op, 1),
+                                       XEXP (op, 0));
+
+         if (GET_CODE (op) == PLUS
+             && !HONOR_SIGNED_ZEROS (mode)
+             && !HONOR_SIGN_DEPENDENT_ROUNDING (mode))
+           {
+             /* (neg (plus A C)) is simplified to (minus -C A).  */
+             if (GET_CODE (XEXP (op, 1)) == CONST_INT
+                 || GET_CODE (XEXP (op, 1)) == CONST_DOUBLE)
+               {
+                 temp = simplify_unary_operation (NEG, mode, XEXP (op, 1),
+                                                  mode);
+                 if (temp)
+                   return simplify_gen_binary (MINUS, mode, temp,
+                                               XEXP (op, 0));
+               }
+
+             /* (neg (plus A B)) is canonicalized to (minus (neg A) B).  */
+             temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
+             return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 1));
+           }
+
+         /* (neg (mult A B)) becomes (mult (neg A) B).
+            This works even for floating-point values.  */
+         if (GET_CODE (op) == MULT
+             && !HONOR_SIGN_DEPENDENT_ROUNDING (mode))
+           {
+             temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
+             return simplify_gen_binary (MULT, mode, temp, XEXP (op, 1));
+           }
+
+         /* NEG commutes with ASHIFT since it is multiplication.  Only do
+            this if we can then eliminate the NEG (e.g., if the operand
+            is a constant).  */
+         if (GET_CODE (op) == ASHIFT)
+           {
+             temp = simplify_unary_operation (NEG, mode, XEXP (op, 0),
+                                              mode);
+             if (temp)
+               return simplify_gen_binary (ASHIFT, mode, temp,
+                                           XEXP (op, 1));
+           }
+
          break;
 
        case SIGN_EXTEND:
@@ -833,6 +1032,15 @@ simplify_unary_operation (code, mode, op, op_mode)
              && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
            return XEXP (op, 0);
 
+         /* Check for a sign extension of a subreg of a promoted
+            variable, where the promotion is sign-extended, and the
+            target mode is the same as the variable's promotion.  */
+         if (GET_CODE (op) == SUBREG
+             && SUBREG_PROMOTED_VAR_P (op)
+             && ! SUBREG_PROMOTED_UNSIGNED_P (op)
+             && GET_MODE (XEXP (op, 0)) == mode)
+           return XEXP (op, 0);
+
 #if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
          if (! POINTERS_EXTEND_UNSIGNED
              && mode == Pmode && GET_MODE (op) == ptr_mode
@@ -845,8 +1053,17 @@ simplify_unary_operation (code, mode, op, op_mode)
 #endif
          break;
 
-#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
        case ZERO_EXTEND:
+         /* Check for a zero extension of a subreg of a promoted
+            variable, where the promotion is zero-extended, and the
+            target mode is the same as the variable's promotion.  */
+         if (GET_CODE (op) == SUBREG
+             && SUBREG_PROMOTED_VAR_P (op)
+             && SUBREG_PROMOTED_UNSIGNED_P (op)
+             && GET_MODE (XEXP (op, 0)) == mode)
+           return XEXP (op, 0);
+
+#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
          if (POINTERS_EXTEND_UNSIGNED > 0
              && mode == Pmode && GET_MODE (op) == ptr_mode
              && (CONSTANT_P (op)
@@ -855,8 +1072,8 @@ simplify_unary_operation (code, mode, op, op_mode)
                      && REG_POINTER (SUBREG_REG (op))
                      && GET_MODE (SUBREG_REG (op)) == Pmode)))
            return convert_memory_address (Pmode, op);
-         break;
 #endif
+         break;
 
        default:
          break;
@@ -866,23 +1083,78 @@ simplify_unary_operation (code, mode, op, op_mode)
     }
 }
 \f
+/* Subroutine of simplify_binary_operation to simplify a commutative,
+   associative binary operation CODE with result mode MODE, operating
+   on OP0 and OP1.  CODE is currently one of PLUS, MULT, AND, IOR, XOR,
+   SMIN, SMAX, UMIN or UMAX.  Return zero if no simplification or
+   canonicalization is possible.  */
+
+static rtx
+simplify_associative_operation (enum rtx_code code, enum machine_mode mode,
+                               rtx op0, rtx op1)
+{
+  rtx tem;
+
+  /* Linearize the operator to the left.  */
+  if (GET_CODE (op1) == code)
+    {
+      /* "(a op b) op (c op d)" becomes "((a op b) op c) op d)".  */
+      if (GET_CODE (op0) == code)
+       {
+         tem = simplify_gen_binary (code, mode, op0, XEXP (op1, 0));
+         return simplify_gen_binary (code, mode, tem, XEXP (op1, 1));
+       }
+
+      /* "a op (b op c)" becomes "(b op c) op a".  */
+      if (! swap_commutative_operands_p (op1, op0))
+       return simplify_gen_binary (code, mode, op1, op0);
+
+      tem = op0;
+      op0 = op1;
+      op1 = tem;
+    }
+
+  if (GET_CODE (op0) == code)
+    {
+      /* Canonicalize "(x op c) op y" as "(x op y) op c".  */
+      if (swap_commutative_operands_p (XEXP (op0, 1), op1))
+       {
+         tem = simplify_gen_binary (code, mode, XEXP (op0, 0), op1);
+         return simplify_gen_binary (code, mode, tem, XEXP (op0, 1));
+       }
+
+      /* Attempt to simplify "(a op b) op c" as "a op (b op c)".  */
+      tem = swap_commutative_operands_p (XEXP (op0, 1), op1)
+           ? simplify_binary_operation (code, mode, op1, XEXP (op0, 1))
+           : simplify_binary_operation (code, mode, XEXP (op0, 1), op1);
+      if (tem != 0)
+        return simplify_gen_binary (code, mode, XEXP (op0, 0), tem);
+
+      /* Attempt to simplify "(a op b) op c" as "(a op c) op b".  */
+      tem = swap_commutative_operands_p (XEXP (op0, 0), op1)
+           ? simplify_binary_operation (code, mode, op1, XEXP (op0, 0))
+           : simplify_binary_operation (code, mode, XEXP (op0, 0), op1);
+      if (tem != 0)
+        return simplify_gen_binary (code, mode, tem, XEXP (op0, 1));
+    }
+
+  return 0;
+}
+
 /* Simplify a binary operation CODE with result mode MODE, operating on OP0
    and OP1.  Return 0 if no simplification is possible.
 
    Don't use this for relational operations such as EQ or LT.
    Use simplify_relational_operation instead.  */
 rtx
-simplify_binary_operation (code, mode, op0, op1)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op0, op1;
+simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
+                          rtx op0, rtx op1)
 {
   HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
   HOST_WIDE_INT val;
   unsigned int width = GET_MODE_BITSIZE (mode);
+  rtx trueop0, trueop1;
   rtx tem;
-  rtx trueop0 = avoid_constant_pool_reference (op0);
-  rtx trueop1 = avoid_constant_pool_reference (op1);
 
   /* Relational operations don't work here.  We must know the mode
      of the operands in order to do the comparison correctly.
@@ -894,12 +1166,14 @@ simplify_binary_operation (code, mode, op0, op1)
 
   /* Make sure the constant is second.  */
   if (GET_RTX_CLASS (code) == 'c'
-      && swap_commutative_operands_p (trueop0, trueop1))
+      && swap_commutative_operands_p (op0, op1))
     {
       tem = op0, op0 = op1, op1 = tem;
-      tem = trueop0, trueop0 = trueop1, trueop1 = tem;
     }
 
+  trueop0 = avoid_constant_pool_reference (op0);
+  trueop1 = avoid_constant_pool_reference (op1);
+
   if (VECTOR_MODE_P (mode)
       && GET_CODE (trueop0) == CONST_VECTOR
       && GET_CODE (trueop1) == CONST_VECTOR)
@@ -943,9 +1217,13 @@ simplify_binary_operation (code, mode, op0, op1)
       f0 = real_value_truncate (mode, f0);
       f1 = real_value_truncate (mode, f1);
 
+      if (HONOR_SNANS (mode)
+         && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
+       return 0;
+
       if (code == DIV
-         && !MODE_HAS_INFINITIES (mode)
-         && REAL_VALUES_EQUAL (f1, dconst0))
+         && REAL_VALUES_EQUAL (f1, dconst0)
+         && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
        return 0;
 
       REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
@@ -982,7 +1260,7 @@ simplify_binary_operation (code, mode, op0, op1)
          neg_double (l2, h2, &lv, &hv);
          l2 = lv, h2 = hv;
 
-         /* .. fall through ...  */
+         /* Fall through....  */
 
        case PLUS:
          add_double (l1, h1, l2, h2, &lv, &hv);
@@ -1105,7 +1383,7 @@ simplify_binary_operation (code, mode, op0, op1)
          if (INTEGRAL_MODE_P (mode)
              && GET_CODE (op0) == NOT
              && trueop1 == const1_rtx)
-           return gen_rtx_NEG (mode, XEXP (op0, 0));
+           return simplify_gen_unary (NEG, mode, XEXP (op0, 0), mode);
 
          /* Handle both-operands-constant cases.  We can only add
             CONST_INTs to constants since the sum of relocatable symbols
@@ -1189,6 +1467,16 @@ simplify_binary_operation (code, mode, op0, op1)
                      && GET_CODE (XEXP (op1, 0)) == PLUS))
              && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0)
            return tem;
+
+         /* Reassociate floating point addition only when the user
+            specifies unsafe math optimizations.  */
+         if (FLOAT_MODE_P (mode)
+             && flag_unsafe_math_optimizations)
+           {
+             tem = simplify_associative_operation (code, mode, op0, op1);
+             if (tem)
+               return tem;
+           }
          break;
 
        case COMPARE:
@@ -1240,11 +1528,11 @@ simplify_binary_operation (code, mode, op0, op1)
             But if the mode has signed zeros, and does not round towards
             -infinity, then 0 - 0 is 0, not -0.  */
          if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode))
-           return gen_rtx_NEG (mode, op1);
+           return simplify_gen_unary (NEG, mode, op1, mode);
 
          /* (-1 - a) is ~a.  */
          if (trueop0 == constm1_rtx)
-           return gen_rtx_NOT (mode, op1);
+           return simplify_gen_unary (NOT, mode, op1, mode);
 
          /* Subtracting 0 has no effect unless the mode has signed zeros
             and supports rounding towards -infinity.  In such a case,
@@ -1312,6 +1600,16 @@ simplify_binary_operation (code, mode, op0, op1)
          if (GET_CODE (op1) == NEG)
            return simplify_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
 
+         /* (-x - c) may be simplified as (-c - x).  */
+         if (GET_CODE (op0) == NEG
+             && (GET_CODE (op1) == CONST_INT
+                 || GET_CODE (op1) == CONST_DOUBLE))
+           {
+             tem = simplify_unary_operation (NEG, mode, op1, mode);
+             if (tem)
+               return simplify_gen_binary (MINUS, mode, tem, XEXP (op0, 0));
+           }
+
          /* If one of the operands is a PLUS or a MINUS, see if we can
             simplify this by the associative law.
             Don't use the associative law for floating point.
@@ -1337,28 +1635,24 @@ simplify_binary_operation (code, mode, op0, op1)
          /* (x - (x & y)) -> (x & ~y) */
          if (GET_CODE (op1) == AND)
            {
-            if (rtx_equal_p (op0, XEXP (op1, 0)))
-              {
-                tem = simplify_gen_unary (NOT, mode, XEXP (op1, 1),
-                                          GET_MODE (XEXP (op1, 1)));
-                return simplify_gen_binary (AND, mode, op0, tem);
-              }
-            if (rtx_equal_p (op0, XEXP (op1, 1)))
-              {
-                tem = simplify_gen_unary (NOT, mode, XEXP (op1, 0),
-                                          GET_MODE (XEXP (op1, 0)));
-                return simplify_gen_binary (AND, mode, op0, tem);
-              }
-          }
+             if (rtx_equal_p (op0, XEXP (op1, 0)))
+               {
+                 tem = simplify_gen_unary (NOT, mode, XEXP (op1, 1),
+                                           GET_MODE (XEXP (op1, 1)));
+                 return simplify_gen_binary (AND, mode, op0, tem);
+               }
+             if (rtx_equal_p (op0, XEXP (op1, 1)))
+               {
+                 tem = simplify_gen_unary (NOT, mode, XEXP (op1, 0),
+                                           GET_MODE (XEXP (op1, 0)));
+                 return simplify_gen_binary (AND, mode, op0, tem);
+               }
+           }
          break;
 
        case MULT:
          if (trueop1 == constm1_rtx)
-           {
-             tem = simplify_unary_operation (NEG, mode, op0, mode);
-
-             return tem ? tem : gen_rtx_NEG (mode, op0);
-           }
+           return simplify_gen_unary (NEG, mode, op0, mode);
 
          /* Maybe simplify x * 0 to 0.  The reduction is not valid if
             x is NaN, since x * 0 is then also NaN.  Nor is it valid
@@ -1386,7 +1680,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && (width <= HOST_BITS_PER_WIDE_INT
                  || val != HOST_BITS_PER_WIDE_INT - 1)
              && ! rtx_equal_function_value_matters)
-           return gen_rtx_ASHIFT (mode, op0, GEN_INT (val));
+           return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
 
          /* x*2 is x+x and x*(-1) is -x */
          if (GET_CODE (trueop1) == CONST_DOUBLE
@@ -1397,10 +1691,20 @@ simplify_binary_operation (code, mode, op0, op1)
              REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
 
              if (REAL_VALUES_EQUAL (d, dconst2))
-               return gen_rtx_PLUS (mode, op0, copy_rtx (op0));
+               return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0));
 
              if (REAL_VALUES_EQUAL (d, dconstm1))
-               return gen_rtx_NEG (mode, op0);
+               return simplify_gen_unary (NEG, mode, op0, mode);
+           }
+
+         /* Reassociate multiplication, but for floating point MULTs
+            only when the user specifies unsafe math optimizations.  */
+         if (! FLOAT_MODE_P (mode)
+             || flag_unsafe_math_optimizations)
+           {
+             tem = simplify_associative_operation (code, mode, op0, op1);
+             if (tem)
+               return tem;
            }
          break;
 
@@ -1419,6 +1723,9 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! side_effects_p (op0)
              && GET_MODE_CLASS (mode) != MODE_CC)
            return constm1_rtx;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case XOR:
@@ -1427,10 +1734,13 @@ simplify_binary_operation (code, mode, op0, op1)
          if (GET_CODE (trueop1) == CONST_INT
              && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
                  == GET_MODE_MASK (mode)))
-           return gen_rtx_NOT (mode, op0);
+           return simplify_gen_unary (NOT, mode, op0, mode);
          if (trueop0 == trueop1 && ! side_effects_p (op0)
              && GET_MODE_CLASS (mode) != MODE_CC)
            return const0_rtx;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case AND:
@@ -1449,6 +1759,9 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! side_effects_p (op0)
              && GET_MODE_CLASS (mode) != MODE_CC)
            return const0_rtx;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case UDIV:
@@ -1456,9 +1769,9 @@ simplify_binary_operation (code, mode, op0, op1)
             below).  */
          if (GET_CODE (trueop1) == CONST_INT
              && (arg1 = exact_log2 (INTVAL (trueop1))) > 0)
-           return gen_rtx_LSHIFTRT (mode, op0, GEN_INT (arg1));
+           return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (arg1));
 
-         /* ... fall through ...  */
+         /* Fall through....  */
 
        case DIV:
          if (trueop1 == CONST1_RTX (mode))
@@ -1497,8 +1810,8 @@ simplify_binary_operation (code, mode, op0, op1)
              if (! REAL_VALUES_EQUAL (d, dconst0))
                {
                  REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
-                 return gen_rtx_MULT (mode, op0,
-                                      CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+                 tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+                 return simplify_gen_binary (MULT, mode, op0, tem);
                }
            }
          break;
@@ -1507,9 +1820,10 @@ simplify_binary_operation (code, mode, op0, op1)
          /* Handle modulus by power of two (mod with 1 handled below).  */
          if (GET_CODE (trueop1) == CONST_INT
              && exact_log2 (INTVAL (trueop1)) > 0)
-           return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
+           return simplify_gen_binary (AND, mode, op0,
+                                       GEN_INT (INTVAL (op1) - 1));
 
-         /* ... fall through ...  */
+         /* Fall through....  */
 
        case MOD:
          if ((trueop0 == const0_rtx || trueop1 == const1_rtx)
@@ -1526,7 +1840,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! side_effects_p (op1))
            return op0;
 
-         /* ... fall through ...  */
+         /* Fall through....  */
 
        case ASHIFT:
        case LSHIFTRT:
@@ -1537,36 +1851,50 @@ simplify_binary_operation (code, mode, op0, op1)
          break;
 
        case SMIN:
-         if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (trueop1) == CONST_INT
+         if (width <= HOST_BITS_PER_WIDE_INT
+             && GET_CODE (trueop1) == CONST_INT
              && INTVAL (trueop1) == (HOST_WIDE_INT) 1 << (width -1)
              && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+         if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case SMAX:
-         if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (trueop1) == CONST_INT
+         if (width <= HOST_BITS_PER_WIDE_INT
+             && GET_CODE (trueop1) == CONST_INT
              && ((unsigned HOST_WIDE_INT) INTVAL (trueop1)
                  == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1)
              && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+         if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case UMIN:
          if (trueop1 == const0_rtx && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+         if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case UMAX:
          if (trueop1 == constm1_rtx && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
+         if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case SS_PLUS:
@@ -1605,15 +1933,15 @@ simplify_binary_operation (code, mode, op0, op1)
                  rtvec v = rtvec_alloc (n_elts);
                  unsigned int i;
 
-                 if (XVECLEN (trueop1, 0) != (int)n_elts)
+                 if (XVECLEN (trueop1, 0) != (int) n_elts)
                    abort ();
                  for (i = 0; i < n_elts; i++)
                    {
-                      rtx x = XVECEXP (trueop1, 0, i);
+                     rtx x = XVECEXP (trueop1, 0, i);
 
-                      if (GET_CODE (x) != CONST_INT)
-                        abort ();
-                      RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, INTVAL (x));
+                     if (GET_CODE (x) != CONST_INT)
+                       abort ();
+                     RTVEC_ELT (v, i) = CONST_VECTOR_ELT (trueop0, INTVAL (x));
                    }
 
                  return gen_rtx_CONST_VECTOR (mode, v);
@@ -1684,7 +2012,7 @@ simplify_binary_operation (code, mode, op0, op1)
 
                return gen_rtx_CONST_VECTOR (mode, v);
              }
-           }
+         }
          return 0;
 
        default:
@@ -1898,9 +2226,7 @@ struct simplify_plus_minus_op_data
 };
 
 static int
-simplify_plus_minus_op_data_cmp (p1, p2)
-     const void *p1;
-     const void *p2;
+simplify_plus_minus_op_data_cmp (const void *p1, const void *p2)
 {
   const struct simplify_plus_minus_op_data *d1 = p1;
   const struct simplify_plus_minus_op_data *d2 = p2;
@@ -1910,19 +2236,16 @@ simplify_plus_minus_op_data_cmp (p1, p2)
 }
 
 static rtx
-simplify_plus_minus (code, mode, op0, op1, force)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op0, op1;
-     int force;
+simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
+                    rtx op1, int force)
 {
   struct simplify_plus_minus_op_data ops[8];
   rtx result, tem;
   int n_ops = 2, input_ops = 2, input_consts = 0, n_consts;
-  int first, negate, changed;
+  int first, changed;
   int i, j;
 
-  memset ((char *) ops, 0, sizeof ops);
+  memset (ops, 0, sizeof ops);
 
   /* Set up the two operands and then expand them until nothing has been
      changed.  If we run out of room in our array, give up; this should
@@ -2090,6 +2413,13 @@ simplify_plus_minus (code, mode, op0, op1, force)
   /* Sort the operations based on swap_commutative_operands_p.  */
   qsort (ops, n_ops, sizeof (*ops), simplify_plus_minus_op_data_cmp);
 
+  /* Create (minus -C X) instead of (neg (const (plus X C))).  */
+  if (n_ops == 2
+      && GET_CODE (ops[1].op) == CONST_INT
+      && CONSTANT_P (ops[0].op)
+      && ops[0].neg)
+    return gen_rtx_fmt_ee (MINUS, mode, ops[1].op, ops[0].op);
+  
   /* We suppressed creation of trivial CONST expressions in the
      combination loop to avoid recursion.  Create one manually now.
      The combination loop should have ensured that there is exactly
@@ -2122,18 +2452,12 @@ simplify_plus_minus (code, mode, op0, op1, force)
          || (n_ops + n_consts == input_ops && n_consts <= input_consts)))
     return NULL_RTX;
 
-  /* Put a non-negated operand first.  If there aren't any, make all
-     operands positive and negate the whole thing later.  */
+  /* Put a non-negated operand first, if possible.  */
 
-  negate = 0;
   for (i = 0; i < n_ops && ops[i].neg; i++)
     continue;
   if (i == n_ops)
-    {
-      for (i = 0; i < n_ops; i++)
-       ops[i].neg = 0;
-      negate = 1;
-    }
+    ops[0].op = gen_rtx_NEG (mode, ops[0].op);
   else if (i != 0)
     {
       tem = ops[0].op;
@@ -2148,7 +2472,7 @@ simplify_plus_minus (code, mode, op0, op1, force)
     result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS,
                             mode, result, ops[i].op);
 
-  return negate ? gen_rtx_NEG (mode, result) : result;
+  return result;
 }
 
 /* Like simplify_binary_operation except used for relational operators.
@@ -2160,10 +2484,8 @@ simplify_plus_minus (code, mode, op0, op1, force)
    it returns either const_true_rtx or const0_rtx.  */
 
 rtx
-simplify_relational_operation (code, mode, op0, op1)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op0, op1;
+simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
+                              rtx op0, rtx op1)
 {
   int equal, op0lt, op0ltu, op1lt, op1ltu;
   rtx tem;
@@ -2179,22 +2501,21 @@ simplify_relational_operation (code, mode, op0, op1)
   if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
     op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
 
-  trueop0 = avoid_constant_pool_reference (op0);
-  trueop1 = avoid_constant_pool_reference (op1);
-
   /* We can't simplify MODE_CC values since we don't know what the
      actual comparison is.  */
   if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC || CC0_P (op0))
     return 0;
 
   /* Make sure the constant is second.  */
-  if (swap_commutative_operands_p (trueop0, trueop1))
+  if (swap_commutative_operands_p (op0, op1))
     {
       tem = op0, op0 = op1, op1 = tem;
-      tem = trueop0, trueop0 = trueop1, trueop1 = tem;
       code = swap_condition (code);
     }
 
+  trueop0 = avoid_constant_pool_reference (op0);
+  trueop1 = avoid_constant_pool_reference (op1);
+
   /* For integer comparisons of A and B maybe we can simplify A - B and can
      then simplify a comparison of that with zero.  If A and B are both either
      a register or a CONST_INT, this can't help; testing for these cases will
@@ -2209,6 +2530,8 @@ simplify_relational_operation (code, mode, op0, op1)
       && ! ((GET_CODE (op0) == REG || GET_CODE (trueop0) == CONST_INT)
            && (GET_CODE (op1) == REG || GET_CODE (trueop1) == CONST_INT))
       && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
+      /* We cannot do this for == or != if tem is a nonzero address.  */
+      && ((code != EQ && code != NE) || ! nonzero_address_p (tem))
       && code != GTU && code != GEU && code != LTU && code != LEU)
     return simplify_relational_operation (signed_condition (code),
                                          mode, tem, const0_rtx);
@@ -2220,8 +2543,10 @@ simplify_relational_operation (code, mode, op0, op1)
     return const0_rtx;
 
   /* For modes without NaNs, if the two operands are equal, we know the
-     result.  */
-  if (!HONOR_NANS (GET_MODE (trueop0)) && rtx_equal_p (trueop0, trueop1))
+     result except if they have side-effects.  */
+  if (! HONOR_NANS (GET_MODE (trueop0))
+      && rtx_equal_p (trueop0, trueop1)
+      && ! side_effects_p (trueop0))
     equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
 
   /* If the operands are floating-point constants, see if we can fold
@@ -2381,7 +2706,18 @@ simplify_relational_operation (code, mode, op0, op1)
              tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
                                                       : trueop0;
              if (GET_CODE (tem) == ABS)
-               return const1_rtx;
+               return const_true_rtx;
+           }
+         break;
+
+       case UNGE:
+         /* Optimize ! (abs(x) < 0.0).  */
+         if (trueop1 == CONST0_RTX (mode))
+           {
+             tem = GET_CODE (trueop0) == FLOAT_EXTEND ? XEXP (trueop0, 0)
+                                                      : trueop0;
+             if (GET_CODE (tem) == ABS)
+               return const_true_rtx;
            }
          break;
 
@@ -2436,10 +2772,9 @@ simplify_relational_operation (code, mode, op0, op1)
    a constant.  Return 0 if no simplifications is possible.  */
 
 rtx
-simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
-     enum rtx_code code;
-     enum machine_mode mode, op0_mode;
-     rtx op0, op1, op2;
+simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
+                           enum machine_mode op0_mode, rtx op0, rtx op1,
+                           rtx op2)
 {
   unsigned int width = GET_MODE_BITSIZE (mode);
 
@@ -2493,18 +2828,33 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
       if (GET_CODE (op0) == CONST_INT)
        return op0 != const0_rtx ? op1 : op2;
 
-      /* Convert a == b ? b : a to "a".  */
-      if (GET_CODE (op0) == NE && ! side_effects_p (op0)
-         && !HONOR_NANS (mode)
-         && rtx_equal_p (XEXP (op0, 0), op1)
-         && rtx_equal_p (XEXP (op0, 1), op2))
+      /* Convert c ? a : a into "a".  */
+      if (rtx_equal_p (op1, op2) && ! side_effects_p (op0))
+       return op1;
+
+      /* Convert a != b ? a : b into "a".  */
+      if (GET_CODE (op0) == NE
+         && ! side_effects_p (op0)
+         && ! HONOR_NANS (mode)
+         && ! HONOR_SIGNED_ZEROS (mode)
+         && ((rtx_equal_p (XEXP (op0, 0), op1)
+              && rtx_equal_p (XEXP (op0, 1), op2))
+             || (rtx_equal_p (XEXP (op0, 0), op2)
+                 && rtx_equal_p (XEXP (op0, 1), op1))))
        return op1;
-      else if (GET_CODE (op0) == EQ && ! side_effects_p (op0)
-         && !HONOR_NANS (mode)
-         && rtx_equal_p (XEXP (op0, 1), op1)
-         && rtx_equal_p (XEXP (op0, 0), op2))
+
+      /* Convert a == b ? a : b into "b".  */
+      if (GET_CODE (op0) == EQ
+         && ! side_effects_p (op0)
+         && ! HONOR_NANS (mode)
+         && ! HONOR_SIGNED_ZEROS (mode)
+         && ((rtx_equal_p (XEXP (op0, 0), op1)
+              && rtx_equal_p (XEXP (op0, 1), op2))
+             || (rtx_equal_p (XEXP (op0, 0), op2)
+                 && rtx_equal_p (XEXP (op0, 1), op1))))
        return op2;
-      else if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && ! side_effects_p (op0))
+
+      if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && ! side_effects_p (op0))
        {
          enum machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
                                        ? GET_MODE (XEXP (op0, 1))
@@ -2518,10 +2868,10 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
          /* See if any simplifications were possible.  */
          if (temp == const0_rtx)
            return op2;
-         else if (temp == const1_rtx)
+         else if (temp == const_true_rtx)
            return op1;
          else if (temp)
-           op0 = temp;
+           abort ();
 
          /* Look for happy constants in op1 and op2.  */
          if (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT)
@@ -2546,6 +2896,7 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
            }
        }
       break;
+
     case VEC_MERGE:
       if (GET_MODE (op0) != mode
          || GET_MODE (op1) != mode
@@ -2556,7 +2907,7 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
        {
           int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
          unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
-         int mask = (1<<n_elts) - 1;
+         int mask = (1 << n_elts) - 1;
 
          if (!(INTVAL (op2) & mask))
            return op1;
@@ -2587,237 +2938,301 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
   return 0;
 }
 
-/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
-   Return 0 if no simplifications is possible.  */
-rtx
-simplify_subreg (outermode, op, innermode, byte)
-     rtx op;
-     unsigned int byte;
-     enum machine_mode outermode, innermode;
-{
-  /* Little bit of sanity checking.  */
-  if (innermode == VOIDmode || outermode == VOIDmode
-      || innermode == BLKmode || outermode == BLKmode)
-    abort ();
-
-  if (GET_MODE (op) != innermode
-      && GET_MODE (op) != VOIDmode)
-    abort ();
+/* Evaluate a SUBREG of a CONST_INT or CONST_DOUBLE or CONST_VECTOR,
+   returning another CONST_INT or CONST_DOUBLE or CONST_VECTOR.
 
-  if (byte % GET_MODE_SIZE (outermode)
-      || byte >= GET_MODE_SIZE (innermode))
-    abort ();
+   Works by unpacking OP into a collection of 8-bit values
+   represented as a little-endian array of 'unsigned char', selecting by BYTE,
+   and then repacking them again for OUTERMODE.  */
 
-  if (outermode == innermode && !byte)
+static rtx
+simplify_immed_subreg (enum machine_mode outermode, rtx op, 
+                      enum machine_mode innermode, unsigned int byte)
+{
+  /* We support up to 512-bit values (for V8DFmode).  */
+  enum {
+    max_bitsize = 512,
+    value_bit = 8,
+    value_mask = (1 << value_bit) - 1
+  };
+  unsigned char value[max_bitsize / value_bit];
+  int value_start;
+  int i;
+  int elem;
+
+  int num_elem;
+  rtx * elems;
+  int elem_bitsize;
+  rtx result_s;
+  rtvec result_v = NULL;
+  enum mode_class outer_class;
+  enum machine_mode outer_submode;
+
+  /* Some ports misuse CCmode.  */
+  if (GET_MODE_CLASS (outermode) == MODE_CC && GET_CODE (op) == CONST_INT)
     return op;
 
-  /* Simplify subregs of vector constants.  */
+  /* Unpack the value.  */
+
   if (GET_CODE (op) == CONST_VECTOR)
     {
-      int elt_size = GET_MODE_SIZE (GET_MODE_INNER (innermode));
-      const unsigned int offset = byte / elt_size;
-      rtx elt;
+      num_elem = CONST_VECTOR_NUNITS (op);
+      elems = &CONST_VECTOR_ELT (op, 0);
+      elem_bitsize = GET_MODE_BITSIZE (GET_MODE_INNER (innermode));
+    }
+  else
+    {
+      num_elem = 1;
+      elems = &op;
+      elem_bitsize = max_bitsize;
+    }
 
-      if (GET_MODE_INNER (innermode) == outermode)
+  if (BITS_PER_UNIT % value_bit != 0)
+    abort ();  /* Too complicated; reducing value_bit may help.  */
+  if (elem_bitsize % BITS_PER_UNIT != 0)
+    abort ();  /* I don't know how to handle endianness of sub-units.  */
+  
+  for (elem = 0; elem < num_elem; elem++)
+    {
+      unsigned char * vp;
+      rtx el = elems[elem];
+      
+      /* Vectors are kept in target memory order.  (This is probably
+        a mistake.)  */
+      {
+       unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT;
+       unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize) 
+                         / BITS_PER_UNIT);
+       unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+       unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+       unsigned bytele = (subword_byte % UNITS_PER_WORD
+                        + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+       vp = value + (bytele * BITS_PER_UNIT) / value_bit;
+      }
+       
+      switch (GET_CODE (el))
        {
-         elt = CONST_VECTOR_ELT (op, offset);
-
-         /* ?? We probably don't need this copy_rtx because constants
-            can be shared.  ?? */
+       case CONST_INT:
+         for (i = 0;
+              i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize; 
+              i += value_bit)
+           *vp++ = INTVAL (el) >> i;
+         /* CONST_INTs are always logically sign-extended.  */
+         for (; i < elem_bitsize; i += value_bit)
+           *vp++ = INTVAL (el) < 0 ? -1 : 0;
+         break;
+      
+       case CONST_DOUBLE:
+         if (GET_MODE (el) == VOIDmode)
+           {
+             /* If this triggers, someone should have generated a
+                CONST_INT instead.  */
+             if (elem_bitsize <= HOST_BITS_PER_WIDE_INT)
+               abort ();
 
-         return copy_rtx (elt);
-       }
-      else if (GET_MODE_INNER (innermode) == GET_MODE_INNER (outermode)
-              && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode))
-       {
-         return (gen_rtx_CONST_VECTOR
-                 (outermode,
-                  gen_rtvec_v (GET_MODE_NUNITS (outermode),
-                               &CONST_VECTOR_ELT (op, offset))));
-       }
-      else if (GET_MODE_CLASS (outermode) == MODE_INT
-              && (GET_MODE_SIZE (outermode) % elt_size == 0))
-       {
-         /* This happens when the target register size is smaller then
-            the vector mode, and we synthesize operations with vectors
-            of elements that are smaller than the register size.  */
-         HOST_WIDE_INT sum = 0, high = 0;
-         unsigned n_elts = (GET_MODE_SIZE (outermode) / elt_size);
-         unsigned i = BYTES_BIG_ENDIAN ? offset : offset + n_elts - 1;
-         unsigned step = BYTES_BIG_ENDIAN ? 1 : -1;
-         int shift = BITS_PER_UNIT * elt_size;
-
-         for (; n_elts--; i += step)
+             for (i = 0; i < HOST_BITS_PER_WIDE_INT; i += value_bit)
+               *vp++ = CONST_DOUBLE_LOW (el) >> i;
+             while (i < HOST_BITS_PER_WIDE_INT * 2 && i < elem_bitsize)
+               {
+                 *vp++
+                   = CONST_DOUBLE_HIGH (el) >> (i - HOST_BITS_PER_WIDE_INT);
+                 i += value_bit;
+               }
+             /* It shouldn't matter what's done here, so fill it with
+                zero.  */
+             for (; i < max_bitsize; i += value_bit)
+               *vp++ = 0;
+           }
+         else if (GET_MODE_CLASS (GET_MODE (el)) == MODE_FLOAT)
            {
-             elt = CONST_VECTOR_ELT (op, i);
-             if (GET_CODE (elt) == CONST_DOUBLE
-                 && GET_MODE_CLASS (GET_MODE (elt)) == MODE_FLOAT)
+             long tmp[max_bitsize / 32];
+             int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
+             
+             if (bitsize > elem_bitsize)
+               abort ();
+             if (bitsize % value_bit != 0)
+               abort ();
+
+             real_to_target (tmp, CONST_DOUBLE_REAL_VALUE (el),
+                             GET_MODE (el));
+
+             /* real_to_target produces its result in words affected by
+                FLOAT_WORDS_BIG_ENDIAN.  However, we ignore this,
+                and use WORDS_BIG_ENDIAN instead; see the documentation
+                of SUBREG in rtl.texi.  */
+             for (i = 0; i < bitsize; i += value_bit)
                {
-                 elt = gen_lowpart_common (int_mode_for_mode (GET_MODE (elt)),
-                                           elt);
-                 if (! elt)
-                   return NULL_RTX;
+                 int ibase;
+                 if (WORDS_BIG_ENDIAN)
+                   ibase = bitsize - 1 - i;
+                 else
+                   ibase = i;
+                 *vp++ = tmp[ibase / 32] >> i % 32;
                }
-             if (GET_CODE (elt) != CONST_INT)
-               return NULL_RTX;
-             /* Avoid overflow.  */
-             if (high >> (HOST_BITS_PER_WIDE_INT - shift))
-               return NULL_RTX;
-             high = high << shift | sum >> (HOST_BITS_PER_WIDE_INT - shift);
-             sum = (sum << shift) + INTVAL (elt);
+             
+             /* It shouldn't matter what's done here, so fill it with
+                zero.  */
+             for (; i < elem_bitsize; i += value_bit)
+               *vp++ = 0;
            }
-         if (GET_MODE_BITSIZE (outermode) <= HOST_BITS_PER_WIDE_INT)
-           return GEN_INT (trunc_int_for_mode (sum, outermode));
-         else if (GET_MODE_BITSIZE (outermode) == 2* HOST_BITS_PER_WIDE_INT)
-           return immed_double_const (sum, high, outermode);
          else
-           return NULL_RTX;
-       }
-      else if (GET_MODE_CLASS (outermode) == MODE_INT
-              && (elt_size % GET_MODE_SIZE (outermode) == 0))
-       {
-         enum machine_mode new_mode
-           = int_mode_for_mode (GET_MODE_INNER (innermode));
-         int subbyte = byte % elt_size;
-
-         op = simplify_subreg (new_mode, op, innermode, byte - subbyte);
-           if (! op)
-             return NULL_RTX;
-         return simplify_subreg (outermode, op, new_mode, subbyte);
+           abort ();
+         break;
+         
+       default:
+         abort ();
        }
-      else if (GET_MODE_CLASS (outermode) == MODE_INT)
-        /* This shouldn't happen, but let's not do anything stupid.  */
-       return NULL_RTX;
     }
 
-  /* Attempt to simplify constant to non-SUBREG expression.  */
-  if (CONSTANT_P (op))
+  /* Now, pick the right byte to start with.  */
+  /* Renumber BYTE so that the least-significant byte is byte 0.  A special
+     case is paradoxical SUBREGs, which shouldn't be adjusted since they
+     will already have offset 0.  */
+  if (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode))
     {
-      int offset, part;
-      unsigned HOST_WIDE_INT val = 0;
+      unsigned ibyte = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode) 
+                       - byte);
+      unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+      unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+      byte = (subword_byte % UNITS_PER_WORD
+             + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+    }
 
-      if (GET_MODE_CLASS (outermode) == MODE_VECTOR_INT
-         || GET_MODE_CLASS (outermode) == MODE_VECTOR_FLOAT)
-       {
-         /* Construct a CONST_VECTOR from individual subregs.  */
-         enum machine_mode submode = GET_MODE_INNER (outermode);
-         int subsize = GET_MODE_UNIT_SIZE (outermode);
-         int i, elts = GET_MODE_NUNITS (outermode);
-         rtvec v = rtvec_alloc (elts);
-         rtx elt;
-
-         for (i = 0; i < elts; i++, byte += subsize)
-           {
-             /* This might fail, e.g. if taking a subreg from a SYMBOL_REF.  */
-             /* ??? It would be nice if we could actually make such subregs
-                on targets that allow such relocations.  */
-             if (byte >= GET_MODE_UNIT_SIZE (innermode))
-               elt = CONST0_RTX (submode);
-             else
-               elt = simplify_subreg (submode, op, innermode, byte);
-             if (! elt)
-               return NULL_RTX;
-             RTVEC_ELT (v, i) = elt;
-           }
-         return gen_rtx_CONST_VECTOR (outermode, v);
-       }
+  /* BYTE should still be inside OP.  (Note that BYTE is unsigned,
+     so if it's become negative it will instead be very large.)  */
+  if (byte >= GET_MODE_SIZE (innermode))
+    abort ();
 
-      /* ??? This code is partly redundant with code below, but can handle
-        the subregs of floats and similar corner cases.
-        Later it we should move all simplification code here and rewrite
-        GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends
-        using SIMPLIFY_SUBREG.  */
-      if (subreg_lowpart_offset (outermode, innermode) == byte
-         && GET_CODE (op) != CONST_VECTOR)
-       {
-         rtx new = gen_lowpart_if_possible (outermode, op);
-         if (new)
-           return new;
-       }
+  /* Convert from bytes to chunks of size value_bit.  */
+  value_start = byte * (BITS_PER_UNIT / value_bit);
 
-      /* Similar comment as above apply here.  */
-      if (GET_MODE_SIZE (outermode) == UNITS_PER_WORD
-         && GET_MODE_SIZE (innermode) > UNITS_PER_WORD
-         && GET_MODE_CLASS (outermode) == MODE_INT)
-       {
-         rtx new = constant_subword (op,
-                                     (byte / UNITS_PER_WORD),
-                                     innermode);
-         if (new)
-           return new;
-       }
+  /* Re-pack the value.  */
+    
+  if (VECTOR_MODE_P (outermode))
+    {
+      num_elem = GET_MODE_NUNITS (outermode);
+      result_v = rtvec_alloc (num_elem);
+      elems = &RTVEC_ELT (result_v, 0);
+      outer_submode = GET_MODE_INNER (outermode);
+    }
+  else
+    {
+      num_elem = 1;
+      elems = &result_s;
+      outer_submode = outermode;
+    }
 
-      if (GET_MODE_CLASS (outermode) != MODE_INT
-         && GET_MODE_CLASS (outermode) != MODE_CC)
-       {
-         enum machine_mode new_mode = int_mode_for_mode (outermode);
+  outer_class = GET_MODE_CLASS (outer_submode);
+  elem_bitsize = GET_MODE_BITSIZE (outer_submode);
 
-         if (new_mode != innermode || byte != 0)
-           {
-             op = simplify_subreg (new_mode, op, innermode, byte);
-             if (! op)
-               return NULL_RTX;
-             return simplify_subreg (outermode, op, new_mode, 0);
-           }
-       }
+  if (elem_bitsize % value_bit != 0)
+    abort ();
+  if (elem_bitsize + value_start * value_bit > max_bitsize)
+    abort ();
 
-      offset = byte * BITS_PER_UNIT;
-      switch (GET_CODE (op))
-       {
-       case CONST_DOUBLE:
-         if (GET_MODE (op) != VOIDmode)
-           break;
+  for (elem = 0; elem < num_elem; elem++)
+    {
+      unsigned char *vp;
+      
+      /* Vectors are stored in target memory order.  (This is probably
+        a mistake.)  */
+      {
+       unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT;
+       unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize) 
+                         / BITS_PER_UNIT);
+       unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+       unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+       unsigned bytele = (subword_byte % UNITS_PER_WORD
+                        + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+       vp = value + value_start + (bytele * BITS_PER_UNIT) / value_bit;
+      }
 
-         /* We can't handle this case yet.  */
-         if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT)
-           return NULL_RTX;
+      switch (outer_class)
+       {
+       case MODE_INT:
+       case MODE_PARTIAL_INT:
+         {
+           unsigned HOST_WIDE_INT hi = 0, lo = 0;
+
+           for (i = 0;
+                i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
+                i += value_bit)
+             lo |= (HOST_WIDE_INT)(*vp++ & value_mask) << i;
+           for (; i < elem_bitsize; i += value_bit)
+             hi |= ((HOST_WIDE_INT)(*vp++ & value_mask)
+                    << (i - HOST_BITS_PER_WIDE_INT));
+           
+           /* immed_double_const doesn't call trunc_int_for_mode.  I don't
+              know why.  */
+           if (elem_bitsize <= HOST_BITS_PER_WIDE_INT)
+             elems[elem] = gen_int_mode (lo, outer_submode);
+           else
+             elems[elem] = immed_double_const (lo, hi, outer_submode);
+         }
+         break;
+      
+       case MODE_FLOAT:
+         {
+           REAL_VALUE_TYPE r;
+           long tmp[max_bitsize / 32];
+           
+           /* real_from_target wants its input in words affected by
+              FLOAT_WORDS_BIG_ENDIAN.  However, we ignore this,
+              and use WORDS_BIG_ENDIAN instead; see the documentation
+              of SUBREG in rtl.texi.  */
+           for (i = 0; i < max_bitsize / 32; i++)
+             tmp[i] = 0;
+           for (i = 0; i < elem_bitsize; i += value_bit)
+             {
+               int ibase;
+               if (WORDS_BIG_ENDIAN)
+                 ibase = elem_bitsize - 1 - i;
+               else
+                 ibase = i;
+               tmp[ibase / 32] |= (*vp++ & value_mask) << i % 32;
+             }
 
-         part = offset >= HOST_BITS_PER_WIDE_INT;
-         if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
-              && BYTES_BIG_ENDIAN)
-             || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT
-                 && WORDS_BIG_ENDIAN))
-           part = !part;
-         val = part ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op);
-         offset %= HOST_BITS_PER_WIDE_INT;
+           real_from_target (&r, tmp, outer_submode);
+           elems[elem] = CONST_DOUBLE_FROM_REAL_VALUE (r, outer_submode);
+         }
+         break;
+           
+       default:
+         abort ();
+       }
+    }
+  if (VECTOR_MODE_P (outermode))
+    return gen_rtx_CONST_VECTOR (outermode, result_v);
+  else
+    return result_s;
+}
 
-         /* We've already picked the word we want from a double, so
-            pretend this is actually an integer.  */
-         innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
+   Return 0 if no simplifications are possible.  */
+rtx
+simplify_subreg (enum machine_mode outermode, rtx op,
+                enum machine_mode innermode, unsigned int byte)
+{
+  /* Little bit of sanity checking.  */
+  if (innermode == VOIDmode || outermode == VOIDmode
+      || innermode == BLKmode || outermode == BLKmode)
+    abort ();
 
-         /* FALLTHROUGH */
-       case CONST_INT:
-         if (GET_CODE (op) == CONST_INT)
-           val = INTVAL (op);
+  if (GET_MODE (op) != innermode
+      && GET_MODE (op) != VOIDmode)
+    abort ();
 
-         /* We don't handle synthesizing of non-integral constants yet.  */
-         if (GET_MODE_CLASS (outermode) != MODE_INT)
-           return NULL_RTX;
+  if (byte % GET_MODE_SIZE (outermode)
+      || byte >= GET_MODE_SIZE (innermode))
+    abort ();
 
-         if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
-           {
-             if (WORDS_BIG_ENDIAN)
-               offset = (GET_MODE_BITSIZE (innermode)
-                         - GET_MODE_BITSIZE (outermode) - offset);
-             if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
-                 && GET_MODE_SIZE (outermode) < UNITS_PER_WORD)
-               offset = (offset + BITS_PER_WORD - GET_MODE_BITSIZE (outermode)
-                         - 2 * (offset % BITS_PER_WORD));
-           }
+  if (outermode == innermode && !byte)
+    return op;
 
-         if (offset >= HOST_BITS_PER_WIDE_INT)
-           return ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx;
-         else
-           {
-             val >>= offset;
-             if (GET_MODE_BITSIZE (outermode) < HOST_BITS_PER_WIDE_INT)
-               val = trunc_int_for_mode (val, outermode);
-             return GEN_INT (val);
-           }
-       default:
-         break;
-       }
-    }
+  if (GET_CODE (op) == CONST_INT
+      || GET_CODE (op) == CONST_DOUBLE
+      || GET_CODE (op) == CONST_VECTOR)
+    return simplify_immed_subreg (outermode, op, innermode, byte);
 
   /* Changing mode twice with SUBREG => just change it once,
      or not at all if changing back op starting mode.  */
@@ -2881,7 +3296,7 @@ simplify_subreg (outermode, op, innermode, byte)
            return NULL_RTX;
        }
 
-      /* Recurse for futher possible simplifications.  */
+      /* Recurse for further possible simplifications.  */
       new = simplify_subreg (outermode, SUBREG_REG (op),
                             GET_MODE (SUBREG_REG (op)),
                             final_offset);
@@ -2913,10 +3328,12 @@ simplify_subreg (outermode, op, innermode, byte)
 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
       && REGNO (op) != ARG_POINTER_REGNUM
 #endif
-      && REGNO (op) != STACK_POINTER_REGNUM)
+      && REGNO (op) != STACK_POINTER_REGNUM
+      && subreg_offset_representable_p (REGNO (op), innermode,
+                                       byte, outermode))
     {
-      int final_regno = subreg_hard_regno (gen_rtx_SUBREG (outermode, op, byte),
-                                          0);
+      rtx tem = gen_rtx_SUBREG (outermode, op, byte);
+      int final_regno = subreg_hard_regno (tem, 0);
 
       /* ??? We do allow it if the current REG is not valid for
         its mode.  This is a kludge to work around how float/complex
@@ -2955,7 +3372,7 @@ simplify_subreg (outermode, op, innermode, byte)
      of real and imaginary part.  */
   if (GET_CODE (op) == CONCAT)
     {
-      int is_realpart = byte < GET_MODE_UNIT_SIZE (innermode);
+      int is_realpart = byte < (unsigned int) GET_MODE_UNIT_SIZE (innermode);
       rtx part = is_realpart ? XEXP (op, 0) : XEXP (op, 1);
       unsigned int final_offset;
       rtx res;
@@ -2964,19 +3381,54 @@ simplify_subreg (outermode, op, innermode, byte)
       res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
       if (res)
        return res;
-      /* We can at least simplify it by referring directly to the relevant part.  */
+      /* We can at least simplify it by referring directly to the
+        relevant part.  */
       return gen_rtx_SUBREG (outermode, part, final_offset);
     }
 
+  /* Optimize SUBREG truncations of zero and sign extended values.  */
+  if ((GET_CODE (op) == ZERO_EXTEND
+       || GET_CODE (op) == SIGN_EXTEND)
+      && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode))
+    {
+      unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte);
+
+      /* If we're requesting the lowpart of a zero or sign extension,
+        there are three possibilities.  If the outermode is the same
+        as the origmode, we can omit both the extension and the subreg.
+        If the outermode is not larger than the origmode, we can apply
+        the truncation without the extension.  Finally, if the outermode
+        is larger than the origmode, but both are integer modes, we
+        can just extend to the appropriate mode.  */
+      if (bitpos == 0)
+       {
+         enum machine_mode origmode = GET_MODE (XEXP (op, 0));
+         if (outermode == origmode)
+           return XEXP (op, 0);
+         if (GET_MODE_BITSIZE (outermode) <= GET_MODE_BITSIZE (origmode))
+           return simplify_gen_subreg (outermode, XEXP (op, 0), origmode,
+                                       subreg_lowpart_offset (outermode,
+                                                              origmode));
+         if (SCALAR_INT_MODE_P (outermode))
+           return simplify_gen_unary (GET_CODE (op), outermode,
+                                      XEXP (op, 0), origmode);
+       }
+
+      /* A SUBREG resulting from a zero extension may fold to zero if
+        it extracts higher bits that the ZERO_EXTEND's source bits.  */
+      if (GET_CODE (op) == ZERO_EXTEND
+         && bitpos >= GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))))
+       return CONST0_RTX (outermode);
+    }
+
   return NULL_RTX;
 }
+
 /* Make a SUBREG operation or equivalent if it folds.  */
 
 rtx
-simplify_gen_subreg (outermode, op, innermode, byte)
-     rtx op;
-     unsigned int byte;
-     enum machine_mode outermode, innermode;
+simplify_gen_subreg (enum machine_mode outermode, rtx op,
+                    enum machine_mode innermode, unsigned int byte)
 {
   rtx new;
   /* Little bit of sanity checking.  */
@@ -3045,11 +3497,11 @@ simplify_gen_subreg (outermode, op, innermode, byte)
     simplification and 1 for tree simplification.  */
 
 rtx
-simplify_rtx (x)
-     rtx x;
+simplify_rtx (rtx x)
 {
   enum rtx_code code = GET_CODE (x);
   enum machine_mode mode = GET_MODE (x);
+  rtx temp;
 
   switch (GET_RTX_CLASS (code))
     {
@@ -3058,15 +3510,9 @@ simplify_rtx (x)
                                       XEXP (x, 0), GET_MODE (XEXP (x, 0)));
     case 'c':
       if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
-       {
-         rtx tem;
+       return simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0));
 
-         tem = XEXP (x, 0);
-         XEXP (x, 0) = XEXP (x, 1);
-         XEXP (x, 1) = tem;
-         return simplify_binary_operation (code, mode,
-                                           XEXP (x, 0), XEXP (x, 1));
-       }
+      /* Fall through....  */
 
     case '2':
       return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
@@ -3078,12 +3524,24 @@ simplify_rtx (x)
                                         XEXP (x, 2));
 
     case '<':
-      return simplify_relational_operation (code,
+      temp = simplify_relational_operation (code,
                                            ((GET_MODE (XEXP (x, 0))
                                              != VOIDmode)
                                             ? GET_MODE (XEXP (x, 0))
                                             : GET_MODE (XEXP (x, 1))),
                                            XEXP (x, 0), XEXP (x, 1));
+#ifdef FLOAT_STORE_FLAG_VALUE
+      if (temp != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+         if (temp == const0_rtx)
+           temp = CONST0_RTX (mode);
+         else
+           temp = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE (mode),
+                                                mode);
+       }
+#endif
+      return temp;
+
     case 'x':
       if (code == SUBREG)
        return simplify_gen_subreg (mode, SUBREG_REG (x),
@@ -3091,11 +3549,23 @@ simplify_rtx (x)
                                    SUBREG_BYTE (x));
       if (code == CONSTANT_P_RTX)
        {
-         if (CONSTANT_P (XEXP (x,0)))
+         if (CONSTANT_P (XEXP (x, 0)))
            return const1_rtx;
        }
-      return NULL;
+      break;
+
+    case 'o':
+      if (code == LO_SUM)
+       {
+         /* Convert (lo_sum (high FOO) FOO) to FOO.  */
+         if (GET_CODE (XEXP (x, 0)) == HIGH
+             && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
+         return XEXP (x, 1);
+       }
+      break;
+
     default:
-      return NULL;
+      break;
     }
+  return NULL;
 }