Remove REAL_VALUE_FROM_CONST_DOUBLE
[gcc.git] / gcc / simplify-rtx.c
index 791f91a34a87b5a0ed1a96e8ce421fe23dcf38aa..1f91afc05213eb096e6cf5b768d2d3f9d7da5d8f 100644 (file)
@@ -1,5 +1,5 @@
 /* RTL simplification functions for GNU compiler.
-   Copyright (C) 1987-2013 Free Software Foundation, Inc.
+   Copyright (C) 1987-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,19 +21,28 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "predict.h"
 #include "rtl.h"
+#include "alias.h"
 #include "tree.h"
+#include "fold-const.h"
+#include "varasm.h"
 #include "tm_p.h"
 #include "regs.h"
-#include "hard-reg-set.h"
 #include "flags.h"
 #include "insn-config.h"
 #include "recog.h"
-#include "function.h"
+#include "insn-codes.h"
+#include "optabs.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "stmt.h"
 #include "expr.h"
 #include "diagnostic-core.h"
-#include "ggc.h"
 #include "target.h"
 
 /* Simplification and canonicalization of RTL.  */
@@ -45,24 +54,23 @@ along with GCC; see the file COPYING3.  If not see
 #define HWI_SIGN_EXTEND(low) \
  ((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
 
-static rtx neg_const_int (enum machine_mode, const_rtx);
+static rtx neg_const_int (machine_mode, const_rtx);
 static bool plus_minus_operand_p (const_rtx);
-static bool simplify_plus_minus_op_data_cmp (rtx, rtx);
-static rtx simplify_plus_minus (enum rtx_code, enum machine_mode, rtx, rtx);
-static rtx simplify_immed_subreg (enum machine_mode, rtx, enum machine_mode,
+static rtx simplify_plus_minus (enum rtx_code, machine_mode, rtx, rtx);
+static rtx simplify_immed_subreg (machine_mode, rtx, machine_mode,
                                  unsigned int);
-static rtx simplify_associative_operation (enum rtx_code, enum machine_mode,
+static rtx simplify_associative_operation (enum rtx_code, machine_mode,
                                           rtx, rtx);
-static rtx simplify_relational_operation_1 (enum rtx_code, enum machine_mode,
-                                           enum machine_mode, rtx, rtx);
-static rtx simplify_unary_operation_1 (enum rtx_code, enum machine_mode, rtx);
-static rtx simplify_binary_operation_1 (enum rtx_code, enum machine_mode,
+static rtx simplify_relational_operation_1 (enum rtx_code, machine_mode,
+                                           machine_mode, rtx, rtx);
+static rtx simplify_unary_operation_1 (enum rtx_code, machine_mode, rtx);
+static rtx simplify_binary_operation_1 (enum rtx_code, machine_mode,
                                        rtx, rtx, rtx, rtx);
 \f
 /* Negate a CONST_INT rtx, truncating (because a conversion from a
    maximally negative number can overflow).  */
 static rtx
-neg_const_int (enum machine_mode mode, const_rtx i)
+neg_const_int (machine_mode mode, const_rtx i)
 {
   return gen_int_mode (-(unsigned HOST_WIDE_INT) INTVAL (i), mode);
 }
@@ -71,7 +79,7 @@ neg_const_int (enum machine_mode mode, const_rtx i)
    the most significant bit of machine mode MODE.  */
 
 bool
-mode_signbit_p (enum machine_mode mode, const_rtx x)
+mode_signbit_p (machine_mode mode, const_rtx x)
 {
   unsigned HOST_WIDE_INT val;
   unsigned int width;
@@ -86,6 +94,22 @@ mode_signbit_p (enum machine_mode mode, const_rtx x)
   if (width <= HOST_BITS_PER_WIDE_INT
       && CONST_INT_P (x))
     val = INTVAL (x);
+#if TARGET_SUPPORTS_WIDE_INT
+  else if (CONST_WIDE_INT_P (x))
+    {
+      unsigned int i;
+      unsigned int elts = CONST_WIDE_INT_NUNITS (x);
+      if (elts != (width + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT)
+       return false;
+      for (i = 0; i < elts - 1; i++)
+       if (CONST_WIDE_INT_ELT (x, i) != 0)
+         return false;
+      val = CONST_WIDE_INT_ELT (x, elts - 1);
+      width %= HOST_BITS_PER_WIDE_INT;
+      if (width == 0)
+       width = HOST_BITS_PER_WIDE_INT;
+    }
+#else
   else if (width <= HOST_BITS_PER_DOUBLE_INT
           && CONST_DOUBLE_AS_INT_P (x)
           && CONST_DOUBLE_LOW (x) == 0)
@@ -93,8 +117,9 @@ mode_signbit_p (enum machine_mode mode, const_rtx x)
       val = CONST_DOUBLE_HIGH (x);
       width -= HOST_BITS_PER_WIDE_INT;
     }
+#endif
   else
-    /* FIXME: We don't yet have a representation for wider modes.  */
+    /* X is not an integer constant.  */
     return false;
 
   if (width < HOST_BITS_PER_WIDE_INT)
@@ -107,7 +132,7 @@ mode_signbit_p (enum machine_mode mode, const_rtx x)
    precision of MODE is too large to handle.  */
 
 bool
-val_signbit_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+val_signbit_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
 
@@ -125,7 +150,7 @@ val_signbit_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
 /* Test whether the most significant bit of mode MODE is set in VAL.
    Returns false if the precision of MODE is too large to handle.  */
 bool
-val_signbit_known_set_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+val_signbit_known_set_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
 
@@ -143,7 +168,7 @@ val_signbit_known_set_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
 /* Test whether the most significant bit of mode MODE is clear in VAL.
    Returns false if the precision of MODE is too large to handle.  */
 bool
-val_signbit_known_clear_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+val_signbit_known_clear_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
 
@@ -162,7 +187,7 @@ val_signbit_known_clear_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
    seeing if the expression folds.  */
 
 rtx
-simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
+simplify_gen_binary (enum rtx_code code, machine_mode mode, rtx op0,
                     rtx op1)
 {
   rtx tem;
@@ -175,7 +200,7 @@ simplify_gen_binary (enum rtx_code code, enum machine_mode mode, rtx op0,
   /* Put complex operands first and constants second if commutative.  */
   if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
       && swap_commutative_operands_p (op0, op1))
-    tem = op0, op0 = op1, op1 = tem;
+    std::swap (op0, op1);
 
   return gen_rtx_fmt_ee (code, mode, op0, op1);
 }
@@ -186,7 +211,7 @@ rtx
 avoid_constant_pool_reference (rtx x)
 {
   rtx c, tmp, addr;
-  enum machine_mode cmode;
+  machine_mode cmode;
   HOST_WIDE_INT offset = 0;
 
   switch (GET_CODE (x))
@@ -199,12 +224,8 @@ avoid_constant_pool_reference (rtx x)
       tmp = XEXP (x, 0);
       c = avoid_constant_pool_reference (tmp);
       if (c != tmp && CONST_DOUBLE_AS_FLOAT_P (c))
-       {
-         REAL_VALUE_TYPE d;
-
-         REAL_VALUE_FROM_CONST_DOUBLE (d, c);
-         return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (x));
-       }
+       return const_double_from_real_value (*CONST_DOUBLE_REAL_VALUE (c),
+                                            GET_MODE (x));
       return x;
 
     default:
@@ -270,7 +291,7 @@ delegitimize_mem_from_attrs (rtx x)
       && MEM_OFFSET_KNOWN_P (x))
     {
       tree decl = MEM_EXPR (x);
-      enum machine_mode mode = GET_MODE (x);
+      machine_mode mode = GET_MODE (x);
       HOST_WIDE_INT offset = 0;
 
       switch (TREE_CODE (decl))
@@ -298,13 +319,13 @@ delegitimize_mem_from_attrs (rtx x)
                                        &mode, &unsignedp, &volatilep, false);
            if (bitsize != GET_MODE_BITSIZE (mode)
                || (bitpos % BITS_PER_UNIT)
-               || (toffset && !host_integerp (toffset, 0)))
+               || (toffset && !tree_fits_shwi_p (toffset)))
              decl = NULL;
            else
              {
                offset += bitpos / BITS_PER_UNIT;
                if (toffset)
-                 offset += TREE_INT_CST_LOW (toffset);
+                 offset += tree_to_shwi (toffset);
              }
            break;
          }
@@ -360,8 +381,8 @@ delegitimize_mem_from_attrs (rtx x)
    the specified operation.  */
 
 rtx
-simplify_gen_unary (enum rtx_code code, enum machine_mode mode, rtx op,
-                   enum machine_mode op_mode)
+simplify_gen_unary (enum rtx_code code, machine_mode mode, rtx op,
+                   machine_mode op_mode)
 {
   rtx tem;
 
@@ -375,8 +396,8 @@ simplify_gen_unary (enum rtx_code code, enum machine_mode mode, rtx op,
 /* Likewise for ternary operations.  */
 
 rtx
-simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
-                     enum machine_mode op0_mode, rtx op0, rtx op1, rtx op2)
+simplify_gen_ternary (enum rtx_code code, machine_mode mode,
+                     machine_mode op0_mode, rtx op0, rtx op1, rtx op2)
 {
   rtx tem;
 
@@ -392,8 +413,8 @@ simplify_gen_ternary (enum rtx_code code, enum machine_mode mode,
    CMP_MODE specifies mode comparison is done in.  */
 
 rtx
-simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
-                        enum machine_mode cmp_mode, rtx op0, rtx op1)
+simplify_gen_relational (enum rtx_code code, machine_mode mode,
+                        machine_mode cmp_mode, rtx op0, rtx op1)
 {
   rtx tem;
 
@@ -414,8 +435,8 @@ simplify_replace_fn_rtx (rtx x, const_rtx old_rtx,
                         rtx (*fn) (rtx, const_rtx, void *), void *data)
 {
   enum rtx_code code = GET_CODE (x);
-  enum machine_mode mode = GET_MODE (x);
-  enum machine_mode op_mode;
+  machine_mode mode = GET_MODE (x);
+  machine_mode op_mode;
   const char *fmt;
   rtx op0, op1, op2, newx, op;
   rtvec vec, newvec;
@@ -498,9 +519,15 @@ simplify_replace_fn_rtx (rtx x, const_rtx old_rtx,
          op0 = simplify_replace_fn_rtx (XEXP (x, 0), old_rtx, fn, data);
          op1 = simplify_replace_fn_rtx (XEXP (x, 1), old_rtx, fn, data);
 
-         /* (lo_sum (high x) x) -> x  */
-         if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1))
-           return op1;
+         /* (lo_sum (high x) y) -> y where x and y have the same base.  */
+         if (GET_CODE (op0) == HIGH)
+           {
+             rtx base0, base1, offset0, offset1;
+             split_const (XEXP (op0, 0), &base0, &offset0);
+             split_const (op1, &base1, &offset1);
+             if (rtx_equal_p (base0, base1))
+               return op1;
+           }
 
          if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
            return x;
@@ -611,8 +638,8 @@ simplify_replace_rtx (rtx x, const_rtx old_rtx, rtx new_rtx)
    assume that truncating it too is a no-op.  */
 
 static rtx
-simplify_truncation (enum machine_mode mode, rtx op,
-                    enum machine_mode op_mode)
+simplify_truncation (machine_mode mode, rtx op,
+                    machine_mode op_mode)
 {
   unsigned int precision = GET_MODE_UNIT_PRECISION (mode);
   unsigned int op_precision = GET_MODE_UNIT_PRECISION (op_mode);
@@ -628,7 +655,7 @@ simplify_truncation (enum machine_mode mode, rtx op,
         truncation without the extension.  Finally, if the outermode
         is larger than the origmode, we can just extend to the appropriate
         mode.  */
-      enum machine_mode origmode = GET_MODE (XEXP (op, 0));
+      machine_mode origmode = GET_MODE (XEXP (op, 0));
       if (mode == origmode)
        return XEXP (op, 0);
       else if (precision <= GET_MODE_UNIT_PRECISION (origmode))
@@ -639,11 +666,14 @@ simplify_truncation (enum machine_mode mode, rtx op,
                                   XEXP (op, 0), origmode);
     }
 
-  /* Simplify (truncate:SI (op:DI (x:DI) (y:DI)))
-     to (op:SI (truncate:SI (x:DI)) (truncate:SI (x:DI))).  */
-  if (GET_CODE (op) == PLUS
-      || GET_CODE (op) == MINUS
-      || GET_CODE (op) == MULT)
+  /* If the machine can perform operations in the truncated mode, distribute
+     the truncation, i.e. simplify (truncate:QI (op:SI (x:SI) (y:SI))) into
+     (op:QI (truncate:QI (x:SI)) (truncate:QI (y:SI))).  */
+  if (1
+      && (!WORD_REGISTER_OPERATIONS || precision >= BITS_PER_WORD)
+      && (GET_CODE (op) == PLUS
+         || GET_CODE (op) == MINUS
+         || GET_CODE (op) == MULT))
     {
       rtx op0 = simplify_gen_unary (TRUNCATE, mode, XEXP (op, 0), op_mode);
       if (op0)
@@ -781,8 +811,8 @@ simplify_truncation (enum machine_mode mode, rtx op,
    MODE with input operand OP whose mode was originally OP_MODE.
    Return zero if no simplification can be made.  */
 rtx
-simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
-                         rtx op, enum machine_mode op_mode)
+simplify_unary_operation (enum rtx_code code, machine_mode mode,
+                         rtx op, machine_mode op_mode)
 {
   rtx trueop, tem;
 
@@ -795,10 +825,36 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
   return simplify_unary_operation_1 (code, mode, op);
 }
 
+/* Return true if FLOAT or UNSIGNED_FLOAT operation OP is known
+   to be exact.  */
+
+static bool
+exact_int_to_float_conversion_p (const_rtx op)
+{
+  int out_bits = significand_size (GET_MODE_INNER (GET_MODE (op)));
+  machine_mode op0_mode = GET_MODE (XEXP (op, 0));
+  /* Constants shouldn't reach here.  */
+  gcc_assert (op0_mode != VOIDmode);
+  int in_prec = GET_MODE_UNIT_PRECISION (op0_mode);
+  int in_bits = in_prec;
+  if (HWI_COMPUTABLE_MODE_P (op0_mode))
+    {
+      unsigned HOST_WIDE_INT nonzero = nonzero_bits (XEXP (op, 0), op0_mode);
+      if (GET_CODE (op) == FLOAT)
+       in_bits -= num_sign_bit_copies (XEXP (op, 0), op0_mode);
+      else if (GET_CODE (op) == UNSIGNED_FLOAT)
+       in_bits = wi::min_precision (wi::uhwi (nonzero, in_prec), UNSIGNED);
+      else
+       gcc_unreachable ();
+      in_bits -= wi::ctz (wi::uhwi (nonzero, in_prec));
+    }
+  return in_bits <= out_bits;
+}
+
 /* Perform some simplifications we can do even if the operands
    aren't constant.  */
 static rtx
-simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
+simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
 {
   enum rtx_code reversed;
   rtx temp;
@@ -825,7 +881,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
 
       /* Similarly, (not (neg X)) is (plus X -1).  */
       if (GET_CODE (op) == NEG)
-       return plus_constant (mode, XEXP (op, 0), -1);
+       return simplify_gen_binary (PLUS, mode, XEXP (op, 0),
+                                   CONSTM1_RTX (mode));
 
       /* (not (xor X C)) for C constant is (xor X D) with D = ~C.  */
       if (GET_CODE (op) == XOR
@@ -858,10 +915,9 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
       /* (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_P (XEXP (op, 1))
          && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
        return simplify_gen_relational (GE, mode, VOIDmode,
                                        XEXP (op, 0), const0_rtx);
@@ -874,7 +930,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && GET_CODE (SUBREG_REG (op)) == ASHIFT
          && XEXP (SUBREG_REG (op), 0) == const1_rtx)
        {
-         enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op));
+         machine_mode inner_mode = GET_MODE (SUBREG_REG (op));
          rtx x;
 
          x = gen_rtx_ROTATE (inner_mode,
@@ -890,11 +946,10 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         with negating logical insns (and-not, nand, etc.).  If result has
         only one NOT, put it first, since that is how the patterns are
         coded.  */
-
       if (GET_CODE (op) == IOR || GET_CODE (op) == AND)
        {
          rtx in1 = XEXP (op, 0), in2 = XEXP (op, 1);
-         enum machine_mode op_mode;
+         machine_mode op_mode;
 
          op_mode = GET_MODE (in1);
          in1 = simplify_gen_unary (NOT, op_mode, in1, op_mode);
@@ -905,14 +960,18 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          in2 = simplify_gen_unary (NOT, op_mode, in2, op_mode);
 
          if (GET_CODE (in2) == NOT && GET_CODE (in1) != NOT)
-           {
-             rtx tem = in2;
-             in2 = in1; in1 = tem;
-           }
+           std::swap (in1, in2);
 
          return gen_rtx_fmt_ee (GET_CODE (op) == IOR ? AND : IOR,
                                 mode, in1, in2);
        }
+
+      /* (not (bswap x)) -> (bswap (not x)).  */
+      if (GET_CODE (op) == BSWAP)
+       {
+         rtx x = simplify_gen_unary (NOT, mode, XEXP (op, 0), mode);
+         return simplify_gen_unary (BSWAP, mode, x, mode);
+       }
       break;
 
     case NEG:
@@ -920,6 +979,32 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
       if (GET_CODE (op) == NEG)
        return XEXP (op, 0);
 
+      /* (neg (x ? (neg y) : y)) == !x ? (neg y) : y.
+        If comparison is not reversible use
+        x ? y : (neg y).  */
+      if (GET_CODE (op) == IF_THEN_ELSE)
+       {
+         rtx cond = XEXP (op, 0);
+         rtx true_rtx = XEXP (op, 1);
+         rtx false_rtx = XEXP (op, 2);
+
+         if ((GET_CODE (true_rtx) == NEG
+              && rtx_equal_p (XEXP (true_rtx, 0), false_rtx))
+              || (GET_CODE (false_rtx) == NEG
+                  && rtx_equal_p (XEXP (false_rtx, 0), true_rtx)))
+           {
+             if (reversed_comparison_code (cond, NULL_RTX) != UNKNOWN)
+               temp = reversed_comparison (cond, mode);
+             else
+               {
+                 temp = cond;
+                 std::swap (true_rtx, false_rtx);
+               }
+             return simplify_gen_ternary (IF_THEN_ELSE, mode,
+                                           mode, temp, true_rtx, false_rtx);
+           }
+       }
+
       /* (neg (plus X 1)) can become (not X).  */
       if (GET_CODE (op) == PLUS
          && XEXP (op, 1) == const1_rtx)
@@ -927,7 +1012,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
 
       /* Similarly, (neg (not X)) is (plus X 1).  */
       if (GET_CODE (op) == NOT)
-       return plus_constant (mode, XEXP (op, 0), 1);
+       return simplify_gen_binary (PLUS, mode, XEXP (op, 0),
+                                   CONST1_RTX (mode));
 
       /* (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
@@ -1004,7 +1090,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && XEXP (op, 1) == const0_rtx
          && SCALAR_INT_MODE_P (GET_MODE (XEXP (op, 0))))
        {
-         enum machine_mode inner = GET_MODE (XEXP (op, 0));
+         machine_mode inner = GET_MODE (XEXP (op, 0));
          int isize = GET_MODE_PRECISION (inner);
          if (STORE_FLAG_VALUE == 1)
            {
@@ -1112,7 +1198,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          = (float_truncate:SF foo:DF).
 
          (float_truncate:DF (float_extend:XF foo:SF))
-         = (float_extend:SF foo:DF).  */
+         = (float_extend:DF foo:SF).  */
       if ((GET_CODE (op) == FLOAT_TRUNCATE
           && flag_unsafe_math_optimizations)
          || GET_CODE (op) == FLOAT_EXTEND)
@@ -1124,14 +1210,10 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
                                   XEXP (op, 0), mode);
 
       /*  (float_truncate (float x)) is (float x)  */
-      if (GET_CODE (op) == FLOAT
+      if ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
          && (flag_unsafe_math_optimizations
-             || (SCALAR_FLOAT_MODE_P (GET_MODE (op))
-                 && ((unsigned)significand_size (GET_MODE (op))
-                     >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
-                         - num_sign_bit_copies (XEXP (op, 0),
-                                                GET_MODE (XEXP (op, 0))))))))
-       return simplify_gen_unary (FLOAT, mode,
+             || exact_int_to_float_conversion_p (op)))
+       return simplify_gen_unary (GET_CODE (op), mode,
                                   XEXP (op, 0),
                                   GET_MODE (XEXP (op, 0)));
 
@@ -1162,12 +1244,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          rounding can't happen.
           */
       if (GET_CODE (op) == FLOAT_EXTEND
-         || (GET_CODE (op) == FLOAT
-             && SCALAR_FLOAT_MODE_P (GET_MODE (op))
-             && ((unsigned)significand_size (GET_MODE (op))
-                 >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
-                     - num_sign_bit_copies (XEXP (op, 0),
-                                            GET_MODE (XEXP (op, 0)))))))
+         || ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
+             && exact_int_to_float_conversion_p (op)))
        return simplify_gen_unary (GET_CODE (op), mode,
                                   XEXP (op, 0),
                                   GET_MODE (XEXP (op, 0)));
@@ -1291,8 +1369,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
              && (rcode == SIGN_EXTEND
                  || (rcode == ASHIFTRT && CONST_INT_P (XEXP (rhs, 1)))))
            {
-             enum machine_mode lmode = GET_MODE (lhs);
-             enum machine_mode rmode = GET_MODE (rhs);
+             machine_mode lmode = GET_MODE (lhs);
+             machine_mode rmode = GET_MODE (rhs);
              int bits;
 
              if (lcode == ASHIFTRT)
@@ -1322,7 +1400,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         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)
+         && SUBREG_PROMOTED_SIGNED_P (op)
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
        {
          temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
@@ -1334,8 +1412,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         (sign_extend:M (zero_extend:N <X>)) is (zero_extend:M <X>).  */
       if (GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND)
        {
-         gcc_assert (GET_MODE_BITSIZE (mode)
-                     > GET_MODE_BITSIZE (GET_MODE (op)));
+         gcc_assert (GET_MODE_PRECISION (mode)
+                     > GET_MODE_PRECISION (GET_MODE (op)));
          return simplify_gen_unary (GET_CODE (op), mode, XEXP (op, 0),
                                     GET_MODE (XEXP (op, 0)));
        }
@@ -1351,7 +1429,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
          && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
        {
-         enum machine_mode tmode
+         machine_mode tmode
            = mode_for_size (GET_MODE_BITSIZE (GET_MODE (op))
                             - INTVAL (XEXP (op, 1)), MODE_INT, 1);
          gcc_assert (GET_MODE_BITSIZE (mode)
@@ -1367,7 +1445,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
            }
        }
 
-#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
+#if defined(POINTERS_EXTEND_UNSIGNED)
       /* As we do not know which address space the pointer is referring to,
         we can do this only if the target does not support different pointer
         or address modes depending on the address space.  */
@@ -1378,7 +1456,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
              || (GET_CODE (op) == SUBREG
                  && REG_P (SUBREG_REG (op))
                  && REG_POINTER (SUBREG_REG (op))
-                 && GET_MODE (SUBREG_REG (op)) == Pmode)))
+                 && GET_MODE (SUBREG_REG (op)) == Pmode))
+         && !targetm.have_ptr_extend ())
        return convert_memory_address (Pmode, op);
 #endif
       break;
