poly_int: get_inner_reference & co.
[gcc.git] / gcc / simplify-rtx.c
index 17568baa8b05fb9059af6250dec45580de5debfd..35c98fb883f0b250c53e75d2e06038ac73d359c5 100644 (file)
@@ -1,5 +1,5 @@
 /* RTL simplification functions for GNU compiler.
-   Copyright (C) 1987-2015 Free Software Foundation, Inc.
+   Copyright (C) 1987-2017 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -26,12 +26,15 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "tree.h"
 #include "predict.h"
+#include "memmodel.h"
 #include "optabs.h"
 #include "emit-rtl.h"
 #include "recog.h"
 #include "diagnostic-core.h"
 #include "varasm.h"
 #include "flags.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
 
 /* Simplification and canonicalization of RTL.  */
 
@@ -40,13 +43,11 @@ along with GCC; see the file COPYING3.  If not see
    occasionally need to sign extend from low to high as if low were a
    signed wide int.  */
 #define HWI_SIGN_EXTEND(low) \
((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
 ((((HOST_WIDE_INT) low) < 0) ? HOST_WIDE_INT_M1 : HOST_WIDE_INT_0)
 
 static rtx neg_const_int (machine_mode, const_rtx);
 static bool plus_minus_operand_p (const_rtx);
 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, machine_mode,
                                           rtx, rtx);
 static rtx simplify_relational_operation_1 (enum rtx_code, machine_mode,
@@ -55,12 +56,17 @@ 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).  */
+/* Negate a CONST_INT rtx.  */
 static rtx
 neg_const_int (machine_mode mode, const_rtx i)
 {
-  return gen_int_mode (-(unsigned HOST_WIDE_INT) INTVAL (i), mode);
+  unsigned HOST_WIDE_INT val = -UINTVAL (i);
+  
+  if (!HWI_COMPUTABLE_MODE_P (mode)
+      && val == UINTVAL (i))
+    return simplify_const_unary_operation (NEG, mode, CONST_CAST_RTX (i),
+                                          mode);
+  return gen_int_mode (val, mode);
 }
 
 /* Test whether expression, X, is an immediate constant that represents
@@ -71,11 +77,12 @@ mode_signbit_p (machine_mode mode, const_rtx x)
 {
   unsigned HOST_WIDE_INT val;
   unsigned int width;
+  scalar_int_mode int_mode;
 
-  if (GET_MODE_CLASS (mode) != MODE_INT)
+  if (!is_int_mode (mode, &int_mode))
     return false;
 
-  width = GET_MODE_PRECISION (mode);
+  width = GET_MODE_PRECISION (int_mode);
   if (width == 0)
     return false;
 
@@ -111,8 +118,8 @@ mode_signbit_p (machine_mode mode, const_rtx x)
     return false;
 
   if (width < HOST_BITS_PER_WIDE_INT)
-    val &= ((unsigned HOST_WIDE_INT) 1 << width) - 1;
-  return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
+    val &= (HOST_WIDE_INT_1U << width) - 1;
+  return val == (HOST_WIDE_INT_1U << (width - 1));
 }
 
 /* Test whether VAL is equal to the most significant bit of mode MODE
@@ -123,16 +130,17 @@ bool
 val_signbit_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
+  scalar_int_mode int_mode;
 
-  if (GET_MODE_CLASS (mode) != MODE_INT)
+  if (!is_int_mode (mode, &int_mode))
     return false;
 
-  width = GET_MODE_PRECISION (mode);
+  width = GET_MODE_PRECISION (int_mode);
   if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
     return false;
 
-  val &= GET_MODE_MASK (mode);
-  return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
+  val &= GET_MODE_MASK (int_mode);
+  return val == (HOST_WIDE_INT_1U << (width - 1));
 }
 
 /* Test whether the most significant bit of mode MODE is set in VAL.
@@ -142,14 +150,15 @@ val_signbit_known_set_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
 
-  if (GET_MODE_CLASS (mode) != MODE_INT)
+  scalar_int_mode int_mode;
+  if (!is_int_mode (mode, &int_mode))
     return false;
 
-  width = GET_MODE_PRECISION (mode);
+  width = GET_MODE_PRECISION (int_mode);
   if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
     return false;
 
-  val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+  val &= HOST_WIDE_INT_1U << (width - 1);
   return val != 0;
 }
 
@@ -160,14 +169,15 @@ val_signbit_known_clear_p (machine_mode mode, unsigned HOST_WIDE_INT val)
 {
   unsigned int width;
 
-  if (GET_MODE_CLASS (mode) != MODE_INT)
+  scalar_int_mode int_mode;
+  if (!is_int_mode (mode, &int_mode))
     return false;
 
-  width = GET_MODE_PRECISION (mode);
+  width = GET_MODE_PRECISION (int_mode);
   if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
     return false;
 
-  val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+  val &= HOST_WIDE_INT_1U << (width - 1);
   return val == 0;
 }
 \f
@@ -251,15 +261,14 @@ avoid_constant_pool_reference (rtx x)
       /* If we're accessing the constant in a different mode than it was
          originally stored, attempt to fix that up via subreg simplifications.
          If that fails we have no choice but to return the original memory.  */
-      if ((offset != 0 || cmode != GET_MODE (x))
-         && offset >= 0 && offset < GET_MODE_SIZE (cmode))
+      if (offset == 0 && cmode == GET_MODE (x))
+       return c;
+      else if (offset >= 0 && offset < GET_MODE_SIZE (cmode))
         {
           rtx tem = simplify_subreg (GET_MODE (x), c, cmode, offset);
           if (tem && CONSTANT_P (tem))
             return tem;
         }
-      else
-        return c;
     }
 
   return x;
@@ -280,7 +289,7 @@ delegitimize_mem_from_attrs (rtx x)
     {
       tree decl = MEM_EXPR (x);
       machine_mode mode = GET_MODE (x);
-      HOST_WIDE_INT offset = 0;
+      poly_int64 offset = 0;
 
       switch (TREE_CODE (decl))
        {
@@ -299,30 +308,26 @@ delegitimize_mem_from_attrs (rtx x)
        case IMAGPART_EXPR:
        case VIEW_CONVERT_EXPR:
          {
-           HOST_WIDE_INT bitsize, bitpos;
+           poly_int64 bitsize, bitpos, bytepos, toffset_val = 0;
            tree toffset;
            int unsignedp, reversep, volatilep = 0;
 
            decl
              = get_inner_reference (decl, &bitsize, &bitpos, &toffset, &mode,
-                                    &unsignedp, &reversep, &volatilep, false);
-           if (bitsize != GET_MODE_BITSIZE (mode)
-               || (bitpos % BITS_PER_UNIT)
-               || (toffset && !tree_fits_shwi_p (toffset)))
+                                    &unsignedp, &reversep, &volatilep);
+           if (maybe_ne (bitsize, GET_MODE_BITSIZE (mode))
+               || !multiple_p (bitpos, BITS_PER_UNIT, &bytepos)
+               || (toffset && !poly_int_tree_p (toffset, &toffset_val)))
              decl = NULL;
            else
-             {
-               offset += bitpos / BITS_PER_UNIT;
-               if (toffset)
-                 offset += tree_to_shwi (toffset);
-             }
+             offset += bytepos + toffset_val;
            break;
          }
        }
 
       if (decl
          && mode == GET_MODE (x)
-         && TREE_CODE (decl) == VAR_DECL
+         && VAR_P (decl)
          && (TREE_STATIC (decl)
              || DECL_THREAD_LOCAL_P (decl))
          && DECL_RTL_SET_P (decl)
@@ -337,6 +342,7 @@ delegitimize_mem_from_attrs (rtx x)
          if (MEM_P (newx))
            {
              rtx n = XEXP (newx, 0), o = XEXP (x, 0);
+             poly_int64 n_offset, o_offset;
 
              /* Avoid creating a new MEM needlessly if we already had
                 the same address.  We do if there's no OFFSET and the
@@ -344,21 +350,14 @@ delegitimize_mem_from_attrs (rtx x)
                 form (plus NEWX OFFSET), or the NEWX is of the form
                 (plus Y (const_int Z)) and X is that with the offset
                 added: (plus Y (const_int Z+OFFSET)).  */
-             if (!((offset == 0
-                    || (GET_CODE (o) == PLUS
-                        && GET_CODE (XEXP (o, 1)) == CONST_INT
-                        && (offset == INTVAL (XEXP (o, 1))
-                            || (GET_CODE (n) == PLUS
-                                && GET_CODE (XEXP (n, 1)) == CONST_INT
-                                && (INTVAL (XEXP (n, 1)) + offset
-                                    == INTVAL (XEXP (o, 1)))
-                                && (n = XEXP (n, 0))))
-                        && (o = XEXP (o, 0))))
+             n = strip_offset (n, &n_offset);
+             o = strip_offset (o, &o_offset);
+             if (!(known_eq (o_offset, n_offset + offset)
                    && rtx_equal_p (o, n)))
                x = adjust_address_nv (newx, mode, offset);
            }
          else if (GET_MODE (x) == GET_MODE (newx)
-                  && offset == 0)
+                  && known_eq (offset, 0))
            x = newx;
        }
     }
