poly_int: GET_MODE_BITSIZE
[platform/upstream/gcc.git] / gcc / gimple-fold.c
index 77c3e75..e99a78a 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement simplification on GIMPLE.
-   Copyright (C) 2010-2015 Free Software Foundation, Inc.
+   Copyright (C) 2010-2018 Free Software Foundation, Inc.
    Split out from tree-ssa-ccp.c.
 
 This file is part of GCC.
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stmt.h"
 #include "expr.h"
@@ -40,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "tree-into-ssa.h"
 #include "tree-dfa.h"
+#include "tree-object-size.h"
 #include "tree-ssa.h"
 #include "tree-ssa-propagate.h"
 #include "ipa-utils.h"
@@ -52,7 +54,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-match.h"
 #include "gomp-constants.h"
 #include "optabs-query.h"
-
+#include "omp-general.h"
+#include "ipa-chkp.h"
+#include "tree-cfg.h"
+#include "fold-const-call.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "asan.h"
+#include "diagnostic-core.h"
+#include "intl.h"
+#include "calls.h"
+#include "tree-vector-builder.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -88,7 +100,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
 
   /* We are concerned only about static/external vars and functions.  */
   if ((!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
-      || (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL))
+      || !VAR_OR_FUNCTION_DECL_P (decl))
     return true;
 
   /* Static objects can be referred only if they was not optimized out yet.  */
@@ -109,7 +121,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
      So we are concerned only when DECL comes from initializer of
      external var or var that has been optimized out.  */
   if (!from_decl
-      || TREE_CODE (from_decl) != VAR_DECL
+      || !VAR_P (from_decl)
       || (!DECL_EXTERNAL (from_decl)
          && (vnode = varpool_node::get (from_decl)) != NULL
          && vnode->definition)
@@ -154,6 +166,19 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
   return !node || !node->global.inlined_to;
 }
 
+/* Create a temporary for TYPE for a statement STMT.  If the current function
+   is in SSA form, a SSA name is created.  Otherwise a temporary register
+   is made.  */
+
+tree
+create_tmp_reg_or_ssa_name (tree type, gimple *stmt)
+{
+  if (gimple_in_ssa_p (cfun))
+    return make_ssa_name (type, stmt);
+  else
+    return create_tmp_reg (type);
+}
+
 /* CVAL is value taken from DECL_INITIAL of variable.  Try to transform it into
    acceptable form for is_gimple_min_invariant.
    FROM_DECL (if non-NULL) specify variable whose constructor contains CVAL.  */
@@ -189,11 +214,12 @@ canonicalize_constructor_val (tree cval, tree from_decl)
       if (!base)
        return NULL_TREE;
 
-      if ((TREE_CODE (base) == VAR_DECL
-          || TREE_CODE (base) == FUNCTION_DECL)
+      if (VAR_OR_FUNCTION_DECL_P (base)
          && !can_refer_decl_in_current_unit_p (base, from_decl))
        return NULL_TREE;
-      if (TREE_CODE (base) == VAR_DECL)
+      if (TREE_TYPE (base) == error_mark_node)
+       return NULL_TREE;
+      if (VAR_P (base))
        TREE_ADDRESSABLE (base) = 1;
       else if (TREE_CODE (base) == FUNCTION_DECL)
        {
@@ -538,7 +564,7 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
     }
   else
     {
-      tree tmp = get_initialized_tmp_var (expr, &stmts, NULL);
+      tree tmp = force_gimple_operand (expr, &stmts, false, NULL_TREE);
       new_stmt = gimple_build_assign (lhs, tmp);
       i = gsi_last (stmts);
       gsi_insert_after_without_update (&i, new_stmt,
@@ -553,7 +579,7 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 
 /* Replace the call at *GSI with the gimple value VAL.  */
 
-static void
+void
 replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
 {
   gimple *stmt = gsi_stmt (*gsi);
@@ -589,9 +615,10 @@ replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple *repl)
       && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
     {
       gimple_set_vdef (repl, gimple_vdef (stmt));
-      gimple_set_vuse (repl, gimple_vuse (stmt));
       SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
     }
+  if (gimple_vuse (stmt))
+    gimple_set_vuse (repl, gimple_vuse (stmt));
   gsi_replace (gsi, repl, false);
   fold_stmt (gsi);
 }
@@ -607,13 +634,42 @@ var_decl_component_p (tree var)
   return SSA_VAR_P (inner);
 }
 
-/* Fold function call to builtin mem{{,p}cpy,move}.  Return
-   false if no simplification can be made.
-   If ENDP is 0, return DEST (like memcpy).
-   If ENDP is 1, return DEST+LEN (like mempcpy).
-   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
-   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
-   (memmove).   */
+/* If the SIZE argument representing the size of an object is in a range
+   of values of which exactly one is valid (and that is zero), return
+   true, otherwise false.  */
+
+static bool
+size_must_be_zero_p (tree size)
+{
+  if (integer_zerop (size))
+    return true;
+
+  if (TREE_CODE (size) != SSA_NAME)
+    return false;
+
+  wide_int min, max;
+  enum value_range_type rtype = get_range_info (size, &min, &max);
+  if (rtype != VR_ANTI_RANGE)
+    return false;
+
+  tree type = TREE_TYPE (size);
+  int prec = TYPE_PRECISION (type);
+
+  wide_int wone = wi::one (prec);
+
+  /* Compute the value of SSIZE_MAX, the largest positive value that
+     can be stored in ssize_t, the signed counterpart of size_t.  */
+  wide_int ssize_max = wi::lshift (wi::one (prec), prec - 1) - 1;
+
+  return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);
+}
+
+/* Fold function call to builtin mem{{,p}cpy,move}.  Try to detect and
+   diagnose (otherwise undefined) overlapping copies without preventing
+   folding.  When folded, GCC guarantees that overlapping memcpy has
+   the same semantics as memmove.  Call to the library memcpy need not
+   provide the same guarantee.  Return false if no simplification can
+   be made.  */
 
 static bool
 gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
@@ -625,8 +681,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  /* If the LEN parameter is zero, return DEST.  */
-  if (integer_zerop (len))
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+                       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+                       && !nowarn);
+
+  /* If the LEN parameter is a constant zero or in range where
+     the only valid value is zero, return DEST.  */
+  if (size_must_be_zero_p (len))
     {
       gimple *repl;
       if (gimple_call_lhs (stmt))
@@ -647,6 +710,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+        It's safe and may even be emitted by GCC itself (see bug
+        32667).  However, diagnose it in explicit calls to the memcpy
+        function.  */
+      if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+       warning_at (loc, OPT_Wrestrict,
+                   "%qD source argument is the same as destination",
+                   func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
        release_ssa_name (gimple_vdef (stmt));
@@ -663,6 +735,18 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
       unsigned int src_align, dest_align;
       tree off0;
 
+      /* Inlining of memcpy/memmove may cause bounds lost (if we copy
+        pointers as wide integer) and also may result in huge function
+        size because of inlined bounds copy.  Thus don't inline for
+        functions we want to instrument.  */
+      if (flag_check_pointer_bounds
+         && chkp_instrumentable_p (cfun->decl)
+         /* Even if data may contain pointers we can inline if copy
+            less than a pointer size.  */
+         && (!tree_fits_uhwi_p (len)
+             || compare_tree_int (len, POINTER_SIZE_UNITS) >= 0))
+       return false;
+
       /* Build accesses at offset zero with a ref-all character type.  */
       off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
                                                         ptr_mode, true), 0);