@@ -1389,7 +1468,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         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) > 0
+         && SUBREG_PROMOTED_UNSIGNED_P (op)
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
        {
          temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
@@ -1413,8 +1492,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
              && (rcode == ZERO_EXTEND
                  || (rcode == LSHIFTRT && CONST_INT_P (XEXP (rhs, 1)))))
            {
-             enum machine_mode lmode = GET_MODE (lhs);
-             enum machine_mode rmode = GET_MODE (rhs);
+             machine_mode lmode = GET_MODE (lhs);
+             machine_mode rmode = GET_MODE (rhs);
              int bits;
 
              if (lcode == LSHIFTRT)
@@ -1446,15 +1525,15 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
 
       /* (zero_extend:M (lshiftrt:N (ashift <X> (const_int I)) (const_int I)))
         is (zero_extend:M (subreg:O <X>)) if there is mode with
-        GET_MODE_BITSIZE (N) - I bits.  */
+        GET_MODE_PRECISION (N) - I bits.  */
       if (GET_CODE (op) == LSHIFTRT
          && GET_CODE (XEXP (op, 0)) == ASHIFT
          && CONST_INT_P (XEXP (op, 1))
          && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
-         && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
+         && GET_MODE_PRECISION (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
        {
-         enum machine_mode tmode
-           = mode_for_size (GET_MODE_BITSIZE (GET_MODE (op))
+         machine_mode tmode
+           = mode_for_size (GET_MODE_PRECISION (GET_MODE (op))
                             - INTVAL (XEXP (op, 1)), MODE_INT, 1);
          if (tmode != BLKmode)
            {
@@ -1465,7 +1544,30 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
            }
        }
 
-#if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
+      /* (zero_extend:M (subreg:N <X:O>)) is <X:O> (for M == O) or
+        (zero_extend:M <X:O>), if X doesn't have any non-zero bits outside
+        of mode N.  E.g.
+        (zero_extend:SI (subreg:QI (and:SI (reg:SI) (const_int 63)) 0)) is
+        (and:SI (reg:SI) (const_int 63)).  */
+      if (GET_CODE (op) == SUBREG
+         && GET_MODE_PRECISION (GET_MODE (op))
+            < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
+         && GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
+            <= HOST_BITS_PER_WIDE_INT
+         && GET_MODE_PRECISION (mode)
+            >= GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
+         && subreg_lowpart_p (op)
+         && (nonzero_bits (SUBREG_REG (op), GET_MODE (SUBREG_REG (op)))
+             & ~GET_MODE_MASK (GET_MODE (op))) == 0)
+       {
+         if (GET_MODE_PRECISION (mode)
+             == GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op))))
+           return SUBREG_REG (op);
+         return simplify_gen_unary (ZERO_EXTEND, mode, SUBREG_REG (op),
+                                    GET_MODE (SUBREG_REG (op)));
+       }
+
+#if defined(POINTERS_EXTEND_UNSIGNED)
       /* As we do not know which address space the pointer is referring to,
         we can do this only if the target does not support different pointer
         or address modes depending on the address space.  */
@@ -1476,7 +1578,8 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
              || (GET_CODE (op) == SUBREG
                  && REG_P (SUBREG_REG (op))
                  && REG_POINTER (SUBREG_REG (op))
-                 && GET_MODE (SUBREG_REG (op)) == Pmode)))
+                 && GET_MODE (SUBREG_REG (op)) == Pmode))
+         && !targetm.have_ptr_extend ())
        return convert_memory_address (Pmode, op);
 #endif
       break;
@@ -1492,11 +1595,10 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
    be MODE with input operand OP whose mode was originally OP_MODE.
    Return zero if the value cannot be computed.  */
 rtx
-simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
-                               rtx op, enum machine_mode op_mode)
+simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
+                               rtx op, machine_mode op_mode)
 {
   unsigned int width = GET_MODE_PRECISION (mode);
-  unsigned int op_width = GET_MODE_PRECISION (op_mode);
 
   if (code == VEC_DUPLICATE)
     {
@@ -1512,7 +1614,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
       if (CONST_SCALAR_INT_P (op) || CONST_DOUBLE_AS_FLOAT_P (op)
          || GET_CODE (op) == CONST_VECTOR)
        {
-          int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+         int elt_size = GET_MODE_UNIT_SIZE (mode);
           unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
          rtvec v = rtvec_alloc (n_elts);
          unsigned int i;
@@ -1522,8 +1624,8 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
              RTVEC_ELT (v, i) = op;
          else
            {
-             enum machine_mode inmode = GET_MODE (op);
-              int in_elt_size = GET_MODE_SIZE (GET_MODE_INNER (inmode));
+             machine_mode inmode = GET_MODE (op);
+             int in_elt_size = GET_MODE_UNIT_SIZE (inmode);
               unsigned in_n_elts = (GET_MODE_SIZE (inmode) / in_elt_size);
 
              gcc_assert (in_n_elts < n_elts);
@@ -1537,10 +1639,10 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
 
   if (VECTOR_MODE_P (mode) && GET_CODE (op) == CONST_VECTOR)
     {
-      int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+      int elt_size = GET_MODE_UNIT_SIZE (mode);
       unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
-      enum machine_mode opmode = GET_MODE (op);
-      int op_elt_size = GET_MODE_SIZE (GET_MODE_INNER (opmode));
+      machine_mode opmode = GET_MODE (op);
+      int op_elt_size = GET_MODE_UNIT_SIZE (opmode);
       unsigned op_n_elts = (GET_MODE_SIZE (opmode) / op_elt_size);
       rtvec v = rtvec_alloc (n_elts);
       unsigned int i;
@@ -1564,353 +1666,134 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
 
   if (code == FLOAT && CONST_SCALAR_INT_P (op))
     {
-      HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
-      if (CONST_INT_P (op))
-       lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv);
-      else
-       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
+      if (op_mode == VOIDmode)
+       {
+         /* CONST_INT have VOIDmode as the mode.  We assume that all
+            the bits of the constant are significant, though, this is
+            a dangerous assumption as many times CONST_INTs are
+            created and used with garbage in the bits outside of the
+            precision of the implied mode of the const_int.  */
+         op_mode = MAX_MODE_INT;
+       }
 
-      REAL_VALUE_FROM_INT (d, lv, hv, mode);
+      real_from_integer (&d, mode, std::make_pair (op, op_mode), SIGNED);
       d = real_value_truncate (mode, d);
-      return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+      return const_double_from_real_value (d, mode);
     }
   else if (code == UNSIGNED_FLOAT && CONST_SCALAR_INT_P (op))
     {
-      HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
-      if (CONST_INT_P (op))
-       lv = INTVAL (op), hv = HWI_SIGN_EXTEND (lv);
-      else
-       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
-
-      if (op_mode == VOIDmode
-         || GET_MODE_PRECISION (op_mode) > HOST_BITS_PER_DOUBLE_INT)
-       /* We should never get a negative number.  */
-       gcc_assert (hv >= 0);
-      else if (GET_MODE_PRECISION (op_mode) <= HOST_BITS_PER_WIDE_INT)
-       hv = 0, lv &= GET_MODE_MASK (op_mode);
+      if (op_mode == VOIDmode)
+       {
+         /* CONST_INT have VOIDmode as the mode.  We assume that all
+            the bits of the constant are significant, though, this is
+            a dangerous assumption as many times CONST_INTs are
+            created and used with garbage in the bits outside of the
+            precision of the implied mode of the const_int.  */
+         op_mode = MAX_MODE_INT;
+       }
 
-      REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
+      real_from_integer (&d, mode, std::make_pair (op, op_mode), UNSIGNED);
       d = real_value_truncate (mode, d);
-      return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+      return const_double_from_real_value (d, mode);
     }
 
-  if (CONST_INT_P (op)
-      && width <= HOST_BITS_PER_WIDE_INT && width > 0)
+  if (CONST_SCALAR_INT_P (op) && width > 0)
     {
-      HOST_WIDE_INT arg0 = INTVAL (op);
-      HOST_WIDE_INT val;
+      wide_int result;
+      machine_mode imode = op_mode == VOIDmode ? mode : op_mode;
+      rtx_mode_t op0 = std::make_pair (op, imode);
+      int int_value;
+
+#if TARGET_SUPPORTS_WIDE_INT == 0
+      /* This assert keeps the simplification from producing a result
+        that cannot be represented in a CONST_DOUBLE but a lot of
+        upstream callers expect that this function never fails to
+        simplify something and so you if you added this to the test
+        above the code would die later anyway.  If this assert
+        happens, you just need to make the port support wide int.  */
+      gcc_assert (width <= HOST_BITS_PER_DOUBLE_INT);
+#endif
 
       switch (code)
        {
        case NOT:
-         val = ~ arg0;
+         result = wi::bit_not (op0);
          break;
 
        case NEG:
-         val = - arg0;
+         result = wi::neg (op0);
          break;
 
        case ABS:
-         val = (arg0 >= 0 ? arg0 : - arg0);
+         result = wi::abs (op0);
          break;
 
        case FFS:
-         arg0 &= GET_MODE_MASK (mode);
-         val = ffs_hwi (arg0);
+         result = wi::shwi (wi::ffs (op0), mode);
          break;
 
        case CLZ:
-         arg0 &= GET_MODE_MASK (mode);
-         if (arg0 == 0 && CLZ_DEFINED_VALUE_AT_ZERO (mode, val))
-           ;
-         else
-           val = GET_MODE_PRECISION (mode) - floor_log2 (arg0) - 1;
+         if (wi::ne_p (op0, 0))
+           int_value = wi::clz (op0);
+         else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, int_value))
+           int_value = GET_MODE_PRECISION (mode);
+         result = wi::shwi (int_value, mode);
          break;
 
        case CLRSB:
-         arg0 &= GET_MODE_MASK (mode);
-         if (arg0 == 0)
-           val = GET_MODE_PRECISION (mode) - 1;
-         else if (arg0 >= 0)
-           val = GET_MODE_PRECISION (mode) - floor_log2 (arg0) - 2;
-         else if (arg0 < 0)
-           val = GET_MODE_PRECISION (mode) - floor_log2 (~arg0) - 2;
-         break;
-
-       case CTZ:
-         arg0 &= GET_MODE_MASK (mode);
-         if (arg0 == 0)
-           {
-             /* Even if the value at zero is undefined, we have to come
-                up with some replacement.  Seems good enough.  */
-             if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, val))
-               val = GET_MODE_PRECISION (mode);
-           }
-         else
-           val = ctz_hwi (arg0);
-         break;
-
-       case POPCOUNT:
-         arg0 &= GET_MODE_MASK (mode);
-         val = 0;
-         while (arg0)
-           val++, arg0 &= arg0 - 1;
-         break;
-
-       case PARITY:
-         arg0 &= GET_MODE_MASK (mode);
-         val = 0;
-         while (arg0)
-           val++, arg0 &= arg0 - 1;
-         val &= 1;
-         break;
-
-       case BSWAP:
-         {
-           unsigned int s;
-
-           val = 0;
-           for (s = 0; s < width; s += 8)
-             {
-               unsigned int d = width - s - 8;
-               unsigned HOST_WIDE_INT byte;
-               byte = (arg0 >> s) & 0xff;
-               val |= byte << d;
-             }
-         }
-         break;
-
-       case TRUNCATE:
-         val = arg0;
-         break;
-
-       case ZERO_EXTEND:
-         /* When zero-extending a CONST_INT, we need to know its
-             original mode.  */
-         gcc_assert (op_mode != VOIDmode);
-         if (op_width == HOST_BITS_PER_WIDE_INT)
-           {
-             /* If we were really extending the mode,
-                we would have to distinguish between zero-extension
-                and sign-extension.  */
-             gcc_assert (width == op_width);
-             val = arg0;
-           }
-         else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
-           val = arg0 & GET_MODE_MASK (op_mode);
-         else
-           return 0;
-         break;
-
-       case SIGN_EXTEND:
-         if (op_mode == VOIDmode)
-           op_mode = mode;
-         op_width = GET_MODE_PRECISION (op_mode);
-         if (op_width == HOST_BITS_PER_WIDE_INT)
-           {
-             /* If we were really extending the mode,
-                we would have to distinguish between zero-extension
-                and sign-extension.  */
-             gcc_assert (width == op_width);
-             val = arg0;
-           }
-         else if (op_width < HOST_BITS_PER_WIDE_INT)
-           {
-             val = arg0 & GET_MODE_MASK (op_mode);
-             if (val_signbit_known_set_p (op_mode, val))
-               val |= ~GET_MODE_MASK (op_mode);
-           }
-         else
-           return 0;
-         break;
-
-       case SQRT:
-       case FLOAT_EXTEND:
-       case FLOAT_TRUNCATE:
-       case SS_TRUNCATE:
-       case US_TRUNCATE:
-       case SS_NEG:
-       case US_NEG:
-       case SS_ABS:
-         return 0;
-
-       default:
-         gcc_unreachable ();
-       }
-
-      return gen_int_mode (val, mode);
-    }
-
-  /* We can do some operations on integer CONST_DOUBLEs.  Also allow
-     for a DImode operation on a CONST_INT.  */
-  else if (width <= HOST_BITS_PER_DOUBLE_INT
-          && (CONST_DOUBLE_AS_INT_P (op) || CONST_INT_P (op)))
-    {
-      double_int first, value;
-
-      if (CONST_DOUBLE_AS_INT_P (op))
-       first = double_int::from_pair (CONST_DOUBLE_HIGH (op),
-                                      CONST_DOUBLE_LOW (op));
-      else
-       first = double_int::from_shwi (INTVAL (op));
-
-      switch (code)
-       {
-       case NOT:
-         value = ~first;
-         break;
-
-       case NEG:
-         value = -first;
-         break;
-
-       case ABS:
-         if (first.is_negative ())
-           value = -first;
-         else
-           value = first;
-         break;
-
-       case FFS:
-         value.high = 0;
-         if (first.low != 0)
-           value.low = ffs_hwi (first.low);
-         else if (first.high != 0)
-           value.low = HOST_BITS_PER_WIDE_INT + ffs_hwi (first.high);
-         else
-           value.low = 0;
-         break;
-
-       case CLZ:
-         value.high = 0;
-         if (first.high != 0)
-           value.low = GET_MODE_PRECISION (mode) - floor_log2 (first.high) - 1
-                     - HOST_BITS_PER_WIDE_INT;
-         else if (first.low != 0)
-           value.low = GET_MODE_PRECISION (mode) - floor_log2 (first.low) - 1;
-         else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, value.low))
-           value.low = GET_MODE_PRECISION (mode);
+         result = wi::shwi (wi::clrsb (op0), mode);
          break;
 
        case CTZ:
-         value.high = 0;
-         if (first.low != 0)
-           value.low = ctz_hwi (first.low);
-         else if (first.high != 0)
-           value.low = HOST_BITS_PER_WIDE_INT + ctz_hwi (first.high);
-         else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, value.low))
-           value.low = GET_MODE_PRECISION (mode);
+         if (wi::ne_p (op0, 0))
+           int_value = wi::ctz (op0);
+         else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, int_value))
+           int_value = GET_MODE_PRECISION (mode);
+         result = wi::shwi (int_value, mode);
          break;
 
        case POPCOUNT:
-         value = double_int_zero;
-         while (first.low)
-           {
-             value.low++;
-             first.low &= first.low - 1;
-           }
-         while (first.high)
-           {
-             value.low++;
-             first.high &= first.high - 1;
-           }
+         result = wi::shwi (wi::popcount (op0), mode);
          break;
 
        case PARITY:
-         value = double_int_zero;
-         while (first.low)
-           {
-             value.low++;
-             first.low &= first.low - 1;
-           }
-         while (first.high)
-           {
-             value.low++;
-             first.high &= first.high - 1;
-           }
-         value.low &= 1;
+         result = wi::shwi (wi::parity (op0), mode);
          break;
 
        case BSWAP:
-         {
-           unsigned int s;
-
-           value = double_int_zero;
-           for (s = 0; s < width; s += 8)
-             {
-               unsigned int d = width - s - 8;
-               unsigned HOST_WIDE_INT byte;
-
-               if (s < HOST_BITS_PER_WIDE_INT)
-                 byte = (first.low >> s) & 0xff;
-               else
-                 byte = (first.high >> (s - HOST_BITS_PER_WIDE_INT)) & 0xff;
-
-               if (d < HOST_BITS_PER_WIDE_INT)
-                 value.low |= byte << d;
-               else
-                 value.high |= byte << (d - HOST_BITS_PER_WIDE_INT);
-             }
-         }
+         result = wide_int (op0).bswap ();
          break;
 
        case TRUNCATE:
-         /* This is just a change-of-mode, so do nothing.  */
-         value = first;
-         break;
-
        case ZERO_EXTEND:
-         gcc_assert (op_mode != VOIDmode);
-
-         if (op_width > HOST_BITS_PER_WIDE_INT)
-           return 0;
-
-         value = double_int::from_uhwi (first.low & GET_MODE_MASK (op_mode));
+         result = wide_int::from (op0, width, UNSIGNED);
          break;
 
        case SIGN_EXTEND:
-         if (op_mode == VOIDmode
-             || op_width > HOST_BITS_PER_WIDE_INT)
-           return 0;
-         else
-           {
-             value.low = first.low & GET_MODE_MASK (op_mode);
-             if (val_signbit_known_set_p (op_mode, value.low))
-               value.low |= ~GET_MODE_MASK (op_mode);
-
-             value.high = HWI_SIGN_EXTEND (value.low);
-           }
+         result = wide_int::from (op0, width, SIGNED);
          break;
 
        case SQRT:
-         return 0;
-
        default:
          return 0;
        }
 
-      return immed_double_int_const (value, mode);
+      return immed_wide_int_const (result, mode);
     }
 
   else if (CONST_DOUBLE_AS_FLOAT_P (op) 
           && SCALAR_FLOAT_MODE_P (mode)
           && SCALAR_FLOAT_MODE_P (GET_MODE (op)))
     {
-      REAL_VALUE_TYPE d, t;
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-
+      REAL_VALUE_TYPE d = *CONST_DOUBLE_REAL_VALUE (op);
       switch (code)
        {
        case SQRT:
-         if (HONOR_SNANS (mode) && real_isnan (&d))
-           return 0;
-         real_sqrt (&t, mode, &d);
-         d = t;
-         break;
+         return 0;
        case ABS:
          d = real_value_abs (&d);
          break;
@@ -1943,13 +1826,12 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
        default:
          gcc_unreachable ();
        }
-      return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+      return const_double_from_real_value (d, mode);
     }
-
   else if (CONST_DOUBLE_AS_FLOAT_P (op)
           && SCALAR_FLOAT_MODE_P (GET_MODE (op))
           && GET_MODE_CLASS (mode) == MODE_INT
-          && width <= HOST_BITS_PER_DOUBLE_INT && width > 0)
+          && width > 0)
     {
       /* Although the overflow semantics of RTL's FIX and UNSIGNED_FIX
         operators are intentionally left unspecified (to ease implementation
@@ -1958,98 +1840,84 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
 
       /* This was formerly used only for non-IEEE float.
         eggert@twinsun.com says it is safe for IEEE also.  */
-      HOST_WIDE_INT xh, xl, th, tl;
-      REAL_VALUE_TYPE x, t;
-      REAL_VALUE_FROM_CONST_DOUBLE (x, op);
+      REAL_VALUE_TYPE t;
+      const REAL_VALUE_TYPE *x = CONST_DOUBLE_REAL_VALUE (op);
+      wide_int wmax, wmin;
+      /* This is part of the abi to real_to_integer, but we check
+        things before making this call.  */
+      bool fail;
+
       switch (code)
        {
        case FIX:
-         if (REAL_VALUE_ISNAN (x))
+         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;
-           }
+         wmax = wi::max_value (width, SIGNED);
+         real_from_integer (&t, VOIDmode, wmax, SIGNED);
+         if (real_less (&t, x))
+           return immed_wide_int_const (wmax, mode);
 
          /* Test against the signed lower bound.  */
-         if (width > HOST_BITS_PER_WIDE_INT)
-           {
-             th = (unsigned HOST_WIDE_INT) (-1)
-                  << (width - HOST_BITS_PER_WIDE_INT - 1);
-             tl = 0;
-           }
-         else
-           {
-             th = -1;
-             tl = (unsigned 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;
+         wmin = wi::min_value (width, SIGNED);
+         real_from_integer (&t, VOIDmode, wmin, SIGNED);
+         if (real_less (x, &t))
+           return immed_wide_int_const (wmin, mode);
+
+         return immed_wide_int_const (real_to_integer (x, &fail, width),
+                                      mode);
 
        case UNSIGNED_FIX:
-         if (REAL_VALUE_ISNAN (x) || REAL_VALUE_NEGATIVE (x))
+         if (REAL_VALUE_ISNAN (*x) || REAL_VALUE_NEGATIVE (*x))
            return const0_rtx;
 
          /* Test against the unsigned upper bound.  */
-         if (width == HOST_BITS_PER_DOUBLE_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;
-           }
+         wmax = wi::max_value (width, UNSIGNED);
+         real_from_integer (&t, VOIDmode, wmax, UNSIGNED);
+         if (real_less (&t, x))
+           return immed_wide_int_const (wmax, mode);
 
-         REAL_VALUE_TO_INT (&xl, &xh, x);
-         break;
+         return immed_wide_int_const (real_to_integer (x, &fail, width),
+                                      mode);
 
        default:
          gcc_unreachable ();
        }
-      return immed_double_const (xl, xh, mode);
     }
 
   return NULL_RTX;
 }
 \f
+/* Subroutine of simplify_binary_operation to simplify a binary operation
+   CODE that can commute with byte swapping, with result mode MODE and
+   operating on OP0 and OP1.  CODE is currently one of AND, IOR or XOR.
+   Return zero if no simplification or canonicalization is possible.  */
+
+static rtx
+simplify_byte_swapping_operation (enum rtx_code code, machine_mode mode,
+                                 rtx op0, rtx op1)
+{
+  rtx tem;
+
+  /* (op (bswap x) C1)) -> (bswap (op x C2)) with C2 swapped.  */
+  if (GET_CODE (op0) == BSWAP && CONST_SCALAR_INT_P (op1))
+    {
+      tem = simplify_gen_binary (code, mode, XEXP (op0, 0),
+                                simplify_gen_unary (BSWAP, mode, op1, mode));
+      return simplify_gen_unary (BSWAP, mode, tem, mode);
+    }
+
+  /* (op (bswap x) (bswap y)) -> (bswap (op x y)).  */
+  if (GET_CODE (op0) == BSWAP && GET_CODE (op1) == BSWAP)
+    {
+      tem = simplify_gen_binary (code, mode, XEXP (op0, 0), XEXP (op1, 0));
+      return simplify_gen_unary (BSWAP, mode, tem, mode);
+    }
+
+  return NULL_RTX;
+}
+
 /* 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,
@@ -2057,7 +1925,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
    canonicalization is possible.  */
 
 static rtx