@@ -391,8 +390,8 @@ simplify_gen_ternary (enum rtx_code code, machine_mode mode,
   rtx tem;
 
   /* If this simplifies, use it.  */
-  if (0 != (tem = simplify_ternary_operation (code, mode, op0_mode,
-                                             op0, op1, op2)))
+  if ((tem = simplify_ternary_operation (code, mode, op0_mode,
+                                        op0, op1, op2)) != 0)
     return tem;
 
   return gen_rtx_fmt_eee (code, mode, op0, op1, op2);
@@ -407,8 +406,8 @@ simplify_gen_relational (enum rtx_code code, machine_mode mode,
 {
   rtx tem;
 
-  if (0 != (tem = simplify_relational_operation (code, mode, cmp_mode,
-                                                op0, op1)))
+  if ((tem = simplify_relational_operation (code, mode, cmp_mode,
+                                           op0, op1)) != 0)
     return tem;
 
   return gen_rtx_fmt_ee (code, mode, op0, op1);
@@ -632,6 +631,8 @@ simplify_truncation (machine_mode mode, rtx op,
 {
   unsigned int precision = GET_MODE_UNIT_PRECISION (mode);
   unsigned int op_precision = GET_MODE_UNIT_PRECISION (op_mode);
+  scalar_int_mode int_mode, int_op_mode, subreg_mode;
+
   gcc_assert (precision <= op_precision);
 
   /* Optimize truncations of zero and sign extended values.  */
@@ -714,6 +715,65 @@ simplify_truncation (machine_mode mode, rtx op,
     return simplify_gen_binary (ASHIFT, mode,
                                XEXP (XEXP (op, 0), 0), XEXP (op, 1));
 
+  /* Likewise (truncate:QI (and:SI (lshiftrt:SI (x:SI) C) C2)) into
+     (and:QI (lshiftrt:QI (truncate:QI (x:SI)) C) C2) for suitable C
+     and C2.  */
+  if (GET_CODE (op) == AND
+      && (GET_CODE (XEXP (op, 0)) == LSHIFTRT
+         || GET_CODE (XEXP (op, 0)) == ASHIFTRT)
+      && CONST_INT_P (XEXP (XEXP (op, 0), 1))
+      && CONST_INT_P (XEXP (op, 1)))
+    {
+      rtx op0 = (XEXP (XEXP (op, 0), 0));
+      rtx shift_op = XEXP (XEXP (op, 0), 1);
+      rtx mask_op = XEXP (op, 1);
+      unsigned HOST_WIDE_INT shift = UINTVAL (shift_op);
+      unsigned HOST_WIDE_INT mask = UINTVAL (mask_op);
+
+      if (shift < precision
+         /* If doing this transform works for an X with all bits set,
+            it works for any X.  */
+         && ((GET_MODE_MASK (mode) >> shift) & mask)
+            == ((GET_MODE_MASK (op_mode) >> shift) & mask)
+         && (op0 = simplify_gen_unary (TRUNCATE, mode, op0, op_mode))
+         && (op0 = simplify_gen_binary (LSHIFTRT, mode, op0, shift_op)))
+       {
+         mask_op = GEN_INT (trunc_int_for_mode (mask, mode));
+         return simplify_gen_binary (AND, mode, op0, mask_op);
+       }
+    }
+
+  /* Turn (truncate:M1 (*_extract:M2 (reg:M2) (len) (pos))) into
+     (*_extract:M1 (truncate:M1 (reg:M2)) (len) (pos')) if possible without
+     changing len.  */
+  if ((GET_CODE (op) == ZERO_EXTRACT || GET_CODE (op) == SIGN_EXTRACT)
+      && REG_P (XEXP (op, 0))
+      && GET_MODE (XEXP (op, 0)) == GET_MODE (op)
+      && CONST_INT_P (XEXP (op, 1))
+      && CONST_INT_P (XEXP (op, 2)))
+    {
+      rtx op0 = XEXP (op, 0);
+      unsigned HOST_WIDE_INT len = UINTVAL (XEXP (op, 1));
+      unsigned HOST_WIDE_INT pos = UINTVAL (XEXP (op, 2));
+      if (BITS_BIG_ENDIAN && pos >= op_precision - precision)
+       {
+         op0 = simplify_gen_unary (TRUNCATE, mode, op0, GET_MODE (op0));
+         if (op0)
+           {
+             pos -= op_precision - precision;
+             return simplify_gen_ternary (GET_CODE (op), mode, mode, op0,
+                                          XEXP (op, 1), GEN_INT (pos));
+           }
+       }
+      else if (!BITS_BIG_ENDIAN && precision >= len + pos)
+       {
+         op0 = simplify_gen_unary (TRUNCATE, mode, op0, GET_MODE (op0));
+         if (op0)
+           return simplify_gen_ternary (GET_CODE (op), mode, mode, op0,
+                                        XEXP (op, 1), XEXP (op, 2));
+       }
+    }
+
   /* Recognize a word extraction from a multi-word subreg.  */
   if ((GET_CODE (op) == LSHIFTRT
        || GET_CODE (op) == ASHIFTRT)
@@ -725,7 +785,7 @@ simplify_truncation (machine_mode mode, rtx op,
       && (INTVAL (XEXP (op, 1)) & (precision - 1)) == 0
       && UINTVAL (XEXP (op, 1)) < op_precision)
     {
-      int byte = subreg_lowpart_offset (mode, op_mode);
+      poly_int64 byte = subreg_lowpart_offset (mode, op_mode);
       int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
       return simplify_gen_subreg (mode, XEXP (op, 0), op_mode,
                                  (WORDS_BIG_ENDIAN
@@ -738,21 +798,22 @@ simplify_truncation (machine_mode mode, rtx op,
      if the MEM has a mode-dependent address.  */
   if ((GET_CODE (op) == LSHIFTRT
        || GET_CODE (op) == ASHIFTRT)
-      && SCALAR_INT_MODE_P (op_mode)
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && is_a <scalar_int_mode> (op_mode, &int_op_mode)
       && MEM_P (XEXP (op, 0))
       && CONST_INT_P (XEXP (op, 1))
-      && (INTVAL (XEXP (op, 1)) % GET_MODE_BITSIZE (mode)) == 0
+      && INTVAL (XEXP (op, 1)) % GET_MODE_BITSIZE (int_mode) == 0
       && INTVAL (XEXP (op, 1)) > 0
-      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (op_mode)
+      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (int_op_mode)
       && ! mode_dependent_address_p (XEXP (XEXP (op, 0), 0),
                                     MEM_ADDR_SPACE (XEXP (op, 0)))
       && ! MEM_VOLATILE_P (XEXP (op, 0))
-      && (GET_MODE_SIZE (mode) >= UNITS_PER_WORD
+      && (GET_MODE_SIZE (int_mode) >= UNITS_PER_WORD
          || WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN))
     {
-      int byte = subreg_lowpart_offset (mode, op_mode);
+      poly_int64 byte = subreg_lowpart_offset (int_mode, int_op_mode);
       int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
-      return adjust_address_nv (XEXP (op, 0), mode,
+      return adjust_address_nv (XEXP (op, 0), int_mode,
                                (WORDS_BIG_ENDIAN
                                 ? byte - shifted_bytes
                                 : byte + shifted_bytes));
@@ -771,21 +832,20 @@ simplify_truncation (machine_mode mode, rtx op,
   /* (truncate:A (subreg:B (truncate:C X) 0)) is
      (truncate:A X).  */
   if (GET_CODE (op) == SUBREG
-      && SCALAR_INT_MODE_P (mode)
+      && is_a <scalar_int_mode> (mode, &int_mode)
       && SCALAR_INT_MODE_P (op_mode)
-      && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (op)))
+      && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op)), &subreg_mode)
       && GET_CODE (SUBREG_REG (op)) == TRUNCATE
       && subreg_lowpart_p (op))
     {
       rtx inner = XEXP (SUBREG_REG (op), 0);
-      if (GET_MODE_PRECISION (mode)
-         <= GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op))))
-       return simplify_gen_unary (TRUNCATE, mode, inner, GET_MODE (inner));
+      if (GET_MODE_PRECISION (int_mode) <= GET_MODE_PRECISION (subreg_mode))
+       return simplify_gen_unary (TRUNCATE, int_mode, inner,
+                                  GET_MODE (inner));
       else
        /* If subreg above is paradoxical and C is narrower
           than A, return (subreg:A (truncate:C X) 0).  */
-       return simplify_gen_subreg (mode, SUBREG_REG (op),
-                                   GET_MODE (SUBREG_REG (op)), 0);
+       return simplify_gen_subreg (int_mode, SUBREG_REG (op), subreg_mode, 0);
     }
 
   /* (truncate:A (truncate:B X)) is (truncate:A X).  */
@@ -793,6 +853,15 @@ simplify_truncation (machine_mode mode, rtx op,
     return simplify_gen_unary (TRUNCATE, mode, XEXP (op, 0),
                               GET_MODE (XEXP (op, 0)));
 
+  /* (truncate:A (ior X C)) is (const_int -1) if C is equal to that already,
+     in mode A.  */
+  if (GET_CODE (op) == IOR
+      && SCALAR_INT_MODE_P (mode)
+      && SCALAR_INT_MODE_P (op_mode)
+      && CONST_INT_P (XEXP (op, 1))
+      && trunc_int_for_mode (INTVAL (XEXP (op, 1)), mode) == -1)
+    return constm1_rtx;
+
   return NULL_RTX;
 }
 \f
@@ -846,7 +915,8 @@ static rtx
 simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
 {
   enum rtx_code reversed;
-  rtx temp;
+  rtx temp, elt, base, step;
+  scalar_int_mode inner, int_mode, op_mode, op0_mode;
 
   switch (code)
     {
@@ -859,7 +929,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         comparison is all ones.   */
       if (COMPARISON_P (op)
          && (mode == BImode || STORE_FLAG_VALUE == -1)
-         && ((reversed = reversed_comparison_code (op, NULL_RTX)) != UNKNOWN))
+         && ((reversed = reversed_comparison_code (op, NULL)) != UNKNOWN))
        return simplify_gen_relational (reversed, mode, VOIDmode,
                                        XEXP (op, 0), XEXP (op, 1));
 
@@ -868,8 +938,10 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
          && XEXP (op, 1) == constm1_rtx)
        return simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
 
-      /* Similarly, (not (neg X)) is (plus X -1).  */
-      if (GET_CODE (op) == NEG)
+      /* Similarly, (not (neg X)) is (plus X -1).  Only do this for
+        modes that have CONSTM1_RTX, i.e. MODE_INT, MODE_PARTIAL_INT
+        and MODE_VECTOR_INT.  */
+      if (GET_CODE (op) == NEG && CONSTM1_RTX (mode))
        return simplify_gen_binary (PLUS, mode, XEXP (op, 0),
                                    CONSTM1_RTX (mode));
 
@@ -905,17 +977,16 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         minus 1 is (ge foo (const_int 0)) if STORE_FLAG_VALUE is -1,
         so we can perform the above simplification.  */
       if (STORE_FLAG_VALUE == -1
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && GET_CODE (op) == ASHIFTRT
          && CONST_INT_P (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
-       return simplify_gen_relational (GE, mode, VOIDmode,
+         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (int_mode) - 1)
+       return simplify_gen_relational (GE, int_mode, VOIDmode,
                                        XEXP (op, 0), const0_rtx);
 
 
-      if (GET_CODE (op) == SUBREG
+      if (partial_subreg_p (op)
          && subreg_lowpart_p (op)
-         && (GET_MODE_SIZE (GET_MODE (op))
-             < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
          && GET_CODE (SUBREG_REG (op)) == ASHIFT
          && XEXP (SUBREG_REG (op), 0) == const1_rtx)
        {
@@ -982,7 +1053,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
               || (GET_CODE (false_rtx) == NEG
                   && rtx_equal_p (XEXP (false_rtx, 0), true_rtx)))
            {
-             if (reversed_comparison_code (cond, NULL_RTX) != UNKNOWN)
+             if (reversed_comparison_code (cond, NULL) != UNKNOWN)
                temp = reversed_comparison (cond, mode);
              else
                {
@@ -1055,7 +1126,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         C is equal to the width of MODE minus 1.  */
       if (GET_CODE (op) == ASHIFTRT
          && CONST_INT_P (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
+         && INTVAL (XEXP (op, 1)) == GET_MODE_UNIT_PRECISION (mode) - 1)
        return simplify_gen_binary (LSHIFTRT, mode,
                                    XEXP (op, 0), XEXP (op, 1));
 
@@ -1063,7 +1134,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         C is equal to the width of MODE minus 1.  */
       if (GET_CODE (op) == LSHIFTRT
          && CONST_INT_P (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
+         && INTVAL (XEXP (op, 1)) == GET_MODE_UNIT_PRECISION (mode) - 1)
        return simplify_gen_binary (ASHIFTRT, mode,
                                    XEXP (op, 0), XEXP (op, 1));
 
@@ -1077,29 +1148,47 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       /* (neg (lt x 0)) is (lshiftrt X C) if STORE_FLAG_VALUE is -1.  */
       if (GET_CODE (op) == LT
          && XEXP (op, 1) == const0_rtx
-         && SCALAR_INT_MODE_P (GET_MODE (XEXP (op, 0))))
+         && is_a <scalar_int_mode> (GET_MODE (XEXP (op, 0)), &inner))
        {
-         machine_mode inner = GET_MODE (XEXP (op, 0));
+         int_mode = as_a <scalar_int_mode> (mode);
          int isize = GET_MODE_PRECISION (inner);
          if (STORE_FLAG_VALUE == 1)
            {
              temp = simplify_gen_binary (ASHIFTRT, inner, XEXP (op, 0),
-                                         GEN_INT (isize - 1));
-             if (mode == inner)
+                                         gen_int_shift_amount (inner,
+                                                               isize - 1));
+             if (int_mode == inner)
                return temp;
-             if (GET_MODE_PRECISION (mode) > isize)
-               return simplify_gen_unary (SIGN_EXTEND, mode, temp, inner);
-             return simplify_gen_unary (TRUNCATE, mode, temp, inner);
+             if (GET_MODE_PRECISION (int_mode) > isize)
+               return simplify_gen_unary (SIGN_EXTEND, int_mode, temp, inner);
+             return simplify_gen_unary (TRUNCATE, int_mode, temp, inner);
            }
          else if (STORE_FLAG_VALUE == -1)
            {
              temp = simplify_gen_binary (LSHIFTRT, inner, XEXP (op, 0),
-                                         GEN_INT (isize - 1));
-             if (mode == inner)
+                                         gen_int_shift_amount (inner,
+                                                               isize - 1));
+             if (int_mode == inner)
                return temp;
-             if (GET_MODE_PRECISION (mode) > isize)
-               return simplify_gen_unary (ZERO_EXTEND, mode, temp, inner);
-             return simplify_gen_unary (TRUNCATE, mode, temp, inner);
+             if (GET_MODE_PRECISION (int_mode) > isize)
+               return simplify_gen_unary (ZERO_EXTEND, int_mode, temp, inner);
+             return simplify_gen_unary (TRUNCATE, int_mode, temp, inner);
+           }
+       }
+
+      if (vec_series_p (op, &base, &step))
+       {
+         /* Only create a new series if we can simplify both parts.  In other
+            cases this isn't really a simplification, and it's not necessarily
+            a win to replace a vector operation with a scalar operation.  */
+         scalar_mode inner_mode = GET_MODE_INNER (mode);
+         base = simplify_unary_operation (NEG, inner_mode, base, inner_mode);
+         if (base)
+           {
+             step = simplify_unary_operation (NEG, inner_mode,
+                                              step, inner_mode);
+             if (step)
+               return gen_vec_series (mode, base, step);
            }
        }
       break;
@@ -1191,10 +1280,9 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       if ((GET_CODE (op) == FLOAT_TRUNCATE
           && flag_unsafe_math_optimizations)
          || GET_CODE (op) == FLOAT_EXTEND)
-       return simplify_gen_unary (GET_MODE_SIZE (GET_MODE (XEXP (op,
-                                                           0)))
-                                  > GET_MODE_SIZE (mode)
-                                  ? FLOAT_TRUNCATE : FLOAT_EXTEND,
+       return simplify_gen_unary (GET_MODE_UNIT_SIZE (GET_MODE (XEXP (op, 0)))
+                                  > GET_MODE_UNIT_SIZE (mode)
+                                  ? FLOAT_TRUNCATE : FLOAT_EXTEND,
                                   mode,
                                   XEXP (op, 0), mode);
 
@@ -1259,8 +1347,10 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
        return op;
 
       /* If operand is known to be only -1 or 0, convert ABS to NEG.  */
-      if (num_sign_bit_copies (op, mode) == GET_MODE_PRECISION (mode))
-       return gen_rtx_NEG (mode, op);
+      if (is_a <scalar_int_mode> (mode, &int_mode)
+         && (num_sign_bit_copies (op, int_mode)
+             == GET_MODE_PRECISION (int_mode)))
+       return gen_rtx_NEG (int_mode, op);
 
       break;
 
@@ -1364,19 +1454,21 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
 
              if (lcode == ASHIFTRT)
                /* Number of bits not shifted off the end.  */
-               bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+               bits = (GET_MODE_UNIT_PRECISION (lmode)
+                       - INTVAL (XEXP (lhs, 1)));
              else /* lcode == SIGN_EXTEND */
                /* Size of inner mode.  */
-               bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+               bits = GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (lhs, 0)));
 
              if (rcode == ASHIFTRT)
-               bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+               bits += (GET_MODE_UNIT_PRECISION (rmode)
+                        - INTVAL (XEXP (rhs, 1)));
              else /* rcode == SIGN_EXTEND */
-               bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+               bits += GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (rhs, 0)));
 
              /* We can only widen multiplies if the result is mathematiclly
                 equivalent.  I.e. if overflow was impossible.  */
-             if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+             if (bits <= GET_MODE_UNIT_PRECISION (GET_MODE (op)))
                return simplify_gen_binary
                         (MULT, mode,
                          simplify_gen_unary (SIGN_EXTEND, mode, lhs, lmode),
@@ -1390,7 +1482,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       if (GET_CODE (op) == SUBREG
          && SUBREG_PROMOTED_VAR_P (op)
          && SUBREG_PROMOTED_SIGNED_P (op)
-         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
+         && !paradoxical_subreg_p (mode, GET_MODE (SUBREG_REG (op))))
        {
          temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
          if (temp)
@@ -1401,8 +1493,8 @@ simplify_unary_operation_1 (enum rtx_code code, 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_PRECISION (mode)
-                     > GET_MODE_PRECISION (GET_MODE (op)));
+         gcc_assert (GET_MODE_UNIT_PRECISION (mode)
+                     > GET_MODE_UNIT_PRECISION (GET_MODE (op)));
          return simplify_gen_unary (GET_CODE (op), mode, XEXP (op, 0),
                                     GET_MODE (XEXP (op, 0)));
        }
@@ -1414,26 +1506,34 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         is similarly (zero_extend:M (subreg:O <X>)).  */
       if ((GET_CODE (op) == ASHIFTRT || GET_CODE (op) == LSHIFTRT)
          && GET_CODE (XEXP (op, 0)) == ASHIFT
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && CONST_INT_P (XEXP (op, 1))
          && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
-         && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
-       {
-         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)
-                     > GET_MODE_BITSIZE (GET_MODE (op)));
-         if (tmode != BLKmode)
+         && (op_mode = as_a <scalar_int_mode> (GET_MODE (op)),
+             GET_MODE_BITSIZE (op_mode) > INTVAL (XEXP (op, 1))))
+       {
+         scalar_int_mode tmode;
+         gcc_assert (GET_MODE_BITSIZE (int_mode)
+                     > GET_MODE_BITSIZE (op_mode));
+         if (int_mode_for_size (GET_MODE_BITSIZE (op_mode)
+                                - INTVAL (XEXP (op, 1)), 1).exists (&tmode))
            {
              rtx inner =
                rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
              if (inner)
                return simplify_gen_unary (GET_CODE (op) == ASHIFTRT
                                           ? SIGN_EXTEND : ZERO_EXTEND,
-                                          mode, inner, tmode);
+                                          int_mode, inner, tmode);
            }
        }
 
+      /* (sign_extend:M (lshiftrt:N <X> (const_int I))) is better as
+         (zero_extend:M (lshiftrt:N <X> (const_int I))) if I is not 0.  */
+      if (GET_CODE (op) == LSHIFTRT
+         && CONST_INT_P (XEXP (op, 1))
+         && XEXP (op, 1) != const0_rtx)
+       return simplify_gen_unary (ZERO_EXTEND, mode, op, GET_MODE (op));
+
 #if defined(POINTERS_EXTEND_UNSIGNED)
       /* As we do not know which address space the pointer is referring to,
         we can do this only if the target does not support different pointer
@@ -1447,7 +1547,14 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
                  && REG_POINTER (SUBREG_REG (op))
                  && GET_MODE (SUBREG_REG (op)) == Pmode))
          && !targetm.have_ptr_extend ())
-       return convert_memory_address (Pmode, op);
+       {
+         temp
+           = convert_memory_address_addr_space_1 (Pmode, op,
+                                                  ADDR_SPACE_GENERIC, false,
+                                                  true);
+         if (temp)
+           return temp;
+       }
 #endif
       break;
 
@@ -1458,7 +1565,7 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       if (GET_CODE (op) == SUBREG
          && SUBREG_PROMOTED_VAR_P (op)
          && SUBREG_PROMOTED_UNSIGNED_P (op)
-         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
+         && !paradoxical_subreg_p (mode, GET_MODE (SUBREG_REG (op))))
        {
          temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
          if (temp)
@@ -1487,19 +1594,21 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
 
              if (lcode == LSHIFTRT)
                /* Number of bits not shifted off the end.  */
-               bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+               bits = (GET_MODE_UNIT_PRECISION (lmode)
+                       - INTVAL (XEXP (lhs, 1)));
              else /* lcode == ZERO_EXTEND */
                /* Size of inner mode.  */
-               bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+               bits = GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (lhs, 0)));
 
              if (rcode == LSHIFTRT)
-               bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+               bits += (GET_MODE_UNIT_PRECISION (rmode)
+                        - INTVAL (XEXP (rhs, 1)));
              else /* rcode == ZERO_EXTEND */
-               bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+               bits += GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (rhs, 0)));
 
              /* We can only widen multiplies if the result is mathematiclly
                 equivalent.  I.e. if overflow was impossible.  */
-             if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+             if (bits <= GET_MODE_UNIT_PRECISION (GET_MODE (op)))
                return simplify_gen_binary
                         (MULT, mode,
                          simplify_gen_unary (ZERO_EXTEND, mode, lhs, lmode),
@@ -1517,19 +1626,21 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         GET_MODE_PRECISION (N) - I bits.  */
       if (GET_CODE (op) == LSHIFTRT
          && GET_CODE (XEXP (op, 0)) == ASHIFT
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && CONST_INT_P (XEXP (op, 1))
          && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
-         && GET_MODE_PRECISION (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
+         && (op_mode = as_a <scalar_int_mode> (GET_MODE (op)),
+             GET_MODE_PRECISION (op_mode) > INTVAL (XEXP (op, 1))))
        {
-         machine_mode tmode
-           = mode_for_size (GET_MODE_PRECISION (GET_MODE (op))
-                            - INTVAL (XEXP (op, 1)), MODE_INT, 1);
-         if (tmode != BLKmode)
+         scalar_int_mode tmode;
+         if (int_mode_for_size (GET_MODE_PRECISION (op_mode)
+                                - INTVAL (XEXP (op, 1)), 1).exists (&tmode))
            {
              rtx inner =
                rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
              if (inner)
-               return simplify_gen_unary (ZERO_EXTEND, mode, inner, tmode);
+               return simplify_gen_unary (ZERO_EXTEND, int_mode,
+                                          inner, tmode);
            }
        }
 
@@ -1538,22 +1649,19 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
         of mode N.  E.g.
         (zero_extend:SI (subreg:QI (and:SI (reg:SI) (const_int 63)) 0)) is
         (and:SI (reg:SI) (const_int 63)).  */
-      if (GET_CODE (op) == SUBREG
-         && GET_MODE_PRECISION (GET_MODE (op))
-            < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
-         && GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
-            <= HOST_BITS_PER_WIDE_INT
-         && GET_MODE_PRECISION (mode)
-            >= GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
+      if (partial_subreg_p (op)
+         && is_a <scalar_int_mode> (mode, &int_mode)
+         && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op)), &op0_mode)
+         && GET_MODE_PRECISION (op0_mode) <= HOST_BITS_PER_WIDE_INT
+         && GET_MODE_PRECISION (int_mode) >= GET_MODE_PRECISION (op0_mode)
          && subreg_lowpart_p (op)
-         && (nonzero_bits (SUBREG_REG (op), GET_MODE (SUBREG_REG (op)))
+         && (nonzero_bits (SUBREG_REG (op), op0_mode)
              & ~GET_MODE_MASK (GET_MODE (op))) == 0)
        {
-         if (GET_MODE_PRECISION (mode)
-             == GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op))))
+         if (GET_MODE_PRECISION (int_mode) == GET_MODE_PRECISION (op0_mode))
            return SUBREG_REG (op);
-         return simplify_gen_unary (ZERO_EXTEND, mode, SUBREG_REG (op),
-                                    GET_MODE (SUBREG_REG (op)));
+         return simplify_gen_unary (ZERO_EXTEND, int_mode, SUBREG_REG (op),
+                                    op0_mode);
        }
 
 #if defined(POINTERS_EXTEND_UNSIGNED)
@@ -1569,7 +1677,14 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
                  && REG_POINTER (SUBREG_REG (op))
                  && GET_MODE (SUBREG_REG (op)) == Pmode))
          && !targetm.have_ptr_extend ())