@@ -682,33 +766,38 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          && !c_strlen (src, 2))
        {
          unsigned ilen = tree_to_uhwi (len);
-         if (exact_log2 (ilen) != -1)
+         if (pow2p_hwi (ilen))
            {
+             /* Detect invalid bounds and overlapping copies and issue
+                either -Warray-bounds or -Wrestrict.  */
+             if (!nowarn
+                 && check_bounds_or_overlap (as_a <gcall *>(stmt),
+                                             dest, src, len, len))
+               gimple_set_no_warning (stmt, true);
+
+             scalar_int_mode mode;
              tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
              if (type
-                 && TYPE_MODE (type) != BLKmode
-                 && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
-                     == ilen * 8)
+                 && is_a <scalar_int_mode> (TYPE_MODE (type), &mode)
+                 && GET_MODE_SIZE (mode) * BITS_PER_UNIT == ilen * 8
                  /* If the destination pointer is not aligned we must be able
                     to emit an unaligned store.  */
-                 && (dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
-                     || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type), dest_align)
-                     || (optab_handler (movmisalign_optab, TYPE_MODE (type))
+                 && (dest_align >= GET_MODE_ALIGNMENT (mode)
+                     || !targetm.slow_unaligned_access (mode, dest_align)
+                     || (optab_handler (movmisalign_optab, mode)
                          != CODE_FOR_nothing)))
                {
                  tree srctype = type;
                  tree desttype = type;
-                 if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                 if (src_align < GET_MODE_ALIGNMENT (mode))
                    srctype = build_aligned_type (type, src_align);
                  tree srcmem = fold_build2 (MEM_REF, srctype, src, off0);
                  tree tem = fold_const_aggregate_ref (srcmem);
                  if (tem)
                    srcmem = tem;
-                 else if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type))
-                          && SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
-                                                    src_align)
-                          && (optab_handler (movmisalign_optab,
-                                             TYPE_MODE (type))
+                 else if (src_align < GET_MODE_ALIGNMENT (mode)
+                          && targetm.slow_unaligned_access (mode, src_align)
+                          && (optab_handler (movmisalign_optab, mode)
                               == CODE_FOR_nothing))
                    srcmem = NULL_TREE;
                  if (srcmem)
@@ -717,16 +806,14 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                      if (is_gimple_reg_type (TREE_TYPE (srcmem)))
                        {
                          new_stmt = gimple_build_assign (NULL_TREE, srcmem);
-                         if (gimple_in_ssa_p (cfun))
-                           srcmem = make_ssa_name (TREE_TYPE (srcmem),
-                                                   new_stmt);
-                         else
-                           srcmem = create_tmp_reg (TREE_TYPE (srcmem));
+                         srcmem
+                           = create_tmp_reg_or_ssa_name (TREE_TYPE (srcmem),
+                                                         new_stmt);
                          gimple_assign_set_lhs (new_stmt, srcmem);
                          gimple_set_vuse (new_stmt, gimple_vuse (stmt));
                          gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
                        }
-                     if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                     if (dest_align < GET_MODE_ALIGNMENT (mode))
                        desttype = build_aligned_type (type, dest_align);
                      new_stmt
                        = gimple_build_assign (fold_build2 (MEM_REF, desttype,
@@ -778,28 +865,26 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
              && TREE_CODE (dest) == ADDR_EXPR)
            {
              tree src_base, dest_base, fn;
-             HOST_WIDE_INT src_offset = 0, dest_offset = 0;
-             HOST_WIDE_INT size = -1;
-             HOST_WIDE_INT maxsize = -1;
+             poly_int64 src_offset = 0, dest_offset = 0;
+             poly_uint64 maxsize;
 
              srcvar = TREE_OPERAND (src, 0);
-             src_base = get_ref_base_and_extent (srcvar, &src_offset,
-                                                 &size, &maxsize);
+             src_base = get_addr_base_and_unit_offset (srcvar, &src_offset);
+             if (src_base == NULL)
+               src_base = srcvar;
              destvar = TREE_OPERAND (dest, 0);
-             dest_base = get_ref_base_and_extent (destvar, &dest_offset,
-                                                  &size, &maxsize);
-             if (tree_fits_uhwi_p (len))
-               maxsize = tree_to_uhwi (len);
-             else
+             dest_base = get_addr_base_and_unit_offset (destvar,
+                                                        &dest_offset);
+             if (dest_base == NULL)
+               dest_base = destvar;
+             if (!poly_int_tree_p (len, &maxsize))
                maxsize = -1;
-             src_offset /= BITS_PER_UNIT;
-             dest_offset /= BITS_PER_UNIT;
              if (SSA_VAR_P (src_base)
                  && SSA_VAR_P (dest_base))
                {
                  if (operand_equal_p (src_base, dest_base, 0)
-                     && ranges_overlap_p (src_offset, maxsize,
-                                          dest_offset, maxsize))
+                     && ranges_maybe_overlap_p (src_offset, maxsize,
+                                                dest_offset, maxsize))
                    return false;
                }
              else if (TREE_CODE (src_base) == MEM_REF
@@ -808,17 +893,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                  if (! operand_equal_p (TREE_OPERAND (src_base, 0),
                                         TREE_OPERAND (dest_base, 0), 0))
                    return false;
-                 offset_int off = mem_ref_offset (src_base) + src_offset;
-                 if (!wi::fits_shwi_p (off))
-                   return false;
-                 src_offset = off.to_shwi ();
-
-                 off = mem_ref_offset (dest_base) + dest_offset;
-                 if (!wi::fits_shwi_p (off))
-                   return false;
-                 dest_offset = off.to_shwi ();
-                 if (ranges_overlap_p (src_offset, maxsize,
-                                       dest_offset, maxsize))
+                 poly_offset_int full_src_offset
+                   = mem_ref_offset (src_base) + src_offset;
+                 poly_offset_int full_dest_offset
+                   = mem_ref_offset (dest_base) + dest_offset;
+                 if (ranges_maybe_overlap_p (full_src_offset, maxsize,
+                                             full_dest_offset, maxsize))
                    return false;
                }
              else
@@ -863,12 +943,6 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 
       if (!tree_fits_shwi_p (len))
        return false;
-      /* FIXME:
-         This logic lose for arguments like (type *)malloc (sizeof (type)),
-         since we strip the casts of up to VOID return value from malloc.
-        Perhaps we ought to inherit type from non-VOID argument here?  */
-      STRIP_NOPS (src);
-      STRIP_NOPS (dest);
       if (!POINTER_TYPE_P (TREE_TYPE (src))
          || !POINTER_TYPE_P (TREE_TYPE (dest)))
        return false;
@@ -878,37 +952,14 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
         using that type.  In theory we could always use a char[len] type
         but that only gains us that the destination and source possibly
         no longer will have their address taken.  */
-      /* As we fold (void *)(p + CST) to (void *)p + CST undo this here.  */
-      if (TREE_CODE (src) == POINTER_PLUS_EXPR)
-       {
-         tree tem = TREE_OPERAND (src, 0);
-         STRIP_NOPS (tem);
-         if (tem != TREE_OPERAND (src, 0))
-           src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
-       }
-      if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
-       {
-         tree tem = TREE_OPERAND (dest, 0);
-         STRIP_NOPS (tem);
-         if (tem != TREE_OPERAND (dest, 0))
-           dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
-       }
       srctype = TREE_TYPE (TREE_TYPE (src));
       if (TREE_CODE (srctype) == ARRAY_TYPE
          && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
-       {
-         srctype = TREE_TYPE (srctype);
-         STRIP_NOPS (src);
-         src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
-       }
+       srctype = TREE_TYPE (srctype);
       desttype = TREE_TYPE (TREE_TYPE (dest));
       if (TREE_CODE (desttype) == ARRAY_TYPE
          && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
-       {
-         desttype = TREE_TYPE (desttype);
-         STRIP_NOPS (dest);
-         dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
-       }
+       desttype = TREE_TYPE (desttype);
       if (TREE_ADDRESSABLE (srctype)
          || TREE_ADDRESSABLE (desttype))
        return false;
@@ -936,43 +987,34 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          || src_align < TYPE_ALIGN (srctype))
        return false;
 
-      destvar = dest;
-      STRIP_NOPS (destvar);
-      if (TREE_CODE (destvar) == ADDR_EXPR
-         && var_decl_component_p (TREE_OPERAND (destvar, 0))
+      destvar = NULL_TREE;
+      if (TREE_CODE (dest) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (dest, 0))
          && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
-       destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
-      else
-       destvar = NULL_TREE;
+       destvar = fold_build2 (MEM_REF, desttype, dest, off0);
 
-      srcvar = src;
-      STRIP_NOPS (srcvar);
-      if (TREE_CODE (srcvar) == ADDR_EXPR
-         && var_decl_component_p (TREE_OPERAND (srcvar, 0))
+      srcvar = NULL_TREE;
+      if (TREE_CODE (src) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (src, 0))
          && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
        {
          if (!destvar
              || src_align >= TYPE_ALIGN (desttype))
            srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
-                                 srcvar, off0);
+                                 src, off0);
          else if (!STRICT_ALIGNMENT)
            {
              srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
                                            src_align);
-             srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
+             srcvar = fold_build2 (MEM_REF, srctype, src, off0);
            }
-         else
-           srcvar = NULL_TREE;
        }
-      else
-       srcvar = NULL_TREE;
 
       if (srcvar == NULL_TREE && destvar == NULL_TREE)
        return false;
 
       if (srcvar == NULL_TREE)
        {
-         STRIP_NOPS (src);
          if (src_align >= TYPE_ALIGN (desttype))
            srcvar = fold_build2 (MEM_REF, desttype, src, off0);
          else
@@ -986,7 +1028,6 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
        }
       else if (destvar == NULL_TREE)
        {
-         STRIP_NOPS (dest);
          if (dest_align >= TYPE_ALIGN (srctype))
            destvar = fold_build2 (MEM_REF, srctype, dest, off0);
          else
@@ -999,19 +1040,44 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
            }
        }
 
+      /* Detect invalid bounds and overlapping copies and issue either
+        -Warray-bounds or -Wrestrict.  */
+      if (!nowarn)
+       check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
        {
-         new_stmt = gimple_build_assign (NULL_TREE, srcvar);
-         if (gimple_in_ssa_p (cfun))
-           srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
-         else
-           srcvar = create_tmp_reg (TREE_TYPE (srcvar));
-         gimple_assign_set_lhs (new_stmt, srcvar);
-         gimple_set_vuse (new_stmt, gimple_vuse (stmt));
-         gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+         tree tem = fold_const_aggregate_ref (srcvar);
+         if (tem)
+           srcvar = tem;
+         if (! is_gimple_min_invariant (srcvar))
+           {
+             new_stmt = gimple_build_assign (NULL_TREE, srcvar);
+             srcvar = create_tmp_reg_or_ssa_name (TREE_TYPE (srcvar),
+                                                  new_stmt);
+             gimple_assign_set_lhs (new_stmt, srcvar);
+             gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+             gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+           }
+         new_stmt = gimple_build_assign (destvar, srcvar);
+         goto set_vop_and_replace;
        }
-      new_stmt = gimple_build_assign (destvar, srcvar);
+
+      /* We get an aggregate copy.  Use an unsigned char[] type to
+        perform the copying to preserve padding and to avoid any issues
+        with TREE_ADDRESSABLE types or float modes behavior on copying.  */
+      desttype = build_array_type_nelts (unsigned_char_type_node,
+                                        tree_to_uhwi (len));
+      srctype = desttype;
+      if (src_align > TYPE_ALIGN (srctype))
+       srctype = build_aligned_type (srctype, src_align);
+      if (dest_align > TYPE_ALIGN (desttype))
+       desttype = build_aligned_type (desttype, dest_align);
+      new_stmt
+       = gimple_build_assign (fold_build2 (MEM_REF, desttype, dest, off0),
+                              fold_build2 (MEM_REF, srctype, src, off0));
+set_vop_and_replace:
       gimple_set_vuse (new_stmt, gimple_vuse (stmt));
       gimple_set_vdef (new_stmt, gimple_vdef (stmt));
       if (gimple_vdef (new_stmt)
@@ -1045,6 +1111,83 @@ done:
   return true;
 }
 
+/* Transform a call to built-in bcmp(a, b, len) at *GSI into one
+   to built-in memcmp (a, b, len).  */
+
+static bool
+gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP);
+
+  if (!fn)
+    return false;
+
+  /* Transform bcmp (a, b, len) into memcmp (a, b, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree a = gimple_call_arg (stmt, 0);
+  tree b = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  gimple *repl = gimple_build_call (fn, 3, a, b, len);
+  replace_call_with_call_and_fold (gsi, repl);
+
+  return true;
+}
+
+/* Transform a call to built-in bcopy (src, dest, len) at *GSI into one
+   to built-in memmove (dest, src, len).  */
+
+static bool
+gimple_fold_builtin_bcopy (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMMOVE);
+
+  if (!fn)
+    return false;
+
+  /* bcopy has been removed from POSIX in Issue 7 but Issue 6 specifies
+     it's quivalent to memmove (not memcpy).  Transform bcopy (src, dest,
+     len) into memmove (dest, src, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree src = gimple_call_arg (stmt, 0);
+  tree dest = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
+  gimple_call_set_fntype (as_a <gcall *> (stmt), TREE_TYPE (fn));
+  replace_call_with_call_and_fold (gsi, repl);
+
+  return true;
+}
+
+/* Transform a call to built-in bzero (dest, len) at *GSI into one
+   to built-in memset (dest, 0, len).  */
+
+static bool
+gimple_fold_builtin_bzero (gimple_stmt_iterator *gsi)
+{
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMSET);
+
+  if (!fn)
+    return false;
+
+  /* Transform bzero (dest, len) into memset (dest, 0, len).  */
+
+  gimple *stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree len = gimple_call_arg (stmt, 1);
+
+  gimple_seq seq = NULL;
+  gimple *repl = gimple_build_call (fn, 3, dest, integer_zero_node, len);
+  gimple_seq_add_stmt_without_update (&seq, repl);
+  gsi_replace_with_seq_vops (gsi, seq);
+  fold_stmt (gsi);
+
+  return true;
+}
+
 /* Fold function call to builtin memset or bzero at *GSI setting the
    memory of size LEN to VAL.  Return whether a simplification was made.  */
 
@@ -1089,7 +1232,7 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
     return NULL_TREE;
 
   length = tree_to_uhwi (len);
-  if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
+  if (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (etype)) != length
       || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
     return NULL_TREE;
 
@@ -1136,21 +1279,34 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
 }
 
 
-/* Return the string length, maximum string length or maximum value of
-   ARG in LENGTH.
-   If ARG is an SSA name variable, follow its use-def chains.  If LENGTH
-   is not NULL and, for TYPE == 0, its value is not equal to the length
-   we determine or if we are unable to determine the length or value,
-   return false.  VISITED is a bitmap of visited variables.
-   TYPE is 0 if string length should be returned, 1 for maximum string
-   length and 2 for maximum value ARG can have.  */
+/* Obtain the minimum and maximum string length or minimum and maximum
+   value of ARG in LENGTH[0] and LENGTH[1], respectively.
+   If ARG is an SSA name variable, follow its use-def chains.  When
+   TYPE == 0, if LENGTH[1] is not equal to the length we determine or
+   if we are unable to determine the length or value, return False.
+   VISITED is a bitmap of visited variables.
+   TYPE is 0 if string length should be obtained, 1 for maximum string
+   length and 2 for maximum value ARG can have.
+   When FUZZY is set and the length of a string cannot be determined,
+   the function instead considers as the maximum possible length the
+   size of a character array it may refer to.
+   Set *FLEXP to true if the range of the string lengths has been
+   obtained from the upper bound of an array at the end of a struct.
+   Such an array may hold a string that's longer than its upper bound
+   due to it being used as a poor-man's flexible array member.  */
 
 static bool
