d: Force TYPE_MODE of classes and non-POD structs as BLKmode
[gcc.git] / gcc / fold-const.c
index b48296ca1118041ed2606d14bad0ad10161391f0..3a0f39a85b8c99d33cee9253f536c6f05898c2f2 100644 (file)
@@ -1,5 +1,5 @@
 /* Fold a constant sub-tree into a single node for C-compiler
-   Copyright (C) 1987-2019 Free Software Foundation, Inc.
+   Copyright (C) 1987-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -992,26 +992,19 @@ wide_int_binop (wide_int &res,
       res = wi::bit_and (arg1, arg2);
       break;
 
-    case RSHIFT_EXPR:
     case LSHIFT_EXPR:
       if (wi::neg_p (arg2))
-       {
-         tmp = -arg2;
-         if (code == RSHIFT_EXPR)
-           code = LSHIFT_EXPR;
-         else
-           code = RSHIFT_EXPR;
-       }
-      else
-        tmp = arg2;
+       return false;
+      res = wi::lshift (arg1, arg2);
+      break;
 
-      if (code == RSHIFT_EXPR)
-       /* It's unclear from the C standard whether shifts can overflow.
-          The following code ignores overflow; perhaps a C standard
-          interpretation ruling is needed.  */
-       res = wi::rshift (arg1, tmp, sign);
-      else
-       res = wi::lshift (arg1, tmp);
+    case RSHIFT_EXPR:
+      if (wi::neg_p (arg2))
+       return false;
+      /* It's unclear from the C standard whether shifts can overflow.
+        The following code ignores overflow; perhaps a C standard
+        interpretation ruling is needed.  */
+      res = wi::rshift (arg1, arg2, sign);
       break;
 
     case RROTATE_EXPR:
@@ -2375,10 +2368,15 @@ fold_convertible_p (const_tree type, const_tree arg)
 
     case REAL_TYPE:
     case FIXED_POINT_TYPE:
-    case VECTOR_TYPE:
     case VOID_TYPE:
       return TREE_CODE (type) == TREE_CODE (orig);
 
+    case VECTOR_TYPE:
+      return (VECTOR_TYPE_P (orig)
+             && known_eq (TYPE_VECTOR_SUBPARTS (type),
+                          TYPE_VECTOR_SUBPARTS (orig))
+             && fold_convertible_p (TREE_TYPE (type), TREE_TYPE (orig)));
+
     default:
       return false;
     }
@@ -2593,6 +2591,7 @@ maybe_lvalue_p (const_tree x)
   case TARGET_EXPR:
   case COND_EXPR:
   case BIND_EXPR:
+  case VIEW_CONVERT_EXPR:
     break;
 
   default:
@@ -2933,6 +2932,11 @@ combine_comparisons (location_t loc,
    If OEP_LEXICOGRAPHIC is set, then also handle expressions with side-effects
    such as MODIFY_EXPR, RETURN_EXPR, as well as STATEMENT_LISTs.
 
+   If OEP_BITWISE is set, then require the values to be bitwise identical
+   rather than simply numerically equal.  Do not take advantage of things
+   like math-related flags or undefined behavior; only return true for
+   values that are provably bitwise identical in all circumstances.
+
    Unless OEP_MATCH_SIDE_EFFECTS is set, the function returns false on
    any operand with side effect.  This is unnecesarily conservative in the
    case we know that arg0 and arg1 are in disjoint code paths (such as in
@@ -2962,6 +2966,11 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
   if (!TREE_TYPE (arg0) || !TREE_TYPE (arg1))
     return false;
 
+  /* Bitwise identity makes no sense if the values have different layouts.  */
+  if ((flags & OEP_BITWISE)
+      && !tree_nop_conversion_p (TREE_TYPE (arg0), TREE_TYPE (arg1)))
+    return false;
+
   /* We cannot consider pointers to different address space equal.  */
   if (POINTER_TYPE_P (TREE_TYPE (arg0))
       && POINTER_TYPE_P (TREE_TYPE (arg1))
@@ -3094,8 +3103,7 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
        if (real_identical (&TREE_REAL_CST (arg0), &TREE_REAL_CST (arg1)))
          return true;
 
-
-       if (!HONOR_SIGNED_ZEROS (arg0))
+       if (!(flags & OEP_BITWISE) && !HONOR_SIGNED_ZEROS (arg0))
          {
            /* If we do not distinguish between signed and unsigned zero,
               consider them equal.  */
@@ -3147,7 +3155,9 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
        break;
       }
 
-  if (flags & OEP_ONLY_CONST)
+  /* Don't handle more cases for OEP_BITWISE, since we can't guarantee that
+     two instances of undefined behavior will give identical results.  */
+  if (flags & (OEP_ONLY_CONST | OEP_BITWISE))
     return false;
 
 /* Define macros to test an operand from arg0 and arg1 for equality and a
@@ -3295,10 +3305,36 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
        case COMPONENT_REF:
          /* Handle operand 2 the same as for ARRAY_REF.  Operand 0
             may be NULL when we're called to compare MEM_EXPRs.  */
-         if (!OP_SAME_WITH_NULL (0)
-             || !OP_SAME (1))
+         if (!OP_SAME_WITH_NULL (0))
            return false;
-         flags &= ~OEP_ADDRESS_OF;
+         {
+           bool compare_address = flags & OEP_ADDRESS_OF;
+
+           /* Most of time we only need to compare FIELD_DECLs for equality.
+              However when determining address look into actual offsets.
+              These may match for unions and unshared record types.  */
+           flags &= ~OEP_ADDRESS_OF;
+           if (!OP_SAME (1))
+             {
+               if (compare_address)
+                 {
+                   if (TREE_OPERAND (arg0, 2)
+                       || TREE_OPERAND (arg1, 2))
+                     return OP_SAME_WITH_NULL (2);
+                   tree field0 = TREE_OPERAND (arg0, 1);
+                   tree field1 = TREE_OPERAND (arg1, 1);
+
+                   if (!operand_equal_p (DECL_FIELD_OFFSET (field0),
+                                         DECL_FIELD_OFFSET (field1), flags)
+                       || !operand_equal_p (DECL_FIELD_BIT_OFFSET (field0),
+                                            DECL_FIELD_BIT_OFFSET (field1),
+                                            flags))
+                     return false;
+                 }
+               else
+                 return false;
+             }
+         }
          return OP_SAME_WITH_NULL (2);
 
        case BIT_FIELD_REF:
@@ -3307,24 +3343,6 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
          flags &= ~OEP_ADDRESS_OF;
          return OP_SAME (1) && OP_SAME (2);
 
-       /* Virtual table call.  */
-       case OBJ_TYPE_REF:
-         {
-           if (!operand_equal_p (OBJ_TYPE_REF_EXPR (arg0),
-                                 OBJ_TYPE_REF_EXPR (arg1), flags))
-             return false;
-           if (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg0))
-               != tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg1)))
-             return false;
-           if (!operand_equal_p (OBJ_TYPE_REF_OBJECT (arg0),
-                                 OBJ_TYPE_REF_OBJECT (arg1), flags))
-             return false;
-           if (!types_same_for_odr (obj_type_ref_class (arg0),
-                                    obj_type_ref_class (arg1)))
-             return false;
-           return true;
-         }
-
        default:
          return false;
        }
@@ -3403,6 +3421,27 @@ operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
            return OP_SAME (0);
          return false;
 
+       case OBJ_TYPE_REF:
+       /* Virtual table reference.  */
+       if (!operand_equal_p (OBJ_TYPE_REF_EXPR (arg0),
+                             OBJ_TYPE_REF_EXPR (arg1), flags))
+         return false;
+       flags &= ~OEP_ADDRESS_OF;
+       if (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg0))
+           != tree_to_uhwi (OBJ_TYPE_REF_TOKEN (arg1)))
+         return false;
+       if (!operand_equal_p (OBJ_TYPE_REF_OBJECT (arg0),
+                             OBJ_TYPE_REF_OBJECT (arg1), flags))
+         return false;
+       if (virtual_method_call_p (arg0))
+         {
+           if (!virtual_method_call_p (arg1))
+             return false;
+           return types_same_for_odr (obj_type_ref_class (arg0),
+                                      obj_type_ref_class (arg1));
+         }
+       return false;
+
        default:
          return false;
        }
@@ -3770,9 +3809,26 @@ operand_compare::hash_operand (const_tree t, inchash::hash &hstate,
              sflags = flags;
              break;
 
+           case COMPONENT_REF:
+             if (sflags & OEP_ADDRESS_OF)
+               {
+                 hash_operand (TREE_OPERAND (t, 0), hstate, flags);
+                 if (TREE_OPERAND (t, 2))
+                   hash_operand (TREE_OPERAND (t, 2), hstate,
+                                 flags & ~OEP_ADDRESS_OF);
+                 else
+                   {
+                     tree field = TREE_OPERAND (t, 1);
+                     hash_operand (DECL_FIELD_OFFSET (field),
+                                   hstate, flags & ~OEP_ADDRESS_OF);
+                     hash_operand (DECL_FIELD_BIT_OFFSET (field),
+                                   hstate, flags & ~OEP_ADDRESS_OF);
+                   }
+                 return;
+               }
+             break;
            case ARRAY_REF:
            case ARRAY_RANGE_REF:
-           case COMPONENT_REF:
            case BIT_FIELD_REF:
              sflags &= ~OEP_ADDRESS_OF;
              break;
@@ -3805,11 +3861,25 @@ operand_compare::hash_operand (const_tree t, inchash::hash &hstate,
              hash_operand (TARGET_EXPR_SLOT (t), hstate, flags);
              return;
 
-           /* Virtual table call.  */
            case OBJ_TYPE_REF:
+           /* Virtual table reference.  */
              inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags);
+             flags &= ~OEP_ADDRESS_OF;
              inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags);
              inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags);
+             if (!virtual_method_call_p (t))
+               return;
+             if (tree c = obj_type_ref_class (t))
+               {
+                 c = TYPE_NAME (TYPE_MAIN_VARIANT (c));
+                 /* We compute mangled names only when free_lang_data is run.
+                    In that case we can hash precisely.  */
+                 if (TREE_CODE (c) == TYPE_DECL
+                     && DECL_ASSEMBLER_NAME_SET_P (c))
+                   hstate.add_object
+                          (IDENTIFIER_HASH_VALUE
+                                  (DECL_ASSEMBLER_NAME (c)));
+               }
              return;
            default:
              break;
@@ -5753,8 +5823,22 @@ fold_cond_expr_with_comparison (location_t loc, tree type,
       case LT_EXPR:
        if (TYPE_UNSIGNED (TREE_TYPE (arg1)))
          break;
-       tem = fold_build1_loc (loc, ABS_EXPR, TREE_TYPE (arg1), arg1);
-       return negate_expr (fold_convert_loc (loc, type, tem));
+       if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (arg1))
+           && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg1)))
+         {
+           /* A <= 0 ? A : -A for A INT_MIN is valid, but -abs(INT_MIN)
+              is not, invokes UB both in abs and in the negation of it.
+              So, use ABSU_EXPR instead.  */
+           tree utype = unsigned_type_for (TREE_TYPE (arg1));
+           tem = fold_build1_loc (loc, ABSU_EXPR, utype, arg1);
+           tem = negate_expr (tem);
+           return fold_convert_loc (loc, type, tem);
+         }
+       else
+         {
+           tem = fold_build1_loc (loc, ABS_EXPR, TREE_TYPE (arg1), arg1);
+           return negate_expr (fold_convert_loc (loc, type, tem));
+         }
       default:
        gcc_assert (TREE_CODE_CLASS (comp_code) == tcc_comparison);
        break;
@@ -5900,6 +5984,13 @@ fold_range_test (location_t loc, enum tree_code code, tree type,
     return 0;
 
   lhs = make_range (op0, &in0_p, &low0, &high0, &strict_overflow_p);
+  /* If op0 is known true or false and this is a short-circuiting
+     operation we must not merge with op1 since that makes side-effects
+     unconditional.  So special-case this.  */
+  if (!lhs
+      && ((code == TRUTH_ORIF_EXPR && in0_p)
+         || (code == TRUTH_ANDIF_EXPR && !in0_p)))
+    return op0;
   rhs = make_range (op1, &in1_p, &low1, &high1, &strict_overflow_p);
 
   /* If this is an OR operation, invert both sides; we will invert
@@ -7715,32 +7806,60 @@ native_encode_complex (const_tree expr, unsigned char *ptr, int len, int off)
   return rsize + isize;
 }
 
-
-/* Subroutine of native_encode_expr.  Encode the VECTOR_CST
-   specified by EXPR into the buffer PTR of length LEN bytes.
-   Return the number of bytes placed in the buffer, or zero
-   upon failure.  */
+/* Like native_encode_vector, but only encode the first COUNT elements.
+   The other arguments are as for native_encode_vector.  */
 
 static int
-native_encode_vector (const_tree expr, unsigned char *ptr, int len, int off)
+native_encode_vector_part (const_tree expr, unsigned char *ptr, int len,
+                          int off, unsigned HOST_WIDE_INT count)
 {
-  unsigned HOST_WIDE_INT i, count;
-  int size, offset;
-  tree itype, elem;
+  tree itype = TREE_TYPE (TREE_TYPE (expr));
+  if (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (expr))
+      && TYPE_PRECISION (itype) <= BITS_PER_UNIT)
+    {
+      /* This is the only case in which elements can be smaller than a byte.
+        Element 0 is always in the lsb of the containing byte.  */
+      unsigned int elt_bits = TYPE_PRECISION (itype);
+      int total_bytes = CEIL (elt_bits * count, BITS_PER_UNIT);
+      if ((off == -1 && total_bytes > len) || off >= total_bytes)
+       return 0;
 
-  offset = 0;
-  if (!VECTOR_CST_NELTS (expr).is_constant (&count))
-    return 0;
-  itype = TREE_TYPE (TREE_TYPE (expr));
-  size = GET_MODE_SIZE (SCALAR_TYPE_MODE (itype));
-  for (i = 0; i < count; i++)
+      if (off == -1)
+       off = 0;
+
+      /* Zero the buffer and then set bits later where necessary.  */
+      int extract_bytes = MIN (len, total_bytes - off);
+      if (ptr)
+       memset (ptr, 0, extract_bytes);
+
+      unsigned int elts_per_byte = BITS_PER_UNIT / elt_bits;
+      unsigned int first_elt = off * elts_per_byte;
+      unsigned int extract_elts = extract_bytes * elts_per_byte;
+      for (unsigned int i = 0; i < extract_elts; ++i)
+       {
+         tree elt = VECTOR_CST_ELT (expr, first_elt + i);
+         if (TREE_CODE (elt) != INTEGER_CST)
+           return 0;
+
+         if (ptr && wi::extract_uhwi (wi::to_wide (elt), 0, 1))
+           {
+             unsigned int bit = i * elt_bits;
+             ptr[bit / BITS_PER_UNIT] |= 1 << (bit % BITS_PER_UNIT);
+           }
+       }
+      return extract_bytes;
+    }
+
+  int offset = 0;
+  int size = GET_MODE_SIZE (SCALAR_TYPE_MODE (itype));
+  for (unsigned HOST_WIDE_INT i = 0; i < count; i++)
     {
       if (off >= size)
        {
          off -= size;
          continue;
        }
-      elem = VECTOR_CST_ELT (expr, i);
+      tree elem = VECTOR_CST_ELT (expr, i);
       int res = native_encode_expr (elem, ptr ? ptr + offset : NULL,
                                    len - offset, off);
       if ((off == -1 && res != size) || res == 0)
@@ -7754,6 +7873,20 @@ native_encode_vector (const_tree expr, unsigned char *ptr, int len, int off)
   return offset;
 }
 
+/* Subroutine of native_encode_expr.  Encode the VECTOR_CST
+   specified by EXPR into the buffer PTR of length LEN bytes.
+   Return the number of bytes placed in the buffer, or zero
+   upon failure.  */
+
+static int
+native_encode_vector (const_tree expr, unsigned char *ptr, int len, int off)
+{
+  unsigned HOST_WIDE_INT count;
+  if (!VECTOR_CST_NELTS (expr).is_constant (&count))
+    return 0;
+  return native_encode_vector_part (expr, ptr, len, off, count);
+}
+
 
 /* Subroutine of native_encode_expr.  Encode the STRING_CST
    specified by EXPR into the buffer PTR of length LEN bytes.
@@ -7778,9 +7911,10 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off)
     return 0;
   if (off == -1)
     off = 0;
+  len = MIN (total_bytes - off, len);
   if (ptr == NULL)
     /* Dry run.  */;
-  else if (TREE_STRING_LENGTH (expr) - off < MIN (total_bytes, len))
+  else
     {
       int written = 0;
       if (off < TREE_STRING_LENGTH (expr))
@@ -7788,20 +7922,18 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off)
          written = MIN (len, TREE_STRING_LENGTH (expr) - off);
          memcpy (ptr, TREE_STRING_POINTER (expr) + off, written);
        }
-      memset (ptr + written, 0,
-             MIN (total_bytes - written, len - written));
+      memset (ptr + written, 0, len - written);
     }
-  else
-    memcpy (ptr, TREE_STRING_POINTER (expr) + off, MIN (total_bytes, len));
-  return MIN (total_bytes - off, len);
+  return len;
 }
 
 
-/* Subroutine of fold_view_convert_expr.  Encode the INTEGER_CST,
-   REAL_CST, COMPLEX_CST or VECTOR_CST specified by EXPR into the
-   buffer PTR of length LEN bytes.  If PTR is NULL, don't actually store
-   anything, just do a dry run.  If OFF is not -1 then start
-   the encoding at byte offset OFF and encode at most LEN bytes.
+/* Subroutine of fold_view_convert_expr.  Encode the INTEGER_CST, REAL_CST,
+   FIXED_CST, COMPLEX_CST, STRING_CST, or VECTOR_CST specified by EXPR into
+   the buffer PTR of size LEN bytes.  If PTR is NULL, don't actually store
+   anything, just do a dry run.  Fail either if OFF is -1 and LEN isn't
+   sufficient to encode the entire EXPR, or if OFF is out of bounds.
+   Otherwise, start at byte offset OFF and encode at most LEN bytes.
    Return the number of bytes placed in the buffer, or zero upon failure.  */
 
 int
@@ -7836,6 +7968,555 @@ native_encode_expr (const_tree expr, unsigned char *ptr, int len, int off)
     }
 }
 
+/* Try to find a type whose byte size is smaller or equal to LEN bytes larger
+   or equal to FIELDSIZE bytes, with underlying mode precision/size multiple
+   of BITS_PER_UNIT.  As native_{interpret,encode}_int works in term of
+   machine modes, we can't just use build_nonstandard_integer_type.  */
+
+tree
+find_bitfield_repr_type (int fieldsize, int len)
+{
+  machine_mode mode;
+  for (int pass = 0; pass < 2; pass++)
+    {
+      enum mode_class mclass = pass ? MODE_PARTIAL_INT : MODE_INT;
+      FOR_EACH_MODE_IN_CLASS (mode, mclass)
+       if (known_ge (GET_MODE_SIZE (mode), fieldsize)
+           && known_eq (GET_MODE_PRECISION (mode),
+                        GET_MODE_BITSIZE (mode))
+           && known_le (GET_MODE_SIZE (mode), len))
+         {
+           tree ret = lang_hooks.types.type_for_mode (mode, 1);
+           if (ret && TYPE_MODE (ret) == mode)
+             return ret;
+         }
+    }
+
+  for (int i = 0; i < NUM_INT_N_ENTS; i ++)
+    if (int_n_enabled_p[i]
+       && int_n_data[i].bitsize >= (unsigned) (BITS_PER_UNIT * fieldsize)
+       && int_n_trees[i].unsigned_type)
+      {
+       tree ret = int_n_trees[i].unsigned_type;
+       mode = TYPE_MODE (ret);
+       if (known_ge (GET_MODE_SIZE (mode), fieldsize)
+           && known_eq (GET_MODE_PRECISION (mode),
+                        GET_MODE_BITSIZE (mode))
+           && known_le (GET_MODE_SIZE (mode), len))
+         return ret;
+      }
+
+  return NULL_TREE;
+}
+
+/* Similar to native_encode_expr, but also handle CONSTRUCTORs, VCEs,
+   NON_LVALUE_EXPRs and nops.  If MASK is non-NULL (then PTR has
+   to be non-NULL and OFF zero), then in addition to filling the
+   bytes pointed by PTR with the value also clear any bits pointed
+   by MASK that are known to be initialized, keep them as is for
+   e.g. uninitialized padding bits or uninitialized fields.  */
+
+int
+native_encode_initializer (tree init, unsigned char *ptr, int len,
+                          int off, unsigned char *mask)
+{
+  int r;
+
+  /* We don't support starting at negative offset and -1 is special.  */
+  if (off < -1 || init == NULL_TREE)
+    return 0;
+
+  gcc_assert (mask == NULL || (off == 0 && ptr));
+
+  STRIP_NOPS (init);
+  switch (TREE_CODE (init))
+    {
+    case VIEW_CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+      return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off,
+                                       mask);
+    default:
+      r = native_encode_expr (init, ptr, len, off);
+      if (mask)
+       memset (mask, 0, r);
+      return r;
+    case CONSTRUCTOR:
+      tree type = TREE_TYPE (init);
+      HOST_WIDE_INT total_bytes = int_size_in_bytes (type);
+      if (total_bytes < 0)
+       return 0;
+      if ((off == -1 && total_bytes > len) || off >= total_bytes)
+       return 0;
+      int o = off == -1 ? 0 : off;
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         HOST_WIDE_INT min_index;
+         unsigned HOST_WIDE_INT cnt;
+         HOST_WIDE_INT curpos = 0, fieldsize, valueinit = -1;
+         constructor_elt *ce;
+
+         if (TYPE_DOMAIN (type) == NULL_TREE
+             || !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
+           return 0;
+
+         fieldsize = int_size_in_bytes (TREE_TYPE (type));
+         if (fieldsize <= 0)
+           return 0;
+
+         min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
+         if (ptr != NULL)
+           memset (ptr, '\0', MIN (total_bytes - off, len));
+
+         for (cnt = 0; ; cnt++)
+           {
+             tree val = NULL_TREE, index = NULL_TREE;
+             HOST_WIDE_INT pos = curpos, count = 0;
+             bool full = false;
+             if (vec_safe_iterate (CONSTRUCTOR_ELTS (init), cnt, &ce))
+               {
+                 val = ce->value;
+                 index = ce->index;
+               }
+             else if (mask == NULL
+                      || CONSTRUCTOR_NO_CLEARING (init)
+                      || curpos >= total_bytes)
+               break;
+             else
+               pos = total_bytes;
+             if (index && TREE_CODE (index) == RANGE_EXPR)
+               {
+                 if (!tree_fits_shwi_p (TREE_OPERAND (index, 0))
+                     || !tree_fits_shwi_p (TREE_OPERAND (index, 1)))
+                   return 0;
+                 pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index)
+                       * fieldsize;
+                 count = (tree_to_shwi (TREE_OPERAND (index, 1))
+                          - tree_to_shwi (TREE_OPERAND (index, 0)));
+               }
+             else if (index)
+               {
+                 if (!tree_fits_shwi_p (index))
+                   return 0;
+                 pos = (tree_to_shwi (index) - min_index) * fieldsize;
+               }
+
+             if (mask && !CONSTRUCTOR_NO_CLEARING (init) && curpos != pos)
+               {
+                 if (valueinit == -1)
+                   {
+                     tree zero = build_zero_cst (TREE_TYPE (type));
+                     r = native_encode_initializer (zero, ptr + curpos,
+                                                    fieldsize, 0,
+                                                    mask + curpos);
+                     if (TREE_CODE (zero) == CONSTRUCTOR)
+                       ggc_free (zero);
+                     if (!r)
+                       return 0;
+                     valueinit = curpos;
+                     curpos += fieldsize;
+                   }
+                 while (curpos != pos)
+                   {
+                     memcpy (ptr + curpos, ptr + valueinit, fieldsize);
+                     memcpy (mask + curpos, mask + valueinit, fieldsize);
+                     curpos += fieldsize;
+                   }
+               }
+
+             curpos = pos;
+             if (val)
+               do
+                 {
+                   if (off == -1
+                       || (curpos >= off
+                           && (curpos + fieldsize
+                               <= (HOST_WIDE_INT) off + len)))
+                     {
+                       if (full)
+                         {
+                           if (ptr)
+                             memcpy (ptr + (curpos - o), ptr + (pos - o),
+                                     fieldsize);
+                           if (mask)
+                             memcpy (mask + curpos, mask + pos, fieldsize);
+                         }
+                       else if (!native_encode_initializer (val,
+                                                            ptr
+                                                            ? ptr + curpos - o
+                                                            : NULL,
+                                                            fieldsize,
+                                                            off == -1 ? -1
+                                                                      : 0,
+                                                            mask
+                                                            ? mask + curpos
+                                                            : NULL))
+                         return 0;
+                       else
+                         {
+                           full = true;
+                           pos = curpos;
+                         }
+                     }
+                   else if (curpos + fieldsize > off
+                            && curpos < (HOST_WIDE_INT) off + len)
+                     {
+                       /* Partial overlap.  */
+                       unsigned char *p = NULL;
+                       int no = 0;
+                       int l;
+                       gcc_assert (mask == NULL);
+                       if (curpos >= off)
+                         {
+                           if (ptr)
+                             p = ptr + curpos - off;
+                           l = MIN ((HOST_WIDE_INT) off + len - curpos,
+                                    fieldsize);
+                         }
+                       else
+                         {
+                           p = ptr;
+                           no = off - curpos;
+                           l = len;
+                         }
+                       if (!native_encode_initializer (val, p, l, no, NULL))
+                         return 0;
+                     }
+                   curpos += fieldsize;
+                 }
+               while (count-- != 0);
+           }
+         return MIN (total_bytes - off, len);
+       }
+      else if (TREE_CODE (type) == RECORD_TYPE
+              || TREE_CODE (type) == UNION_TYPE)
+       {
+         unsigned HOST_WIDE_INT cnt;
+         constructor_elt *ce;
+         tree fld_base = TYPE_FIELDS (type);
+         tree to_free = NULL_TREE;
+
+         gcc_assert (TREE_CODE (type) == RECORD_TYPE || mask == NULL);
+         if (ptr != NULL)
+           memset (ptr, '\0', MIN (total_bytes - o, len));
+         for (cnt = 0; ; cnt++)
+           {
+             tree val = NULL_TREE, field = NULL_TREE;
+             HOST_WIDE_INT pos = 0, fieldsize;
+             unsigned HOST_WIDE_INT bpos = 0, epos = 0;
+
+             if (to_free)
+               {
+                 ggc_free (to_free);
+                 to_free = NULL_TREE;
+               }
+
+             if (vec_safe_iterate (CONSTRUCTOR_ELTS (init), cnt, &ce))
+               {
+                 val = ce->value;
+                 field = ce->index;
+                 if (field == NULL_TREE)
+                   return 0;
+
+                 pos = int_byte_position (field);
+                 if (off != -1 && (HOST_WIDE_INT) off + len <= pos)
+                   continue;
+               }
+             else if (mask == NULL
+                      || CONSTRUCTOR_NO_CLEARING (init))
+               break;
+             else
+               pos = total_bytes;
+
+             if (mask && !CONSTRUCTOR_NO_CLEARING (init))
+               {
+                 tree fld;
+                 for (fld = fld_base; fld; fld = DECL_CHAIN (fld))
+                   {
+                     if (TREE_CODE (fld) != FIELD_DECL)
+                       continue;
+                     if (fld == field)
+                       break;
+                     if (DECL_PADDING_P (fld))
+                       continue;
+                     if (DECL_SIZE_UNIT (fld) == NULL_TREE
+                         || !tree_fits_shwi_p (DECL_SIZE_UNIT (fld)))
+                       return 0;
+                     if (integer_zerop (DECL_SIZE_UNIT (fld)))
+                       continue;
+                     break;
+                   }
+                 if (fld == NULL_TREE)
+                   {
+                     if (ce == NULL)
+                       break;
+                     return 0;
+                   }
+                 fld_base = DECL_CHAIN (fld);
+                 if (fld != field)
+                   {
+                     cnt--;
+                     field = fld;
+                     pos = int_byte_position (field);
+                     val = build_zero_cst (TREE_TYPE (fld));
+                     if (TREE_CODE (val) == CONSTRUCTOR)
+                       to_free = val;
+                   }
+               }
+
+             if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
+                 && TYPE_DOMAIN (TREE_TYPE (field))
+                 && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
+               {
+                 if (mask || off != -1)
+                   return 0;
+                 if (val == NULL_TREE)
+                   continue;
+                 if (TREE_CODE (TREE_TYPE (val)) != ARRAY_TYPE)
+                   return 0;
+                 fieldsize = int_size_in_bytes (TREE_TYPE (val));
+                 if (fieldsize < 0
+                     || (int) fieldsize != fieldsize
+                     || (pos + fieldsize) > INT_MAX)
+                   return 0;
+                 if (pos + fieldsize > total_bytes)
+                   {
+                     if (ptr != NULL && total_bytes < len)
+                       memset (ptr + total_bytes, '\0',
+                               MIN (pos + fieldsize, len) - total_bytes);
+                     total_bytes = pos + fieldsize;
+                   }
+               }
+             else
+               {
+                 if (DECL_SIZE_UNIT (field) == NULL_TREE
+                     || !tree_fits_shwi_p (DECL_SIZE_UNIT (field)))
+                   return 0;
+                 fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field));
+               }
+             if (fieldsize == 0)
+               continue;
+
+             if (DECL_BIT_FIELD (field))
+               {
+                 if (!tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field)))
+                   return 0;
+                 fieldsize = TYPE_PRECISION (TREE_TYPE (field));
+                 bpos = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
+                 if (bpos % BITS_PER_UNIT)
+                   bpos %= BITS_PER_UNIT;
+                 else
+                   bpos = 0;
+                 fieldsize += bpos;
+                 epos = fieldsize % BITS_PER_UNIT;
+                 fieldsize += BITS_PER_UNIT - 1;
+                 fieldsize /= BITS_PER_UNIT;
+               }
+
+             if (off != -1 && pos + fieldsize <= off)
+               continue;
+
+             if (val == NULL_TREE)
+               continue;
+
+             if (DECL_BIT_FIELD (field))
+               {
+                 /* FIXME: Handle PDP endian.  */
+                 if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+                   return 0;
+
+                 if (TREE_CODE (val) != INTEGER_CST)
+                   return 0;
+
+                 tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field);
+                 tree repr_type = NULL_TREE;
+                 HOST_WIDE_INT rpos = 0;
+                 if (repr && INTEGRAL_TYPE_P (TREE_TYPE (repr)))
+                   {
+                     rpos = int_byte_position (repr);
+                     repr_type = TREE_TYPE (repr);
+                   }
+                 else
+                   {
+                     repr_type = find_bitfield_repr_type (fieldsize, len);
+                     if (repr_type == NULL_TREE)
+                       return 0;
+                     HOST_WIDE_INT repr_size = int_size_in_bytes (repr_type);
+                     gcc_assert (repr_size > 0 && repr_size <= len);
+                     if (pos + repr_size <= o + len)
+                       rpos = pos;
+                     else
+                       {
+                         rpos = o + len - repr_size;
+                         gcc_assert (rpos <= pos);
+                       }
+                   }
+
+                 if (rpos > pos)
+                   return 0;
+                 wide_int w = wi::to_wide (val, TYPE_PRECISION (repr_type));
+                 int diff = (TYPE_PRECISION (repr_type)
+                             - TYPE_PRECISION (TREE_TYPE (field)));
+                 HOST_WIDE_INT bitoff = (pos - rpos) * BITS_PER_UNIT + bpos;
+                 if (!BYTES_BIG_ENDIAN)
+                   w = wi::lshift (w, bitoff);
+                 else
+                   w = wi::lshift (w, diff - bitoff);
+                 val = wide_int_to_tree (repr_type, w);
+
+                 unsigned char buf[MAX_BITSIZE_MODE_ANY_INT
+                                   / BITS_PER_UNIT + 1];
+                 int l = native_encode_int (val, buf, sizeof buf, 0);
+                 if (l * BITS_PER_UNIT != TYPE_PRECISION (repr_type))
+                   return 0;
+
+                 if (ptr == NULL)
+                   continue;
+
+                 /* If the bitfield does not start at byte boundary, handle
+                    the partial byte at the start.  */
+                 if (bpos
+                     && (off == -1 || (pos >= off && len >= 1)))
+                   {
+                     if (!BYTES_BIG_ENDIAN)
+                       {
+                         int msk = (1 << bpos) - 1;
+                         buf[pos - rpos] &= ~msk;
+                         buf[pos - rpos] |= ptr[pos - o] & msk;
+                         if (mask)
+                           {
+                             if (fieldsize > 1 || epos == 0)
+                               mask[pos] &= msk;
+                             else
+                               mask[pos] &= (msk | ~((1 << epos) - 1));
+                           }
+                       }
+                     else
+                       {
+                         int msk = (1 << (BITS_PER_UNIT - bpos)) - 1;
+                         buf[pos - rpos] &= msk;
+                         buf[pos - rpos] |= ptr[pos - o] & ~msk;
+                         if (mask)
+                           {
+                             if (fieldsize > 1 || epos == 0)
+                               mask[pos] &= ~msk;
+                             else
+                               mask[pos] &= (~msk
+                                             | ((1 << (BITS_PER_UNIT - epos))
+                                                - 1));
+                           }
+                       }
+                   }
+                 /* If the bitfield does not end at byte boundary, handle
+                    the partial byte at the end.  */
+                 if (epos
+                     && (off == -1
+                         || pos + fieldsize <= (HOST_WIDE_INT) off + len))
+                   {
+                     if (!BYTES_BIG_ENDIAN)
+                       {
+                         int msk = (1 << epos) - 1;
+                         buf[pos - rpos + fieldsize - 1] &= msk;
+                         buf[pos - rpos + fieldsize - 1]
+                           |= ptr[pos + fieldsize - 1 - o] & ~msk;
+                         if (mask && (fieldsize > 1 || bpos == 0))
+                           mask[pos + fieldsize - 1] &= ~msk;
+                       }
+                      else
+                       {
+                         int msk = (1 << (BITS_PER_UNIT - epos)) - 1;
+                         buf[pos - rpos + fieldsize - 1] &= ~msk;
+                         buf[pos - rpos + fieldsize - 1]
+                           |= ptr[pos + fieldsize - 1 - o] & msk;
+                         if (mask && (fieldsize > 1 || bpos == 0))
+                           mask[pos + fieldsize - 1] &= msk;
+                       }
+                   }
+                 if (off == -1
+                     || (pos >= off
+                         && (pos + fieldsize <= (HOST_WIDE_INT) off + len)))
+                   {
+                     memcpy (ptr + pos - o, buf + (pos - rpos), fieldsize);
+                     if (mask && (fieldsize > (bpos != 0) + (epos != 0)))
+                       memset (mask + pos + (bpos != 0), 0,
+                               fieldsize - (bpos != 0) - (epos != 0));
+                   }
+                 else
+                   {
+                     /* Partial overlap.  */
+                     HOST_WIDE_INT fsz = fieldsize;
+                     gcc_assert (mask == NULL);
+                     if (pos < off)
+                       {
+                         fsz -= (off - pos);
+                         pos = off;
+                       }
+                     if (pos + fsz > (HOST_WIDE_INT) off + len)
+                       fsz = (HOST_WIDE_INT) off + len - pos;
+                     memcpy (ptr + pos - off, buf + (pos - rpos), fsz);
+                   }
+                 continue;
+               }
+
+             if (off == -1
+                 || (pos >= off
+                     && (pos + fieldsize <= (HOST_WIDE_INT) off + len)))
+               {
+                 int fldsize = fieldsize;
+                 if (off == -1)
+                   {
+                     tree fld = DECL_CHAIN (field);
+                     while (fld)
+                       {
+                         if (TREE_CODE (fld) == FIELD_DECL)
+                           break;
+                         fld = DECL_CHAIN (fld);
+                       }
+                     if (fld == NULL_TREE)
+                       fldsize = len - pos;
+                   }
+                 r = native_encode_initializer (val, ptr ? ptr + pos - o
+                                                         : NULL,
+                                                fldsize,
+                                                off == -1 ? -1 : 0,
+                                                mask ? mask + pos : NULL);
+                 if (!r)
+                   return 0;
+                 if (off == -1
+                     && fldsize != fieldsize
+                     && r > fieldsize
+                     && pos + r > total_bytes)
+                   total_bytes = pos + r;
+               }
+             else
+               {
+                 /* Partial overlap.  */
+                 unsigned char *p = NULL;
+                 int no = 0;
+                 int l;
+                 gcc_assert (mask == NULL);
+                 if (pos >= off)
+                   {
+                     if (ptr)
+                       p = ptr + pos - off;
+                     l = MIN ((HOST_WIDE_INT) off + len - pos,
+                               fieldsize);
+                   }
+                 else
+                   {
+                     p = ptr;
+                     no = off - pos;
+                     l = len;
+                   }
+                 if (!native_encode_initializer (val, p, l, no, NULL))
+                   return 0;
+               }
+           }
+         return MIN (total_bytes - off, len);
+       }
+      return 0;
+    }
+}
+
 
 /* Subroutine of native_interpret_expr.  Interpret the contents of
    the buffer PTR of length LEN as an INTEGER_CST of type TYPE.
@@ -7934,7 +8615,19 @@ native_interpret_real (tree type, const unsigned char *ptr, int len)
     }
 
   real_from_target (&r, tmp, mode);
-  return build_real (type, r);
+  tree ret = build_real (type, r);
+  if (MODE_COMPOSITE_P (mode))
+    {
+      /* For floating point values in composite modes, punt if this folding
+        doesn't preserve bit representation.  As the mode doesn't have fixed
+        precision while GCC pretends it does, there could be valid values that
+        GCC can't really represent accurately.  See PR95450.  */
+      unsigned char buf[24];
+      if (native_encode_expr (ret, buf, total_bytes, 0) != total_bytes
+         || memcmp (ptr, buf, total_bytes) != 0)
+       ret = NULL_TREE;
+    }
+  return ret;
 }
 
 
@@ -7961,17 +8654,66 @@ native_interpret_complex (tree type, const unsigned char *ptr, int len)
   return build_complex (type, rpart, ipart);
 }
 
+/* Read a vector of type TYPE from the target memory image given by BYTES,
+   which contains LEN bytes.  The vector is known to be encodable using
+   NPATTERNS interleaved patterns with NELTS_PER_PATTERN elements each.
 
-/* Subroutine of native_interpret_expr.  Interpret the contents of
-   the buffer PTR of length LEN as a VECTOR_CST of type TYPE.
-   If the buffer cannot be interpreted, return NULL_TREE.  */
+   Return the vector on success, otherwise return null.  */
 
 static tree
-native_interpret_vector (tree type, const unsigned char *ptr, unsigned int len)
+native_interpret_vector_part (tree type, const unsigned char *bytes,
+                             unsigned int len, unsigned int npatterns,
+                             unsigned int nelts_per_pattern)
 {
-  tree etype, elem;
-  unsigned int i, size;
-  unsigned HOST_WIDE_INT count;
+  tree elt_type = TREE_TYPE (type);
+  if (VECTOR_BOOLEAN_TYPE_P (type)
+      && TYPE_PRECISION (elt_type) <= BITS_PER_UNIT)
+    {
+      /* This is the only case in which elements can be smaller than a byte.
+        Element 0 is always in the lsb of the containing byte.  */
+      unsigned int elt_bits = TYPE_PRECISION (elt_type);
+      if (elt_bits * npatterns * nelts_per_pattern > len * BITS_PER_UNIT)
+       return NULL_TREE;
+
+      tree_vector_builder builder (type, npatterns, nelts_per_pattern);
+      for (unsigned int i = 0; i < builder.encoded_nelts (); ++i)
+       {
+         unsigned int bit_index = i * elt_bits;
+         unsigned int byte_index = bit_index / BITS_PER_UNIT;
+         unsigned int lsb = bit_index % BITS_PER_UNIT;
+         builder.quick_push (bytes[byte_index] & (1 << lsb)
+                             ? build_all_ones_cst (elt_type)
+                             : build_zero_cst (elt_type));
+       }
+      return builder.build ();
+    }
+
+  unsigned int elt_bytes = tree_to_uhwi (TYPE_SIZE_UNIT (elt_type));
+  if (elt_bytes * npatterns * nelts_per_pattern > len)
+    return NULL_TREE;
+
+  tree_vector_builder builder (type, npatterns, nelts_per_pattern);
+  for (unsigned int i = 0; i < builder.encoded_nelts (); ++i)
+    {
+      tree elt = native_interpret_expr (elt_type, bytes, elt_bytes);
+      if (!elt)
+       return NULL_TREE;
+      builder.quick_push (elt);
+      bytes += elt_bytes;
+    }
+  return builder.build ();
+}
+
+/* Subroutine of native_interpret_expr.  Interpret the contents of
+   the buffer PTR of length LEN as a VECTOR_CST of type TYPE.
+   If the buffer cannot be interpreted, return NULL_TREE.  */
+
+static tree
+native_interpret_vector (tree type, const unsigned char *ptr, unsigned int len)
+{
+  tree etype;
+  unsigned int size;
+  unsigned HOST_WIDE_INT count;
 
   etype = TREE_TYPE (type);
   size = GET_MODE_SIZE (SCALAR_TYPE_MODE (etype));
@@ -7979,15 +8721,7 @@ native_interpret_vector (tree type, const unsigned char *ptr, unsigned int len)
       || size * count > len)
     return NULL_TREE;
 
-  tree_vector_builder elements (type, count, 1);
-  for (i = 0; i < count; ++i)
-    {
-      elem = native_interpret_expr (etype, ptr+(i*size), size);
-      if (!elem)
-       return NULL_TREE;
-      elements.quick_push (elem);
-    }
-  return elements.build ();
+  return native_interpret_vector_part (type, ptr, len, count, 1);
 }
 
 
@@ -8029,7 +8763,7 @@ native_interpret_expr (tree type, const unsigned char *ptr, int len)
 /* Returns true if we can interpret the contents of a native encoding
    as TYPE.  */
 
-static bool
+bool
 can_native_interpret_type_p (tree type)
 {
   switch (TREE_CODE (type))
@@ -8049,6 +8783,291 @@ can_native_interpret_type_p (tree type)
     }
 }
 
+/* Attempt to interpret aggregate of TYPE from bytes encoded in target
+   byte order at PTR + OFF with LEN bytes.  Does not handle unions.  */
+
+tree
+native_interpret_aggregate (tree type, const unsigned char *ptr, int off,
+                           int len)
+{
+  vec<constructor_elt, va_gc> *elts = NULL;
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      HOST_WIDE_INT eltsz = int_size_in_bytes (TREE_TYPE (type));
+      if (eltsz < 0 || eltsz > len || TYPE_DOMAIN (type) == NULL_TREE)
+       return NULL_TREE;
+
+      HOST_WIDE_INT cnt = 0;
+      if (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
+       {
+         if (!tree_fits_shwi_p (TYPE_MAX_VALUE (TYPE_DOMAIN (type))))
+           return NULL_TREE;
+         cnt = tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + 1;
+       }
+      if (eltsz == 0)
+       cnt = 0;
+      HOST_WIDE_INT pos = 0;
+      for (HOST_WIDE_INT i = 0; i < cnt; i++, pos += eltsz)
+       {
+         tree v = NULL_TREE;
+         if (pos >= len || pos + eltsz > len)
+           return NULL_TREE;
+         if (can_native_interpret_type_p (TREE_TYPE (type)))
+           {
+             v = native_interpret_expr (TREE_TYPE (type),
+                                        ptr + off + pos, eltsz);
+             if (v == NULL_TREE)
+               return NULL_TREE;
+           }
+         else if (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE
+                  || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+           v = native_interpret_aggregate (TREE_TYPE (type), ptr, off + pos,
+                                           eltsz);
+         if (v == NULL_TREE)
+           return NULL_TREE;
+         CONSTRUCTOR_APPEND_ELT (elts, size_int (i), v);
+       }
+      return build_constructor (type, elts);
+    }
+  if (TREE_CODE (type) != RECORD_TYPE)
+    return NULL_TREE;
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL || DECL_PADDING_P (field))
+       continue;
+      tree fld = field;
+      HOST_WIDE_INT bitoff = 0, pos = 0, sz = 0;
+      int diff = 0;
+      tree v = NULL_TREE;
+      if (DECL_BIT_FIELD (field))
+       {
+         fld = DECL_BIT_FIELD_REPRESENTATIVE (field);
+         if (fld && INTEGRAL_TYPE_P (TREE_TYPE (fld)))
+           {
+             poly_int64 bitoffset;
+             poly_uint64 field_offset, fld_offset;
+             if (poly_int_tree_p (DECL_FIELD_OFFSET (field), &field_offset)
+                 && poly_int_tree_p (DECL_FIELD_OFFSET (fld), &fld_offset))
+               bitoffset = (field_offset - fld_offset) * BITS_PER_UNIT;
+             else
+               bitoffset = 0;
+             bitoffset += (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field))
+                           - tree_to_uhwi (DECL_FIELD_BIT_OFFSET (fld)));
+             diff = (TYPE_PRECISION (TREE_TYPE (fld))
+                     - TYPE_PRECISION (TREE_TYPE (field)));
+             if (!bitoffset.is_constant (&bitoff)
+                 || bitoff < 0
+                 || bitoff > diff)
+               return NULL_TREE;
+           }
+         else
+           {
+             if (!tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field)))
+               return NULL_TREE;
+             int fieldsize = TYPE_PRECISION (TREE_TYPE (field));
+             int bpos = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
+             bpos %= BITS_PER_UNIT;
+             fieldsize += bpos;
+             fieldsize += BITS_PER_UNIT - 1;
+             fieldsize /= BITS_PER_UNIT;
+             tree repr_type = find_bitfield_repr_type (fieldsize, len);
+             if (repr_type == NULL_TREE)
+               return NULL_TREE;
+             sz = int_size_in_bytes (repr_type);
+             if (sz < 0 || sz > len)
+               return NULL_TREE;
+             pos = int_byte_position (field);
+             if (pos < 0 || pos > len || pos + fieldsize > len)
+               return NULL_TREE;
+             HOST_WIDE_INT rpos;
+             if (pos + sz <= len)
+               rpos = pos;
+             else
+               {
+                 rpos = len - sz;
+                 gcc_assert (rpos <= pos);
+               }
+             bitoff = (HOST_WIDE_INT) (pos - rpos) * BITS_PER_UNIT + bpos;
+             pos = rpos;
+             diff = (TYPE_PRECISION (repr_type)
+                     - TYPE_PRECISION (TREE_TYPE (field)));
+             v = native_interpret_expr (repr_type, ptr + off + pos, sz);
+             if (v == NULL_TREE)
+               return NULL_TREE;
+             fld = NULL_TREE;
+           }
+       }
+
+      if (fld)
+       {
+         sz = int_size_in_bytes (TREE_TYPE (fld));
+         if (sz < 0 || sz > len)
+           return NULL_TREE;
+         tree byte_pos = byte_position (fld);
+         if (!tree_fits_shwi_p (byte_pos))
+           return NULL_TREE;
+         pos = tree_to_shwi (byte_pos);
+         if (pos < 0 || pos > len || pos + sz > len)
+           return NULL_TREE;
+       }
+      if (fld == NULL_TREE)
+       /* Already handled above.  */;
+      else if (can_native_interpret_type_p (TREE_TYPE (fld)))
+       {
+         v = native_interpret_expr (TREE_TYPE (fld),
+                                    ptr + off + pos, sz);
+         if (v == NULL_TREE)
+           return NULL_TREE;
+       }
+      else if (TREE_CODE (TREE_TYPE (fld)) == RECORD_TYPE
+              || TREE_CODE (TREE_TYPE (fld)) == ARRAY_TYPE)
+       v = native_interpret_aggregate (TREE_TYPE (fld), ptr, off + pos, sz);
+      if (v == NULL_TREE)
+       return NULL_TREE;
+      if (fld != field)
+       {
+         if (TREE_CODE (v) != INTEGER_CST)
+           return NULL_TREE;
+
+         /* FIXME: Figure out how to handle PDP endian bitfields.  */
+         if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+           return NULL_TREE;
+         if (!BYTES_BIG_ENDIAN)
+           v = wide_int_to_tree (TREE_TYPE (field),
+                                 wi::lrshift (wi::to_wide (v), bitoff));
+         else
+           v = wide_int_to_tree (TREE_TYPE (field),
+                                 wi::lrshift (wi::to_wide (v),
+                                              diff - bitoff));
+       }
+      CONSTRUCTOR_APPEND_ELT (elts, field, v);
+    }
+  return build_constructor (type, elts);
+}
+
+/* Routines for manipulation of native_encode_expr encoded data if the encoded
+   or extracted constant positions and/or sizes aren't byte aligned.  */
+
+/* Shift left the bytes in PTR of SZ elements by AMNT bits, carrying over the
+   bits between adjacent elements.  AMNT should be within
+   [0, BITS_PER_UNIT).
+   Example, AMNT = 2:
+   00011111|11100000 << 2 = 01111111|10000000
+   PTR[1]  | PTR[0]         PTR[1]  | PTR[0].  */
+
+void
+shift_bytes_in_array_left (unsigned char *ptr, unsigned int sz,
+                          unsigned int amnt)
+{
+  if (amnt == 0)
+    return;
+
+  unsigned char carry_over = 0U;
+  unsigned char carry_mask = (~0U) << (unsigned char) (BITS_PER_UNIT - amnt);
+  unsigned char clear_mask = (~0U) << amnt;
+
+  for (unsigned int i = 0; i < sz; i++)
+    {
+      unsigned prev_carry_over = carry_over;
+      carry_over = (ptr[i] & carry_mask) >> (BITS_PER_UNIT - amnt);
+
+      ptr[i] <<= amnt;
+      if (i != 0)
+       {
+         ptr[i] &= clear_mask;
+         ptr[i] |= prev_carry_over;
+       }
+    }
+}
+
+/* Like shift_bytes_in_array_left but for big-endian.
+   Shift right the bytes in PTR of SZ elements by AMNT bits, carrying over the
+   bits between adjacent elements.  AMNT should be within
+   [0, BITS_PER_UNIT).
+   Example, AMNT = 2:
+   00011111|11100000 >> 2 = 00000111|11111000
+   PTR[0]  | PTR[1]         PTR[0]  | PTR[1].  */
+
+void
+shift_bytes_in_array_right (unsigned char *ptr, unsigned int sz,
+                           unsigned int amnt)
+{
+  if (amnt == 0)
+    return;
+
+  unsigned char carry_over = 0U;
+  unsigned char carry_mask = ~(~0U << amnt);
+
+  for (unsigned int i = 0; i < sz; i++)
+    {
+      unsigned prev_carry_over = carry_over;
+      carry_over = ptr[i] & carry_mask;
+
+      carry_over <<= (unsigned char) BITS_PER_UNIT - amnt;
+      ptr[i] >>= amnt;
+      ptr[i] |= prev_carry_over;
+    }
+}
+
+/* Try to view-convert VECTOR_CST EXPR to VECTOR_TYPE TYPE by operating
+   directly on the VECTOR_CST encoding, in a way that works for variable-
+   length vectors.  Return the resulting VECTOR_CST on success or null
+   on failure.  */
+
+static tree
+fold_view_convert_vector_encoding (tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  poly_uint64 type_bits, expr_bits;
+  if (!poly_int_tree_p (TYPE_SIZE (type), &type_bits)
+      || !poly_int_tree_p (TYPE_SIZE (expr_type), &expr_bits))
+    return NULL_TREE;
+
+  poly_uint64 type_units = TYPE_VECTOR_SUBPARTS (type);
+  poly_uint64 expr_units = TYPE_VECTOR_SUBPARTS (expr_type);
+  unsigned int type_elt_bits = vector_element_size (type_bits, type_units);
+  unsigned int expr_elt_bits = vector_element_size (expr_bits, expr_units);
+
+  /* We can only preserve the semantics of a stepped pattern if the new
+     vector element is an integer of the same size.  */
+  if (VECTOR_CST_STEPPED_P (expr)
+      && (!INTEGRAL_TYPE_P (type) || type_elt_bits != expr_elt_bits))
+    return NULL_TREE;
+
+  /* The number of bits needed to encode one element from every pattern
+     of the original vector.  */
+  unsigned int expr_sequence_bits
+    = VECTOR_CST_NPATTERNS (expr) * expr_elt_bits;
+
+  /* The number of bits needed to encode one element from every pattern
+     of the result.  */
+  unsigned int type_sequence_bits
+    = least_common_multiple (expr_sequence_bits, type_elt_bits);
+
+  /* Don't try to read more bytes than are available, which can happen
+     for constant-sized vectors if TYPE has larger elements than EXPR_TYPE.
+     The general VIEW_CONVERT handling can cope with that case, so there's
+     no point complicating things here.  */
+  unsigned int nelts_per_pattern = VECTOR_CST_NELTS_PER_PATTERN (expr);
+  unsigned int buffer_bytes = CEIL (nelts_per_pattern * type_sequence_bits,
+                                   BITS_PER_UNIT);
+  unsigned int buffer_bits = buffer_bytes * BITS_PER_UNIT;
+  if (known_gt (buffer_bits, expr_bits))
+    return NULL_TREE;
+
+  /* Get enough bytes of EXPR to form the new encoding.  */
+  auto_vec<unsigned char, 128> buffer (buffer_bytes);
+  buffer.quick_grow (buffer_bytes);
+  if (native_encode_vector_part (expr, buffer.address (), buffer_bytes, 0,
+                                buffer_bits / expr_elt_bits)
+      != (int) buffer_bytes)
+    return NULL_TREE;
+
+  /* Reencode the bytes as TYPE.  */
+  unsigned int type_npatterns = type_sequence_bits / type_elt_bits;
+  return native_interpret_vector_part (type, &buffer[0], buffer.length (),
+                                      type_npatterns, nelts_per_pattern);
+}
 
 /* Fold a VIEW_CONVERT_EXPR of a constant expression EXPR to type
    TYPE at compile-time.  If we're unable to perform the conversion
@@ -8065,6 +9084,10 @@ fold_view_convert_expr (tree type, tree expr)
   if (CHAR_BIT != 8 || BITS_PER_UNIT != 8)
     return NULL_TREE;
 
+  if (VECTOR_TYPE_P (type) && TREE_CODE (expr) == VECTOR_CST)
+    if (tree res = fold_view_convert_vector_encoding (type, expr))
+      return res;
+
   len = native_encode_expr (expr, buffer, sizeof (buffer));
   if (len == 0)
     return NULL_TREE;
@@ -8091,7 +9114,12 @@ build_fold_addr_expr_with_type_loc (location_t loc, tree t, tree ptrtype)
     }
   else if (TREE_CODE (t) == MEM_REF
           && integer_zerop (TREE_OPERAND (t, 1)))
-    return TREE_OPERAND (t, 0);
+    {
+      t = TREE_OPERAND (t, 0);
+
+      if (TREE_TYPE (t) != ptrtype)
+       t = fold_convert_loc (loc, ptrtype, t);
+    }
   else if (TREE_CODE (t) == MEM_REF
           && TREE_CODE (TREE_OPERAND (t, 0)) == INTEGER_CST)
     return fold_binary (POINTER_PLUS_EXPR, ptrtype,
@@ -9628,8 +10656,7 @@ tree_expr_nonzero_p (tree t)
 bool
 expr_not_equal_to (tree t, const wide_int &w)
 {
-  wide_int min, max, nz;
-  value_range_kind rtype;
+  value_range vr;
   switch (TREE_CODE (t))
     {
     case INTEGER_CST:
@@ -9638,17 +10665,9 @@ expr_not_equal_to (tree t, const wide_int &w)
     case SSA_NAME:
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
        return false;
-      rtype = get_range_info (t, &min, &max);
-      if (rtype == VR_RANGE)
-       {
-         if (wi::lt_p (max, w, TYPE_SIGN (TREE_TYPE (t))))
-           return true;
-         if (wi::lt_p (w, min, TYPE_SIGN (TREE_TYPE (t))))
-           return true;
-       }
-      else if (rtype == VR_ANTI_RANGE
-              && wi::le_p (min, w, TYPE_SIGN (TREE_TYPE (t)))
-              && wi::le_p (w, max, TYPE_SIGN (TREE_TYPE (t))))
+      get_range_info (t, vr);
+      if (!vr.undefined_p ()
+         && !vr.contains_p (wide_int_to_tree (TREE_TYPE (t), w)))
        return true;
       /* If T has some known zero bits and W has any of those bits set,
         then T is known not to be equal to W.  */
@@ -9847,7 +10866,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type,
          if (!base)
            return NULL_TREE;
          return fold_build2 (MEM_REF, type,
-                             build_fold_addr_expr (base),
+                             build1 (ADDR_EXPR, TREE_TYPE (arg0), base),
                              int_const_binop (PLUS_EXPR, arg1,
                                               size_int (coffset)));
        }
@@ -10711,11 +11730,11 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type,
 
       /* Convert -A / -B to A / B when the type is signed and overflow is
         undefined.  */
-      if ((!INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_UNDEFINED (type))
+      if ((!ANY_INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_UNDEFINED (type))
          && TREE_CODE (op0) == NEGATE_EXPR
          && negate_expr_p (op1))
        {
-         if (INTEGRAL_TYPE_P (type))
+         if (ANY_INTEGRAL_TYPE_P (type))
            fold_overflow_warning (("assuming signed overflow does not occur "
                                    "when distributing negation across "
                                    "division"),
@@ -10725,11 +11744,11 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type,
                                                    TREE_OPERAND (arg0, 0)),
                                  negate_expr (op1));
        }
-      if ((!INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_UNDEFINED (type))
+      if ((!ANY_INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_UNDEFINED (type))
          && TREE_CODE (arg1) == NEGATE_EXPR
          && negate_expr_p (op0))
        {
-         if (INTEGRAL_TYPE_P (type))
+         if (ANY_INTEGRAL_TYPE_P (type))
            fold_overflow_warning (("assuming signed overflow does not occur "
                                    "when distributing negation across "
                                    "division"),
@@ -11039,45 +12058,53 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type,
         C1 is a valid shift constant, and C2 is a power of two, i.e.
         a single bit.  */
       if (TREE_CODE (arg0) == BIT_AND_EXPR
-         && TREE_CODE (TREE_OPERAND (arg0, 0)) == RSHIFT_EXPR
-         && TREE_CODE (TREE_OPERAND (TREE_OPERAND (arg0, 0), 1))
-            == INTEGER_CST
          && integer_pow2p (TREE_OPERAND (arg0, 1))
          && integer_zerop (arg1))
        {
-         tree itype = TREE_TYPE (arg0);
-         tree arg001 = TREE_OPERAND (TREE_OPERAND (arg0, 0), 1);
-         prec = TYPE_PRECISION (itype);
-
-         /* Check for a valid shift count.  */
-         if (wi::ltu_p (wi::to_wide (arg001), prec))
+         tree arg00 = TREE_OPERAND (arg0, 0);
+         STRIP_NOPS (arg00);
+         if (TREE_CODE (arg00) == RSHIFT_EXPR
+             && TREE_CODE (TREE_OPERAND (arg00, 1)) == INTEGER_CST)
            {
-             tree arg01 = TREE_OPERAND (arg0, 1);
-             tree arg000 = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
-             unsigned HOST_WIDE_INT log2 = tree_log2 (arg01);
-             /* If (C2 << C1) doesn't overflow, then ((X >> C1) & C2) != 0
-                can be rewritten as (X & (C2 << C1)) != 0.  */
-             if ((log2 + TREE_INT_CST_LOW (arg001)) < prec)
+             tree itype = TREE_TYPE (arg00);
+             tree arg001 = TREE_OPERAND (arg00, 1);
+             prec = TYPE_PRECISION (itype);
+
+             /* Check for a valid shift count.  */
+             if (wi::ltu_p (wi::to_wide (arg001), prec))
                {
-                 tem = fold_build2_loc (loc, LSHIFT_EXPR, itype, arg01, arg001);
-                 tem = fold_build2_loc (loc, BIT_AND_EXPR, itype, arg000, tem);
-                 return fold_build2_loc (loc, code, type, tem,
-                                         fold_convert_loc (loc, itype, arg1));
-               }
-             /* Otherwise, for signed (arithmetic) shifts,
-                ((X >> C1) & C2) != 0 is rewritten as X < 0, and
-                ((X >> C1) & C2) == 0 is rewritten as X >= 0.  */
-             else if (!TYPE_UNSIGNED (itype))
-               return fold_build2_loc (loc, code == EQ_EXPR ? GE_EXPR : LT_EXPR, type,
-                                   arg000, build_int_cst (itype, 0));
-             /* Otherwise, of unsigned (logical) shifts,
-                ((X >> C1) & C2) != 0 is rewritten as (X,false), and
-                ((X >> C1) & C2) == 0 is rewritten as (X,true).  */
-             else
-               return omit_one_operand_loc (loc, type,
+                 tree arg01 = TREE_OPERAND (arg0, 1);
+                 tree arg000 = TREE_OPERAND (arg00, 0);
+                 unsigned HOST_WIDE_INT log2 = tree_log2 (arg01);
+                 /* If (C2 << C1) doesn't overflow, then
+                    ((X >> C1) & C2) != 0 can be rewritten as
+                    (X & (C2 << C1)) != 0.  */
+                 if ((log2 + TREE_INT_CST_LOW (arg001)) < prec)
+                   {
+                     tem = fold_build2_loc (loc, LSHIFT_EXPR, itype,
+                                            arg01, arg001);
+                     tem = fold_build2_loc (loc, BIT_AND_EXPR, itype,
+                                            arg000, tem);
+                     return fold_build2_loc (loc, code, type, tem,
+                               fold_convert_loc (loc, itype, arg1));
+                   }
+                 /* Otherwise, for signed (arithmetic) shifts,
+                    ((X >> C1) & C2) != 0 is rewritten as X < 0, and
+                    ((X >> C1) & C2) == 0 is rewritten as X >= 0.  */
+                 else if (!TYPE_UNSIGNED (itype))
+                   return fold_build2_loc (loc, code == EQ_EXPR ? GE_EXPR
+                                                                : LT_EXPR,
+                                           type, arg000,
+                                           build_int_cst (itype, 0));
+                 /* Otherwise, of unsigned (logical) shifts,
+                    ((X >> C1) & C2) != 0 is rewritten as (X,false), and
+                    ((X >> C1) & C2) == 0 is rewritten as (X,true).  */
+                 else
+                   return omit_one_operand_loc (loc, type,
                                         code == EQ_EXPR ? integer_one_node
                                                         : integer_zero_node,
                                         arg000);
+               }
            }
        }
 
@@ -11194,50 +12221,6 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type,
          return omit_one_operand_loc (loc, type, res, arg0);
        }
 
-      /* Fold (X & C) op (Y & C) as (X ^ Y) & C op 0", and symmetries.  */
-      if (TREE_CODE (arg0) == BIT_AND_EXPR
-         && TREE_CODE (arg1) == BIT_AND_EXPR)
-       {
-         tree arg00 = TREE_OPERAND (arg0, 0);
-         tree arg01 = TREE_OPERAND (arg0, 1);
-         tree arg10 = TREE_OPERAND (arg1, 0);
-         tree arg11 = TREE_OPERAND (arg1, 1);
-         tree itype = TREE_TYPE (arg0);
-
-         if (operand_equal_p (arg01, arg11, 0))
-           {
-             tem = fold_convert_loc (loc, itype, arg10);
-             tem = fold_build2_loc (loc, BIT_XOR_EXPR, itype, arg00, tem);
-             tem = fold_build2_loc (loc, BIT_AND_EXPR, itype, tem, arg01);
-             return fold_build2_loc (loc, code, type, tem,
-                                     build_zero_cst (itype));
-           }
-         if (operand_equal_p (arg01, arg10, 0))
-           {
-             tem = fold_convert_loc (loc, itype, arg11);
-             tem = fold_build2_loc (loc, BIT_XOR_EXPR, itype, arg00, tem);
-             tem = fold_build2_loc (loc, BIT_AND_EXPR, itype, tem, arg01);
-             return fold_build2_loc (loc, code, type, tem,
-                                     build_zero_cst (itype));
-           }
-         if (operand_equal_p (arg00, arg11, 0))
-           {
-             tem = fold_convert_loc (loc, itype, arg10);
-             tem = fold_build2_loc (loc, BIT_XOR_EXPR, itype, arg01, tem);
-             tem = fold_build2_loc (loc, BIT_AND_EXPR, itype, tem, arg00);
-             return fold_build2_loc (loc, code, type, tem,
-                                     build_zero_cst (itype));
-           }
-         if (operand_equal_p (arg00, arg10, 0))
-           {
-             tem = fold_convert_loc (loc, itype, arg11);
-             tem = fold_build2_loc (loc, BIT_XOR_EXPR, itype, arg01, tem);
-             tem = fold_build2_loc (loc, BIT_AND_EXPR, itype, tem, arg00);
-             return fold_build2_loc (loc, code, type, tem,
-                                     build_zero_cst (itype));
-           }
-       }
-
       if (TREE_CODE (arg0) == BIT_XOR_EXPR
          && TREE_CODE (arg1) == BIT_XOR_EXPR)
        {
@@ -12047,7 +13030,9 @@ fold_ternary_loc (location_t loc, enum tree_code code, tree type,
          && tree_fits_uhwi_p (op2))
        {
          tree eltype = TREE_TYPE (TREE_TYPE (arg0));
-         unsigned HOST_WIDE_INT width = tree_to_uhwi (TYPE_SIZE (eltype));
+         unsigned HOST_WIDE_INT width
+           = (TREE_CODE (eltype) == BOOLEAN_TYPE
+              ? TYPE_PRECISION (eltype) : tree_to_uhwi (TYPE_SIZE (eltype)));
          unsigned HOST_WIDE_INT n = tree_to_uhwi (arg1);
          unsigned HOST_WIDE_INT idx = tree_to_uhwi (op2);
 
@@ -13126,6 +14111,248 @@ multiple_of_p (tree type, const_tree top, const_tree bottom)
     }
 }
 
+/* Return true if expression X cannot be (or contain) a NaN or infinity.
+   This function returns true for integer expressions, and returns
+   false if uncertain.  */
+
+bool
+tree_expr_finite_p (const_tree x)
+{
+  machine_mode mode = element_mode (x);
+  if (!HONOR_NANS (mode) && !HONOR_INFINITIES (mode))
+    return true;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isfinite (TREE_REAL_CST_PTR (x));
+    case COMPLEX_CST:
+      return tree_expr_finite_p (TREE_REALPART (x))
+            && tree_expr_finite_p (TREE_IMAGPART (x));
+    case FLOAT_EXPR:
+      return true;
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+    case NEGATE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 0))
+            && tree_expr_finite_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 1))
+            && tree_expr_finite_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_finite_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_finite_p (CALL_EXPR_ARG (x, 0))
+                && tree_expr_finite_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return false;
+       }
+
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X evaluates to an infinity.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_infinite_p (const_tree x)
+{
+  if (!HONOR_INFINITIES (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isinf (TREE_REAL_CST_PTR (x));
+    case ABS_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_infinite_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_infinite_p (TREE_OPERAND (x, 1))
+            && tree_expr_infinite_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to an infinity.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_infinite_p (const_tree x)
+{
+  if (!HONOR_INFINITIES (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isinf (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case ABS_EXPR:
+    case NEGATE_EXPR:
+      return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_infinite_p (TREE_OPERAND (x, 2));
+    default:
+      return true;
+    }
+}
+
+/* Return true if expression X evaluates to a signaling NaN.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_signaling_nan_p (const_tree x)
+{
+  if (!HONOR_SNANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_issignaling_nan (TREE_REAL_CST_PTR (x));
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_signaling_nan_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_signaling_nan_p (TREE_OPERAND (x, 1))
+            && tree_expr_signaling_nan_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to a signaling NaN.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_signaling_nan_p (const_tree x)
+{
+  if (!HONOR_SNANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_issignaling_nan (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0))
+            || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0))
+                || tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return true;
+       }
+    default:
+      return true;
+    }
+}
+
+/* Return true if expression X evaluates to a NaN.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_nan_p (const_tree x)
+{
+  if (!HONOR_NANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isnan (TREE_REAL_CST_PTR (x));
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_nan_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_nan_p (TREE_OPERAND (x, 1))
+            && tree_expr_nan_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to a NaN.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_nan_p (const_tree x)
+{
+  if (!HONOR_NANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isnan (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+      return !tree_expr_finite_p (TREE_OPERAND (x, 0))
+            || !tree_expr_finite_p (TREE_OPERAND (x, 1));
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0))
+            || tree_expr_maybe_nan_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_nan_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0))
+                || tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return true;
+       }
+    default:
+      return true;
+    }
+}
+
 #define tree_expr_nonnegative_warnv_p(X, Y) \
   _Pragma ("GCC error \"Use RECURSE for recursive calls\"") 0
 
@@ -13303,7 +14530,13 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
       return false;
 
     case BIT_AND_EXPR:
+      return RECURSE (op0) || RECURSE (op1);
+
     case MAX_EXPR:
+      /* Usually RECURSE (op0) || RECURSE (op1) but NaNs complicate
+        things.  */
+      if (tree_expr_maybe_nan_p (op0) || tree_expr_maybe_nan_p (op1))
+       return RECURSE (op0) && RECURSE (op1);
       return RECURSE (op0) || RECURSE (op1);
 
     case BIT_IOR_EXPR:
@@ -13401,8 +14634,10 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1,
     CASE_CFN_POPCOUNT:
     CASE_CFN_CLZ:
     CASE_CFN_CLRSB:
+    case CFN_BUILT_IN_BSWAP16:
     case CFN_BUILT_IN_BSWAP32:
     case CFN_BUILT_IN_BSWAP64:
+    case CFN_BUILT_IN_BSWAP128:
       /* Always true.  */
       return true;
 
@@ -13461,8 +14696,18 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1,
 
     CASE_CFN_FMAX:
     CASE_CFN_FMAX_FN:
-      /* True if the 1st OR 2nd arguments are nonnegative.  */
-      return RECURSE (arg0) || RECURSE (arg1);
+      /* Usually RECURSE (arg0) || RECURSE (arg1) but NaNs complicate
+        things.  In the presence of sNaNs, we're only guaranteed to be
+        non-negative if both operands are non-negative.  In the presence
+        of qNaNs, we're non-negative if either operand is non-negative
+        and can't be a qNaN, or if both operands are non-negative.  */
+      if (tree_expr_maybe_signaling_nan_p (arg0) ||
+         tree_expr_maybe_signaling_nan_p (arg1))
+        return RECURSE (arg0) && RECURSE (arg1);
+      return RECURSE (arg0) ? (!tree_expr_maybe_nan_p (arg0)
+                              || RECURSE (arg1))
+                           : (RECURSE (arg1)
+                              && !tree_expr_maybe_nan_p (arg1));
 
     CASE_CFN_FMIN:
     CASE_CFN_FMIN_FN:
@@ -14951,6 +16196,8 @@ ptr_difference_const (tree e1, tree e2, poly_int64_pod *diff)
 tree
 convert_to_ptrofftype_loc (location_t loc, tree off)
 {
+  if (ptrofftype_p (TREE_TYPE (off)))
+    return off;
   return fold_convert_loc (loc, sizetype, off);
 }
 
@@ -14970,24 +16217,32 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off)
                          ptr, size_int (off));
 }
 
-/* Return a pointer P to a NUL-terminated string representing the sequence
-   of constant characters referred to by SRC (or a subsequence of such
-   characters within it if SRC is a reference to a string plus some
-   constant offset).  If STRLEN is non-null, store the number of bytes
-   in the string constant including the terminating NUL char.  *STRLEN is
-   typically strlen(P) + 1 in the absence of embedded NUL characters.  */
+/* Return a pointer to a NUL-terminated string containing the sequence
+   of bytes corresponding to the representation of the object referred to
+   by SRC (or a subsequence of such bytes within it if SRC is a reference
+   to an initialized constant array plus some constant offset).
+   Set *STRSIZE the number of bytes in the constant sequence including
+   the terminating NUL byte.  *STRSIZE is equal to sizeof(A) - OFFSET
+   where A is the array that stores the constant sequence that SRC points
+   to and OFFSET is the byte offset of SRC from the beginning of A.  SRC
+   need not point to a string or even an array of characters but may point
+   to an object of any type.  */
 
 const char *
-c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */)
+getbyterep (tree src, unsigned HOST_WIDE_INT *strsize)
 {
+  /* The offset into the array A storing the string, and A's byte size.  */
   tree offset_node;
   tree mem_size;
 
-  if (strlen)
-    *strlen = 0;
+  if (strsize)
+    *strsize = 0;
 
-  src = string_constant (src, &offset_node, &mem_size, NULL);
-  if (src == 0)
+  if (strsize)
+    src = byte_representation (src, &offset_node, &mem_size, NULL);
+  else
+    src = string_constant (src, &offset_node, &mem_size, NULL);
+  if (!src)
     return NULL;
 
   unsigned HOST_WIDE_INT offset = 0;
@@ -15002,34 +16257,39 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */)
   if (!tree_fits_uhwi_p (mem_size))
     return NULL;
 
-  /* STRING_LENGTH is the size of the string literal, including any
-     embedded NULs.  STRING_SIZE is the size of the array the string
-     literal is stored in.  */
-  unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src);
-  unsigned HOST_WIDE_INT string_size = tree_to_uhwi (mem_size);
-
-  /* Ideally this would turn into a gcc_checking_assert over time.  */
-  if (string_length > string_size)
-    string_length = string_size;
-
+  /* ARRAY_SIZE is the byte size of the array the constant sequence
+     is stored in and equal to sizeof A.  INIT_BYTES is the number
+     of bytes in the constant sequence used to initialize the array,
+     including any embedded NULs as well as the terminating NUL (for
+     strings), but not including any trailing zeros/NULs past
+     the terminating one appended implicitly to a string literal to
+     zero out the remainder of the array it's stored in.  For example,
+     given:
+       const char a[7] = "abc\0d";
+       n = strlen (a + 1);
+     ARRAY_SIZE is 7, INIT_BYTES is 6, and OFFSET is 1.  For a valid
+     (i.e., nul-terminated) string with no embedded nuls, INIT_BYTES
+     is equal to strlen (A) + 1.  */
+  const unsigned HOST_WIDE_INT array_size = tree_to_uhwi (mem_size);
+  unsigned HOST_WIDE_INT init_bytes = TREE_STRING_LENGTH (src);
   const char *string = TREE_STRING_POINTER (src);
 
   /* Ideally this would turn into a gcc_checking_assert over time.  */
-  if (string_length > string_size)
-    string_length = string_size;
+  if (init_bytes > array_size)
+    init_bytes = array_size;
 
-  if (string_length == 0
-      || offset >= string_size)
+  if (init_bytes == 0 || offset >= array_size)
     return NULL;
 
-  if (strlen)
+  if (strsize)
     {
-      /* Compute and store the length of the substring at OFFSET.
-        All offsets past the initial length refer to null strings.  */
-      if (offset < string_length)
-       *strlen = string_length - offset;
+      /* Compute and store the number of characters from the beginning
+        of the substring at OFFSET to the end, including the terminating
+        nul.  Offsets past the initial length refer to null strings.  */
+      if (offset < init_bytes)
+       *strsize = init_bytes - offset;
       else
-       *strlen = 1;
+       *strsize = 1;
     }
   else
     {
@@ -15037,11 +16297,23 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */)
       /* Support only properly NUL-terminated single byte strings.  */
       if (tree_to_uhwi (TYPE_SIZE_UNIT (eltype)) != 1)
        return NULL;
-      if (string[string_length - 1] != '\0')
+      if (string[init_bytes - 1] != '\0')
        return NULL;
     }
 
-  return offset < string_length ? string + offset : "";
+  return offset < init_bytes ? string + offset : "";
+}
+
+/* Return a pointer to a NUL-terminated string corresponding to
+   the expression STR referencing a constant string, possibly
+   involving a constant offset.  Return null if STR either doesn't
+   reference a constant string or if it involves a nonconstant
+   offset.  */
+
+const char *
+c_getstr (tree str)
+{
+  return getbyterep (str, NULL);
 }
 
 /* Given a tree T, compute which bits in T may be nonzero.  */