OSDN Git Service

* emit-rtl.c, final.c, fold-const.c, gengenrtl.c, optabs.c,
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index 574513f..5596e0c 100644 (file)
@@ -2,27 +2,26 @@
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
    1999, 2000, 2001 Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 
 #include "config.h"
 #include "system.h"
-#include <setjmp.h>
 
 #include "rtl.h"
 #include "tm_p.h"
@@ -96,9 +95,28 @@ Boston, MA 02111-1307, USA.  */
 #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));
+                                                enum machine_mode, rtx,
+                                                rtx, int));
 static void check_fold_consts          PARAMS ((PTR));
+static void simplify_unary_real                PARAMS ((PTR));
+static void simplify_binary_real       PARAMS ((PTR));
+static void simplify_binary_is2orm1    PARAMS ((PTR));
+
+\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;
+{
+  return GEN_INT (trunc_int_for_mode (- INTVAL (i), mode));
+}
+
 \f
 /* Make a binary operation by properly ordering the operands and 
    seeing if the expression folds.  */
@@ -118,23 +136,50 @@ simplify_gen_binary (code, mode, op0, op1)
 
   /* If this simplifies, do it.  */
   tem = simplify_binary_operation (code, mode, op0, op1);
-
   if (tem)
     return tem;
 
-  /* Handle addition and subtraction of CONST_INT specially.  Otherwise,
-     just form the operation.  */
+  /* Handle addition and subtraction specially.  Otherwise, just form
+     the operation.  */
 
-  if (code == PLUS && GET_CODE (op1) == CONST_INT
-      && GET_MODE (op0) != VOIDmode)
-    return plus_constant (op0, INTVAL (op1));
-  else if (code == MINUS && GET_CODE (op1) == CONST_INT
-          && GET_MODE (op0) != VOIDmode)
-    return plus_constant (op0, - INTVAL (op1));
+  if (code == PLUS || code == MINUS)
+    return simplify_plus_minus (code, mode, op0, op1, 1);
   else
     return gen_rtx_fmt_ee (code, mode, op0, op1);
 }
 \f
+/* If X is a MEM referencing the constant pool, return the real value.
+   Otherwise return X.  */
+rtx
+avoid_constant_pool_reference (x)
+     rtx x;
+{
+  rtx c, addr;
+  enum machine_mode cmode;
+
+  if (GET_CODE (x) != MEM)
+    return x;
+  addr = XEXP (x, 0);
+
+  if (GET_CODE (addr) != SYMBOL_REF
+      || ! CONSTANT_POOL_ADDRESS_P (addr))
+    return x;
+
+  c = get_pool_constant (addr);
+  cmode = get_pool_mode (addr);
+
+  /* If we're accessing the constant in a different mode than it was
+     originally stored, attempt to fix that up via subreg simplifications.
+     If that fails we have no choice but to return the original memory.  */
+  if (cmode != GET_MODE (x))
+    {
+      c = simplify_subreg (GET_MODE (x), c, cmode, 0);
+      return c ? c : x;
+    }
+
+  return c;
+}
+\f
 /* Make a unary operation by first seeing if it folds and otherwise making
    the specified operation.  */
 
@@ -232,21 +277,38 @@ simplify_replace_rtx (x, old, new)
                             simplify_replace_rtx (XEXP (x, 0), old, new),
                             simplify_replace_rtx (XEXP (x, 1), old, new));
     case '<':
-      return
-       simplify_gen_relational (code, mode,
-                                (GET_MODE (XEXP (x, 0)) != VOIDmode
-                                 ? GET_MODE (XEXP (x, 0))
-                                 : GET_MODE (XEXP (x, 1))),
-                                simplify_replace_rtx (XEXP (x, 0), old, new),
-                                simplify_replace_rtx (XEXP (x, 1), old, new));
+      {
+       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':
-      return
-       simplify_gen_ternary (code, mode, GET_MODE (XEXP (x, 0)),
-                             simplify_replace_rtx (XEXP (x, 0), old, new),
-                             simplify_replace_rtx (XEXP (x, 1), old, new),
-                             simplify_replace_rtx (XEXP (x, 2), old, new));
+      {
+       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 'x':
       /* The only case we try to handle is a SUBREG.  */
@@ -265,32 +327,78 @@ simplify_replace_rtx (x, old, new)
 
     default:
       if (GET_CODE (x) == MEM)
-       {
-         /* We can't use change_address here, since it verifies memory address
-            for corectness.  We don't want such check, since we may handle
-            addresses previously incorect (such as ones in push instructions)
-            and it is caller's work to verify whether resulting insn match.  */
-         rtx addr = simplify_replace_rtx (XEXP (x, 0), old, new);
-         rtx mem;
-         if (XEXP (x, 0) != addr)
-           {
-             mem = gen_rtx_MEM (GET_MODE (x), addr);
-             MEM_COPY_ATTRIBUTES (mem, x);
-           }
-         else
-           mem = x;
-         return mem;
-       }
+       return
+         replace_equiv_address_nv (x,
+                                   simplify_replace_rtx (XEXP (x, 0),
+                                                         old, new));
 
       return x;
     }
   return x;
 }
 \f
+/* Subroutine of simplify_unary_operation, called via do_float_handler.
+   Handles simplification of unary ops on floating point values.  */
+struct simplify_unary_real_args
+{
+  rtx operand;
+  rtx result;
+  enum machine_mode mode;
+  enum rtx_code code;
+  bool want_integer;
+};
+#define REAL_VALUE_ABS(d_) \
+   (REAL_VALUE_NEGATIVE (d_) ? REAL_VALUE_NEGATE (d_) : (d_))
+
+static void
+simplify_unary_real (p)
+     PTR p;
+{
+  REAL_VALUE_TYPE d;
+
+  struct simplify_unary_real_args *args =
+    (struct simplify_unary_real_args *) p;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (d, args->operand);
+
+  if (args->want_integer)
+    {
+      HOST_WIDE_INT i;
+
+      switch (args->code)
+       {
+       case FIX:               i = REAL_VALUE_FIX (d);           break;
+       case UNSIGNED_FIX:      i = REAL_VALUE_UNSIGNED_FIX (d);  break;
+       default:
+         abort ();
+       }
+      args->result = GEN_INT (trunc_int_for_mode (i, args->mode));
+    }
+  else
+    {
+      switch (args->code)
+       {
+       case SQRT:
+         /* We don't attempt to optimize this.  */
+         args->result = 0;
+         return;
+
+       case ABS:             d = REAL_VALUE_ABS (d);                   break;
+       case NEG:             d = REAL_VALUE_NEGATE (d);                break;
+       case FLOAT_TRUNCATE:  d = real_value_truncate (args->mode, d);  break;
+       case FLOAT_EXTEND:    /* All this does is change the mode.  */  break;
+       case FIX:             d = REAL_VALUE_RNDZINT (d);               break;
+       case UNSIGNED_FIX:    d = REAL_VALUE_UNSIGNED_RNDZINT (d);      break;
+       default:
+         abort ();
+       }
+      args->result = CONST_DOUBLE_FROM_REAL_VALUE (d, args->mode);
+    }
+}
+
 /* Try to simplify a unary operation CODE whose output mode is to be
    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;
@@ -299,56 +407,38 @@ simplify_unary_operation (code, mode, op, op_mode)
      enum machine_mode op_mode;
 {
   unsigned int width = GET_MODE_BITSIZE (mode);
+  rtx trueop = avoid_constant_pool_reference (op);
 
   /* The order of these tests is critical so that, for example, we don't
      check the wrong mode (input vs. output) for a conversion operation,
      such as FIX.  At some point, this should be simplified.  */
 