-get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
+get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
+                 bool fuzzy, bool *flexp)
 {
   tree var, val;
   gimple *def_stmt;
 
+  /* The minimum and maximum length.  The MAXLEN pointer stays unchanged
+     but MINLEN may be cleared during the execution of the function.  */
+  tree *minlen = length;
+  tree *const maxlen = length + 1;
+
   if (TREE_CODE (arg) != SSA_NAME)
     {
       /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
@@ -1161,8 +1317,8 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
          tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
          if (TREE_CODE (aop0) == INDIRECT_REF
              && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
-           return get_maxval_strlen (TREE_OPERAND (aop0, 0),
-                                     length, visited, type);
+           return get_range_strlen (TREE_OPERAND (aop0, 0),
+                                    length, visited, type, fuzzy, flexp);
        }
 
       if (type == 2)
@@ -1174,26 +1330,79 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
        }
       else
        val = c_strlen (arg, 1);
+
+      if (!val && fuzzy)
+       {
+         if (TREE_CODE (arg) == ADDR_EXPR)
+           return get_range_strlen (TREE_OPERAND (arg, 0), length,
+                                    visited, type, fuzzy, flexp);
+
+         if (TREE_CODE (arg) == COMPONENT_REF
+             && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
+           {
+             /* Use the type of the member array to determine the upper
+                bound on the length of the array.  This may be overly
+                optimistic if the array itself isn't NUL-terminated and
+                the caller relies on the subsequent member to contain
+                the NUL.
+                Set *FLEXP to true if the array whose bound is being
+                used is at the end of a struct.  */
+             if (array_at_struct_end_p (arg))
+               *flexp = true;
+
+             arg = TREE_OPERAND (arg, 1);
+             val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+             if (!val || integer_zerop (val))
+               return false;
+             val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+                                integer_one_node);
+             /* Set the minimum size to zero since the string in
+                the array could have zero length.  */
+             *minlen = ssize_int (0);
+           }
+
+         if (VAR_P (arg) 
+             && TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+           {
+             val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+             if (!val || TREE_CODE (val) != INTEGER_CST || integer_zerop (val))
+               return false;
+             val = wide_int_to_tree (TREE_TYPE (val), 
+                                     wi::sub(wi::to_wide (val), 1));
+             /* Set the minimum size to zero since the string in
+                the array could have zero length.  */
+             *minlen = ssize_int (0);
+           }
+       }
+
       if (!val)
        return false;
 
-      if (*length)
+      if (minlen
+         && (!*minlen
+             || (type > 0
+                 && TREE_CODE (*minlen) == INTEGER_CST
+                 && TREE_CODE (val) == INTEGER_CST
+                 && tree_int_cst_lt (val, *minlen))))
+       *minlen = val;
+
+      if (*maxlen)
        {
          if (type > 0)
            {
-             if (TREE_CODE (*length) != INTEGER_CST
+             if (TREE_CODE (*maxlen) != INTEGER_CST
                  || TREE_CODE (val) != INTEGER_CST)
                return false;
 
-             if (tree_int_cst_lt (*length, val))
-               *length = val;
+             if (tree_int_cst_lt (*maxlen, val))
+               *maxlen = val;
              return true;
            }
-         else if (simple_cst_equal (val, *length) != 1)
+         else if (simple_cst_equal (val, *maxlen) != 1)
            return false;
        }
 
-      *length = val;
+      *maxlen = val;
       return true;
     }
 
@@ -1221,15 +1430,15 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
             || gimple_assign_unary_nop_p (def_stmt))
           {
             tree rhs = gimple_assign_rhs1 (def_stmt);
-            return get_maxval_strlen (rhs, length, visited, type);
+           return get_range_strlen (rhs, length, visited, type, fuzzy, flexp);
           }
        else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
          {
            tree op2 = gimple_assign_rhs2 (def_stmt);
            tree op3 = gimple_assign_rhs3 (def_stmt);
-           return get_maxval_strlen (op2, length, visited, type)
-                  && get_maxval_strlen (op3, length, visited, type);
-          }
+           return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
+             && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
+         }
         return false;
 
       case GIMPLE_PHI:
@@ -1251,8 +1460,13 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
             if (arg == gimple_phi_result (def_stmt))
               continue;
 
-            if (!get_maxval_strlen (arg, length, visited, type))
-              return false;
+           if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp))
+             {
+               if (fuzzy)
+                 *maxlen = build_all_ones_cst (size_type_node);
+               else
+                 return false;
+             }
           }
         }
         return true;
@@ -1262,17 +1476,49 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
     }
 }
 
+/* Determine the minimum and maximum value or string length that ARG
+   refers to and store each in the first two elements of MINMAXLEN.
+   For expressions that point to strings of unknown lengths that are
+   character arrays, use the upper bound of the array as the maximum
+   length.  For example, given an expression like 'x ? array : "xyz"'
+   and array declared as 'char array[8]', MINMAXLEN[0] will be set
+   to 3 and MINMAXLEN[1] to 7, the longest string that could be
+   stored in array.
+   Return true if the range of the string lengths has been obtained
+   from the upper bound of an array at the end of a struct.  Such
+   an array may hold a string that's longer than its upper bound
+   due to it being used as a poor-man's flexible array member.  */
+
+bool
+get_range_strlen (tree arg, tree minmaxlen[2])
+{
+  bitmap visited = NULL;
+
+  minmaxlen[0] = NULL_TREE;
+  minmaxlen[1] = NULL_TREE;
+
+  bool flexarray = false;
+  get_range_strlen (arg, minmaxlen, &visited, 1, true, &flexarray);
+
+  if (visited)
+    BITMAP_FREE (visited);
+
+  return flexarray;
+}
+
 tree
 get_maxval_strlen (tree arg, int type)
 {
   bitmap visited = NULL;
-  tree len = NULL_TREE;
-  if (!get_maxval_strlen (arg, &len, &visited, type))
-    len = NULL_TREE;
+  tree len[2] = { NULL_TREE, NULL_TREE };
+
+  bool dummy;
+  if (!get_range_strlen (arg, len, &visited, type, false, &dummy))
+    len[1] = NULL_TREE;
   if (visited)
     BITMAP_FREE (visited);
 
-  return len;
+  return len[1];
 }
 
 
@@ -1284,12 +1530,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
                            tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+                 "%qD source argument is the same as destination",
+                 func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -1322,12 +1575,34 @@ static bool
 gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
                             tree dest, tree src, tree len)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
-  tree fn;
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  bool nonstring = get_attr_nonstring_decl (dest) != NULL_TREE;
 
   /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
+      /* Avoid warning if the destination refers to a an array/pointer
+        decorate with attribute nonstring.  */
+      if (!nonstring)
+       {
+         tree fndecl = gimple_call_fndecl (stmt);
+         gcall *call = as_a <gcall *> (stmt);
+
+         /* Warn about the lack of nul termination: the result is not
+            a (nul-terminated) string.  */
+         tree slen = get_maxval_strlen (src, 0);
+         if (slen && !integer_zerop (slen))
+           warning_at (loc, OPT_Wstringop_truncation,
+                       "%G%qD destination unchanged after copying no bytes "
+                       "from a string of length %E",
+                       call, fndecl, slen);
+         else
+           warning_at (loc, OPT_Wstringop_truncation,
+                       "%G%qD destination unchanged after copying no bytes",
+                       call, fndecl);
+       }
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -1342,16 +1617,49 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
   if (!slen || TREE_CODE (slen) != INTEGER_CST)
     return false;
 
-  slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+  /* The size of the source string including the terminating nul.  */
+  tree ssize = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
 
   /* We do not support simplification of this case, though we do
      support it when expanding trees into RTL.  */
   /* FIXME: generate a call to __builtin_memset.  */
-  if (tree_int_cst_lt (slen, len))
+  if (tree_int_cst_lt (ssize, len))
     return false;
 
+  if (!nonstring)
+    {
+      if (tree_int_cst_lt (len, slen))
+       {
+         tree fndecl = gimple_call_fndecl (stmt);
+         gcall *call = as_a <gcall *> (stmt);
+
+         warning_at (loc, OPT_Wstringop_truncation,
+                     (tree_int_cst_equal (size_one_node, len)
+                      ? G_("%G%qD output truncated copying %E byte "
+                           "from a string of length %E")
+                      : G_("%G%qD output truncated copying %E bytes "
+                           "from a string of length %E")),
+                     call, fndecl, len, slen);
+       }
+      else if (tree_int_cst_equal (len, slen))
+       {
+         tree fndecl = gimple_call_fndecl (stmt);
+         gcall *call = as_a <gcall *> (stmt);
+
+         warning_at (loc, OPT_Wstringop_truncation,
+                     (tree_int_cst_equal (size_one_node, len)
+                      ? G_("%G%qD output truncated before terminating nul "
+                           "copying %E byte from a string of the same "
+                           "length")
+                      : G_("%G%qD output truncated before terminating nul "
+                           "copying %E bytes from a string of the same "
+                           "length")),
+                     call, fndecl, len);
+       }
+    }
+
   /* OK transform into builtin memcpy.  */
-  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
   if (!fn)
     return false;
 
@@ -1360,9 +1668,156 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
                                  NULL_TREE, true, GSI_SAME_STMT);
   gimple *repl = gimple_build_call (fn, 3, dest, src, len);
   replace_call_with_call_and_fold (gsi, repl);
+
   return true;
 }
 