-       return convert_memory_address (Pmode, op);
+       {
+         temp
+           = convert_memory_address_addr_space_1 (Pmode, op,
+                                                  ADDR_SPACE_GENERIC, false,
+                                                  true);
+         if (temp)
+           return temp;
+       }
 #endif
       break;
 
@@ -1577,6 +1692,28 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       break;
     }
 
+  if (VECTOR_MODE_P (mode) && vec_duplicate_p (op, &elt))
+    {
+      /* Try applying the operator to ELT and see if that simplifies.
+        We can duplicate the result if so.
+
+        The reason we don't use simplify_gen_unary is that it isn't
+        necessarily a win to convert things like:
+
+          (neg:V (vec_duplicate:V (reg:S R)))
+
+        to:
+
+          (vec_duplicate:V (neg:S (reg:S R)))
+
+        The first might be done entirely in vector registers while the
+        second might need a move between register files.  */
+      temp = simplify_unary_operation (code, GET_MODE_INNER (mode),
+                                      elt, GET_MODE_INNER (GET_MODE (op)));
+      if (temp)
+       return gen_vec_duplicate (mode, temp);
+    }
+
   return 0;
 }
 
@@ -1587,7 +1724,7 @@ rtx
 simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
                                rtx op, machine_mode op_mode)
 {
-  unsigned int width = GET_MODE_PRECISION (mode);
+  scalar_int_mode result_mode;
 
   if (code == VEC_DUPLICATE)
     {
@@ -1600,28 +1737,17 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          gcc_assert (GET_MODE_INNER (mode) == GET_MODE_INNER
                                                (GET_MODE (op)));
       }
-      if (CONST_SCALAR_INT_P (op) || CONST_DOUBLE_AS_FLOAT_P (op)
-         || GET_CODE (op) == CONST_VECTOR)
+      if (CONST_SCALAR_INT_P (op) || CONST_DOUBLE_AS_FLOAT_P (op))
+       return gen_const_vec_duplicate (mode, op);
+      if (GET_CODE (op) == CONST_VECTOR)
        {
-         int elt_size = GET_MODE_UNIT_SIZE (mode);
-          unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+         unsigned int n_elts = GET_MODE_NUNITS (mode);
+         unsigned int in_n_elts = CONST_VECTOR_NUNITS (op);
+         gcc_assert (in_n_elts < n_elts);
+         gcc_assert ((n_elts % in_n_elts) == 0);
          rtvec v = rtvec_alloc (n_elts);
-         unsigned int i;
-
-         if (GET_CODE (op) != CONST_VECTOR)
-           for (i = 0; i < n_elts; i++)
-             RTVEC_ELT (v, i) = op;
-         else
-           {
-             machine_mode inmode = GET_MODE (op);
-             int in_elt_size = GET_MODE_UNIT_SIZE (inmode);
-              unsigned in_n_elts = (GET_MODE_SIZE (inmode) / in_elt_size);
-
-             gcc_assert (in_n_elts < n_elts);
-             gcc_assert ((n_elts % in_n_elts) == 0);
-             for (i = 0; i < n_elts; i++)
-               RTVEC_ELT (v, i) = CONST_VECTOR_ELT (op, i % in_n_elts);
-           }
+         for (unsigned i = 0; i < n_elts; i++)
+           RTVEC_ELT (v, i) = CONST_VECTOR_ELT (op, i % in_n_elts);
          return gen_rtx_CONST_VECTOR (mode, v);
        }
     }
@@ -1667,7 +1793,13 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          op_mode = MAX_MODE_INT;
        }
 
-      real_from_integer (&d, mode, std::make_pair (op, op_mode), SIGNED);
+      real_from_integer (&d, mode, rtx_mode_t (op, op_mode), SIGNED);
+
+      /* Avoid the folding if flag_signaling_nans is on and
+         operand is a signaling NaN.  */
+      if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+        return 0;
+
       d = real_value_truncate (mode, d);
       return const_double_from_real_value (d, mode);
     }
@@ -1685,16 +1817,25 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          op_mode = MAX_MODE_INT;
        }
 
-      real_from_integer (&d, mode, std::make_pair (op, op_mode), UNSIGNED);
+      real_from_integer (&d, mode, rtx_mode_t (op, op_mode), UNSIGNED);
+
+      /* Avoid the folding if flag_signaling_nans is on and
+         operand is a signaling NaN.  */
+      if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+        return 0;
+
       d = real_value_truncate (mode, d);
       return const_double_from_real_value (d, mode);
     }
 
-  if (CONST_SCALAR_INT_P (op) && width > 0)
+  if (CONST_SCALAR_INT_P (op) && is_a <scalar_int_mode> (mode, &result_mode))
     {
+      unsigned int width = GET_MODE_PRECISION (result_mode);
       wide_int result;
-      machine_mode imode = op_mode == VOIDmode ? mode : op_mode;
-      rtx_mode_t op0 = std::make_pair (op, imode);
+      scalar_int_mode imode = (op_mode == VOIDmode
+                              ? result_mode
+                              : as_a <scalar_int_mode> (op_mode));
+      rtx_mode_t op0 = rtx_mode_t (op, imode);
       int int_value;
 
 #if TARGET_SUPPORTS_WIDE_INT == 0
@@ -1722,35 +1863,35 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          break;
 
        case FFS:
-         result = wi::shwi (wi::ffs (op0), mode);
+         result = wi::shwi (wi::ffs (op0), result_mode);
          break;
 
        case CLZ:
          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);
+         else if (! CLZ_DEFINED_VALUE_AT_ZERO (imode, int_value))
+           int_value = GET_MODE_PRECISION (imode);
+         result = wi::shwi (int_value, result_mode);
          break;
 
        case CLRSB:
-         result = wi::shwi (wi::clrsb (op0), mode);
+         result = wi::shwi (wi::clrsb (op0), result_mode);
          break;
 
        case CTZ:
          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);
+         else if (! CTZ_DEFINED_VALUE_AT_ZERO (imode, int_value))
+           int_value = GET_MODE_PRECISION (imode);
+         result = wi::shwi (int_value, result_mode);
          break;
 
        case POPCOUNT:
-         result = wi::shwi (wi::popcount (op0), mode);
+         result = wi::shwi (wi::popcount (op0), result_mode);
          break;
 
        case PARITY:
-         result = wi::shwi (wi::parity (op0), mode);
+         result = wi::shwi (wi::parity (op0), result_mode);
          break;
 
        case BSWAP:
@@ -1771,7 +1912,7 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          return 0;
        }
 
-      return immed_wide_int_const (result, mode);
+      return immed_wide_int_const (result, result_mode);
     }
 
   else if (CONST_DOUBLE_AS_FLOAT_P (op) 
@@ -1790,15 +1931,27 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
          d = real_value_negate (&d);
          break;
        case FLOAT_TRUNCATE:
+         /* Don't perform the operation if flag_signaling_nans is on
+            and the operand is a signaling NaN.  */
+         if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+           return NULL_RTX;
          d = real_value_truncate (mode, d);
          break;
        case FLOAT_EXTEND:
+         /* Don't perform the operation if flag_signaling_nans is on
+            and the operand is a signaling NaN.  */
+         if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+           return NULL_RTX;
          /* All this does is change the mode, unless changing
             mode class.  */
          if (GET_MODE_CLASS (mode) != GET_MODE_CLASS (GET_MODE (op)))
            real_convert (&d, mode, &d);
          break;
        case FIX:
+         /* Don't perform the operation if flag_signaling_nans is on
+            and the operand is a signaling NaN.  */
+         if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+           return NULL_RTX;
          real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL);
          break;
        case NOT:
@@ -1819,9 +1972,9 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
     }
   else if (CONST_DOUBLE_AS_FLOAT_P (op)
           && SCALAR_FLOAT_MODE_P (GET_MODE (op))
-          && GET_MODE_CLASS (mode) == MODE_INT
-          && width > 0)
+          && is_int_mode (mode, &result_mode))
     {
+      unsigned int width = GET_MODE_PRECISION (result_mode);
       /* Although the overflow semantics of RTL's FIX and UNSIGNED_FIX
         operators are intentionally left unspecified (to ease implementation
         by target backends), for consistency, this routine implements the
@@ -1875,6 +2028,26 @@ simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
        }
     }
 
+  /* Handle polynomial integers.  */
+  else if (CONST_POLY_INT_P (op))
+    {
+      poly_wide_int result;
+      switch (code)
+       {
+       case NEG:
+         result = -const_poly_int_value (op);
+         break;
+
+       case NOT:
+         result = ~const_poly_int_value (op);
+         break;
+
+       default:
+         return NULL_RTX;
+       }
+      return immed_wide_int_const (result, mode);
+    }
+
   return NULL_RTX;
 }
 \f
@@ -2003,6 +2176,46 @@ simplify_binary_operation (enum rtx_code code, machine_mode mode,
   return NULL_RTX;
 }
 
+/* Subroutine of simplify_binary_operation_1 that looks for cases in
+   which OP0 and OP1 are both vector series or vector duplicates
+   (which are really just series with a step of 0).  If so, try to
+   form a new series by applying CODE to the bases and to the steps.
+   Return null if no simplification is possible.
+
+   MODE is the mode of the operation and is known to be a vector
+   integer mode.  */
+
+static rtx
+simplify_binary_operation_series (rtx_code code, machine_mode mode,
+                                 rtx op0, rtx op1)
+{
+  rtx base0, step0;
+  if (vec_duplicate_p (op0, &base0))
+    step0 = const0_rtx;
+  else if (!vec_series_p (op0, &base0, &step0))
+    return NULL_RTX;
+
+  rtx base1, step1;
+  if (vec_duplicate_p (op1, &base1))
+    step1 = const0_rtx;
+  else if (!vec_series_p (op1, &base1, &step1))
+    return NULL_RTX;
+
+  /* Only create a new series if we can simplify both parts.  In other
+     cases this isn't really a simplification, and it's not necessarily
+     a win to replace a vector operation with a scalar operation.  */
+  scalar_mode inner_mode = GET_MODE_INNER (mode);
+  rtx new_base = simplify_binary_operation (code, inner_mode, base0, base1);
+  if (!new_base)
+    return NULL_RTX;
+
+  rtx new_step = simplify_binary_operation (code, inner_mode, step0, step1);
+  if (!new_step)
+    return NULL_RTX;
+
+  return gen_vec_series (mode, new_base, new_step);
+}
+
 /* Subroutine of simplify_binary_operation.  Simplify a binary operation
    CODE with result mode MODE, operating on OP0 and OP1.  If OP0 and/or
    OP1 are constant pool references, TRUEOP0 and TRUEOP1 represent the
@@ -2012,9 +2225,10 @@ static rtx
 simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                             rtx op0, rtx op1, rtx trueop0, rtx trueop1)
 {
-  rtx tem, reversed, opleft, opright;
+  rtx tem, reversed, opleft, opright, elt0, elt1;
   HOST_WIDE_INT val;
-  unsigned int width = GET_MODE_PRECISION (mode);
+  scalar_int_mode int_mode, inner_mode;
+  poly_int64 offset;
 
   /* Even if we can't compute a constant result,
      there are some cases worth simplifying.  */
@@ -2065,66 +2279,66 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
         have X (if C is 2 in the example above).  But don't make
         something more expensive than we had before.  */
 
-      if (SCALAR_INT_MODE_P (mode))
+      if (is_a <scalar_int_mode> (mode, &int_mode))
        {
          rtx lhs = op0, rhs = op1;
 
-         wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
-         wide_int coeff1 = wi::one (GET_MODE_PRECISION (mode));
+         wide_int coeff0 = wi::one (GET_MODE_PRECISION (int_mode));
+         wide_int coeff1 = wi::one (GET_MODE_PRECISION (int_mode));
 
          if (GET_CODE (lhs) == NEG)
            {
-             coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
+             coeff0 = wi::minus_one (GET_MODE_PRECISION (int_mode));
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == MULT
                   && CONST_SCALAR_INT_P (XEXP (lhs, 1)))
            {
-             coeff0 = std::make_pair (XEXP (lhs, 1), mode);
+             coeff0 = rtx_mode_t (XEXP (lhs, 1), int_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)) < GET_MODE_PRECISION (mode))
+                  && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (int_mode))
            {
              coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
-                                           GET_MODE_PRECISION (mode));
+                                           GET_MODE_PRECISION (int_mode));
              lhs = XEXP (lhs, 0);
            }
 
          if (GET_CODE (rhs) == NEG)
            {
-             coeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
+             coeff1 = wi::minus_one (GET_MODE_PRECISION (int_mode));
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == MULT
                   && CONST_INT_P (XEXP (rhs, 1)))
            {
-             coeff1 = std::make_pair (XEXP (rhs, 1), mode);
+             coeff1 = rtx_mode_t (XEXP (rhs, 1), int_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)) < GET_MODE_PRECISION (mode))
+                  && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (int_mode))
            {
              coeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
-                                           GET_MODE_PRECISION (mode));
+                                           GET_MODE_PRECISION (int_mode));
              rhs = XEXP (rhs, 0);
            }
 
          if (rtx_equal_p (lhs, rhs))
            {
-             rtx orig = gen_rtx_PLUS (mode, op0, op1);
+             rtx orig = gen_rtx_PLUS (int_mode, op0, op1);
              rtx coeff;
              bool speed = optimize_function_for_speed_p (cfun);
 
-             coeff = immed_wide_int_const (coeff0 + coeff1, mode);
+             coeff = immed_wide_int_const (coeff0 + coeff1, int_mode);
 
-             tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return (set_src_cost (tem, mode, speed)
-                     <= set_src_cost (orig, mode, speed) ? tem : 0);
+             tem = simplify_gen_binary (MULT, int_mode, lhs, coeff);
+             return (set_src_cost (tem, int_mode, speed)
+                     <= set_src_cost (orig, int_mode, speed) ? tem : 0);
            }
        }
 
