OSDN Git Service

* gcc-interface/trans.c (can_equal_min_or_max_val_p): Be prepared for
[pf3gnuchains/gcc-fork.git] / gcc / fold-const.c
index e4c2589..1f864a7 100644 (file)
@@ -1,7 +1,7 @@
 /* Fold a constant sub-tree into a single node for C-compiler
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+   2012 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -3832,6 +3832,10 @@ make_range_step (location_t loc, enum tree_code code, tree arg0, tree arg1,
   switch (code)
     {
     case TRUTH_NOT_EXPR:
+      /* We can only do something if the range is testing for zero.  */
+      if (low == NULL_TREE || high == NULL_TREE
+         || ! integer_zerop (low) || ! integer_zerop (high))
+       return NULL_TREE;
       *p_in_p = ! in_p;
       return arg0;
 
@@ -3904,6 +3908,17 @@ make_range_step (location_t loc, enum tree_code code, tree arg0, tree arg1,
       return arg0;
 
     case NEGATE_EXPR:
+      /* If flag_wrapv and ARG0_TYPE is signed, make sure
+        low and high are non-NULL, then normalize will DTRT.  */
+      if (!TYPE_UNSIGNED (arg0_type)
+         && !TYPE_OVERFLOW_UNDEFINED (arg0_type))
+       {
+         if (low == NULL_TREE)
+           low = TYPE_MIN_VALUE (arg0_type);
+         if (high == NULL_TREE)
+           high = TYPE_MAX_VALUE (arg0_type);
+       }
+
       /* (-x) IN [a,b] -> x in [-b, -a]  */
       n_low = range_binop (MINUS_EXPR, exp_type,
                           build_int_cst (exp_type, 0),
@@ -5701,6 +5716,11 @@ extract_muldiv_1 (tree t, tree c, enum tree_code code, tree wide_type,
         break;
       /* FALLTHROUGH */
     case NEGATE_EXPR:
+      /* For division and modulus, type can't be unsigned, as e.g.
+        (-(x / 2U)) / 2U isn't equal to -((x / 2U) / 2U) for x >= 2.
+        For signed types, even with wrapping overflow, this is fine.  */
+      if (code != MULT_EXPR && TYPE_UNSIGNED (type))
+       break;
       if ((t1 = extract_muldiv (op0, c, code, wide_type, strict_overflow_p))
          != 0)
        return fold_build1 (tcode, ctype, fold_convert (ctype, t1));
@@ -6026,10 +6046,11 @@ fold_binary_op_with_conditional_arg (location_t loc,
     }
 
   /* This transformation is only worthwhile if we don't have to wrap ARG
-     in a SAVE_EXPR and the operation can be simplified on at least one
-     of the branches once its pushed inside the COND_EXPR.  */
+     in a SAVE_EXPR and the operation can be simplified without recursing
+     on at least one of the branches once its pushed inside the COND_EXPR.  */
   if (!TREE_CONSTANT (arg)
       && (TREE_SIDE_EFFECTS (arg)
+         || TREE_CODE (arg) == COND_EXPR || TREE_CODE (arg) == VEC_COND_EXPR
          || TREE_CONSTANT (true_value) || TREE_CONSTANT (false_value)))
     return NULL_TREE;
 
@@ -6781,12 +6802,14 @@ fold_sign_changed_comparison (location_t loc, enum tree_code code, tree type,
           && TREE_TYPE (TREE_OPERAND (arg1, 0)) == inner_type))
     return NULL_TREE;
 
-  if ((TYPE_UNSIGNED (inner_type) != TYPE_UNSIGNED (outer_type)
-       || POINTER_TYPE_P (inner_type) != POINTER_TYPE_P (outer_type))
+  if (TYPE_UNSIGNED (inner_type) != TYPE_UNSIGNED (outer_type)
       && code != NE_EXPR
       && code != EQ_EXPR)
     return NULL_TREE;
 
+  if (POINTER_TYPE_P (inner_type) != POINTER_TYPE_P (outer_type))
+    return NULL_TREE;
+
   if (TREE_CODE (arg1) == INTEGER_CST)
     arg1 = force_fit_type_double (inner_type, tree_to_double_int (arg1),
                                  0, TREE_OVERFLOW (arg1));
@@ -6850,64 +6873,81 @@ try_move_mult_to_index (location_t loc, tree addr, tree op1)
       s = integer_one_node;
     }
 
-  for (;; ref = TREE_OPERAND (ref, 0))
+  /* Handle &x.array the same as we would handle &x.array[0].  */
+  if (TREE_CODE (ref) == COMPONENT_REF
+      && TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
     {
-      if (TREE_CODE (ref) == ARRAY_REF)
+      tree domain;
+
+      /* Remember if this was a multi-dimensional array.  */
+      if (TREE_CODE (TREE_OPERAND (ref, 0)) == ARRAY_REF)
+       mdim = true;
+
+      domain = TYPE_DOMAIN (TREE_TYPE (ref));
+      if (! domain)
+       goto cont;
+      itype = TREE_TYPE (domain);
+
+      step = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ref)));
+      if (TREE_CODE (step) != INTEGER_CST)
+       goto cont;
+
+      if (s)
        {
-         tree domain;
+         if (! tree_int_cst_equal (step, s))
+           goto cont;
+       }
+      else
+       {
+         /* Try if delta is a multiple of step.  */
+         tree tmp = div_if_zero_remainder (EXACT_DIV_EXPR, op1, step);
+         if (! tmp)
+           goto cont;
+         delta = tmp;
+       }
 
-         /* Remember if this was a multi-dimensional array.  */
-         if (TREE_CODE (TREE_OPERAND (ref, 0)) == ARRAY_REF)
-           mdim = true;
+      /* Only fold here if we can verify we do not overflow one
+        dimension of a multi-dimensional array.  */
+      if (mdim)
+       {
+         tree tmp;
 
-         domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (ref, 0)));
-         if (! domain)
-           continue;
-         itype = TREE_TYPE (domain);
+         if (!TYPE_MIN_VALUE (domain)
+             || !TYPE_MAX_VALUE (domain)
+             || TREE_CODE (TYPE_MAX_VALUE (domain)) != INTEGER_CST)
+           goto cont;
 