+/* Fold function call to builtin strchr or strrchr.
+   If both arguments are constant, evaluate and fold the result,
+   otherwise simplify str(r)chr (str, 0) into str + strlen (str).
+   In general strlen is significantly faster than strchr
+   due to being a simpler operation.  */
+static bool
+gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree str = gimple_call_arg (stmt, 0);
+  tree c = gimple_call_arg (stmt, 1);
+  location_t loc = gimple_location (stmt);
+  const char *p;
+  char ch;
+
+  if (!gimple_call_lhs (stmt))
+    return false;
+
+  if ((p = c_getstr (str)) && target_char_cst_p (c, &ch))
+    {
+      const char *p1 = is_strrchr ? strrchr (p, ch) : strchr (p, ch);
+
+      if (p1 == NULL)
+       {
+         replace_call_with_value (gsi, integer_zero_node);
+         return true;
+       }
+
+      tree len = build_int_cst (size_type_node, p1 - p);
+      gimple_seq stmts = NULL;
+      gimple *new_stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                             POINTER_PLUS_EXPR, str, len);
+      gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  if (!integer_zerop (c))
+    return false;
+
+  /* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size.  */
+  if (is_strrchr && optimize_function_for_size_p (cfun))
+    {
+      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+
+      if (strchr_fn)
+       {
+         gimple *repl = gimple_build_call (strchr_fn, 2, str, c);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+
+      return false;
+    }
+
+  tree len;
+  tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+
+  if (!strlen_fn)
+    return false;
+
+  /* Create newstr = strlen (str).  */
+  gimple_seq stmts = NULL;
+  gimple *new_stmt = gimple_build_call (strlen_fn, 1, str);
+  gimple_set_location (new_stmt, loc);
+  len = create_tmp_reg_or_ssa_name (size_type_node);
+  gimple_call_set_lhs (new_stmt, len);
+  gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+
+  /* Create (str p+ strlen (str)).  */
+  new_stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                 POINTER_PLUS_EXPR, str, len);
+  gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+  gsi_replace_with_seq_vops (gsi, stmts);
+  /* gsi now points at the assignment to the lhs, get a
+     stmt iterator to the strlen.
+     ???  We can't use gsi_for_stmt as that doesn't work when the
+     CFG isn't built yet.  */
+  gimple_stmt_iterator gsi2 = *gsi;
+  gsi_prev (&gsi2);
+  fold_stmt (&gsi2);
+  return true;
+}
+
+/* Fold function call to builtin strstr.
+   If both arguments are constant, evaluate and fold the result,
+   additionally fold strstr (x, "") into x and strstr (x, "c")
+   into strchr (x, 'c').  */
+static bool
+gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree haystack = gimple_call_arg (stmt, 0);
+  tree needle = gimple_call_arg (stmt, 1);
+  const char *p, *q;
+
+  if (!gimple_call_lhs (stmt))
+    return false;
+
+  q = c_getstr (needle);
+  if (q == NULL)
+    return false;
+
+  if ((p = c_getstr (haystack)))
+    {
+      const char *r = strstr (p, q);
+
+      if (r == NULL)
+       {
+         replace_call_with_value (gsi, integer_zero_node);
+         return true;
+       }
+
+      tree len = build_int_cst (size_type_node, r - p);
+      gimple_seq stmts = NULL;
+      gimple *new_stmt
+       = gimple_build_assign (gimple_call_lhs (stmt), POINTER_PLUS_EXPR,
+                              haystack, len);
+      gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* For strstr (x, "") return x.  */
+  if (q[0] == '\0')
+    {
+      replace_call_with_value (gsi, haystack);
+      return true;
+    }
+
+  /* Transform strstr (x, "c") into strchr (x, 'c').  */
+  if (q[1] == '\0')
+    {
+      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+      if (strchr_fn)
+       {
+         tree c = build_int_cst (integer_type_node, q[0]);
+         gimple *repl = gimple_build_call (strchr_fn, 2, haystack, c);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
+
+  return false;
+}
+
 /* Simplify a call to the strcat builtin.  DST and SRC are the arguments
    to the call.
 
@@ -1417,10 +1872,7 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
   gimple_seq stmts = NULL, stmts2;
   gimple *repl = gimple_build_call (strlen_fn, 1, dst);
   gimple_set_location (repl, loc);
-  if (gimple_in_ssa_p (cfun))
-    newdst = make_ssa_name (size_type_node);
-  else
-    newdst = create_tmp_reg (size_type_node);
+  newdst = create_tmp_reg_or_ssa_name (size_type_node);
   gimple_call_set_lhs (repl, newdst);
   gimple_seq_add_stmt_without_update (&stmts, repl);
 
@@ -1493,39 +1945,338 @@ gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
   return true;
 }
 
-/* Simplify a call to the strncat builtin.  */
+/* Simplify a call to the strncat builtin.  */
+
+static bool
+gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree dst = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  const char *p = c_getstr (src);
+
+  /* If the requested length is zero, or the src parameter string
+     length is zero, return the dst parameter.  */
+  if (integer_zerop (len) || (p && *p == '\0'))
+    {
+      replace_call_with_value (gsi, dst);
+      return true;
+    }
+
+  if (TREE_CODE (len) != INTEGER_CST || !p)
+    return false;
+
+  unsigned srclen = strlen (p);
+
+  int cmpsrc = compare_tree_int (len, srclen);
+
+  /* Return early if the requested len is less than the string length.
+     Warnings will be issued elsewhere later.  */
+  if (cmpsrc < 0)
+    return false;
+
+  unsigned HOST_WIDE_INT dstsize;
+
+  bool nowarn = gimple_no_warning_p (stmt);
+
+  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
+    {
+      int cmpdst = compare_tree_int (len, dstsize);
+
+      if (cmpdst >= 0)
+       {
+         tree fndecl = gimple_call_fndecl (stmt);
+
+         /* Strncat copies (at most) LEN bytes and always appends
+            the terminating NUL so the specified bound should never
+            be equal to (or greater than) the size of the destination.
+            If it is, the copy could overflow.  */
+         location_t loc = gimple_location (stmt);
+         nowarn = warning_at (loc, OPT_Wstringop_overflow_,
+                              cmpdst == 0
+                              ? G_("%G%qD specified bound %E equals "
+                                   "destination size")
+                              : G_("%G%qD specified bound %E exceeds "
+                                   "destination size %wu"),
+                              stmt, fndecl, len, dstsize);
+         if (nowarn)
+           gimple_set_no_warning (stmt, true);
+       }
+    }
+
+  if (!nowarn && cmpsrc == 0)
+    {
+      tree fndecl = gimple_call_fndecl (stmt);
+
+      /* To avoid certain truncation the specified bound should also
+        not be equal to (or less than) the length of the source.  */
+      location_t loc = gimple_location (stmt);
+      if (warning_at (loc, OPT_Wstringop_overflow_,
+                     "%G%qD specified bound %E equals source length",
+                     stmt, fndecl, len))
+       gimple_set_no_warning (stmt, true);
+    }
+
+  tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
+
+  /* If the replacement _DECL isn't initialized, don't do the
+     transformation.  */
+  if (!fn)
+    return false;
+
+  /* Otherwise, emit a call to strcat.  */
+  gcall *repl = gimple_build_call (fn, 2, dst, src);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
+   LEN, and SIZE.  */
+
+static bool 
+gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  tree size = gimple_call_arg (stmt, 3);
+  tree fn;
+  const char *p;
+
+  p = c_getstr (src);
+  /* If the SRC parameter is "" or if LEN is 0, return DEST.  */
+  if ((p && *p == '\0')
+      || integer_zerop (len))
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  if (! integer_all_onesp (size))
+    {
+      tree src_len = c_strlen (src, 1);
+      if (src_len
+         && tree_fits_uhwi_p (src_len)
+         && tree_fits_uhwi_p (len)
+         && ! tree_int_cst_lt (len, src_len))
+       {
+         /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
+         fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
+         if (!fn)
+           return false;
+
+         gimple *repl = gimple_build_call (fn, 3, dest, src, size);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+      return false;
+    }
+
+  /* If __builtin_strncat_chk is used, assume strncat is available.  */
+  fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
+  if (!fn)
+    return false;
+
+  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Build and append gimple statements to STMTS that would load a first
+   character of a memory location identified by STR.  LOC is location
+   of the statement.  */
+
+static tree
+gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
+{
+  tree var;
+
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
+  gassign *stmt = gimple_build_assign (NULL_TREE, temp);
+  var = create_tmp_reg_or_ssa_name (cst_uchar_node, stmt);
+
+  gimple_assign_set_lhs (stmt, var);
+  gimple_seq_add_stmt_without_update (stmts, stmt);
+
+  return var;
+}
+
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
+
+static bool
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  HOST_WIDE_INT length = -1;
+
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+       length = tree_to_uhwi (len);
+    }
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
+
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+       {
+       case BUILT_IN_STRCMP:
+         {
+           r = strcmp (p1, p2);
+           known_result = true;
+           break;
+         }
+       case BUILT_IN_STRNCMP:
+         {
+           if (length == -1)
+             break;
+           r = strncmp (p1, p2, length);
+           known_result = true;
+           break;
+         }
+       /* Only handleable situation is where the string are equal (result 0),
+          which is already handled by operand_equal_p case.  */
+       case BUILT_IN_STRCASECMP:
+         break;
+       case BUILT_IN_STRNCASECMP:
+         {
+           if (length == -1)
+             break;
+           r = strncmp (p1, p2, length);
+           if (r == 0)
+             known_result = true;
+           break;
+         }
+       default:
+         gcc_unreachable ();
+       }
+
+      if (known_result)
+       {
+         replace_call_with_value (gsi, build_cmp_result (type, r));
+         return true;
+       }
+    }
+
+  bool nonzero_length = length >= 1
+    || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCASECMP;
+
+  location_t loc = gimple_location (stmt);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+       {
+         stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+       {
+         tree c = create_tmp_reg_or_ssa_name (integer_type_node);
+         stmt = gimple_build_assign (c, NOP_EXPR, var);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+         stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+      tree temp1 = gimple_load_first_char (loc, str1, &stmts);
+      tree temp2 = gimple_load_first_char (loc, str2, &stmts);
 
-static bool
-gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
-{
-  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
-  tree dst = gimple_call_arg (stmt, 0);
-  tree src = gimple_call_arg (stmt, 1);
-  tree len = gimple_call_arg (stmt, 2);
+      if (lhs)
+       {
+         tree c1 = create_tmp_reg_or_ssa_name (integer_type_node);
+         gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
+         gimple_seq_add_stmt_without_update (&stmts, convert1);
 
-  const char *p = c_getstr (src);
+         tree c2 = create_tmp_reg_or_ssa_name (integer_type_node);
+         gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
+         gimple_seq_add_stmt_without_update (&stmts, convert2);
 
-  /* If the requested length is zero, or the src parameter string
-     length is zero, return the dst parameter.  */
-  if (integer_zerop (len) || (p && *p == '\0'))
-    {
-      replace_call_with_value (gsi, dst);
+         stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
       return true;
     }
 
-  /* If the requested len is greater than or equal to the string
-     length, call strcat.  */
-  if (TREE_CODE (len) == INTEGER_CST && p
-      && compare_tree_int (len, strlen (p)) >= 0)
+  /* If length is larger than the length of one constant string, 
+     replace strncmp with corresponding strcmp */ 
+  if (fcode == BUILT_IN_STRNCMP 
+      && length > 0
+      && ((p2 && (size_t) length > strlen (p2)) 
+          || (p1 && (size_t) length > strlen (p1))))
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
-
-      /* If the replacement _DECL isn't initialized, don't do the
-        transformation.  */
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCMP);
       if (!fn)
-       return false;
-
-      gcall *repl = gimple_build_call (fn, 2, dst, src);
+        return false;
+      gimple *repl = gimple_build_call (fn, 2, str1, str2);
       replace_call_with_call_and_fold (gsi, repl);
       return true;
     }
@@ -1533,60 +2284,66 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
   return false;
 }
 
-/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
-   LEN, and SIZE.  */
+/* Fold a call to the memchr pointed by GSI iterator.  */
 
-static bool 
-gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
-  tree dest = gimple_call_arg (stmt, 0);
-  tree src = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
   tree len = gimple_call_arg (stmt, 2);
-  tree size = gimple_call_arg (stmt, 3);
-  tree fn;
-  const char *p;
 
-  p = c_getstr (src);
-  /* If the SRC parameter is "" or if LEN is 0, return DEST.  */
-  if ((p && *p == '\0')
-      || integer_zerop (len))
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
     {
-      replace_call_with_value (gsi, dest);
+      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
       return true;
     }
 
-  if (! tree_fits_uhwi_p (size))
+  char c;
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len)
+      || !target_char_cst_p (arg2, &c))
     return false;
 
-  if (! integer_all_onesp (size))
+  unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
+  unsigned HOST_WIDE_INT string_length;
+  const char *p1 = c_getstr (arg1, &string_length);
+
+  if (p1)
     {
-      tree src_len = c_strlen (src, 1);
-      if (src_len
-         && tree_fits_uhwi_p (src_len)
-         && tree_fits_uhwi_p (len)
-         && ! tree_int_cst_lt (len, src_len))
+      const char *r = (const char *)memchr (p1, c, MIN (length, string_length));
+      if (r == NULL)
        {
-         /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
-         fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
-         if (!fn)
-           return false;
+         if (length <= string_length)
+           {
+             replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+             return true;
+           }
+       }
+      else
+       {
+         unsigned HOST_WIDE_INT offset = r - p1;
+         gimple_seq stmts = NULL;
+         if (lhs != NULL_TREE)
+           {
+             tree offset_cst = build_int_cst (TREE_TYPE (len), offset);
+             gassign *stmt = gimple_build_assign (lhs, POINTER_PLUS_EXPR,
+                                                  arg1, offset_cst);
+             gimple_seq_add_stmt_without_update (&stmts, stmt);
+           }
+         else
+           gimple_seq_add_stmt_without_update (&stmts,
+                                               gimple_build_nop ());
 
-         gimple *repl = gimple_build_call (fn, 3, dest, src, size);
-         replace_call_with_call_and_fold (gsi, repl);
+         gsi_replace_with_seq_vops (gsi, stmts);
          return true;
        }
-      return false;
     }
 