@@ -2182,6 +2396,14 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
          if (tem)
            return tem;
        }
+
+      /* Handle vector series.  */
+      if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+       {
+         tem = simplify_binary_operation_series (code, mode, op0, op1);
+         if (tem)
+           return tem;
+       }
       break;
 
     case COMPARE:
@@ -2197,10 +2419,10 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
              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)
+               && GET_MODE (xop00) == mode
+               && GET_MODE (xop10) == mode
+               && GET_MODE_CLASS (mode) == MODE_CC)
              return xop00;
        }
       break;
@@ -2221,8 +2443,11 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode))
        return simplify_gen_unary (NEG, mode, op1, mode);
 
-      /* (-1 - a) is ~a.  */
-      if (trueop0 == constm1_rtx)
+      /* (-1 - a) is ~a, unless the expression contains symbolic
+        constants, in which case not retaining additions and
+        subtractions could cause invalid assembly to be produced.  */
+      if (trueop0 == constm1_rtx
+         && !contains_symbolic_reference_p (op1))
        return simplify_gen_unary (NOT, mode, op1, mode);
 
       /* Subtracting 0 has no effect unless the mode has signed zeros
@@ -2239,67 +2464,67 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
         have X (if C is 2 in the example above).  But don't make
         something more expensive than we had before.  */
 
-      if (SCALAR_INT_MODE_P (mode))
+      if (is_a <scalar_int_mode> (mode, &int_mode))
        {
          rtx lhs = op0, rhs = op1;
 
-         wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
-         wide_int negcoeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
+         wide_int coeff0 = wi::one (GET_MODE_PRECISION (int_mode));
+         wide_int negcoeff1 = wi::minus_one (GET_MODE_PRECISION (int_mode));
 
          if (GET_CODE (lhs) == NEG)
            {
-             coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
+             coeff0 = wi::minus_one (GET_MODE_PRECISION (int_mode));
              lhs = XEXP (lhs, 0);
            }
          else if (GET_CODE (lhs) == MULT
                   && CONST_SCALAR_INT_P (XEXP (lhs, 1)))
            {
-             coeff0 = std::make_pair (XEXP (lhs, 1), mode);
+             coeff0 = rtx_mode_t (XEXP (lhs, 1), int_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)) < GET_MODE_PRECISION (mode))
+                  && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (int_mode))
            {
              coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
-                                           GET_MODE_PRECISION (mode));
+                                           GET_MODE_PRECISION (int_mode));
              lhs = XEXP (lhs, 0);
            }
 
          if (GET_CODE (rhs) == NEG)
            {
-             negcoeff1 = wi::one (GET_MODE_PRECISION (mode));
+             negcoeff1 = wi::one (GET_MODE_PRECISION (int_mode));
              rhs = XEXP (rhs, 0);
            }
          else if (GET_CODE (rhs) == MULT
                   && CONST_INT_P (XEXP (rhs, 1)))
            {
-             negcoeff1 = wi::neg (std::make_pair (XEXP (rhs, 1), mode));
+             negcoeff1 = wi::neg (rtx_mode_t (XEXP (rhs, 1), int_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)) < GET_MODE_PRECISION (mode))
+                  && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (int_mode))
            {
              negcoeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
-                                              GET_MODE_PRECISION (mode));
+                                              GET_MODE_PRECISION (int_mode));
              negcoeff1 = -negcoeff1;
              rhs = XEXP (rhs, 0);
            }
 
          if (rtx_equal_p (lhs, rhs))
            {
-             rtx orig = gen_rtx_MINUS (mode, op0, op1);
+             rtx orig = gen_rtx_MINUS (int_mode, op0, op1);
              rtx coeff;
              bool speed = optimize_function_for_speed_p (cfun);
 
-             coeff = immed_wide_int_const (coeff0 + negcoeff1, mode);
+             coeff = immed_wide_int_const (coeff0 + negcoeff1, int_mode);
 
-             tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return (set_src_cost (tem, mode, speed)
-                     <= set_src_cost (orig, mode, speed) ? tem : 0);
+             tem = simplify_gen_binary (MULT, int_mode, lhs, coeff);
+             return (set_src_cost (tem, int_mode, speed)
+                     <= set_src_cost (orig, int_mode, speed) ? tem : 0);
            }
        }
 
@@ -2316,6 +2541,12 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
            return simplify_gen_binary (MINUS, mode, tem, XEXP (op0, 0));
        }
 
+      if ((GET_CODE (op0) == CONST
+          || GET_CODE (op0) == SYMBOL_REF
+          || GET_CODE (op0) == LABEL_REF)
+         && poly_int_rtx_p (op1, &offset))
+       return plus_constant (mode, op0, trunc_int_for_mode (-offset, mode));
+
       /* Don't let a relocatable value get a negative coeff.  */
       if (CONST_INT_P (op1) && GET_MODE (op0) != VOIDmode)
        return simplify_gen_binary (PLUS, mode,
@@ -2390,6 +2621,14 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
              || plus_minus_operand_p (op1))
          && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
        return tem;
+
+      /* Handle vector series.  */
+      if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+       {
+         tem = simplify_binary_operation_series (code, mode, op0, op1);
+         if (tem)
+           return tem;
+       }
       break;
 
     case MULT:
@@ -2450,9 +2689,10 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       /* Convert multiply by constant power of two into shift.  */
       if (CONST_SCALAR_INT_P (trueop1))
        {
-         val = wi::exact_log2 (std::make_pair (trueop1, mode));
+         val = wi::exact_log2 (rtx_mode_t (trueop1, mode));
          if (val >= 0)
-           return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
+           return simplify_gen_binary (ASHIFT, mode, op0,
+                                       gen_int_shift_amount (mode, val));
        }
 
       /* x*2 is x+x and x*(-1) is -x */
@@ -2530,7 +2770,7 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
          HOST_WIDE_INT c1 = INTVAL (XEXP (op0, 1));
          HOST_WIDE_INT c2 = INTVAL (trueop1);
 
-         /* If (C1&C2) == C1, then (X&C1)|C2 becomes X.  */
+         /* If (C1&C2) == C1, then (X&C1)|C2 becomes C2.  */
          if ((c1 & c2) == c1
              && !side_effects_p (XEXP (op0, 0)))
            return trueop1;
@@ -2538,14 +2778,6 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
          /* If (C1|C2) == ~0 then (X&C1)|C2 becomes X|C2.  */
          if (((c1|c2) & mask) == mask)
            return simplify_gen_binary (IOR, mode, XEXP (op0, 0), op1);
-
-         /* Minimize the number of bits set in C1, i.e. C1 := C1 & ~C2.  */
-         if (((c1 & ~c2) & mask) != (c1 & mask))
-           {
-             tem = simplify_gen_binary (AND, mode, XEXP (op0, 0),
-                                        gen_int_mode (c1 & ~c2, mode));
-             return simplify_gen_binary (IOR, mode, tem, op1);
-           }
        }
 
       /* Convert (A & B) | A to A.  */
@@ -2576,45 +2808,30 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
           && CONST_INT_P (XEXP (opleft, 1))
           && CONST_INT_P (XEXP (opright, 1))
           && (INTVAL (XEXP (opleft, 1)) + INTVAL (XEXP (opright, 1))
-              == GET_MODE_PRECISION (mode)))
+             == GET_MODE_UNIT_PRECISION (mode)))
         return gen_rtx_ROTATE (mode, XEXP (opright, 0), XEXP (opleft, 1));
 
       /* Same, but for ashift that has been "simplified" to a wider mode
         by simplify_shift_const.  */
 
       if (GET_CODE (opleft) == SUBREG
+         && is_a <scalar_int_mode> (mode, &int_mode)
+         && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (opleft)),
+                                    &inner_mode)
           && GET_CODE (SUBREG_REG (opleft)) == ASHIFT
           && GET_CODE (opright) == LSHIFTRT
           && GET_CODE (XEXP (opright, 0)) == SUBREG
-          && GET_MODE (opleft) == GET_MODE (XEXP (opright, 0))
-          && SUBREG_BYTE (opleft) == SUBREG_BYTE (XEXP (opright, 0))
-          && (GET_MODE_SIZE (GET_MODE (opleft))
-              < GET_MODE_SIZE (GET_MODE (SUBREG_REG (opleft))))
+         && known_eq (SUBREG_BYTE (opleft), SUBREG_BYTE (XEXP (opright, 0)))
+         && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode)
           && rtx_equal_p (XEXP (SUBREG_REG (opleft), 0),
                           SUBREG_REG (XEXP (opright, 0)))
           && CONST_INT_P (XEXP (SUBREG_REG (opleft), 1))
           && CONST_INT_P (XEXP (opright, 1))
-          && (INTVAL (XEXP (SUBREG_REG (opleft), 1)) + INTVAL (XEXP (opright, 1))
-              == GET_MODE_PRECISION (mode)))
-        return gen_rtx_ROTATE (mode, XEXP (opright, 0),
-                               XEXP (SUBREG_REG (opleft), 1));
-
-      /* If we have (ior (and (X C1) C2)), simplify this by making
-        C1 as small as possible if C1 actually changes.  */
-      if (CONST_INT_P (op1)
-         && (HWI_COMPUTABLE_MODE_P (mode)
-             || INTVAL (op1) > 0)
-         && GET_CODE (op0) == AND
-         && CONST_INT_P (XEXP (op0, 1))
-         && CONST_INT_P (op1)
-         && (UINTVAL (XEXP (op0, 1)) & UINTVAL (op1)) != 0)
-       {
-         rtx tmp = simplify_gen_binary (AND, mode, XEXP (op0, 0),
-                                        gen_int_mode (UINTVAL (XEXP (op0, 1))
-                                                      & ~UINTVAL (op1),
-                                                      mode));
-         return simplify_gen_binary (IOR, mode, tmp, op1);
-       }
+         && (INTVAL (XEXP (SUBREG_REG (opleft), 1))
+             + INTVAL (XEXP (opright, 1))
+             == GET_MODE_PRECISION (int_mode)))
+       return gen_rtx_ROTATE (int_mode, XEXP (opright, 0),
+                              XEXP (SUBREG_REG (opleft), 1));
 
       /* If OP0 is (ashiftrt (plus ...) C), it might actually be
          a (sign_extend (plus ...)).  Then check if OP1 is a CONST_INT and
@@ -2627,8 +2844,8 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
           && CONST_INT_P (XEXP (op0, 1))
           && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT)
         {
-          int count = INTVAL (XEXP (op0, 1));
-          HOST_WIDE_INT mask = INTVAL (trueop1) << count;
+         int count = INTVAL (XEXP (op0, 1));
+         HOST_WIDE_INT mask = UINTVAL (trueop1) << count;
 
           if (mask >> count == INTVAL (trueop1)
              && trunc_int_for_mode (mask, mode) == mask
@@ -2813,6 +3030,37 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
            }
        }
 
+      /* If we have (xor (and (xor A B) C) A) with C a constant we can instead
+        do (ior (and A ~C) (and B C)) which is a machine instruction on some
+        machines, and also has shorter instruction path length.  */
+      if (GET_CODE (op0) == AND
+         && GET_CODE (XEXP (op0, 0)) == XOR
+         && CONST_INT_P (XEXP (op0, 1))
+         && rtx_equal_p (XEXP (XEXP (op0, 0), 0), trueop1))
+       {
+         rtx a = trueop1;
+         rtx b = XEXP (XEXP (op0, 0), 1);
+         rtx c = XEXP (op0, 1);
+         rtx nc = simplify_gen_unary (NOT, mode, c, mode);
+         rtx a_nc = simplify_gen_binary (AND, mode, a, nc);
+         rtx bc = simplify_gen_binary (AND, mode, b, c);
+         return simplify_gen_binary (IOR, mode, a_nc, bc);
+       }
+      /* Similarly, (xor (and (xor A B) C) B) as (ior (and A C) (and B ~C))  */
+      else if (GET_CODE (op0) == AND
+         && GET_CODE (XEXP (op0, 0)) == XOR
+         && CONST_INT_P (XEXP (op0, 1))
+         && rtx_equal_p (XEXP (XEXP (op0, 0), 1), trueop1))
+       {
+         rtx a = XEXP (XEXP (op0, 0), 0);
+         rtx b = trueop1;
+         rtx c = XEXP (op0, 1);
+         rtx nc = simplify_gen_unary (NOT, mode, c, mode);
+         rtx b_nc = simplify_gen_binary (AND, mode, b, nc);
+         rtx ac = simplify_gen_binary (AND, mode, a, c);
+         return simplify_gen_binary (IOR, mode, ac, b_nc);
+       }
+
       /* (xor (comparison foo bar) (const_int 1)) can become the reversed
         comparison if STORE_FLAG_VALUE is 1.  */
       if (STORE_FLAG_VALUE == 1
@@ -2825,19 +3073,21 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
         is (lt foo (const_int 0)), so we can perform the above
         simplification if STORE_FLAG_VALUE is 1.  */
 
-      if (STORE_FLAG_VALUE == 1
+      if (is_a <scalar_int_mode> (mode, &int_mode)
+         && STORE_FLAG_VALUE == 1
          && trueop1 == const1_rtx
          && GET_CODE (op0) == LSHIFTRT
          && CONST_INT_P (XEXP (op0, 1))
-         && INTVAL (XEXP (op0, 1)) == GET_MODE_PRECISION (mode) - 1)
-       return gen_rtx_GE (mode, XEXP (op0, 0), const0_rtx);
+         && INTVAL (XEXP (op0, 1)) == GET_MODE_PRECISION (int_mode) - 1)
+       return gen_rtx_GE (int_mode, XEXP (op0, 0), const0_rtx);
 
       /* (xor (comparison foo bar) (const_int sign-bit))
         when STORE_FLAG_VALUE is the sign bit.  */
-      if (val_signbit_p (mode, STORE_FLAG_VALUE)
+      if (is_a <scalar_int_mode> (mode, &int_mode)
+         && val_signbit_p (int_mode, STORE_FLAG_VALUE)
          && trueop1 == const_true_rtx
          && COMPARISON_P (op0)
-         && (reversed = reversed_comparison (op0, mode)))
+         && (reversed = reversed_comparison (op0, int_mode)))
        return reversed;
 
       tem = simplify_byte_swapping_operation (code, mode, op0, op1);
@@ -3049,7 +3299,8 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
 
     case UDIV:
       /* 0/x is 0 (or x&0 if x has side-effects).  */
-      if (trueop0 == CONST0_RTX (mode))
+      if (trueop0 == CONST0_RTX (mode)
+         && !cfun->can_throw_non_call_exceptions)
        {
          if (side_effects_p (op1))
            return simplify_gen_binary (AND, mode, op1, trueop0);
@@ -3065,7 +3316,8 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       /* Convert divide by power of two into shift.  */
       if (CONST_INT_P (trueop1)
          && (val = exact_log2 (UINTVAL (trueop1))) > 0)
-       return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (val));
+       return simplify_gen_binary (LSHIFTRT, mode, op0,
+                                   gen_int_shift_amount (mode, val));
       break;
 
     case DIV:
@@ -3183,11 +3435,14 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
 #if defined(HAVE_rotate) && defined(HAVE_rotatert)
       if (CONST_INT_P (trueop1)
          && IN_RANGE (INTVAL (trueop1),
-                      GET_MODE_PRECISION (mode) / 2 + (code == ROTATE),
-                      GET_MODE_PRECISION (mode) - 1))
-       return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
-                                   mode, op0, GEN_INT (GET_MODE_PRECISION (mode)
-                                                       - INTVAL (trueop1)));
+                      GET_MODE_UNIT_PRECISION (mode) / 2 + (code == ROTATE),
+                      GET_MODE_UNIT_PRECISION (mode) - 1))
+       {
+         int new_amount = GET_MODE_UNIT_PRECISION (mode) - INTVAL (trueop1);
+         rtx new_amount_rtx = gen_int_shift_amount (mode, new_amount);
+         return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
+                                     mode, op0, new_amount_rtx);
+       }
 #endif
       /* FALLTHRU */
     case ASHIFTRT:
@@ -3196,51 +3451,52 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       if (trueop0 == CONST0_RTX (mode) && ! side_effects_p (op1))
        return op0;
       /* Rotating ~0 always results in ~0.  */
-      if (CONST_INT_P (trueop0) && width <= HOST_BITS_PER_WIDE_INT
+      if (CONST_INT_P (trueop0)
+         && HWI_COMPUTABLE_MODE_P (mode)
          && UINTVAL (trueop0) == GET_MODE_MASK (mode)
          && ! side_effects_p (op1))
        return op0;
+
+    canonicalize_shift:
       /* 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>))
+        ([a|l]shiftrt: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>))
+        (subreg:M1 ([a|l]shiftrt:M2 (reg:M2) (const_int <c1 + c2>))
                    <low_part>).  */
-      if (code == ASHIFTRT
-         && !VECTOR_MODE_P (mode)
+      if ((code == ASHIFTRT || code == LSHIFTRT)
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && SUBREG_P (op0)
          && CONST_INT_P (op1)
          && GET_CODE (SUBREG_REG (op0)) == LSHIFTRT
-         && !VECTOR_MODE_P (GET_MODE (SUBREG_REG (op0)))
+         && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op0)),
+                                    &inner_mode)
          && CONST_INT_P (XEXP (SUBREG_REG (op0), 1))
-         && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
-             > GET_MODE_BITSIZE (mode))
+         && GET_MODE_BITSIZE (inner_mode) > GET_MODE_BITSIZE (int_mode)
          && (INTVAL (XEXP (SUBREG_REG (op0), 1))
-             == (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
-                 - GET_MODE_BITSIZE (mode)))
+             == GET_MODE_BITSIZE (inner_mode) - GET_MODE_BITSIZE (int_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)),
+         rtx tmp = gen_int_shift_amount
+           (inner_mode, INTVAL (XEXP (SUBREG_REG (op0), 1)) + INTVAL (op1));
+         tmp = simplify_gen_binary (code, inner_mode,
                                     XEXP (SUBREG_REG (op0), 0),
                                     tmp);
-         return lowpart_subreg (mode, tmp, inner_mode);
+         return lowpart_subreg (int_mode, tmp, inner_mode);
        }
-    canonicalize_shift:
+
       if (SHIFT_COUNT_TRUNCATED && CONST_INT_P (op1))
        {
-         val = INTVAL (op1) & (GET_MODE_PRECISION (mode) - 1);
+         val = INTVAL (op1) & (GET_MODE_UNIT_PRECISION (mode) - 1);
          if (val != INTVAL (op1))
-           return simplify_gen_binary (code, mode, op0, GEN_INT (val));
+           return simplify_gen_binary (code, mode, op0,
+                                       gen_int_shift_amount (mode, val));
        }
       break;
 
