OSDN Git Service

* config/darwin-c.c (add_framework): Copy the directory name as it
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index 27fe4f3..74377b6 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.
 
@@ -50,9 +50,16 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  ((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
 
 static rtx neg_const_int (enum machine_mode, rtx);
+static bool mode_signbit_p (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);
+static rtx simplify_relational_operation_1 (enum rtx_code, enum machine_mode,
+                                           enum machine_mode, rtx, rtx);
 \f
 /* Negate a CONST_INT rtx, truncating (because a conversion from a
    maximally negative number can overflow).  */
@@ -62,6 +69,39 @@ neg_const_int (enum machine_mode mode, rtx i)
   return gen_int_mode (- INTVAL (i), mode);
 }
 
+/* Test whether expression, X, is an immediate constant that represents
+   the most significant bit of machine mode MODE.  */
+
+static bool
+mode_signbit_p (enum machine_mode mode, rtx x)
+{
+  unsigned HOST_WIDE_INT val;
+  unsigned int width;
+
+  if (GET_MODE_CLASS (mode) != MODE_INT)
+    return false;
+
+  width = GET_MODE_BITSIZE (mode);
+  if (width == 0)
+    return false;
+  
+  if (width <= HOST_BITS_PER_WIDE_INT
+      && GET_CODE (x) == CONST_INT)
+    val = INTVAL (x);
+  else if (width <= 2 * HOST_BITS_PER_WIDE_INT
+          && GET_CODE (x) == CONST_DOUBLE
+          && CONST_DOUBLE_LOW (x) == 0)
+    {
+      val = CONST_DOUBLE_HIGH (x);
+      width -= HOST_BITS_PER_WIDE_INT;
+    }
+  else
+    return false;
+
+  if (width < HOST_BITS_PER_WIDE_INT)
+    val &= ((unsigned HOST_WIDE_INT) 1 << width) - 1;
+  return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
+}
 \f
 /* Make a binary operation by properly ordering the operands and
    seeing if the expression folds.  */
@@ -73,7 +113,7 @@ simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
   rtx tem;
 
   /* Put complex operands first and constants second if commutative.  */
-  if (GET_RTX_CLASS (code) == 'c'
+  if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
       && swap_commutative_operands_p (op0, op1))
     tem = op0, op0 = op1, op1 = tem;
 
@@ -127,8 +167,8 @@ avoid_constant_pool_reference (rtx x)
 
   addr = XEXP (x, 0);
 
-  /* Call target hook to avoid the effects of -fpic etc...  */
-  addr = (*targetm.delegitimize_address) (addr);
+  /* Call target hook to avoid the effects of -fpic etc....  */
+  addr = targetm.delegitimize_address (addr);
 
   if (GET_CODE (addr) == LO_SUM)
     addr = XEXP (addr, 1);
@@ -183,10 +223,9 @@ simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
 
   return gen_rtx_fmt_eee (code, mode, op0, op1, op2);
 }
-\f
+
 /* Likewise, for relational operations.
-   CMP_MODE specifies mode comparison is done in.
-  */
+   CMP_MODE specifies mode comparison is done in.  */
 
 rtx
 simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