-  /* If __builtin_strncat_chk is used, assume strncat is available.  */
-  fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
-  if (!fn)
-    return false;
-
-  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
-  replace_call_with_call_and_fold (gsi, repl);
-  return true;
+  return false;
 }
 
 /* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
@@ -1686,6 +2443,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+       {
+         tree func = gimple_call_fndecl (stmt);
+
+         warning_at (loc, OPT_Wrestrict,
+                     "%qD source argument is the same as destination",
+                     func);
+       }
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
        {
          replace_call_with_value (gsi, dest);
@@ -1695,7 +2461,8 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
        {
          gimple_seq stmts = NULL;
          len = gimple_convert_to_ptrofftype (&stmts, loc, len);
-         tree temp = gimple_build (&stmts, loc, POINTER_PLUS_EXPR, dest, len);
+         tree temp = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
+                                   TREE_TYPE (dest), dest, len);
          gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
          replace_call_with_value (gsi, temp);
          return true;
@@ -1786,6 +2553,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+                 "%qD source argument is the same as destination",
+                 func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2162,11 +2935,9 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
    ORIG may be null if this is a 2-argument call.  We don't attempt to
    simplify calls with more than 3 arguments.
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.  If IGNORED is true, it means that
-   the caller does not use the returned value of the function.  */
+   Return true if simplification was possible, otherwise false.  */
 
-static bool
+bool
 gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
@@ -2287,11 +3058,9 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
    FMT, and ORIG.  ORIG may be null if this is a 3-argument call.  We don't
    attempt to simplify calls with more than 4 arguments.
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.  If IGNORED is true, it means that
-   the caller does not use the returned value of the function.  */
+   Return true if simplification was possible, otherwise false.  */
 
-static bool
+bool
 gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
 {
   gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
@@ -2743,6 +3512,28 @@ gimple_fold_builtin_acc_on_device (gimple_stmt_iterator *gsi, tree arg0)
   return true;
 }
 
+/* Fold realloc (0, n) -> malloc (n).  */
+
+static bool
+gimple_fold_builtin_realloc (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree arg = gimple_call_arg (stmt, 0);
+  tree size = gimple_call_arg (stmt, 1);
+
+  if (operand_equal_p (arg, null_pointer_node, 0))
+    {
+      tree fn_malloc = builtin_decl_implicit (BUILT_IN_MALLOC);
+      if (fn_malloc)
+       {
+         gcall *repl = gimple_build_call (fn_malloc, 1, size);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
+  return false;
+}
+
 /* Fold the non-target builtin at *GSI and return whether any simplification
    was made.  */
 
@@ -2761,16 +3552,17 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
   switch (fcode)
     {
+    case BUILT_IN_BCMP:
+      return gimple_fold_builtin_bcmp (gsi);
+    case BUILT_IN_BCOPY:
+      return gimple_fold_builtin_bcopy (gsi);
     case BUILT_IN_BZERO:
-      return gimple_fold_builtin_memset (gsi, integer_zero_node,
-                                        gimple_call_arg (stmt, 1));
+      return gimple_fold_builtin_bzero (gsi);
+
     case BUILT_IN_MEMSET:
       return gimple_fold_builtin_memset (gsi,
                                         gimple_call_arg (stmt, 1),
                                         gimple_call_arg (stmt, 2));
-    case BUILT_IN_BCOPY:
-      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
-                                           gimple_call_arg (stmt, 0), 3);
     case BUILT_IN_MEMCPY:
       return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
                                            gimple_call_arg (stmt, 1), 0);
@@ -2803,6 +3595,21 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                         gimple_call_arg (stmt, 1));
     case BUILT_IN_STRNCAT:
       return gimple_fold_builtin_strncat (gsi);
+    case BUILT_IN_INDEX:
+    case BUILT_IN_STRCHR:
+      return gimple_fold_builtin_strchr (gsi, false);
+    case BUILT_IN_RINDEX:
+    case BUILT_IN_STRRCHR:
+      return gimple_fold_builtin_strchr (gsi, true);
+    case BUILT_IN_STRSTR:
+      return gimple_fold_builtin_strstr (gsi);
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
                                        gimple_call_arg (stmt, 1), false);
@@ -2839,10 +3646,7 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
       return gimple_fold_builtin_snprintf_chk (gsi, fcode);
-    case BUILT_IN_SNPRINTF:
-      return gimple_fold_builtin_snprintf (gsi);
-    case BUILT_IN_SPRINTF:
-      return gimple_fold_builtin_sprintf (gsi);
+
     case BUILT_IN_FPRINTF:
     case BUILT_IN_FPRINTF_UNLOCKED:
     case BUILT_IN_VFPRINTF:
@@ -2886,6 +3690,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_ACC_ON_DEVICE:
       return gimple_fold_builtin_acc_on_device (gsi,
                                                gimple_call_arg (stmt, 0));
+    case BUILT_IN_REALLOC:
+      return gimple_fold_builtin_realloc (gsi);
+
     default:;
     }
 
@@ -2906,6 +3713,192 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Transform IFN_GOACC_DIM_SIZE and IFN_GOACC_DIM_POS internal
+   function calls to constants, where possible.  */
+
+static tree
+fold_internal_goacc_dim (const gimple *call)
+{
+  int axis = oacc_get_ifn_dim_arg (call);
+  int size = oacc_get_fn_dim_size (current_function_decl, axis);
+  tree result = NULL_TREE;
+  tree type = TREE_TYPE (gimple_call_lhs (call));
+
+  switch (gimple_call_internal_fn (call))
+    {
+    case IFN_GOACC_DIM_POS:
+      /* If the size is 1, we know the answer.  */
+      if (size == 1)
+       result = build_int_cst (type, 0);
+      break;
+    case IFN_GOACC_DIM_SIZE:
+      /* If the size is not dynamic, we know the answer.  */
+      if (size)
+       result = build_int_cst (type, size);
+      break;
+    default:
+      break;
+    }
+
+  return result;
+}
+
+/* Return true if stmt is __atomic_compare_exchange_N call which is suitable
+   for conversion into ATOMIC_COMPARE_EXCHANGE if the second argument is
+   &var where var is only addressable because of such calls.  */
+
+bool
+optimize_atomic_compare_exchange_p (gimple *stmt)
+{
+  if (gimple_call_num_args (stmt) != 6
+      || !flag_inline_atomics
+      || !optimize
+      || sanitize_flags_p (SANITIZE_THREAD | SANITIZE_ADDRESS)
+      || !gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+      || !gimple_vdef (stmt)
+      || !gimple_vuse (stmt))
+    return false;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_2:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16:
+      break;
+    default:
+      return false;
+    }
+
+  tree expected = gimple_call_arg (stmt, 1);
+  if (TREE_CODE (expected) != ADDR_EXPR
+      || !SSA_VAR_P (TREE_OPERAND (expected, 0)))
+    return false;
+
+  tree etype = TREE_TYPE (TREE_OPERAND (expected, 0));
+  if (!is_gimple_reg_type (etype)
+      || !auto_var_in_fn_p (TREE_OPERAND (expected, 0), current_function_decl)
+      || TREE_THIS_VOLATILE (etype)
+      || VECTOR_TYPE_P (etype)
+      || TREE_CODE (etype) == COMPLEX_TYPE
+      /* Don't optimize floating point expected vars, VIEW_CONVERT_EXPRs
+        might not preserve all the bits.  See PR71716.  */
+      || SCALAR_FLOAT_TYPE_P (etype)
+      || maybe_ne (TYPE_PRECISION (etype),
+                  GET_MODE_BITSIZE (TYPE_MODE (etype))))
+    return false;
+
+  tree weak = gimple_call_arg (stmt, 3);
+  if (!integer_zerop (weak) && !integer_onep (weak))
+    return false;
+
+  tree parmt = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  tree itype = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (parmt)));
+  machine_mode mode = TYPE_MODE (itype);
+
+  if (direct_optab_handler (atomic_compare_and_swap_optab, mode)
+      == CODE_FOR_nothing
+      && optab_handler (sync_compare_and_swap_optab, mode) == CODE_FOR_nothing)
+    return false;
+
+  if (int_size_in_bytes (etype) != GET_MODE_SIZE (mode))
+    return false;
+
+  return true;
+}
+
+/* Fold
+     r = __atomic_compare_exchange_N (p, &e, d, w, s, f);
+   into
+     _Complex uintN_t t = ATOMIC_COMPARE_EXCHANGE (p, e, d, w * 256 + N, s, f);
+     i = IMAGPART_EXPR <t>;
+     r = (_Bool) i;
+     e = REALPART_EXPR <t>;  */
+
+void
+fold_builtin_atomic_compare_exchange (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree fndecl = gimple_call_fndecl (stmt);
+  tree parmt = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  tree itype = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (parmt)));
+  tree ctype = build_complex_type (itype);
+  tree expected = TREE_OPERAND (gimple_call_arg (stmt, 1), 0);
+  bool throws = false;
+  edge e = NULL;
+  gimple *g = gimple_build_assign (make_ssa_name (TREE_TYPE (expected)),
+                                  expected);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  gimple_stmt_iterator gsiret = gsi_for_stmt (g);
+  if (!useless_type_conversion_p (itype, TREE_TYPE (expected)))
+    {
+      g = gimple_build_assign (make_ssa_name (itype), VIEW_CONVERT_EXPR,
+                              build1 (VIEW_CONVERT_EXPR, itype,
+                                      gimple_assign_lhs (g)));
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+    }
+  int flag = (integer_onep (gimple_call_arg (stmt, 3)) ? 256 : 0)
+            + int_size_in_bytes (itype);
+  g = gimple_build_call_internal (IFN_ATOMIC_COMPARE_EXCHANGE, 6,
+                                 gimple_call_arg (stmt, 0),
+                                 gimple_assign_lhs (g),
+                                 gimple_call_arg (stmt, 2),
+                                 build_int_cst (integer_type_node, flag),
+                                 gimple_call_arg (stmt, 4),
+                                 gimple_call_arg (stmt, 5));
+  tree lhs = make_ssa_name (ctype);
+  gimple_call_set_lhs (g, lhs);
+  gimple_set_vdef (g, gimple_vdef (stmt));
+  gimple_set_vuse (g, gimple_vuse (stmt));
+  SSA_NAME_DEF_STMT (gimple_vdef (g)) = g;
+  tree oldlhs = gimple_call_lhs (stmt);
+  if (stmt_can_throw_internal (stmt))
+    {
+      throws = true;
+      e = find_fallthru_edge (gsi_bb (*gsi)->succs);
+    }
+  gimple_call_set_nothrow (as_a <gcall *> (g),
+                          gimple_call_nothrow_p (as_a <gcall *> (stmt)));
+  gimple_call_set_lhs (stmt, NULL_TREE);
+  gsi_replace (gsi, g, true);
+  if (oldlhs)
+    {
+      g = gimple_build_assign (make_ssa_name (itype), IMAGPART_EXPR,
+                              build1 (IMAGPART_EXPR, itype, lhs));
+      if (throws)
+       {
+         gsi_insert_on_edge_immediate (e, g);
+         *gsi = gsi_for_stmt (g);
+       }
+      else
+       gsi_insert_after (gsi, g, GSI_NEW_STMT);
+      g = gimple_build_assign (oldlhs, NOP_EXPR, gimple_assign_lhs (g));
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+  g = gimple_build_assign (make_ssa_name (itype), REALPART_EXPR,
+                          build1 (REALPART_EXPR, itype, lhs));
+  if (throws && oldlhs == NULL_TREE)
+    {
+      gsi_insert_on_edge_immediate (e, g);
+      *gsi = gsi_for_stmt (g);
+    }
+  else
+    gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!useless_type_conversion_p (TREE_TYPE (expected), itype))
+    {
+      g = gimple_build_assign (make_ssa_name (TREE_TYPE (expected)),
+                              VIEW_CONVERT_EXPR,
+                              build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expected),
+                                      gimple_assign_lhs (g)));
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+  g = gimple_build_assign (expected, SSA_NAME, gimple_assign_lhs (g));
+  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  *gsi = gsiret;
+}
+
 /* Return true if ARG0 CODE ARG1 in infinite signed precision operation
    doesn't fit into TYPE.  The test for overflow should be regardless of
    -fwrapv, and even for unsigned types.  */
@@ -2999,10 +3992,23 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                }
              if (targets.length () == 1)
                {
-                 gimple_call_set_fndecl (stmt, targets[0]->decl);
+                 tree fndecl = targets[0]->decl;
+                 gimple_call_set_fndecl (stmt, fndecl);
                  changed = true;
+                 /* If changing the call to __cxa_pure_virtual
+                    or similar noreturn function, adjust gimple_call_fntype
+                    too.  */
+                 if (gimple_call_noreturn_p (stmt)
+                     && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl)))
+                     && TYPE_ARG_TYPES (TREE_TYPE (fndecl))
+                     && (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
+                         == void_type_node))
+                   gimple_call_set_fntype (stmt, TREE_TYPE (fndecl));
                  /* If the call becomes noreturn, remove the lhs.  */
-                 if (lhs && (gimple_call_flags (stmt) & ECF_NORETURN))
+                 if (lhs
+                     && gimple_call_noreturn_p (stmt)
+                     && (VOID_TYPE_P (TREE_TYPE (gimple_call_fntype (stmt)))
+                         || should_remove_lhs_p (lhs)))
                    {
                      if (TREE_CODE (lhs) == SSA_NAME)
                        {
@@ -3020,24 +4026,18 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                  tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
                  gimple *new_stmt = gimple_build_call (fndecl, 0);
                  gimple_set_location (new_stmt, gimple_location (stmt));
+                 /* If the call had a SSA name as lhs morph that into
+                    an uninitialized value.  */
                  if (lhs && TREE_CODE (lhs) == SSA_NAME)
                    {
                      tree var = create_tmp_var (TREE_TYPE (lhs));
-                     tree def = get_or_create_ssa_default_def (cfun, var);
-
-                     /* To satisfy condition for
-                        cgraph_update_edges_for_call_stmt_node,
-                        we need to preserve GIMPLE_CALL statement
-                        at position of GSI iterator.  */
-                     update_call_from_tree (gsi, def);
-                     gsi_insert_before (gsi, new_stmt, GSI_NEW_STMT);
-                   }
-                 else
-                   {
-                     gimple_set_vuse (new_stmt, gimple_vuse (stmt));
-                     gimple_set_vdef (new_stmt, gimple_vdef (stmt));
-                     gsi_replace (gsi, new_stmt, false);
+                     SET_SSA_NAME_VAR_OR_IDENTIFIER (lhs, var);
+                     SSA_NAME_DEF_STMT (lhs) = gimple_build_nop ();
+                     set_ssa_default_def (cfun, var, lhs);
                    }
+                 gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                 gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+                 gsi_replace (gsi, new_stmt, false);
                  return true;
                }
            }
@@ -3094,18 +4094,47 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                                        gimple_call_arg (stmt, 2));
          break;
        case IFN_UBSAN_OBJECT_SIZE:
-         if (integer_all_onesp (gimple_call_arg (stmt, 2))
-             || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
-                 && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
-                 && tree_int_cst_le (gimple_call_arg (stmt, 1),
-                                     gimple_call_arg (stmt, 2))))
+         {
+           tree offset = gimple_call_arg (stmt, 1);
+           tree objsize = gimple_call_arg (stmt, 2);
+           if (integer_all_onesp (objsize)
+               || (TREE_CODE (offset) == INTEGER_CST
+                   && TREE_CODE (objsize) == INTEGER_CST
+                   && tree_int_cst_le (offset, objsize)))
+             {
+               replace_call_with_value (gsi, NULL_TREE);
+               return true;
+             }
+         }
+         break;
+       case IFN_UBSAN_PTR:
+         if (integer_zerop (gimple_call_arg (stmt, 1)))
            {
-             gsi_replace (gsi, gimple_build_nop (), false);
-             unlink_stmt_vdef (stmt);
-             release_defs (stmt);
+             replace_call_with_value (gsi, NULL_TREE);
              return true;
            }
          break;
+       case IFN_UBSAN_BOUNDS:
+         {
+           tree index = gimple_call_arg (stmt, 1);
+           tree bound = gimple_call_arg (stmt, 2);
+           if (TREE_CODE (index) == INTEGER_CST
+               && TREE_CODE (bound) == INTEGER_CST)
+             {
+               index = fold_convert (TREE_TYPE (bound), index);
+               if (TREE_CODE (index) == INTEGER_CST
+                   && tree_int_cst_le (index, bound))
+                 {
+                   replace_call_with_value (gsi, NULL_TREE);
+                   return true;
+                 }
+             }
+         }
+         break;
+       case IFN_GOACC_DIM_SIZE:
+       case IFN_GOACC_DIM_POS:
+         result = fold_internal_goacc_dim (stmt);
+         break;
        case IFN_UBSAN_CHECK_ADD:
          subcode = PLUS_EXPR;
          break;
@@ -3267,7 +4296,14 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
       || (ops[2]
          && TREE_CODE (ops[2]) == SSA_NAME
          && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])
-         && !has_use_on_stmt (ops[2], stmt)))
+         && !has_use_on_stmt (ops[2], stmt))
+      || (COMPARISON_CLASS_P (ops[0])
+         && ((TREE_CODE (TREE_OPERAND (ops[0], 0)) == SSA_NAME
+              && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 0))
+              && !has_use_on_stmt (TREE_OPERAND (ops[0], 0), stmt))
+             || (TREE_CODE (TREE_OPERAND (ops[0], 1)) == SSA_NAME
+                 && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 1))
+                 && !has_use_on_stmt (TREE_OPERAND (ops[0], 1), stmt)))))
     return false;
 
   /* Don't insert new statements when INPLACE is true, even if we could
@@ -3325,8 +4361,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
          || gimple_num_ops (stmt) > get_gimple_rhs_num_ops (rcode))
        {
          maybe_build_generic_op (rcode,
-                                 TREE_TYPE (gimple_assign_lhs (stmt)),
-                                 &ops[0], ops[1], ops[2]);
+                                 TREE_TYPE (gimple_assign_lhs (stmt)), ops);
          gimple_assign_set_rhs_with_ops (gsi, rcode, ops[0], ops[1], ops[2]);
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
@@ -3341,7 +4376,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
        }
     }
   else if (rcode.is_fn_code ()
-          && gimple_call_builtin_p (stmt, rcode))
+          && gimple_call_combined_fn (stmt) == rcode)
     {
       unsigned i;
       for (i = 0; i < gimple_call_num_args (stmt); ++i)
@@ -3394,6 +4429,44 @@ maybe_canonicalize_mem_ref_addr (tree *t)
   if (TREE_CODE (*t) == ADDR_EXPR)
     t = &TREE_OPERAND (*t, 0);
 
+  /* The C and C++ frontends use an ARRAY_REF for indexing with their
+     generic vector extension.  The actual vector referenced is
+     view-converted to an array type for this purpose.  If the index
+     is constant the canonical representation in the middle-end is a
+     BIT_FIELD_REF so re-write the former to the latter here.  */
+  if (TREE_CODE (*t) == ARRAY_REF
+      && TREE_CODE (TREE_OPERAND (*t, 0)) == VIEW_CONVERT_EXPR
+      && TREE_CODE (TREE_OPERAND (*t, 1)) == INTEGER_CST
+      && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0))))
+    {
+      tree vtype = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0));
+      if (VECTOR_TYPE_P (vtype))
+       {
+         tree low = array_ref_low_bound (*t);
+         if (TREE_CODE (low) == INTEGER_CST)
+           {
+             if (tree_int_cst_le (low, TREE_OPERAND (*t, 1)))
+               {
+                 widest_int idx = wi::sub (wi::to_widest (TREE_OPERAND (*t, 1)),
+                                           wi::to_widest (low));
+                 idx = wi::mul (idx, wi::to_widest
+                                        (TYPE_SIZE (TREE_TYPE (*t))));
+                 widest_int ext
+                   = wi::add (idx, wi::to_widest (TYPE_SIZE (TREE_TYPE (*t))));
+                 if (wi::les_p (ext, wi::to_widest (TYPE_SIZE (vtype))))
+                   {
+                     *t = build3_loc (EXPR_LOCATION (*t), BIT_FIELD_REF,
+                                      TREE_TYPE (*t),
+                                      TREE_OPERAND (TREE_OPERAND (*t, 0), 0),
+                                      TYPE_SIZE (TREE_TYPE (*t)),
+                                      wide_int_to_tree (bitsizetype, idx));
+                     res = true;
+                   }
+               }
+           }
+       }
+    }
+
   while (handled_component_p (*t))
     t = &TREE_OPERAND (*t, 0);
 
@@ -3408,7 +4481,7 @@ maybe_canonicalize_mem_ref_addr (tree *t)
              || handled_component_p (TREE_OPERAND (addr, 0))))
        {
          tree base;
-         HOST_WIDE_INT coffset;
+         poly_int64 coffset;
          base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
                                                &coffset);
          if (!base)
@@ -3475,7 +4548,9 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
 {
   bool changed = false;
   gimple *stmt = gsi_stmt (*gsi);
+  bool nowarning = gimple_no_warning_p (stmt);
   unsigned i;
+  fold_defer_overflow_warnings ();
 
   /* First do required canonicalization of [TARGET_]MEM_REF addresses
      after propagation.
@@ -3508,7 +4583,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
            {
              tree rhs1 = gimple_assign_rhs1 (stmt);
              tree rhs2 = gimple_assign_rhs2 (stmt);
-             if (tree_swap_operands_p (rhs1, rhs2, false))
+             if (tree_swap_operands_p (rhs1, rhs2))
                {
                  gimple_assign_set_rhs1 (stmt, rhs2);
                  gimple_assign_set_rhs2 (stmt, rhs1);
@@ -3574,7 +4649,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        /* Canonicalize operand order.  */
        tree lhs = gimple_cond_lhs (stmt);
        tree rhs = gimple_cond_rhs (stmt);
-       if (tree_swap_operands_p (lhs, rhs, false))
+       if (tree_swap_operands_p (lhs, rhs))
          {
            gcond *gc = as_a <gcond *> (stmt);
            gimple_cond_set_lhs (gc, rhs);
@@ -3748,6 +4823,24 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        }
       break;
 
+    case GIMPLE_RETURN:
+      {
+       greturn *ret_stmt = as_a<greturn *> (stmt);
+       tree ret = gimple_return_retval(ret_stmt);
+
+       if (ret && TREE_CODE (ret) == SSA_NAME && valueize)
+         {
+           tree val = valueize (ret);
+           if (val && val != ret
+               && may_propagate_copy (ret, val))
+             {
+               gimple_return_set_retval (ret_stmt, val);
+               changed = true;
+             }
+         }
+      }
+      break;
+
     default:;
     }
 
@@ -3768,6 +4861,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        }
     }
 
+  fold_undefer_overflow_warnings (changed && !nowarning, stmt, 0);
   return changed;
 }
 
@@ -4941,7 +6035,7 @@ gimple_fold_stmt_to_constant_1 (gimple *stmt, tree (*valueize) (tree),
              fprintf (dump_file, "Match-and-simplified ");
              print_gimple_expr (dump_file, stmt, 0, TDF_SLIM);
              fprintf (dump_file, " to ");
-             print_generic_expr (dump_file, res, 0);
+             print_generic_expr (dump_file, res);
              fprintf (dump_file, "\n");
            }
          return res;