-         step = array_ref_element_size (ref);
-         if (TREE_CODE (step) != INTEGER_CST)
-           continue;
+         tmp = fold_binary_loc (loc, PLUS_EXPR, itype,
+                                fold_convert_loc (loc, itype,
+                                                  TYPE_MIN_VALUE (domain)),
+                                fold_convert_loc (loc, itype, delta));
+         if (TREE_CODE (tmp) != INTEGER_CST
+             || tree_int_cst_lt (TYPE_MAX_VALUE (domain), tmp))
+           goto cont;
+       }
 
-         if (s)
-           {
-             if (! tree_int_cst_equal (step, s))
-                continue;
-           }
-         else
-           {
-             /* Try if delta is a multiple of step.  */
-             tree tmp = div_if_zero_remainder (EXACT_DIV_EXPR, op1, step);
-             if (! tmp)
-               continue;
-             delta = tmp;
-           }
+      /* We found a suitable component reference.  */
 
-         /* Only fold here if we can verify we do not overflow one
-            dimension of a multi-dimensional array.  */
-         if (mdim)
-           {
-             tree tmp;
+      pref = TREE_OPERAND (addr, 0);
+      ret = copy_node (pref);
+      SET_EXPR_LOCATION (ret, loc);
 
-             if (TREE_CODE (TREE_OPERAND (ref, 1)) != INTEGER_CST
-                 || !TYPE_MAX_VALUE (domain)
-                 || TREE_CODE (TYPE_MAX_VALUE (domain)) != INTEGER_CST)
-               continue;
+      ret = build4_loc (loc, ARRAY_REF, TREE_TYPE (TREE_TYPE (ref)), ret,
+                       fold_build2_loc
+                         (loc, PLUS_EXPR, itype,
+                          fold_convert_loc (loc, itype,
+                                            TYPE_MIN_VALUE
+                                              (TYPE_DOMAIN (TREE_TYPE (ref)))),
+                          fold_convert_loc (loc, itype, delta)),
+                       NULL_TREE, NULL_TREE);
+      return build_fold_addr_expr_loc (loc, ret);
+    }
 
-             tmp = fold_binary_loc (loc, PLUS_EXPR, itype,
-                                    fold_convert_loc (loc, itype,
-                                                      TREE_OPERAND (ref, 1)),
-                                    fold_convert_loc (loc, itype, delta));
-             if (!tmp
-                 || TREE_CODE (tmp) != INTEGER_CST
-                 || tree_int_cst_lt (TYPE_MAX_VALUE (domain), tmp))
-               continue;
-           }
+cont:
 
-         break;
-       }
-      else if (TREE_CODE (ref) == COMPONENT_REF
-              && TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
+  for (;; ref = TREE_OPERAND (ref, 0))
+    {
+      if (TREE_CODE (ref) == ARRAY_REF)
        {
          tree domain;
 
@@ -6915,12 +6955,12 @@ try_move_mult_to_index (location_t loc, tree addr, tree op1)
          if (TREE_CODE (TREE_OPERAND (ref, 0)) == ARRAY_REF)
            mdim = true;
 
-         domain = TYPE_DOMAIN (TREE_TYPE (ref));
+         domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (ref, 0)));
          if (! domain)
            continue;
          itype = TREE_TYPE (domain);
 
-         step = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ref)));
+         step = array_ref_element_size (ref);
          if (TREE_CODE (step) != INTEGER_CST)
            continue;
 
@@ -6944,16 +6984,17 @@ try_move_mult_to_index (location_t loc, tree addr, tree op1)
            {
              tree tmp;
 
-             if (!TYPE_MIN_VALUE (domain)
+             if (TREE_CODE (TREE_OPERAND (ref, 1)) != INTEGER_CST
                  || !TYPE_MAX_VALUE (domain)
                  || TREE_CODE (TYPE_MAX_VALUE (domain)) != INTEGER_CST)
                continue;
 
              tmp = fold_binary_loc (loc, PLUS_EXPR, itype,
                                     fold_convert_loc (loc, itype,
-                                                      TYPE_MIN_VALUE (domain)),
+                                                      TREE_OPERAND (ref, 1)),
                                     fold_convert_loc (loc, itype, delta));
-             if (TREE_CODE (tmp) != INTEGER_CST
+             if (!tmp
+                 || TREE_CODE (tmp) != INTEGER_CST
                  || tree_int_cst_lt (TYPE_MAX_VALUE (domain), tmp))
                continue;
            }
@@ -6982,29 +7023,11 @@ try_move_mult_to_index (location_t loc, tree addr, tree op1)
       pos = TREE_OPERAND (pos, 0);
     }
 
-  if (TREE_CODE (ref) == ARRAY_REF)
-    {
-      TREE_OPERAND (pos, 1)
-       = fold_build2_loc (loc, PLUS_EXPR, itype,
-                          fold_convert_loc (loc, itype, TREE_OPERAND (pos, 1)),
-                          fold_convert_loc (loc, itype, delta));
-      return fold_build1_loc (loc, ADDR_EXPR, TREE_TYPE (addr), ret);
-    }
-  else if (TREE_CODE (ref) == COMPONENT_REF)
-    {
-      gcc_assert (ret == pos);
-      ret = build4_loc (loc, ARRAY_REF, TREE_TYPE (TREE_TYPE (ref)), ret,
-                       fold_build2_loc
-                         (loc, PLUS_EXPR, itype,
-                          fold_convert_loc (loc, itype,
-                                            TYPE_MIN_VALUE
-                                              (TYPE_DOMAIN (TREE_TYPE (ref)))),
-                          fold_convert_loc (loc, itype, delta)),
-                       NULL_TREE, NULL_TREE);
-      return build_fold_addr_expr_loc (loc, ret);
-    }
-  else
-    gcc_unreachable ();
+  TREE_OPERAND (pos, 1)
+    = fold_build2_loc (loc, PLUS_EXPR, itype,
+                      fold_convert_loc (loc, itype, TREE_OPERAND (pos, 1)),
+                      fold_convert_loc (loc, itype, delta));
+  return fold_build1_loc (loc, ADDR_EXPR, TREE_TYPE (addr), ret);
 }
 
 
@@ -7651,6 +7674,8 @@ build_fold_addr_expr_loc (location_t loc, tree t)
   return build_fold_addr_expr_with_type_loc (loc, t, ptrtype);
 }
 
+static bool vec_cst_ctor_to_array (tree, tree *);
+
 /* Fold a unary expression of code CODE and type TYPE with operand
    OP0.  Return the folded expression if folding is successful.
    Otherwise, return NULL_TREE.  */
@@ -8294,6 +8319,44 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0)
        }
       return NULL_TREE;
 
+    case VEC_UNPACK_LO_EXPR:
+    case VEC_UNPACK_HI_EXPR:
+    case VEC_UNPACK_FLOAT_LO_EXPR:
+    case VEC_UNPACK_FLOAT_HI_EXPR:
+      {
+       unsigned int nelts = TYPE_VECTOR_SUBPARTS (type), i;
+       tree *elts, vals = NULL_TREE;
+       enum tree_code subcode;
+
+       gcc_assert (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0)) == nelts * 2);
+       if (TREE_CODE (arg0) != VECTOR_CST)
+         return NULL_TREE;
+
+       elts = XALLOCAVEC (tree, nelts * 2);
+       if (!vec_cst_ctor_to_array (arg0, elts))
+         return NULL_TREE;
+
+       if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_UNPACK_LO_EXPR
+                                  || code == VEC_UNPACK_FLOAT_LO_EXPR))
+         elts += nelts;
+
+       if (code == VEC_UNPACK_LO_EXPR || code == VEC_UNPACK_HI_EXPR)
+         subcode = NOP_EXPR;
+       else
+         subcode = FLOAT_EXPR;
+
+       for (i = 0; i < nelts; i++)
+         {
+           elts[i] = fold_convert_const (subcode, TREE_TYPE (type), elts[i]);
+           if (elts[i] == NULL_TREE || !CONSTANT_CLASS_P (elts[i]))
+             return NULL_TREE;
+         }
+
+       for (i = 0; i < nelts; i++)
+         vals = tree_cons (NULL_TREE, elts[nelts - i - 1], vals);
+       return build_vector (type, vals);
+      }
+
     default:
       return NULL_TREE;
     } /* switch (code) */
@@ -8846,6 +8909,17 @@ fold_comparison (location_t loc, enum tree_code code, tree type,
              indirect_base0 = true;
            }
          offset0 = TREE_OPERAND (arg0, 1);
+         if (host_integerp (offset0, 0))
+           {
+             HOST_WIDE_INT off = size_low_cst (offset0);
+             if ((HOST_WIDE_INT) (((unsigned HOST_WIDE_INT) off)
+                                  * BITS_PER_UNIT)
+                 / BITS_PER_UNIT == (HOST_WIDE_INT) off)
+               {
+                 bitpos0 = off * BITS_PER_UNIT;
+                 offset0 = NULL_TREE;
+               }
+           }
        }
 
       base1 = arg1;
@@ -8869,6 +8943,17 @@ fold_comparison (location_t loc, enum tree_code code, tree type,
              indirect_base1 = true;
            }
          offset1 = TREE_OPERAND (arg1, 1);
+         if (host_integerp (offset1, 0))
+           {
+             HOST_WIDE_INT off = size_low_cst (offset1);
+             if ((HOST_WIDE_INT) (((unsigned HOST_WIDE_INT) off)
+                                  * BITS_PER_UNIT)
+                 / BITS_PER_UNIT == (HOST_WIDE_INT) off)
+               {
+                 bitpos1 = off * BITS_PER_UNIT;
+                 offset1 = NULL_TREE;
+               }
+           }
        }
 
       /* A local variable can never be pointed to by
@@ -9528,6 +9613,124 @@ get_pointer_modulus_and_residue (tree expr, unsigned HOST_WIDE_INT *residue,
   return 1;
 }
 
+/* Helper function for fold_vec_perm.  Store elements of VECTOR_CST or
+   CONSTRUCTOR ARG into array ELTS and return true if successful.  */
+
+static bool
+vec_cst_ctor_to_array (tree arg, tree *elts)
+{
+  unsigned int nelts = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg)), i;
+
+  if (TREE_CODE (arg) == VECTOR_CST)
+    {
+      tree t;
+
+      for (i = 0, t = TREE_VECTOR_CST_ELTS (arg);
+          i < nelts && t; i++, t = TREE_CHAIN (t))
+       elts[i] = TREE_VALUE (t);
+      if (t)
+       return false;
+    }
+  else if (TREE_CODE (arg) == CONSTRUCTOR)
+    {
+      constructor_elt *elt;
+
+      FOR_EACH_VEC_ELT (constructor_elt, CONSTRUCTOR_ELTS (arg), i, elt)
+       if (i >= nelts)
+         return false;
+       else
+         elts[i] = elt->value;
+    }
+  else
+    return false;
+  for (; i < nelts; i++)
+    elts[i]
+      = fold_convert (TREE_TYPE (TREE_TYPE (arg)), integer_zero_node);
+  return true;
+}
+
+/* Attempt to fold vector permutation of ARG0 and ARG1 vectors using SEL
+   selector.  Return the folded VECTOR_CST or CONSTRUCTOR if successful,
+   NULL_TREE otherwise.  */
+
+static tree
+fold_vec_perm (tree type, tree arg0, tree arg1, const unsigned char *sel)
+{
+  unsigned int nelts = TYPE_VECTOR_SUBPARTS (type), i;
+  tree *elts;
+  bool need_ctor = false;
+
+  gcc_assert (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0)) == nelts
+             && TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg1)) == nelts);
+  if (TREE_TYPE (TREE_TYPE (arg0)) != TREE_TYPE (type)
+      || TREE_TYPE (TREE_TYPE (arg1)) != TREE_TYPE (type))
+    return NULL_TREE;
+
+  elts = XALLOCAVEC (tree, nelts * 3);
+  if (!vec_cst_ctor_to_array (arg0, elts)
+      || !vec_cst_ctor_to_array (arg1, elts + nelts))
+    return NULL_TREE;
+
+  for (i = 0; i < nelts; i++)
+    {
+      if (!CONSTANT_CLASS_P (elts[sel[i]]))
+       need_ctor = true;
+      elts[i + 2 * nelts] = unshare_expr (elts[sel[i]]);
+    }
+
+  if (need_ctor)
+    {
+      VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, nelts);
+      for (i = 0; i < nelts; i++)
+       CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, elts[2 * nelts + i]);
+      return build_constructor (type, v);
+    }
+  else
+    {
+      tree vals = NULL_TREE;
+      for (i = 0; i < nelts; i++)
+       vals = tree_cons (NULL_TREE, elts[3 * nelts - i - 1], vals);
+      return build_vector (type, vals);
+    }
+}
+
+/* Try to fold a pointer difference of type TYPE two address expressions of
+   array references AREF0 and AREF1 using location LOC.  Return a
+   simplified expression for the difference or NULL_TREE.  */
+
+static tree
+fold_addr_of_array_ref_difference (location_t loc, tree type,
+                                  tree aref0, tree aref1)
+{
+  tree base0 = TREE_OPERAND (aref0, 0);
+  tree base1 = TREE_OPERAND (aref1, 0);
+  tree base_offset = build_int_cst (type, 0);
+
+  /* If the bases are array references as well, recurse.  If the bases
+     are pointer indirections compute the difference of the pointers.
+     If the bases are equal, we are set.  */
+  if ((TREE_CODE (base0) == ARRAY_REF
+       && TREE_CODE (base1) == ARRAY_REF
+       && (base_offset
+          = fold_addr_of_array_ref_difference (loc, type, base0, base1)))
+      || (INDIRECT_REF_P (base0)
+         && INDIRECT_REF_P (base1)
+         && (base_offset = fold_binary_loc (loc, MINUS_EXPR, type,
+                                            TREE_OPERAND (base0, 0),
+                                            TREE_OPERAND (base1, 0))))
+      || operand_equal_p (base0, base1, 0))
+    {
+      tree op0 = fold_convert_loc (loc, type, TREE_OPERAND (aref0, 1));
+      tree op1 = fold_convert_loc (loc, type, TREE_OPERAND (aref1, 1));
+      tree esz = fold_convert_loc (loc, type, array_ref_element_size (aref0));
+      tree diff = build2 (MINUS_EXPR, type, op0, op1);
+      return fold_build2_loc (loc, PLUS_EXPR, type,
+                             base_offset,
+                             fold_build2_loc (loc, MULT_EXPR, type,
+                                              diff, esz));
+    }
+  return NULL_TREE;
+}
 
 /* Fold a binary expression of code CODE and type TYPE with operands
    OP0 and OP1.  LOC is the location of the resulting expression.
@@ -9842,12 +10045,15 @@ fold_binary_loc (location_t loc,
            }
        }
 
-      /* Handle (A1 * C1) + (A2 * C2) with A1, A2 or C1, C2 being the
-        same or one.  Make sure type is not saturating.
-        fold_plusminus_mult_expr will re-associate.  */
+      /* Handle (A1 * C1) + (A2 * C2) with A1, A2 or C1, C2 being the same or
+        one.  Make sure the type is not saturating and has the signedness of
+        the stripped operands, as fold_plusminus_mult_expr will re-associate.
+        ??? The latter condition should use TYPE_OVERFLOW_* flags instead.  */
       if ((TREE_CODE (arg0) == MULT_EXPR
           || TREE_CODE (arg1) == MULT_EXPR)
          && !TYPE_SATURATING (type)
+         && TYPE_UNSIGNED (type) == TYPE_UNSIGNED (TREE_TYPE (arg0))
+         && TYPE_UNSIGNED (type) == TYPE_UNSIGNED (TREE_TYPE (arg1))
          && (!FLOAT_TYPE_P (type) || flag_associative_math))
         {
          tree tem = fold_plusminus_mult_expr (loc, code, type, arg0, arg1);
@@ -10440,19 +10646,11 @@ fold_binary_loc (location_t loc,
          && TREE_CODE (arg1) == ADDR_EXPR
          && TREE_CODE (TREE_OPERAND (arg1, 0)) == ARRAY_REF)
         {
-         tree aref0 = TREE_OPERAND (arg0, 0);
-         tree aref1 = TREE_OPERAND (arg1, 0);
-         if (operand_equal_p (TREE_OPERAND (aref0, 0),
-                              TREE_OPERAND (aref1, 0), 0))
-           {
-             tree op0 = fold_convert_loc (loc, type, TREE_OPERAND (aref0, 1));
-             tree op1 = fold_convert_loc (loc, type, TREE_OPERAND (aref1, 1));
-             tree esz = array_ref_element_size (aref0);
-             tree diff = build2 (MINUS_EXPR, type, op0, op1);
-             return fold_build2_loc (loc, MULT_EXPR, type, diff,
-                                 fold_convert_loc (loc, type, esz));
-
-           }
+         tree tem = fold_addr_of_array_ref_difference (loc, type,
+                                                       TREE_OPERAND (arg0, 0),
+                                                       TREE_OPERAND (arg1, 0));
+         if (tem)
+           return tem;
        }
 
       if (FLOAT_TYPE_P (type)
@@ -10462,12 +10660,15 @@ fold_binary_loc (location_t loc,
          && (tem = distribute_real_division (loc, code, type, arg0, arg1)))
        return tem;
 
-      /* Handle (A1 * C1) - (A2 * C2) with A1, A2 or C1, C2 being the
-        same or one.  Make sure type is not saturating.
-        fold_plusminus_mult_expr will re-associate.  */
+      /* Handle (A1 * C1) - (A2 * C2) with A1, A2 or C1, C2 being the same or
+        one.  Make sure the type is not saturating and has the signedness of
+        the stripped operands, as fold_plusminus_mult_expr will re-associate.
+        ??? The latter condition should use TYPE_OVERFLOW_* flags instead.  */
       if ((TREE_CODE (arg0) == MULT_EXPR
           || TREE_CODE (arg1) == MULT_EXPR)
          && !TYPE_SATURATING (type)
+         && TYPE_UNSIGNED (type) == TYPE_UNSIGNED (TREE_TYPE (arg0))
+         && TYPE_UNSIGNED (type) == TYPE_UNSIGNED (TREE_TYPE (arg1))
          && (!FLOAT_TYPE_P (type) || flag_associative_math))
         {
          tree tem = fold_plusminus_mult_expr (loc, code, type, arg0, arg1);
@@ -10817,66 +11018,50 @@ fold_binary_loc (location_t loc,
          && TREE_CODE (arg1) == INTEGER_CST
          && TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST)
        {
-         unsigned HOST_WIDE_INT hi1, lo1, hi2, lo2, hi3, lo3, mlo, mhi;
+         double_int c1, c2, c3, msk;
          int width = TYPE_PRECISION (type), w;
-         hi1 = TREE_INT_CST_HIGH (TREE_OPERAND (arg0, 1));
-         lo1 = TREE_INT_CST_LOW (TREE_OPERAND (arg0, 1));
-         hi2 = TREE_INT_CST_HIGH (arg1);
-         lo2 = TREE_INT_CST_LOW (arg1);
+         c1 = tree_to_double_int (TREE_OPERAND (arg0, 1));
+         c2 = tree_to_double_int (arg1);
 
          /* If (C1&C2) == C1, then (X&C1)|C2 becomes (X,C2).  */
-         if ((hi1 & hi2) == hi1 && (lo1 & lo2) == lo1)
+         if (double_int_equal_p (double_int_and (c1, c2), c1))
            return omit_one_operand_loc (loc, type, arg1,
-                                    TREE_OPERAND (arg0, 0));
+                                        TREE_OPERAND (arg0, 0));
 
-         if (width > HOST_BITS_PER_WIDE_INT)
-           {
-             mhi = (unsigned HOST_WIDE_INT) -1
-                   >> (2 * HOST_BITS_PER_WIDE_INT - width);
-             mlo = -1;
-           }
-         else
-           {
-             mhi = 0;
-             mlo = (unsigned HOST_WIDE_INT) -1
-                   >> (HOST_BITS_PER_WIDE_INT - width);
-           }
+         msk = double_int_mask (width);
 
          /* If (C1|C2) == ~0 then (X&C1)|C2 becomes X|C2.  */
-         if ((~(hi1 | hi2) & mhi) == 0 && (~(lo1 | lo2) & mlo) == 0)
+         if (double_int_zero_p (double_int_and_not (msk,
+                                                    double_int_ior (c1, c2))))
            return fold_build2_loc (loc, BIT_IOR_EXPR, type,
-                               TREE_OPERAND (arg0, 0), arg1);
+                                   TREE_OPERAND (arg0, 0), arg1);
 
          /* Minimize the number of bits set in C1, i.e. C1 := C1 & ~C2,
             unless (C1 & ~C2) | (C2 & C3) for some C3 is a mask of some
             mode which allows further optimizations.  */
-         hi1 &= mhi;
-         lo1 &= mlo;
-         hi2 &= mhi;
-         lo2 &= mlo;
-         hi3 = hi1 & ~hi2;
-         lo3 = lo1 & ~lo2;
+         c1 = double_int_and (c1, msk);
+         c2 = double_int_and (c2, msk);
+         c3 = double_int_and_not (c1, c2);
          for (w = BITS_PER_UNIT;
               w <= width && w <= HOST_BITS_PER_WIDE_INT;
               w <<= 1)
            {
              unsigned HOST_WIDE_INT mask
                = (unsigned HOST_WIDE_INT) -1 >> (HOST_BITS_PER_WIDE_INT - w);
-             if (((lo1 | lo2) & mask) == mask
-                 && (lo1 & ~mask) == 0 && hi1 == 0)
+             if (((c1.low | c2.low) & mask) == mask
+                 && (c1.low & ~mask) == 0 && c1.high == 0)
                {
-                 hi3 = 0;
-                 lo3 = mask;
+                 c3 = uhwi_to_double_int (mask);
                  break;
                }
            }
-         if (hi3 != hi1 || lo3 != lo1)
+         if (!double_int_equal_p (c3, c1))
            return fold_build2_loc (loc, BIT_IOR_EXPR, type,
-                               fold_build2_loc (loc, BIT_AND_EXPR, type,
-                                            TREE_OPERAND (arg0, 0),
-                                            build_int_cst_wide (type,
-                                                                lo3, hi3)),
-                               arg1);
+                                   fold_build2_loc (loc, BIT_AND_EXPR, type,
+                                                    TREE_OPERAND (arg0, 0),
+                                                    double_int_to_tree (type,
+                                                                        c3)),
+                                   arg1);
        }
 
       /* (X & Y) | Y is (X, Y).  */
@@ -12672,13 +12857,13 @@ fold_binary_loc (location_t loc,
       if (TREE_CODE (arg0) == BIT_XOR_EXPR
          && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
        return fold_build2_loc (loc, code, type, TREE_OPERAND (arg0, 0),
-                               build_int_cst (TREE_TYPE (arg0), 0));
+                               build_zero_cst (TREE_TYPE (arg0)));
       /* Likewise (X ^ Y) == X becomes Y == 0.  X has no side-effects.  */
       if (TREE_CODE (arg0) == BIT_XOR_EXPR
          && operand_equal_p (TREE_OPERAND (arg0, 0), arg1, 0)
          && reorder_operands_p (TREE_OPERAND (arg0, 1), arg1))
        return fold_build2_loc (loc, code, type, TREE_OPERAND (arg0, 1),
-                               build_int_cst (TREE_TYPE (arg0), 0));
+                               build_zero_cst (TREE_TYPE (arg0)));
 
       /* (X ^ C1) op C2 can be rewritten as X op (C1 ^ C2).  */
       if (TREE_CODE (arg0) == BIT_XOR_EXPR
@@ -12766,7 +12951,7 @@ fold_binary_loc (location_t loc,
                                                          BIT_XOR_EXPR, itype,
                                                          arg00, arg10),
                                             arg01),
-                               build_int_cst (itype, 0));
+                               build_zero_cst (itype));
 
          if (operand_equal_p (arg01, arg10, 0))
            return fold_build2_loc (loc, code, type,
@@ -12775,7 +12960,7 @@ fold_binary_loc (location_t loc,
                                                          BIT_XOR_EXPR, itype,
                                                          arg00, arg11),
                                             arg01),
-                               build_int_cst (itype, 0));
+                               build_zero_cst (itype));
 
          if (operand_equal_p (arg00, arg11, 0))
            return fold_build2_loc (loc, code, type,
@@ -12784,7 +12969,7 @@ fold_binary_loc (location_t loc,
                                                          BIT_XOR_EXPR, itype,
                                                          arg01, arg10),
                                             arg00),
-                               build_int_cst (itype, 0));
+                               build_zero_cst (itype));
 
          if (operand_equal_p (arg00, arg10, 0))
            return fold_build2_loc (loc, code, type,
@@ -12793,7 +12978,7 @@ fold_binary_loc (location_t loc,
                                                          BIT_XOR_EXPR, itype,
                                                          arg01, arg11),
                                             arg00),
-                               build_int_cst (itype, 0));
+                               build_zero_cst (itype));
        }
 
       if (TREE_CODE (arg0) == BIT_XOR_EXPR
@@ -13273,10 +13458,22 @@ fold_binary_loc (location_t loc,
                                   TREE_OPERAND (arg1, 1)),
                           build_int_cst (TREE_TYPE (arg0), 0));
 
+      /* Similarly for X < (cast) (1 << Y).  But cast can't be narrowing,
+        otherwise Y might be >= # of bits in X's type and thus e.g.
+        (unsigned char) (1 << Y) for Y 15 might be 0.
+        If the cast is widening, then 1 << Y should have unsigned type,
+        otherwise if Y is number of bits in the signed shift type minus 1,
+        we can't optimize this.  E.g. (unsigned long long) (1 << Y) for Y
+        31 might be 0xffffffff80000000.  */
       if ((code == LT_EXPR || code == GE_EXPR)
          && TYPE_UNSIGNED (TREE_TYPE (arg0))
          && CONVERT_EXPR_P (arg1)
          && TREE_CODE (TREE_OPERAND (arg1, 0)) == LSHIFT_EXPR
+         && (TYPE_PRECISION (TREE_TYPE (arg1))
+             >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg1, 0))))
+         && (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg1, 0)))
+             || (TYPE_PRECISION (TREE_TYPE (arg1))
+                 == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg1, 0)))))
          && integer_onep (TREE_OPERAND (TREE_OPERAND (arg1, 0), 0)))
        {
          tem = build2 (RSHIFT_EXPR, TREE_TYPE (arg0), arg0,
@@ -13381,6 +13578,73 @@ fold_binary_loc (location_t loc,
       /* An ASSERT_EXPR should never be passed to fold_binary.  */
       gcc_unreachable ();
 
+    case VEC_PACK_TRUNC_EXPR:
+    case VEC_PACK_FIX_TRUNC_EXPR:
+      {
+       unsigned int nelts = TYPE_VECTOR_SUBPARTS (type), i;
+       tree *elts, vals = NULL_TREE;
+
+       gcc_assert (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0)) == nelts / 2
+                   && TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg1)) == nelts / 2);
+       if (TREE_CODE (arg0) != VECTOR_CST || TREE_CODE (arg1) != VECTOR_CST)
+         return NULL_TREE;
+
+       elts = XALLOCAVEC (tree, nelts);
+       if (!vec_cst_ctor_to_array (arg0, elts)
+           || !vec_cst_ctor_to_array (arg1, elts + nelts / 2))
+         return NULL_TREE;
+
+       for (i = 0; i < nelts; i++)
+         {
+           elts[i] = fold_convert_const (code == VEC_PACK_TRUNC_EXPR
+                                         ? NOP_EXPR : FIX_TRUNC_EXPR,
+                                         TREE_TYPE (type), elts[i]);
+           if (elts[i] == NULL_TREE || !CONSTANT_CLASS_P (elts[i]))
+             return NULL_TREE;
+         }
+
+       for (i = 0; i < nelts; i++)
+         vals = tree_cons (NULL_TREE, elts[nelts - i - 1], vals);
+       return build_vector (type, vals);
+      }
+
+    case VEC_WIDEN_MULT_LO_EXPR:
+    case VEC_WIDEN_MULT_HI_EXPR:
+      {
+       unsigned int nelts = TYPE_VECTOR_SUBPARTS (type), i;
+       tree *elts, vals = NULL_TREE;
+
+       gcc_assert (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0)) == nelts * 2
+                   && TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg1)) == nelts * 2);
+       if (TREE_CODE (arg0) != VECTOR_CST || TREE_CODE (arg1) != VECTOR_CST)
+         return NULL_TREE;
+
+       elts = XALLOCAVEC (tree, nelts * 4);
+       if (!vec_cst_ctor_to_array (arg0, elts)
+           || !vec_cst_ctor_to_array (arg1, elts + nelts * 2))
+         return NULL_TREE;
+
+       if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_WIDEN_MULT_LO_EXPR))
+         elts += nelts;
+
+       for (i = 0; i < nelts; i++)
+         {
+           elts[i] = fold_convert_const (NOP_EXPR, TREE_TYPE (type), elts[i]);
+           elts[i + nelts * 2]
+             = fold_convert_const (NOP_EXPR, TREE_TYPE (type),
+                                   elts[i + nelts * 2]);
+           if (elts[i] == NULL_TREE || elts[i + nelts * 2] == NULL_TREE)
+             return NULL_TREE;
+           elts[i] = const_binop (MULT_EXPR, elts[i], elts[i + nelts * 2]);
+           if (elts[i] == NULL_TREE || !CONSTANT_CLASS_P (elts[i]))
+             return NULL_TREE;
+         }
+
+       for (i = 0; i < nelts; i++)
+         vals = tree_cons (NULL_TREE, elts[nelts - i - 1], vals);
+       return build_vector (type, vals);
+      }
+
     default:
       return NULL_TREE;
     } /* switch (code) */