@@ -194,38 +233,10 @@ simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
 {
   rtx tem;
 
-  if ((tem = simplify_relational_operation (code, cmp_mode, op0, op1)) != 0)
+  if (0 != (tem = simplify_relational_operation (code, mode, cmp_mode,
+                                                op0, op1)))
     return tem;
 
-  /* For the following tests, ensure const0_rtx is op1.  */
-  if (op0 == const0_rtx && swap_commutative_operands_p (op0, op1))
-    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);
-
-  /* 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)
-    {
-      /* 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);
-        }
-    }
-
-  /* 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
@@ -237,6 +248,8 @@ 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
@@ -247,84 +260,79 @@ simplify_replace_rtx (rtx x, rtx old, rtx 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);
-      }
-
-    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));
-    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);
-      }
-
-    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));
-      }
+    case RTX_UNARY:
+      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 RTX_BIN_ARITH:
+    case RTX_COMM_ARITH:
+      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 RTX_COMPARE:
+    case RTX_COMM_COMPARE:
+      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 RTX_TERNARY:
+    case RTX_BITFIELD_OPS:
+      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':
+    case RTX_EXTRA:
       /* 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':
+    case RTX_OBJ:
       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)
@@ -332,11 +340,10 @@ simplify_replace_rtx (rtx x, rtx old, rtx new)
          if (REG_P (old) && REGNO (x) == REGNO (old))
            return new;
        }
-
-      return x;
+      break;
 
     default:
-      return x;
+      break;
     }
   return x;
 }
@@ -388,6 +395,8 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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)
     {
@@ -637,24 +646,23 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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:
@@ -747,7 +755,16 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
        case FIX:
          real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL);
          break;
+       case NOT:
+         {
+           long tmp[4];
+           int i;
 
+           real_to_target (tmp, &d, GET_MODE (trueop));
+           for (i = 0; i < 4; i++)
+             tmp[i] = ~tmp[i];
+           real_from_target (&d, tmp, mode);
+         }
        default:
          abort ();
        }
@@ -757,19 +774,99 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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.
@@ -777,6 +874,8 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
   else
     {
       enum rtx_code reversed;
+      rtx temp;
+
       /* There are some simplifications we can do even if the operands
         aren't constant.  */
       switch (code)
@@ -787,17 +886,157 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
            return XEXP (op, 0);
 
          /* (not (eq X Y)) == (ne X Y), etc.  */
-         if (mode == BImode && GET_RTX_CLASS (GET_CODE (op)) == '<'
+         if (COMPARISON_P (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 (plus X C)) for signbit C is (xor X D) with D = ~C.  */
+         if (GET_CODE (op) == PLUS
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+             && mode_signbit_p (mode, XEXP (op, 1))
+             && (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
+             && COMPARISON_P (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));
+           }
+
+         /* (neg (ashiftrt X C)) can be replaced by (lshiftrt X C) when
+            C is equal to the width of MODE minus 1.  */
+         if (GET_CODE (op) == ASHIFTRT
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+             && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+               return simplify_gen_binary (LSHIFTRT, mode,
+                                           XEXP (op, 0), XEXP (op, 1));
+
+         /* (neg (lshiftrt X C)) can be replaced by (ashiftrt X C) when
+            C is equal to the width of MODE minus 1.  */
+         if (GET_CODE (op) == LSHIFTRT
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+             && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+               return simplify_gen_binary (ASHIFTRT, mode,
+                                           XEXP (op, 0), XEXP (op, 1));
+
          break;
 
        case SIGN_EXTEND:
@@ -812,6 +1051,15 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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
@@ -824,8 +1072,17 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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)
@@ -834,8 +1091,8 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
                      && REG_POINTER (SUBREG_REG (op))
                      && GET_MODE (SUBREG_REG (op)) == Pmode)))
            return convert_memory_address (Pmode, op);
-         break;
 #endif
+         break;
 
        default:
          break;
@@ -845,6 +1102,64 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode 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.
 
@@ -857,26 +1172,30 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
   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);
 
+#ifdef ENABLE_CHECKING
   /* Relational operations don't work here.  We must know the mode
      of the operands in order to do the comparison correctly.
      Assuming a full word can give incorrect results.
      Consider comparing 128 with -128 in QImode.  */
 
-  if (GET_RTX_CLASS (code) == '<')
+  if (GET_RTX_CLASS (code) == RTX_COMPARE
+      || GET_RTX_CLASS (code) == RTX_COMM_COMPARE)
     abort ();
+#endif
 
   /* Make sure the constant is second.  */