@@ -4973,7 +6067,7 @@ gimple_fold_stmt_to_constant_1 (gimple *stmt, tree (*valueize) (tree),
              else if (TREE_CODE (rhs) == ADDR_EXPR
                       && !is_gimple_min_invariant (rhs))
                {
-                 HOST_WIDE_INT offset = 0;
+                 poly_int64 offset = 0;
                  tree base;
                  base = get_addr_base_and_unit_offset_1 (TREE_OPERAND (rhs, 0),
                                                          &offset,
@@ -4986,26 +6080,26 @@ gimple_fold_stmt_to_constant_1 (gimple *stmt, tree (*valueize) (tree),
                }
              else if (TREE_CODE (rhs) == CONSTRUCTOR
                       && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
-                      && (CONSTRUCTOR_NELTS (rhs)
-                          == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+                      && known_eq (CONSTRUCTOR_NELTS (rhs),
+                                   TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
                {
-                 unsigned i;
-                 tree val, *vec;
+                 unsigned i, nelts;
+                 tree val;
 
-                 vec = XALLOCAVEC (tree,
-                                   TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)));
+                 nelts = CONSTRUCTOR_NELTS (rhs);
+                 tree_vector_builder vec (TREE_TYPE (rhs), nelts, 1);
                  FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
                    {
                      val = (*valueize) (val);
                      if (TREE_CODE (val) == INTEGER_CST
                          || TREE_CODE (val) == REAL_CST
                          || TREE_CODE (val) == FIXED_CST)
-                       vec[i] = val;
+                       vec.quick_push (val);
                      else
                        return NULL_TREE;
                    }
 
-                 return build_vector (TREE_TYPE (rhs), vec);
+                 return vec.build ();
                }
              if (subcode == OBJ_TYPE_REF)
                {
@@ -5139,6 +6233,14 @@ gimple_fold_stmt_to_constant_1 (gimple *stmt, tree (*valueize) (tree),
              case IFN_UBSAN_CHECK_MUL:
                subcode = MULT_EXPR;
                break;
+             case IFN_BUILTIN_EXPECT:
+                 {
+                   tree arg0 = gimple_call_arg (stmt, 0);
+                   tree op0 = (*valueize) (arg0);
+                   if (TREE_CODE (op0) == INTEGER_CST)
+                     return op0;
+                   return NULL_TREE;
+                 }
              default:
                return NULL_TREE;
              }
@@ -5233,17 +6335,19 @@ gimple_fold_stmt_to_constant (gimple *stmt, tree (*valueize) (tree))
    is not explicitly available, but it is known to be zero
    such as 'static const int a;'.  */
 static tree
-get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
+get_base_constructor (tree base, poly_int64_pod *bit_offset,
                      tree (*valueize)(tree))
 {
-  HOST_WIDE_INT bit_offset2, size, max_size;
+  poly_int64 bit_offset2, size, max_size;
+  bool reverse;
+
   if (TREE_CODE (base) == MEM_REF)
     {
       if (!integer_zerop (TREE_OPERAND (base, 1)))
        {
          if (!tree_fits_shwi_p (TREE_OPERAND (base, 1)))
            return NULL_TREE;
-         *bit_offset += (mem_ref_offset (base).to_short_addr ()
+         *bit_offset += (mem_ref_offset (base).force_shwi ()
                          * BITS_PER_UNIT);
        }
 
@@ -5254,6 +6358,9 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
         return NULL_TREE;
       base = TREE_OPERAND (base, 0);
     }
+  else if (valueize
+          && TREE_CODE (base) == SSA_NAME)
+    base = valueize (base);
 
   /* Get a CONSTRUCTOR.  If BASE is a VAR_DECL, get its
      DECL_INITIAL.  If BASE is a nested reference into another
@@ -5275,19 +6382,26 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
        return init;
       }
 
+    case VIEW_CONVERT_EXPR:
+      return get_base_constructor (TREE_OPERAND (base, 0),
+                                  bit_offset, valueize);
+
     case ARRAY_REF:
     case COMPONENT_REF:
-      base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size);
-      if (max_size == -1 || size != max_size)
+      base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size,
+                                     &reverse);
+      if (!known_size_p (max_size) || maybe_ne (size, max_size))
        return NULL_TREE;
       *bit_offset +=  bit_offset2;
       return get_base_constructor (base, bit_offset, valueize);
 
-    case STRING_CST:
     case CONSTRUCTOR:
       return base;
 
     default:
+      if (CONSTANT_CLASS_P (base))
+       return base;
+
       return NULL_TREE;
     }
 }
@@ -5301,13 +6415,10 @@ fold_array_ctor_reference (tree type, tree ctor,
                           unsigned HOST_WIDE_INT size,
                           tree from_decl)
 {
-  unsigned HOST_WIDE_INT cnt;
-  tree cfield, cval;
   offset_int low_bound;
   offset_int elt_size;
-  offset_int index, max_index;
   offset_int access_index;
-  tree domain_type = NULL_TREE, index_type = NULL_TREE;
+  tree domain_type = NULL_TREE;
   HOST_WIDE_INT inner_offset;
 
   /* Compute low bound and elt size.  */
@@ -5316,22 +6427,22 @@ fold_array_ctor_reference (tree type, tree ctor,
   if (domain_type && TYPE_MIN_VALUE (domain_type))
     {
       /* Static constructors for variably sized objects makes no sense.  */
-      gcc_assert (TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST);
-      index_type = TREE_TYPE (TYPE_MIN_VALUE (domain_type));
+      if (TREE_CODE (TYPE_MIN_VALUE (domain_type)) != INTEGER_CST)
+       return NULL_TREE;
       low_bound = wi::to_offset (TYPE_MIN_VALUE (domain_type));
     }
   else
     low_bound = 0;
   /* Static constructors for variably sized objects makes no sense.  */
-  gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))))
-             == INTEGER_CST);
+  if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor)))) != INTEGER_CST)
+    return NULL_TREE;
   elt_size = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))));
 
   /* We can handle only constantly sized accesses that are known to not
      be larger than size of array element.  */
   if (!TYPE_SIZE_UNIT (type)
       || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
-      || wi::lts_p (elt_size, wi::to_offset (TYPE_SIZE_UNIT (type)))
+      || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type))
       || elt_size == 0)
     return NULL_TREE;
 
@@ -5339,9 +6450,6 @@ fold_array_ctor_reference (tree type, tree ctor,
   access_index = wi::udiv_trunc (offset_int (offset / BITS_PER_UNIT),
                                 elt_size);
   access_index += low_bound;
-  if (index_type)
-    access_index = wi::ext (access_index, TYPE_PRECISION (index_type),
-                           TYPE_SIGN (index_type));
 
   /* And offset within the access.  */
   inner_offset = offset % (elt_size.to_uhwi () * BITS_PER_UNIT);
@@ -5350,43 +6458,9 @@ fold_array_ctor_reference (tree type, tree ctor,
      care to fold accesses spanning multiple array indexes.  */
   if (inner_offset + size > elt_size.to_uhwi () * BITS_PER_UNIT)
     return NULL_TREE;
+  if (tree val = get_array_ctor_element_at_index (ctor, access_index))
+    return fold_ctor_reference (type, val, inner_offset, size, from_decl);
 
-  index = low_bound - 1;
-  if (index_type)
-    index = wi::ext (index, TYPE_PRECISION (index_type),
-                    TYPE_SIGN (index_type));
-
-  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
-    {
-      /* Array constructor might explicitely set index, or specify range
-        or leave index NULL meaning that it is next index after previous
-        one.  */
-      if (cfield)
-       {
-         if (TREE_CODE (cfield) == INTEGER_CST)
-           max_index = index = wi::to_offset (cfield);
-         else
-           {
-             gcc_assert (TREE_CODE (cfield) == RANGE_EXPR);
-             index = wi::to_offset (TREE_OPERAND (cfield, 0));
-             max_index = wi::to_offset (TREE_OPERAND (cfield, 1));
-           }
-       }
-      else
-       {
-         index += 1;
-         if (index_type)
-           index = wi::ext (index, TYPE_PRECISION (index_type),
-                            TYPE_SIGN (index_type));
-         max_index = index;
-       }
-
-      /* Do we have match?  */
-      if (wi::cmpu (access_index, index) >= 0
-         && wi::cmpu (access_index, max_index) <= 0)
-       return fold_ctor_reference (type, cval, inner_offset, size,
-                                   from_decl);
-    }
   /* When memory is not explicitely mentioned in constructor,
      it is 0 (or out of range).  */
   return build_zero_cst (type);
@@ -5423,8 +6497,7 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
 
       /* Compute bit offset of the field.  */
       bitoffset = (wi::to_offset (field_offset)
-                  + wi::lshift (wi::to_offset (byte_offset),
-                                LOG2_BITS_PER_UNIT));
+                  + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
       /* Compute bit offset where the field ends.  */
       if (field_size != NULL_TREE)
        bitoffset_end = bitoffset + wi::to_offset (field_size);
@@ -5445,7 +6518,7 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
             fields.  */
          if (wi::cmps (access_end, bitoffset_end) > 0)
            return NULL_TREE;
-         if (wi::lts_p (offset, bitoffset))
+         if (offset < bitoffset)
            return NULL_TREE;
          return fold_ctor_reference (type, cval,
                                      inner_offset.to_uhwi (), size,
@@ -5456,20 +6529,25 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
   return build_zero_cst (type);
 }
 
-/* CTOR is value initializing memory, fold reference of type TYPE and size SIZE
-   to the memory at bit OFFSET.  */
+/* CTOR is value initializing memory, fold reference of type TYPE and
+   size POLY_SIZE to the memory at bit POLY_OFFSET.  */
 
 tree
-fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
-                    unsigned HOST_WIDE_INT size, tree from_decl)
+fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset,
+                    poly_uint64 poly_size, tree from_decl)
 {
   tree ret;
 
   /* We found the field with exact match.  */
   if (useless_type_conversion_p (type, TREE_TYPE (ctor))
-      && !offset)
+      && known_eq (poly_offset, 0U))
     return canonicalize_constructor_val (unshare_expr (ctor), from_decl);
 
