re PR debug/66691 (ICE on valid code at -O3 with -g enabled in simplify_subreg, at...
[gcc.git] / gcc / simplify-rtx.c
index 3019fd86112dd7d1a99e136a2df31909b2542e2f..ca8310d15563a05be41957df933e302928ba29e6 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.
 
@@ -23,7 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "rtl.h"
+#include "alias.h"
+#include "symtab.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "varasm.h"
 #include "tm_p.h"
 #include "regs.h"
@@ -32,10 +35,18 @@ along with GCC; see the file COPYING3.  If not see
 #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"
+#include "predict.h"
 
 /* Simplification and canonicalization of RTL.  */
 
@@ -46,24 +57,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);
 }
@@ -72,7 +82,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;
@@ -87,6 +97,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)
@@ -94,8 +120,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)
@@ -108,7 +135,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;
 
@@ -126,7 +153,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;
 
@@ -144,7 +171,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;
 
@@ -163,7 +190,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;
@@ -176,7 +203,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);
 }
@@ -187,7 +214,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))
@@ -271,7 +298,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))
@@ -361,8 +388,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;
 
@@ -376,8 +403,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;
 
@@ -393,8 +420,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;
 
@@ -415,8 +442,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;
@@ -499,9 +526,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;
@@ -612,8 +645,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);
@@ -629,7 +662,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))
@@ -787,8 +820,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;
 
@@ -804,7 +837,7 @@ simplify_unary_operation (enum rtx_code code, enum machine_mode mode,
 /* 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;
@@ -867,7 +900,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         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);
@@ -880,7 +913,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,
@@ -899,7 +932,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
       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);
@@ -910,10 +943,7 @@ 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);
@@ -1017,7 +1047,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)
            {
@@ -1125,7 +1155,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)
@@ -1137,14 +1167,14 @@ 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,
+       return simplify_gen_unary (GET_CODE (op), mode,
                                   XEXP (op, 0),
                                   GET_MODE (XEXP (op, 0)));
 
@@ -1175,7 +1205,7 @@ 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
+         || ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
              && SCALAR_FLOAT_MODE_P (GET_MODE (op))
              && ((unsigned)significand_size (GET_MODE (op))
                  >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
@@ -1304,8 +1334,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)
@@ -1335,7 +1365,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);
@@ -1347,8 +1377,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)));
        }
@@ -1364,7 +1394,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)
@@ -1402,7 +1432,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);
@@ -1426,8 +1456,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)
@@ -1459,15 +1489,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)
            {
@@ -1528,11 +1558,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)
     {
@@ -1558,7 +1587,7 @@ 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);
+             machine_mode inmode = GET_MODE (op);
               int in_elt_size = GET_MODE_SIZE (GET_MODE_INNER (inmode));
               unsigned in_n_elts = (GET_MODE_SIZE (inmode) / in_elt_size);
 
@@ -1575,7 +1604,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
     {
       int elt_size = GET_MODE_SIZE (GET_MODE_INNER (mode));
       unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
-      enum machine_mode opmode = GET_MODE (op);
+      machine_mode opmode = GET_MODE (op);
       int op_elt_size = GET_MODE_SIZE (GET_MODE_INNER (opmode));
       unsigned op_n_elts = (GET_MODE_SIZE (opmode) / op_elt_size);
       rtvec v = rtvec_alloc (n_elts);
@@ -1600,336 +1629,123 @@ 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);
     }
   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);
     }
 
-  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 = - (unsigned HOST_WIDE_INT) 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) 
@@ -1977,11 +1793,10 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode 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
@@ -1990,9 +1805,13 @@ 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);
+      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:
@@ -2000,44 +1819,18 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
            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);
+         wmax = wi::max_value (width, SIGNED);
+         real_from_integer (&t, VOIDmode, wmax, SIGNED);
          if (REAL_VALUES_LESS (t, x))
-           {
-             xh = th;
-             xl = tl;
-             break;
-           }
+           return immed_wide_int_const (wmax, mode);
 
          /* Test against the signed lower bound.  */
-         if (width > HOST_BITS_PER_WIDE_INT)
-           {
-             th = HOST_WIDE_INT_M1U << (width - HOST_BITS_PER_WIDE_INT - 1);
-             tl = 0;
-           }
-         else
-           {
-             th = -1;
-             tl = HOST_WIDE_INT_M1U << (width - 1);
-           }
-         real_from_integer (&t, VOIDmode, tl, th, 0);
+         wmin = wi::min_value (width, SIGNED);
+         real_from_integer (&t, VOIDmode, wmin, SIGNED);
          if (REAL_VALUES_LESS (x, t))
-           {
-             xh = th;
-             xl = tl;
-             break;
-           }
-         REAL_VALUE_TO_INT (&xl, &xh, x);
+           return immed_wide_int_const (wmin, mode);
+
+         return immed_wide_int_const (real_to_integer (&x, &fail, width), mode);
          break;
 
        case UNSIGNED_FIX:
@@ -2045,37 +1838,18 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
            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);
+         wmax = wi::max_value (width, UNSIGNED);
+         real_from_integer (&t, VOIDmode, wmax, UNSIGNED);
          if (REAL_VALUES_LESS (t, x))