-simplify_associative_operation (enum rtx_code code, enum machine_mode mode,
+simplify_associative_operation (enum rtx_code code, machine_mode mode,
                                rtx op0, rtx op1)
 {
   rtx tem;
@@ -2076,9 +1944,7 @@ simplify_associative_operation (enum rtx_code code, enum machine_mode mode,
       if (! swap_commutative_operands_p (op1, op0))
        return simplify_gen_binary (code, mode, op1, op0);
 
-      tem = op0;
-      op0 = op1;
-      op1 = tem;
+      std::swap (op0, op1);
     }
 
   if (GET_CODE (op0) == code)
@@ -2111,7 +1977,7 @@ simplify_associative_operation (enum rtx_code code, enum machine_mode mode,
    Don't use this for relational operations such as EQ or LT.
    Use simplify_relational_operation instead.  */
 rtx
-simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
+simplify_binary_operation (enum rtx_code code, machine_mode mode,
                           rtx op0, rtx op1)
 {
   rtx trueop0, trueop1;
@@ -2127,9 +1993,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
   /* Make sure the constant is second.  */
   if (GET_RTX_CLASS (code) == RTX_COMM_ARITH
       && swap_commutative_operands_p (op0, op1))
-    {
-      tem = op0, op0 = op1, op1 = tem;
-    }
+    std::swap (op0, op1);
 
   trueop0 = avoid_constant_pool_reference (op0);
   trueop1 = avoid_constant_pool_reference (op1);
@@ -2146,7 +2010,7 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
    actual constants.  */
 
 static rtx
-simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
+simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                             rtx op0, rtx op1, rtx trueop0, rtx trueop1)
 {
   rtx tem, reversed, opleft, opright;
@@ -2204,49 +2068,50 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
       if (SCALAR_INT_MODE_P (mode))
        {
-         double_int coeff0, coeff1;
          rtx lhs = op0, rhs = op1;
 
-         coeff0 = double_int_one;
-         coeff1 = double_int_one;
+         wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
+         wide_int coeff1 = wi::one (GET_MODE_PRECISION (mode));
 
          if (GET_CODE (lhs) == NEG)
            {
-             coeff0 = double_int_minus_one;
+             coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == MULT
-                  && CONST_INT_P (XEXP (lhs, 1)))
+                  && CONST_SCALAR_INT_P (XEXP (lhs, 1)))
            {
-             coeff0 = double_int::from_shwi (INTVAL (XEXP (lhs, 1)));
+             coeff0 = std::make_pair (XEXP (lhs, 1), mode);
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == ASHIFT
                   && CONST_INT_P (XEXP (lhs, 1))
                    && INTVAL (XEXP (lhs, 1)) >= 0
-                  && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+                  && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (mode))
            {
-             coeff0 = double_int_zero.set_bit (INTVAL (XEXP (lhs, 1)));
+             coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
+                                           GET_MODE_PRECISION (mode));
              lhs = XEXP (lhs, 0);
            }
 
          if (GET_CODE (rhs) == NEG)
            {
-             coeff1 = double_int_minus_one;
+             coeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == MULT
                   && CONST_INT_P (XEXP (rhs, 1)))
            {
-             coeff1 = double_int::from_shwi (INTVAL (XEXP (rhs, 1)));
+             coeff1 = std::make_pair (XEXP (rhs, 1), mode);
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == ASHIFT
                   && CONST_INT_P (XEXP (rhs, 1))
                   && INTVAL (XEXP (rhs, 1)) >= 0
-                  && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+                  && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (mode))
            {
-             coeff1 = double_int_zero.set_bit (INTVAL (XEXP (rhs, 1)));
+             coeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
+                                           GET_MODE_PRECISION (mode));
              rhs = XEXP (rhs, 0);
            }
 
@@ -2254,15 +2119,13 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
            {
              rtx orig = gen_rtx_PLUS (mode, op0, op1);
              rtx coeff;
-             double_int val;
              bool speed = optimize_function_for_speed_p (cfun);
 
-             val = coeff0 + coeff1;
-             coeff = immed_double_int_const (val, mode);
+             coeff = immed_wide_int_const (coeff0 + coeff1, mode);
 
              tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return set_src_cost (tem, speed) <= set_src_cost (orig, speed)
-               ? tem : 0;
+             return (set_src_cost (tem, mode, speed)
+                     <= set_src_cost (orig, mode, speed) ? tem : 0);
            }
        }
 
@@ -2331,15 +2194,14 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          rtx xop00 = XEXP (op0, 0);
          rtx xop10 = XEXP (op1, 0);
 
-#ifdef HAVE_cc0
          if (GET_CODE (xop00) == CC0 && GET_CODE (xop10) == CC0)
-#else
+             return xop00;
+
            if (REG_P (xop00) && REG_P (xop10)
                && GET_MODE (xop00) == GET_MODE (xop10)
                && REGNO (xop00) == REGNO (xop10)
                && GET_MODE_CLASS (GET_MODE (xop00)) == MODE_CC
                && GET_MODE_CLASS (GET_MODE (xop10)) == MODE_CC)
-#endif
              return xop00;
        }
       break;
@@ -2380,49 +2242,50 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
       if (SCALAR_INT_MODE_P (mode))
        {
-         double_int coeff0, negcoeff1;
          rtx lhs = op0, rhs = op1;
 
-         coeff0 = double_int_one;
-         negcoeff1 = double_int_minus_one;
+         wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
+         wide_int negcoeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
 
          if (GET_CODE (lhs) == NEG)
            {
-             coeff0 = double_int_minus_one;
+             coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == MULT
-                  && CONST_INT_P (XEXP (lhs, 1)))
+                  && CONST_SCALAR_INT_P (XEXP (lhs, 1)))
            {
-             coeff0 = double_int::from_shwi (INTVAL (XEXP (lhs, 1)));
+             coeff0 = std::make_pair (XEXP (lhs, 1), mode);
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == ASHIFT
                   && CONST_INT_P (XEXP (lhs, 1))
                   && INTVAL (XEXP (lhs, 1)) >= 0
-                  && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+                  && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (mode))
            {
-             coeff0 = double_int_zero.set_bit (INTVAL (XEXP (lhs, 1)));
+             coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
+                                           GET_MODE_PRECISION (mode));
              lhs = XEXP (lhs, 0);
            }
 
          if (GET_CODE (rhs) == NEG)
            {
-             negcoeff1 = double_int_one;
+             negcoeff1 = wi::one (GET_MODE_PRECISION (mode));
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == MULT
                   && CONST_INT_P (XEXP (rhs, 1)))
            {
-             negcoeff1 = double_int::from_shwi (-INTVAL (XEXP (rhs, 1)));
+             negcoeff1 = wi::neg (std::make_pair (XEXP (rhs, 1), mode));
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == ASHIFT
                   && CONST_INT_P (XEXP (rhs, 1))
                   && INTVAL (XEXP (rhs, 1)) >= 0
-                  && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+                  && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (mode))
            {
-             negcoeff1 = double_int_zero.set_bit (INTVAL (XEXP (rhs, 1)));
+             negcoeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
+                                              GET_MODE_PRECISION (mode));
              negcoeff1 = -negcoeff1;
              rhs = XEXP (rhs, 0);
            }
@@ -2431,15 +2294,13 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
            {
              rtx orig = gen_rtx_MINUS (mode, op0, op1);
              rtx coeff;
-             double_int val;
              bool speed = optimize_function_for_speed_p (cfun);
 
-             val = coeff0 + negcoeff1;
-             coeff = immed_double_int_const (val, mode);
+             coeff = immed_wide_int_const (coeff0 + negcoeff1, mode);
 
              tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return set_src_cost (tem, speed) <= set_src_cost (orig, speed)
-               ? tem : 0;
+             return (set_src_cost (tem, mode, speed)
+                     <= set_src_cost (orig, mode, speed) ? tem : 0);
            }
        }
 
@@ -2587,26 +2448,13 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && 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 (CONST_INT_P (trueop1)
-         && (val = exact_log2 (UINTVAL (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.  */
-         && (width <= HOST_BITS_PER_WIDE_INT
-             || val != HOST_BITS_PER_WIDE_INT - 1))
-       return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
-
-      /* Likewise for multipliers wider than a word.  */
-      if (CONST_DOUBLE_AS_INT_P (trueop1)
-         && GET_MODE (op0) == mode
-         && CONST_DOUBLE_LOW (trueop1) == 0
-         && (val = exact_log2 (CONST_DOUBLE_HIGH (trueop1))) >= 0
-         && (val < HOST_BITS_PER_DOUBLE_INT - 1
-             || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_DOUBLE_INT))
-       return simplify_gen_binary (ASHIFT, mode, op0,
-                                   GEN_INT (val + HOST_BITS_PER_WIDE_INT));
+      /* Convert multiply by constant power of two into shift.  */
+      if (CONST_SCALAR_INT_P (trueop1))
+       {
+         val = wi::exact_log2 (std::make_pair (trueop1, mode));
+         if (val >= 0)
+           return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
+       }
 
       /* x*2 is x+x and x*(-1) is -x */
       if (CONST_DOUBLE_AS_FLOAT_P (trueop1)
@@ -2614,14 +2462,13 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && !DECIMAL_FLOAT_MODE_P (GET_MODE (trueop1))
          && GET_MODE (op0) == mode)
        {
-         REAL_VALUE_TYPE d;
-         REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
+         const REAL_VALUE_TYPE *d1 = CONST_DOUBLE_REAL_VALUE (trueop1);
 
-         if (REAL_VALUES_EQUAL (d, dconst2))
+         if (real_equal (d1, &dconst2))
            return simplify_gen_binary (PLUS, mode, op0, copy_rtx (op0));
 
          if (!HONOR_SNANS (mode)
-             && REAL_VALUES_EQUAL (d, dconstm1))
+             && real_equal (d1, &dconstm1))
            return simplify_gen_unary (NEG, mode, op0, mode);
        }
 
@@ -2762,12 +2609,13 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && CONST_INT_P (XEXP (op0, 1))
          && CONST_INT_P (op1)
          && (UINTVAL (XEXP (op0, 1)) & UINTVAL (op1)) != 0)
-       return simplify_gen_binary (IOR, mode,
-                                   simplify_gen_binary
-                                         (AND, mode, XEXP (op0, 0),
-                                          GEN_INT (UINTVAL (XEXP (op0, 1))
-                                                   & ~UINTVAL (op1))),
-                                   op1);
+       {
+         rtx tmp = simplify_gen_binary (AND, mode, XEXP (op0, 0),
+                                        gen_int_mode (UINTVAL (XEXP (op0, 1))
+                                                      & ~UINTVAL (op1),
+                                                      mode));
+         return simplify_gen_binary (IOR, mode, tmp, op1);
+       }
 
       /* If OP0 is (ashiftrt (plus ...) C), it might actually be
          a (sign_extend (plus ...)).  Then check if OP1 is a CONST_INT and
@@ -2784,6 +2632,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
           HOST_WIDE_INT mask = INTVAL (trueop1) << count;
 
           if (mask >> count == INTVAL (trueop1)
+             && trunc_int_for_mode (mask, mode) == mask
               && (mask & nonzero_bits (XEXP (op0, 0), mode)) == 0)
            return simplify_gen_binary (ASHIFTRT, mode,
                                        plus_constant (mode, XEXP (op0, 0),
@@ -2791,6 +2640,10 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                                        XEXP (op0, 1));
         }
 
+      tem = simplify_byte_swapping_operation (code, mode, op0, op1);
+      if (tem)
+       return tem;
+
       tem = simplify_associative_operation (code, mode, op0, op1);
       if (tem)
        return tem;
@@ -2867,6 +2720,39 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                                                        XEXP (op0, 1), mode),
                                    op1);
 
+      /* Given (xor (ior (xor A B) C) D), where B, C and D are
+        constants, simplify to (xor (ior A C) (B&~C)^D), canceling
+        out bits inverted twice and not set by C.  Similarly, given
+        (xor (and (xor A B) C) D), simplify without inverting C in
+        the xor operand: (xor (and A C) (B&C)^D).
+      */
+      else if ((GET_CODE (op0) == IOR || GET_CODE (op0) == AND)
+              && GET_CODE (XEXP (op0, 0)) == XOR
+              && CONST_INT_P (op1)
+              && CONST_INT_P (XEXP (op0, 1))
+              && CONST_INT_P (XEXP (XEXP (op0, 0), 1)))
+       {
+         enum rtx_code op = GET_CODE (op0);
+         rtx a = XEXP (XEXP (op0, 0), 0);
+         rtx b = XEXP (XEXP (op0, 0), 1);
+         rtx c = XEXP (op0, 1);
+         rtx d = op1;
+         HOST_WIDE_INT bval = INTVAL (b);
+         HOST_WIDE_INT cval = INTVAL (c);
+         HOST_WIDE_INT dval = INTVAL (d);
+         HOST_WIDE_INT xcval;
+
+         if (op == IOR)
+           xcval = ~cval;
+         else
+           xcval = cval;
+
+         return simplify_gen_binary (XOR, mode,
+                                     simplify_gen_binary (op, mode, a, c),
+                                     gen_int_mode ((bval & xcval) ^ dval,
+                                                   mode));
+       }
+
       /* Given (xor (and A B) C), using P^Q == (~P&Q) | (~Q&P),
         we can transform like this:
             (A&B)^C == ~(A&B)&C | ~C&(A&B)
@@ -2883,26 +2769,47 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          HOST_WIDE_INT bval = INTVAL (b);
          HOST_WIDE_INT cval = INTVAL (c);
 
-         rtx na_c
-           = simplify_binary_operation (AND, mode,
-                                        simplify_gen_unary (NOT, mode, a, mode),
-                                        c);
+         /* Instead of computing ~A&C, we compute its negated value,
+            ~(A|~C).  If it yields -1, ~A&C is zero, so we can
+            optimize for sure.  If it does not simplify, we still try
+            to compute ~A&C below, but since that always allocates
+            RTL, we don't try that before committing to returning a
+            simplified expression.  */
+         rtx n_na_c = simplify_binary_operation (IOR, mode, a,
+                                                 GEN_INT (~cval));
+
          if ((~cval & bval) == 0)
            {
+             rtx na_c = NULL_RTX;
+             if (n_na_c)
+               na_c = simplify_gen_unary (NOT, mode, n_na_c, mode);
+             else
+               {
+                 /* If ~A does not simplify, don't bother: we don't
+                    want to simplify 2 operations into 3, and if na_c
+                    were to simplify with na, n_na_c would have
+                    simplified as well.  */
+                 rtx na = simplify_unary_operation (NOT, mode, a, mode);
+                 if (na)
+                   na_c = simplify_gen_binary (AND, mode, na, c);
+               }
+
              /* Try to simplify ~A&C | ~B&C.  */
              if (na_c != NULL_RTX)
                return simplify_gen_binary (IOR, mode, na_c,
-                                           GEN_INT (~bval & cval));
+                                           gen_int_mode (~bval & cval, mode));
            }
          else
            {
              /* If ~A&C is zero, simplify A&(~C&B) | ~B&C.  */
-             if (na_c == const0_rtx)
+             if (n_na_c == CONSTM1_RTX (mode))
                {
                  rtx a_nc_b = simplify_gen_binary (AND, mode, a,
-                                                   GEN_INT (~cval & bval));
+                                                   gen_int_mode (~cval & bval,
+                                                                 mode));
                  return simplify_gen_binary (IOR, mode, a_nc_b,
-                                             GEN_INT (~bval & cval));
+                                             gen_int_mode (~bval & cval,
+                                                           mode));
                }
            }
        }