+  /* The remaining optimizations need a constant size and offset.  */
+  unsigned HOST_WIDE_INT size, offset;
+  if (!poly_size.is_constant (&size) || !poly_offset.is_constant (&offset))
+    return NULL_TREE;
+
   /* We are at the end of walk, see if we can view convert the
      result.  */
   if (!AGGREGATE_TYPE_P (TREE_TYPE (ctor)) && !offset
@@ -5478,9 +6556,12 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
       && !compare_tree_int (TYPE_SIZE (TREE_TYPE (ctor)), size))
     {
       ret = canonicalize_constructor_val (unshare_expr (ctor), from_decl);
-      ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
       if (ret)
-       STRIP_USELESS_TYPE_CONVERSION (ret);
+       {
+         ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
+         if (ret)
+           STRIP_USELESS_TYPE_CONVERSION (ret);
+       }
       return ret;
     }
   /* For constants and byte-aligned/sized reads try to go through
@@ -5492,9 +6573,10 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
       && size <= MAX_BITSIZE_MODE_ANY_MODE)
     {
       unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
-      if (native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
-                             offset / BITS_PER_UNIT) > 0)
-       return native_interpret_expr (type, buf, size / BITS_PER_UNIT);
+      int len = native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
+                                   offset / BITS_PER_UNIT);
+      if (len > 0)
+       return native_interpret_expr (type, buf, len);
     }
   if (TREE_CODE (ctor) == CONSTRUCTOR)
     {
@@ -5519,8 +6601,9 @@ tree
 fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
 {
   tree ctor, idx, base;
-  HOST_WIDE_INT offset, size, max_size;
+  poly_int64 offset, size, max_size;
   tree tem;
+  bool reverse;
 
   if (TREE_THIS_VOLATILE (t))
     return NULL_TREE;
@@ -5544,23 +6627,23 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
       if (TREE_CODE (TREE_OPERAND (t, 1)) == SSA_NAME
          && valueize
          && (idx = (*valueize) (TREE_OPERAND (t, 1)))
-         && TREE_CODE (idx) == INTEGER_CST)
+         && poly_int_tree_p (idx))
        {
          tree low_bound, unit_size;
 
          /* If the resulting bit-offset is constant, track it.  */
          if ((low_bound = array_ref_low_bound (t),
-              TREE_CODE (low_bound) == INTEGER_CST)
+              poly_int_tree_p (low_bound))
              && (unit_size = array_ref_element_size (t),
                  tree_fits_uhwi_p (unit_size)))
            {
-             offset_int woffset
-               = wi::sext (wi::to_offset (idx) - wi::to_offset (low_bound),
+             poly_offset_int woffset
+               = wi::sext (wi::to_poly_offset (idx)
+                           - wi::to_poly_offset (low_bound),
                            TYPE_PRECISION (TREE_TYPE (idx)));
 
-             if (wi::fits_shwi_p (woffset))
+             if (woffset.to_shwi (&offset))
                {
-                 offset = woffset.to_shwi ();
                  /* TODO: This code seems wrong, multiply then check
                     to see if it fits.  */
                  offset *= tree_to_uhwi (unit_size);
@@ -5573,7 +6656,7 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
                    return build_zero_cst (TREE_TYPE (t));
                  /* Out of bound array access.  Value is undefined,
                     but don't fold.  */
-                 if (offset < 0)
+                 if (maybe_lt (offset, 0))
                    return NULL_TREE;
                  /* We can not determine ctor.  */
                  if (!ctor)
@@ -5591,21 +6674,21 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
     case BIT_FIELD_REF:
     case TARGET_MEM_REF:
     case MEM_REF:
-      base = get_ref_base_and_extent (t, &offset, &size, &max_size);
+      base = get_ref_base_and_extent (t, &offset, &size, &max_size, &reverse);
       ctor = get_base_constructor (base, &offset, valueize);
 
       /* Empty constructor.  Always fold to 0.  */
       if (ctor == error_mark_node)
        return build_zero_cst (TREE_TYPE (t));
       /* We do not know precise address.  */
-      if (max_size == -1 || max_size != size)
+      if (!known_size_p (max_size) || maybe_ne (max_size, size))
        return NULL_TREE;
       /* We can not determine ctor.  */
       if (!ctor)
        return NULL_TREE;
 
       /* Out of bound array access.  Value is undefined, but don't fold.  */
-      if (offset < 0)
+      if (maybe_lt (offset, 0))
        return NULL_TREE;
 
       return fold_ctor_reference (TREE_TYPE (t), ctor, offset, size,
@@ -5655,8 +6738,7 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
     *can_refer = true;
 
   /* First of all double check we have virtual table.  */
-  if (TREE_CODE (v) != VAR_DECL
-      || !DECL_VIRTUAL_P (v))
+  if (!VAR_P (v) || !DECL_VIRTUAL_P (v))
     {
       /* Pass down that we lost track of the target.  */
       if (can_refer)
@@ -5673,7 +6755,6 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   gcc_assert (init);
   if (init == error_mark_node)
     {
-      gcc_assert (in_lto_p);
       /* Pass down that we lost track of the target.  */
       if (can_refer)
        *can_refer = false;
@@ -5774,8 +6855,8 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo,
   return gimple_get_virt_method_for_vtable (token, v, offset, can_refer);
 }
 
-/* Given a pointer value OP0, return a simplified version of an
-   indirection through OP0, or NULL_TREE if no simplification is
+/* Given a pointer value T, return a simplified version of an
+   indirection through T, or NULL_TREE if no simplification is
    possible.  Note that the resulting type may be different from
    the type pointed to in the sense that it is still compatible
    from the langhooks point of view. */
@@ -5789,7 +6870,8 @@ gimple_fold_indirect_ref (tree t)
 
   STRIP_NOPS (sub);
   subtype = TREE_TYPE (sub);
-  if (!POINTER_TYPE_P (subtype))
+  if (!POINTER_TYPE_P (subtype)
+      || TYPE_REF_CAN_ALIAS_ALL (ptype))
     return NULL_TREE;
 
   if (TREE_CODE (sub) == ADDR_EXPR)
@@ -5849,8 +6931,8 @@ gimple_fold_indirect_ref (tree t)
             = tree_to_shwi (part_width) / BITS_PER_UNIT;
           unsigned HOST_WIDE_INT indexi = offset * BITS_PER_UNIT;
           tree index = bitsize_int (indexi);
-          if (offset / part_widthi
-             < TYPE_VECTOR_SUBPARTS (TREE_TYPE (addrtype)))
+         if (known_lt (offset / part_widthi,
+                       TYPE_VECTOR_SUBPARTS (TREE_TYPE (addrtype))))
             return fold_build3 (BIT_FIELD_REF, type, TREE_OPERAND (addr, 0),
                                 part_width, index);
        }
@@ -5870,7 +6952,7 @@ gimple_fold_indirect_ref (tree t)
          || DECL_P (TREE_OPERAND (addr, 0)))
        return fold_build2 (MEM_REF, type,
                            addr,
-                           wide_int_to_tree (ptype, off));
+                           wide_int_to_tree (ptype, wi::to_wide (off)));
     }
 
   /* *(foo *)fooarrptr => (*fooarrptr)[0] */
@@ -5976,10 +7058,7 @@ gimple_build (gimple_seq *seq, location_t loc,
   tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-       res = make_ssa_name (type);
-      else
-       res = create_tmp_reg (type);
+      res = create_tmp_reg_or_ssa_name (type);
       gimple *stmt;
       if (code == REALPART_EXPR
          || code == IMAGPART_EXPR
@@ -6005,10 +7084,7 @@ gimple_build (gimple_seq *seq, location_t loc,
   tree res = gimple_simplify (code, type, op0, op1, seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-       res = make_ssa_name (type);
-      else
-       res = create_tmp_reg (type);
+      res = create_tmp_reg_or_ssa_name (type);
       gimple *stmt = gimple_build_assign (res, code, op0, op1);
       gimple_set_location (stmt, loc);
       gimple_seq_add_stmt_without_update (seq, stmt);
@@ -6029,10 +7105,7 @@ gimple_build (gimple_seq *seq, location_t loc,
                              seq, gimple_build_valueize);
   if (!res)
     {
-      if (gimple_in_ssa_p (cfun))
-       res = make_ssa_name (type);
-      else
-       res = create_tmp_reg (type);
+      res = create_tmp_reg_or_ssa_name (type);
       gimple *stmt;
       if (code == BIT_FIELD_REF)
        stmt = gimple_build_assign (res, code,
@@ -6062,10 +7135,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 1, arg0);
       if (!VOID_TYPE_P (type))
        {
-         if (gimple_in_ssa_p (cfun))
-           res = make_ssa_name (type);
-         else
-           res = create_tmp_reg (type);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -6091,10 +7161,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 2, arg0, arg1);
       if (!VOID_TYPE_P (type))
        {
-         if (gimple_in_ssa_p (cfun))
-           res = make_ssa_name (type);
-         else
-           res = create_tmp_reg (type);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -6122,10 +7189,7 @@ gimple_build (gimple_seq *seq, location_t loc,
       gimple *stmt = gimple_build_call (decl, 3, arg0, arg1, arg2);
       if (!VOID_TYPE_P (type))
        {
-         if (gimple_in_ssa_p (cfun))
-           res = make_ssa_name (type);
-         else
-           res = create_tmp_reg (type);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -6162,6 +7226,69 @@ gimple_convert_to_ptrofftype (gimple_seq *seq, location_t loc, tree op)
   return gimple_convert (seq, loc, sizetype, op);
 }
 
+/* Build a vector of type TYPE in which each element has the value OP.
+   Return a gimple value for the result, appending any new statements
+   to SEQ.  */
+
+tree
+gimple_build_vector_from_val (gimple_seq *seq, location_t loc, tree type,
+                             tree op)
+{
+  if (!TYPE_VECTOR_SUBPARTS (type).is_constant ()
+      && !CONSTANT_CLASS_P (op))
+    return gimple_build (seq, loc, VEC_DUPLICATE_EXPR, type, op);
+
+  tree res, vec = build_vector_from_val (type, op);
+  if (is_gimple_val (vec))
+    return vec;
+  if (gimple_in_ssa_p (cfun))
+    res = make_ssa_name (type);
+  else
+    res = create_tmp_reg (type);
+  gimple *stmt = gimple_build_assign (res, vec);
+  gimple_set_location (stmt, loc);
+  gimple_seq_add_stmt_without_update (seq, stmt);
+  return res;
+}
+
+/* Build a vector from BUILDER, handling the case in which some elements
+   are non-constant.  Return a gimple value for the result, appending any
+   new instructions to SEQ.
+
+   BUILDER must not have a stepped encoding on entry.  This is because
+   the function is not geared up to handle the arithmetic that would
+   be needed in the variable case, and any code building a vector that
+   is known to be constant should use BUILDER->build () directly.  */
+
+tree
+gimple_build_vector (gimple_seq *seq, location_t loc,
+                    tree_vector_builder *builder)
+{
+  gcc_assert (builder->nelts_per_pattern () <= 2);
+  unsigned int encoded_nelts = builder->encoded_nelts ();
+  for (unsigned int i = 0; i < encoded_nelts; ++i)
+    if (!TREE_CONSTANT ((*builder)[i]))
+      {
+       tree type = builder->type ();
+       unsigned int nelts = TYPE_VECTOR_SUBPARTS (type).to_constant ();
+       vec<constructor_elt, va_gc> *v;
+       vec_alloc (v, nelts);
+       for (i = 0; i < nelts; ++i)
+         CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, builder->elt (i));
+
+       tree res;
+       if (gimple_in_ssa_p (cfun))
+         res = make_ssa_name (type);
+       else
+         res = create_tmp_reg (type);
+       gimple *stmt = gimple_build_assign (res, build_constructor (type, v));
+       gimple_set_location (stmt, loc);
+       gimple_seq_add_stmt_without_update (seq, stmt);
+       return res;
+      }
+  return builder->build ();
+}
+
 /* Return true if the result of assignment STMT is known to be non-negative.
    If the return value is based on the assumption that signed overflow is
    undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
@@ -6211,7 +7338,7 @@ gimple_call_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
     gimple_call_arg (stmt, 1) : NULL_TREE;
 
   return tree_call_nonnegative_warnv_p (gimple_expr_type (stmt),
-                                       gimple_call_fndecl (stmt),
+                                       gimple_call_combined_fn (stmt),
                                        arg0,
                                        arg1,
                                        strict_overflow_p, depth);
@@ -6262,7 +7389,7 @@ gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
 
 /* Return true if the floating-point value computed by assignment STMT
    is known to have an integer value.  We also allow +Inf, -Inf and NaN
-   to be considered integer values.
+   to be considered integer values. Return false for signaling NaN.
 
    DEPTH is the current nesting depth of the query.  */
 
@@ -6291,7 +7418,7 @@ gimple_assign_integer_valued_real_p (gimple *stmt, int depth)
 
 /* Return true if the floating-point value computed by call STMT is known
    to have an integer value.  We also allow +Inf, -Inf and NaN to be
-   considered integer values.
+   considered integer values. Return false for signaling NaN.
 
    DEPTH is the current nesting depth of the query.  */
 
@@ -6304,13 +7431,13 @@ gimple_call_integer_valued_real_p (gimple *stmt, int depth)
   tree arg1 = (gimple_call_num_args (stmt) > 1
               ? gimple_call_arg (stmt, 1)
               : NULL_TREE);
-  return integer_valued_real_call_p (gimple_call_fndecl (stmt),
+  return integer_valued_real_call_p (gimple_call_combined_fn (stmt),
                                     arg0, arg1, depth);
 }
 
 /* Return true if the floating-point result of phi STMT is known to have
    an integer value.  We also allow +Inf, -Inf and NaN to be considered
-   integer values.
+   integer values. Return false for signaling NaN.
 
    DEPTH is the current nesting depth of the query.  */
 
@@ -6328,7 +7455,7 @@ gimple_phi_integer_valued_real_p (gimple *stmt, int depth)
 
 /* Return true if the floating-point value computed by STMT is known
    to have an integer value.  We also allow +Inf, -Inf and NaN to be
-   considered integer values.
+   considered integer values. Return false for signaling NaN.
 
    DEPTH is the current nesting depth of the query.  */