-           {
-             xh = th;
-             xl = tl;
-             break;
-           }
+           return immed_wide_int_const (wmax, mode);
 
-         REAL_VALUE_TO_INT (&xl, &xh, x);
+         return immed_wide_int_const (real_to_integer (&x, &fail, width),
+                                      mode);
          break;
 
        default:
          gcc_unreachable ();
        }
-      return immed_double_const (xl, xh, mode);
     }
 
   return NULL_RTX;
@@ -2087,7 +1861,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
    Return zero if no simplification or canonicalization is possible.  */
 
 static rtx
-simplify_byte_swapping_operation (enum rtx_code code, enum machine_mode mode,
+simplify_byte_swapping_operation (enum rtx_code code, machine_mode mode,
                                  rtx op0, rtx op1)
 {
   rtx tem;
@@ -2117,7 +1891,7 @@ simplify_byte_swapping_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;
@@ -2136,9 +1910,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)
@@ -2171,7 +1943,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;
@@ -2187,9 +1959,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);
@@ -2206,7 +1976,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;
@@ -2264,49 +2034,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);
            }
 
@@ -2314,11 +2085,9 @@ 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)
@@ -2391,15 +2160,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;
@@ -2440,49 +2208,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);
            }
@@ -2491,11 +2260,9 @@ 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)
@@ -2647,26 +2414,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)
@@ -2933,6 +2687,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)
@@ -2949,12 +2736,31 @@ 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,
@@ -2963,7 +2769,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode 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_mode (~cval & bval,
@@ -3053,7 +2859,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));
@@ -3066,7 +2872,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);
@@ -3179,15 +2985,27 @@ 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;
@@ -3330,13 +3148,15 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
         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_BITSIZE (mode) / 2 + (code == ROTATE),
-                      GET_MODE_BITSIZE (mode) - 1))
+                      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_BITSIZE (mode)
+                                   mode, op0, GEN_INT (GET_MODE_PRECISION (mode)
                                                        - INTVAL (trueop1)));
+#endif
       /* FALLTHRU */
     case ASHIFTRT:
       if (trueop1 == CONST0_RTX (mode))
@@ -3348,10 +3168,47 @@ 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 simplify_gen_subreg (mode, tmp, inner_mode,
+                                     subreg_lowpart_offset (mode,
+                                                            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));
        }
@@ -3377,7 +3234,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)
@@ -3468,7 +3325,7 @@ 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);
+             machine_mode opmode = GET_MODE (op0);
              int elt_size = GET_MODE_SIZE (GET_MODE_INNER (opmode));
              int n_elts = GET_MODE_SIZE (opmode) / elt_size;
 
@@ -3490,7 +3347,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);
@@ -3620,6 +3477,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_SIZE (GET_MODE_INNER (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
@@ -3633,7 +3534,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
@@ -3673,13 +3588,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));
 
@@ -3767,11 +3707,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)
@@ -3780,9 +3718,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;
@@ -3964,300 +3902,145 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode 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 = (unsigned HOST_WIDE_INT) arg0s + arg1s;
-         break;
-
-       case MINUS:
-         val = (unsigned HOST_WIDE_INT) arg0s - arg1s;
-         break;
-
-       case MULT:
-         val = (unsigned HOST_WIDE_INT) 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 |= HOST_WIDE_INT_M1U << (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;
@@ -4265,20 +4048,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.
+/* 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.  */
 
-   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.  */
-
-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;
@@ -4286,23 +4059,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);
@@ -4319,6 +4105,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++)
        {
@@ -4330,7 +4117,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);
@@ -4338,9 +4125,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:
@@ -4351,7 +4148,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)))
@@ -4361,20 +4158,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;
 
@@ -4385,7 +4182,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;
 
@@ -4429,23 +4226,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--)
@@ -4462,10 +4265,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)))
@@ -4474,7 +4277,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);
@@ -4482,16 +4286,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;
@@ -4507,20 +4316,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
@@ -4591,8 +4402,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;
 
@@ -4650,7 +4461,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)
@@ -4674,8 +4485,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);
 
@@ -4820,16 +4631,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)
@@ -4840,6 +4651,36 @@ 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
@@ -4940,14 +4781,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;
@@ -4980,7 +4822,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);
     }
 
@@ -5017,10 +4859,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);
@@ -5067,59 +4909,21 @@ simplify_const_relational_operation (enum rtx_code code,
 
   /* 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);
        }
     }
@@ -5346,8 +5150,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);
@@ -5379,7 +5183,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);
@@ -5449,7 +5253,7 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
 
       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;
@@ -5560,6 +5364,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)
@@ -5575,25 +5395,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;
@@ -5604,7 +5422,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))
@@ -5614,6 +5433,10 @@ 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)
@@ -5663,8 +5486,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
@@ -5687,7 +5522,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)));
@@ -5807,24 +5643,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;
 
@@ -5832,7 +5679,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,
@@ -5890,8 +5737,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);
@@ -5921,7 +5768,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;
 
@@ -5989,7 +5836,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),
@@ -5997,8 +5844,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;
        }
@@ -6115,8 +5961,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;
 
@@ -6179,7 +6025,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))
     {