-  if (GET_RTX_CLASS (code) == 'c'
-      && swap_commutative_operands_p (trueop0, trueop1))
+  if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
+      && 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)
@@ -913,22 +1232,56 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
       && GET_CODE (trueop1) == CONST_DOUBLE
       && mode == GET_MODE (op0) && mode == GET_MODE (op1))
     {
-      REAL_VALUE_TYPE f0, f1, value;
+      if (code == AND
+         || code == IOR
+         || code == XOR)
+       {
+         long tmp0[4];
+         long tmp1[4];
+         REAL_VALUE_TYPE r;
+         int i;
+
+         real_to_target (tmp0, CONST_DOUBLE_REAL_VALUE (op0),
+                         GET_MODE (op0));
+         real_to_target (tmp1, CONST_DOUBLE_REAL_VALUE (op1),
+                         GET_MODE (op1));
+         for (i = 0; i < 4; i++)
+           {
+             if (code == AND)
+               tmp0[i] &= tmp1[i];
+             else if (code == IOR)
+               tmp0[i] |= tmp1[i];
+             else if (code == XOR)
+               tmp0[i] ^= tmp1[i];
+             else
+               abort ();
+           }
+          real_from_target (&r, tmp0, mode);
+          return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
+       }
+      else
+       {
+         REAL_VALUE_TYPE f0, f1, value;
 
-      REAL_VALUE_FROM_CONST_DOUBLE (f0, trueop0);
-      REAL_VALUE_FROM_CONST_DOUBLE (f1, trueop1);
-      f0 = real_value_truncate (mode, f0);
-      f1 = real_value_truncate (mode, f1);
+         REAL_VALUE_FROM_CONST_DOUBLE (f0, trueop0);
+         REAL_VALUE_FROM_CONST_DOUBLE (f1, trueop1);
+         f0 = real_value_truncate (mode, f0);
+         f1 = real_value_truncate (mode, f1);
 
-      if (code == DIV
-         && !MODE_HAS_INFINITIES (mode)
-         && REAL_VALUES_EQUAL (f1, dconst0))
-       return 0;
+         if (HONOR_SNANS (mode)
+             && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
+           return 0;
+
+         if (code == DIV
+             && REAL_VALUES_EQUAL (f1, dconst0)
+             && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
+           return 0;
 
-      REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
+         REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
 
-      value = real_value_truncate (mode, value);
-      return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
+         value = real_value_truncate (mode, value);
+         return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
+       }
     }
 
   /* We can fold some multi-word operations.  */
@@ -939,8 +1292,8 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
       && (GET_CODE (trueop1) == CONST_DOUBLE
          || GET_CODE (trueop1) == CONST_INT))
     {
-      unsigned HOST_WIDE_INT l1, l2, lv;
-      HOST_WIDE_INT h1, h2, hv;
+      unsigned HOST_WIDE_INT l1, l2, lv, lt;
+      HOST_WIDE_INT h1, h2, hv, ht;
 
       if (GET_CODE (trueop0) == CONST_DOUBLE)
        l1 = CONST_DOUBLE_LOW (trueop0), h1 = CONST_DOUBLE_HIGH (trueop0);
@@ -959,7 +1312,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          neg_double (l2, h2, &lv, &hv);
          l2 = lv, h2 = hv;
 
-         /* .. fall through ...  */
+         /* Fall through....  */
 
        case PLUS:
          add_double (l1, h1, l2, h2, &lv, &hv);
@@ -969,10 +1322,29 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          mul_double (l1, h1, l2, h2, &lv, &hv);
          break;
 
-       case DIV:  case MOD:   case UDIV:  case UMOD:
-         /* We'd need to include tree.h to do this and it doesn't seem worth
-            it.  */
-         return 0;
+       case DIV:
+         if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2,
+                                   &lv, &hv, &lt, &ht))
+           return 0;
+         break;
+
+       case MOD:
+         if (div_and_round_double (TRUNC_DIV_EXPR, 0, l1, h1, l2, h2,
+                                   &lt, &ht, &lv, &hv))
+           return 0;
+         break;
+
+       case UDIV:
+         if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2,
+                                   &lv, &hv, &lt, &ht))
+           return 0;
+         break;
+
+       case UMOD:
+         if (div_and_round_double (TRUNC_DIV_EXPR, 1, l1, h1, l2, h2,
+                                   &lt, &ht, &lv, &hv))
+           return 0;
+         break;
 
        case AND:
          lv = l1 & l2, hv = h1 & h2;