@@ -2934,6 +2841,10 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && (reversed = reversed_comparison (op0, mode)))
        return reversed;
 
+      tem = simplify_byte_swapping_operation (code, mode, op0, op1);
+      if (tem)
+       return tem;
+
       tem = simplify_associative_operation (code, mode, op0, op1);
       if (tem)
        return tem;
@@ -2981,7 +2892,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && (~GET_MODE_MASK (GET_MODE (XEXP (op0, 0)))
              & UINTVAL (trueop1)) == 0)
        {
-         enum machine_mode imode = GET_MODE (XEXP (op0, 0));
+         machine_mode imode = GET_MODE (XEXP (op0, 0));
          tem = simplify_gen_binary (AND, imode, XEXP (op0, 0),
                                     gen_int_mode (INTVAL (trueop1),
                                                   imode));
@@ -2994,7 +2905,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       if (GET_CODE (op0) == TRUNCATE && CONST_INT_P (trueop1))
        {
          rtx x = XEXP (op0, 0);
-         enum machine_mode xmode = GET_MODE (x);
+         machine_mode xmode = GET_MODE (x);
          tem = simplify_gen_binary (AND, xmode, x,
                                     gen_int_mode (INTVAL (trueop1), xmode));
          return simplify_gen_unary (TRUNCATE, mode, tem, xmode);
@@ -3107,15 +3018,31 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       /* (and X (ior (not X) Y) -> (and X Y) */
       if (GET_CODE (op1) == IOR
          && GET_CODE (XEXP (op1, 0)) == NOT
-         && op0 == XEXP (XEXP (op1, 0), 0))
+         && rtx_equal_p (op0, XEXP (XEXP (op1, 0), 0)))
        return simplify_gen_binary (AND, mode, op0, XEXP (op1, 1));
 
       /* (and (ior (not X) Y) X) -> (and X Y) */
       if (GET_CODE (op0) == IOR
          && GET_CODE (XEXP (op0, 0)) == NOT
-         && op1 == XEXP (XEXP (op0, 0), 0))
+         && rtx_equal_p (op1, XEXP (XEXP (op0, 0), 0)))
        return simplify_gen_binary (AND, mode, op1, XEXP (op0, 1));
 
+      /* (and X (ior Y (not X)) -> (and X Y) */
+      if (GET_CODE (op1) == IOR
+         && GET_CODE (XEXP (op1, 1)) == NOT
+         && rtx_equal_p (op0, XEXP (XEXP (op1, 1), 0)))
+       return simplify_gen_binary (AND, mode, op0, XEXP (op1, 0));
+
+      /* (and (ior Y (not X)) X) -> (and X Y) */
+      if (GET_CODE (op0) == IOR
+         && GET_CODE (XEXP (op0, 1)) == NOT
+         && rtx_equal_p (op1, XEXP (XEXP (op0, 1), 0)))
+       return simplify_gen_binary (AND, mode, op1, XEXP (op0, 0));
+
+      tem = simplify_byte_swapping_operation (code, mode, op0, op1);
+      if (tem)
+       return tem;
+
       tem = simplify_associative_operation (code, mode, op0, op1);
       if (tem)
        return tem;
@@ -3163,21 +3090,21 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          if (CONST_DOUBLE_AS_FLOAT_P (trueop1)
              && trueop1 != CONST0_RTX (mode))
            {
-             REAL_VALUE_TYPE d;
-             REAL_VALUE_FROM_CONST_DOUBLE (d, trueop1);
+             const REAL_VALUE_TYPE *d1 = CONST_DOUBLE_REAL_VALUE (trueop1);
 
              /* x/-1.0 is -x.  */
-             if (REAL_VALUES_EQUAL (d, dconstm1)
+             if (real_equal (d1, &dconstm1)
                  && !HONOR_SNANS (mode))
                return simplify_gen_unary (NEG, mode, op0, mode);
 
              /* Change FP division by a constant into multiplication.
                 Only do this with -freciprocal-math.  */
              if (flag_reciprocal_math
-                 && !REAL_VALUES_EQUAL (d, dconst0))
+                 && !real_equal (d1, &dconst0))
                {
-                 REAL_ARITHMETIC (d, RDIV_EXPR, dconst1, d);
-                 tem = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+                 REAL_VALUE_TYPE d;
+                 real_arithmetic (&d, RDIV_EXPR, &dconst1, d1);
+                 tem = const_double_from_real_value (d, mode);
                  return simplify_gen_binary (MULT, mode, op0, tem);
                }
            }
@@ -3228,7 +3155,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       if (CONST_INT_P (trueop1)
          && exact_log2 (UINTVAL (trueop1)) > 0)
        return simplify_gen_binary (AND, mode, op0,
-                                   GEN_INT (INTVAL (op1) - 1));
+                                   gen_int_mode (INTVAL (op1) - 1, mode));
       break;
 
     case MOD:
@@ -3250,6 +3177,20 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
     case ROTATERT:
     case ROTATE:
+      /* Canonicalize rotates by constant amount.  If op1 is bitsize / 2,
+        prefer left rotation, if op1 is from bitsize / 2 + 1 to
+        bitsize - 1, use other direction of rotate with 1 .. bitsize / 2 - 1
+        amount instead.  */
+#if defined(HAVE_rotate) && defined(HAVE_rotatert)
+      if (CONST_INT_P (trueop1)
+         && IN_RANGE (INTVAL (trueop1),
+                      GET_MODE_PRECISION (mode) / 2 + (code == ROTATE),
+                      GET_MODE_PRECISION (mode) - 1))
+       return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
+                                   mode, op0, GEN_INT (GET_MODE_PRECISION (mode)
+                                                       - INTVAL (trueop1)));
+#endif
+      /* FALLTHRU */
     case ASHIFTRT:
       if (trueop1 == CONST0_RTX (mode))
        return op0;
@@ -3260,10 +3201,45 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && UINTVAL (trueop0) == GET_MODE_MASK (mode)
          && ! side_effects_p (op1))
        return op0;
+      /* Given:
+        scalar modes M1, M2
+        scalar constants c1, c2
+        size (M2) > size (M1)
+        c1 == size (M2) - size (M1)
+        optimize:
+        (ashiftrt:M1 (subreg:M1 (lshiftrt:M2 (reg:M2) (const_int <c1>))
+                                <low_part>)
+                     (const_int <c2>))
+        to:
+        (subreg:M1 (ashiftrt:M2 (reg:M2) (const_int <c1 + c2>))
+                   <low_part>).  */
+      if (code == ASHIFTRT
+         && !VECTOR_MODE_P (mode)
+         && SUBREG_P (op0)
+         && CONST_INT_P (op1)
+         && GET_CODE (SUBREG_REG (op0)) == LSHIFTRT
+         && !VECTOR_MODE_P (GET_MODE (SUBREG_REG (op0)))
+         && CONST_INT_P (XEXP (SUBREG_REG (op0), 1))
+         && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+             > GET_MODE_BITSIZE (mode))
+         && (INTVAL (XEXP (SUBREG_REG (op0), 1))
+             == (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+                 - GET_MODE_BITSIZE (mode)))
+         && subreg_lowpart_p (op0))
+       {
+         rtx tmp = GEN_INT (INTVAL (XEXP (SUBREG_REG (op0), 1))
+                            + INTVAL (op1));
+         machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+         tmp = simplify_gen_binary (ASHIFTRT,
+                                    GET_MODE (SUBREG_REG (op0)),
+                                    XEXP (SUBREG_REG (op0), 0),
+                                    tmp);
+         return lowpart_subreg (mode, tmp, inner_mode);
+       }
     canonicalize_shift:
       if (SHIFT_COUNT_TRUNCATED && CONST_INT_P (op1))
        {
-         val = INTVAL (op1) & (GET_MODE_BITSIZE (mode) - 1);
+         val = INTVAL (op1) & (GET_MODE_PRECISION (mode) - 1);
          if (val != INTVAL (op1))
            return simplify_gen_binary (code, mode, op0, GEN_INT (val));
        }
@@ -3289,7 +3265,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && STORE_FLAG_VALUE == 1
          && INTVAL (trueop1) < (HOST_WIDE_INT)width)
        {
-         enum machine_mode imode = GET_MODE (XEXP (op0, 0));
+         machine_mode imode = GET_MODE (XEXP (op0, 0));
          unsigned HOST_WIDE_INT zero_val = 0;
 
          if (CLZ_DEFINED_VALUE_AT_ZERO (imode, zero_val)
@@ -3380,8 +3356,8 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
              rtx op0 = XEXP (trueop0, 0);
              rtx op1 = XEXP (trueop0, 1);
 
-             enum machine_mode opmode = GET_MODE (op0);
-             int elt_size = GET_MODE_SIZE (GET_MODE_INNER (opmode));
+             machine_mode opmode = GET_MODE (op0);
+             int elt_size = GET_MODE_UNIT_SIZE (opmode);
              int n_elts = GET_MODE_SIZE (opmode) / elt_size;
 
              int i = INTVAL (XVECEXP (trueop1, 0, 0));
@@ -3402,7 +3378,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                  rtx op00 = XEXP (op0, 0);
                  rtx op01 = XEXP (op0, 1);
 
-                 enum machine_mode mode00, mode01;
+                 machine_mode mode00, mode01;
                  int n_elts00, n_elts01;
 
                  mode00 = GET_MODE (op00);
@@ -3411,7 +3387,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                  /* Find out number of elements of each operand.  */
                  if (VECTOR_MODE_P (mode00))
                    {
-                     elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode00));
+                     elt_size = GET_MODE_UNIT_SIZE (mode00);
                      n_elts00 = GET_MODE_SIZE (mode00) / elt_size;
                    }
                  else
@@ -3419,7 +3395,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
                  if (VECTOR_MODE_P (mode01))
                    {
-                     elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode01));
+                     elt_size = GET_MODE_UNIT_SIZE (mode01);
                      n_elts01 = GET_MODE_SIZE (mode01) / elt_size;
                    }
                  else
@@ -3460,7 +3436,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
          if (GET_CODE (trueop0) == CONST_VECTOR)
            {
-             int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+             int elt_size = GET_MODE_UNIT_SIZE (mode);
              unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
              rtvec v = rtvec_alloc (n_elts);
              unsigned int i;
@@ -3532,6 +3508,50 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
              return simplify_gen_binary (VEC_CONCAT, mode, subop0, subop1);
            }
+
+         /* If we select one half of a vec_concat, return that.  */
+         if (GET_CODE (trueop0) == VEC_CONCAT
+             && CONST_INT_P (XVECEXP (trueop1, 0, 0)))
+           {
+             rtx subop0 = XEXP (trueop0, 0);
+             rtx subop1 = XEXP (trueop0, 1);
+             machine_mode mode0 = GET_MODE (subop0);
+             machine_mode mode1 = GET_MODE (subop1);
+             int li = GET_MODE_UNIT_SIZE (mode0);
+             int l0 = GET_MODE_SIZE (mode0) / li;
+             int l1 = GET_MODE_SIZE (mode1) / li;
+             int i0 = INTVAL (XVECEXP (trueop1, 0, 0));
+             if (i0 == 0 && !side_effects_p (op1) && mode == mode0)
+               {
+                 bool success = true;
+                 for (int i = 1; i < l0; ++i)
+                   {
+                     rtx j = XVECEXP (trueop1, 0, i);
+                     if (!CONST_INT_P (j) || INTVAL (j) != i)
+                       {
+                         success = false;
+                         break;
+                       }
+                   }
+                 if (success)
+                   return subop0;
+               }
+             if (i0 == l0 && !side_effects_p (op0) && mode == mode1)
+               {
+                 bool success = true;
+                 for (int i = 1; i < l1; ++i)
+                   {
+                     rtx j = XVECEXP (trueop1, 0, i);
+                     if (!CONST_INT_P (j) || INTVAL (j) != i0 + i)
+                       {
+                         success = false;
+                         break;
+                       }
+                   }
+                 if (success)
+                   return subop1;
+               }
+           }
        }
 
       if (XVECLEN (trueop1, 0) == 1
@@ -3545,7 +3565,21 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          while (GET_MODE (vec) != mode
                 && GET_CODE (vec) == VEC_CONCAT)
            {
-             HOST_WIDE_INT vec_size = GET_MODE_SIZE (GET_MODE (XEXP (vec, 0)));
+             HOST_WIDE_INT vec_size;
+
+             if (CONST_INT_P (XEXP (vec, 0)))
+               {
+                 /* vec_concat of two const_ints doesn't make sense with
+                    respect to modes.  */
+                 if (CONST_INT_P (XEXP (vec, 1)))
+                   return 0;
+
+                 vec_size = GET_MODE_SIZE (GET_MODE (trueop0))
+                            - GET_MODE_SIZE (GET_MODE (XEXP (vec, 1)));
+               }
+             else
+               vec_size = GET_MODE_SIZE (GET_MODE (XEXP (vec, 0)));
+
              if (offset < vec_size)
                vec = XEXP (vec, 0);
              else
@@ -3585,13 +3619,38 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
            }
        }
 
+      /* If we have two nested selects that are inverses of each
+        other, replace them with the source operand.  */
+      if (GET_CODE (trueop0) == VEC_SELECT
+         && GET_MODE (XEXP (trueop0, 0)) == mode)
+       {
+         rtx op0_subop1 = XEXP (trueop0, 1);
+         gcc_assert (GET_CODE (op0_subop1) == PARALLEL);
+         gcc_assert (XVECLEN (trueop1, 0) == GET_MODE_NUNITS (mode));
+
+         /* Apply the outer ordering vector to the inner one.  (The inner
+            ordering vector is expressly permitted to be of a different
+            length than the outer one.)  If the result is { 0, 1, ..., n-1 }
+            then the two VEC_SELECTs cancel.  */
+         for (int i = 0; i < XVECLEN (trueop1, 0); ++i)
+           {
+             rtx x = XVECEXP (trueop1, 0, i);
+             if (!CONST_INT_P (x))
+               return 0;
+             rtx y = XVECEXP (op0_subop1, 0, INTVAL (x));
+             if (!CONST_INT_P (y) || i != INTVAL (y))
+               return 0;
+           }
+         return XEXP (trueop0, 0);
+       }
+
       return 0;
     case VEC_CONCAT:
       {
-       enum machine_mode op0_mode = (GET_MODE (trueop0) != VOIDmode
+       machine_mode op0_mode = (GET_MODE (trueop0) != VOIDmode
                                      ? GET_MODE (trueop0)
                                      : GET_MODE_INNER (mode));
-       enum machine_mode op1_mode = (GET_MODE (trueop1) != VOIDmode
+       machine_mode op1_mode = (GET_MODE (trueop1) != VOIDmode
                                      ? GET_MODE (trueop1)
                                      : GET_MODE_INNER (mode));
 
@@ -3618,7 +3677,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                || CONST_SCALAR_INT_P (trueop1) 
                || CONST_DOUBLE_AS_FLOAT_P (trueop1)))
          {
-           int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+           int elt_size = GET_MODE_UNIT_SIZE (mode);
            unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
            rtvec v = rtvec_alloc (n_elts);
            unsigned int i;
@@ -3679,11 +3738,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 }
 
 rtx
-simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
+simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
                                 rtx op0, rtx op1)
 {
-  HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
-  HOST_WIDE_INT val;
   unsigned int width = GET_MODE_PRECISION (mode);
 
   if (VECTOR_MODE_P (mode)
@@ -3692,9 +3749,9 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
       && GET_CODE (op1) == CONST_VECTOR)
     {
       unsigned n_elts = GET_MODE_NUNITS (mode);
-      enum machine_mode op0mode = GET_MODE (op0);
+      machine_mode op0mode = GET_MODE (op0);
       unsigned op0_n_elts = GET_MODE_NUNITS (op0mode);
-      enum machine_mode op1mode = GET_MODE (op1);
+      machine_mode op1mode = GET_MODE (op1);
       unsigned op1_n_elts = GET_MODE_NUNITS (op1mode);
       rtvec v = rtvec_alloc (n_elts);
       unsigned int i;
@@ -3790,24 +3847,22 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
              }
            }
           real_from_target (&r, tmp0, mode);
-          return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
+          return const_double_from_real_value (r, mode);
        }
       else
        {
          REAL_VALUE_TYPE f0, f1, value, result;
          bool inexact;
 
-         REAL_VALUE_FROM_CONST_DOUBLE (f0, op0);
-         REAL_VALUE_FROM_CONST_DOUBLE (f1, op1);
-         real_convert (&f0, mode, &f0);
-         real_convert (&f1, mode, &f1);
+         real_convert (&f0, mode, CONST_DOUBLE_REAL_VALUE (op0));
+         real_convert (&f1, mode, CONST_DOUBLE_REAL_VALUE (op1));
 
          if (HONOR_SNANS (mode)
              && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
            return 0;
 
          if (code == DIV
-             && REAL_VALUES_EQUAL (f1, dconst0)
+             && real_equal (&f1, &dconst0)
              && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
            return 0;
 
@@ -3840,9 +3895,9 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
          if (code == MULT && MODE_HAS_INFINITIES (mode) && HONOR_NANS (mode)
              && flag_trapping_math
-             && ((REAL_VALUE_ISINF (f0) && REAL_VALUES_EQUAL (f1, dconst0))
+             && ((REAL_VALUE_ISINF (f0) && real_equal (&f1, &dconst0))
                  || (REAL_VALUE_ISINF (f1)
-                     && REAL_VALUES_EQUAL (f0, dconst0))))
+                     && real_equal (&f0, &dconst0))))
            /* Inf * 0 = NaN plus exception.  */
            return 0;
 
@@ -3871,305 +3926,150 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
              && (inexact || !real_identical (&result, &value)))
            return NULL_RTX;
 
-         return CONST_DOUBLE_FROM_REAL_VALUE (result, mode);
+         return const_double_from_real_value (result, mode);
        }
     }
 
   /* We can fold some multi-word operations.  */
-  if (GET_MODE_CLASS (mode) == MODE_INT
-      && width == HOST_BITS_PER_DOUBLE_INT
-      && (CONST_DOUBLE_AS_INT_P (op0) || CONST_INT_P (op0))
-      && (CONST_DOUBLE_AS_INT_P (op1) || CONST_INT_P (op1)))
+  if ((GET_MODE_CLASS (mode) == MODE_INT
+       || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+      && CONST_SCALAR_INT_P (op0)
+      && CONST_SCALAR_INT_P (op1))
     {
-      double_int o0, o1, res, tmp;
+      wide_int result;
       bool overflow;
-
-      o0 = rtx_to_double_int (op0);
-      o1 = rtx_to_double_int (op1);
-
+      rtx_mode_t pop0 = std::make_pair (op0, mode);
+      rtx_mode_t pop1 = std::make_pair (op1, mode);
+
+#if TARGET_SUPPORTS_WIDE_INT == 0
+      /* This assert keeps the simplification from producing a result
+        that cannot be represented in a CONST_DOUBLE but a lot of
+        upstream callers expect that this function never fails to
+        simplify something and so you if you added this to the test
+        above the code would die later anyway.  If this assert
+        happens, you just need to make the port support wide int.  */
+      gcc_assert (width <= HOST_BITS_PER_DOUBLE_INT);
+#endif
       switch (code)
        {
        case MINUS:
-         /* A - B == A + (-B).  */
-         o1 = -o1;
-
-         /* Fall through....  */
+         result = wi::sub (pop0, pop1);
+         break;
 
        case PLUS:
-         res = o0 + o1;
+         result = wi::add (pop0, pop1);
          break;
 
        case MULT:
-         res = o0 * o1;
+         result = wi::mul (pop0, pop1);
          break;
 
        case DIV:
-          res = o0.divmod_with_overflow (o1, false, TRUNC_DIV_EXPR,
-                                        &tmp, &overflow);
+         result = wi::div_trunc (pop0, pop1, SIGNED, &overflow);
          if (overflow)
-           return 0;
+           return NULL_RTX;
          break;
 
        case MOD:
-          tmp = o0.divmod_with_overflow (o1, false, TRUNC_DIV_EXPR,
-                                        &res, &overflow);
+         result = wi::mod_trunc (pop0, pop1, SIGNED, &overflow);
          if (overflow)
-           return 0;
+           return NULL_RTX;
          break;
 
        case UDIV:
-          res = o0.divmod_with_overflow (o1, true, TRUNC_DIV_EXPR,
-                                        &tmp, &overflow);
+         result = wi::div_trunc (pop0, pop1, UNSIGNED, &overflow);
          if (overflow)
-           return 0;
+           return NULL_RTX;
          break;
 
        case UMOD:
-          tmp = o0.divmod_with_overflow (o1, true, TRUNC_DIV_EXPR,
-                                        &res, &overflow);
+         result = wi::mod_trunc (pop0, pop1, UNSIGNED, &overflow);
          if (overflow)
-           return 0;
+           return NULL_RTX;
          break;
 
        case AND:
-         res = o0 & o1;
+         result = wi::bit_and (pop0, pop1);
          break;
 
        case IOR:
-         res = o0 | o1;
+         result = wi::bit_or (pop0, pop1);
          break;
 
        case XOR:
-         res = o0 ^ o1;
+         result = wi::bit_xor (pop0, pop1);
          break;
 
        case SMIN:
-         res = o0.smin (o1);
+         result = wi::smin (pop0, pop1);
          break;
 
        case SMAX:
-         res = o0.smax (o1);
+         result = wi::smax (pop0, pop1);
          break;
 
        case UMIN:
-         res = o0.umin (o1);
+         result = wi::umin (pop0, pop1);
          break;
 
        case UMAX:
-         res = o0.umax (o1);
+         result = wi::umax (pop0, pop1);
          break;
 
-       case LSHIFTRT:   case ASHIFTRT:
+       case LSHIFTRT:
+       case ASHIFTRT:
        case ASHIFT:
-       case ROTATE:     case ROTATERT:
          {
-           unsigned HOST_WIDE_INT cnt;
-
+           wide_int wop1 = pop1;
            if (SHIFT_COUNT_TRUNCATED)
-             {
-               o1.high = 0; 
-               o1.low &= GET_MODE_PRECISION (mode) - 1;
-             }
-
-           if (!o1.fits_uhwi ()
-               || o1.to_uhwi () >= GET_MODE_PRECISION (mode))
-             return 0;
-
-           cnt = o1.to_uhwi ();
-           unsigned short prec = GET_MODE_PRECISION (mode);
-
-           if (code == LSHIFTRT || code == ASHIFTRT)
-             res = o0.rshift (cnt, prec, code == ASHIFTRT);
-           else if (code == ASHIFT)
-             res = o0.alshift (cnt, prec);
-           else if (code == ROTATE)
-             res = o0.lrotate (cnt, prec);
-           else /* code == ROTATERT */
-             res = o0.rrotate (cnt, prec);
-         }
-         break;
-
-       default:
-         return 0;
-       }
-
-      return immed_double_int_const (res, mode);
-    }
-
-  if (CONST_INT_P (op0) && CONST_INT_P (op1)
-      && width <= HOST_BITS_PER_WIDE_INT && width != 0)
-    {
-      /* 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);
-
-      if (width < HOST_BITS_PER_WIDE_INT)
-        {
-          arg0 &= GET_MODE_MASK (mode);
-          arg1 &= GET_MODE_MASK (mode);
-
-          arg0s = arg0;
-         if (val_signbit_known_set_p (mode, arg0s))
-           arg0s |= ~GET_MODE_MASK (mode);
-
-          arg1s = arg1;
-         if (val_signbit_known_set_p (mode, arg1s))
-           arg1s |= ~GET_MODE_MASK (mode);
-       }
-      else
-       {
-         arg0s = arg0;
-         arg1s = arg1;
-       }
-
-      /* Compute the value of the arithmetic.  */
-
-      switch (code)
-       {
-       case PLUS:
-         val = arg0s + arg1s;
-         break;
-
-       case MINUS:
-         val = arg0s - arg1s;
-         break;
-
-       case MULT:
-         val = arg0s * arg1s;
-         break;
-
-       case DIV:
-         if (arg1s == 0
-             || ((unsigned HOST_WIDE_INT) arg0s
-                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
-                 && arg1s == -1))
-           return 0;
-         val = arg0s / arg1s;
-         break;
-
-       case MOD:
-         if (arg1s == 0
-             || ((unsigned HOST_WIDE_INT) arg0s
-                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
-                 && arg1s == -1))
-           return 0;
-         val = arg0s % arg1s;
-         break;
-
-       case UDIV:
-         if (arg1 == 0
-             || ((unsigned HOST_WIDE_INT) arg0s
-                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
-                 && arg1s == -1))
-           return 0;
-         val = (unsigned HOST_WIDE_INT) arg0 / arg1;
-         break;
-
-       case UMOD:
-         if (arg1 == 0
-             || ((unsigned HOST_WIDE_INT) arg0s
-                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
-                 && arg1s == -1))
-           return 0;
-         val = (unsigned HOST_WIDE_INT) arg0 % arg1;
-         break;
-
-       case AND:
-         val = arg0 & arg1;
-         break;
-
-       case IOR:
-         val = arg0 | arg1;
-         break;
-
-       case XOR:
-         val = arg0 ^ arg1;
-         break;
-
-       case LSHIFTRT:
-       case ASHIFT:
-       case ASHIFTRT:
-         /* Truncate the shift if SHIFT_COUNT_TRUNCATED, otherwise make sure
-            the value is in range.  We can't return any old value for
-            out-of-range arguments because either the middle-end (via
-            shift_truncation_mask) or the back-end might be relying on
-            target-specific knowledge.  Nor can we rely on
-            shift_truncation_mask, since the shift might not be part of an
-            ashlM3, lshrM3 or ashrM3 instruction.  */
-         if (SHIFT_COUNT_TRUNCATED)
-           arg1 = (unsigned HOST_WIDE_INT) arg1 % width;
-         else if (arg1 < 0 || arg1 >= GET_MODE_BITSIZE (mode))
-           return 0;
-
-         val = (code == ASHIFT
-                ? ((unsigned HOST_WIDE_INT) arg0) << arg1
-                : ((unsigned HOST_WIDE_INT) arg0) >> arg1);
+             wop1 = wi::umod_trunc (wop1, width);
+           else if (wi::geu_p (wop1, width))
+             return NULL_RTX;
 
-         /* Sign-extend the result for arithmetic right shifts.  */
-         if (code == ASHIFTRT && arg0s < 0 && arg1 > 0)
-           val |= ((unsigned HOST_WIDE_INT) (-1)) << (width - arg1);
-         break;
+           switch (code)
+             {
+             case LSHIFTRT:
+               result = wi::lrshift (pop0, wop1);
+               break;
 
-       case ROTATERT:
-         if (arg1 < 0)
-           return 0;
+             case ASHIFTRT:
+               result = wi::arshift (pop0, wop1);
+               break;
 
-         arg1 %= width;
-         val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1))
-                | (((unsigned HOST_WIDE_INT) arg0) >> arg1));
-         break;
+             case ASHIFT:
+               result = wi::lshift (pop0, wop1);
+               break;
 
+             default:
+               gcc_unreachable ();
+             }
+           break;
+         }
        case ROTATE:
-         if (arg1 < 0)
-           return 0;
-
-         arg1 %= width;
-         val = ((((unsigned HOST_WIDE_INT) arg0) << arg1)
-                | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1)));
-         break;
-
-       case COMPARE:
-         /* Do nothing here.  */
-         return 0;
-
-       case SMIN:
-         val = arg0s <= arg1s ? arg0s : arg1s;
-         break;
-
-       case UMIN:
-         val = ((unsigned HOST_WIDE_INT) arg0
-                <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
-         break;
-
-       case SMAX:
-         val = arg0s > arg1s ? arg0s : arg1s;
-         break;
+       case ROTATERT:
+         {
+           if (wi::neg_p (pop1))
+             return NULL_RTX;
 
-       case UMAX:
-         val = ((unsigned HOST_WIDE_INT) arg0
-                > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
-         break;
+           switch (code)
+             {
+             case ROTATE:
+               result = wi::lrotate (pop0, pop1);
+               break;
 
-       case SS_PLUS:
-       case US_PLUS:
-       case SS_MINUS:
-       case US_MINUS:
-       case SS_MULT:
-       case US_MULT:
-       case SS_DIV:
-       case US_DIV:
-       case SS_ASHIFT:
-       case US_ASHIFT:
-         /* ??? There are simplifications that can be done.  */
-         return 0;
+             case ROTATERT:
+               result = wi::rrotate (pop0, pop1);
+               break;
 
+             default:
+               gcc_unreachable ();
+             }
+           break;
+         }
        default:
-         gcc_unreachable ();
+         return NULL_RTX;
        }
-
-      return gen_int_mode (val, mode);
+      return immed_wide_int_const (result, mode);
     }
 
   return NULL_RTX;
@@ -4177,20 +4077,10 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
 
 \f
-/* Simplify a PLUS or MINUS, at least one of whose operands may be another
-   PLUS or MINUS.
-
-   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.  */
+/* Return a positive integer if X should sort after Y.  The value
+   returned is 1 if and only if X and Y are both regs.  */
 
-struct simplify_plus_minus_op_data
-{
-  rtx op;
-  short neg;
-};
-
-static bool
+static int
 simplify_plus_minus_op_data_cmp (rtx x, rtx y)
 {
   int result;
@@ -4198,23 +4088,36 @@ simplify_plus_minus_op_data_cmp (rtx x, rtx y)
   result = (commutative_operand_precedence (y)
            - commutative_operand_precedence (x));
   if (result)
-    return result > 0;
+    return result + result;
 
   /* Group together equal REGs to do more simplification.  */
   if (REG_P (x) && REG_P (y))
     return REGNO (x) > REGNO (y);
-  else
-    return false;
+
+  return 0;
 }
 
+/* Simplify and canonicalize a PLUS or MINUS, at least one of whose
+   operands may be another PLUS or MINUS.
+
+   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.
+
+   May return NULL_RTX when no changes were made.  */
+
 static rtx
-simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
+simplify_plus_minus (enum rtx_code code, machine_mode mode, rtx op0,
                     rtx op1)
 {
-  struct simplify_plus_minus_op_data ops[8];
+  struct simplify_plus_minus_op_data
+  {
+    rtx op;
+    short neg;
+  } ops[16];
   rtx result, tem;
-  int n_ops = 2, input_ops = 2;
-  int changed, n_constants = 0, canonicalized = 0;
+  int n_ops = 2;
+  int changed, n_constants, canonicalized = 0;
   int i, j;
 
   memset (ops, 0, sizeof ops);
@@ -4231,6 +4134,7 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
   do
     {
       changed = 0;
+      n_constants = 0;
 
       for (i = 0; i < n_ops; i++)
        {
@@ -4242,7 +4146,7 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
            {
            case PLUS:
            case MINUS:
-             if (n_ops == 7)
+             if (n_ops == ARRAY_SIZE (ops))
                return NULL_RTX;
 
              ops[n_ops].op = XEXP (this_op, 1);
@@ -4250,9 +4154,19 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
              n_ops++;
 
              ops[i].op = XEXP (this_op, 0);
-             input_ops++;
              changed = 1;
-             canonicalized |= this_neg;
+             /* If this operand was negated then we will potentially
+                canonicalize the expression.  Similarly if we don't
+                place the operands adjacent we're re-ordering the
+                expression and thus might be performing a
+                canonicalization.  Ignore register re-ordering.
+                ??? It might be better to shuffle the ops array here,
+                but then (plus (plus (A, B), plus (C, D))) wouldn't
+                be seen as non-canonical.  */
+             if (this_neg
+                 || (i != n_ops - 2
+                     && !(REG_P (ops[i].op) && REG_P (ops[n_ops - 1].op))))
+               canonicalized = 1;
              break;
 
            case NEG:
@@ -4263,7 +4177,7 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
              break;
 
            case CONST:
-             if (n_ops < 7
+             if (n_ops != ARRAY_SIZE (ops)
                  && GET_CODE (XEXP (this_op, 0)) == PLUS
                  && CONSTANT_P (XEXP (XEXP (this_op, 0), 0))
                  && CONSTANT_P (XEXP (XEXP (this_op, 0), 1)))
@@ -4273,20 +4187,20 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
                  ops[n_ops].neg = this_neg;
                  n_ops++;
                  changed = 1;
-                 canonicalized = 1;
+                 canonicalized = 1;
                }
              break;
 
            case NOT:
              /* ~a -> (-a - 1) */
-             if (n_ops != 7)
+             if (n_ops != ARRAY_SIZE (ops))
                {
                  ops[n_ops].op = CONSTM1_RTX (mode);
                  ops[n_ops++].neg = this_neg;
                  ops[i].op = XEXP (this_op, 0);
                  ops[i].neg = !this_neg;
                  changed = 1;
-                 canonicalized = 1;
+                 canonicalized = 1;
                }
              break;
 
@@ -4297,7 +4211,7 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
                  ops[i].op = neg_const_int (mode, this_op);
                  ops[i].neg = 0;
                  changed = 1;
-                 canonicalized = 1;
+                 canonicalized = 1;
                }
              break;
 
@@ -4341,23 +4255,29 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
     }
 
   /* Now simplify each pair of operands until nothing changes.  */
-  do
+  while (1)
     {
-      /* Insertion sort is good enough for an eight-element array.  */
+      /* Insertion sort is good enough for a small array.  */
       for (i = 1; i < n_ops; i++)
-        {
-          struct simplify_plus_minus_op_data save;
-          j = i - 1;
-          if (!simplify_plus_minus_op_data_cmp (ops[j].op, ops[i].op))
+       {
+         struct simplify_plus_minus_op_data save;
+         int cmp;
+
+         j = i - 1;
+         cmp = simplify_plus_minus_op_data_cmp (ops[j].op, ops[i].op);
+         if (cmp <= 0)
            continue;
+         /* Just swapping registers doesn't count as canonicalization.  */
+         if (cmp != 1)
+           canonicalized = 1;
 
-          canonicalized = 1;
-          save = ops[i];
-          do
+         save = ops[i];
+         do
            ops[j + 1] = ops[j];
-          while (j-- && simplify_plus_minus_op_data_cmp (ops[j].op, save.op));
-          ops[j + 1] = save;
-        }
+         while (j--
+                && simplify_plus_minus_op_data_cmp (ops[j].op, save.op) > 0);
+         ops[j + 1] = save;
+       }
 
       changed = 0;
       for (i = n_ops - 1; i > 0; i--)
@@ -4374,10 +4294,10 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
                  {
                    ncode = MINUS;
                    if (lneg)
-                     tem = lhs, lhs = rhs, rhs = tem;
+                     std::swap (lhs, rhs);
                  }
                else if (swap_commutative_operands_p (lhs, rhs))
-                 tem = lhs, lhs = rhs, rhs = tem;
+                 std::swap (lhs, rhs);
 
                if ((GET_CODE (lhs) == CONST || CONST_INT_P (lhs))
                    && (GET_CODE (rhs) == CONST || CONST_INT_P (rhs)))
@@ -4386,7 +4306,8 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
 
                    tem_lhs = GET_CODE (lhs) == CONST ? XEXP (lhs, 0) : lhs;
                    tem_rhs = GET_CODE (rhs) == CONST ? XEXP (rhs, 0) : rhs;
-                   tem = simplify_binary_operation (ncode, mode, tem_lhs, tem_rhs);
+                   tem = simplify_binary_operation (ncode, mode, tem_lhs,
+                                                    tem_rhs);
 
                    if (tem && !CONSTANT_P (tem))
                      tem = gen_rtx_CONST (GET_MODE (tem), tem);
@@ -4394,16 +4315,21 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
                else
                  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))
+               if (tem)
                  {
+                   /* 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.
+                      Also, if we find such a simplification, don't try
+                      any more combinations with this rhs:  We must have
+                      something like symbol+offset, ie. one of the
+                      trivial CONST expressions we handle later.  */
+                   if (GET_CODE (tem) == CONST
+                       && GET_CODE (XEXP (tem, 0)) == ncode
+                       && XEXP (XEXP (tem, 0), 0) == lhs
+                       && XEXP (XEXP (tem, 0), 1) == rhs)
+                     break;
                    lneg &= rneg;
                    if (GET_CODE (tem) == NEG)
                      tem = XEXP (tem, 0), lneg = !lneg;
@@ -4419,20 +4345,22 @@ simplify_plus_minus (enum rtx_code code, enum machine_mode mode, rtx op0,
              }
          }
 
-      /* If nothing changed, fail.  */
-      if (!canonicalized)
-        return NULL_RTX;
+      if (!changed)
+       break;
 
       /* Pack all the operands to the lower-numbered entries.  */
       for (i = 0, j = 0; j < n_ops; j++)
-        if (ops[j].op)
-          {
+       if (ops[j].op)
+         {
            ops[i] = ops[j];
            i++;
-          }
+         }
       n_ops = i;
     }
-  while (changed);
+
+  /* If nothing changed, fail.  */
+  if (!canonicalized)
+    return NULL_RTX;
 
   /* Create (minus -C X) instead of (neg (const (plus X C))).  */
   if (n_ops == 2
@@ -4503,8 +4431,8 @@ plus_minus_operand_p (const_rtx x)
    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,
-                              enum machine_mode cmp_mode, rtx op0, rtx op1)
+simplify_relational_operation (enum rtx_code code, machine_mode mode,
+                              machine_mode cmp_mode, rtx op0, rtx op1)
 {
   rtx tem, trueop0, trueop1;
 
@@ -4524,7 +4452,7 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
          {
            REAL_VALUE_TYPE val;
            val = FLOAT_STORE_FLAG_VALUE (mode);
-           return CONST_DOUBLE_FROM_REAL_VALUE (val, mode);
+           return const_double_from_real_value (val, mode);
          }
 #else
          return NULL_RTX;
@@ -4562,7 +4490,7 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
   /* 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);
+    std::swap (op0, op1), code = swap_condition (code);
 
   /* If op0 is a compare, extract the comparison arguments from it.  */
   if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
@@ -4586,8 +4514,8 @@ simplify_relational_operation (enum rtx_code code, enum machine_mode mode,
    mode the comparison is done in, so it is the mode of the operands.  */
 
 static rtx
-simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
-                                enum machine_mode cmp_mode, rtx op0, rtx op1)
+simplify_relational_operation_1 (enum rtx_code code, machine_mode mode,
+                                machine_mode cmp_mode, rtx op0, rtx op1)
 {
   enum rtx_code op0code = GET_CODE (op0);
 
@@ -4732,16 +4660,16 @@ simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
       && op0code == XOR
       && rtx_equal_p (XEXP (op0, 0), op1)
       && !side_effects_p (XEXP (op0, 0)))
-    return simplify_gen_relational (code, mode, cmp_mode,
-                                   XEXP (op0, 1), const0_rtx);
+    return simplify_gen_relational (code, mode, cmp_mode, XEXP (op0, 1),
+                                   CONST0_RTX (mode));
 
   /* Likewise (eq/ne (xor x y) y) simplifies to (eq/ne x 0).  */
   if ((code == EQ || code == NE)
       && op0code == XOR
       && rtx_equal_p (XEXP (op0, 1), op1)
       && !side_effects_p (XEXP (op0, 1)))
-    return simplify_gen_relational (code, mode, cmp_mode,
-                                   XEXP (op0, 0), const0_rtx);
+    return simplify_gen_relational (code, mode, cmp_mode, XEXP (op0, 0),
+                                   CONST0_RTX (mode));
 
   /* (eq/ne (xor x C1) C2) simplifies to (eq/ne x (C1^C2)).  */
   if ((code == EQ || code == NE)
@@ -4752,6 +4680,51 @@ simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
                                    simplify_gen_binary (XOR, cmp_mode,
                                                         XEXP (op0, 1), op1));
 
+  /* (eq/ne (and x y) x) simplifies to (eq/ne (and (not y) x) 0), which
+     can be implemented with a BICS instruction on some targets, or
+     constant-folded if y is a constant.  */
+  if ((code == EQ || code == NE)
+      && op0code == AND
+      && rtx_equal_p (XEXP (op0, 0), op1)
+      && !side_effects_p (op1)
+      && op1 != CONST0_RTX (cmp_mode))
+    {
+      rtx not_y = simplify_gen_unary (NOT, cmp_mode, XEXP (op0, 1), cmp_mode);
+      rtx lhs = simplify_gen_binary (AND, cmp_mode, not_y, XEXP (op0, 0));
+
+      return simplify_gen_relational (code, mode, cmp_mode, lhs,
+                                     CONST0_RTX (cmp_mode));
+    }
+
+  /* Likewise for (eq/ne (and x y) y).  */
+  if ((code == EQ || code == NE)
+      && op0code == AND
+      && rtx_equal_p (XEXP (op0, 1), op1)
+      && !side_effects_p (op1)
+      && op1 != CONST0_RTX (cmp_mode))
+    {
+      rtx not_x = simplify_gen_unary (NOT, cmp_mode, XEXP (op0, 0), cmp_mode);
+      rtx lhs = simplify_gen_binary (AND, cmp_mode, not_x, XEXP (op0, 1));
+
+      return simplify_gen_relational (code, mode, cmp_mode, lhs,
+                                     CONST0_RTX (cmp_mode));
+    }
+
+  /* (eq/ne (bswap x) C1) simplifies to (eq/ne x C2) with C2 swapped.  */
+  if ((code == EQ || code == NE)
+      && GET_CODE (op0) == BSWAP
+      && CONST_SCALAR_INT_P (op1))
+    return simplify_gen_relational (code, mode, cmp_mode, XEXP (op0, 0),
+                                   simplify_gen_unary (BSWAP, cmp_mode,
+                                                       op1, cmp_mode));
+
+  /* (eq/ne (bswap x) (bswap y)) simplifies to (eq/ne x y).  */
+  if ((code == EQ || code == NE)
+      && GET_CODE (op0) == BSWAP
+      && GET_CODE (op1) == BSWAP)
+    return simplify_gen_relational (code, mode, cmp_mode,
+                                   XEXP (op0, 0), XEXP (op1, 0));
+
   if (op0code == POPCOUNT && op1 == const0_rtx)
     switch (code)
       {
@@ -4837,14 +4810,15 @@ comparison_result (enum rtx_code code, int known_results)
     }
 }
 
-/* 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.  */
+/* Check if the given comparison (done in the given MODE) is actually
+   a tautology or a contradiction.  If the mode is VOID_mode, the
+   comparison is done in "infinite precision".  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,
+                                    machine_mode mode,
                                     rtx op0, rtx op1)
 {
   rtx tem;
@@ -4877,7 +4851,7 @@ simplify_const_relational_operation (enum rtx_code code,
   /* Make sure the constant is second.  */
   if (swap_commutative_operands_p (op0, op1))
     {
-      tem = op0, op0 = op1, op1 = tem;
+      std::swap (op0, op1);
       code = swap_condition (code);
     }
 