@@ -3260,23 +3516,23 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
        return op0;
       /* Optimize (lshiftrt (clz X) C) as (eq X 0).  */
       if (GET_CODE (op0) == CLZ
+         && is_a <scalar_int_mode> (GET_MODE (XEXP (op0, 0)), &inner_mode)
          && CONST_INT_P (trueop1)
          && STORE_FLAG_VALUE == 1
-         && INTVAL (trueop1) < (HOST_WIDE_INT)width)
+         && INTVAL (trueop1) < GET_MODE_UNIT_PRECISION (mode))
        {
-         machine_mode imode = GET_MODE (XEXP (op0, 0));
          unsigned HOST_WIDE_INT zero_val = 0;
 
-         if (CLZ_DEFINED_VALUE_AT_ZERO (imode, zero_val)
-             && zero_val == GET_MODE_PRECISION (imode)
+         if (CLZ_DEFINED_VALUE_AT_ZERO (inner_mode, zero_val)
+             && zero_val == GET_MODE_PRECISION (inner_mode)
              && INTVAL (trueop1) == exact_log2 (zero_val))
-           return simplify_gen_relational (EQ, mode, imode,
+           return simplify_gen_relational (EQ, mode, inner_mode,
                                            XEXP (op0, 0), const0_rtx);
        }
       goto canonicalize_shift;
 
     case SMIN:
-      if (width <= HOST_BITS_PER_WIDE_INT
+      if (HWI_COMPUTABLE_MODE_P (mode)
          && mode_signbit_p (mode, trueop1)
          && ! side_effects_p (op0))
        return op1;
@@ -3288,7 +3544,7 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       break;
 
     case SMAX:
-      if (width <= HOST_BITS_PER_WIDE_INT
+      if (HWI_COMPUTABLE_MODE_P (mode)
          && CONST_INT_P (trueop1)
          && (UINTVAL (trueop1) == GET_MODE_MASK (mode) >> 1)
          && ! side_effects_p (op0))
@@ -3331,6 +3587,13 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       /* ??? There are simplifications that can be done.  */
       return 0;
 
+    case VEC_SERIES:
+      if (op1 == CONST0_RTX (GET_MODE_INNER (mode)))
+       return gen_vec_duplicate (mode, op0);
+      if (CONSTANT_P (op0) && CONSTANT_P (op1))
+       return gen_const_vec_series (mode, op0, op1);
+      return 0;
+
     case VEC_SELECT:
       if (!VECTOR_MODE_P (mode))
        {
@@ -3340,6 +3603,9 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
          gcc_assert (XVECLEN (trueop1, 0) == 1);
          gcc_assert (CONST_INT_P (XVECEXP (trueop1, 0, 0)));
 
+         if (vec_duplicate_p (trueop0, &elt0))
+           return elt0;
+
          if (GET_CODE (trueop0) == CONST_VECTOR)
            return CONST_VECTOR_ELT (trueop0, INTVAL (XVECEXP
                                                      (trueop1, 0, 0)));
@@ -3355,9 +3621,7 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
              rtx op0 = XEXP (trueop0, 0);
              rtx op1 = XEXP (trueop0, 1);
 
-             machine_mode opmode = GET_MODE (op0);
-             int elt_size = GET_MODE_UNIT_SIZE (opmode);
-             int n_elts = GET_MODE_SIZE (opmode) / elt_size;
+             int n_elts = GET_MODE_NUNITS (GET_MODE (op0));
 
              int i = INTVAL (XVECEXP (trueop1, 0, 0));
              int elem;
@@ -3384,21 +3648,8 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                  mode01 = GET_MODE (op01);
 
                  /* Find out number of elements of each operand.  */
-                 if (VECTOR_MODE_P (mode00))
-                   {
-                     elt_size = GET_MODE_UNIT_SIZE (mode00);
-                     n_elts00 = GET_MODE_SIZE (mode00) / elt_size;
-                   }
-                 else
-                   n_elts00 = 1;
-
-                 if (VECTOR_MODE_P (mode01))
-                   {
-                     elt_size = GET_MODE_UNIT_SIZE (mode01);
-                     n_elts01 = GET_MODE_SIZE (mode01) / elt_size;
-                   }
-                 else
-                   n_elts01 = 1;
+                 n_elts00 = GET_MODE_NUNITS (mode00);
+                 n_elts01 = GET_MODE_NUNITS (mode01);
 
                  gcc_assert (n_elts == n_elts00 + n_elts01);
 
@@ -3422,9 +3673,6 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                                    tmp_op, gen_rtx_PARALLEL (VOIDmode, vec));
              return tmp;
            }
-         if (GET_CODE (trueop0) == VEC_DUPLICATE
-             && GET_MODE (XEXP (trueop0, 0)) == mode)
-           return XEXP (trueop0, 0);
        }
       else
        {
@@ -3433,6 +3681,11 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                      == GET_MODE_INNER (GET_MODE (trueop0)));
          gcc_assert (GET_CODE (trueop1) == PARALLEL);
 
+         if (vec_duplicate_p (trueop0, &elt0))
+           /* It doesn't matter which elements are selected by trueop1,
+              because they are all the same.  */
+           return gen_vec_duplicate (mode, elt0);
+
          if (GET_CODE (trueop0) == CONST_VECTOR)
            {
              int elt_size = GET_MODE_UNIT_SIZE (mode);
@@ -3516,9 +3769,8 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
              rtx subop1 = XEXP (trueop0, 1);
              machine_mode mode0 = GET_MODE (subop0);
              machine_mode mode1 = GET_MODE (subop1);
-             int li = GET_MODE_UNIT_SIZE (mode0);
-             int l0 = GET_MODE_SIZE (mode0) / li;
-             int l1 = GET_MODE_SIZE (mode1) / li;
+             int l0 = GET_MODE_NUNITS (mode0);
+             int l1 = GET_MODE_NUNITS (mode1);
              int i0 = INTVAL (XVECEXP (trueop1, 0, 0));
              if (i0 == 0 && !side_effects_p (op1) && mode == mode0)
                {
@@ -3606,7 +3858,7 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
              for (int i = 0; i < XVECLEN (trueop1, 0); i++)
                {
                  rtx j = XVECEXP (trueop1, 0, i);
-                 if (sel & (1 << UINTVAL (j)))
+                 if (sel & (HOST_WIDE_INT_1U << UINTVAL (j)))
                    all_operand1 = false;
                  else
                    all_operand0 = false;
@@ -3676,14 +3928,10 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
                || CONST_SCALAR_INT_P (trueop1) 
                || CONST_DOUBLE_AS_FLOAT_P (trueop1)))
          {
-           int elt_size = GET_MODE_UNIT_SIZE (mode);
-           unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+           unsigned n_elts = GET_MODE_NUNITS (mode);
+           unsigned in_n_elts = GET_MODE_NUNITS (op0_mode);
            rtvec v = rtvec_alloc (n_elts);
            unsigned int i;
-           unsigned in_n_elts = 1;
-
-           if (VECTOR_MODE_P (op0_mode))
-             in_n_elts = (GET_MODE_SIZE (op0_mode) / elt_size);
            for (i = 0; i < n_elts; i++)
              {
                if (i < in_n_elts)
@@ -3733,6 +3981,32 @@ simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
       gcc_unreachable ();
     }
 
+  if (mode == GET_MODE (op0)
+      && mode == GET_MODE (op1)
+      && vec_duplicate_p (op0, &elt0)
+      && vec_duplicate_p (op1, &elt1))
+    {
+      /* Try applying the operator to ELT and see if that simplifies.
+        We can duplicate the result if so.
+
+        The reason we don't use simplify_gen_binary is that it isn't
+        necessarily a win to convert things like:
+
+          (plus:V (vec_duplicate:V (reg:S R1))
+                  (vec_duplicate:V (reg:S R2)))
+
+        to:
+
+          (vec_duplicate:V (plus:S (reg:S R1) (reg:S R2)))
+
+        The first might be done entirely in vector registers while the
+        second might need a move between register files.  */
+      tem = simplify_binary_operation (code, GET_MODE_INNER (mode),
+                                      elt0, elt1);
+      if (tem)
+       return gen_vec_duplicate (mode, tem);
+    }
+
   return 0;
 }
 
@@ -3740,23 +4014,17 @@ rtx
 simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
                                 rtx op0, rtx op1)
 {
-  unsigned int width = GET_MODE_PRECISION (mode);
-
   if (VECTOR_MODE_P (mode)
       && code != VEC_CONCAT
       && GET_CODE (op0) == CONST_VECTOR
       && GET_CODE (op1) == CONST_VECTOR)
     {
-      unsigned n_elts = GET_MODE_NUNITS (mode);
-      machine_mode op0mode = GET_MODE (op0);
-      unsigned op0_n_elts = GET_MODE_NUNITS (op0mode);
-      machine_mode op1mode = GET_MODE (op1);
-      unsigned op1_n_elts = GET_MODE_NUNITS (op1mode);
+      unsigned int n_elts = CONST_VECTOR_NUNITS (op0);
+      gcc_assert (n_elts == (unsigned int) CONST_VECTOR_NUNITS (op1));
+      gcc_assert (n_elts == GET_MODE_NUNITS (mode));
       rtvec v = rtvec_alloc (n_elts);
       unsigned int i;
 
-      gcc_assert (op0_n_elts == n_elts);
-      gcc_assert (op1_n_elts == n_elts);
       for (i = 0; i < n_elts; i++)
        {
          rtx x = simplify_binary_operation (code, GET_MODE_INNER (mode),
@@ -3851,15 +4119,20 @@ simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
       else
        {
          REAL_VALUE_TYPE f0, f1, value, result;
+         const REAL_VALUE_TYPE *opr0, *opr1;
          bool inexact;
 
-         real_convert (&f0, mode, CONST_DOUBLE_REAL_VALUE (op0));
-         real_convert (&f1, mode, CONST_DOUBLE_REAL_VALUE (op1));
+         opr0 = CONST_DOUBLE_REAL_VALUE (op0);
+         opr1 = CONST_DOUBLE_REAL_VALUE (op1);
 
          if (HONOR_SNANS (mode)
-             && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
+             && (REAL_VALUE_ISSIGNALING_NAN (*opr0)
+                 || REAL_VALUE_ISSIGNALING_NAN (*opr1)))
            return 0;
 
+         real_convert (&f0, mode, opr0);
+         real_convert (&f1, mode, opr1);
+
          if (code == DIV
              && real_equal (&f1, &dconst0)
              && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
@@ -3930,15 +4203,15 @@ simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
     }
 
   /* We can fold some multi-word operations.  */
-  if ((GET_MODE_CLASS (mode) == MODE_INT
-       || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+  scalar_int_mode int_mode;
+  if (is_a <scalar_int_mode> (mode, &int_mode)
       && CONST_SCALAR_INT_P (op0)
       && CONST_SCALAR_INT_P (op1))
     {
       wide_int result;
       bool overflow;
-      rtx_mode_t pop0 = std::make_pair (op0, mode);
-      rtx_mode_t pop1 = std::make_pair (op1, mode);
+      rtx_mode_t pop0 = rtx_mode_t (op0, int_mode);
+      rtx_mode_t pop1 = rtx_mode_t (op1, int_mode);
 
 #if TARGET_SUPPORTS_WIDE_INT == 0
       /* This assert keeps the simplification from producing a result
@@ -3947,7 +4220,7 @@ simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
         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);
+      gcc_assert (GET_MODE_PRECISION (int_mode) <= HOST_BITS_PER_DOUBLE_INT);
 #endif
       switch (code)
        {
@@ -4021,8 +4294,8 @@ simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
          {
            wide_int wop1 = pop1;
            if (SHIFT_COUNT_TRUNCATED)
-             wop1 = wi::umod_trunc (wop1, width);
-           else if (wi::geu_p (wop1, width))
+             wop1 = wi::umod_trunc (wop1, GET_MODE_PRECISION (int_mode));
+           else if (wi::geu_p (wop1, GET_MODE_PRECISION (int_mode)))
              return NULL_RTX;
 
            switch (code)
@@ -4068,7 +4341,58 @@ simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
        default:
          return NULL_RTX;
        }
-      return immed_wide_int_const (result, mode);
+      return immed_wide_int_const (result, int_mode);
+    }
+
+  /* Handle polynomial integers.  */
+  if (NUM_POLY_INT_COEFFS > 1
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && poly_int_rtx_p (op0)
+      && poly_int_rtx_p (op1))
+    {
+      poly_wide_int result;
+      switch (code)
+       {
+       case PLUS:
+         result = wi::to_poly_wide (op0, mode) + wi::to_poly_wide (op1, mode);
+         break;
+
+       case MINUS:
+         result = wi::to_poly_wide (op0, mode) - wi::to_poly_wide (op1, mode);
+         break;
+
+       case MULT:
+         if (CONST_SCALAR_INT_P (op1))
+           result = wi::to_poly_wide (op0, mode) * rtx_mode_t (op1, mode);
+         else
+           return NULL_RTX;
+         break;
+
+       case ASHIFT:
+         if (CONST_SCALAR_INT_P (op1))
+           {
+             wide_int shift = rtx_mode_t (op1, mode);
+             if (SHIFT_COUNT_TRUNCATED)
+               shift = wi::umod_trunc (shift, GET_MODE_PRECISION (int_mode));
+             else if (wi::geu_p (shift, GET_MODE_PRECISION (int_mode)))
+               return NULL_RTX;
+             result = wi::to_poly_wide (op0, mode) << shift;
+           }
+         else
+           return NULL_RTX;
+         break;
+
+       case IOR:
+         if (!CONST_SCALAR_INT_P (op1)
+             || !can_ior_p (wi::to_poly_wide (op0, mode),
+                            rtx_mode_t (op1, mode), &result))
+           return NULL_RTX;
+         break;
+
+       default:
+         return NULL_RTX;
+       }
+      return immed_wide_int_const (result, int_mode);
     }
 
   return NULL_RTX;
@@ -4357,9 +4681,26 @@ simplify_plus_minus (enum rtx_code code, machine_mode mode, rtx op0,
       n_ops = i;
     }
 
-  /* If nothing changed, fail.  */
+  /* If nothing changed, check that rematerialization of rtl instructions
+     is still required.  */
   if (!canonicalized)
-    return NULL_RTX;
+    {
+      /* Perform rematerialization if only all operands are registers and
+        all operations are PLUS.  */
+      /* ??? Also disallow (non-global, non-frame) fixed registers to work
+        around rs6000 and how it uses the CA register.  See PR67145.  */
+      for (i = 0; i < n_ops; i++)
+       if (ops[i].neg
+           || !REG_P (ops[i].op)
+           || (REGNO (ops[i].op) < FIRST_PSEUDO_REGISTER
+               && fixed_regs[REGNO (ops[i].op)]
+               && !global_regs[REGNO (ops[i].op)]
+               && ops[i].op != frame_pointer_rtx
+               && ops[i].op != arg_pointer_rtx
+               && ops[i].op != stack_pointer_rtx))
+         return NULL_RTX;
+      goto gen_result;
+    }
 
   /* Create (minus -C X) instead of (neg (const (plus X C))).  */
   if (n_ops == 2
@@ -4381,9 +4722,12 @@ simplify_plus_minus (enum rtx_code code, machine_mode mode, rtx op0,
       rtx value = ops[n_ops - 1].op;
       if (ops[n_ops - 1].neg ^ ops[n_ops - 2].neg)
        value = neg_const_int (mode, value);
-      ops[n_ops - 2].op = plus_constant (mode, ops[n_ops - 2].op,
-                                        INTVAL (value));
-      n_ops--;
+      if (CONST_INT_P (value))
+       {
+         ops[n_ops - 2].op = plus_constant (mode, ops[n_ops - 2].op,
+                                            INTVAL (value));
+         n_ops--;
+       }
     }
 
   /* Put a non-negated operand first, if possible.  */
@@ -4401,6 +4745,7 @@ simplify_plus_minus (enum rtx_code code, machine_mode mode, rtx op0,
     }
 
   /* Now make the result by performing the requested operations.  */
+ gen_result:
   result = ops[0].op;
   for (i = 1; i < n_ops; i++)
     result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS,
@@ -4463,20 +4808,13 @@ simplify_relational_operation (enum rtx_code code, machine_mode mode,
            return CONST0_RTX (mode);
 #ifdef VECTOR_STORE_FLAG_VALUE
          {
-           int i, units;
-           rtvec v;
-
            rtx val = VECTOR_STORE_FLAG_VALUE (mode);
            if (val == NULL_RTX)
              return NULL_RTX;
            if (val == const1_rtx)
              return CONST1_RTX (mode);
 
-           units = GET_MODE_NUNITS (mode);
-           v = rtvec_alloc (units);
-           for (i = 0; i < units; i++)
-             RTVEC_ELT (v, i) = val;
-           return gen_rtx_raw_CONST_VECTOR (mode, v);
+           return gen_const_vec_duplicate (mode, val);
          }
 #else
          return NULL_RTX;
@@ -4532,7 +4870,7 @@ simplify_relational_operation_1 (enum rtx_code code, machine_mode mode,
        }
       else if (code == EQ)
        {
-         enum rtx_code new_code = reversed_comparison_code (op0, NULL_RTX);
+         enum rtx_code new_code = reversed_comparison_code (op0, NULL);
          if (new_code != UNKNOWN)
            return simplify_gen_relational (new_code, mode, VOIDmode,
                                            XEXP (op0, 0), XEXP (op0, 1));
@@ -4555,6 +4893,19 @@ simplify_relational_operation_1 (enum rtx_code code, machine_mode mode,
                                      cmp_mode, XEXP (op0, 0), new_cmp);
     }
 
+  /* (GTU (PLUS a C) (C - 1)) where C is a non-zero constant can be
+     transformed into (LTU a -C).  */
+  if (code == GTU && GET_CODE (op0) == PLUS && CONST_INT_P (op1)
+      && CONST_INT_P (XEXP (op0, 1))
+      && (UINTVAL (op1) == UINTVAL (XEXP (op0, 1)) - 1)
+      && XEXP (op0, 1) != const0_rtx)
+    {
+      rtx new_cmp
+       = simplify_gen_unary (NEG, cmp_mode, XEXP (op0, 1), cmp_mode);
+      return simplify_gen_relational (LTU, mode, cmp_mode,
+                                      XEXP (op0, 0), new_cmp);
+    }
+
   /* Canonicalize (LTU/GEU (PLUS a b) b) as (LTU/GEU (PLUS a b) a).  */
   if ((code == LTU || code == GEU)
       && GET_CODE (op0) == PLUS
@@ -4634,18 +4985,19 @@ simplify_relational_operation_1 (enum rtx_code code, machine_mode mode,
 
   /* (ne:SI (zero_extract:SI FOO (const_int 1) BAR) (const_int 0))) is
      the same as (zero_extract:SI FOO (const_int 1) BAR).  */
+  scalar_int_mode int_mode, int_cmp_mode;
   if (code == NE
       && op1 == const0_rtx
-      && GET_MODE_CLASS (mode) == MODE_INT
-      && cmp_mode != VOIDmode
+      && is_int_mode (mode, &int_mode)
+      && is_a <scalar_int_mode> (cmp_mode, &int_cmp_mode)
       /* ??? Work-around BImode bugs in the ia64 backend.  */
-      && mode != BImode
-      && cmp_mode != BImode
-      && nonzero_bits (op0, cmp_mode) == 1
+      && int_mode != BImode
+      && int_cmp_mode != BImode
+      && nonzero_bits (op0, int_cmp_mode) == 1
       && STORE_FLAG_VALUE == 1)
-    return GET_MODE_SIZE (mode) > GET_MODE_SIZE (cmp_mode)
-          ? simplify_gen_unary (ZERO_EXTEND, mode, op0, cmp_mode)
-          : lowpart_subreg (mode, op0, cmp_mode);
+    return GET_MODE_SIZE (int_mode) > GET_MODE_SIZE (int_cmp_mode)
+          ? simplify_gen_unary (ZERO_EXTEND, int_mode, op0, int_cmp_mode)
+          : lowpart_subreg (int_mode, op0, int_cmp_mode);
 
   /* (eq/ne (xor x y) 0) simplifies to (eq/ne x y).  */
   if ((code == EQ || code == NE)
@@ -4871,7 +5223,7 @@ simplify_const_relational_operation (enum rtx_code code,
       && (code == EQ || code == NE)
       && ! ((REG_P (op0) || CONST_INT_P (trueop0))
            && (REG_P (op1) || CONST_INT_P (trueop1)))
-      && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
+      && (tem = simplify_binary_operation (MINUS, mode, op0, op1)) != 0
       /* We cannot do this if tem is a nonzero address.  */
       && ! nonzero_address_p (tem))
     return simplify_const_relational_operation (signed_condition (code),
@@ -4941,8 +5293,8 @@ simplify_const_relational_operation (enum rtx_code code,
         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);
+      rtx_mode_t ptrueop0 = rtx_mode_t (trueop0, cmode);
+      rtx_mode_t ptrueop1 = rtx_mode_t (trueop1, cmode);
 
       if (wi::eq_p (ptrueop0, ptrueop1))
        return comparison_result (code, CMP_EQ);
@@ -4955,12 +5307,14 @@ simplify_const_relational_operation (enum rtx_code code,
     }
 
   /* Optimize comparisons with upper and lower bounds.  */
-  if (HWI_COMPUTABLE_MODE_P (mode)
-      && CONST_INT_P (trueop1)
+  scalar_int_mode int_mode;
+  if (CONST_INT_P (trueop1)
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && HWI_COMPUTABLE_MODE_P (int_mode)
       && !side_effects_p (trueop0))
     {
       int sign;
-      unsigned HOST_WIDE_INT nonzero = nonzero_bits (trueop0, mode);
+      unsigned HOST_WIDE_INT nonzero = nonzero_bits (trueop0, int_mode);
       HOST_WIDE_INT val = INTVAL (trueop1);
       HOST_WIDE_INT mmin, mmax;
 
@@ -4973,7 +5327,7 @@ simplify_const_relational_operation (enum rtx_code code,
        sign = 1;
 
       /* Get a reduced range if the sign bit is zero.  */
-      if (nonzero <= (GET_MODE_MASK (mode) >> 1))
+      if (nonzero <= (GET_MODE_MASK (int_mode) >> 1))
        {
          mmin = 0;
          mmax = nonzero;
@@ -4981,13 +5335,14 @@ simplify_const_relational_operation (enum rtx_code code,
       else
        {
          rtx mmin_rtx, mmax_rtx;
-         get_mode_bounds (mode, sign, mode, &mmin_rtx, &mmax_rtx);
+         get_mode_bounds (int_mode, sign, int_mode, &mmin_rtx, &mmax_rtx);
 
          mmin = INTVAL (mmin_rtx);
          mmax = INTVAL (mmax_rtx);
          if (sign)
            {
-             unsigned int sign_copies = num_sign_bit_copies (trueop0, mode);
+             unsigned int sign_copies
+               = num_sign_bit_copies (trueop0, int_mode);
 
              mmin >>= (sign_copies - 1);
              mmax >>= (sign_copies - 1);
@@ -5070,7 +5425,9 @@ simplify_const_relational_operation (enum rtx_code code,
     }
 
   /* Optimize integer comparisons with zero.  */
-  if (trueop1 == const0_rtx && !side_effects_p (trueop0))
+  if (is_a <scalar_int_mode> (mode, &int_mode)
+      && trueop1 == const0_rtx
+      && !side_effects_p (trueop0))
     {
       /* Some addresses are known to be nonzero.  We don't know
         their sign, but equality comparisons are known.  */
@@ -5089,10 +5446,10 @@ simplify_const_relational_operation (enum rtx_code code,
          rtx inner_const = avoid_constant_pool_reference (XEXP (op0, 1));
          if (CONST_INT_P (inner_const) && inner_const != const0_rtx)
            {
-             int sign_bitnum = GET_MODE_PRECISION (mode) - 1;
+             int sign_bitnum = GET_MODE_PRECISION (int_mode) - 1;
              int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
                              && (UINTVAL (inner_const)
-                                 & ((unsigned HOST_WIDE_INT) 1
+                                 & (HOST_WIDE_INT_1U
                                     << sign_bitnum)));
 
              switch (code)
@@ -5130,34 +5487,14 @@ simplify_const_relational_operation (enum rtx_code code,
        {
        case LT:
          /* Optimize abs(x) < 0.0.  */
-         if (!HONOR_SNANS (mode)
-             && (!INTEGRAL_MODE_P (mode)
-                 || (!flag_wrapv && !flag_trapv && flag_strict_overflow)))
-           {
-             if (INTEGRAL_MODE_P (mode)
-                 && (issue_strict_overflow_warning
-                     (WARN_STRICT_OVERFLOW_CONDITIONAL)))
-               warning (OPT_Wstrict_overflow,
-                        ("assuming signed overflow does not occur when "
-                         "assuming abs (x) < 0 is false"));
-              return const0_rtx;
-           }
+         if (!INTEGRAL_MODE_P (mode) && !HONOR_SNANS (mode))
+           return const0_rtx;
          break;
 
        case GE:
          /* Optimize abs(x) >= 0.0.  */
-         if (!HONOR_NANS (mode)
-             && (!INTEGRAL_MODE_P (mode)
-                 || (!flag_wrapv && !flag_trapv && flag_strict_overflow)))
-           {
-             if (INTEGRAL_MODE_P (mode)
-                 && (issue_strict_overflow_warning
-                 (WARN_STRICT_OVERFLOW_CONDITIONAL)))
-               warning (OPT_Wstrict_overflow,
-                        ("assuming signed overflow does not occur when "
-                         "assuming abs (x) >= 0 is true"));
-             return const_true_rtx;
-           }
+         if (!INTEGRAL_MODE_P (mode) && !HONOR_NANS (mode))
+           return const_true_rtx;
          break;
 
        case UNGE:
@@ -5171,6 +5508,50 @@ simplify_const_relational_operation (enum rtx_code code,
 
   return 0;
 }
+
+/* Recognize expressions of the form (X CMP 0) ? VAL : OP (X)
+   where OP is CLZ or CTZ and VAL is the value from CLZ_DEFINED_VALUE_AT_ZERO
+   or CTZ_DEFINED_VALUE_AT_ZERO respectively and return OP (X) if the expression
+   can be simplified to that or NULL_RTX if not.
+   Assume X is compared against zero with CMP_CODE and the true
+   arm is TRUE_VAL and the false arm is FALSE_VAL.  */
+
+static rtx
+simplify_cond_clz_ctz (rtx x, rtx_code cmp_code, rtx true_val, rtx false_val)
+{
+  if (cmp_code != EQ && cmp_code != NE)
+    return NULL_RTX;
+
+  /* Result on X == 0 and X !=0 respectively.  */
+  rtx on_zero, on_nonzero;
+  if (cmp_code == EQ)
+    {
+      on_zero = true_val;
+      on_nonzero = false_val;
+    }
+  else
+    {
+      on_zero = false_val;
+      on_nonzero = true_val;
+    }
+
+  rtx_code op_code = GET_CODE (on_nonzero);
+  if ((op_code != CLZ && op_code != CTZ)
+      || !rtx_equal_p (XEXP (on_nonzero, 0), x)
+      || !CONST_INT_P (on_zero))
+    return NULL_RTX;
+
+  HOST_WIDE_INT op_val;
+  scalar_int_mode mode ATTRIBUTE_UNUSED
+    = as_a <scalar_int_mode> (GET_MODE (XEXP (on_nonzero, 0)));
+  if (((op_code == CLZ && CLZ_DEFINED_VALUE_AT_ZERO (mode, op_val))
+       || (op_code == CTZ && CTZ_DEFINED_VALUE_AT_ZERO (mode, op_val)))
+      && op_val == INTVAL (on_zero))
+    return on_nonzero;
+
+  return NULL_RTX;
+}
+
 \f
 /* Simplify CODE, an operation with result mode MODE and three operands,
    OP0, OP1, and OP2.  OP0_MODE was the mode of OP0 before it became
@@ -5181,13 +5562,9 @@ 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);
   bool any_change = false;
   rtx tem, trueop2;
-
-  /* VOIDmode means "infinite" precision.  */
-  if (width == 0)
-    width = HOST_BITS_PER_WIDE_INT;
+  scalar_int_mode int_mode, int_op0_mode;
 
   switch (code)
     {
@@ -5221,30 +5598,34 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
       if (CONST_INT_P (op0)
          && CONST_INT_P (op1)
          && CONST_INT_P (op2)
-         && ((unsigned) INTVAL (op1) + (unsigned) INTVAL (op2) <= width)
-         && width <= (unsigned) HOST_BITS_PER_WIDE_INT)
+         && is_a <scalar_int_mode> (mode, &int_mode)
+         && INTVAL (op1) + INTVAL (op2) <= GET_MODE_PRECISION (int_mode)
+         && HWI_COMPUTABLE_MODE_P (int_mode))
        {
          /* Extracting a bit-field from a constant */
          unsigned HOST_WIDE_INT val = UINTVAL (op0);
          HOST_WIDE_INT op1val = INTVAL (op1);
          HOST_WIDE_INT op2val = INTVAL (op2);
-         if (BITS_BIG_ENDIAN)
-           val >>= GET_MODE_PRECISION (op0_mode) - op2val - op1val;
-         else
+         if (!BITS_BIG_ENDIAN)
            val >>= op2val;
+         else if (is_a <scalar_int_mode> (op0_mode, &int_op0_mode))
+           val >>= GET_MODE_PRECISION (int_op0_mode) - op2val - op1val;
+         else
+           /* Not enough information to calculate the bit position.  */
+           break;
 
          if (HOST_BITS_PER_WIDE_INT != op1val)
            {
              /* First zero-extend.  */
-             val &= ((unsigned HOST_WIDE_INT) 1 << op1val) - 1;
+             val &= (HOST_WIDE_INT_1U << op1val) - 1;
              /* If desired, propagate sign bit.  */
              if (code == SIGN_EXTRACT
-                 && (val & ((unsigned HOST_WIDE_INT) 1 << (op1val - 1)))
+                 && (val & (HOST_WIDE_INT_1U << (op1val - 1)))
                     != 0)
-               val |= ~ (((unsigned HOST_WIDE_INT) 1 << op1val) - 1);
+               val |= ~ ((HOST_WIDE_INT_1U << op1val) - 1);
            }
 
-         return gen_int_mode (val, mode);
+         return gen_int_mode (val, int_mode);
        }
       break;
 
@@ -5304,6 +5685,19 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
            }
        }
 
+      /* Convert x == 0 ? N : clz (x) into clz (x) when
+        CLZ_DEFINED_VALUE_AT_ZERO is defined to N for the mode of x.
+        Similarly for ctz (x).  */
+      if (COMPARISON_P (op0) && !side_effects_p (op0)
+         && XEXP (op0, 1) == const0_rtx)
+       {
+         rtx simplified
+           = simplify_cond_clz_ctz (XEXP (op0, 0), GET_CODE (op0),
+                                    op1, op2);
+         if (simplified)
+           return simplified;
+       }
+
       if (COMPARISON_P (op0) && ! side_effects_p (op0))
        {
          machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
@@ -5322,7 +5716,7 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
              else if (t == 0 && f == STORE_FLAG_VALUE)
                {
                  enum rtx_code tmp;
-                 tmp = reversed_comparison_code (op0, NULL_RTX);
+                 tmp = reversed_comparison_code (op0, NULL);
                  if (tmp == UNKNOWN)
                    break;
                  code = tmp;
@@ -5334,8 +5728,6 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
                                              XEXP (op0, 0), XEXP (op0, 1));
            }
 
-         if (cmp_mode == VOIDmode)
-           cmp_mode = op0_mode;
          temp = simplify_relational_operation (GET_CODE (op0), op0_mode,
                                                cmp_mode, XEXP (op0, 0),
                                                XEXP (op0, 1));
@@ -5358,14 +5750,13 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
       trueop2 = avoid_constant_pool_reference (op2);
       if (CONST_INT_P (trueop2))
        {
-         int elt_size = GET_MODE_UNIT_SIZE (mode);
-         unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+         unsigned n_elts = GET_MODE_NUNITS (mode);
          unsigned HOST_WIDE_INT sel = UINTVAL (trueop2);
          unsigned HOST_WIDE_INT mask;
          if (n_elts == HOST_BITS_PER_WIDE_INT)
            mask = -1;
          else
-           mask = ((unsigned HOST_WIDE_INT) 1 << n_elts) - 1;
+           mask = (HOST_WIDE_INT_1U << n_elts) - 1;
 
          if (!(sel & mask) && !side_effects_p (op0))
            return op1;
@@ -5381,7 +5772,7 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
              unsigned int i;
 
              for (i = 0; i < n_elts; i++)
-               RTVEC_ELT (v, i) = ((sel & ((unsigned HOST_WIDE_INT) 1 << i))
+               RTVEC_ELT (v, i) = ((sel & (HOST_WIDE_INT_1U << i))
                                    ? CONST_VECTOR_ELT (trueop0, i)
                                    : CONST_VECTOR_ELT (trueop1, i));
              return gen_rtx_CONST_VECTOR (mode, v);
@@ -5433,6 +5824,59 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
                    return op1;
                }
            }
+         /* Replace (vec_merge (vec_duplicate (X)) (const_vector [A, B])
+            (const_int N))
+            with (vec_concat (X) (B)) if N == 1 or
+            (vec_concat (A) (X)) if N == 2.  */
+         if (GET_CODE (op0) == VEC_DUPLICATE
+             && GET_CODE (op1) == CONST_VECTOR
+             && CONST_VECTOR_NUNITS (op1) == 2
+             && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+             && IN_RANGE (sel, 1, 2))
+           {
+             rtx newop0 = XEXP (op0, 0);
+             rtx newop1 = CONST_VECTOR_ELT (op1, 2 - sel);
+             if (sel == 2)
+               std::swap (newop0, newop1);
+             return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+           }
+         /* Replace (vec_merge (vec_duplicate x) (vec_concat (y) (z)) (const_int N))
+            with (vec_concat x z) if N == 1, or (vec_concat y x) if N == 2.
+            Only applies for vectors of two elements.  */
+         if (GET_CODE (op0) == VEC_DUPLICATE
+             && GET_CODE (op1) == VEC_CONCAT
+             && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+             && GET_MODE_NUNITS (GET_MODE (op1)) == 2
+             && IN_RANGE (sel, 1, 2))
+           {
+             rtx newop0 = XEXP (op0, 0);
+             rtx newop1 = XEXP (op1, 2 - sel);
+             rtx otherop = XEXP (op1, sel - 1);
+             if (sel == 2)
+               std::swap (newop0, newop1);
+             /* Don't want to throw away the other part of the vec_concat if
+                it has side-effects.  */
+             if (!side_effects_p (otherop))
+               return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+           }
+
+         /* Replace (vec_merge (vec_duplicate x) (vec_duplicate y)
+                                (const_int n))
+            with (vec_concat x y) or (vec_concat y x) depending on value
+            of N.  */
+         if (GET_CODE (op0) == VEC_DUPLICATE
+             && GET_CODE (op1) == VEC_DUPLICATE
+             && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+             && GET_MODE_NUNITS (GET_MODE (op1)) == 2
+             && IN_RANGE (sel, 1, 2))
+           {
+             rtx newop0 = XEXP (op0, 0);
+             rtx newop1 = XEXP (op1, 0);
+             if (sel == 2)
+               std::swap (newop0, newop1);
+
+             return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+           }
        }
 
       if (rtx_equal_p (op0, op1)
@@ -5457,8 +5901,8 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
    and then repacking them again for OUTERMODE.  */
 
 static rtx
-simplify_immed_subreg (machine_mode outermode, rtx op,
-                      machine_mode innermode, unsigned int byte)
+simplify_immed_subreg (fixed_size_mode outermode, rtx op,
+                      fixed_size_mode innermode, unsigned int byte)
 {
   enum {
     value_bit = 8,
@@ -5472,10 +5916,10 @@ simplify_immed_subreg (machine_mode outermode, rtx op,
   int num_elem;
   rtx * elems;
   int elem_bitsize;
-  rtx result_s;
+  rtx result_s = NULL;
   rtvec result_v = NULL;
   enum mode_class outer_class;
-  machine_mode outer_submode;
+  scalar_mode outer_submode;
   int max_bitsize;
 
   /* Some ports misuse CCmode.  */
@@ -5541,10 +5985,11 @@ simplify_immed_subreg (machine_mode outermode, rtx op,
 
        case CONST_WIDE_INT:
          {
-           rtx_mode_t val = std::make_pair (el, innermode);
+           rtx_mode_t val = rtx_mode_t (el, GET_MODE_INNER (innermode));
            unsigned char extend = wi::sign_mask (val);
+           int prec = wi::get_precision (val);
 
-           for (i = 0; i < elem_bitsize; i += value_bit)
+           for (i = 0; i < prec && i < elem_bitsize; i += value_bit)
              *vp++ = wi::extract_uhwi (val, i, value_bit);
            for (; i < elem_bitsize; i += value_bit)
              *vp++ = extend;
@@ -5577,9 +6022,11 @@ simplify_immed_subreg (machine_mode outermode, rtx op,
            {
              /* 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));
+             scalar_float_mode el_mode;
+
+             el_mode = as_a <scalar_float_mode> (GET_MODE (el));
+             int bitsize = GET_MODE_BITSIZE (el_mode);
 
-             gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (el)));
              gcc_assert (bitsize <= elem_bitsize);
              gcc_assert (bitsize % value_bit == 0);
 
@@ -5728,14 +6175,12 @@ simplify_immed_subreg (machine_mode outermode, rtx op,
        case MODE_DECIMAL_FLOAT:
          {
            REAL_VALUE_TYPE r;
-           long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
+           long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32] = { 0 };
 
            /* real_from_target wants its input in words affected by
               FLOAT_WORDS_BIG_ENDIAN.  However, we ignore this,
               and use WORDS_BIG_ENDIAN instead; see the documentation
               of SUBREG in rtl.texi.  */
-           for (i = 0; i < max_bitsize / 32; i++)
-             tmp[i] = 0;
            for (i = 0; i < elem_bitsize; i += value_bit)
              {
                int ibase;
@@ -5787,7 +6232,7 @@ simplify_immed_subreg (machine_mode outermode, rtx op,
    Return 0 if no simplifications are possible.  */
 rtx
 simplify_subreg (machine_mode outermode, rtx op,
-                machine_mode innermode, unsigned int byte)
+                machine_mode innermode, poly_uint64 byte)
 {
   /* Little bit of sanity checking.  */
   gcc_assert (innermode != VOIDmode);
@@ -5798,81 +6243,87 @@ simplify_subreg (machine_mode outermode, rtx op,
   gcc_assert (GET_MODE (op) == innermode
              || GET_MODE (op) == VOIDmode);
 
-  if ((byte % GET_MODE_SIZE (outermode)) != 0)
+  if (!multiple_p (byte, GET_MODE_SIZE (outermode)))
     return NULL_RTX;
 
-  if (byte >= GET_MODE_SIZE (innermode))
+  if (maybe_ge (byte, GET_MODE_SIZE (innermode)))
     return NULL_RTX;
 
-  if (outermode == innermode && !byte)
+  if (outermode == innermode && known_eq (byte, 0U))
     return op;
 
+  if (multiple_p (byte, GET_MODE_UNIT_SIZE (innermode)))
+    {
+      rtx elt;
+
+      if (VECTOR_MODE_P (outermode)
+         && GET_MODE_INNER (outermode) == GET_MODE_INNER (innermode)
+         && vec_duplicate_p (op, &elt))
+       return gen_vec_duplicate (outermode, elt);
+
+      if (outermode == GET_MODE_INNER (innermode)
+         && vec_duplicate_p (op, &elt))
+       return elt;
+    }
+
   if (CONST_SCALAR_INT_P (op)
       || CONST_DOUBLE_AS_FLOAT_P (op)
       || GET_CODE (op) == CONST_FIXED
       || GET_CODE (op) == CONST_VECTOR)
-    return simplify_immed_subreg (outermode, op, innermode, byte);
+    {
+      /* simplify_immed_subreg deconstructs OP into bytes and constructs
+        the result from bytes, so it only works if the sizes of the modes
+        and the value of the offset are known at compile time.  Cases that
+        that apply to general modes and offsets should be handled here
+        before calling simplify_immed_subreg.  */
+      fixed_size_mode fs_outermode, fs_innermode;
+      unsigned HOST_WIDE_INT cbyte;
+      if (is_a <fixed_size_mode> (outermode, &fs_outermode)
+         && is_a <fixed_size_mode> (innermode, &fs_innermode)
+         && byte.is_constant (&cbyte))
+       return simplify_immed_subreg (fs_outermode, op, fs_innermode, cbyte);
+
+      return NULL_RTX;
+    }
 
   /* Changing mode twice with SUBREG => just change it once,
      or not at all if changing back op starting mode.  */
   if (GET_CODE (op) == SUBREG)
     {
       machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
-      int final_offset = byte + SUBREG_BYTE (op);
       rtx newx;
 
       if (outermode == innermostmode
-         && byte == 0 && SUBREG_BYTE (op) == 0)
+         && known_eq (byte, 0U)
+         && known_eq (SUBREG_BYTE (op), 0))
        return SUBREG_REG (op);
 
-      /* The SUBREG_BYTE represents offset, as if the value were stored
-        in memory.  Irritating exception is paradoxical subreg, where
-        we define SUBREG_BYTE to be 0.  On big endian machines, this
-        value should be negative.  For a moment, undo this exception.  */
-      if (byte == 0 && GET_MODE_SIZE (innermode) < GET_MODE_SIZE (outermode))
-       {
-         int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
-         if (WORDS_BIG_ENDIAN)
-           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
-         if (BYTES_BIG_ENDIAN)
-           final_offset += difference % UNITS_PER_WORD;
-       }
-      if (SUBREG_BYTE (op) == 0
-         && GET_MODE_SIZE (innermostmode) < GET_MODE_SIZE (innermode))
-       {
-         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (innermode));
-         if (WORDS_BIG_ENDIAN)
-           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
-         if (BYTES_BIG_ENDIAN)
-           final_offset += difference % UNITS_PER_WORD;
-       }
+      /* Work out the memory offset of the final OUTERMODE value relative
+        to the inner value of OP.  */
+      poly_int64 mem_offset = subreg_memory_offset (outermode,
+                                                   innermode, byte);
+      poly_int64 op_mem_offset = subreg_memory_offset (op);
+      poly_int64 final_offset = mem_offset + op_mem_offset;
 
       /* See whether resulting subreg will be paradoxical.  */
-      if (GET_MODE_SIZE (innermostmode) > GET_MODE_SIZE (outermode))
+      if (!paradoxical_subreg_p (outermode, innermostmode))
        {
          /* In nonparadoxical subregs we can't handle negative offsets.  */
-         if (final_offset < 0)
+         if (maybe_lt (final_offset, 0))
            return NULL_RTX;
          /* Bail out in case resulting subreg would be incorrect.  */
-         if (final_offset % GET_MODE_SIZE (outermode)
-             || (unsigned) final_offset >= GET_MODE_SIZE (innermostmode))
+         if (!multiple_p (final_offset, GET_MODE_SIZE (outermode))
+             || maybe_ge (final_offset, GET_MODE_SIZE (innermostmode)))
            return NULL_RTX;
        }
       else
        {
-         int offset = 0;
-         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (outermode));
-
-         /* In paradoxical subreg, see if we are still looking on lower part.
-            If so, our SUBREG_BYTE will be 0.  */
-         if (WORDS_BIG_ENDIAN)
-           offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
-         if (BYTES_BIG_ENDIAN)
-           offset += difference % UNITS_PER_WORD;
-         if (offset == final_offset)
-           final_offset = 0;
-         else
+         poly_int64 required_offset = subreg_memory_offset (outermode,
+                                                            innermostmode, 0);
+         if (maybe_ne (final_offset, required_offset))
            return NULL_RTX;
+         /* Paradoxical subregs always have byte offset 0.  */
+         final_offset = 0;
        }
 
       /* Recurse for further possible simplifications.  */
@@ -5913,29 +6364,16 @@ simplify_subreg (machine_mode outermode, rtx op,
       final_regno = simplify_subreg_regno (regno, innermode, byte, outermode);
       if (HARD_REGISTER_NUM_P (final_regno))
        {
-         rtx x;
-         int final_offset = byte;
-
-         /* Adjust offset for paradoxical subregs.  */
-         if (byte == 0
-             && GET_MODE_SIZE (innermode) < GET_MODE_SIZE (outermode))
-           {
-             int difference = (GET_MODE_SIZE (innermode)
-                               - GET_MODE_SIZE (outermode));
-             if (WORDS_BIG_ENDIAN)
-               final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
-             if (BYTES_BIG_ENDIAN)
-               final_offset += difference % UNITS_PER_WORD;
-           }
-
-         x = gen_rtx_REG_offset (op, outermode, final_regno, final_offset);
+         rtx x = gen_rtx_REG_offset (op, outermode, final_regno,
+                                     subreg_memory_offset (outermode,
+                                                           innermode, byte));
 
          /* Propagate original regno.  We don't have any way to specify
             the offset inside original regno, so do so only for lowpart.
             The information is used only by alias analysis that can not
             grog partial register anyway.  */
 
-         if (subreg_lowpart_offset (outermode, innermode) == byte)
+         if (known_eq (subreg_lowpart_offset (outermode, innermode), byte))
            ORIGINAL_REGNO (x) = ORIGINAL_REGNO (op);
          return x;
        }
@@ -5955,32 +6393,42 @@ simplify_subreg (machine_mode outermode, rtx op,
       && GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
     return adjust_address_nv (op, outermode, byte);
 
-  /* Handle complex values represented as CONCAT
-     of real and imaginary part.  */
-  if (GET_CODE (op) == CONCAT)
+  /* Handle complex or vector values represented as CONCAT or VEC_CONCAT
+     of two parts.  */
+  if (GET_CODE (op) == CONCAT
+      || GET_CODE (op) == VEC_CONCAT)
     {
-      unsigned int part_size, final_offset;
+      unsigned int part_size;
+      poly_uint64 final_offset;
       rtx part, res;
 
-      part_size = GET_MODE_UNIT_SIZE (GET_MODE (XEXP (op, 0)));
-      if (byte < part_size)
+      machine_mode part_mode = GET_MODE (XEXP (op, 0));
+      if (part_mode == VOIDmode)
+       part_mode = GET_MODE_INNER (GET_MODE (op));
+      part_size = GET_MODE_SIZE (part_mode);
+      if (known_lt (byte, part_size))
        {
          part = XEXP (op, 0);
          final_offset = byte;
        }
-      else
+      else if (known_ge (byte, part_size))
        {
          part = XEXP (op, 1);
          final_offset = byte - part_size;
        }
+      else
+       return NULL_RTX;
 
-      if (final_offset + GET_MODE_SIZE (outermode) > part_size)
+      if (maybe_gt (final_offset + GET_MODE_SIZE (outermode), part_size))
        return NULL_RTX;
 
-      res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
+      part_mode = GET_MODE (part);
+      if (part_mode == VOIDmode)
+       part_mode = GET_MODE_INNER (GET_MODE (op));
+      res = simplify_subreg (outermode, part, part_mode, final_offset);
       if (res)
        return res;
-      if (validate_subreg (outermode, GET_MODE (part), part, final_offset))
+      if (validate_subreg (outermode, part_mode, part, final_offset))
        return gen_rtx_SUBREG (outermode, part, final_offset);
       return NULL_RTX;
     }
@@ -5989,19 +6437,35 @@ simplify_subreg (machine_mode outermode, rtx op,
      it extracts higher bits that the ZERO_EXTEND's source bits.  */
   if (GET_CODE (op) == ZERO_EXTEND && SCALAR_INT_MODE_P (innermode))
     {
-      unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte);
-      if (bitpos >= GET_MODE_PRECISION (GET_MODE (XEXP (op, 0))))
+      poly_uint64 bitpos = subreg_lsb_1 (outermode, innermode, byte);
+      if (known_ge (bitpos, GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))))
        return CONST0_RTX (outermode);
     }
 
-  if (SCALAR_INT_MODE_P (outermode)
-      && SCALAR_INT_MODE_P (innermode)
-      && GET_MODE_PRECISION (outermode) < GET_MODE_PRECISION (innermode)
-      && byte == subreg_lowpart_offset (outermode, innermode))
+  scalar_int_mode int_outermode, int_innermode;
+  if (is_a <scalar_int_mode> (outermode, &int_outermode)
+      && is_a <scalar_int_mode> (innermode, &int_innermode)
+      && known_eq (byte, subreg_lowpart_offset (int_outermode, int_innermode)))
     {
-      rtx tem = simplify_truncation (outermode, op, innermode);
-      if (tem)
-       return tem;
+      /* Handle polynomial integers.  The upper bits of a paradoxical
+        subreg are undefined, so this is safe regardless of whether
+        we're truncating or extending.  */
+      if (CONST_POLY_INT_P (op))
+       {
+         poly_wide_int val
+           = poly_wide_int::from (const_poly_int_value (op),
+                                  GET_MODE_PRECISION (int_outermode),
+                                  SIGNED);
+         return immed_wide_int_const (val, int_outermode);
+       }
+
+      if (GET_MODE_PRECISION (int_outermode)
+         < GET_MODE_PRECISION (int_innermode))
+       {
+         rtx tem = simplify_truncation (int_outermode, op, int_innermode);
+         if (tem)
+           return tem;
+       }
     }
 
   return NULL_RTX;
@@ -6011,7 +6475,7 @@ simplify_subreg (machine_mode outermode, rtx op,
 
 rtx
 simplify_gen_subreg (machine_mode outermode, rtx op,
-                    machine_mode innermode, unsigned int byte)
+                    machine_mode innermode, poly_uint64 byte)
 {
   rtx newx;
 
@@ -6096,7 +6560,7 @@ simplify_rtx (const_rtx x)
       if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
        return simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0));
 
-      /* Fall through....  */
+      /* Fall through.  */
 
     case RTX_BIN_ARITH:
       return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
@@ -6139,3 +6603,233 @@ simplify_rtx (const_rtx x)
     }
   return NULL;
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Make a unique pseudo REG of mode MODE for use by selftests.  */
+
+static rtx
+make_test_reg (machine_mode mode)
+{
+  static int test_reg_num = LAST_VIRTUAL_REGISTER + 1;
+
+  return gen_rtx_REG (mode, test_reg_num++);
+}
+
+/* Test vector simplifications involving VEC_DUPLICATE in which the
+   operands and result have vector mode MODE.  SCALAR_REG is a pseudo
+   register that holds one element of MODE.  */
+
+static void
+test_vector_ops_duplicate (machine_mode mode, rtx scalar_reg)
+{
+  scalar_mode inner_mode = GET_MODE_INNER (mode);
+  rtx duplicate = gen_rtx_VEC_DUPLICATE (mode, scalar_reg);
+  unsigned int nunits = GET_MODE_NUNITS (mode);
+  if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+    {
+      /* Test some simple unary cases with VEC_DUPLICATE arguments.  */
+      rtx not_scalar_reg = gen_rtx_NOT (inner_mode, scalar_reg);
+      rtx duplicate_not = gen_rtx_VEC_DUPLICATE (mode, not_scalar_reg);
+      ASSERT_RTX_EQ (duplicate,
+                    simplify_unary_operation (NOT, mode,
+                                              duplicate_not, mode));
+
+      rtx neg_scalar_reg = gen_rtx_NEG (inner_mode, scalar_reg);
+      rtx duplicate_neg = gen_rtx_VEC_DUPLICATE (mode, neg_scalar_reg);
+      ASSERT_RTX_EQ (duplicate,
+                    simplify_unary_operation (NEG, mode,
+                                              duplicate_neg, mode));
+
+      /* Test some simple binary cases with VEC_DUPLICATE arguments.  */
+      ASSERT_RTX_EQ (duplicate,
+                    simplify_binary_operation (PLUS, mode, duplicate,
+                                               CONST0_RTX (mode)));
+
+      ASSERT_RTX_EQ (duplicate,
+                    simplify_binary_operation (MINUS, mode, duplicate,
+                                               CONST0_RTX (mode)));
+
+      ASSERT_RTX_PTR_EQ (CONST0_RTX (mode),
+                        simplify_binary_operation (MINUS, mode, duplicate,
+                                                   duplicate));
+    }
+
+  /* Test a scalar VEC_SELECT of a VEC_DUPLICATE.  */
+  rtx zero_par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (1, const0_rtx));
+  ASSERT_RTX_PTR_EQ (scalar_reg,
+                    simplify_binary_operation (VEC_SELECT, inner_mode,
+                                               duplicate, zero_par));
+
+  /* And again with the final element.  */
+  rtx last_index = gen_int_mode (GET_MODE_NUNITS (mode) - 1, word_mode);
+  rtx last_par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (1, last_index));
+  ASSERT_RTX_PTR_EQ (scalar_reg,
+                    simplify_binary_operation (VEC_SELECT, inner_mode,
+                                               duplicate, last_par));
+
+  /* Test a scalar subreg of a VEC_DUPLICATE.  */
+  poly_uint64 offset = subreg_lowpart_offset (inner_mode, mode);
+  ASSERT_RTX_EQ (scalar_reg,
+                simplify_gen_subreg (inner_mode, duplicate,
+                                     mode, offset));
+
+  machine_mode narrower_mode;
+  if (nunits > 2
+      && mode_for_vector (inner_mode, 2).exists (&narrower_mode)
+      && VECTOR_MODE_P (narrower_mode))
+    {
+      /* Test VEC_SELECT of a vector.  */
+      rtx vec_par
+       = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, const1_rtx, const0_rtx));
+      rtx narrower_duplicate
+       = gen_rtx_VEC_DUPLICATE (narrower_mode, scalar_reg);
+      ASSERT_RTX_EQ (narrower_duplicate,
+                    simplify_binary_operation (VEC_SELECT, narrower_mode,
+                                               duplicate, vec_par));
+
+      /* Test a vector subreg of a VEC_DUPLICATE.  */
+      poly_uint64 offset = subreg_lowpart_offset (narrower_mode, mode);
+      ASSERT_RTX_EQ (narrower_duplicate,
+                    simplify_gen_subreg (narrower_mode, duplicate,
+                                         mode, offset));
+    }
+}
+
+/* Test vector simplifications involving VEC_SERIES in which the
+   operands and result have vector mode MODE.  SCALAR_REG is a pseudo
+   register that holds one element of MODE.  */
+
+static void
+test_vector_ops_series (machine_mode mode, rtx scalar_reg)
+{
+  /* Test unary cases with VEC_SERIES arguments.  */
+  scalar_mode inner_mode = GET_MODE_INNER (mode);
+  rtx duplicate = gen_rtx_VEC_DUPLICATE (mode, scalar_reg);
+  rtx neg_scalar_reg = gen_rtx_NEG (inner_mode, scalar_reg);
+  rtx series_0_r = gen_rtx_VEC_SERIES (mode, const0_rtx, scalar_reg);
+  rtx series_0_nr = gen_rtx_VEC_SERIES (mode, const0_rtx, neg_scalar_reg);
+  rtx series_nr_1 = gen_rtx_VEC_SERIES (mode, neg_scalar_reg, const1_rtx);
+  rtx series_r_m1 = gen_rtx_VEC_SERIES (mode, scalar_reg, constm1_rtx);
+  rtx series_r_r = gen_rtx_VEC_SERIES (mode, scalar_reg, scalar_reg);
+  rtx series_nr_nr = gen_rtx_VEC_SERIES (mode, neg_scalar_reg,
+                                        neg_scalar_reg);
+  ASSERT_RTX_EQ (series_0_r,
+                simplify_unary_operation (NEG, mode, series_0_nr, mode));
+  ASSERT_RTX_EQ (series_r_m1,
+                simplify_unary_operation (NEG, mode, series_nr_1, mode));
+  ASSERT_RTX_EQ (series_r_r,
+                simplify_unary_operation (NEG, mode, series_nr_nr, mode));
+
+  /* Test that a VEC_SERIES with a zero step is simplified away.  */
+  ASSERT_RTX_EQ (duplicate,
+                simplify_binary_operation (VEC_SERIES, mode,
+                                           scalar_reg, const0_rtx));
+
+  /* Test PLUS and MINUS with VEC_SERIES.  */
+  rtx series_0_1 = gen_const_vec_series (mode, const0_rtx, const1_rtx);
+  rtx series_0_m1 = gen_const_vec_series (mode, const0_rtx, constm1_rtx);
+  rtx series_r_1 = gen_rtx_VEC_SERIES (mode, scalar_reg, const1_rtx);
+  ASSERT_RTX_EQ (series_r_r,
+                simplify_binary_operation (PLUS, mode, series_0_r,
+                                           duplicate));
+  ASSERT_RTX_EQ (series_r_1,
+                simplify_binary_operation (PLUS, mode, duplicate,
+                                           series_0_1));
+  ASSERT_RTX_EQ (series_r_m1,
+                simplify_binary_operation (PLUS, mode, duplicate,
+                                           series_0_m1));
+  ASSERT_RTX_EQ (series_0_r,
+                simplify_binary_operation (MINUS, mode, series_r_r,
+                                           duplicate));
+  ASSERT_RTX_EQ (series_r_m1,
+                simplify_binary_operation (MINUS, mode, duplicate,
+                                           series_0_1));
+  ASSERT_RTX_EQ (series_r_1,
+                simplify_binary_operation (MINUS, mode, duplicate,
+                                           series_0_m1));
+  ASSERT_RTX_EQ (series_0_m1,
+                simplify_binary_operation (VEC_SERIES, mode, const0_rtx,
+                                           constm1_rtx));
+}
+
+/* Verify some simplifications involving vectors.  */
+
+static void
+test_vector_ops ()
+{
+  for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
+    {
+      machine_mode mode = (machine_mode) i;
+      if (VECTOR_MODE_P (mode))
+       {
+         rtx scalar_reg = make_test_reg (GET_MODE_INNER (mode));
+         test_vector_ops_duplicate (mode, scalar_reg);
+         if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
+             && GET_MODE_NUNITS (mode) > 2)
+           test_vector_ops_series (mode, scalar_reg);
+       }
+    }
+}
+
+template<unsigned int N>
+struct simplify_const_poly_int_tests
+{
+  static void run ();
+};
+
+template<>
+struct simplify_const_poly_int_tests<1>
+{
+  static void run () {}
+};
+
+/* Test various CONST_POLY_INT properties.  */
+
+template<unsigned int N>
+void
+simplify_const_poly_int_tests<N>::run ()
+{
+  rtx x1 = gen_int_mode (poly_int64 (1, 1), QImode);
+  rtx x2 = gen_int_mode (poly_int64 (-80, 127), QImode);
+  rtx x3 = gen_int_mode (poly_int64 (-79, -128), QImode);
+  rtx x4 = gen_int_mode (poly_int64 (5, 4), QImode);
+  rtx x5 = gen_int_mode (poly_int64 (30, 24), QImode);
+  rtx x6 = gen_int_mode (poly_int64 (20, 16), QImode);
+  rtx x7 = gen_int_mode (poly_int64 (7, 4), QImode);
+  rtx x8 = gen_int_mode (poly_int64 (30, 24), HImode);
+  rtx x9 = gen_int_mode (poly_int64 (-30, -24), HImode);
+  rtx x10 = gen_int_mode (poly_int64 (-31, -24), HImode);
+  rtx two = GEN_INT (2);
+  rtx six = GEN_INT (6);
+  poly_uint64 offset = subreg_lowpart_offset (QImode, HImode);
+
+  /* These tests only try limited operation combinations.  Fuller arithmetic
+     testing is done directly on poly_ints.  */
+  ASSERT_EQ (simplify_unary_operation (NEG, HImode, x8, HImode), x9);
+  ASSERT_EQ (simplify_unary_operation (NOT, HImode, x8, HImode), x10);
+  ASSERT_EQ (simplify_unary_operation (TRUNCATE, QImode, x8, HImode), x5);
+  ASSERT_EQ (simplify_binary_operation (PLUS, QImode, x1, x2), x3);
+  ASSERT_EQ (simplify_binary_operation (MINUS, QImode, x3, x1), x2);
+  ASSERT_EQ (simplify_binary_operation (MULT, QImode, x4, six), x5);
+  ASSERT_EQ (simplify_binary_operation (MULT, QImode, six, x4), x5);
+  ASSERT_EQ (simplify_binary_operation (ASHIFT, QImode, x4, two), x6);
+  ASSERT_EQ (simplify_binary_operation (IOR, QImode, x4, two), x7);
+  ASSERT_EQ (simplify_subreg (HImode, x5, QImode, 0), x8);
+  ASSERT_EQ (simplify_subreg (QImode, x8, HImode, offset), x5);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+simplify_rtx_c_tests ()
+{
+  test_vector_ops ();
+  simplify_const_poly_int_tests<NUM_POLY_INT_COEFFS>::run ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */