]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/match.pd
[Ada] Fix documentation for GNAT.Command_Line.Exit_From_Command_Line
[thirdparty/gcc.git] / gcc / match.pd
index f189cb19b4188b400aac00e30961e292151c9910..93dcef9d66db17a1f9afb4f7282a6a113abea96d 100644 (file)
@@ -2,7 +2,7 @@
    This file is consumed by genmatch which produces gimple-match.c
    and generic-match.c from it.
 
-   Copyright (C) 2014-2018 Free Software Foundation, Inc.
+   Copyright (C) 2014-2019 Free Software Foundation, Inc.
    Contributed by Richard Biener <rguenther@suse.de>
    and Prathamesh Kulkarni  <bilbotheelffriend@gmail.com>
 
@@ -29,12 +29,15 @@ along with GCC; see the file COPYING3.  If not see
    integer_each_onep integer_truep integer_nonzerop
    real_zerop real_onep real_minus_onep
    zerop
+   initializer_each_zero_or_onep
    CONSTANT_CLASS_P
    tree_expr_nonnegative_p
    tree_expr_nonzero_p
    integer_valued_real_p
    integer_pow2p
-   HONOR_NANS)
+   uniform_integer_cst_p
+   HONOR_NANS
+   uniform_vector_p)
 
 /* Operator lists.  */
 (define_operator_list tcc_comparison
@@ -74,7 +77,27 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (FLOOR)
 DEFINE_INT_AND_FLOAT_ROUND_FN (CEIL)
 DEFINE_INT_AND_FLOAT_ROUND_FN (ROUND)
 DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
-    
+
+/* Binary operations and their associated IFN_COND_* function.  */
+(define_operator_list UNCOND_BINARY
+  plus minus
+  mult trunc_div trunc_mod rdiv
+  min max
+  bit_and bit_ior bit_xor
+  lshift rshift)
+(define_operator_list COND_BINARY
+  IFN_COND_ADD IFN_COND_SUB
+  IFN_COND_MUL IFN_COND_DIV IFN_COND_MOD IFN_COND_RDIV
+  IFN_COND_MIN IFN_COND_MAX
+  IFN_COND_AND IFN_COND_IOR IFN_COND_XOR
+  IFN_COND_SHL IFN_COND_SHR)
+
+/* Same for ternary operations.  */
+(define_operator_list UNCOND_TERNARY
+  IFN_FMA IFN_FMS IFN_FNMA IFN_FNMS)
+(define_operator_list COND_TERNARY
+  IFN_COND_FMA IFN_COND_FMS IFN_COND_FNMA IFN_COND_FNMS)
+
 /* As opposed to convert?, this still creates a single pattern, so
    it is not a suitable replacement for convert? in all cases.  */
 (match (nop_convert @0)
@@ -88,7 +111,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       && tree_nop_conversion_p (TREE_TYPE (type), TREE_TYPE (TREE_TYPE (@0))))))
 /* This one has to be last, or it shadows the others.  */
 (match (nop_convert @0)
- @0) 
+ @0)
+
+/* Transform likes of (char) ABS_EXPR <(int) x> into (char) ABSU_EXPR <x>
+   ABSU_EXPR returns unsigned absolute value of the operand and the operand
+   of the ABSU_EXPR will have the corresponding signed type.  */
+(simplify (abs (convert @0))
+ (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
+      && !TYPE_UNSIGNED (TREE_TYPE (@0))
+      && element_precision (type) > element_precision (TREE_TYPE (@0)))
+  (with { tree utype = unsigned_type_for (TREE_TYPE (@0)); }
+   (convert (absu:utype @0)))))
+
 
 /* Simplifications of operations with one constant operand and
    simplifications to constants or single values.  */
@@ -121,6 +155,28 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (if (fold_real_zero_addition_p (type, @1, 1))
   (non_lvalue @0)))
 
+/* Even if the fold_real_zero_addition_p can't simplify X + 0.0
+   into X, we can optimize (X + 0.0) + 0.0 or (X + 0.0) - 0.0
+   or (X - 0.0) + 0.0 into X + 0.0 and (X - 0.0) - 0.0 into X - 0.0
+   if not -frounding-math.  For sNaNs the first operation would raise
+   exceptions but turn the result into qNan, so the second operation
+   would not raise it.   */
+(for inner_op (plus minus)
+ (for outer_op (plus minus)
+  (simplify
+   (outer_op (inner_op@3 @0 REAL_CST@1) REAL_CST@2)
+    (if (real_zerop (@1)
+        && real_zerop (@2)
+        && !HONOR_SIGN_DEPENDENT_ROUNDING (type))
+     (with { bool inner_plus = ((inner_op == PLUS_EXPR)
+                               ^ REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@1)));
+            bool outer_plus
+              = ((outer_op == PLUS_EXPR)
+                 ^ REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@2))); }
+      (if (outer_plus && !inner_plus)
+       (outer_op @0 @2)
+       @3))))))
+
 /* Simplify x - x.
    This is unsafe for certain floats even in non-IEEE formats.
    In IEEE, it is unsafe because it does wrong for NaNs.
@@ -164,6 +220,30 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
            || !COMPLEX_FLOAT_TYPE_P (type)))
    (negate @0)))
 
+/* Transform { 0 or 1 } * { 0 or 1 } into { 0 or 1 } & { 0 or 1 } */
+(simplify
+ (mult SSA_NAME@1 SSA_NAME@2)
+  (if (INTEGRAL_TYPE_P (type)
+       && get_nonzero_bits (@1) == 1
+       && get_nonzero_bits (@2) == 1)
+   (bit_and @1 @2)))
+
+/* Transform x * { 0 or 1, 0 or 1, ... } into x & { 0 or -1, 0 or -1, ...},
+   unless the target has native support for the former but not the latter.  */
+(simplify
+ (mult @0 VECTOR_CST@1)
+ (if (initializer_each_zero_or_onep (@1)
+      && !HONOR_SNANS (type)
+      && !HONOR_SIGNED_ZEROS (type))
+  (with { tree itype = FLOAT_TYPE_P (type) ? unsigned_type_for (type) : type; }
+   (if (itype
+       && (!VECTOR_MODE_P (TYPE_MODE (type))
+           || (VECTOR_MODE_P (TYPE_MODE (itype))
+               && optab_handler (and_optab,
+                                 TYPE_MODE (itype)) != CODE_FOR_nothing)))
+    (view_convert (bit_and:itype (view_convert @0)
+                                (ne @1 { build_zero_cst (type); })))))))
+
 (for cmp (gt ge lt le)
      outp (convert convert negate negate)
      outn (negate negate convert convert)
@@ -256,7 +336,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
      And not for _Fract types where we can't build 1.  */
   (if (!integer_zerop (@0) && !ALL_FRACT_MODE_P (TYPE_MODE (type)))
    { build_one_cst (type); }))
- /* X / abs (X) is X < 0 ? -1 : 1.  */ 
+ /* X / abs (X) is X < 0 ? -1 : 1.  */
  (simplify
    (div:C @0 (abs @0))
    (if (INTEGRAL_TYPE_P (type)
@@ -282,30 +362,32 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    and floor_div is trickier and combining round_div even more so.  */
 (for div (trunc_div exact_div)
  (simplify
-  (div (div @0 INTEGER_CST@1) INTEGER_CST@2)
+  (div (div@3 @0 INTEGER_CST@1) INTEGER_CST@2)
   (with {
-    bool overflow_p;
+    wi::overflow_type overflow;
     wide_int mul = wi::mul (wi::to_wide (@1), wi::to_wide (@2),
-                           TYPE_SIGN (type), &overflow_p);
+                           TYPE_SIGN (type), &overflow);
    }
-   (if (!overflow_p)
-    (div @0 { wide_int_to_tree (type, mul); })
-    (if (TYPE_UNSIGNED (type)
-        || mul != wi::min_value (TYPE_PRECISION (type), SIGNED))
-     { build_zero_cst (type); })))))
+   (if (div == EXACT_DIV_EXPR
+       || optimize_successive_divisions_p (@2, @3))
+    (if (!overflow)
+     (div @0 { wide_int_to_tree (type, mul); })
+     (if (TYPE_UNSIGNED (type)
+         || mul != wi::min_value (TYPE_PRECISION (type), SIGNED))
+      { build_zero_cst (type); }))))))
 
 /* Combine successive multiplications.  Similar to above, but handling
    overflow is different.  */
 (simplify
  (mult (mult @0 INTEGER_CST@1) INTEGER_CST@2)
  (with {
-   bool overflow_p;
+   wi::overflow_type overflow;
    wide_int mul = wi::mul (wi::to_wide (@1), wi::to_wide (@2),
-                          TYPE_SIGN (type), &overflow_p);
+                          TYPE_SIGN (type), &overflow);
   }
   /* Skip folding on overflow: the only special case is @1 * @2 == -INT_MIN,
      otherwise undefined overflow implies that @0 must be zero.  */
-  (if (!overflow_p || TYPE_OVERFLOW_WRAPS (type))
+  (if (!overflow || TYPE_OVERFLOW_WRAPS (type))
    (mult @0 { wide_int_to_tree (type, mul); }))))
 
 /* Optimize A / A to 1.0 if we don't care about
@@ -376,6 +458,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (rdiv @0 (negate @1))
  (rdiv (negate @0) @1))
 
+(if (flag_unsafe_math_optimizations)
+ /* Simplify (C / x op 0.0) to x op 0.0 for C != 0, C != Inf/Nan.
+    Since C / x may underflow to zero, do this only for unsafe math.  */
+ (for op (lt le gt ge)
+      neg_op (gt ge lt le)
+  (simplify
+   (op (rdiv REAL_CST@0 @1) real_zerop@2)
+   (if (!HONOR_SIGNED_ZEROS (@1) && !HONOR_INFINITIES (@1))
+    (switch
+     (if (real_less (&dconst0, TREE_REAL_CST_PTR (@0)))
+      (op @1 @2))
+     /* For C < 0, use the inverted operator.  */
+     (if (real_less (TREE_REAL_CST_PTR (@0), &dconst0))
+      (neg_op @1 @2)))))))
+
 /* Optimize (X & (-A)) / A where A is a power of 2, to X >> log2(A) */
 (for div (trunc_div ceil_div floor_div round_div exact_div)
  (simplify
@@ -441,7 +538,15 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        && TYPE_OVERFLOW_UNDEFINED (type)
        && wi::multiple_of_p (wi::to_wide (@1), wi::to_wide (@2),
                             TYPE_SIGN (type)))
-   { build_zero_cst (type); })))
+   { build_zero_cst (type); }))
+ /* For (X % C) == 0, if X is signed and C is power of 2, use unsigned
+    modulo and comparison, since it is simpler and equivalent.  */
+ (for cmp (eq ne)
+  (simplify
+   (cmp (mod @0 integer_pow2p@2) integer_zerop@1)
+   (if (!TYPE_UNSIGNED (TREE_TYPE (@0)))
+    (with { tree utype = unsigned_type_for (TREE_TYPE (@0)); }
+     (cmp (mod (convert:utype @0) (convert:utype @2)) (convert:utype @1)))))))
 
 /* X % -C is the same as X % C.  */
 (simplify
@@ -510,7 +615,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* Simplify (t * 2) / 2) -> t.  */
 (for div (trunc_div ceil_div floor_div round_div exact_div)
  (simplify
-  (div (mult @0 @1) @1)
+  (div (mult:c @0 @1) @1)
   (if (ANY_INTEGRAL_TYPE_P (type)
        && TYPE_OVERFLOW_UNDEFINED (type))
    @0)))
@@ -553,6 +658,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (mult (abs@1 @0) @1)
  (mult @0 @0))
 
+/* Convert absu(x)*absu(x) -> x*x.  */
+(simplify
+ (mult (absu@1 @0) @1)
+ (mult (convert@2 @0) @2))
+
 /* cos(copysign(x, y)) -> cos(x).  Similarly for cosh.  */
 (for coss (COS COSH)
      copysigns (COPYSIGN)
@@ -747,6 +857,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (bit_not (bit_and:cs (bit_not @0) @1))
  (bit_ior @0 (bit_not @1)))
 
+/* ~(~a | b) --> a & ~b */
+(simplify
+ (bit_not (bit_ior:cs (bit_not @0) @1))
+ (bit_and @0 (bit_not @1)))
+
 /* Simplify (~X & Y) to X ^ Y if we know that (X & ~Y) is 0.  */
 #if GIMPLE
 (simplify
@@ -756,6 +871,60 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (bit_xor @0 @1)))
 #endif
 
+/* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M,
+   ((A & N) + B) & M -> (A + B) & M
+   Similarly if (N & M) == 0,
+   ((A | N) + B) & M -> (A + B) & M
+   and for - instead of + (or unary - instead of +)
+   and/or ^ instead of |.
+   If B is constant and (B & M) == 0, fold into A & M.  */
+(for op (plus minus)
+ (for bitop (bit_and bit_ior bit_xor)
+  (simplify
+   (bit_and (op:s (bitop:s@0 @3 INTEGER_CST@4) @1) INTEGER_CST@2)
+    (with
+     { tree pmop[2];
+       tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, bitop,
+                                      @3, @4, @1, ERROR_MARK, NULL_TREE,
+                                      NULL_TREE, pmop); }
+     (if (utype)
+      (convert (bit_and (op (convert:utype { pmop[0]; })
+                           (convert:utype { pmop[1]; }))
+                       (convert:utype @2))))))
+  (simplify
+   (bit_and (op:s @0 (bitop:s@1 @3 INTEGER_CST@4)) INTEGER_CST@2)
+    (with
+     { tree pmop[2];
+       tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, ERROR_MARK,
+                                      NULL_TREE, NULL_TREE, @1, bitop, @3,
+                                      @4, pmop); }
+     (if (utype)
+      (convert (bit_and (op (convert:utype { pmop[0]; })
+                           (convert:utype { pmop[1]; }))
+                       (convert:utype @2)))))))
+ (simplify
+  (bit_and (op:s @0 @1) INTEGER_CST@2)
+   (with
+    { tree pmop[2];
+      tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, ERROR_MARK,
+                                     NULL_TREE, NULL_TREE, @1, ERROR_MARK,
+                                     NULL_TREE, NULL_TREE, pmop); }
+    (if (utype)
+     (convert (bit_and (op (convert:utype { pmop[0]; })
+                          (convert:utype { pmop[1]; }))
+                      (convert:utype @2)))))))
+(for bitop (bit_and bit_ior bit_xor)
+ (simplify
+  (bit_and (negate:s (bitop:s@0 @2 INTEGER_CST@3)) INTEGER_CST@1)
+   (with
+    { tree pmop[2];
+      tree utype = fold_bit_and_mask (TREE_TYPE (@0), @1, NEGATE_EXPR, @0,
+                                     bitop, @2, @3, NULL_TREE, ERROR_MARK,
+                                     NULL_TREE, NULL_TREE, pmop); }
+    (if (utype)
+     (convert (bit_and (negate (convert:utype { pmop[0]; }))
+                      (convert:utype @1)))))))
+
 /* X % Y is smaller than Y.  */
 (for cmp (lt ge)
  (simplify
@@ -833,6 +1002,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (bitop:c @0 (bit_not (bitop:cs @0 @1)))
   (bitop @0 (bit_not @1))))
 
+/* (~x & y) | ~(x | y) -> ~x */
+(simplify
+ (bit_ior:c (bit_and:c (bit_not@2 @0) @1) (bit_not (bit_ior:c @0 @1)))
+ @2)
+
+/* (x | y) ^ (x | ~y) -> ~x */
+(simplify
+ (bit_xor:c (bit_ior:c @0 @1) (bit_ior:c @0 (bit_not @1)))
+ (bit_not @0))
+
+/* (x & y) | ~(x | y) -> ~(x ^ y) */
+(simplify
+ (bit_ior:c (bit_and:s @0 @1) (bit_not:s (bit_ior:s @0 @1)))
+ (bit_not (bit_xor @0 @1)))
+
+/* (~x | y) ^ (x ^ y) -> x | ~y */
+(simplify
+ (bit_xor:c (bit_ior:cs (bit_not @0) @1) (bit_xor:s @0 @1))
+ (bit_ior @0 (bit_not @1)))
+
+/* (x ^ y) | ~(x | y) -> ~(x & y) */
+(simplify
+ (bit_ior:c (bit_xor:s @0 @1) (bit_not:s (bit_ior:s @0 @1)))
+ (bit_not (bit_and @0 @1)))
+
 /* (x | y) & ~x -> y & ~x */
 /* (x & y) | ~x -> y | ~x */
 (for bitop (bit_and bit_ior)
@@ -898,6 +1092,16 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (bit_and:c (bit_ior:c @0 @1) (bit_xor:c @1 (bit_not @0)))
  (bit_and @0 @1))
 
+/* (~x | y) & (x | ~y) -> ~(x ^ y) */
+(simplify
+ (bit_and (bit_ior:cs (bit_not @0) @1) (bit_ior:cs @0 (bit_not @1)))
+ (bit_not (bit_xor @0 @1)))
+
+/* (~x | y) ^ (x | ~y) -> x ^ y */
+(simplify
+ (bit_xor (bit_ior:c (bit_not @0) @1) (bit_ior:c @0 (bit_not @1)))
+ (bit_xor @0 @1))
+
 /* ~x & ~y -> ~(x | y)
    ~x | ~y -> ~(x & y) */
 (for op (bit_and bit_ior)
@@ -944,7 +1148,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 (for opo (bit_and bit_xor)
      opi (bit_xor bit_and)
  (simplify
-  (opo:c (opi:c @0 @1) @1) 
+  (opo:c (opi:cs @0 @1) @1)
   (bit_and (bit_not @0) @1)))
 
 /* Given a bit-wise operation CODE applied to ARG0 and ARG1, see if both
@@ -990,16 +1194,35 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       && tree_nop_conversion_p (type, TREE_TYPE (@2)))
   (bit_xor (convert @1) (convert @2))))
 
+/* Convert abs (abs (X)) into abs (X).
+   also absu (absu (X)) into absu (X).  */
 (simplify
  (abs (abs@1 @0))
  @1)
+
+(simplify
+ (absu (convert@2 (absu@1 @0)))
+ (if (tree_nop_conversion_p (TREE_TYPE (@2), TREE_TYPE (@1)))
+  @1))
+
+/* Convert abs[u] (-X) -> abs[u] (X).  */
 (simplify
  (abs (negate @0))
  (abs @0))
+
+(simplify
+ (absu (negate @0))
+ (absu @0))
+
+/* Convert abs[u] (X)  where X is nonnegative -> (X).  */
 (simplify
  (abs tree_expr_nonnegative_p@0)
  @0)
 
+(simplify
+ (absu tree_expr_nonnegative_p@0)
+ (convert @0))
+
 /* A few cases of fold-const.c negate_expr_p predicate.  */
 (match negate_expr_p
  INTEGER_CST
@@ -1033,7 +1256,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (tree_nop_conversion_p (type, TREE_TYPE (@0))
        && tree_nop_conversion_p (type, TREE_TYPE (@1)))
    (mult (convert @0) (convert (negate @1)))))
+
 /* -(A + B) -> (-B) - A.  */
 (simplify
  (negate (plus:c @0 negate_expr_p@1))
@@ -1283,9 +1506,26 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* X / 4 < Y / 4 iff X < Y when the division is known to be exact.  */
 (for cmp (simple_comparison)
  (simplify
-  (cmp (exact_div @0 INTEGER_CST@2) (exact_div @1 @2))
-  (if (wi::gt_p (wi::to_wide (@2), 0, TYPE_SIGN (TREE_TYPE (@2))))
-   (cmp @0 @1))))
+  (cmp (convert?@3 (exact_div @0 INTEGER_CST@2)) (convert? (exact_div @1 @2)))
+  (if (element_precision (@3) >= element_precision (@0)
+       && types_match (@0, @1))
+   (if (wi::lt_p (wi::to_wide (@2), 0, TYPE_SIGN (TREE_TYPE (@2))))
+    (if (!TYPE_UNSIGNED (TREE_TYPE (@3)))
+     (cmp @1 @0)
+     (if (tree_expr_nonzero_p (@0) && tree_expr_nonzero_p (@1))
+      (with
+       {
+       tree utype = unsigned_type_for (TREE_TYPE (@0));
+       }
+       (cmp (convert:utype @1) (convert:utype @0)))))
+    (if (wi::gt_p (wi::to_wide (@2), 1, TYPE_SIGN (TREE_TYPE (@2))))
+     (if (TYPE_UNSIGNED (TREE_TYPE (@0)) || !TYPE_UNSIGNED (TREE_TYPE (@3)))
+      (cmp @0 @1)
+      (with
+       {
+       tree utype = unsigned_type_for (TREE_TYPE (@0));
+       }
+       (cmp (convert:utype @0) (convert:utype @1)))))))))
 
 /* X / C1 op C2 into a simple range test.  */
 (for cmp (simple_comparison)
@@ -1417,6 +1657,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (op:c (plus:c@2 @0 @1) @1)
   (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
        && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0))
        && (CONSTANT_CLASS_P (@0) || single_use (@2)))
    (op @0 { build_zero_cst (TREE_TYPE (@0)); }))))
 /* For equality, this is also true with wrapping overflow.  */
@@ -1450,14 +1691,15 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (op @1 { build_zero_cst (TREE_TYPE (@1)); }))))
 
 /* Transform:
- * (X / Y) == 0 -> X < Y if X, Y are unsigned.
- * (X / Y) != 0 -> X >= Y, if X, Y are unsigned.
- */
+   (X / Y) == 0 -> X < Y if X, Y are unsigned.
+   (X / Y) != 0 -> X >= Y, if X, Y are unsigned.  */
 (for cmp (eq ne)
      ocmp (lt ge)
  (simplify
   (cmp (trunc_div @0 @1) integer_zerop)
   (if (TYPE_UNSIGNED (TREE_TYPE (@0))
+       /* Complex ==/!= is allowed, but not </>=.  */
+       && TREE_CODE (TREE_TYPE (@0)) != COMPLEX_TYPE
        && (VECTOR_TYPE_P (type) || !VECTOR_TYPE_P (TREE_TYPE (@0))))
    (ocmp @0 @1))))
 
@@ -1589,14 +1831,14 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (if (ptr_difference_const (@0, @1, &diff))
     { build_int_cst_type (type, diff); }))))
 (simplify
- (pointer_diff (convert?@2 ADDR_EXPR@0) (convert?@3 @1))
+ (pointer_diff (convert?@2 ADDR_EXPR@0) (convert1?@3 @1))
  (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0))
       && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1)))
   (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @1, &diff))
     { build_int_cst_type (type, diff); }))))
 (simplify
- (pointer_diff (convert?@2 @0) (convert?@3 ADDR_EXPR@1))
+ (pointer_diff (convert?@2 @0) (convert1?@3 ADDR_EXPR@1))
  (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0))
       && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1)))
   (with { poly_int64 diff; }
@@ -1733,9 +1975,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
               CONSTANT_CLASS_P@2)
      /* If one of the types wraps, use that one.  */
      (if (!ANY_INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_WRAPS (type))
-      (if (outer_op == PLUS_EXPR)
-       (plus (view_convert @0) (inner_op @2 (view_convert @1)))
-       (minus (view_convert @0) (neg_inner_op @2 (view_convert @1))))
+      /* If all 3 captures are CONSTANT_CLASS_P, punt, as we might recurse
+        forever if something doesn't simplify into a constant.  */
+      (if (!CONSTANT_CLASS_P (@0))
+       (if (outer_op == PLUS_EXPR)
+       (plus (view_convert @0) (inner_op @2 (view_convert @1)))
+       (minus (view_convert @0) (neg_inner_op @2 (view_convert @1)))))
       (if (!ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
           || TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
        (if (outer_op == PLUS_EXPR)
@@ -1756,10 +2001,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
           (neg_inner_op @0 { wide_int_to_tree (type, wi::to_wide (cst)); })
           /* Last resort, use some unsigned type.  */
           (with { tree utype = unsigned_type_for (type); }
-           (view_convert (inner_op
-                          (view_convert:utype @0)
-                          (view_convert:utype
-                           { drop_tree_overflow (cst); })))))))))))))
+           (if (utype)
+            (view_convert (inner_op
+                           (view_convert:utype @0)
+                           (view_convert:utype
+                            { drop_tree_overflow (cst); }))))))))))))))
 
   /* (CST1 - A) +- CST2 -> CST3 - A  */
   (for outer_op (plus minus)
@@ -1832,7 +2078,10 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     /* The second argument of pointer_plus must be interpreted as signed, and
        thus sign-extended if necessary.  */
     (with { tree stype = signed_type_for (TREE_TYPE (@1)); }
-     (convert (convert:stype @1))))
+     /* Use view_convert instead of convert here, as POINTER_PLUS_EXPR
+       second arg is unsigned even when we need to consider it as signed,
+       we don't want to diagnose overflow here.  */
+     (convert (view_convert:stype @1))))
 
   /* (T)P - (T)(P + A) -> -(T) A */
   (simplify
@@ -1876,7 +2125,10 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     /* The second argument of pointer_plus must be interpreted as signed, and
        thus sign-extended if necessary.  */
     (with { tree stype = signed_type_for (TREE_TYPE (@1)); }
-     (negate (convert (convert:stype @1)))))
+     /* Use view_convert instead of convert here, as POINTER_PLUS_EXPR
+       second arg is unsigned even when we need to consider it as signed,
+       we don't want to diagnose overflow here.  */
+     (negate (convert (view_convert:stype @1)))))
 
   /* (T)(P + A) - (T)(P + B) -> (T)A - (T)B */
   (simplify
@@ -1927,8 +2179,48 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     /* The second argument of pointer_plus must be interpreted as signed, and
        thus sign-extended if necessary.  */
     (with { tree stype = signed_type_for (TREE_TYPE (@1)); }
-     (minus (convert (convert:stype @1)) (convert (convert:stype @2)))))))
-
+     /* Use view_convert instead of convert here, as POINTER_PLUS_EXPR
+       second arg is unsigned even when we need to consider it as signed,
+       we don't want to diagnose overflow here.  */
+     (minus (convert (view_convert:stype @1))
+           (convert (view_convert:stype @2)))))))
+
+/* (A * C) +- (B * C) -> (A+-B) * C and (A * C) +- A -> A * (C+-1).
+    Modeled after fold_plusminus_mult_expr.  */
+(if (!TYPE_SATURATING (type)
+     && (!FLOAT_TYPE_P (type) || flag_associative_math))
+ (for plusminus (plus minus)
+  (simplify
+   (plusminus (mult:cs@3 @0 @1) (mult:cs@4 @0 @2))
+   (if ((!ANY_INTEGRAL_TYPE_P (type)
+        || TYPE_OVERFLOW_WRAPS (type)
+        || (INTEGRAL_TYPE_P (type)
+            && tree_expr_nonzero_p (@0)
+            && expr_not_equal_to (@0, wi::minus_one (TYPE_PRECISION (type)))))
+       /* If @1 +- @2 is constant require a hard single-use on either
+          original operand (but not on both).  */
+       && (single_use (@3) || single_use (@4)))
+    (mult (plusminus @1 @2) @0)))
+  /* We cannot generate constant 1 for fract.  */
+  (if (!ALL_FRACT_MODE_P (TYPE_MODE (type)))
+   (simplify
+    (plusminus @0 (mult:c@3 @0 @2))
+    (if ((!ANY_INTEGRAL_TYPE_P (type)
+         || TYPE_OVERFLOW_WRAPS (type)
+         || (INTEGRAL_TYPE_P (type)
+             && tree_expr_nonzero_p (@0)
+             && expr_not_equal_to (@0, wi::minus_one (TYPE_PRECISION (type)))))
+        && single_use (@3))
+     (mult (plusminus { build_one_cst (type); } @2) @0)))
+   (simplify
+    (plusminus (mult:c@3 @0 @2) @0)
+    (if ((!ANY_INTEGRAL_TYPE_P (type)
+         || TYPE_OVERFLOW_WRAPS (type)
+         || (INTEGRAL_TYPE_P (type)
+             && tree_expr_nonzero_p (@0)
+             && expr_not_equal_to (@0, wi::minus_one (TYPE_PRECISION (type)))))
+        && single_use (@3))
+     (mult (plusminus @2 { build_one_cst (type); }) @0))))))
 
 /* Simplifications of MIN_EXPR, MAX_EXPR, fmin() and fmax().  */
 
@@ -2354,6 +2646,13 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
               && TYPE_UNSIGNED (TREE_TYPE (@1)))))
    (view_convert @1)))
 
+/* Simplify a view-converted empty constructor.  */
+(simplify
+  (view_convert CONSTRUCTOR@0)
+  (if (TREE_CODE (@0) != SSA_NAME
+       && CONSTRUCTOR_NELTS (@0) == 0)
+   { build_zero_cst (type); }))
+
 /* Re-association barriers around constants and other re-association
    barriers can be removed.  */
 (simplify
@@ -2487,6 +2786,32 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (mult (convert1? (exact_div @0 @@1)) (convert2? @1))
   (convert @0))
 
+/* Simplify (A / B) * B + (A % B) -> A.  */
+(for div (trunc_div ceil_div floor_div round_div)
+     mod (trunc_mod ceil_mod floor_mod round_mod)
+  (simplify
+   (plus:c (mult:c (div @0 @1) @1) (mod @0 @1))
+   @0))
+
+/* ((X /[ex] A) +- B) * A  -->  X +- A * B.  */
+(for op (plus minus)
+ (simplify
+  (mult (convert1? (op (convert2? (exact_div @0 INTEGER_CST@@1)) INTEGER_CST@2)) @1)
+  (if (tree_nop_conversion_p (type, TREE_TYPE (@2))
+       && tree_nop_conversion_p (TREE_TYPE (@0), TREE_TYPE (@2)))
+   (with
+     {
+       wi::overflow_type overflow;
+       wide_int mul = wi::mul (wi::to_wide (@1), wi::to_wide (@2),
+                              TYPE_SIGN (type), &overflow);
+     }
+     (if (types_match (type, TREE_TYPE (@2))
+        && types_match (TREE_TYPE (@0), TREE_TYPE (@2)) && !overflow)
+      (op @0 { wide_int_to_tree (type, mul); })
+      (with { tree utype = unsigned_type_for (type); }
+       (convert (op (convert:utype @0)
+                   (mult (convert:utype @1) (convert:utype @2))))))))))
+
 /* Canonicalization of binary operations.  */
 
 /* Convert X + -C into X - C.  */
@@ -2529,8 +2854,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* Reassociate (X * CST) * Y to (X * Y) * CST.  This does not introduce
    signed overflow for CST != 0 && CST != -1.  */
 (simplify
- (mult:c (mult:s @0 INTEGER_CST@1) @2)
+ (mult:c (mult:s@3 @0 INTEGER_CST@1) @2)
  (if (TREE_CODE (@2) != INTEGER_CST
+      && single_use (@3)
       && !integer_zerop (@1) && !integer_minus_onep (@1))
   (mult (mult @0 @2) @1)))
 
@@ -2620,6 +2946,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (integer_zerop (@0))
    @2)))
 
+/* Sink unary operations to constant branches, but only if we do fold it to
+   constants.  */
+(for op (negate bit_not abs absu)
+ (simplify
+  (op (vec_cond @0 VECTOR_CST@1 VECTOR_CST@2))
+  (with
+   {
+     tree cst1, cst2;
+     cst1 = const_unop (op, type, @1);
+     if (cst1)
+       cst2 = const_unop (op, type, @2);
+   }
+   (if (cst1 && cst2)
+    (vec_cond @0 { cst1; } { cst2; })))))
+
 /* Simplification moved from fold_cond_expr_with_comparison.  It may also
    be extended.  */
 /* This pattern implements two kinds simplification:
@@ -2732,7 +3073,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
             && (cmp == LT_EXPR || cmp == GE_EXPR)))
      (with
       {
-       bool overflow = false;
+       wi::overflow_type overflow = wi::OVF_NONE;
        enum tree_code code, cmp_code = cmp;
        wide_int real_c1;
        wide_int c1 = wi::to_wide (@1);
@@ -2816,21 +3157,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     from if-conversion.  */
  (simplify
   (cnd @0 @1 (cnd @2 @3 @4))
-  (if (COMPARISON_CLASS_P (@0)
-       && COMPARISON_CLASS_P (@2)
-       && invert_tree_comparison
-           (TREE_CODE (@0), HONOR_NANS (TREE_OPERAND (@0, 0))) == TREE_CODE (@2)
-       && operand_equal_p (TREE_OPERAND (@0, 0), TREE_OPERAND (@2, 0), 0)
-       && operand_equal_p (TREE_OPERAND (@0, 1), TREE_OPERAND (@2, 1), 0))
+  (if (inverse_conditions_p (@0, @2))
    (cnd @0 @1 @3)))
  (simplify
   (cnd @0 (cnd @1 @2 @3) @4)
-  (if (COMPARISON_CLASS_P (@0)
-       && COMPARISON_CLASS_P (@1)
-       && invert_tree_comparison
-           (TREE_CODE (@0), HONOR_NANS (TREE_OPERAND (@0, 0))) == TREE_CODE (@1)
-       && operand_equal_p (TREE_OPERAND (@0, 0), TREE_OPERAND (@1, 0), 0)
-       && operand_equal_p (TREE_OPERAND (@0, 1), TREE_OPERAND (@1, 1), 0))
+  (if (inverse_conditions_p (@0, @1))
    (cnd @0 @3 @4)))
 
  /* A ? B : B -> B.  */
@@ -2875,16 +3206,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 (for cmp  (le gt)
      acmp (lt ge)
  (simplify
-  (cmp @0 INTEGER_CST@1)
-  (if (tree_int_cst_sgn (@1) == -1)
-   (acmp @0 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) + 1); }))))
+  (cmp @0 uniform_integer_cst_p@1)
+  (with { tree cst = uniform_integer_cst_p (@1); }
+   (if (tree_int_cst_sgn (cst) == -1)
+     (acmp @0 { build_uniform_cst (TREE_TYPE (@1),
+                                  wide_int_to_tree (TREE_TYPE (cst),
+                                                    wi::to_wide (cst)
+                                                    + 1)); })))))
 (for cmp  (ge lt)
      acmp (gt le)
  (simplify
-  (cmp @0 INTEGER_CST@1)
-  (if (tree_int_cst_sgn (@1) == 1)
-   (acmp @0 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) - 1); }))))
-
+  (cmp @0 uniform_integer_cst_p@1)
+  (with { tree cst = uniform_integer_cst_p (@1); }
+   (if (tree_int_cst_sgn (cst) == 1)
+    (acmp @0 { build_uniform_cst (TREE_TYPE (@1),
+                                 wide_int_to_tree (TREE_TYPE (cst),
+                                 wi::to_wide (cst) - 1)); })))))
 
 /* We can simplify a logical negation of a comparison to the
    inverted comparison.  As we cannot compute an expression
@@ -2951,7 +3288,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (if (tree_int_cst_sgn (@1) < 0)
      (scmp @0 @2)
      (cmp @0 @2))))))
+
 /* Simplify comparison of something with itself.  For IEEE
    floating-point, we can only do some of these simplifications.  */
 (for cmp (eq ge le)
@@ -3022,11 +3359,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
         }
       tree newtype
         = (TYPE_PRECISION (TREE_TYPE (@0)) > TYPE_PRECISION (type1)
-          ? TREE_TYPE (@0) : type1); 
+          ? TREE_TYPE (@0) : type1);
     }
     (if (TYPE_PRECISION (TREE_TYPE (@2)) > TYPE_PRECISION (newtype))
      (cmp (convert:newtype @0) (convert:newtype @1))))))
+
  (simplify
   (cmp @0 REAL_CST@1)
   /* IEEE doesn't distinguish +0 and -0 in comparisons.  */
@@ -3050,18 +3387,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
          code = swap_tree_comparison (code);
      }
      (switch
-      /* x > +Inf is always false, if with ignore sNANs.  */
+      /* x > +Inf is always false, if we ignore NaNs or exceptions.  */
       (if (code == GT_EXPR
-          && ! HONOR_SNANS (@0))
+          && !(HONOR_NANS (@0) && flag_trapping_math))
        { constant_boolean_node (false, type); })
       (if (code == LE_EXPR)
-       /* x <= +Inf is always true, if we don't case about NaNs.  */
+       /* x <= +Inf is always true, if we don't care about NaNs.  */
        (if (! HONOR_NANS (@0))
        { constant_boolean_node (true, type); }
-       /* x <= +Inf is the same as x == x, i.e. !isnan(x).  */
-       (eq @0 @0)))
-      /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX.  */
-      (if (code == EQ_EXPR || code == GE_EXPR)
+       /* x <= +Inf is the same as x == x, i.e. !isnan(x), but this loses
+          an "invalid" exception.  */
+       (if (!flag_trapping_math)
+        (eq @0 @0))))
+      /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX, but
+        for == this introduces an exception for x a NaN.  */
+      (if ((code == EQ_EXPR && !(HONOR_NANS (@0) && flag_trapping_math))
+          || code == GE_EXPR)
        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
        (if (neg)
         (lt @0 { build_real (TREE_TYPE (@0), max); })
@@ -3072,7 +3413,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (if (neg)
         (ge @0 { build_real (TREE_TYPE (@0), max); })
         (le @0 { build_real (TREE_TYPE (@0), max); }))))
-      /* x != +Inf is always equal to !(x > DBL_MAX).  */
+      /* x != +Inf is always equal to !(x > DBL_MAX), but this introduces
+        an exception for x a NaN so use an unordered comparison.  */
       (if (code == NE_EXPR)
        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
        (if (! HONOR_NANS (@0))
@@ -3080,10 +3422,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
          (ge @0 { build_real (TREE_TYPE (@0), max); })
          (le @0 { build_real (TREE_TYPE (@0), max); }))
         (if (neg)
-         (bit_xor (lt @0 { build_real (TREE_TYPE (@0), max); })
-          { build_one_cst (type); })
-         (bit_xor (gt @0 { build_real (TREE_TYPE (@0), max); })
-          { build_one_cst (type); }))))))))))
+         (unge @0 { build_real (TREE_TYPE (@0), max); })
+         (unle @0 { build_real (TREE_TYPE (@0), max); }))))))))))
 
  /* If this is a comparison of a real constant with a PLUS_EXPR
     or a MINUS_EXPR of a real constant, we can convert it into a
@@ -3205,6 +3545,35 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (if (! HONOR_NANS (@0))
        (cmp @0 @1))))))
 
+/* Optimize various special cases of (FTYPE) N CMP (FTYPE) M.  */
+(for cmp  (lt le eq ne ge gt unordered ordered unlt unle ungt unge uneq ltgt)
+     icmp (lt le eq ne ge gt unordered ordered lt   le   gt   ge   eq   ne)
+ (simplify
+  (cmp (float@0 @1) (float @2))
+   (if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (@0))
+       && ! DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@0)))
+    (with
+     {
+       format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@0))));
+       tree type1 = TREE_TYPE (@1);
+       bool type1_signed_p = TYPE_SIGN (type1) == SIGNED;
+       tree type2 = TREE_TYPE (@2);
+       bool type2_signed_p = TYPE_SIGN (type2) == SIGNED;
+     }
+     (if (fmt.can_represent_integral_type_p (type1)
+         && fmt.can_represent_integral_type_p (type2))
+      (if (cmp == ORDERED_EXPR || cmp == UNORDERED_EXPR)
+       { constant_boolean_node (cmp == ORDERED_EXPR, type); }
+       (if (TYPE_PRECISION (type1) > TYPE_PRECISION (type2)
+            && type1_signed_p >= type2_signed_p)
+        (icmp @1 (convert @2))
+        (if (TYPE_PRECISION (type1) < TYPE_PRECISION (type2)
+             && type1_signed_p <= type2_signed_p)
+         (icmp (convert:type2 @1) @2)
+         (if (TYPE_PRECISION (type1) == TYPE_PRECISION (type2)
+              && type1_signed_p == type2_signed_p)
+         (icmp @1 @2))))))))))
+
 /* Optimize various special cases of (FTYPE) N CMP CST.  */
 (for cmp  (lt le eq ne ge gt)
      icmp (le le eq ne ge ge)
@@ -3215,7 +3584,6 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (with
      {
        tree itype = TREE_TYPE (@0);
-       signop isign = TYPE_SIGN (itype);
        format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@1))));
        const REAL_VALUE_TYPE *cst = TREE_REAL_CST_PTR (@1);
        /* Be careful to preserve any potential exceptions due to
@@ -3225,17 +3593,13 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        bool exception_p
          = real_isnan (cst) && (cst->signalling
                                || (cmp != EQ_EXPR && cmp != NE_EXPR));
-       /* INT?_MIN is power-of-two so it takes
-         only one mantissa bit.  */
-       bool signed_p = isign == SIGNED;
-       bool itype_fits_ftype_p
-        = TYPE_PRECISION (itype) - signed_p <= significand_size (fmt);
      }
      /* TODO: allow non-fitting itype and SNaNs when
        -fno-trapping-math.  */
-     (if (itype_fits_ftype_p && ! exception_p)
+     (if (fmt.can_represent_integral_type_p (itype) && ! exception_p)
       (with
        {
+        signop isign = TYPE_SIGN (itype);
         REAL_VALUE_TYPE imin, imax;
         real_from_integer (&imin, fmt, wi::min_value (itype), isign);
         real_from_integer (&imax, fmt, wi::max_value (itype), isign);
@@ -3271,7 +3635,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
            (FTYPE) N == CST -> 0
            (FTYPE) N != CST -> 1.  */
        (if (cmp == EQ_EXPR || cmp == NE_EXPR)
-        { constant_boolean_node (cmp == NE_EXPR, type); }) 
+        { constant_boolean_node (cmp == NE_EXPR, type); })
        /* Otherwise replace with sensible integer constant.  */
        (with
         {
@@ -3289,7 +3653,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (if (TREE_CODE (@1) == INTEGER_CST)
      (with
       {
-       bool ovf;
+       wi::overflow_type ovf;
        wide_int prod = wi::mul (wi::to_wide (@2), wi::to_wide (@1),
                                 TYPE_SIGN (TREE_TYPE (@1)), &ovf);
       }
@@ -3302,7 +3666,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (wi::gt_p (wi::to_wide (@1), 0, TYPE_SIGN (TREE_TYPE (@1))))
    (with
     {
-      bool ovf;
+      wi::overflow_type ovf;
       wide_int prod = wi::mul (wi::to_wide (@2), wi::to_wide (@1),
                               TYPE_SIGN (TREE_TYPE (@1)), &ovf);
     }
@@ -3312,6 +3676,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
                              != (cmp == LT_EXPR || cmp == LE_EXPR), type); }
      (cmp @0 { wide_int_to_tree (TREE_TYPE (@0), prod); }))))))
 
+/* Fold (size_t)(A /[ex] B) CMP C to (size_t)A CMP (size_t)B * C or A CMP' 0.
+
+   For small C (less than max/B), this is (size_t)A CMP (size_t)B * C.
+   For large C (more than min/B+2^size), this is also true, with the
+   multiplication computed modulo 2^size.
+   For intermediate C, this just tests the sign of A.  */
+(for cmp  (lt le gt ge)
+     cmp2 (ge ge lt lt)
+ (simplify
+  (cmp (convert (exact_div @0 INTEGER_CST@1)) INTEGER_CST@2)
+  (if (tree_nop_conversion_p (TREE_TYPE (@0), TREE_TYPE (@2))
+       && TYPE_UNSIGNED (TREE_TYPE (@2)) && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && wi::gt_p (wi::to_wide (@1), 0, TYPE_SIGN (TREE_TYPE (@1))))
+   (with
+    {
+      tree utype = TREE_TYPE (@2);
+      wide_int denom = wi::to_wide (@1);
+      wide_int right = wi::to_wide (@2);
+      wide_int smax = wi::sdiv_trunc (wi::max_value (TREE_TYPE (@0)), denom);
+      wide_int smin = wi::sdiv_trunc (wi::min_value (TREE_TYPE (@0)), denom);
+      bool small = wi::leu_p (right, smax);
+      bool large = wi::geu_p (right, smin);
+    }
+    (if (small || large)
+     (cmp (convert:utype @0) (mult @2 (convert @1)))
+     (cmp2 @0 { build_zero_cst (TREE_TYPE (@0)); }))))))
+
 /* Unordered tests if either argument is a NaN.  */
 (simplify
  (bit_ior (unordered @0 @0) (unordered @1 @1))
@@ -3407,8 +3798,10 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        /* Disable this optimization if we're casting a function pointer
          type on targets that require function pointer canonicalization.  */
        && !(targetm.have_canonicalize_funcptr_for_compare ()
-           && TREE_CODE (TREE_TYPE (@00)) == POINTER_TYPE
-           && TREE_CODE (TREE_TYPE (TREE_TYPE (@00))) == FUNCTION_TYPE)
+           && ((POINTER_TYPE_P (TREE_TYPE (@00))
+                && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@00))))
+               || (POINTER_TYPE_P (TREE_TYPE (@10))
+                   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@10))))))
        && single_use (@0))
    (if (TYPE_PRECISION (TREE_TYPE (@00)) == TYPE_PRECISION (TREE_TYPE (@0))
        && (TREE_CODE (@10) == INTEGER_CST
@@ -3513,22 +3906,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (simplify
   (cmp (bit_and@2 @0 integer_pow2p@1) @1)
   (icmp @2 { build_zero_cst (TREE_TYPE (@0)); })))
+
 /* If we have (A & C) != 0 ? D : 0 where C and D are powers of 2,
    convert this into a shift followed by ANDing with D.  */
 (simplify
  (cond
   (ne (bit_and @0 integer_pow2p@1) integer_zerop)
-  integer_pow2p@2 integer_zerop)
- (with {
-    int shift = (wi::exact_log2 (wi::to_wide (@2))
-                - wi::exact_log2 (wi::to_wide (@1)));
-  }
-  (if (shift > 0)
-   (bit_and
-    (lshift (convert @0) { build_int_cst (integer_type_node, shift); }) @2)
-   (bit_and
-    (convert (rshift @0 { build_int_cst (integer_type_node, -shift); })) @2))))
+  INTEGER_CST@2 integer_zerop)
+ (if (integer_pow2p (@2))
+  (with {
+     int shift = (wi::exact_log2 (wi::to_wide (@2))
+                 - wi::exact_log2 (wi::to_wide (@1)));
+   }
+   (if (shift > 0)
+    (bit_and
+     (lshift (convert @0) { build_int_cst (integer_type_node, shift); }) @2)
+    (bit_and
+     (convert (rshift @0 { build_int_cst (integer_type_node, -shift); }))
+     @2)))))
 
 /* If we have (A & C) != 0 where C is the sign bit of A, convert
    this into A < 0.  Similarly for (A & C) == 0 into A >= 0.  */
@@ -3548,8 +3943,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 (simplify
  (cond
   (lt @0 integer_zerop)
-  integer_pow2p@1 integer_zerop)
- (if (!TYPE_UNSIGNED (TREE_TYPE (@0)))
+  INTEGER_CST@1 integer_zerop)
+ (if (integer_pow2p (@1)
+      && !TYPE_UNSIGNED (TREE_TYPE (@0)))
   (with {
     int shift = element_precision (@0) - wi::exact_log2 (wi::to_wide (@1)) - 1;
    }
@@ -3606,8 +4002,78 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
                    || TREE_CODE (base1) == SSA_NAME
                    || TREE_CODE (base1) == STRING_CST))
          equal = (base0 == base1);
+       if (equal == 0)
+        {
+          HOST_WIDE_INT ioff0 = -1, ioff1 = -1;
+          off0.is_constant (&ioff0);
+          off1.is_constant (&ioff1);
+          if ((DECL_P (base0) && TREE_CODE (base1) == STRING_CST)
+              || (TREE_CODE (base0) == STRING_CST && DECL_P (base1))
+              || (TREE_CODE (base0) == STRING_CST
+                  && TREE_CODE (base1) == STRING_CST
+                  && ioff0 >= 0 && ioff1 >= 0
+                  && ioff0 < TREE_STRING_LENGTH (base0)
+                  && ioff1 < TREE_STRING_LENGTH (base1)
+                  /* This is a too conservative test that the STRING_CSTs
+                     will not end up being string-merged.  */
+                  && strncmp (TREE_STRING_POINTER (base0) + ioff0,
+                              TREE_STRING_POINTER (base1) + ioff1,
+                              MIN (TREE_STRING_LENGTH (base0) - ioff0,
+                                   TREE_STRING_LENGTH (base1) - ioff1)) != 0))
+            ;
+          else if (!DECL_P (base0) || !DECL_P (base1))
+            equal = 2;
+          else if (cmp != EQ_EXPR && cmp != NE_EXPR)
+            equal = 2;
+          /* If this is a pointer comparison, ignore for now even
+             valid equalities where one pointer is the offset zero
+             of one object and the other to one past end of another one.  */
+          else if (!INTEGRAL_TYPE_P (TREE_TYPE (@2)))
+            ;
+          /* Assume that automatic variables can't be adjacent to global
+             variables.  */
+          else if (is_global_var (base0) != is_global_var (base1))
+            ;
+          else
+            {
+              tree sz0 = DECL_SIZE_UNIT (base0);
+              tree sz1 = DECL_SIZE_UNIT (base1);
+              /* If sizes are unknown, e.g. VLA or not representable,
+                 punt.  */
+              if (!tree_fits_poly_int64_p (sz0)
+                  || !tree_fits_poly_int64_p (sz1))
+                equal = 2;
+              else
+                {
+                  poly_int64 size0 = tree_to_poly_int64 (sz0);
+                  poly_int64 size1 = tree_to_poly_int64 (sz1);
+                  /* If one offset is pointing (or could be) to the beginning
+                     of one object and the other is pointing to one past the
+                     last byte of the other object, punt.  */
+                  if (maybe_eq (off0, 0) && maybe_eq (off1, size1))
+                    equal = 2;
+                  else if (maybe_eq (off1, 0) && maybe_eq (off0, size0))
+                    equal = 2;
+                  /* If both offsets are the same, there are some cases
+                     we know that are ok.  Either if we know they aren't
+                     zero, or if we know both sizes are no zero.  */
+                  if (equal == 2
+                      && known_eq (off0, off1)
+                      && (known_ne (off0, 0)
+                          || (known_ne (size0, 0) && known_ne (size1, 0))))
+                    equal = 0;
+                }
+            }
+        }
      }
-     (if (equal == 1)
+     (if (equal == 1
+         && (cmp == EQ_EXPR || cmp == NE_EXPR
+             /* If the offsets are equal we can ignore overflow.  */
+             || known_eq (off0, off1)
+             || TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
+                /* Or if we compare using pointers to decls or strings.  */
+             || (POINTER_TYPE_P (TREE_TYPE (@2))
+                 && (DECL_P (base0) || TREE_CODE (base0) == STRING_CST))))
       (switch
        (if (cmp == EQ_EXPR && (known_eq (off0, off1) || known_ne (off0, off1)))
        { constant_boolean_node (known_eq (off0, off1), type); })
@@ -3621,16 +4087,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        { constant_boolean_node (known_ge (off0, off1), type); })
        (if (cmp == GT_EXPR && (known_gt (off0, off1) || known_le (off0, off1)))
        { constant_boolean_node (known_gt (off0, off1), type); }))
-      (if (equal == 0
-          && DECL_P (base0) && DECL_P (base1)
-          /* If we compare this as integers require equal offset.  */
-          && (!INTEGRAL_TYPE_P (TREE_TYPE (@2))
-              || known_eq (off0, off1)))
-       (switch
-       (if (cmp == EQ_EXPR)
-        { constant_boolean_node (false, type); })
-       (if (cmp == NE_EXPR)
-        { constant_boolean_node (true, type); })))))))))
+      (if (equal == 0)
+       (switch
+        (if (cmp == EQ_EXPR)
+         { constant_boolean_node (false, type); })
+        (if (cmp == NE_EXPR)
+         { constant_boolean_node (true, type); })))))))))
 
 /* Simplify pointer equality compares using PTA.  */
 (for neeq (ne eq)
@@ -3638,7 +4100,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (neeq @0 @1)
   (if (POINTER_TYPE_P (TREE_TYPE (@0))
        && ptrs_compare_unequal (@0, @1))
-   { neeq == EQ_EXPR ? boolean_false_node : boolean_true_node; })))
+   { constant_boolean_node (neeq != EQ_EXPR, type); })))
 
 /* PR70920: Transform (intptr_t)x eq/ne CST to x eq/ne (typeof x) CST.
    and (typeof ptr_cst) x eq/ne ptr_cst to x eq/ne (typeof x) CST.
@@ -3649,10 +4111,13 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 (for cmp (ne eq)
  (simplify
   (cmp (convert @0) INTEGER_CST@1)
-  (if ((POINTER_TYPE_P (TREE_TYPE (@0)) && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@0)))
-       && INTEGRAL_TYPE_P (TREE_TYPE (@1)))
-      || (INTEGRAL_TYPE_P (TREE_TYPE (@0)) && POINTER_TYPE_P (TREE_TYPE (@1))
-         && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@1)))))
+  (if (((POINTER_TYPE_P (TREE_TYPE (@0))
+        && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@0)))
+        && INTEGRAL_TYPE_P (TREE_TYPE (@1)))
+       || (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+           && POINTER_TYPE_P (TREE_TYPE (@1))
+           && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (@1)))))
+       && TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1)))
    (cmp @0 (convert @1)))))
 
 /* Non-equality compare simplifications from fold_binary  */
@@ -3660,19 +4125,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  /* Comparisons with the highest or lowest possible integer of
     the specified precision will have known values.  */
  (simplify
-  (cmp (convert?@2 @0) INTEGER_CST@1)
-  (if ((INTEGRAL_TYPE_P (TREE_TYPE (@1)) || POINTER_TYPE_P (TREE_TYPE (@1)))
+  (cmp (convert?@2 @0) uniform_integer_cst_p@1)
+  (if ((INTEGRAL_TYPE_P (TREE_TYPE (@1))
+       || POINTER_TYPE_P (TREE_TYPE (@1))
+       || VECTOR_INTEGER_TYPE_P (TREE_TYPE (@1)))
        && tree_nop_conversion_p (TREE_TYPE (@2), TREE_TYPE (@0)))
    (with
     {
-      tree arg1_type = TREE_TYPE (@1);
+      tree cst = uniform_integer_cst_p (@1);
+      tree arg1_type = TREE_TYPE (cst);
       unsigned int prec = TYPE_PRECISION (arg1_type);
       wide_int max = wi::max_value (arg1_type);
       wide_int signed_max = wi::max_value (prec, SIGNED);
       wide_int min = wi::min_value (arg1_type);
     }
     (switch
-     (if (wi::to_wide (@1) == max)
+     (if (wi::to_wide (cst) == max)
       (switch
        (if (cmp == GT_EXPR)
        { constant_boolean_node (false, type); })
@@ -3682,7 +4150,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        { constant_boolean_node (true, type); })
        (if (cmp == LT_EXPR)
        (ne @2 @1))))
-     (if (wi::to_wide (@1) == min)
+     (if (wi::to_wide (cst) == min)
       (switch
        (if (cmp == LT_EXPR)
         { constant_boolean_node (false, type); })
@@ -3692,19 +4160,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
         { constant_boolean_node (true, type); })
        (if (cmp == GT_EXPR)
         (ne @2 @1))))
-     (if (wi::to_wide (@1) == max - 1)
+     (if (wi::to_wide (cst) == max - 1)
       (switch
        (if (cmp == GT_EXPR)
-       (eq @2 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) + 1); }))
+       (eq @2 { build_uniform_cst (TREE_TYPE (@1),
+                                   wide_int_to_tree (TREE_TYPE (cst),
+                                                     wi::to_wide (cst)
+                                                     + 1)); }))
        (if (cmp == LE_EXPR)
-       (ne @2 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) + 1); }))))
-     (if (wi::to_wide (@1) == min + 1)
+       (ne @2 { build_uniform_cst (TREE_TYPE (@1),
+                                   wide_int_to_tree (TREE_TYPE (cst),
+                                                     wi::to_wide (cst)
+                                                     + 1)); }))))
+     (if (wi::to_wide (cst) == min + 1)
       (switch
        (if (cmp == GE_EXPR)
-        (ne @2 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) - 1); }))
+        (ne @2 { build_uniform_cst (TREE_TYPE (@1),
+                                   wide_int_to_tree (TREE_TYPE (cst),
+                                                     wi::to_wide (cst)
+                                                     - 1)); }))
        (if (cmp == LT_EXPR)
-        (eq @2 { wide_int_to_tree (TREE_TYPE (@1), wi::to_wide (@1) - 1); }))))
-     (if (wi::to_wide (@1) == signed_max
+        (eq @2 { build_uniform_cst (TREE_TYPE (@1),
+                                   wide_int_to_tree (TREE_TYPE (cst),
+                                                     wi::to_wide (cst)
+                                                     - 1)); }))))
+     (if (wi::to_wide (cst) == signed_max
          && TYPE_UNSIGNED (arg1_type)
          /* We will flip the signedness of the comparison operator
             associated with the mode of @1, so the sign bit is
@@ -3716,11 +4196,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       /* The following case also applies to X < signed_max+1
         and X >= signed_max+1 because previous transformations.  */
       (if (cmp == LE_EXPR || cmp == GT_EXPR)
-       (with { tree st = signed_type_for (arg1_type); }
-        (if (cmp == LE_EXPR)
-        (ge (convert:st @0) { build_zero_cst (st); })
-        (lt (convert:st @0) { build_zero_cst (st); }))))))))))
+       (with { tree st = signed_type_for (TREE_TYPE (@1)); }
+               (switch
+        (if (cst == @1 && cmp == LE_EXPR)
+         (ge (convert:st @0) { build_zero_cst (st); }))
+        (if (cst == @1 && cmp == GT_EXPR)
+         (lt (convert:st @0) { build_zero_cst (st); }))
+        (if (cmp == LE_EXPR)
+         (ge (view_convert:st @0) { build_zero_cst (st); }))
+        (if (cmp == GT_EXPR)
+         (lt (view_convert:st @0) { build_zero_cst (st); })))))))))))
+
 (for cmp (unordered ordered unlt unle ungt unge uneq ltgt)
  /* If the second operand is NaN, the result is constant.  */
  (simplify
@@ -3839,6 +4325,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (rdiv @2 @1))
    (rdiv (op @0 @2) @1)))
 
+ (for cmp (lt le gt ge)
+      neg_cmp (gt ge lt le)
+  /* Simplify (x * C1) cmp C2 -> x cmp (C2 / C1), where C1 != 0.  */
+  (simplify
+   (cmp (mult @0 REAL_CST@1) REAL_CST@2)
+   (with
+    { tree tem = const_binop (RDIV_EXPR, type, @2, @1); }
+    (if (tem
+        && !(REAL_VALUE_ISINF (TREE_REAL_CST (tem))
+             || (real_zerop (tem) && !real_zerop (@1))))
+     (switch
+      (if (real_less (&dconst0, TREE_REAL_CST_PTR (@1)))
+       (cmp @0 { tem; }))
+      (if (real_less (TREE_REAL_CST_PTR (@1), &dconst0))
+       (neg_cmp @0 { tem; })))))))
+
  /* Simplify sqrt(x) * sqrt(y) -> sqrt(x*y).  */
  (for root (SQRT CBRT)
   (simplify
@@ -3941,15 +4443,53 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (logs (pows @0 @1))
    (mult @1 (logs @0))))
 
- /* pow(C,x) -> exp(log(C)*x) if C > 0.  */
+ /* pow(C,x) -> exp(log(C)*x) if C > 0,
+    or if C is a positive power of 2,
+    pow(C,x) -> exp2(log2(C)*x).  */
+#if GIMPLE
  (for pows (POW)
       exps (EXP)
       logs (LOG)
+      exp2s (EXP2)
+      log2s (LOG2)
   (simplify
    (pows REAL_CST@0 @1)
-    (if (real_compare (GT_EXPR, TREE_REAL_CST_PTR (@0), &dconst0)
-        && real_isfinite (TREE_REAL_CST_PTR (@0)))
-     (exps (mult (logs @0) @1)))))
+   (if (real_compare (GT_EXPR, TREE_REAL_CST_PTR (@0), &dconst0)
+       && real_isfinite (TREE_REAL_CST_PTR (@0))
+       /* As libmvec doesn't have a vectorized exp2, defer optimizing
+          the use_exp2 case until after vectorization.  It seems actually
+          beneficial for all constants to postpone this until later,
+          because exp(log(C)*x), while faster, will have worse precision
+          and if x folds into a constant too, that is unnecessary
+          pessimization.  */
+       && canonicalize_math_after_vectorization_p ())
+    (with {
+       const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (@0);
+       bool use_exp2 = false;
+       if (targetm.libc_has_function (function_c99_misc)
+          && value->cl == rvc_normal)
+        {
+          REAL_VALUE_TYPE frac_rvt = *value;
+          SET_REAL_EXP (&frac_rvt, 1);
+          if (real_equal (&frac_rvt, &dconst1))
+            use_exp2 = true;
+        }
+     }
+     (if (!use_exp2)
+      (if (optimize_pow_to_exp (@0, @1))
+       (exps (mult (logs @0) @1)))
+      (exp2s (mult (log2s @0) @1)))))))
+#endif
+
+ /* pow(C,x)*expN(y) -> expN(logN(C)*x+y) if C > 0.  */
+ (for pows (POW)
+      exps (EXP EXP2 EXP10 POW10)
+      logs (LOG LOG2 LOG10 LOG10)
+  (simplify
+   (mult:c (pows:s REAL_CST@0 @1) (exps:s @2))
+   (if (real_compare (GT_EXPR, TREE_REAL_CST_PTR (@0), &dconst0)
+       && real_isfinite (TREE_REAL_CST_PTR (@0)))
+    (exps (plus (mult (logs @0) @1) @2)))))
 
  (for sqrts (SQRT)
       cbrts (CBRT)
@@ -3975,6 +4515,64 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (tans (atans @0))
    @0)))
 
+ /* Simplify sin(atan(x)) -> x / sqrt(x*x + 1). */
+ (for sins (SIN)
+      atans (ATAN)
+      sqrts (SQRT)
+      copysigns (COPYSIGN)
+  (simplify
+   (sins (atans:s @0))
+   (with
+     {
+      REAL_VALUE_TYPE r_cst;
+      build_sinatan_real (&r_cst, type);
+      tree t_cst = build_real (type, r_cst);
+      tree t_one = build_one_cst (type);
+     }
+    (if (SCALAR_FLOAT_TYPE_P (type))
+     (cond (lt (abs @0) { t_cst; })
+      (rdiv @0 (sqrts (plus (mult @0 @0) { t_one; })))
+      (copysigns { t_one; } @0))))))
+
+/* Simplify cos(atan(x)) -> 1 / sqrt(x*x + 1). */
+ (for coss (COS)
+      atans (ATAN)
+      sqrts (SQRT)
+      copysigns (COPYSIGN)
+  (simplify
+   (coss (atans:s @0))
+   (with
+     {
+      REAL_VALUE_TYPE r_cst;
+      build_sinatan_real (&r_cst, type);
+      tree t_cst = build_real (type, r_cst);
+      tree t_one = build_one_cst (type);
+      tree t_zero = build_zero_cst (type);
+     }
+    (if (SCALAR_FLOAT_TYPE_P (type))
+     (cond (lt (abs @0) { t_cst; })
+      (rdiv { t_one; } (sqrts (plus (mult @0 @0) { t_one; })))
+      (copysigns { t_zero; } @0))))))
+
+ (if (!flag_errno_math)
+  /* Simplify sinh(atanh(x)) -> x / sqrt((1 - x)*(1 + x)). */
+  (for sinhs (SINH)
+       atanhs (ATANH)
+       sqrts (SQRT)
+   (simplify
+    (sinhs (atanhs:s @0))
+    (with { tree t_one = build_one_cst (type); }
+    (rdiv @0 (sqrts (mult (minus { t_one; } @0) (plus { t_one; } @0)))))))
+
+  /* Simplify cosh(atanh(x)) -> 1 / sqrt((1 - x)*(1 + x)) */
+  (for coshs (COSH)
+       atanhs (ATANH)
+       sqrts (SQRT)
+   (simplify
+    (coshs (atanhs:s @0))
+    (with { tree t_one = build_one_cst (type); }
+    (rdiv { t_one; } (sqrts (mult (minus { t_one; } @0) (plus { t_one; } @0))))))))
+
 /* cabs(x+0i) or cabs(0+xi) -> abs(x).  */
 (simplify
  (CABS (complex:C @0 real_zerop@1))
@@ -4336,43 +4934,115 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (wi::to_wide (@1) == -1)
    (rdiv { build_real (type, dconst1); } @0))))
 
-/* Narrowing of arithmetic and logical operations. 
+/* Narrowing of arithmetic and logical operations.
 
    These are conceptually similar to the transformations performed for
    the C/C++ front-ends by shorten_binary_op and shorten_compare.  Long
    term we want to move all that code out of the front-ends into here.  */
 
-/* If we have a narrowing conversion of an arithmetic operation where
-   both operands are widening conversions from the same type as the outer
-   narrowing conversion.  Then convert the innermost operands to a suitable
-   unsigned type (to avoid introducing undefined behavior), perform the
-   operation and convert the result to the desired type.  */
-(for op (plus minus)
-  (simplify
-    (convert (op:s (convert@2 @0) (convert?@3 @1)))
-    (if (INTEGRAL_TYPE_P (type)
-        /* We check for type compatibility between @0 and @1 below,
-           so there's no need to check that @1/@3 are integral types.  */
-        && INTEGRAL_TYPE_P (TREE_TYPE (@0))
-        && INTEGRAL_TYPE_P (TREE_TYPE (@2))
-        /* The precision of the type of each operand must match the
-           precision of the mode of each operand, similarly for the
-           result.  */
-        && type_has_mode_precision_p (TREE_TYPE (@0))
-        && type_has_mode_precision_p (TREE_TYPE (@1))
-        && type_has_mode_precision_p (type)
-        /* The inner conversion must be a widening conversion.  */
-        && TYPE_PRECISION (TREE_TYPE (@2)) > TYPE_PRECISION (TREE_TYPE (@0))
-        && types_match (@0, type)
-        && (types_match (@0, @1)
-            /* Or the second operand is const integer or converted const
-               integer from valueize.  */
-            || TREE_CODE (@1) == INTEGER_CST))
-      (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
-       (op @0 (convert @1))
-       (with { tree utype = unsigned_type_for (TREE_TYPE (@0)); }
-        (convert (op (convert:utype @0)
-                     (convert:utype @1))))))))
+/* Convert (outertype)((innertype0)a+(innertype1)b)
+   into ((newtype)a+(newtype)b) where newtype
+   is the widest mode from all of these.  */
+(for op (plus minus mult rdiv)
+ (simplify
+   (convert (op:s@0 (convert1?@3 @1) (convert2?@4 @2)))
+   /* If we have a narrowing conversion of an arithmetic operation where
+      both operands are widening conversions from the same type as the outer
+      narrowing conversion.  Then convert the innermost operands to a
+      suitable unsigned type (to avoid introducing undefined behavior),
+      perform the operation and convert the result to the desired type.  */
+   (if (INTEGRAL_TYPE_P (type)
+       && op != MULT_EXPR
+       && op != RDIV_EXPR
+       /* We check for type compatibility between @0 and @1 below,
+          so there's no need to check that @2/@4 are integral types.  */
+       && INTEGRAL_TYPE_P (TREE_TYPE (@1))
+       && INTEGRAL_TYPE_P (TREE_TYPE (@3))
+       /* The precision of the type of each operand must match the
+          precision of the mode of each operand, similarly for the
+          result.  */
+       && type_has_mode_precision_p (TREE_TYPE (@1))
+       && type_has_mode_precision_p (TREE_TYPE (@2))
+       && type_has_mode_precision_p (type)
+       /* The inner conversion must be a widening conversion.  */
+       && TYPE_PRECISION (TREE_TYPE (@3)) > TYPE_PRECISION (TREE_TYPE (@1))
+       && types_match (@1, type)
+       && (types_match (@1, @2)
+           /* Or the second operand is const integer or converted const
+              integer from valueize.  */
+           || TREE_CODE (@2) == INTEGER_CST))
+     (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
+       (op @1 (convert @2))
+       (with { tree utype = unsigned_type_for (TREE_TYPE (@1)); }
+       (convert (op (convert:utype @1)
+                    (convert:utype @2)))))
+     (if (FLOAT_TYPE_P (type)
+         && DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@0))
+              == DECIMAL_FLOAT_TYPE_P (type))
+      (with { tree arg0 = strip_float_extensions (@1);
+             tree arg1 = strip_float_extensions (@2);
+             tree itype = TREE_TYPE (@0);
+             tree ty1 = TREE_TYPE (arg0);
+             tree ty2 = TREE_TYPE (arg1);
+             enum tree_code code = TREE_CODE (itype); }
+       (if (FLOAT_TYPE_P (ty1)
+            && FLOAT_TYPE_P (ty2))
+        (with { tree newtype = type;
+                if (TYPE_MODE (ty1) == SDmode
+                    || TYPE_MODE (ty2) == SDmode
+                    || TYPE_MODE (type) == SDmode)
+                  newtype = dfloat32_type_node;
+                if (TYPE_MODE (ty1) == DDmode
+                    || TYPE_MODE (ty2) == DDmode
+                    || TYPE_MODE (type) == DDmode)
+                  newtype = dfloat64_type_node;
+                if (TYPE_MODE (ty1) == TDmode
+                    || TYPE_MODE (ty2) == TDmode
+                    || TYPE_MODE (type) == TDmode)
+                  newtype = dfloat128_type_node; }
+         (if ((newtype == dfloat32_type_node
+               || newtype == dfloat64_type_node
+               || newtype == dfloat128_type_node)
+             && newtype == type
+             && types_match (newtype, type))
+           (op (convert:newtype @1) (convert:newtype @2))
+           (with { if (TYPE_PRECISION (ty1) > TYPE_PRECISION (newtype))
+                     newtype = ty1;
+                   if (TYPE_PRECISION (ty2) > TYPE_PRECISION (newtype))
+                     newtype = ty2; }
+              /* Sometimes this transformation is safe (cannot
+                 change results through affecting double rounding
+                 cases) and sometimes it is not.  If NEWTYPE is
+                 wider than TYPE, e.g. (float)((long double)double
+                 + (long double)double) converted to
+                 (float)(double + double), the transformation is
+                 unsafe regardless of the details of the types
+                 involved; double rounding can arise if the result
+                 of NEWTYPE arithmetic is a NEWTYPE value half way
+                 between two representable TYPE values but the
+                 exact value is sufficiently different (in the
+                 right direction) for this difference to be
+                 visible in ITYPE arithmetic.  If NEWTYPE is the
+                 same as TYPE, however, the transformation may be
+                 safe depending on the types involved: it is safe
+                 if the ITYPE has strictly more than twice as many
+                 mantissa bits as TYPE, can represent infinities
+                 and NaNs if the TYPE can, and has sufficient
+                 exponent range for the product or ratio of two
+                 values representable in the TYPE to be within the
+                 range of normal values of ITYPE.  */
+             (if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype)
+                  && (flag_unsafe_math_optimizations
+                      || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
+                          && real_can_shorten_arithmetic (TYPE_MODE (itype),
+                                                          TYPE_MODE (type))
+                          && !excess_precision_type (newtype)))
+                  && !types_match (itype, newtype))
+                (convert:type (op (convert:newtype @1)
+                                  (convert:newtype @2)))
+        )))) )
+   ))
+)))
 
 /* This is another case of narrowing, specifically when there's an outer
    BIT_AND_EXPR which masks off bits outside the type of the innermost
@@ -4408,12 +5078,13 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
      (convert (bit_and (op (convert:utype @0) (convert:utype @1))
               (convert:utype @4))))))))
 
-/* Transform (@0 < @1 and @0 < @2) to use min, 
+/* Transform (@0 < @1 and @0 < @2) to use min,
    (@0 > @1 and @0 > @2) to use max */
-(for op (lt le gt ge)
-     ext (min min max max)
+(for logic (bit_and bit_and bit_and bit_and bit_ior bit_ior bit_ior bit_ior)
+     op    (lt      le      gt      ge      lt      le      gt      ge     )
+     ext   (min     min     max     max     max     max     min     min    )
  (simplify
-  (bit_and (op:cs @0 @1) (op:cs @0 @2))
+  (logic (op:cs @0 @1) (op:cs @0 @2))
   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
        && TREE_CODE (@0) != INTEGER_CST)
    (op @0 (ext @1 @2)))))
@@ -4478,6 +5149,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 
 /* Canonicalizations of BIT_FIELD_REFs.  */
 
+(simplify
+ (BIT_FIELD_REF (BIT_FIELD_REF @0 @1 @2) @3 @4)
+ (BIT_FIELD_REF @0 @3 { const_binop (PLUS_EXPR, bitsizetype, @2, @4); }))
+
+(simplify
+ (BIT_FIELD_REF (view_convert @0) @1 @2)
+ (BIT_FIELD_REF @0 @1 @2))
+
+(simplify
+ (BIT_FIELD_REF @0 @1 integer_zerop)
+ (if (tree_int_cst_equal (@1, TYPE_SIZE (TREE_TYPE (@0))))
+  (view_convert @0)))
+
 (simplify
  (BIT_FIELD_REF @0 @1 @2)
  (switch
@@ -4550,7 +5234,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
         { build_constructor (type, NULL); }
        (if (count == 1)
         (if (elt < CONSTRUCTOR_NELTS (ctor))
-         { CONSTRUCTOR_ELT (ctor, elt)->value; }
+         (view_convert { CONSTRUCTOR_ELT (ctor, elt)->value; })
          { build_zero_cst (type); })
         {
           vec<constructor_elt, va_gc> *vals;
@@ -4568,7 +5252,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (if (CONSTRUCTOR_NELTS (ctor) <= idx / const_k)
         { build_zero_cst (type); })
        (if (n == const_k)
-        { CONSTRUCTOR_ELT (ctor, idx / const_k)->value; })
+        (view_convert { CONSTRUCTOR_ELT (ctor, idx / const_k)->value; }))
        (BIT_FIELD_REF { CONSTRUCTOR_ELT (ctor, idx / const_k)->value; }
                       @1 { bitsize_int ((idx % const_k) * width); })))))))))
 
@@ -4597,3 +5281,378 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        || wi::geu_p (wi::to_wide (@rpos),
                      wi::to_wide (@ipos) + isize))
     (BIT_FIELD_REF @0 @rsize @rpos)))))
+
+(if (canonicalize_math_after_vectorization_p ())
+ (for fmas (FMA)
+  (simplify
+   (fmas:c (negate @0) @1 @2)
+   (IFN_FNMA @0 @1 @2))
+  (simplify
+   (fmas @0 @1 (negate @2))
+   (IFN_FMS @0 @1 @2))
+  (simplify
+   (fmas:c (negate @0) @1 (negate @2))
+   (IFN_FNMS @0 @1 @2))
+  (simplify
+   (negate (fmas@3 @0 @1 @2))
+   (if (single_use (@3))
+    (IFN_FNMS @0 @1 @2))))
+
+ (simplify
+  (IFN_FMS:c (negate @0) @1 @2)
+  (IFN_FNMS @0 @1 @2))
+ (simplify
+  (IFN_FMS @0 @1 (negate @2))
+  (IFN_FMA @0 @1 @2))
+ (simplify
+  (IFN_FMS:c (negate @0) @1 (negate @2))
+  (IFN_FNMA @0 @1 @2))
+ (simplify
+  (negate (IFN_FMS@3 @0 @1 @2))
+   (if (single_use (@3))
+    (IFN_FNMA @0 @1 @2)))
+
+ (simplify
+  (IFN_FNMA:c (negate @0) @1 @2)
+  (IFN_FMA @0 @1 @2))
+ (simplify
+  (IFN_FNMA @0 @1 (negate @2))
+  (IFN_FNMS @0 @1 @2))
+ (simplify
+  (IFN_FNMA:c (negate @0) @1 (negate @2))
+  (IFN_FMS @0 @1 @2))
+ (simplify
+  (negate (IFN_FNMA@3 @0 @1 @2))
+  (if (single_use (@3))
+   (IFN_FMS @0 @1 @2)))
+
+ (simplify
+  (IFN_FNMS:c (negate @0) @1 @2)
+  (IFN_FMS @0 @1 @2))
+ (simplify
+  (IFN_FNMS @0 @1 (negate @2))
+  (IFN_FNMA @0 @1 @2))
+ (simplify
+  (IFN_FNMS:c (negate @0) @1 (negate @2))
+  (IFN_FMA @0 @1 @2))
+ (simplify
+  (negate (IFN_FNMS@3 @0 @1 @2))
+  (if (single_use (@3))
+   (IFN_FMA @0 @1 @2))))
+
+/* POPCOUNT simplifications.  */
+(for popcount (BUILT_IN_POPCOUNT BUILT_IN_POPCOUNTL BUILT_IN_POPCOUNTLL
+              BUILT_IN_POPCOUNTIMAX)
+  /* popcount(X&1) is nop_expr(X&1).  */
+  (simplify
+    (popcount @0)
+    (if (tree_nonzero_bits (@0) == 1)
+      (convert @0)))
+  /* popcount(X) + popcount(Y) is popcount(X|Y) when X&Y must be zero.  */
+  (simplify
+    (plus (popcount:s @0) (popcount:s @1))
+    (if (wi::bit_and (tree_nonzero_bits (@0), tree_nonzero_bits (@1)) == 0)
+      (popcount (bit_ior @0 @1))))
+  /* popcount(X) == 0 is X == 0, and related (in)equalities.  */
+  (for cmp (le eq ne gt)
+       rep (eq eq ne ne)
+    (simplify
+      (cmp (popcount @0) integer_zerop)
+      (rep @0 { build_zero_cst (TREE_TYPE (@0)); }))))
+
+/* Simplify:
+
+     a = a1 op a2
+     r = c ? a : b;
+
+   to:
+
+     r = c ? a1 op a2 : b;
+
+   if the target can do it in one go.  This makes the operation conditional
+   on c, so could drop potentially-trapping arithmetic, but that's a valid
+   simplification if the result of the operation isn't needed.
+
+   Avoid speculatively generating a stand-alone vector comparison                                                                                
+   on targets that might not support them.  Any target implementing                                                                              
+   conditional internal functions must support the same comparisons                                                                              
+   inside and outside a VEC_COND_EXPR.  */                                                                                                       
+
+#if GIMPLE
+(for uncond_op (UNCOND_BINARY)
+     cond_op (COND_BINARY)
+ (simplify
+  (vec_cond @0 (view_convert? (uncond_op@4 @1 @2)) @3)
+  (with { tree op_type = TREE_TYPE (@4); }
+   (if (vectorized_internal_fn_supported_p (as_internal_fn (cond_op), op_type)
+       && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @0 @1 @2 (view_convert:op_type @3))))))
+ (simplify
+  (vec_cond @0 @1 (view_convert? (uncond_op@4 @2 @3)))
+  (with { tree op_type = TREE_TYPE (@4); }
+   (if (vectorized_internal_fn_supported_p (as_internal_fn (cond_op), op_type)
+       && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op (bit_not @0) @2 @3 (view_convert:op_type @1)))))))
+
+/* Same for ternary operations.  */
+(for uncond_op (UNCOND_TERNARY)
+     cond_op (COND_TERNARY)
+ (simplify
+  (vec_cond @0 (view_convert? (uncond_op@5 @1 @2 @3)) @4)
+  (with { tree op_type = TREE_TYPE (@5); }
+   (if (vectorized_internal_fn_supported_p (as_internal_fn (cond_op), op_type)
+       && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @0 @1 @2 @3 (view_convert:op_type @4))))))
+ (simplify
+  (vec_cond @0 @1 (view_convert? (uncond_op@5 @2 @3 @4)))
+  (with { tree op_type = TREE_TYPE (@5); }
+   (if (vectorized_internal_fn_supported_p (as_internal_fn (cond_op), op_type)
+       && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op (bit_not @0) @2 @3 @4
+                 (view_convert:op_type @1)))))))
+#endif
+
+/* Detect cases in which a VEC_COND_EXPR effectively replaces the
+   "else" value of an IFN_COND_*.  */
+(for cond_op (COND_BINARY)
+ (simplify
+  (vec_cond @0 (view_convert? (cond_op @0 @1 @2 @3)) @4)
+  (with { tree op_type = TREE_TYPE (@3); }
+   (if (element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @0 @1 @2 (view_convert:op_type @4))))))
+ (simplify
+  (vec_cond @0 @1 (view_convert? (cond_op @2 @3 @4 @5)))
+  (with { tree op_type = TREE_TYPE (@5); }
+   (if (inverse_conditions_p (@0, @2)
+        && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @2 @3 @4 (view_convert:op_type @1)))))))
+
+/* Same for ternary operations.  */
+(for cond_op (COND_TERNARY)
+ (simplify
+  (vec_cond @0 (view_convert? (cond_op @0 @1 @2 @3 @4)) @5)
+  (with { tree op_type = TREE_TYPE (@4); }
+   (if (element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @0 @1 @2 @3 (view_convert:op_type @5))))))
+ (simplify
+  (vec_cond @0 @1 (view_convert? (cond_op @2 @3 @4 @5 @6)))
+  (with { tree op_type = TREE_TYPE (@6); }
+   (if (inverse_conditions_p (@0, @2)
+        && element_precision (type) == element_precision (op_type))
+    (view_convert (cond_op @2 @3 @4 @5 (view_convert:op_type @1)))))))
+
+/* For pointers @0 and @2 and nonnegative constant offset @1, look for
+   expressions like:
+
+   A: (@0 + @1 < @2) | (@2 + @1 < @0)
+   B: (@0 + @1 <= @2) | (@2 + @1 <= @0)
+
+   If pointers are known not to wrap, B checks whether @1 bytes starting
+   at @0 and @2 do not overlap, while A tests the same thing for @1 + 1
+   bytes.  A is more efficiently tested as:
+
+   A: (sizetype) (@0 + @1 - @2) > @1 * 2
+
+   The equivalent expression for B is given by replacing @1 with @1 - 1:
+
+   B: (sizetype) (@0 + (@1 - 1) - @2) > (@1 - 1) * 2
+
+   @0 and @2 can be swapped in both expressions without changing the result.
+
+   The folds rely on sizetype's being unsigned (which is always true)
+   and on its being the same width as the pointer (which we have to check).
+
+   The fold replaces two pointer_plus expressions, two comparisons and
+   an IOR with a pointer_plus, a pointer_diff, and a comparison, so in
+   the best case it's a saving of two operations.  The A fold retains one
+   of the original pointer_pluses, so is a win even if both pointer_pluses
+   are used elsewhere.  The B fold is a wash if both pointer_pluses are
+   used elsewhere, since all we end up doing is replacing a comparison with
+   a pointer_plus.  We do still apply the fold under those circumstances
+   though, in case applying it to other conditions eventually makes one of the
+   pointer_pluses dead.  */
+(for ior (truth_orif truth_or bit_ior)
+ (for cmp (le lt)
+  (simplify
+   (ior (cmp:cs (pointer_plus@3 @0 INTEGER_CST@1) @2)
+       (cmp:cs (pointer_plus@4 @2 @1) @0))
+   (if (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
+       && TYPE_OVERFLOW_WRAPS (sizetype)
+       && TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (sizetype))
+    /* Calculate the rhs constant.  */
+    (with { offset_int off = wi::to_offset (@1) - (cmp == LE_EXPR ? 1 : 0);
+           offset_int rhs = off * 2; }
+     /* Always fails for negative values.  */
+     (if (wi::min_precision (rhs, UNSIGNED) <= TYPE_PRECISION (sizetype))
+      /* Since the order of @0 and @2 doesn't matter, let tree_swap_operands_p
+        pick a canonical order.  This increases the chances of using the
+        same pointer_plus in multiple checks.  */
+      (with { bool swap_p = tree_swap_operands_p (@0, @2);
+             tree rhs_tree = wide_int_to_tree (sizetype, rhs); }
+       (if (cmp == LT_EXPR)
+       (gt (convert:sizetype
+            (pointer_diff:ssizetype { swap_p ? @4 : @3; }
+                                    { swap_p ? @0 : @2; }))
+           { rhs_tree; })
+       (gt (convert:sizetype
+            (pointer_diff:ssizetype
+             (pointer_plus { swap_p ? @2 : @0; }
+                           { wide_int_to_tree (sizetype, off); })
+             { swap_p ? @0 : @2; }))
+           { rhs_tree; })))))))))
+
+/* Fold REDUC (@0 & @1) -> @0[I] & @1[I] if element I is the only nonzero
+   element of @1.  */
+(for reduc (IFN_REDUC_PLUS IFN_REDUC_IOR IFN_REDUC_XOR)
+ (simplify (reduc (view_convert? (bit_and @0 VECTOR_CST@1)))
+  (with { int i = single_nonzero_element (@1); }
+   (if (i >= 0)
+    (with { tree elt = vector_cst_elt (@1, i);
+           tree elt_type = TREE_TYPE (elt);
+           unsigned int elt_bits = tree_to_uhwi (TYPE_SIZE (elt_type));
+           tree size = bitsize_int (elt_bits);
+           tree pos = bitsize_int (elt_bits * i); }
+     (view_convert
+      (bit_and:elt_type
+       (BIT_FIELD_REF:elt_type @0 { size; } { pos; })
+       { elt; })))))))
+
+(simplify
+ (vec_perm @0 @1 VECTOR_CST@2)
+ (with
+  {
+    tree op0 = @0, op1 = @1, op2 = @2;
+
+    /* Build a vector of integers from the tree mask.  */
+    vec_perm_builder builder;
+    if (!tree_to_vec_perm_builder (&builder, op2))
+      return NULL_TREE;
+
+    /* Create a vec_perm_indices for the integer vector.  */
+    poly_uint64 nelts = TYPE_VECTOR_SUBPARTS (type);
+    bool single_arg = (op0 == op1);
+    vec_perm_indices sel (builder, single_arg ? 1 : 2, nelts);
+  }
+  (if (sel.series_p (0, 1, 0, 1))
+   { op0; }
+   (if (sel.series_p (0, 1, nelts, 1))
+    { op1; }
+    (with
+     {
+       if (!single_arg)
+         {
+          if (sel.all_from_input_p (0))
+            op1 = op0;
+          else if (sel.all_from_input_p (1))
+            {
+              op0 = op1;
+              sel.rotate_inputs (1);
+            }
+          else if (known_ge (poly_uint64 (sel[0]), nelts))
+            {
+              std::swap (op0, op1);
+              sel.rotate_inputs (1);
+            }
+         }
+       gassign *def;
+       tree cop0 = op0, cop1 = op1;
+       if (TREE_CODE (op0) == SSA_NAME
+           && (def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op0)))
+          && gimple_assign_rhs_code (def) == CONSTRUCTOR)
+        cop0 = gimple_assign_rhs1 (def);
+       if (TREE_CODE (op1) == SSA_NAME
+           && (def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op1)))
+          && gimple_assign_rhs_code (def) == CONSTRUCTOR)
+        cop1 = gimple_assign_rhs1 (def);
+
+       tree t;
+    }
+    (if ((TREE_CODE (cop0) == VECTOR_CST
+         || TREE_CODE (cop0) == CONSTRUCTOR)
+        && (TREE_CODE (cop1) == VECTOR_CST
+            || TREE_CODE (cop1) == CONSTRUCTOR)
+        && (t = fold_vec_perm (type, cop0, cop1, sel)))
+     { t; }
+     (with
+      {
+       bool changed = (op0 == op1 && !single_arg);
+       tree ins = NULL_TREE;
+       unsigned at = 0;
+
+       /* See if the permutation is performing a single element
+          insert from a CONSTRUCTOR or constant and use a BIT_INSERT_EXPR
+          in that case.  But only if the vector mode is supported,
+          otherwise this is invalid GIMPLE.  */
+        if (TYPE_MODE (type) != BLKmode
+           && (TREE_CODE (cop0) == VECTOR_CST
+               || TREE_CODE (cop0) == CONSTRUCTOR
+               || TREE_CODE (cop1) == VECTOR_CST
+               || TREE_CODE (cop1) == CONSTRUCTOR))
+          {
+           if (sel.series_p (1, 1, nelts + 1, 1))
+             {
+               /* After canonicalizing the first elt to come from the
+                  first vector we only can insert the first elt from
+                  the first vector.  */
+               at = 0;
+               if ((ins = fold_read_from_vector (cop0, sel[0])))
+                 op0 = op1;
+             }
+           else
+             {
+               unsigned int encoded_nelts = sel.encoding ().encoded_nelts ();
+               for (at = 0; at < encoded_nelts; ++at)
+                 if (maybe_ne (sel[at], at))
+                   break;
+               if (at < encoded_nelts && sel.series_p (at + 1, 1, at + 1, 1))
+                 {
+                   if (known_lt (at, nelts))
+                     ins = fold_read_from_vector (cop0, sel[at]);
+                   else
+                     ins = fold_read_from_vector (cop1, sel[at] - nelts);
+                 }
+             }
+         }
+
+       /* Generate a canonical form of the selector.  */
+       if (!ins && sel.encoding () != builder)
+         {
+           /* Some targets are deficient and fail to expand a single
+              argument permutation while still allowing an equivalent
+              2-argument version.  */
+           tree oldop2 = op2;
+           if (sel.ninputs () == 2
+              || can_vec_perm_const_p (TYPE_MODE (type), sel, false))
+             op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel);
+           else
+             {
+               vec_perm_indices sel2 (builder, 2, nelts);
+               if (can_vec_perm_const_p (TYPE_MODE (type), sel2, false))
+                 op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel2);
+               else
+                 /* Not directly supported with either encoding,
+                    so use the preferred form.  */
+                 op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel);
+             }
+           if (!operand_equal_p (op2, oldop2, 0))
+             changed = true;
+         }
+      }
+      (if (ins)
+       (bit_insert { op0; } { ins; }
+         { bitsize_int (at * tree_to_uhwi (TYPE_SIZE (TREE_TYPE (type)))); })
+       (if (changed)
+        (vec_perm { op0; } { op1; } { op2; }))))))))))
+
+/* VEC_PERM_EXPR (v, v, mask) -> v where v contains same element.  */
+
+(match vec_same_elem_p
+ @0
+ (if (uniform_vector_p (@0))))
+
+(match vec_same_elem_p
+ (vec_duplicate @0))
+
+(simplify
+ (vec_perm vec_same_elem_p@0 @0 @1)
+ @0)