@@ -4914,10 +4888,10 @@ simplify_const_relational_operation (enum rtx_code code,
      result except if they have side-effects.  Even with NaNs we know
      the result of unordered comparisons and, if signaling NaNs are
      irrelevant, also the result of LT/GT/LTGT.  */
-  if ((! HONOR_NANS (GET_MODE (trueop0))
+  if ((! HONOR_NANS (trueop0)
        || code == UNEQ || code == UNLE || code == UNGE
        || ((code == LT || code == GT || code == LTGT)
-          && ! HONOR_SNANS (GET_MODE (trueop0))))
+          && ! HONOR_SNANS (trueop0)))
       && rtx_equal_p (trueop0, trueop1)
       && ! side_effects_p (trueop0))
     return comparison_result (code, CMP_EQ);
@@ -4928,13 +4902,11 @@ simplify_const_relational_operation (enum rtx_code code,
       && CONST_DOUBLE_AS_FLOAT_P (trueop1)
       && SCALAR_FLOAT_MODE_P (GET_MODE (trueop0)))
     {
-      REAL_VALUE_TYPE d0, d1;
-
-      REAL_VALUE_FROM_CONST_DOUBLE (d0, trueop0);
-      REAL_VALUE_FROM_CONST_DOUBLE (d1, trueop1);
+      const REAL_VALUE_TYPE *d0 = CONST_DOUBLE_REAL_VALUE (trueop0);
+      const REAL_VALUE_TYPE *d1 = CONST_DOUBLE_REAL_VALUE (trueop1);
 
       /* Comparisons are unordered iff at least one of the values is NaN.  */
-      if (REAL_VALUE_ISNAN (d0) || REAL_VALUE_ISNAN (d1))
+      if (REAL_VALUE_ISNAN (*d0) || REAL_VALUE_ISNAN (*d1))
        switch (code)
          {
          case UNEQ:
@@ -4958,72 +4930,35 @@ simplify_const_relational_operation (enum rtx_code code,
          }
 
       return comparison_result (code,
-                               (REAL_VALUES_EQUAL (d0, d1) ? CMP_EQ :
-                                REAL_VALUES_LESS (d0, d1) ? CMP_LT : CMP_GT));
+                               (real_equal (d0, d1) ? CMP_EQ :
+                                real_less (d0, d1) ? CMP_LT : CMP_GT));
     }
 
   /* Otherwise, see if the operands are both integers.  */
   if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
-       && (CONST_DOUBLE_AS_INT_P (trueop0) || CONST_INT_P (trueop0))
-       && (CONST_DOUBLE_AS_INT_P (trueop1) || CONST_INT_P (trueop1)))
+      && CONST_SCALAR_INT_P (trueop0) && CONST_SCALAR_INT_P (trueop1))
     {
-      int width = GET_MODE_PRECISION (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 (CONST_DOUBLE_AS_INT_P (trueop0))
-       {
-         l0u = l0s = CONST_DOUBLE_LOW (trueop0);
-         h0u = h0s = CONST_DOUBLE_HIGH (trueop0);
-       }
-      else
-       {
-         l0u = l0s = INTVAL (trueop0);
-         h0u = h0s = HWI_SIGN_EXTEND (l0s);
-       }
-
-      if (CONST_DOUBLE_AS_INT_P (trueop1))
-       {
-         l1u = l1s = CONST_DOUBLE_LOW (trueop1);
-         h1u = h1s = CONST_DOUBLE_HIGH (trueop1);
-       }
-      else
-       {
-         l1u = l1s = INTVAL (trueop1);
-         h1u = h1s = HWI_SIGN_EXTEND (l1s);
-       }
-
-      /* If WIDTH is nonzero and smaller than HOST_BITS_PER_WIDE_INT,
-        we have to sign or zero-extend the values.  */
-      if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
-       {
-         l0u &= GET_MODE_MASK (mode);
-         l1u &= GET_MODE_MASK (mode);
-
-         if (val_signbit_known_set_p (mode, l0s))
-           l0s |= ~GET_MODE_MASK (mode);
-
-         if (val_signbit_known_set_p (mode, l1s))
-           l1s |= ~GET_MODE_MASK (mode);
-       }
-      if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
-       h0u = h1u = 0, h0s = HWI_SIGN_EXTEND (l0s), h1s = HWI_SIGN_EXTEND (l1s);
-
-      if (h0u == h1u && l0u == l1u)
+      /* It would be nice if we really had a mode here.  However, the
+        largest int representable on the target is as good as
+        infinite.  */
+      machine_mode cmode = (mode == VOIDmode) ? MAX_MODE_INT : mode;
+      rtx_mode_t ptrueop0 = std::make_pair (trueop0, cmode);
+      rtx_mode_t ptrueop1 = std::make_pair (trueop1, cmode);
+
+      if (wi::eq_p (ptrueop0, ptrueop1))
        return comparison_result (code, CMP_EQ);
       else
        {
-         int cr;
-         cr = (h0s < h1s || (h0s == h1s && l0u < l1u)) ? CMP_LT : CMP_GT;
-         cr |= (h0u < h1u || (h0u == h1u && l0u < l1u)) ? CMP_LTU : CMP_GTU;
+         int cr = wi::lts_p (ptrueop0, ptrueop1) ? CMP_LT : CMP_GT;
+         cr |= wi::ltu_p (ptrueop0, ptrueop1) ? CMP_LTU : CMP_GTU;
          return comparison_result (code, cr);
        }
     }
 
   /* Optimize comparisons with upper and lower bounds.  */
   if (HWI_COMPUTABLE_MODE_P (mode)
-      && CONST_INT_P (trueop1))
+      && CONST_INT_P (trueop1)
+      && !side_effects_p (trueop0))
     {
       int sign;
       unsigned HOST_WIDE_INT nonzero = nonzero_bits (trueop0, mode);
@@ -5136,7 +5071,7 @@ simplify_const_relational_operation (enum rtx_code code,
     }
 
   /* Optimize integer comparisons with zero.  */
-  if (trueop1 == const0_rtx)
+  if (trueop1 == const0_rtx && !side_effects_p (trueop0))
     {
       /* Some addresses are known to be nonzero.  We don't know
         their sign, but equality comparisons are known.  */
@@ -5187,7 +5122,7 @@ simplify_const_relational_operation (enum rtx_code code,
     }
 
   /* Optimize comparison of ABS with zero.  */
-  if (trueop1 == CONST0_RTX (mode)
+  if (trueop1 == CONST0_RTX (mode) && !side_effects_p (trueop0)
       && (GET_CODE (trueop0) == ABS
          || (GET_CODE (trueop0) == FLOAT_EXTEND
              && GET_CODE (XEXP (trueop0, 0)) == ABS)))
@@ -5243,8 +5178,8 @@ simplify_const_relational_operation (enum rtx_code code,
    a constant.  Return 0 if no simplifications is possible.  */
 
 rtx
-simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
-                           enum machine_mode op0_mode, rtx op0, rtx op1,
+simplify_ternary_operation (enum rtx_code code, machine_mode mode,
+                           machine_mode op0_mode, rtx op0, rtx op1,
                            rtx op2)
 {
   unsigned int width = GET_MODE_PRECISION (mode);
@@ -5276,7 +5211,7 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
       /* Canonicalize the two multiplication operands.  */
       /* a * -b + c  =>  -b * a + c.  */
       if (swap_commutative_operands_p (op0, op1))
-       tem = op0, op0 = op1, op1 = tem, any_change = true;
+       std::swap (op0, op1), any_change = true;
 
       if (any_change)
        return gen_rtx_FMA (mode, op0, op1, op2);
@@ -5344,9 +5279,35 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
                  && rtx_equal_p (XEXP (op0, 1), op1))))
        return op2;
 
+      /* Convert (!c) != {0,...,0} ? a : b into
+         c != {0,...,0} ? b : a for vector modes.  */
+      if (VECTOR_MODE_P (GET_MODE (op1))
+         && GET_CODE (op0) == NE
+         && GET_CODE (XEXP (op0, 0)) == NOT
+         && GET_CODE (XEXP (op0, 1)) == CONST_VECTOR)
+       {
+         rtx cv = XEXP (op0, 1);
+         int nunits = CONST_VECTOR_NUNITS (cv);
+         bool ok = true;
+         for (int i = 0; i < nunits; ++i)
+           if (CONST_VECTOR_ELT (cv, i) != const0_rtx)
+             {
+               ok = false;
+               break;
+             }
+         if (ok)
+           {
+             rtx new_op0 = gen_rtx_NE (GET_MODE (op0),
+                                       XEXP (XEXP (op0, 0), 0),
+                                       XEXP (op0, 1));
+             rtx retval = gen_rtx_IF_THEN_ELSE (mode, new_op0, op2, op1);
+             return retval;
+           }
+       }
+
       if (COMPARISON_P (op0) && ! side_effects_p (op0))
        {
-         enum machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
+         machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
                                        ? GET_MODE (XEXP (op0, 1))
                                        : GET_MODE (XEXP (op0, 0)));
          rtx temp;
@@ -5398,7 +5359,7 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
       trueop2 = avoid_constant_pool_reference (op2);
       if (CONST_INT_P (trueop2))
        {
-         int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
+         int elt_size = GET_MODE_UNIT_SIZE (mode);
          unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
          unsigned HOST_WIDE_INT sel = UINTVAL (trueop2);
          unsigned HOST_WIDE_INT mask;
@@ -5457,6 +5418,22 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
                                                 op0, XEXP (op1, 0), op2);
                }
            }
+
+         /* Replace (vec_merge (vec_duplicate (vec_select a parallel (i))) a 1 << i)
+            with a.  */
+         if (GET_CODE (op0) == VEC_DUPLICATE
+             && GET_CODE (XEXP (op0, 0)) == VEC_SELECT
+             && GET_CODE (XEXP (XEXP (op0, 0), 1)) == PARALLEL
+             && mode_nunits[GET_MODE (XEXP (op0, 0))] == 1)
+           {
+             tem = XVECEXP ((XEXP (XEXP (op0, 0), 1)), 0, 0);
+             if (CONST_INT_P (tem) && CONST_INT_P (op2))
+               {
+                 if (XEXP (XEXP (op0, 0), 0) == op1
+                     && UINTVAL (op2) == HOST_WIDE_INT_1U << UINTVAL (tem))
+                   return op1;
+               }
+           }
        }
 
       if (rtx_equal_p (op0, op1)
@@ -5472,25 +5449,23 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
   return 0;
 }
 
-/* Evaluate a SUBREG of a CONST_INT or CONST_DOUBLE or CONST_FIXED
-   or CONST_VECTOR,
-   returning another CONST_INT or CONST_DOUBLE or CONST_FIXED or CONST_VECTOR.
+/* Evaluate a SUBREG of a CONST_INT or CONST_WIDE_INT or CONST_DOUBLE
+   or CONST_FIXED or CONST_VECTOR, returning another CONST_INT or
+   CONST_WIDE_INT or CONST_DOUBLE or CONST_FIXED or CONST_VECTOR.
 
    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.  */
 
 static rtx
-simplify_immed_subreg (enum machine_mode outermode, rtx op,
-                      enum machine_mode innermode, unsigned int byte)
+simplify_immed_subreg (machine_mode outermode, rtx op,
+                      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];
+  unsigned char value[MAX_BITSIZE_MODE_ANY_MODE / value_bit];
   int value_start;
   int i;
   int elem;
@@ -5501,7 +5476,8 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
   rtx result_s;
   rtvec result_v = NULL;
   enum mode_class outer_class;
-  enum machine_mode outer_submode;
+  machine_mode outer_submode;
+  int max_bitsize;
 
   /* Some ports misuse CCmode.  */
   if (GET_MODE_CLASS (outermode) == MODE_CC && CONST_INT_P (op))
@@ -5511,13 +5487,17 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
   if (COMPLEX_MODE_P (outermode))
     return NULL_RTX;
 
+  /* We support any size mode.  */
+  max_bitsize = MAX (GET_MODE_BITSIZE (outermode),
+                    GET_MODE_BITSIZE (innermode));
+
   /* Unpack the value.  */
 
   if (GET_CODE (op) == CONST_VECTOR)
     {
       num_elem = CONST_VECTOR_NUNITS (op);
       elems = &CONST_VECTOR_ELT (op, 0);
-      elem_bitsize = GET_MODE_BITSIZE (GET_MODE_INNER (innermode));
+      elem_bitsize = GET_MODE_UNIT_BITSIZE (innermode);
     }
   else
     {
@@ -5560,8 +5540,20 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
            *vp++ = INTVAL (el) < 0 ? -1 : 0;
          break;
 
+       case CONST_WIDE_INT:
+         {
+           rtx_mode_t val = std::make_pair (el, innermode);
+           unsigned char extend = wi::sign_mask (val);
+
+           for (i = 0; i < elem_bitsize; i += value_bit)
+             *vp++ = wi::extract_uhwi (val, i, value_bit);
+           for (; i < elem_bitsize; i += value_bit)
+             *vp++ = extend;
+         }
+         break;
+
        case CONST_DOUBLE:
-         if (GET_MODE (el) == VOIDmode)
+         if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (el) == VOIDmode)
            {
              unsigned char extend = 0;
              /* If this triggers, someone should have generated a
@@ -5584,7 +5576,8 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
            }
          else
            {
-             long tmp[max_bitsize / 32];
+             /* This is big enough for anything on the platform.  */
+             long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
              int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
 
              gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (el)));
@@ -5661,21 +5654,17 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
   value_start = byte * (BITS_PER_UNIT / value_bit);
 
   /* Re-pack the value.  */
+  num_elem = GET_MODE_NUNITS (outermode);
 
   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;
-    }
+    elems = &result_s;
 
+  outer_submode = GET_MODE_INNER (outermode);
   outer_class = GET_MODE_CLASS (outer_submode);
   elem_bitsize = GET_MODE_BITSIZE (outer_submode);
 
@@ -5704,24 +5693,35 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
        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 |= (unsigned HOST_WIDE_INT)(*vp++ & value_mask) << i;
-           for (; i < elem_bitsize; i += value_bit)
-             hi |= (unsigned 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 if (elem_bitsize <= HOST_BITS_PER_DOUBLE_INT)
-             elems[elem] = immed_double_const (lo, hi, outer_submode);
-           else
+           int u;
+           int base = 0;
+           int units
+             = (GET_MODE_BITSIZE (outer_submode) + HOST_BITS_PER_WIDE_INT - 1)
+             / HOST_BITS_PER_WIDE_INT;
+           HOST_WIDE_INT tmp[MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT];
+           wide_int r;
+
+           if (GET_MODE_PRECISION (outer_submode) > MAX_BITSIZE_MODE_ANY_INT)
+             return NULL_RTX;
+           for (u = 0; u < units; u++)
+             {
+               unsigned HOST_WIDE_INT buf = 0;
+               for (i = 0;
+                    i < HOST_BITS_PER_WIDE_INT && base + i < elem_bitsize;
+                    i += value_bit)
+                 buf |= (unsigned HOST_WIDE_INT)(*vp++ & value_mask) << i;
+
+               tmp[u] = buf;
+               base += HOST_BITS_PER_WIDE_INT;
+             }
+           r = wide_int::from_array (tmp, units,
+                                     GET_MODE_PRECISION (outer_submode));
+#if TARGET_SUPPORTS_WIDE_INT == 0
+           /* Make sure r will fit into CONST_INT or CONST_DOUBLE.  */
+           if (wi::min_precision (r, SIGNED) > HOST_BITS_PER_DOUBLE_INT)
              return NULL_RTX;
+#endif
+           elems[elem] = immed_wide_int_const (r, outer_submode);
          }
          break;
 
@@ -5729,7 +5729,7 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
        case MODE_DECIMAL_FLOAT:
          {
            REAL_VALUE_TYPE r;
-           long tmp[max_bitsize / 32];
+           long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
 
            /* real_from_target wants its input in words affected by
               FLOAT_WORDS_BIG_ENDIAN.  However, we ignore this,
@@ -5748,7 +5748,7 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
              }
 
            real_from_target (&r, tmp, outer_submode);
-           elems[elem] = CONST_DOUBLE_FROM_REAL_VALUE (r, outer_submode);
+           elems[elem] = const_double_from_real_value (r, outer_submode);
          }
          break;
 
@@ -5787,8 +5787,8 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
 /* 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)
+simplify_subreg (machine_mode outermode, rtx op,
+                machine_mode innermode, unsigned int byte)
 {
   /* Little bit of sanity checking.  */
   gcc_assert (innermode != VOIDmode);
@@ -5818,7 +5818,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
      or not at all if changing back op starting mode.  */
   if (GET_CODE (op) == SUBREG)
     {
-      enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
+      machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
       int final_offset = byte + SUBREG_BYTE (op);
       rtx newx;
 
@@ -5886,7 +5886,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
        {
          newx = gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
          if (SUBREG_PROMOTED_VAR_P (op)
-             && SUBREG_PROMOTED_UNSIGNED_P (op) >= 0
+             && SUBREG_PROMOTED_SIGN (op) >= 0
              && GET_MODE_CLASS (outermode) == MODE_INT
              && IN_RANGE (GET_MODE_SIZE (outermode),
                           GET_MODE_SIZE (innermode),
@@ -5894,8 +5894,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
              && subreg_lowpart_p (newx))
            {
              SUBREG_PROMOTED_VAR_P (newx) = 1;
-             SUBREG_PROMOTED_UNSIGNED_SET
-               (newx, SUBREG_PROMOTED_UNSIGNED_P (op));
+             SUBREG_PROMOTED_SET (newx, SUBREG_PROMOTED_GET (op));
            }
          return newx;
        }
@@ -6012,8 +6011,8 @@ simplify_subreg (enum machine_mode outermode, rtx op,
 /* Make a SUBREG operation or equivalent if it folds.  */
 
 rtx
-simplify_gen_subreg (enum machine_mode outermode, rtx op,
-                    enum machine_mode innermode, unsigned int byte)
+simplify_gen_subreg (machine_mode outermode, rtx op,
+                    machine_mode innermode, unsigned int byte)
 {
   rtx newx;
 
@@ -6032,6 +6031,17 @@ simplify_gen_subreg (enum machine_mode outermode, rtx op,
   return NULL_RTX;
 }
 
+/* Generates a subreg to get the least significant part of EXPR (in mode
+   INNER_MODE) to OUTER_MODE.  */
+
+rtx
+lowpart_subreg (machine_mode outer_mode, rtx expr,
+                            machine_mode inner_mode)
+{
+  return simplify_gen_subreg (outer_mode, expr, inner_mode,
+                             subreg_lowpart_offset (outer_mode, inner_mode));
+}
+
 /* Simplify X, an rtx expression.
 
    Return the simplified expression or NULL if no simplifications
@@ -6076,7 +6086,7 @@ rtx
 simplify_rtx (const_rtx x)
 {
   const enum rtx_code code = GET_CODE (x);
-  const enum machine_mode mode = GET_MODE (x);
+  const machine_mode mode = GET_MODE (x);
 
   switch (GET_RTX_CLASS (code))
     {