@@ -13767,6 +14031,55 @@ fold_ternary_loc (location_t loc, enum tree_code code, tree type,
 
       return fold_fma (loc, type, arg0, arg1, arg2);
 
+    case VEC_PERM_EXPR:
+      if (TREE_CODE (arg2) == VECTOR_CST)
+       {
+         unsigned int nelts = TYPE_VECTOR_SUBPARTS (type), i;
+         unsigned char *sel = XALLOCAVEC (unsigned char, nelts);
+         tree t;
+         bool need_mask_canon = false;
+
+         gcc_assert (nelts == TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg2)));
+         for (i = 0, t = TREE_VECTOR_CST_ELTS (arg2);
+              i < nelts && t; i++, t = TREE_CHAIN (t))
+           {
+             if (TREE_CODE (TREE_VALUE (t)) != INTEGER_CST)
+               return NULL_TREE;
+
+             sel[i] = TREE_INT_CST_LOW (TREE_VALUE (t)) & (2 * nelts - 1);
+             if (TREE_INT_CST_HIGH (TREE_VALUE (t))
+                 || ((unsigned HOST_WIDE_INT)
+                     TREE_INT_CST_LOW (TREE_VALUE (t)) != sel[i]))
+               need_mask_canon = true;
+           }
+         if (t)
+           return NULL_TREE;
+         for (; i < nelts; i++)
+           sel[i] = 0;
+
+         if ((TREE_CODE (arg0) == VECTOR_CST
+              || TREE_CODE (arg0) == CONSTRUCTOR)
+             && (TREE_CODE (arg1) == VECTOR_CST
+                 || TREE_CODE (arg1) == CONSTRUCTOR))
+           {
+             t = fold_vec_perm (type, arg0, arg1, sel);
+             if (t != NULL_TREE)
+               return t;
+           }
+
+         if (need_mask_canon && arg2 == op2)
+           {
+             tree list = NULL_TREE, eltype = TREE_TYPE (TREE_TYPE (arg2));
+             for (i = 0; i < nelts; i++)
+               list = tree_cons (NULL_TREE,
+                                 build_int_cst (eltype, sel[nelts - i - 1]),
+                                 list);
+             t = build_vector (TREE_TYPE (arg2), list);
+             return build3_loc (loc, VEC_PERM_EXPR, type, op0, op1, t);
+           }
+       }
+      return NULL_TREE;
+
     default:
       return NULL_TREE;
     } /* switch (code) */
@@ -13993,7 +14306,8 @@ fold_checksum_tree (const_tree expr, struct md5_ctx *ctx, htab_t ht)
        }
     }
   md5_process_bytes (expr, tree_size (expr), ctx);
-  fold_checksum_tree (TREE_TYPE (expr), ctx, ht);
+  if (CODE_CONTAINS_STRUCT (code, TS_TYPED))
+    fold_checksum_tree (TREE_TYPE (expr), ctx, ht);
   if (TREE_CODE_CLASS (code) != tcc_type
       && TREE_CODE_CLASS (code) != tcc_declaration
       && code != TREE_LIST