@@ -1029,10 +1401,8 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
        case LSHIFTRT:   case ASHIFTRT:
        case ASHIFT:
        case ROTATE:     case ROTATERT:
-#ifdef SHIFT_COUNT_TRUNCATED
          if (SHIFT_COUNT_TRUNCATED)
            l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0;
-#endif
 
          if (h2 != 0 || l2 >= GET_MODE_BITSIZE (mode))
            return 0;
@@ -1082,7 +1452,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          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
@@ -1151,6 +1521,17 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
                }
            }
 
+         /* (plus (xor X C1) C2) is (xor X (C1^C2)) if C2 is signbit.  */
+         if ((GET_CODE (op1) == CONST_INT
+              || GET_CODE (op1) == CONST_DOUBLE)
+             && GET_CODE (op0) == XOR
+             && (GET_CODE (XEXP (op0, 1)) == CONST_INT
+                 || GET_CODE (XEXP (op0, 1)) == CONST_DOUBLE)
+             && mode_signbit_p (mode, op1))
+           return simplify_gen_binary (XOR, mode, XEXP (op0, 0),
+                                       simplify_gen_binary (XOR, mode, op1,
+                                                            XEXP (op0, 1)));
+
          /* 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.
@@ -1166,6 +1547,16 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
                      && 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:
@@ -1217,11 +1608,11 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
             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,
@@ -1289,6 +1680,16 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          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.
@@ -1331,11 +1732,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
 
        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
@@ -1363,7 +1760,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
              && (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
@@ -1374,10 +1771,20 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
              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;
 
@@ -1396,6 +1803,9 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
              && ! 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:
@@ -1404,10 +1814,31 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          if (GET_CODE (trueop1) == CONST_INT
              && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
                  == GET_MODE_MASK (mode)))
-           return gen_rtx_NOT (mode, op0);
-         if (trueop0 == trueop1 && ! side_effects_p (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;
+
+         /* Canonicalize XOR of the most significant bit to PLUS.  */
+         if ((GET_CODE (op1) == CONST_INT
+              || GET_CODE (op1) == CONST_DOUBLE)
+             && mode_signbit_p (mode, op1))
+           return simplify_gen_binary (PLUS, mode, op0, op1);
+         /* (xor (plus X C1) C2) is (xor X (C1^C2)) if C1 is signbit.  */
+         if ((GET_CODE (op1) == CONST_INT
+              || GET_CODE (op1) == CONST_DOUBLE)
+             && GET_CODE (op0) == PLUS
+             && (GET_CODE (XEXP (op0, 1)) == CONST_INT
+                 || GET_CODE (XEXP (op0, 1)) == CONST_DOUBLE)
+             && mode_signbit_p (mode, XEXP (op0, 1)))
+           return simplify_gen_binary (XOR, mode, XEXP (op0, 0),
+                                       simplify_gen_binary (XOR, mode, op1,
+                                                            XEXP (op0, 1)));
+             
+         tem = simplify_associative_operation (code, mode, op0, op1);
+         if (tem)
+           return tem;
          break;
 
        case AND:
@@ -1426,72 +1857,133 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
              && ! 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:
-         /* Convert divide by power of two into shift (divide by 1 handled
-            below).  */
-         if (GET_CODE (trueop1) == CONST_INT
-             && (arg1 = exact_log2 (INTVAL (trueop1))) > 0)
-           return gen_rtx_LSHIFTRT (mode, op0, GEN_INT (arg1));
-
-         /* ... fall through ...  */
-
-       case DIV:
-         if (trueop1 == CONST1_RTX (mode))
+         /* 0/x is 0 (or x&0 if x has side-effects).  */
+         if (trueop0 == const0_rtx)
+           return side_effects_p (op1)
+                  ? simplify_gen_binary (AND, mode, op1, const0_rtx)
+                  : const0_rtx;
+         /* x/1 is x.  */
+         if (trueop1 == const1_rtx)
            {
-             /* On some platforms DIV uses narrower mode than its
-                operands.  */
+             /* Handle narrowing UDIV.  */
              rtx x = gen_lowpart_common (mode, op0);
              if (x)
                return x;
-             else if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode)
+             if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode)
                return gen_lowpart_SUBREG (mode, op0);