-#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
-
-  if (code == FLOAT && GET_MODE (op) == VOIDmode
-      && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+  if (code == FLOAT && GET_MODE (trueop) == VOIDmode
+      && (GET_CODE (trueop) == CONST_DOUBLE || GET_CODE (trueop) == CONST_INT))
     {
       HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
-      if (GET_CODE (op) == CONST_INT)
-       lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv);
+      if (GET_CODE (trueop) == CONST_INT)
+       lv = INTVAL (trueop), hv = HWI_SIGN_EXTEND (lv);
       else
-       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
+       lv = CONST_DOUBLE_LOW (trueop),  hv = CONST_DOUBLE_HIGH (trueop);
 
-#ifdef REAL_ARITHMETIC
       REAL_VALUE_FROM_INT (d, lv, hv, mode);
-#else
-      if (hv < 0)
-       {
-         d = (double) (~ hv);
-         d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
-               * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-         d += (double) (unsigned HOST_WIDE_INT) (~ lv);
-         d = (- d - 1.0);
-       }
-      else
-       {
-         d = (double) hv;
-         d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
-               * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-         d += (double) (unsigned HOST_WIDE_INT) lv;
-       }
-#endif  /* REAL_ARITHMETIC */
       d = real_value_truncate (mode, d);
       return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
     }
-  else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
-          && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+  else if (code == UNSIGNED_FLOAT && GET_MODE (trueop) == VOIDmode
+          && (GET_CODE (trueop) == CONST_DOUBLE
+              || GET_CODE (trueop) == CONST_INT))
     {
       HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
-      if (GET_CODE (op) == CONST_INT)
-       lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv);
+      if (GET_CODE (trueop) == CONST_INT)
+       lv = INTVAL (trueop), hv = HWI_SIGN_EXTEND (lv);
       else
-       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
+       lv = CONST_DOUBLE_LOW (trueop),  hv = CONST_DOUBLE_HIGH (trueop);
 
       if (op_mode == VOIDmode)
        {
@@ -362,25 +452,16 @@ simplify_unary_operation (code, mode, op, op_mode)
       else
        hv = 0, lv &= GET_MODE_MASK (op_mode);
 
-#ifdef REAL_ARITHMETIC
       REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
-#else
-
-      d = (double) (unsigned HOST_WIDE_INT) hv;
-      d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
-           * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-      d += (double) (unsigned HOST_WIDE_INT) lv;
-#endif  /* REAL_ARITHMETIC */
       d = real_value_truncate (mode, d);
       return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
     }
-#endif
 