-             else
-               return op0;
+             return op0;
            }
+         /* Convert divide by power of two into shift.  */
+         if (GET_CODE (trueop1) == CONST_INT
+             && (arg1 = exact_log2 (INTVAL (trueop1))) > 0)
+           return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (arg1));
+         break;
 
-         /* Maybe change 0 / x to 0.  This transformation isn't safe for
-            modes with NaNs, since 0 / 0 will then be NaN rather than 0.
-            Nor is it safe for modes with signed zeros, since dividing
-            0 by a negative number gives -0, not 0.  */
-         if (!HONOR_NANS (mode)
-             && !HONOR_SIGNED_ZEROS (mode)
-             && trueop0 == CONST0_RTX (mode)
-             && ! side_effects_p (op1))
-           return op0;
-
-         /* Change division by a constant into multiplication.  Only do
-            this with -funsafe-math-optimizations.  */
-         else if (GET_CODE (trueop1) == CONST_DOUBLE
-                  && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT
-                  && trueop1 != CONST0_RTX (mode)
-                  && flag_unsafe_math_optimizations)
+       case DIV:
+         /* Handle floating point and integers separately.  */
+         if (GET_MODE_CLASS (mode) == MODE_FLOAT)
            {
-             REAL_VALUE_TYPE d;
-             REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
+             /* Maybe change 0.0 / x to 0.0.  This transformation isn't
+                safe for modes with NaNs, since 0.0 / 0.0 will then be
+                NaN rather than 0.0.  Nor is it safe for modes with signed
+                zeros, since dividing 0 by a negative number gives -0.0  */
+             if (trueop0 == CONST0_RTX (mode)
+                 && !HONOR_NANS (mode)
+                 && !HONOR_SIGNED_ZEROS (mode)
+                 && ! side_effects_p (op1))
+               return op0;
+             /* x/1.0 is x.  */
+             if (trueop1 == CONST1_RTX (mode)
+                 && !HONOR_SNANS (mode))
+               return op0;
 
-             if (! REAL_VALUES_EQUAL (d, dconst0))
+             if (GET_CODE (trueop1) == CONST_DOUBLE
+                 && trueop1 != CONST0_RTX (mode))
                {
-                 REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
-                 return gen_rtx_MULT (mode, op0,
-                                      CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+                 REAL_VALUE_TYPE d;
+                 REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
+
+                 /* x/-1.0 is -x.  */
+                 if (REAL_VALUES_EQUAL (d, dconstm1)
+                     && !HONOR_SNANS (mode))
+                   return simplify_gen_unary (NEG, mode, op0, mode);
+
+                 /* Change FP division by a constant into multiplication.
+                    Only do this with -funsafe-math-optimizations.  */
+                 if (flag_unsafe_math_optimizations
+                     && !REAL_VALUES_EQUAL (d, dconst0))
+                   {
+                     REAL_ARITHMETIC (d, RDIV_EXPR, dconst1, d);
+                     tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+                     return simplify_gen_binary (MULT, mode, op0, tem);
+                   }
+               }
+           }
+         else
+           {
+             /* 0/x is 0 (or x&0 if x has side-effects).  */
+             if (trueop0 == const0_rtx)
+               return side_effects_p (op1)
+                      ? simplify_gen_binary (AND, mode, op1, const0_rtx)
+                      : const0_rtx;
+             /* x/1 is x.  */
+             if (trueop1 == const1_rtx)
+               {
+                 /* Handle narrowing DIV.  */
+                 rtx x = gen_lowpart_common (mode, op0);
+                 if (x)
+                   return x;
+                 if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode)
+                   return gen_lowpart_SUBREG (mode, op0);
+                 return op0;
+               }
+             /* x/-1 is -x.  */
+             if (trueop1 == constm1_rtx)
+               {
+                 rtx x = gen_lowpart_common (mode, op0);
+                 if (!x)
+                   x = (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode)
+                       ? gen_lowpart_SUBREG (mode, op0) : op0;
+                 return simplify_gen_unary (NEG, mode, x, mode);
                }
            }
          break;
 
        case UMOD:
-         /* Handle modulus by power of two (mod with 1 handled below).  */
+         /* 0%x is 0 (or x&0 if x has side-effects).  */
+         if (trueop0 == const0_rtx)
+           return side_effects_p (op1)
+                  ? simplify_gen_binary (AND, mode, op1, const0_rtx)
+                  : const0_rtx;
+         /* x%1 is 0 (of x&0 if x has side-effects).  */
+         if (trueop1 == const1_rtx)
+           return side_effects_p (op0)
+                  ? simplify_gen_binary (AND, mode, op0, const0_rtx)
+                  : const0_rtx;
+         /* Implement modulus by power of two as AND.  */
          if (GET_CODE (trueop1) == CONST_INT
              && exact_log2 (INTVAL (trueop1)) > 0)
-           return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
-
-         /* ... fall through ...  */
+           return simplify_gen_binary (AND, mode, op0,
+                                       GEN_INT (INTVAL (op1) - 1));
+         break;
 
        case MOD:
-         if ((trueop0 == const0_rtx || trueop1 == const1_rtx)
-             && ! side_effects_p (op0) && ! side_effects_p (op1))
-           return const0_rtx;
+         /* 0%x is 0 (or x&0 if x has side-effects).  */
+         if (trueop0 == const0_rtx)
+           return side_effects_p (op1)
+                  ? simplify_gen_binary (AND, mode, op1, const0_rtx)
+                  : const0_rtx;
+         /* x%1 and x%-1 is 0 (or x&0 if x has side-effects).  */
+         if (trueop1 == const1_rtx || trueop1 == constm1_rtx)
+           return side_effects_p (op0)
+                  ? simplify_gen_binary (AND, mode, op0, const0_rtx)
+                  : const0_rtx;
          break;
 
        case ROTATERT:
@@ -1503,7 +1995,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
              && ! side_effects_p (op1))
            return op0;
 
-         /* ... fall through ...  */
+         /* Fall through....  */
 
        case ASHIFT:
        case LSHIFTRT:
@@ -1514,36 +2006,50 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
          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:
@@ -1762,10 +2268,8 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
       if (arg1 < 0)
        return 0;
 
-#ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
        arg1 %= width;
-#endif
 
       val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
       break;
@@ -1774,10 +2278,8 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
       if (arg1 < 0)
        return 0;
 
-#ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
        arg1 %= width;
-#endif
 
       val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
       break;
@@ -1786,10 +2288,8 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
       if (arg1 < 0)
        return 0;
 
-#ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
        arg1 %= width;
-#endif
 
       val = arg0s >> arg1;
 
@@ -1891,10 +2391,10 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
   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
@@ -2062,6 +2562,13 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
   /* 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
@@ -2094,18 +2601,12 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
          || (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;
@@ -2120,20 +2621,113 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
     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.
-   MODE is the mode of the operands, not that of the result.  If MODE
-   is VOIDmode, both operands must also be VOIDmode and we compare the
-   operands in "infinite precision".
-
-   If no simplification is possible, this function returns zero.  Otherwise,
-   it returns either const_true_rtx or const0_rtx.  */
+   MODE is the mode of the result. If MODE is VOIDmode, both operands must
+   also be VOIDmode.
 
+   CMP_MODE specifies in which mode the comparison is done in, so it is
+   the mode of the operands.  If CMP_MODE is VOIDmode, it is taken from
+   the operands or, if both are VOIDmode, the operands are compared in
+   "infinite precision".  */
 rtx
 simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
-                              rtx op0, rtx op1)
+                              enum machine_mode cmp_mode, rtx op0, rtx op1)
+{
+  rtx tem, trueop0, trueop1;
+
+  if (cmp_mode == VOIDmode)
+    cmp_mode = GET_MODE (op0);
+  if (cmp_mode == VOIDmode)
+    cmp_mode = GET_MODE (op1);
+
+  tem = simplify_const_relational_operation (code, cmp_mode, op0, op1);
+  if (tem)
+    {
+#ifdef FLOAT_STORE_FLAG_VALUE
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+          if (tem == const0_rtx)
+            return CONST0_RTX (mode);
+          else if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+           {
+             REAL_VALUE_TYPE val;
+             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 (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)
+    return simplify_relational_operation (code, mode, VOIDmode,
+                                         XEXP (op0, 0), XEXP (op0, 1));
+
+  if (mode == VOIDmode
+      || GET_MODE_CLASS (cmp_mode) == MODE_CC
+      || CC0_P (op0))
+    return NULL_RTX;
+
+  trueop0 = avoid_constant_pool_reference (op0);
+  trueop1 = avoid_constant_pool_reference (op1);
+  return simplify_relational_operation_1 (code, mode, cmp_mode,
+                                         trueop0, trueop1);
+}
+
+/* This part of simplify_relational_operation is only used when CMP_MODE
+   is not in class MODE_CC (i.e. it is a real comparison).
+
+   MODE is the mode of the result, while CMP_MODE specifies in which
+   mode the comparison is done in, so it is the mode of the operands.  */
+rtx
+simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
+                                enum machine_mode cmp_mode, rtx op0, rtx op1)
+{
+  if (GET_CODE (op1) == CONST_INT)
+    {
+      if (INTVAL (op1) == 0 && COMPARISON_P (op0))
+       {
+         /* If op0 is a comparison, extract the comparison arguments form it.  */
+         if (code == NE)
+           {
+             if (GET_MODE (op0) == cmp_mode)
+               return simplify_rtx (op0);
+             else
+               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));
+           }
+       }
+    }
+
+  return NULL_RTX;
+}
+
+/* Check if the given comparison (done in the given MODE) is actually a
+   tautology or a contradiction.
+   If no simplification is possible, this function returns zero.
+   Otherwise, it returns either const_true_rtx or const0_rtx.  */
+
+rtx
+simplify_const_relational_operation (enum rtx_code code,
+                                    enum machine_mode mode,
+                                    rtx op0, rtx op1)
 {
   int equal, op0lt, op0ltu, op1lt, op1ltu;
   rtx tem;
@@ -2149,22 +2743,21 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
   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
@@ -2173,15 +2766,18 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
      If CODE is an unsigned comparison, then we can never do this optimization,
      because it gives an incorrect result if the subtraction wraps around zero.
      ANSI C defines unsigned operations such that they never overflow, and
-     thus such cases can not be ignored.  */
+     thus such cases can not be ignored; but we cannot do it even for
+     signed comparisons for languages such as Java, so test flag_wrapv.  */
 
-  if (INTEGRAL_MODE_P (mode) && trueop1 != const0_rtx
+  if (!flag_wrapv && INTEGRAL_MODE_P (mode) && trueop1 != const0_rtx
       && ! ((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);
+    return simplify_const_relational_operation (signed_condition (code),
+                                               mode, tem, const0_rtx);
 
   if (flag_unsafe_math_optimizations && code == ORDERED)
     return const_true_rtx;
@@ -2475,35 +3071,38 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
       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 (COMPARISON_P (op0) && ! side_effects_p (op0))
        {
          enum machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
                                        ? GET_MODE (XEXP (op0, 1))
                                        : GET_MODE (XEXP (op0, 0)));
          rtx temp;
-         if (cmp_mode == VOIDmode)
-           cmp_mode = op0_mode;
-         temp = simplify_relational_operation (GET_CODE (op0), cmp_mode,
-                                               XEXP (op0, 0), XEXP (op0, 1));
-
-         /* See if any simplifications were possible.  */
-         if (temp == const0_rtx)
-           return op2;
-         else if (temp == const1_rtx)
-           return op1;
-         else if (temp)
-           op0 = temp;
 
          /* Look for happy constants in op1 and op2.  */
          if (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT)
@@ -2524,10 +3123,27 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
              else
                break;
 
-             return gen_rtx_fmt_ee (code, mode, XEXP (op0, 0), XEXP (op0, 1));
+             return simplify_gen_relational (code, mode, cmp_mode,
+                                             XEXP (op0, 0), XEXP (op0, 1));
+           }
+
+         if (cmp_mode == VOIDmode)
+           cmp_mode = op0_mode;
+         temp = simplify_relational_operation (GET_CODE (op0), op0_mode,
+                                               cmp_mode, XEXP (op0, 0),
+                                               XEXP (op0, 1));
+
+         /* See if any simplifications were possible.  */
+         if (temp)
+           {
+             if (GET_CODE (temp) == CONST_INT)
+               return temp == const0_rtx ? op2 : op1;
+             else if (temp)
+               return gen_rtx_IF_THEN_ELSE (mode, temp, op1, op2);
            }
        }
       break;
+
     case VEC_MERGE:
       if (GET_MODE (op0) != mode
          || GET_MODE (op1) != mode
@@ -2569,238 +3185,301 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
   return 0;
 }
 
-/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
-   Return 0 if no simplifications is 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 ();
+/* Evaluate a SUBREG of a CONST_INT or CONST_DOUBLE or CONST_VECTOR,
+   returning another CONST_INT or CONST_DOUBLE or CONST_VECTOR.
 
-  if (GET_MODE (op) != innermode
-      && GET_MODE (op) != VOIDmode)
-    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 (byte % GET_MODE_SIZE (outermode)
-      || byte >= GET_MODE_SIZE (innermode))
-    abort ();
-
-  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;
-         unsigned HOST_WIDE_INT unit_mask;
-
-         unit_mask = (unsigned HOST_WIDE_INT) -1
-           >> (sizeof (HOST_WIDE_INT) * BITS_PER_UNIT - shift);
-
-         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) & unit_mask);
+             
+             /* 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 (VECTOR_MODE_P (outermode))
-       {
-         /* 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_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.  */
@@ -2896,10 +3575,12 @@ simplify_subreg (enum machine_mode outermode, rtx op,
 #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
@@ -2938,7 +3619,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
      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;
@@ -2947,12 +3628,49 @@ simplify_subreg (enum machine_mode outermode, rtx op,
       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
@@ -3033,38 +3751,35 @@ simplify_rtx (rtx x)
 
   switch (GET_RTX_CLASS (code))
     {
-    case '1':
+    case RTX_UNARY:
       return simplify_unary_operation (code, mode,
                                       XEXP (x, 0), GET_MODE (XEXP (x, 0)));
-    case 'c':
+    case RTX_COMM_ARITH:
       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':
+    case RTX_BIN_ARITH:
       return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
 
-    case '3':
-    case 'b':
+    case RTX_TERNARY:
+    case RTX_BITFIELD_OPS:
       return simplify_ternary_operation (code, mode, GET_MODE (XEXP (x, 0)),
                                         XEXP (x, 0), XEXP (x, 1),
                                         XEXP (x, 2));
 
-    case '<':
-      return 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));
-    case 'x':
+    case RTX_COMPARE:
+    case RTX_COMM_COMPARE:
+      return simplify_relational_operation (code, mode,
+                                            ((GET_MODE (XEXP (x, 0))
+                                             != VOIDmode)
+                                            ? GET_MODE (XEXP (x, 0))
+                                            : GET_MODE (XEXP (x, 1))),
+                                            XEXP (x, 0),
+                                            XEXP (x, 1));
+
+    case RTX_EXTRA:
       if (code == SUBREG)
        return simplify_gen_subreg (mode, SUBREG_REG (x),
                                    GET_MODE (SUBREG_REG (x)),
@@ -3074,8 +3789,20 @@ simplify_rtx (rtx x)
          if (CONSTANT_P (XEXP (x, 0)))
            return const1_rtx;
        }
-      return NULL;
+      break;
+
+    case RTX_OBJ:
+      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;
 }