-  if (GET_CODE (op) == CONST_INT
+  if (GET_CODE (trueop) == CONST_INT
       && width <= HOST_BITS_PER_WIDE_INT && width > 0)
     {
-      register HOST_WIDE_INT arg0 = INTVAL (op);
-      register HOST_WIDE_INT val;
+      HOST_WIDE_INT arg0 = INTVAL (trueop);
+      HOST_WIDE_INT val;
 
       switch (code)
        {
@@ -408,8 +489,10 @@ simplify_unary_operation (code, mode, op, op_mode)
          break;
 
        case ZERO_EXTEND:
+         /* When zero-extending a CONST_INT, we need to know its
+             original mode.  */
          if (op_mode == VOIDmode)
-           op_mode = mode;
+           abort ();
          if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
            {
              /* If we were really extending the mode,
@@ -452,6 +535,8 @@ simplify_unary_operation (code, mode, op, op_mode)
        case SQRT:
        case FLOAT_EXTEND:
        case FLOAT_TRUNCATE:
+       case SS_TRUNCATE:
+       case US_TRUNCATE:
          return 0;
 
        default:
@@ -465,16 +550,18 @@ simplify_unary_operation (code, mode, op, op_mode)
 
   /* We can do some operations on integer CONST_DOUBLEs.  Also allow
      for a DImode operation on a CONST_INT.  */
-  else if (GET_MODE (op) == VOIDmode && width <= HOST_BITS_PER_INT * 2
-          && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+  else if (GET_MODE (trueop) == VOIDmode
+          && width <= HOST_BITS_PER_WIDE_INT * 2
+          && (GET_CODE (trueop) == CONST_DOUBLE
+              || GET_CODE (trueop) == CONST_INT))
     {
       unsigned HOST_WIDE_INT l1, lv;
       HOST_WIDE_INT h1, hv;
 
-      if (GET_CODE (op) == CONST_DOUBLE)
-       l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op);
+      if (GET_CODE (trueop) == CONST_DOUBLE)
+       l1 = CONST_DOUBLE_LOW (trueop), h1 = CONST_DOUBLE_HIGH (trueop);
       else
-       l1 = INTVAL (op), h1 = HWI_SIGN_EXTEND (l1);
+       l1 = INTVAL (trueop), h1 = HWI_SIGN_EXTEND (l1);
 
       switch (code)
        {
@@ -508,8 +595,10 @@ simplify_unary_operation (code, mode, op, op_mode)
          break;
 
        case ZERO_EXTEND:
-         if (op_mode == VOIDmode
-             || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
+         if (op_mode == VOIDmode)
+           abort ();
+
+         if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
            return 0;
 
          hv = 0;
@@ -542,100 +631,38 @@ simplify_unary_operation (code, mode, op, op_mode)
       return immed_double_const (lv, hv, mode);
     }
 
-#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
-  else if (GET_CODE (op) == CONST_DOUBLE
+  else if (GET_CODE (trueop) == CONST_DOUBLE
           && GET_MODE_CLASS (mode) == MODE_FLOAT)
     {
-      REAL_VALUE_TYPE d;
-      jmp_buf handler;
-      rtx x;
-
-      if (setjmp (handler))
-       /* There used to be a warning here, but that is inadvisable.
-          People may want to cause traps, and the natural way
-          to do it should not get a warning.  */
-       return 0;
-
-      set_float_handler (handler);
-
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-
-      switch (code)
-       {
-       case NEG:
-         d = REAL_VALUE_NEGATE (d);
-         break;
-
-       case ABS:
-         if (REAL_VALUE_NEGATIVE (d))
-           d = REAL_VALUE_NEGATE (d);
-         break;
-
-       case FLOAT_TRUNCATE:
-         d = real_value_truncate (mode, d);
-         break;
-
-       case FLOAT_EXTEND:
-         /* All this does is change the mode.  */
-         break;
-
-       case FIX:
-         d = REAL_VALUE_RNDZINT (d);
-         break;
-
-       case UNSIGNED_FIX:
-         d = REAL_VALUE_UNSIGNED_RNDZINT (d);
-         break;
-
-       case SQRT:
-         return 0;
+      struct simplify_unary_real_args args;
+      args.operand = trueop;
+      args.mode = mode;
+      args.code = code;
+      args.want_integer = false;
 
-       default:
-         abort ();
-       }
+      if (do_float_handler (simplify_unary_real, (PTR) &args))
+       return args.result;
 
-      x = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
-      set_float_handler (NULL);
-      return x;
+      return 0;
     }
 
-  else if (GET_CODE (op) == CONST_DOUBLE
-          && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
+  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)
     {
-      REAL_VALUE_TYPE d;
-      jmp_buf handler;
-      HOST_WIDE_INT val;
-
-      if (setjmp (handler))
-       return 0;
+      struct simplify_unary_real_args args;
+      args.operand = trueop;
+      args.mode = mode;
+      args.code = code;
+      args.want_integer = true;
 
-      set_float_handler (handler);
+      if (do_float_handler (simplify_unary_real, (PTR) &args))
+       return args.result;
 
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-
-      switch (code)
-       {
-       case FIX:
-         val = REAL_VALUE_FIX (d);
-         break;
-
-       case UNSIGNED_FIX:
-         val = REAL_VALUE_UNSIGNED_FIX (d);
-         break;
-
-       default:
-         abort ();
-       }
-
-      set_float_handler (NULL);
-
-      val = trunc_int_for_mode (val, mode);
-
-      return GEN_INT (val);
+      return 0;
     }
-#endif
+
   /* This was formerly used only for non-IEEE float.
      eggert@twinsun.com says it is safe for IEEE also.  */
   else
@@ -668,7 +695,7 @@ simplify_unary_operation (code, mode, op, op_mode)
          /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2))))
             becomes just the MINUS if its mode is MODE.  This allows
             folding switch statements on machines using casesi (such as
-            the Vax).  */
+            the VAX).  */
          if (GET_CODE (op) == TRUNCATE
              && GET_MODE (XEXP (op, 0)) == mode
              && GET_CODE (XEXP (op, 0)) == MINUS
@@ -676,7 +703,7 @@ simplify_unary_operation (code, mode, op, op_mode)
              && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
            return XEXP (op, 0);
 
-#ifdef POINTERS_EXTEND_UNSIGNED
+#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
          if (! POINTERS_EXTEND_UNSIGNED
              && mode == Pmode && GET_MODE (op) == ptr_mode
              && (CONSTANT_P (op)
@@ -688,9 +715,9 @@ simplify_unary_operation (code, mode, op, op_mode)
 #endif
          break;
 
-#ifdef POINTERS_EXTEND_UNSIGNED
+#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
        case ZERO_EXTEND:
-         if (POINTERS_EXTEND_UNSIGNED
+         if (POINTERS_EXTEND_UNSIGNED > 0
              && mode == Pmode && GET_MODE (op) == ptr_mode
              && (CONSTANT_P (op)
                  || (GET_CODE (op) == SUBREG
@@ -709,22 +736,81 @@ simplify_unary_operation (code, mode, op, op_mode)
     }
 }
 \f
+/* Subroutine of simplify_binary_operation, called via do_float_handler.
+   Handles simplification of binary ops on floating point values.  */
+struct simplify_binary_real_args
+{
+  rtx trueop0, trueop1;
+  rtx result;
+  enum rtx_code code;
+  enum machine_mode mode;
+};
+
+static void
+simplify_binary_real (p)
+     PTR p;
+{
+  REAL_VALUE_TYPE f0, f1, value;
+  struct simplify_binary_real_args *args =
+    (struct simplify_binary_real_args *) p;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (f0, args->trueop0);
+  REAL_VALUE_FROM_CONST_DOUBLE (f1, args->trueop1);
+  f0 = real_value_truncate (args->mode, f0);
+  f1 = real_value_truncate (args->mode, f1);
+
+#ifndef REAL_INFINITY
+  if (args->code == DIV && REAL_VALUES_EQUAL (f1, dconst0))
+    {
+      args->result = 0;
+      return;
+    }
+#endif
+  REAL_ARITHMETIC (value, rtx_to_tree_code (args->code), f0, f1);
+
+  value = real_value_truncate (args->mode, value);
+  args->result = CONST_DOUBLE_FROM_REAL_VALUE (value, args->mode);
+}
+
+/* Another subroutine called via do_float_handler.  This one tests
+   the floating point value given against 2. and -1.  */
+struct simplify_binary_is2orm1_args
+{
+  rtx value;
+  bool is_2;
+  bool is_m1;
+};
+
+static void
+simplify_binary_is2orm1 (p)
+     PTR p;
+{
+  REAL_VALUE_TYPE d;
+  struct simplify_binary_is2orm1_args *args =
+    (struct simplify_binary_is2orm1_args *) p;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (d, args->value);
+  args->is_2 = REAL_VALUES_EQUAL (d, dconst2);
+  args->is_m1 = REAL_VALUES_EQUAL (d, dconstm1);
+}
+
 /* 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;
 {
-  register HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
+  HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
   HOST_WIDE_INT val;
   unsigned int width = GET_MODE_BITSIZE (mode);
   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.
@@ -734,84 +820,50 @@ simplify_binary_operation (code, mode, op0, op1)
   if (GET_RTX_CLASS (code) == '<')
     abort ();
 
-#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+  /* Make sure the constant is second.  */
+  if (GET_RTX_CLASS (code) == 'c'
+      && swap_commutative_operands_p (trueop0, trueop1))
+    {
+      tem = op0, op0 = op1, op1 = tem;
+      tem = trueop0, trueop0 = trueop1, trueop1 = tem;
+    }
+
   if (GET_MODE_CLASS (mode) == MODE_FLOAT
-      && GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
+      && GET_CODE (trueop0) == CONST_DOUBLE
+      && GET_CODE (trueop1) == CONST_DOUBLE
       && mode == GET_MODE (op0) && mode == GET_MODE (op1))
     {
-      REAL_VALUE_TYPE f0, f1, value;
-      jmp_buf handler;
-
-      if (setjmp (handler))
-       return 0;
-
-      set_float_handler (handler);
-
-      REAL_VALUE_FROM_CONST_DOUBLE (f0, op0);
-      REAL_VALUE_FROM_CONST_DOUBLE (f1, op1);
-      f0 = real_value_truncate (mode, f0);
-      f1 = real_value_truncate (mode, f1);
-
-#ifdef REAL_ARITHMETIC
-#ifndef REAL_INFINITY
-      if (code == DIV && REAL_VALUES_EQUAL (f1, dconst0))
-       return 0;
-#endif
-      REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
-#else
-      switch (code)
-       {
-       case PLUS:
-         value = f0 + f1;
-         break;
-       case MINUS:
-         value = f0 - f1;
-         break;
-       case MULT:
-         value = f0 * f1;
-         break;
-       case DIV:
-#ifndef REAL_INFINITY
-         if (f1 == 0)
-           return 0;
-#endif
-         value = f0 / f1;
-         break;
-       case SMIN:
-         value = MIN (f0, f1);
-         break;
-       case SMAX:
-         value = MAX (f0, f1);
-         break;
-       default:
-         abort ();
-       }
-#endif
-
-      value = real_value_truncate (mode, value);
-      set_float_handler (NULL);
-      return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
+      struct simplify_binary_real_args args;
+      args.trueop0 = trueop0;
+      args.trueop1 = trueop1;
+      args.mode = mode;
+      args.code = code;
+
+      if (do_float_handler (simplify_binary_real, (PTR) &args))
+       return args.result;
+      return 0;
     }
-#endif  /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
 
   /* We can fold some multi-word operations.  */
   if (GET_MODE_CLASS (mode) == MODE_INT
       && width == HOST_BITS_PER_WIDE_INT * 2
-      && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
-      && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
+      && (GET_CODE (trueop0) == CONST_DOUBLE
+         || GET_CODE (trueop0) == CONST_INT)
+      && (GET_CODE (trueop1) == CONST_DOUBLE
+         || GET_CODE (trueop1) == CONST_INT))
     {
       unsigned HOST_WIDE_INT l1, l2, lv;
       HOST_WIDE_INT h1, h2, hv;
 
-      if (GET_CODE (op0) == CONST_DOUBLE)
-       l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
+      if (GET_CODE (trueop0) == CONST_DOUBLE)
+       l1 = CONST_DOUBLE_LOW (trueop0), h1 = CONST_DOUBLE_HIGH (trueop0);
       else
-       l1 = INTVAL (op0), h1 = HWI_SIGN_EXTEND (l1);
+       l1 = INTVAL (trueop0), h1 = HWI_SIGN_EXTEND (l1);
 
-      if (GET_CODE (op1) == CONST_DOUBLE)
-       l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1);
+      if (GET_CODE (trueop1) == CONST_DOUBLE)
+       l2 = CONST_DOUBLE_LOW (trueop1), h2 = CONST_DOUBLE_HIGH (trueop1);
       else
-       l2 = INTVAL (op1), h2 = HWI_SIGN_EXTEND (l2);
+       l2 = INTVAL (trueop1), h2 = HWI_SIGN_EXTEND (l2);
 
       switch (code)
        {
@@ -931,7 +983,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && FLOAT_MODE_P (mode) && ! flag_unsafe_math_optimizations)
            break;
 
-         if (op1 == CONST0_RTX (mode))
+         if (trueop1 == CONST0_RTX (mode))
            return op0;
 
          /* ((-a) + b) -> (b - a) and similarly for (a + (-b)) */
@@ -943,8 +995,7 @@ simplify_binary_operation (code, mode, op0, op1)
          /* (~a) + 1 -> -a */
          if (INTEGRAL_MODE_P (mode)
              && GET_CODE (op0) == NOT
-             && GET_CODE (op1) == CONST_INT
-             && INTVAL (op1) == 1)
+             && trueop1 == const1_rtx)
            return gen_rtx_NEG (mode, XEXP (op0, 0));
 
          /* Handle both-operands-constant cases.  We can only add
@@ -1022,8 +1073,12 @@ simplify_binary_operation (code, mode, op0, op1)
 
          if (INTEGRAL_MODE_P (mode)
              && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
-                 || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
-             && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
+                 || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS
+                 || (GET_CODE (op0) == CONST
+                     && GET_CODE (XEXP (op0, 0)) == PLUS)
+                 || (GET_CODE (op1) == CONST
+                     && GET_CODE (XEXP (op1, 0)) == PLUS))
+             && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0)
            return tem;
          break;
 
@@ -1037,7 +1092,7 @@ simplify_binary_operation (code, mode, op0, op1)
 
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
               || ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
-             && op1 == CONST0_RTX (mode))
+             && trueop1 == CONST0_RTX (mode))
            return op0;
 #endif
 
@@ -1060,8 +1115,8 @@ simplify_binary_operation (code, mode, op0, op1)
 #endif
                return xop00;
            }
-
          break;              
+
        case MINUS:
          /* None of these optimizations can be done for IEEE
             floating point.  */
@@ -1072,21 +1127,21 @@ simplify_binary_operation (code, mode, op0, op1)
          /* We can't assume x-x is 0 even with non-IEEE floating point,
             but since it is zero except in very strange circumstances, we
             will treat it as zero with -funsafe-math-optimizations.  */
-         if (rtx_equal_p (op0, op1)
+         if (rtx_equal_p (trueop0, trueop1)
              && ! side_effects_p (op0)
              && (! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations))
            return CONST0_RTX (mode);
 
          /* Change subtraction from zero into negation.  */
-         if (op0 == CONST0_RTX (mode))
+         if (trueop0 == CONST0_RTX (mode))
            return gen_rtx_NEG (mode, op1);
 
          /* (-1 - a) is ~a.  */
-         if (op0 == constm1_rtx)
+         if (trueop0 == constm1_rtx)
            return gen_rtx_NOT (mode, op1);
 
          /* Subtracting 0 has no effect.  */
-         if (op1 == CONST0_RTX (mode))
+         if (trueop1 == CONST0_RTX (mode))
            return op0;
 
          /* See if this is something like X * C - X or vice versa or
@@ -1155,13 +1210,19 @@ simplify_binary_operation (code, mode, op0, op1)
 
          if (INTEGRAL_MODE_P (mode)
              && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
-                 || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
-             && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
+                 || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS
+                 || (GET_CODE (op0) == CONST
+                     && GET_CODE (XEXP (op0, 0)) == PLUS)
+                 || (GET_CODE (op1) == CONST
+                     && GET_CODE (XEXP (op1, 0)) == PLUS))
+             && (tem = simplify_plus_minus (code, mode, op0, op1, 0)) != 0)
            return tem;
 
          /* Don't let a relocatable value get a negative coeff.  */
          if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
-           return plus_constant (op0, - INTVAL (op1));
+           return simplify_gen_binary (PLUS, mode,
+                                       op0,
+                                       neg_const_int (mode, op1));
 
          /* (x - (x & y)) -> (x & ~y) */
          if (GET_CODE (op1) == AND)
@@ -1176,7 +1237,7 @@ simplify_binary_operation (code, mode, op0, op1)
          break;
 
        case MULT:
-         if (op1 == constm1_rtx)
+         if (trueop1 == constm1_rtx)
            {
              tem = simplify_unary_operation (NEG, mode, op0, mode);
 
@@ -1186,20 +1247,20 @@ simplify_binary_operation (code, mode, op0, op1)
          /* In IEEE floating point, x*0 is not always 0.  */
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
               || ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
-             && op1 == CONST0_RTX (mode)
+             && trueop1 == CONST0_RTX (mode)
              && ! side_effects_p (op0))
            return op1;
 
          /* In IEEE floating point, x*1 is not equivalent to x for nans.
             However, ANSI says we can drop signals,
             so we can do this anyway.  */
-         if (op1 == CONST1_RTX (mode))
+         if (trueop1 == CONST1_RTX (mode))
            return op0;
 
          /* Convert multiply by constant power of two into shift unless
             we are still generating RTL.  This test is a kludge.  */
-         if (GET_CODE (op1) == CONST_INT
-             && (val = exact_log2 (INTVAL (op1))) >= 0
+         if (GET_CODE (trueop1) == CONST_INT
+             && (val = exact_log2 (INTVAL (trueop1))) >= 0
              /* If the mode is larger than the host word size, and the
                 uppermost bit is set, then this isn't a power of two due
                 to implicit sign extension.  */
@@ -1208,38 +1269,32 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! rtx_equal_function_value_matters)
            return gen_rtx_ASHIFT (mode, op0, GEN_INT (val));
 
-         if (GET_CODE (op1) == CONST_DOUBLE
-             && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT)
+         if (GET_CODE (trueop1) == CONST_DOUBLE
+             && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT)
            {
-             REAL_VALUE_TYPE d;
-             jmp_buf handler;
-             int op1is2, op1ism1;
+             struct simplify_binary_is2orm1_args args;
 
-             if (setjmp (handler))
+             args.value = trueop1;
+             if (! do_float_handler (simplify_binary_is2orm1, (PTR) &args))
                return 0;
 
-             set_float_handler (handler);
-             REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
-             op1is2 = REAL_VALUES_EQUAL (d, dconst2);
-             op1ism1 = REAL_VALUES_EQUAL (d, dconstm1);
-             set_float_handler (NULL);
-
              /* x*2 is x+x and x*(-1) is -x */
-             if (op1is2 && GET_MODE (op0) == mode)
+             if (args.is_2 && GET_MODE (op0) == mode)
                return gen_rtx_PLUS (mode, op0, copy_rtx (op0));
 
-             else if (op1ism1 && GET_MODE (op0) == mode)
+             else if (args.is_m1 && GET_MODE (op0) == mode)
                return gen_rtx_NEG (mode, op0);
            }
          break;
 
        case IOR:
-         if (op1 == const0_rtx)
+         if (trueop1 == const0_rtx)
            return op0;
-         if (GET_CODE (op1) == CONST_INT
-             && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+         if (GET_CODE (trueop1) == CONST_INT
+             && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
+                 == GET_MODE_MASK (mode)))
            return op1;
-         if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+         if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
          /* A | (~A) -> -1 */
          if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
@@ -1250,23 +1305,25 @@ simplify_binary_operation (code, mode, op0, op1)
          break;
 
        case XOR:
-         if (op1 == const0_rtx)
+         if (trueop1 == const0_rtx)
            return op0;
-         if (GET_CODE (op1) == CONST_INT
-             && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+         if (GET_CODE (trueop1) == CONST_INT
+             && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
+                 == GET_MODE_MASK (mode)))
            return gen_rtx_NOT (mode, op0);
-         if (op0 == op1 && ! side_effects_p (op0)
+         if (trueop0 == trueop1 && ! side_effects_p (op0)
              && GET_MODE_CLASS (mode) != MODE_CC)
            return const0_rtx;
          break;
 
        case AND:
-         if (op1 == const0_rtx && ! side_effects_p (op0))
+         if (trueop1 == const0_rtx && ! side_effects_p (op0))
            return const0_rtx;
-         if (GET_CODE (op1) == CONST_INT
-             && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+         if (GET_CODE (trueop1) == CONST_INT
+             && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
+                 == GET_MODE_MASK (mode)))
            return op0;
-         if (op0 == op1 && ! side_effects_p (op0)
+         if (trueop0 == trueop1 && ! side_effects_p (op0)
              && GET_MODE_CLASS (mode) != MODE_CC)
            return op0;
          /* A & (~A) -> 0 */
@@ -1280,60 +1337,62 @@ simplify_binary_operation (code, mode, op0, op1)
        case UDIV:
          /* Convert divide by power of two into shift (divide by 1 handled
             below).  */
-         if (GET_CODE (op1) == CONST_INT
-             && (arg1 = exact_log2 (INTVAL (op1))) > 0)
+         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 (op1 == CONST1_RTX (mode))
-           return op0;
+         if (trueop1 == CONST1_RTX (mode))
+           {
+             /* On some platforms DIV uses narrower mode than its
+                operands.  */
+             rtx x = gen_lowpart_common (mode, op0);
+             if (x)
+               return x;
+             else if (mode != GET_MODE (op0) && GET_MODE (op0) != VOIDmode)
+               return gen_lowpart_SUBREG (mode, op0);
+             else
+               return op0;
+           }
 
          /* In IEEE floating point, 0/x is not always 0.  */
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
               || ! FLOAT_MODE_P (mode) || flag_unsafe_math_optimizations)
-             && op0 == CONST0_RTX (mode)
+             && trueop0 == CONST0_RTX (mode)
              && ! side_effects_p (op1))
            return op0;
 
-#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
          /* Change division by a constant into multiplication.  Only do
             this with -funsafe-math-optimizations.  */
-         else if (GET_CODE (op1) == CONST_DOUBLE
-                  && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
-                  && op1 != CONST0_RTX (mode)
+         else if (GET_CODE (trueop1) == CONST_DOUBLE
+                  && GET_MODE_CLASS (GET_MODE (trueop1)) == MODE_FLOAT
+                  && trueop1 != CONST0_RTX (mode)
                   && flag_unsafe_math_optimizations)
            {
              REAL_VALUE_TYPE d;
-             REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
+             REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
 
              if (! REAL_VALUES_EQUAL (d, dconst0))
                {
-#if defined (REAL_ARITHMETIC)
                  REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
                  return gen_rtx_MULT (mode, op0, 
                                       CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
-#else
-                 return
-                   gen_rtx_MULT (mode, op0, 
-                                 CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
-#endif
                }
            }
-#endif
          break;
 
        case UMOD:
          /* Handle modulus by power of two (mod with 1 handled below).  */
-         if (GET_CODE (op1) == CONST_INT
-             && exact_log2 (INTVAL (op1)) > 0)
+         if (GET_CODE (trueop1) == CONST_INT
+             && exact_log2 (INTVAL (trueop1)) > 0)
            return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
 
          /* ... fall through ...  */
 
        case MOD:
-         if ((op0 == const0_rtx || op1 == const1_rtx)
+         if ((trueop0 == const0_rtx || trueop1 == const1_rtx)
              && ! side_effects_p (op0) && ! side_effects_p (op1))
            return const0_rtx;
          break;
@@ -1341,8 +1400,8 @@ simplify_binary_operation (code, mode, op0, op1)
        case ROTATERT:
        case ROTATE:
          /* Rotating ~0 always results in ~0.  */
-         if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT
-             && (unsigned HOST_WIDE_INT) INTVAL (op0) == GET_MODE_MASK (mode)
+         if (GET_CODE (trueop0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT
+             && (unsigned HOST_WIDE_INT) INTVAL (trueop0) == GET_MODE_MASK (mode)
              && ! side_effects_p (op1))
            return op0;
 
@@ -1351,45 +1410,52 @@ simplify_binary_operation (code, mode, op0, op1)
        case ASHIFT:
        case ASHIFTRT:
        case LSHIFTRT:
-         if (op1 == const0_rtx)
+         if (trueop1 == const0_rtx)
            return op0;
-         if (op0 == const0_rtx && ! side_effects_p (op1))
+         if (trueop0 == const0_rtx && ! side_effects_p (op1))
            return op0;
          break;
 
        case SMIN:
-         if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT 
-             && INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1)
+         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 (op0, op1) && ! side_effects_p (op0))
+         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
          break;
           
        case SMAX:
-         if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
-             && ((unsigned HOST_WIDE_INT) INTVAL (op1)
+         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 (op0, op1) && ! side_effects_p (op0))
+         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
          break;
 
        case UMIN:
-         if (op1 == const0_rtx && ! side_effects_p (op0))
+         if (trueop1 == const0_rtx && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
          break;
            
        case UMAX:
-         if (op1 == constm1_rtx && ! side_effects_p (op0))
+         if (trueop1 == constm1_rtx && ! side_effects_p (op0))
            return op1;
-         else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+         else if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
            return op0;
          break;
 
+       case SS_PLUS:
+       case US_PLUS:
+       case SS_MINUS:
+       case US_MINUS:
+         /* ??? There are simplifications that can be done.  */
+         return 0;
+
        default:
          abort ();
        }
@@ -1400,8 +1466,8 @@ simplify_binary_operation (code, mode, op0, op1)
   /* Get the integer argument values in two forms:
      zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S.  */
 
-  arg0 = INTVAL (op0);
-  arg1 = INTVAL (op1);
+  arg0 = INTVAL (trueop0);
+  arg1 = INTVAL (trueop1);
 
   if (width < HOST_BITS_PER_WIDE_INT)
     {
@@ -1580,19 +1646,40 @@ simplify_binary_operation (code, mode, op0, op1)
 
    Rather than test for specific case, we do this by a brute-force method
    and do all possible simplifications until no more changes occur.  Then
-   we rebuild the operation.  */
+   we rebuild the operation. 
+
+   If FORCE is true, then always generate the rtx.  This is used to 
+   canonicalize stuff emitted from simplify_gen_binary.  */
+
+struct simplify_plus_minus_op_data
+{
+  rtx op;
+  int neg;
+};
+
+static int
+simplify_plus_minus_op_data_cmp (p1, p2)
+     const void *p1;
+     const void *p2;
+{
+  const struct simplify_plus_minus_op_data *d1 = p1;
+  const struct simplify_plus_minus_op_data *d2 = p2;
+
+  return (commutative_operand_precedence (d2->op)
+         - commutative_operand_precedence (d1->op));
+}
 
 static rtx
-simplify_plus_minus (code, mode, op0, op1)
+simplify_plus_minus (code, mode, op0, op1, force)
      enum rtx_code code;
      enum machine_mode mode;
      rtx op0, op1;
+     int force;
 {
-  rtx ops[8];
-  int negs[8];
+  struct simplify_plus_minus_op_data ops[8];
   rtx result, tem;
-  int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0;
-  int first = 1, negate = 0, changed;
+  int n_ops = 2, input_ops = 2, input_consts = 0, n_consts;
+  int first, negate, changed;
   int i, j;
 
   memset ((char *) ops, 0, sizeof ops);
@@ -1601,153 +1688,229 @@ simplify_plus_minus (code, mode, op0, op1)
      changed.  If we run out of room in our array, give up; this should
      almost never happen.  */
 
-  ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS);
+  ops[0].op = op0;
+  ops[0].neg = 0;
+  ops[1].op = op1;
+  ops[1].neg = (code == MINUS);
 
-  changed = 1;
-  while (changed)
+  do
     {
       changed = 0;
 
       for (i = 0; i < n_ops; i++)
-       switch (GET_CODE (ops[i]))
-         {
-         case PLUS:
-         case MINUS:
-           if (n_ops == 7)
-             return 0;
-
-           ops[n_ops] = XEXP (ops[i], 1);
-           negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i];
-           ops[i] = XEXP (ops[i], 0);
-           input_ops++;
-           changed = 1;
-           break;
+       {
+         rtx this_op = ops[i].op;
+         int this_neg = ops[i].neg;
+         enum rtx_code this_code = GET_CODE (this_op);
 
-         case NEG:
-           ops[i] = XEXP (ops[i], 0);
-           negs[i] = ! negs[i];
-           changed = 1;
-           break;
+         switch (this_code)
+           {
+           case PLUS:
+           case MINUS:
+             if (n_ops == 7)
+               {
+                 if (force)
+                   abort ();
+                 return NULL_RTX;
+               }
 
-         case CONST:
-           ops[i] = XEXP (ops[i], 0);
-           input_consts++;
-           changed = 1;
-           break;
+             ops[n_ops].op = XEXP (this_op, 1);
+             ops[n_ops].neg = (this_code == MINUS) ^ this_neg;
+             n_ops++;
+
+             ops[i].op = XEXP (this_op, 0);
+             input_ops++;
+             changed = 1;
+             break;
+
+           case NEG:
+             ops[i].op = XEXP (this_op, 0);
+             ops[i].neg = ! this_neg;
+             changed = 1;
+             break;
+
+           case CONST:
+             if (n_ops < 7
+                 && GET_CODE (XEXP (this_op, 0)) == PLUS
+                 && CONSTANT_P (XEXP (XEXP (this_op, 0), 0))
+                 && CONSTANT_P (XEXP (XEXP (this_op, 0), 1)))
+               {
+                 ops[i].op = XEXP (XEXP (this_op, 0), 0);
+                 ops[n_ops].op = XEXP (XEXP (this_op, 0), 1);
+                 ops[n_ops].neg = this_neg;
+                 n_ops++;
+                 input_consts++;
+                 changed = 1;
+               }
+             break;
 
-         case NOT:
-           /* ~a -> (-a - 1) */
-           if (n_ops != 7)
-             {
-               ops[n_ops] = constm1_rtx;
-               negs[n_ops++] = negs[i];
-               ops[i] = XEXP (ops[i], 0);
-               negs[i] = ! negs[i];
-               changed = 1;
-             }
-           break;
+           case NOT:
+             /* ~a -> (-a - 1) */
+             if (n_ops != 7)
+               {
+                 ops[n_ops].op = constm1_rtx;
+                 ops[n_ops++].neg = this_neg;
+                 ops[i].op = XEXP (this_op, 0);
+                 ops[i].neg = !this_neg;
+                 changed = 1;
+               }
+             break;
 
-         case CONST_INT:
-           if (negs[i])
-             ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1;
-           break;
+           case CONST_INT:
+             if (this_neg)
+               {
+                 ops[i].op = neg_const_int (mode, this_op);
+                 ops[i].neg = 0;
+                 changed = 1;
+               }
+             break;
 
-         default:
-           break;
-         }
+           default:
+             break;
+           }
+       }
     }
+  while (changed);
 
   /* If we only have two operands, we can't do anything.  */
-  if (n_ops <= 2)
-    return 0;
+  if (n_ops <= 2 && !force)
+    return NULL_RTX;
+
+  /* Count the number of CONSTs we didn't split above.  */
+  for (i = 0; i < n_ops; i++)
+    if (GET_CODE (ops[i].op) == CONST)
+      input_consts++;
 
   /* Now simplify each pair of operands until nothing changes.  The first
      time through just simplify constants against each other.  */
 
-  changed = 1;
-  while (changed)
+  first = 1;
+  do
     {
       changed = first;
 
       for (i = 0; i < n_ops - 1; i++)
        for (j = i + 1; j < n_ops; j++)
-         if (ops[i] != 0 && ops[j] != 0
-             && (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j]))))
-           {
-             rtx lhs = ops[i], rhs = ops[j];
-             enum rtx_code ncode = PLUS;
-
-             if (negs[i] && ! negs[j])
-               lhs = ops[j], rhs = ops[i], ncode = MINUS;
-             else if (! negs[i] && negs[j])
-               ncode = MINUS;
-
-             tem = simplify_binary_operation (ncode, mode, lhs, rhs);
-             if (tem)
-               {
-                 ops[i] = tem, ops[j] = 0;
-                 negs[i] = negs[i] && negs[j];
-                 if (GET_CODE (tem) == NEG)
-                   ops[i] = XEXP (tem, 0), negs[i] = ! negs[i];
+         {
+           rtx lhs = ops[i].op, rhs = ops[j].op;
+           int lneg = ops[i].neg, rneg = ops[j].neg;
 
-                 if (GET_CODE (ops[i]) == CONST_INT && negs[i])
-                   ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0;
-                 changed = 1;
-               }
-           }
+           if (lhs != 0 && rhs != 0
+               && (! first || (CONSTANT_P (lhs) && CONSTANT_P (rhs))))
+             {
+               enum rtx_code ncode = PLUS;
+
+               if (lneg != rneg)
+                 {
+                   ncode = MINUS;
+                   if (lneg)
+                     tem = lhs, lhs = rhs, rhs = tem;
+                 }
+               else if (swap_commutative_operands_p (lhs, rhs))
+                 tem = lhs, lhs = rhs, rhs = tem;
+
+               tem = simplify_binary_operation (ncode, mode, lhs, rhs);
+
+               /* Reject "simplifications" that just wrap the two 
+                  arguments in a CONST.  Failure to do so can result
+                  in infinite recursion with simplify_binary_operation
+                  when it calls us to simplify CONST operations.  */
+               if (tem
+                   && ! (GET_CODE (tem) == CONST
+                         && GET_CODE (XEXP (tem, 0)) == ncode
+                         && XEXP (XEXP (tem, 0), 0) == lhs
+                         && XEXP (XEXP (tem, 0), 1) == rhs)
+                   /* Don't allow -x + -1 -> ~x simplifications in the
+                      first pass.  This allows us the chance to combine
+                      the -1 with other constants.  */
+                   && ! (first
+                         && GET_CODE (tem) == NOT
+                         && XEXP (tem, 0) == rhs))
+                 {
+                   lneg &= rneg;
+                   if (GET_CODE (tem) == NEG)
+                     tem = XEXP (tem, 0), lneg = !lneg;
+                   if (GET_CODE (tem) == CONST_INT && lneg)
+                     tem = neg_const_int (mode, tem), lneg = 0;
+
+                   ops[i].op = tem;
+                   ops[i].neg = lneg;
+                   ops[j].op = NULL_RTX;
+                   changed = 1;
+                 }
+             }
+         }
 
       first = 0;
     }
+  while (changed);
 
-  /* Pack all the operands to the lower-numbered entries and give up if
-     we didn't reduce the number of operands we had.  Make sure we
-     count a CONST as two operands.  If we have the same number of
-     operands, but have made more CONSTs than we had, this is also
-     an improvement, so accept it.  */
-
+  /* Pack all the operands to the lower-numbered entries.  */
   for (i = 0, j = 0; j < n_ops; j++)
-    if (ops[j] != 0)
-      {
-       ops[i] = ops[j], negs[i++] = negs[j];
-       if (GET_CODE (ops[j]) == CONST)
-         n_consts++;
-      }
+    if (ops[j].op)
+      ops[i++] = ops[j];
+  n_ops = i;
 
-  if (i + n_consts > input_ops
-      || (i + n_consts == input_ops && n_consts <= input_consts))
-    return 0;
+  /* Sort the operations based on swap_commutative_operands_p.  */
+  qsort (ops, n_ops, sizeof (*ops), simplify_plus_minus_op_data_cmp);
 
-  n_ops = i;
+  /* 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
+     one CONST_INT, and the sort will have ensured that it is last
+     in the array and that any other constant will be next-to-last.  */
 
-  /* If we have a CONST_INT, put it last.  */
-  for (i = 0; i < n_ops - 1; i++)
-    if (GET_CODE (ops[i]) == CONST_INT)
-      {
-       tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem;
-       j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j;
-      }
+  if (n_ops > 1
+      && GET_CODE (ops[n_ops - 1].op) == CONST_INT
+      && CONSTANT_P (ops[n_ops - 2].op))
+    {
+      rtx value = ops[n_ops - 1].op;
+      if (ops[n_ops - 1].neg ^ ops[n_ops - 2].neg)
+       value = neg_const_int (mode, value);
+      ops[n_ops - 2].op = plus_constant (ops[n_ops - 2].op, INTVAL (value));
+      n_ops--;
+    }
+
+  /* Count the number of CONSTs that we generated.  */
+  n_consts = 0;
+  for (i = 0; i < n_ops; i++)
+    if (GET_CODE (ops[i].op) == CONST)
+      n_consts++;
+
+  /* Give up if we didn't reduce the number of operands we had.  Make
+     sure we count a CONST as two operands.  If we have the same
+     number of operands, but have made more CONSTs than before, this
+     is also an improvement, so accept it.  */
+  if (!force
+      && (n_ops + n_consts > input_ops
+          || (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.  */
-  for (i = 0; i < n_ops && negs[i]; i++)
-    ;
 
+  negate = 0;
+  for (i = 0; i < n_ops && ops[i].neg; i++)
+    continue;
   if (i == n_ops)
     {
       for (i = 0; i < n_ops; i++)
-       negs[i] = 0;
+       ops[i].neg = 0;
       negate = 1;
     }
   else if (i != 0)
     {
-      tem = ops[0], ops[0] = ops[i], ops[i] = tem;
-      j = negs[0], negs[0] = negs[i], negs[i] = j;
+      tem = ops[0].op;
+      ops[0] = ops[i];
+      ops[i].op = tem;
+      ops[i].neg = 1;
     }
 
   /* Now make the result by performing the requested operations.  */
-  result = ops[0];
+  result = ops[0].op;
   for (i = 1; i < n_ops; i++)
-    result = simplify_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]);
+    result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS,
+                            mode, result, ops[i].op);
 
   return negate ? gen_rtx_NEG (mode, result) : result;
 }
@@ -1797,6 +1960,8 @@ simplify_relational_operation (code, mode, op0, op1)
 {
   int equal, op0lt, op0ltu, op1lt, op1ltu;
   rtx tem;
+  rtx trueop0;
+  rtx trueop1;
 
   if (mode == VOIDmode
       && (GET_MODE (op0) != VOIDmode
@@ -1807,6 +1972,9 @@ 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
@@ -1817,9 +1985,10 @@ simplify_relational_operation (code, mode, op0, op1)
     return 0;
 
   /* Make sure the constant is second.  */
-  if (swap_commutative_operands_p (op0, op1))
+  if (swap_commutative_operands_p (trueop0, trueop1))
     {
       tem = op0, op0 = op1, op1 = tem;
+      tem = trueop0, trueop0 = trueop1, trueop1 = tem;
       code = swap_condition (code);
     }
 
@@ -1833,9 +2002,9 @@ simplify_relational_operation (code, mode, op0, op1)
      ANSI C defines unsigned operations such that they never overflow, and
      thus such cases can not be ignored.  */
 
-  if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
-      && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
-           && (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
+  if (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))
       && code != GTU && code != GEU && code != LTU && code != LEU)
     return simplify_relational_operation (signed_condition (code),
@@ -1849,23 +2018,23 @@ simplify_relational_operation (code, mode, op0, op1)
 
   /* For non-IEEE floating-point, if the two operands are equal, we know the
      result.  */
-  if (rtx_equal_p (op0, op1)
+  if (rtx_equal_p (trueop0, trueop1)
       && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-         || ! FLOAT_MODE_P (GET_MODE (op0)) 
+         || ! FLOAT_MODE_P (GET_MODE (trueop0)) 
          || flag_unsafe_math_optimizations))
     equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
 
   /* If the operands are floating-point constants, see if we can fold
      the result.  */
-#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
-  else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
-          && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+  else if (GET_CODE (trueop0) == CONST_DOUBLE
+          && GET_CODE (trueop1) == CONST_DOUBLE
+          && GET_MODE_CLASS (GET_MODE (trueop0)) == MODE_FLOAT)
     {
       struct cfc_args args;
 
       /* Setup input for check_fold_consts() */
-      args.op0 = op0;
-      args.op1 = op1;
+      args.op0 = trueop0;
+      args.op1 = trueop1;
       
       
       if (!do_float_handler (check_fold_consts, (PTR) &args))
@@ -1899,37 +2068,38 @@ simplify_relational_operation (code, mode, op0, op1)
       op0lt = op0ltu = args.op0lt;
       op1lt = op1ltu = args.op1lt;
     }
-#endif  /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
 
   /* Otherwise, see if the operands are both integers.  */
   else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
-          && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
-          && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
+          && (GET_CODE (trueop0) == CONST_DOUBLE
+              || GET_CODE (trueop0) == CONST_INT)
+          && (GET_CODE (trueop1) == CONST_DOUBLE
+              || GET_CODE (trueop1) == CONST_INT))
     {
       int width = GET_MODE_BITSIZE (mode);
       HOST_WIDE_INT l0s, h0s, l1s, h1s;
       unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
 
       /* Get the two words comprising each integer constant.  */
-      if (GET_CODE (op0) == CONST_DOUBLE)
+      if (GET_CODE (trueop0) == CONST_DOUBLE)
        {
-         l0u = l0s = CONST_DOUBLE_LOW (op0);
-         h0u = h0s = CONST_DOUBLE_HIGH (op0);
+         l0u = l0s = CONST_DOUBLE_LOW (trueop0);
+         h0u = h0s = CONST_DOUBLE_HIGH (trueop0);
        }
       else
        {
-         l0u = l0s = INTVAL (op0);
+         l0u = l0s = INTVAL (trueop0);
          h0u = h0s = HWI_SIGN_EXTEND (l0s);
        }
          
-      if (GET_CODE (op1) == CONST_DOUBLE)
+      if (GET_CODE (trueop1) == CONST_DOUBLE)
        {
-         l1u = l1s = CONST_DOUBLE_LOW (op1);
-         h1u = h1s = CONST_DOUBLE_HIGH (op1);
+         l1u = l1s = CONST_DOUBLE_LOW (trueop1);
+         h1u = h1s = CONST_DOUBLE_HIGH (trueop1);
        }
       else
        {
-         l1u = l1s = INTVAL (op1);
+         l1u = l1s = INTVAL (trueop1);
          h1u = h1s = HWI_SIGN_EXTEND (l1s);
        }
 
@@ -1964,8 +2134,8 @@ simplify_relational_operation (code, mode, op0, op1)
        case EQ:
          /* References to the frame plus a constant or labels cannot
             be zero, but a SYMBOL_REF can due to #pragma weak.  */
-         if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
-              || GET_CODE (op0) == LABEL_REF)
+         if (((NONZERO_BASE_PLUS_P (op0) && trueop1 == const0_rtx)
+              || GET_CODE (trueop0) == LABEL_REF)
 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
              /* On some machines, the ap reg can be 0 sometimes.  */
              && op0 != arg_pointer_rtx
@@ -1975,8 +2145,8 @@ simplify_relational_operation (code, mode, op0, op1)
          break;
 
        case NE:
-         if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
-              || GET_CODE (op0) == LABEL_REF)
+         if (((NONZERO_BASE_PLUS_P (op0) && trueop1 == const0_rtx)
+              || GET_CODE (trueop0) == LABEL_REF)
 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
              && op0 != arg_pointer_rtx
 #endif
@@ -1986,27 +2156,27 @@ simplify_relational_operation (code, mode, op0, op1)
 
        case GEU:
          /* Unsigned values are never negative.  */
-         if (op1 == const0_rtx)
+         if (trueop1 == const0_rtx)
            return const_true_rtx;
          break;
 
        case LTU:
-         if (op1 == const0_rtx)
+         if (trueop1 == const0_rtx)
            return const0_rtx;
          break;
 
        case LEU:
          /* Unsigned values are never greater than the largest
             unsigned value.  */
-         if (GET_CODE (op1) == CONST_INT
-             && (unsigned HOST_WIDE_INT) INTVAL (op1) == GET_MODE_MASK (mode)
+         if (GET_CODE (trueop1) == CONST_INT
+             && (unsigned HOST_WIDE_INT) INTVAL (trueop1) == GET_MODE_MASK (mode)
            && INTEGRAL_MODE_P (mode))
          return const_true_rtx;
          break;
 
        case GTU:
-         if (GET_CODE (op1) == CONST_INT
-             && (unsigned HOST_WIDE_INT) INTVAL (op1) == GET_MODE_MASK (mode)
+         if (GET_CODE (trueop1) == CONST_INT
+             && (unsigned HOST_WIDE_INT) INTVAL (trueop1) == GET_MODE_MASK (mode)
              && INTEGRAL_MODE_P (mode))
            return const0_rtx;
          break;
@@ -2208,9 +2378,9 @@ simplify_subreg (outermode, op, innermode, byte)
   if (CONSTANT_P (op))
     {
       int offset, part;
-      unsigned HOST_WIDE_INT val;
+      unsigned HOST_WIDE_INT val = 0;
 
-      /* ??? This code is partly redundant with code bellow, but can handle
+      /* ??? 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
@@ -2243,7 +2413,7 @@ simplify_subreg (outermode, op, innermode, byte)
 
          /* We can't handle this case yet.  */
          if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT)
-           return NULL;
+           return NULL_RTX;
 
          part = offset >= HOST_BITS_PER_WIDE_INT;
          if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
@@ -2265,7 +2435,7 @@ simplify_subreg (outermode, op, innermode, byte)
 
          /* We don't handle synthetizing of non-integral constants yet.  */
          if (GET_MODE_CLASS (outermode) != MODE_INT)
-           return NULL;
+           return NULL_RTX;
 
          if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
            {
@@ -2297,41 +2467,63 @@ simplify_subreg (outermode, op, innermode, byte)
   if (GET_CODE (op) == SUBREG)
     {
       enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
-      unsigned int final_offset = byte + SUBREG_BYTE (op);
+      int final_offset = byte + SUBREG_BYTE (op);
       rtx new;
 
       if (outermode == innermostmode
          && byte == 0 && SUBREG_BYTE (op) == 0)
        return SUBREG_REG (op);
 
-      if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
-         && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode)
-         && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (innermostmode))
+      /* The SUBREG_BYTE represents offset, as if the value were stored
+        in memory.  Irritating exception is paradoxical subreg, where
+        we define SUBREG_BYTE to be 0.  On big endian machines, this
+        value should be negative.  For a moment, undo this exception.  */
+      if (byte == 0 && GET_MODE_SIZE (innermode) < GET_MODE_SIZE (outermode))
+       {
+         int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
+         if (WORDS_BIG_ENDIAN)
+           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           final_offset += difference % UNITS_PER_WORD;
+       }
+      if (SUBREG_BYTE (op) == 0
+         && GET_MODE_SIZE (innermostmode) < GET_MODE_SIZE (innermode))
+       {
+         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (innermode));
+         if (WORDS_BIG_ENDIAN)
+           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           final_offset += difference % UNITS_PER_WORD;
+       }
+
+      /* See whether resulting subreg will be paradoxical.  */
+      if (GET_MODE_SIZE (innermostmode) > GET_MODE_SIZE (outermode))
+       {
+         /* In nonparadoxical subregs we can't handle negative offsets.  */
+         if (final_offset < 0)
+           return NULL_RTX;
+         /* Bail out in case resulting subreg would be incorrect.  */
+         if (final_offset % GET_MODE_SIZE (outermode)
+             || (unsigned) final_offset >= GET_MODE_SIZE (innermostmode))
+           return NULL_RTX;
+       }
+      else
        {
-         /* Inner SUBREG is paradoxical, outer is not.  On big endian
-            we have to special case this.  */
-         if (SUBREG_BYTE (op))
-           abort(); /* Can a paradoxical subreg have nonzero offset? */
-         if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
-           final_offset = (byte - GET_MODE_SIZE (innermode)
-                           + GET_MODE_SIZE (innermostmode));
-         else if (WORDS_BIG_ENDIAN)
-           final_offset = ((final_offset % UNITS_PER_WORD)
-                           + ((byte - GET_MODE_SIZE (innermode)
-                               + GET_MODE_SIZE (innermostmode))
-                              * UNITS_PER_WORD) / UNITS_PER_WORD);
+         int offset = 0;
+         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (outermode));
+
+         /* In paradoxical subreg, see if we are still looking on lower part.
+            If so, our SUBREG_BYTE will be 0.  */
+         if (WORDS_BIG_ENDIAN)
+           offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           offset += difference % UNITS_PER_WORD;
+         if (offset == final_offset)
+           final_offset = 0;
          else
-           final_offset = (((final_offset * UNITS_PER_WORD)
-                            / UNITS_PER_WORD)
-                           + ((byte - GET_MODE_SIZE (innermode)
-                               + GET_MODE_SIZE (innermostmode))
-                              % UNITS_PER_WORD));
+           return NULL_RTX;
        }
 
-      /* Bail out in case resulting subreg would be incorrect.  */
-      if (final_offset % GET_MODE_SIZE (outermode)
-         || final_offset >= GET_MODE_SIZE (innermostmode))
-       return NULL;
       /* Recurse for futher possible simplifications.  */
       new = simplify_subreg (outermode, SUBREG_REG (op),
                             GET_MODE (SUBREG_REG (op)),
@@ -2377,7 +2569,18 @@ simplify_subreg (outermode, op, innermode, byte)
         arguments are passed on 32-bit Sparc and should be fixed.  */
       if (HARD_REGNO_MODE_OK (final_regno, outermode)
          || ! HARD_REGNO_MODE_OK (REGNO (op), innermode))
-       return gen_rtx_REG (outermode, final_regno);
+       {
+         rtx x = gen_rtx_REG (outermode, final_regno);
+
+         /* Propagate original regno.  We don't have any way to specify
+            the offset inside orignal regno, so do so only for lowpart.
+            The information is used only by alias analysis that can not
+            grog partial register anyway.  */
+
+         if (subreg_lowpart_offset (outermode, innermode) == byte)
+           ORIGINAL_REGNO (x) = ORIGINAL_REGNO (op);
+         return x;
+       }
     }
 
   /* If we have a SUBREG of a register that we are replacing and we are
@@ -2387,26 +2590,28 @@ simplify_subreg (outermode, op, innermode, byte)
 
   if (GET_CODE (op) == MEM
       && ! mode_dependent_address_p (XEXP (op, 0))
-      && ! MEM_VOLATILE_P (op)
+      /* Allow splitting of volatile memory references in case we don't
+         have instruction to move the whole thing.  */
+      && (! MEM_VOLATILE_P (op)
+         || ! have_insn_for (SET, innermode))
       && GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
-    {
-      rtx new;
-
-      new = gen_rtx_MEM (outermode, plus_constant (XEXP (op, 0), byte));
-      MEM_COPY_ATTRIBUTES (new, op);
-      return new;
-    }
+    return adjust_address_nv (op, outermode, byte);
 
   /* Handle complex values represented as CONCAT
      of real and imaginary part.  */
   if (GET_CODE (op) == CONCAT)
     {
-      int is_realpart = byte < GET_MODE_UNIT_SIZE (innermode) / 2;
+      int is_realpart = byte < GET_MODE_UNIT_SIZE (innermode);
       rtx part = is_realpart ? XEXP (op, 0) : XEXP (op, 1);
       unsigned int final_offset;
-
-      final_offset = byte % (GET_MODE_UNIT_SIZE (innermode) / 2);
-      return simplify_subreg (outermode, part, GET_MODE (part), final_offset);
+      rtx res;
+
+      final_offset = byte % (GET_MODE_UNIT_SIZE (innermode));
+      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.  */
+      return gen_rtx_SUBREG (outermode, part, final_offset);
     }
 
   return NULL_RTX;
@@ -2433,6 +2638,9 @@ simplify_gen_subreg (outermode, op, innermode, byte)
       || byte >= GET_MODE_SIZE (innermode))
     abort ();
 
+  if (GET_CODE (op) == QUEUED)
+    return NULL_RTX;
+
   new = simplify_subreg (outermode, op, innermode, byte);
   if (new)
     return new;
@@ -2494,8 +2702,19 @@ simplify_rtx (x)
     case '1':
       return simplify_unary_operation (code, mode,
                                       XEXP (x, 0), GET_MODE (XEXP (x, 0)));
-    case '2':
     case 'c':
+      if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
+       {
+         rtx tem;
+
+         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));
+       }
+
+    case '2':
       return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
 
     case '3':