]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Add new gimple-ssa-warn-access pass.
authorMartin Sebor <msebor@redhat.com>
Wed, 28 Jul 2021 21:28:10 +0000 (15:28 -0600)
committerMartin Sebor <msebor@redhat.com>
Wed, 28 Jul 2021 22:02:17 +0000 (16:02 -0600)
gcc/ChangeLog:

* Makefile.in (OBJS): Add gimple-ssa-warn-access.o and pointer-query.o.
* attribs.h (fndecl_dealloc_argno): Move fndecl_dealloc_argno to tree.h.
* builtins.c (compute_objsize_r): Move to pointer-query.cc.
(access_ref::access_ref): Same.
(access_ref::phi): Same.
(access_ref::get_ref): Same.
(access_ref::size_remaining): Same.
(access_ref::offset_in_range): Same.
(access_ref::add_offset): Same.
(access_ref::inform_access): Same.
(ssa_name_limit_t::visit_phi): Same.
(ssa_name_limit_t::leave_phi): Same.
(ssa_name_limit_t::next): Same.
(ssa_name_limit_t::next_phi): Same.
(ssa_name_limit_t::~ssa_name_limit_t): Same.
(pointer_query::pointer_query): Same.
(pointer_query::get_ref): Same.
(pointer_query::put_ref): Same.
(pointer_query::flush_cache): Same.
(warn_string_no_nul): Move to gimple-ssa-warn-access.cc.
(check_nul_terminated_array): Same.
(unterminated_array): Same.
(maybe_warn_for_bound): Same.
(check_read_access): Same.
(warn_for_access): Same.
(get_size_range): Same.
(check_access): Same.
(gimple_call_alloc_size): Move to tree.c.
(gimple_parm_array_size): Move to pointer-query.cc.
(get_offset_range): Same.
(gimple_call_return_array): Same.
(handle_min_max_size): Same.
(handle_array_ref): Same.
(handle_mem_ref): Same.
(compute_objsize): Same.
(gimple_call_alloc_p): Move to gimple-ssa-warn-access.cc.
(call_dealloc_argno): Same.
(fndecl_dealloc_argno): Same.
(new_delete_mismatch_p): Same.
(matching_alloc_calls_p): Same.
(warn_dealloc_offset): Same.
(maybe_emit_free_warning): Same.
* builtins.h (check_nul_terminated_array): Move to
gimple-ssa-warn-access.h.
(check_nul_terminated_array): Same.
(warn_string_no_nul): Same.
(unterminated_array): Same.
(class ssa_name_limit_t): Same.
(class pointer_query): Same.
(struct access_ref): Same.
(class range_query): Same.
(struct access_data): Same.
(gimple_call_alloc_size): Same.
(gimple_parm_array_size): Same.
(compute_objsize): Same.
(class access_data): Same.
(maybe_emit_free_warning): Same.
* calls.c (initialize_argument_information): Remove call to
maybe_emit_free_warning.
* gimple-array-bounds.cc: Include new header..
* gimple-fold.c: Same.
* gimple-ssa-sprintf.c: Same.
* gimple-ssa-warn-restrict.c: Same.
* passes.def: Add pass_warn_access.
* tree-pass.h (make_pass_warn_access): Declare.
* tree-ssa-strlen.c: Include new headers.
* tree.c (fndecl_dealloc_argno): Move here from builtins.c.
* tree.h (fndecl_dealloc_argno): Move here from attribs.h.
* gimple-ssa-warn-access.cc: New file.
* gimple-ssa-warn-access.h: New file.
* pointer-query.cc: New file.
* pointer-query.h: New file.

gcc/cp/ChangeLog:

* init.c: Include new header.

19 files changed:
gcc/Makefile.in
gcc/attribs.h
gcc/builtins.c
gcc/builtins.h
gcc/calls.c
gcc/cp/init.c
gcc/gimple-array-bounds.cc
gcc/gimple-fold.c
gcc/gimple-ssa-sprintf.c
gcc/gimple-ssa-warn-access.cc [new file with mode: 0644]
gcc/gimple-ssa-warn-access.h [new file with mode: 0644]
gcc/gimple-ssa-warn-restrict.c
gcc/passes.def
gcc/pointer-query.cc [new file with mode: 0644]
gcc/pointer-query.h [new file with mode: 0644]
gcc/tree-pass.h
gcc/tree-ssa-strlen.c
gcc/tree.c
gcc/tree.h

index 29bd4edb4b7e0353db64380a44585ec2eb26a651..98eb479a125e2bdd936eac011b8df506d9ee702d 100644 (file)
@@ -1414,6 +1414,7 @@ OBJS = \
        gimple-ssa-store-merging.o \
        gimple-ssa-strength-reduction.o \
        gimple-ssa-sprintf.o \
+       gimple-ssa-warn-access.o \
        gimple-ssa-warn-alloca.o \
        gimple-ssa-warn-restrict.o \
        gimple-streamer-in.o \
@@ -1524,6 +1525,7 @@ OBJS = \
        ordered-hash-map-tests.o \
        passes.o \
        plugin.o \
+       pointer-query.o \
        postreload-gcse.o \
        postreload.o \
        predict.o \
index df78eb152f9b6d4e502b93f06f11b3548998c037..87231b954c6e776dbce3903d9d9e913d58021260 100644 (file)
@@ -316,6 +316,4 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
 extern attr_access *get_parm_access (rdwr_map &, tree,
                                     tree = current_function_decl);
 
-extern unsigned fndecl_dealloc_argno (tree fndecl);
-
 #endif // GCC_ATTRIBS_H
index 170d776c41001ab1085891eff1bcb75a1659193a..845a8bb12012c76e7d874058e347967858cfbc62 100644 (file)
@@ -80,6 +80,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "attr-fnspec.h"
 #include "demangle.h"
 #include "gimple-range.h"
+#include "pointer-query.h"
+#include "gimple-ssa-warn-access.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -185,8 +187,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
 static tree fold_builtin_object_size (tree, tree);
 static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
-static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
-                              pointer_query *);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -199,565 +199,6 @@ static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
 
-access_ref::access_ref (tree bound /* = NULL_TREE */,
-                       bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
-  base0 (true), parmarray ()
-{
-  /* Set to valid.  */
-  offrng[0] = offrng[1] = 0;
-  offmax[0] = offmax[1] = 0;
-  /* Invalidate.   */
-  sizrng[0] = sizrng[1] = -1;
-
-  /* Set the default bounds of the access and adjust below.  */
-  bndrng[0] = minaccess ? 1 : 0;
-  bndrng[1] = HOST_WIDE_INT_M1U;
-
-  /* When BOUND is nonnull and a range can be extracted from it,
-     set the bounds of the access to reflect both it and MINACCESS.
-     BNDRNG[0] is the size of the minimum access.  */
-  tree rng[2];
-  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
-    {
-      bndrng[0] = wi::to_offset (rng[0]);
-      bndrng[1] = wi::to_offset (rng[1]);
-      bndrng[0] = bndrng[0] > 0 && minaccess ? 1 : 0;
-    }
-}
-
-/* Return the PHI node REF refers to or null if it doesn't.  */
-
-gphi *
-access_ref::phi () const
-{
-  if (!ref || TREE_CODE (ref) != SSA_NAME)
-    return NULL;
-
-  gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-  if (gimple_code (def_stmt) != GIMPLE_PHI)
-    return NULL;
-
-  return as_a <gphi *> (def_stmt);
-}
-
-/* Determine and return the largest object to which *THIS.  If *THIS
-   refers to a PHI and PREF is nonnull, fill *PREF with the details
-   of the object determined by compute_objsize(ARG, OSTYPE) for each
-   PHI argument ARG.  */
-
-tree
-access_ref::get_ref (vec<access_ref> *all_refs,
-                    access_ref *pref /* = NULL */,
-                    int ostype /* = 1 */,
-                    ssa_name_limit_t *psnlim /* = NULL */,
-                    pointer_query *qry /* = NULL */) const
-{
-  gphi *phi_stmt = this->phi ();
-  if (!phi_stmt)
-    return ref;
-
-  /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might
-     cause unbounded recursion.  */
-  ssa_name_limit_t snlim_buf;
-  if (!psnlim)
-    psnlim = &snlim_buf;
-
-  if (!psnlim->visit_phi (ref))
-    return NULL_TREE;
-
-  /* Reflects the range of offsets of all PHI arguments refer to the same
-     object (i.e., have the same REF).  */
-  access_ref same_ref;
-  /* The conservative result of the PHI reflecting the offset and size
-     of the largest PHI argument, regardless of whether or not they all
-     refer to the same object.  */
-  pointer_query empty_qry;
-  if (!qry)
-    qry = &empty_qry;
-
-  access_ref phi_ref;
-  if (pref)
-    {
-      phi_ref = *pref;
-      same_ref = *pref;
-    }
-
-  /* Set if any argument is a function array (or VLA) parameter not
-     declared [static].  */
-  bool parmarray = false;
-  /* The size of the smallest object referenced by the PHI arguments.  */
-  offset_int minsize = 0;
-  const offset_int maxobjsize = wi::to_offset (max_object_size ());
-  /* The offset of the PHI, not reflecting those of its arguments.  */
-  const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
-
-  const unsigned nargs = gimple_phi_num_args (phi_stmt);
-  for (unsigned i = 0; i < nargs; ++i)
-    {
-      access_ref phi_arg_ref;
-      tree arg = gimple_phi_arg_def (phi_stmt, i);
-      if (!compute_objsize_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
-         || phi_arg_ref.sizrng[0] < 0)
-       /* A PHI with all null pointer arguments.  */
-       return NULL_TREE;
-
-      /* Add PREF's offset to that of the argument.  */
-      phi_arg_ref.add_offset (orng[0], orng[1]);
-      if (TREE_CODE (arg) == SSA_NAME)
-       qry->put_ref (arg, phi_arg_ref);
-
-      if (all_refs)
-       all_refs->safe_push (phi_arg_ref);
-
-      const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
-                                  || phi_arg_ref.sizrng[1] != maxobjsize);
-
-      parmarray |= phi_arg_ref.parmarray;
-
-      const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
-
-      if (phi_ref.sizrng[0] < 0)
-       {
-         if (!nullp)
-           same_ref = phi_arg_ref;
-         phi_ref = phi_arg_ref;
-         if (arg_known_size)
-           minsize = phi_arg_ref.sizrng[0];
-         continue;
-       }
-
-      const bool phi_known_size = (phi_ref.sizrng[0] != 0
-                                  || phi_ref.sizrng[1] != maxobjsize);
-
-      if (phi_known_size && phi_arg_ref.sizrng[0] < minsize)
-       minsize = phi_arg_ref.sizrng[0];
-
-      /* Disregard null pointers in PHIs with two or more arguments.
-        TODO: Handle this better!  */
-      if (nullp)
-       continue;
-
-      /* Determine the amount of remaining space in the argument.  */
-      offset_int argrem[2];
-      argrem[1] = phi_arg_ref.size_remaining (argrem);
-
-      /* Determine the amount of remaining space computed so far and
-        if the remaining space in the argument is more use it instead.  */
-      offset_int phirem[2];
-      phirem[1] = phi_ref.size_remaining (phirem);
-
-      if (phi_arg_ref.ref != same_ref.ref)
-       same_ref.ref = NULL_TREE;
-
-      if (phirem[1] < argrem[1]
-         || (phirem[1] == argrem[1]
-             && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1]))
-       /* Use the argument with the most space remaining as the result,
-          or the larger one if the space is equal.  */
-       phi_ref = phi_arg_ref;
-
-      /* Set SAME_REF.OFFRNG to the maximum range of all arguments.  */
-      if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
-       same_ref.offrng[0] = phi_arg_ref.offrng[0];
-      if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
-       same_ref.offrng[1] = phi_arg_ref.offrng[1];
-    }
-
-  if (!same_ref.ref && same_ref.offrng[0] != 0)
-    /* Clear BASE0 if not all the arguments refer to the same object and
-       if not all their offsets are zero-based.  This allows the final
-       PHI offset to out of bounds for some arguments but not for others
-       (or negative even of all the arguments are BASE0), which is overly
-       permissive.  */
-    phi_ref.base0 = false;
-
-  if (same_ref.ref)
-    phi_ref = same_ref;
-  else
-    {
-      /* Replace the lower bound of the largest argument with the size
-        of the smallest argument, and set PARMARRAY if any argument
-        was one.  */
-      phi_ref.sizrng[0] = minsize;
-      phi_ref.parmarray = parmarray;
-    }
-
-  if (phi_ref.sizrng[0] < 0)
-    {
-      /* Fail if none of the PHI's arguments resulted in updating PHI_REF
-        (perhaps because they have all been already visited by prior
-        recursive calls).  */
-      psnlim->leave_phi (ref);
-      return NULL_TREE;
-    }
-
-  /* Avoid changing *THIS.  */
-  if (pref && pref != this)
-    *pref = phi_ref;
-
-  psnlim->leave_phi (ref);
-
-  return phi_ref.ref;
-}
-
-/* Return the maximum amount of space remaining and if non-null, set
-   argument to the minimum.  */
-
-offset_int
-access_ref::size_remaining (offset_int *pmin /* = NULL */) const
-{
-  offset_int minbuf;
-  if (!pmin)
-    pmin = &minbuf;
-
-  /* add_offset() ensures the offset range isn't inverted.  */
-  gcc_checking_assert (offrng[0] <= offrng[1]);
-
-  if (base0)
-    {
-      /* The offset into referenced object is zero-based (i.e., it's
-        not referenced by a pointer into middle of some unknown object).  */
-      if (offrng[0] < 0 && offrng[1] < 0)
-       {
-         /* If the offset is negative the remaining size is zero.  */
-         *pmin = 0;
-         return 0;
-       }
-
-      if (sizrng[1] <= offrng[0])
-       {
-         /* If the starting offset is greater than or equal to the upper
-            bound on the size of the object, the space remaining is zero.
-            As a special case, if it's equal, set *PMIN to -1 to let
-            the caller know the offset is valid and just past the end.  */
-         *pmin = sizrng[1] == offrng[0] ? -1 : 0;
-         return 0;
-       }
-
-      /* Otherwise return the size minus the lower bound of the offset.  */
-      offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
-
-      *pmin = sizrng[0] - or0;
-      return sizrng[1] - or0;
-    }
-
-  /* The offset to the referenced object isn't zero-based (i.e., it may
-     refer to a byte other than the first.  The size of such an object
-     is constrained only by the size of the address space (the result
-     of max_object_size()).  */
-  if (sizrng[1] <= offrng[0])
-    {
-      *pmin = 0;
-      return 0;
-    }
-
-  offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
-
-  *pmin = sizrng[0] - or0;
-  return sizrng[1] - or0;
-}
-
-/* Return true if the offset and object size are in range for SIZE.  */
-
-bool
-access_ref::offset_in_range (const offset_int &size) const
-{
-  if (size_remaining () < size)
-    return false;
-
-  if (base0)
-    return offmax[0] >= 0 && offmax[1] <= sizrng[1];
-
-  offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-  return offmax[0] > -maxoff && offmax[1] < maxoff;
-}
-
-/* Add the range [MIN, MAX] to the offset range.  For known objects (with
-   zero-based offsets) at least one of whose offset's bounds is in range,
-   constrain the other (or both) to the bounds of the object (i.e., zero
-   and the upper bound of its size).  This improves the quality of
-   diagnostics.  */
-
-void access_ref::add_offset (const offset_int &min, const offset_int &max)
-{
-  if (min <= max)
-    {
-      /* To add an ordinary range just add it to the bounds.  */
-      offrng[0] += min;
-      offrng[1] += max;
-    }
-  else if (!base0)
-    {
-      /* To add an inverted range to an offset to an unknown object
-        expand it to the maximum.  */
-      add_max_offset ();
-      return;
-    }
-  else
-    {
-      /* To add an inverted range to an offset to an known object set
-        the upper bound to the maximum representable offset value
-        (which may be greater than MAX_OBJECT_SIZE).
-        The lower bound is either the sum of the current offset and
-        MIN when abs(MAX) is greater than the former, or zero otherwise.
-        Zero because then then inverted range includes the negative of
-        the lower bound.  */
-      offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-      offrng[1] = maxoff;
-
-      if (max >= 0)
-       {
-         offrng[0] = 0;
-         if (offmax[0] > 0)
-           offmax[0] = 0;
-         return;
-       }
-
-      offset_int absmax = wi::abs (max);
-      if (offrng[0] < absmax)
-       {
-         offrng[0] += min;
-         /* Cap the lower bound at the upper (set to MAXOFF above)
-            to avoid inadvertently recreating an inverted range.  */
-         if (offrng[1] < offrng[0])
-           offrng[0] = offrng[1];
-       }
-      else
-       offrng[0] = 0;
-    }
-
-  /* Set the minimum and maximmum computed so far. */
-  if (offrng[1] < 0 && offrng[1] < offmax[0])
-    offmax[0] = offrng[1];
-  if (offrng[0] > 0 && offrng[0] > offmax[1])
-    offmax[1] = offrng[0];
-
-  if (!base0)
-    return;
-
-  /* When referencing a known object check to see if the offset computed
-     so far is in bounds... */
-  offset_int remrng[2];
-  remrng[1] = size_remaining (remrng);
-  if (remrng[1] > 0 || remrng[0] < 0)
-    {
-      /* ...if so, constrain it so that neither bound exceeds the size of
-        the object.  Out of bounds offsets are left unchanged, and, for
-        better or worse, become in bounds later.  They should be detected
-        and diagnosed at the point they first become invalid by
-        -Warray-bounds.  */
-      if (offrng[0] < 0)
-       offrng[0] = 0;
-      if (offrng[1] > sizrng[1])
-       offrng[1] = sizrng[1];
-    }
-}
-
-/* Set a bit for the PHI in VISITED and return true if it wasn't
-   already set.  */
-
-bool
-ssa_name_limit_t::visit_phi (tree ssa_name)
-{
-  if (!visited)
-    visited = BITMAP_ALLOC (NULL);
-
-  /* Return false if SSA_NAME has already been visited.  */
-  return bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name));
-}
-
-/* Clear a bit for the PHI in VISITED.  */
-
-void
-ssa_name_limit_t::leave_phi (tree ssa_name)
-{
-  /* Return false if SSA_NAME has already been visited.  */
-  bitmap_clear_bit (visited, SSA_NAME_VERSION (ssa_name));
-}
-
-/* Return false if the SSA_NAME chain length counter has reached
-   the limit, otherwise increment the counter and return true.  */
-
-bool
-ssa_name_limit_t::next ()
-{
-  /* Return a negative value to let caller avoid recursing beyond
-     the specified limit.  */
-  if (ssa_def_max == 0)
-    return false;
-
-  --ssa_def_max;
-  return true;
-}
-
-/* If the SSA_NAME has already been "seen" return a positive value.
-   Otherwise add it to VISITED.  If the SSA_NAME limit has been
-   reached, return a negative value.  Otherwise return zero.  */
-
-int
-ssa_name_limit_t::next_phi (tree ssa_name)
-{
-  {
-    gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
-    /* Return a positive value if the PHI has already been visited.  */
-    if (gimple_code (def_stmt) == GIMPLE_PHI
-       && !visit_phi (ssa_name))
-      return 1;
-  }
-
-  /* Return a negative value to let caller avoid recursing beyond
-     the specified limit.  */
-  if (ssa_def_max == 0)
-    return -1;
-
-  --ssa_def_max;
-
-  return 0;
-}
-
-ssa_name_limit_t::~ssa_name_limit_t ()
-{
-  if (visited)
-    BITMAP_FREE (visited);
-}
-
-/* Default ctor.  Initialize object with pointers to the range_query
-   and cache_type instances to use or null.  */
-
-pointer_query::pointer_query (range_query *qry /* = NULL */,
-                             cache_type *cache /* = NULL */)
-: rvals (qry), var_cache (cache), hits (), misses (),
-  failures (), depth (), max_depth ()
-{
-  /* No op.  */
-}
-
-/* Return a pointer to the cached access_ref instance for the SSA_NAME
-   PTR if it's there or null otherwise.  */
-
-const access_ref *
-pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
-{
-  if (!var_cache)
-    {
-      ++misses;
-      return NULL;
-    }
-
-  unsigned version = SSA_NAME_VERSION (ptr);
-  unsigned idx = version << 1 | (ostype & 1);
-  if (var_cache->indices.length () <= idx)
-    {
-      ++misses;
-      return NULL;
-    }
-
-  unsigned cache_idx = var_cache->indices[idx];
-  if (var_cache->access_refs.length () <= cache_idx)
-    {
-      ++misses;
-      return NULL;
-    }
-
-  access_ref &cache_ref = var_cache->access_refs[cache_idx];
-  if (cache_ref.ref)
-    {
-      ++hits;
-      return &cache_ref;
-    }
-
-  ++misses;
-  return NULL;
-}
-
-/* Retrieve the access_ref instance for a variable from the cache if it's
-   there or compute it and insert it into the cache if it's nonnonull.  */
-
-bool
-pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
-{
-  const unsigned version
-    = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
-
-  if (var_cache && version)
-    {
-      unsigned idx = version << 1 | (ostype & 1);
-      if (idx < var_cache->indices.length ())
-       {
-         unsigned cache_idx = var_cache->indices[idx] - 1;
-         if (cache_idx < var_cache->access_refs.length ()
-             && var_cache->access_refs[cache_idx].ref)
-           {
-             ++hits;
-             *pref = var_cache->access_refs[cache_idx];
-             return true;
-           }
-       }
-
-      ++misses;
-    }
-
-  if (!compute_objsize (ptr, ostype, pref, this))
-    {
-      ++failures;
-      return false;
-    }
-
-  return true;
-}
-
-/* Add a copy of the access_ref REF for the SSA_NAME to the cache if it's
-   nonnull.  */
-
-void
-pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
-{
-  /* Only add populated/valid entries.  */
-  if (!var_cache || !ref.ref || ref.sizrng[0] < 0)
-    return;
-
-  /* Add REF to the two-level cache.  */
-  unsigned version = SSA_NAME_VERSION (ptr);
-  unsigned idx = version << 1 | (ostype & 1);
-
-  /* Grow INDICES if necessary.  An index is valid if it's nonzero.
-     Its value minus one is the index into ACCESS_REFS.  Not all
-     entries are valid.  */
-  if (var_cache->indices.length () <= idx)
-    var_cache->indices.safe_grow_cleared (idx + 1);
-
-  if (!var_cache->indices[idx])
-    var_cache->indices[idx] = var_cache->access_refs.length () + 1;
-
-  /* Grow ACCESS_REF cache if necessary.  An entry is valid if its
-     REF member is nonnull.  All entries except for the last two
-     are valid.  Once nonnull, the REF value must stay unchanged.  */
-  unsigned cache_idx = var_cache->indices[idx];
-  if (var_cache->access_refs.length () <= cache_idx)
-    var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
-
-  access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
-  if (cache_ref.ref)
-  {
-    gcc_checking_assert (cache_ref.ref == ref.ref);
-    return;
-  }
-
-  cache_ref = ref;
-}
-
-/* Flush the cache if it's nonnull.  */
-
-void
-pointer_query::flush_cache ()
-{
-  if (!var_cache)
-    return;
-  var_cache->indices.release ();
-  var_cache->access_refs.release ();
-}
-
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -1106,218 +547,6 @@ string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
   return n;
 }
 
-/* For a call EXPR at LOC to a function FNAME that expects a string
-   in the argument ARG, issue a diagnostic due to it being a called
-   with an argument that is a character array with no terminating
-   NUL.  SIZE is the EXACT size of the array, and BNDRNG the number
-   of characters in which the NUL is expected.  Either EXPR or FNAME
-   may be null but noth both.  SIZE may be null when BNDRNG is null.  */
-
-void
-warn_string_no_nul (location_t loc, tree expr, const char *fname,
-                   tree arg, tree decl, tree size /* = NULL_TREE */,
-                   bool exact /* = false */,
-                   const wide_int bndrng[2] /* = NULL */)
-{
-  const opt_code opt = OPT_Wstringop_overread;
-  if ((expr && warning_suppressed_p (expr, opt))
-      || warning_suppressed_p (arg, opt))
-    return;
-
-  loc = expansion_point_location_if_in_system_header (loc);
-  bool warned;
-
-  /* Format the bound range as a string to keep the nuber of messages
-     from exploding.  */
-  char bndstr[80];
-  *bndstr = 0;
-  if (bndrng)
-    {
-      if (bndrng[0] == bndrng[1])
-       sprintf (bndstr, "%llu", (unsigned long long) bndrng[0].to_uhwi ());
-      else
-       sprintf (bndstr, "[%llu, %llu]",
-                (unsigned long long) bndrng[0].to_uhwi (),
-                (unsigned long long) bndrng[1].to_uhwi ());
-    }
-
-  const tree maxobjsize = max_object_size ();
-  const wide_int maxsiz = wi::to_wide (maxobjsize);
-  if (expr)
-    {
-      tree func = get_callee_fndecl (expr);
-      if (bndrng)
-       {
-         if (wi::ltu_p (maxsiz, bndrng[0]))
-           warned = warning_at (loc, opt,
-                                "%qD specified bound %s exceeds "
-                                "maximum object size %E",
-                                func, bndstr, maxobjsize);
-         else
-           {
-             bool maybe = wi::to_wide (size) == bndrng[0];
-             warned = warning_at (loc, opt,
-                                  exact
-                                  ? G_("%qD specified bound %s exceeds "
-                                       "the size %E of unterminated array")
-                                  : (maybe
-                                     ? G_("%qD specified bound %s may "
-                                          "exceed the size of at most %E "
-                                          "of unterminated array")
-                                     : G_("%qD specified bound %s exceeds "
-                                          "the size of at most %E "
-                                          "of unterminated array")),
-                                  func, bndstr, size);
-           }
-       }
-      else
-       warned = warning_at (loc, opt,
-                            "%qD argument missing terminating nul",
-                            func);
-    }
-  else
-    {
-      if (bndrng)
-       {
-         if (wi::ltu_p (maxsiz, bndrng[0]))
-           warned = warning_at (loc, opt,
-                                "%qs specified bound %s exceeds "
-                                "maximum object size %E",
-                                fname, bndstr, maxobjsize);
-         else
-           {
-             bool maybe = wi::to_wide (size) == bndrng[0];
-             warned = warning_at (loc, opt,
-                                  exact
-                                  ? G_("%qs specified bound %s exceeds "
-                                       "the size %E of unterminated array")
-                                  : (maybe
-                                     ? G_("%qs specified bound %s may "
-                                          "exceed the size of at most %E "
-                                          "of unterminated array")
-                                     : G_("%qs specified bound %s exceeds "
-                                          "the size of at most %E "
-                                          "of unterminated array")),
-                                  fname, bndstr, size);
-           }
-       }
-      else
-       warned = warning_at (loc, opt,
-                            "%qs argument missing terminating nul",
-                            fname);
-    }
-
-  if (warned)
-    {
-      inform (DECL_SOURCE_LOCATION (decl),
-             "referenced argument declared here");
-      suppress_warning (arg, opt);
-      if (expr)
-       suppress_warning (expr, opt);
-    }
-}
-
-/* For a call EXPR (which may be null) that expects a string argument
-   SRC as an argument, returns false if SRC is a character array with
-   no terminating NUL.  When nonnull, BOUND is the number of characters
-   in which to expect the terminating NUL.  RDONLY is true for read-only
-   accesses such as strcmp, false for read-write such as strcpy.  When
-   EXPR is also issues a warning.  */
-
-bool
-check_nul_terminated_array (tree expr, tree src,
-                           tree bound /* = NULL_TREE */)
-{
-  /* The constant size of the array SRC points to.  The actual size
-     may be less of EXACT is true, but not more.  */
-  tree size;
-  /* True if SRC involves a non-constant offset into the array.  */
-  bool exact;
-  /* The unterminated constant array SRC points to.  */
-  tree nonstr = unterminated_array (src, &size, &exact);
-  if (!nonstr)
-    return true;
-
-  /* NONSTR refers to the non-nul terminated constant array and SIZE
-     is the constant size of the array in bytes.  EXACT is true when
-     SIZE is exact.  */
-
-  wide_int bndrng[2];
-  if (bound)
-    {
-      value_range r;
-
-      get_global_range_query ()->range_of_expr (r, bound);
-
-      if (r.kind () != VR_RANGE)
-       return true;
-
-      bndrng[0] = r.lower_bound ();
-      bndrng[1] = r.upper_bound ();
-
-      if (exact)
-       {
-         if (wi::leu_p (bndrng[0], wi::to_wide (size)))
-           return true;
-       }
-      else if (wi::lt_p (bndrng[0], wi::to_wide (size), UNSIGNED))
-       return true;
-    }
-
-  if (expr)
-    warn_string_no_nul (EXPR_LOCATION (expr), expr, NULL, src, nonstr,
-                       size, exact, bound ? bndrng : NULL);
-
-  return false;
-}
-
-/* If EXP refers to an unterminated constant character array return
-   the declaration of the object of which the array is a member or
-   element and if SIZE is not null, set *SIZE to the size of
-   the unterminated array and set *EXACT if the size is exact or
-   clear it otherwise.  Otherwise return null.  */
-
-tree
-unterminated_array (tree exp, tree *size /* = NULL */, bool *exact /* = NULL */)
-{
-  /* C_STRLEN will return NULL and set DECL in the info
-     structure if EXP references a unterminated array.  */
-  c_strlen_data lendata = { };
-  tree len = c_strlen (exp, 1, &lendata);
-  if (len == NULL_TREE && lendata.minlen && lendata.decl)
-     {
-       if (size)
-       {
-         len = lendata.minlen;
-         if (lendata.off)
-           {
-             /* Constant offsets are already accounted for in LENDATA.MINLEN,
-                but not in a SSA_NAME + CST expression.  */
-             if (TREE_CODE (lendata.off) == INTEGER_CST)
-               *exact = true;
-             else if (TREE_CODE (lendata.off) == PLUS_EXPR
-                      && TREE_CODE (TREE_OPERAND (lendata.off, 1)) == INTEGER_CST)
-               {
-                 /* Subtract the offset from the size of the array.  */
-                 *exact = false;
-                 tree temp = TREE_OPERAND (lendata.off, 1);
-                 temp = fold_convert (ssizetype, temp);
-                 len = fold_build2 (MINUS_EXPR, ssizetype, len, temp);
-               }
-             else
-               *exact = false;
-           }
-         else
-           *exact = true;
-
-         *size = len;
-       }
-       return lendata.decl;
-     }
-
-  return NULL_TREE;
-}
-
 /* Compute the length of a null-terminated character string or wide
    character string handling character sizes of 1, 2, and 4 bytes.
    TREE_STRING_LENGTH is not the right way because it evaluates to
@@ -3969,10097 +3198,7353 @@ determine_block_size (tree len, rtx len_rtx,
                          GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
-/* Issue a warning OPT for a bounded call EXP with a bound in RANGE
-   accessing an object with SIZE.  */
-
+/* A convenience wrapper for check_access above to check access
+   by a read-only function like puts.  */
+
 static bool
-maybe_warn_for_bound (opt_code opt, location_t loc, tree exp, tree func,
-                     tree bndrng[2], tree size, const access_data *pad = NULL)
+check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
+                  int ost /* = 1 */)
 {
-  if (!bndrng[0] || warning_suppressed_p (exp, opt))
-    return false;
-
-  tree maxobjsize = max_object_size ();
-
-  bool warned = false;
-
-  if (opt == OPT_Wstringop_overread)
-    {
-      bool maybe = pad && pad->src.phi ();
-
-      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
-       {
-         if (bndrng[0] == bndrng[1])
-           warned = (func
-                     ? warning_at (loc, opt,
-                                   (maybe
-                                    ? G_("%qD specified bound %E may "
-                                         "exceed maximum object size %E")
-                                    : G_("%qD specified bound %E "
-                                         "exceeds maximum object size %E")),
-                                   func, bndrng[0], maxobjsize)
-                     : warning_at (loc, opt,
-                                   (maybe
-                                    ? G_("specified bound %E may "
-                                         "exceed maximum object size %E")
-                                    : G_("specified bound %E "
-                                         "exceeds maximum object size %E")),
-                                   bndrng[0], maxobjsize));
-         else
-           warned = (func
-                     ? warning_at (loc, opt,
-                                   (maybe
-                                    ? G_("%qD specified bound [%E, %E] may "
-                                         "exceed maximum object size %E")
-                                    : G_("%qD specified bound [%E, %E] "
-                                         "exceeds maximum object size %E")),
-                                   func, bndrng[0], bndrng[1], maxobjsize)
-                     : warning_at (loc, opt,
-                                   (maybe
-                                    ? G_("specified bound [%E, %E] may "
-                                         "exceed maximum object size %E")
-                                    : G_("specified bound [%E, %E] "
-                                         "exceeds maximum object size %E")),
-                                   bndrng[0], bndrng[1], maxobjsize));
-       }
-      else if (!size || tree_int_cst_le (bndrng[0], size))
-       return false;
-      else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD specified bound %E may exceed "
-                                     "source size %E")
-                                : G_("%qD specified bound %E exceeds "
-                                     "source size %E")),
-                               func, bndrng[0], size)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("specified bound %E may exceed "
-                                     "source size %E")
-                                : G_("specified bound %E exceeds "
-                                     "source size %E")),
-                               bndrng[0], size));
-      else
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD specified bound [%E, %E] may "
-                                     "exceed source size %E")
-                                : G_("%qD specified bound [%E, %E] exceeds "
-                                     "source size %E")),
-                               func, bndrng[0], bndrng[1], size)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("specified bound [%E, %E] may exceed "
-                                     "source size %E")
-                                : G_("specified bound [%E, %E] exceeds "
-                                     "source size %E")),
-                               bndrng[0], bndrng[1], size));
-      if (warned)
-       {
-         if (pad && pad->src.ref)
-           {
-             if (DECL_P (pad->src.ref))
-               inform (DECL_SOURCE_LOCATION (pad->src.ref),
-                       "source object declared here");
-             else if (EXPR_HAS_LOCATION (pad->src.ref))
-               inform (EXPR_LOCATION (pad->src.ref),
-                       "source object allocated here");
-           }
-         suppress_warning (exp, opt);
-       }
-
-      return warned;
-    }
-
-  bool maybe = pad && pad->dst.phi ();
-  if (tree_int_cst_lt (maxobjsize, bndrng[0]))
-    {
-      if (bndrng[0] == bndrng[1])
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD specified size %E may "
-                                     "exceed maximum object size %E")
-                                : G_("%qD specified size %E "
-                                     "exceeds maximum object size %E")),
-                               func, bndrng[0], maxobjsize)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("specified size %E may exceed "
-                                     "maximum object size %E")
-                                : G_("specified size %E exceeds "
-                                     "maximum object size %E")),
-                               bndrng[0], maxobjsize));
-      else
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD specified size between %E and %E "
-                                     "may exceed maximum object size %E")
-                                : G_("%qD specified size between %E and %E "
-                                     "exceeds maximum object size %E")),
-                               func, bndrng[0], bndrng[1], maxobjsize)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("specified size between %E and %E "
-                                     "may exceed maximum object size %E")
-                                : G_("specified size between %E and %E "
-                                     "exceeds maximum object size %E")),
-                               bndrng[0], bndrng[1], maxobjsize));
-    }
-  else if (!size || tree_int_cst_le (bndrng[0], size))
-    return false;
-  else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
-    warned = (func
-             ? warning_at (loc, opt,
-                           (maybe
-                            ? G_("%qD specified bound %E may exceed "
-                                 "destination size %E")
-                            : G_("%qD specified bound %E exceeds "
-                                 "destination size %E")),
-                           func, bndrng[0], size)
-             : warning_at (loc, opt,
-                           (maybe
-                            ? G_("specified bound %E may exceed "
-                                 "destination size %E")
-                            : G_("specified bound %E exceeds "
-                                 "destination size %E")),
-                           bndrng[0], size));
-  else
-    warned = (func
-             ? warning_at (loc, opt,
-                           (maybe
-                            ? G_("%qD specified bound [%E, %E] may exceed "
-                                 "destination size %E")
-                            : G_("%qD specified bound [%E, %E] exceeds "
-                                 "destination size %E")),
-                           func, bndrng[0], bndrng[1], size)
-             : warning_at (loc, opt,
-                           (maybe
-                            ? G_("specified bound [%E, %E] exceeds "
-                                 "destination size %E")
-                            : G_("specified bound [%E, %E] exceeds "
-                                 "destination size %E")),
-                           bndrng[0], bndrng[1], size));
-
-  if (warned)
-    {
-      if (pad && pad->dst.ref)
-       {
-         if (DECL_P (pad->dst.ref))
-           inform (DECL_SOURCE_LOCATION (pad->dst.ref),
-                   "destination object declared here");
-         else if (EXPR_HAS_LOCATION (pad->dst.ref))
-           inform (EXPR_LOCATION (pad->dst.ref),
-                   "destination object allocated here");
-       }
-      suppress_warning (exp, opt);
-    }
+  if (!warn_stringop_overread)
+    return true;
 
-  return warned;
+  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
+    bound = fold_convert (size_type_node, bound);
+  access_data data (exp, access_read_only, NULL_TREE, false, bound, true);
+  compute_objsize (src, ost, &data.src);
+  return check_access (exp, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
+                      /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
+                      &data);
 }
 
-/* For an expression EXP issue an access warning controlled by option OPT
-   with access to a region SIZE bytes in size in the RANGE of sizes.
-   WRITE is true for a write access, READ for a read access, neither for
-   call that may or may not perform an access but for which the range
-   is expected to valid.
-   Returns true when a warning has been issued.  */
+/* Helper to determine and check the sizes of the source and the destination
+   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  EXP is the
+   call expression, DEST is the destination argument, SRC is the source
+   argument or null, and LEN is the number of bytes.  Use Object Size type-0
+   regardless of the OPT_Wstringop_overflow_ setting.  Return true on success
+   (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
-                tree size, bool write, bool read, bool maybe)
-{
-  bool warned = false;
-
-  if (write && read)
-    {
-      if (tree_int_cst_equal (range[0], range[1]))
-       warned = (func
-                 ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("%qD may access %E byte in a region "
-                                    "of size %E")
-                               : G_("%qD accessing %E byte in a region "
-                                    "of size %E")),
-                               (maybe
-                                ? G_ ("%qD may access %E bytes in a region "
-                                      "of size %E")
-                                : G_ ("%qD accessing %E bytes in a region "
-                                      "of size %E")),
-                              func, range[0], size)
-                 : warning_n (loc, opt, tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("may access %E byte in a region "
-                                    "of size %E")
-                               : G_("accessing %E byte in a region "
-                                    "of size %E")),
-                              (maybe
-                               ? G_("may access %E bytes in a region "
-                                    "of size %E")
-                               : G_("accessing %E bytes in a region "
-                                    "of size %E")),
-                              range[0], size));
-      else if (tree_int_cst_sign_bit (range[1]))
-       {
-         /* Avoid printing the upper bound if it's invalid.  */
-         warned = (func
-                   ? warning_at (loc, opt,
-                                 (maybe
-                                  ? G_("%qD may access %E or more bytes "
-                                       "in a region of size %E")
-                                  : G_("%qD accessing %E or more bytes "
-                                       "in a region of size %E")),
-                                 func, range[0], size)
-                   : warning_at (loc, opt,
-                                 (maybe
-                                  ? G_("may access %E or more bytes "
-                                       "in a region of size %E")
-                                  : G_("accessing %E or more bytes "
-                                       "in a region of size %E")),
-                                 range[0], size));
-       }
-      else
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD may access between %E and %E "
-                                     "bytes in a region of size %E")
-                                : G_("%qD accessing between %E and %E "
-                                     "bytes in a region of size %E")),
-                               func, range[0], range[1], size)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("may access between %E and %E bytes "
-                                     "in a region of size %E")
-                                : G_("accessing between %E and %E bytes "
-                                     "in a region of size %E")),
-                               range[0], range[1], size));
-      return warned;
-    }
-
-  if (write)
-    {
-      if (tree_int_cst_equal (range[0], range[1]))
-       warned = (func
-                 ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("%qD may write %E byte into a region "
-                                    "of size %E")
-                               : G_("%qD writing %E byte into a region "
-                                    "of size %E overflows the destination")),
-                              (maybe
-                               ? G_("%qD may write %E bytes into a region "
-                                    "of size %E")
-                               : G_("%qD writing %E bytes into a region "
-                                    "of size %E overflows the destination")),
-                              func, range[0], size)
-                 : warning_n (loc, opt, tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("may write %E byte into a region "
-                                    "of size %E")
-                               : G_("writing %E byte into a region "
-                                    "of size %E overflows the destination")),
-                              (maybe
-                               ? G_("may write %E bytes into a region "
-                                    "of size %E")
-                               : G_("writing %E bytes into a region "
-                                    "of size %E overflows the destination")),
-                              range[0], size));
-      else if (tree_int_cst_sign_bit (range[1]))
-       {
-         /* Avoid printing the upper bound if it's invalid.  */
-         warned = (func
-                   ? warning_at (loc, opt,
-                                 (maybe
-                                  ? G_("%qD may write %E or more bytes "
-                                       "into a region of size %E")
-                                  : G_("%qD writing %E or more bytes "
-                                       "into a region of size %E overflows "
-                                       "the destination")),
-                                 func, range[0], size)
-                   : warning_at (loc, opt,
-                                 (maybe
-                                  ? G_("may write %E or more bytes into "
-                                       "a region of size %E")
-                                  : G_("writing %E or more bytes into "
-                                       "a region of size %E overflows "
-                                       "the destination")),
-                                 range[0], size));
-       }
-      else
-       warned = (func
-                 ? warning_at (loc, opt,
-                               (maybe
-                                ? G_("%qD may write between %E and %E bytes "
-                                     "into a region of size %E")
-                                : G_("%qD writing between %E and %E bytes "
-                                     "into a region of size %E overflows "
-                                     "the destination")),
-                               func, range[0], range[1], size)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("may write between %E and %E bytes "
-                                     "into a region of size %E")
-                                : G_("writing between %E and %E bytes "
-                                     "into a region of size %E overflows "
-                                     "the destination")),
-                               range[0], range[1], size));
-      return warned;
-    }
-
-  if (read)
-    {
-      if (tree_int_cst_equal (range[0], range[1]))
-       warned = (func
-                 ? warning_n (loc, OPT_Wstringop_overread,
-                              tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("%qD may read %E byte from a region "
-                                    "of size %E")
-                               : G_("%qD reading %E byte from a region "
-                                    "of size %E")),
-                              (maybe
-                               ? G_("%qD may read %E bytes from a region "
-                                    "of size %E")
-                               : G_("%qD reading %E bytes from a region "
-                                    "of size %E")),
-                              func, range[0], size)
-                 : warning_n (loc, OPT_Wstringop_overread,
-                              tree_to_uhwi (range[0]),
-                              (maybe
-                               ? G_("may read %E byte from a region "
-                                    "of size %E")
-                               : G_("reading %E byte from a region "
-                                    "of size %E")),
-                              (maybe
-                               ? G_("may read %E bytes from a region "
-                                    "of size %E")
-                               : G_("reading %E bytes from a region "
-                                    "of size %E")),
-                              range[0], size));
-      else if (tree_int_cst_sign_bit (range[1]))
-       {
-         /* Avoid printing the upper bound if it's invalid.  */
-         warned = (func
-                   ? warning_at (loc, OPT_Wstringop_overread,
-                                 (maybe
-                                  ? G_("%qD may read %E or more bytes "
-                                       "from a region of size %E")
-                                  : G_("%qD reading %E or more bytes "
-                                       "from a region of size %E")),
-                                 func, range[0], size)
-                   : warning_at (loc, OPT_Wstringop_overread,
-                                 (maybe
-                                  ? G_("may read %E or more bytes "
-                                       "from a region of size %E")
-                                  : G_("reading %E or more bytes "
-                                       "from a region of size %E")),
-                                 range[0], size));
-       }
-      else
-       warned = (func
-                 ? warning_at (loc, OPT_Wstringop_overread,
-                               (maybe
-                                ? G_("%qD may read between %E and %E bytes "
-                                     "from a region of size %E")
-                                : G_("%qD reading between %E and %E bytes "
-                                     "from a region of size %E")),
-                               func, range[0], range[1], size)
-                 : warning_at (loc, opt,
-                               (maybe
-                                ? G_("may read between %E and %E bytes "
-                                     "from a region of size %E")
-                                : G_("reading between %E and %E bytes "
-                                     "from a region of size %E")),
-                               range[0], range[1], size));
-
-      if (warned)
-       suppress_warning (exp, OPT_Wstringop_overread);
-
-      return warned;
-    }
-
-  if (tree_int_cst_equal (range[0], range[1])
-      || tree_int_cst_sign_bit (range[1]))
-    warned = (func
-             ? warning_n (loc, OPT_Wstringop_overread,
-                          tree_to_uhwi (range[0]),
-                          "%qD expecting %E byte in a region of size %E",
-                          "%qD expecting %E bytes in a region of size %E",
-                          func, range[0], size)
-             : warning_n (loc, OPT_Wstringop_overread,
-                          tree_to_uhwi (range[0]),
-                          "expecting %E byte in a region of size %E",
-                          "expecting %E bytes in a region of size %E",
-                          range[0], size));
-  else if (tree_int_cst_sign_bit (range[1]))
-    {
-      /* Avoid printing the upper bound if it's invalid.  */
-      warned = (func
-               ? warning_at (loc, OPT_Wstringop_overread,
-                             "%qD expecting %E or more bytes in a region "
-                             "of size %E",
-                             func, range[0], size)
-               : warning_at (loc, OPT_Wstringop_overread,
-                             "expecting %E or more bytes in a region "
-                             "of size %E",
-                             range[0], size));
-    }
-  else
-    warned = (func
-             ? warning_at (loc, OPT_Wstringop_overread,
-                           "%qD expecting between %E and %E bytes in "
-                           "a region of size %E",
-                           func, range[0], range[1], size)
-             : warning_at (loc, OPT_Wstringop_overread,
-                           "expecting between %E and %E bytes in "
-                           "a region of size %E",
-                           range[0], range[1], size));
-
-  if (warned)
-    suppress_warning (exp, OPT_Wstringop_overread);
+check_memop_access (tree exp, tree dest, tree src, tree size)
+{
+  /* For functions like memset and memcpy that operate on raw memory
+     try to determine the size of the largest source and destination
+     object using type-0 Object Size regardless of the object size
+     type specified by the option.  */
+  access_data data (exp, access_read_write);
+  tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, 0, &data.dst);
 
-  return warned;
+  return check_access (exp, size, /*maxread=*/NULL_TREE,
+                      srcsize, dstsize, data.mode, &data);
 }
 
-/* Issue one inform message describing each target of an access REF.
-   WRITE is set for a write access and clear for a read access.  */
+/* Validate memchr arguments without performing any expansion.
+   Return NULL_RTX.  */
 
-void
-access_ref::inform_access (access_mode mode) const
+static rtx
+expand_builtin_memchr (tree exp, rtx)
 {
-  const access_ref &aref = *this;
-  if (!aref.ref)
-    return;
-
-  if (aref.phi ())
-    {
-      /* Set MAXREF to refer to the largest object and fill ALL_REFS
-        with data for all objects referenced by the PHI arguments.  */
-      access_ref maxref;
-      auto_vec<access_ref> all_refs;
-      if (!get_ref (&all_refs, &maxref))
-       return;
-
-      /* Except for MAXREF, the rest of the arguments' offsets need not
-        reflect one added to the PHI itself.  Determine the latter from
-        MAXREF on which the result is based.  */
-      const offset_int orng[] =
-       {
-         offrng[0] - maxref.offrng[0],
-         wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
-       };
-
-      /* Add the final PHI's offset to that of each of the arguments
-        and recurse to issue an inform message for it.  */
-      for (unsigned i = 0; i != all_refs.length (); ++i)
-       {
-         /* Skip any PHIs; those could lead to infinite recursion.  */
-         if (all_refs[i].phi ())
-           continue;
-
-         all_refs[i].add_offset (orng[0], orng[1]);
-         all_refs[i].inform_access (mode);
-       }
-      return;
-    }
-
-  /* Convert offset range and avoid including a zero range since it
-     isn't necessarily meaningful.  */
-  HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
-  HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
-  HOST_WIDE_INT minoff;
-  HOST_WIDE_INT maxoff = diff_max;
-  if (wi::fits_shwi_p (aref.offrng[0]))
-    minoff = aref.offrng[0].to_shwi ();
-  else
-    minoff = aref.offrng[0] < 0 ? diff_min : diff_max;
-
-  if (wi::fits_shwi_p (aref.offrng[1]))
-    maxoff = aref.offrng[1].to_shwi ();
-
-  if (maxoff <= diff_min || maxoff >= diff_max)
-    /* Avoid mentioning an upper bound that's equal to or in excess
-       of the maximum of ptrdiff_t.  */
-    maxoff = minoff;
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  /* Convert size range and always include it since all sizes are
-     meaningful. */
-  unsigned long long minsize = 0, maxsize = 0;
-  if (wi::fits_shwi_p (aref.sizrng[0])
-      && wi::fits_shwi_p (aref.sizrng[1]))
-    {
-      minsize = aref.sizrng[0].to_shwi ();
-      maxsize = aref.sizrng[1].to_shwi ();
-    }
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-  /* SIZRNG doesn't necessarily have the same range as the allocation
-     size determined by gimple_call_alloc_size ().  */
-  char sizestr[80];
-  if (minsize == maxsize)
-    sprintf (sizestr, "%llu", minsize);
-  else
-    sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
-
-  char offstr[80];
-  if (minoff == 0
-      && (maxoff == 0 || aref.sizrng[1] <= maxoff))
-    offstr[0] = '\0';
-  else if (minoff == maxoff)
-    sprintf (offstr, "%lli", (long long) minoff);
-  else
-    sprintf (offstr, "[%lli, %lli]", (long long) minoff, (long long) maxoff);
+  check_read_access (exp, arg1, len, 0);
 
-  location_t loc = UNKNOWN_LOCATION;
+  return NULL_RTX;
+}
 
-  tree ref = this->ref;
-  tree allocfn = NULL_TREE;
-  if (TREE_CODE (ref) == SSA_NAME)
-    {
-      gimple *stmt = SSA_NAME_DEF_STMT (ref);
-      if (is_gimple_call (stmt))
-       {
-         loc = gimple_location (stmt);
-         if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
-           {
-             /* Strip the SSA_NAME suffix from the variable name and
-                recreate an identifier with the VLA's original name.  */
-             ref = gimple_call_lhs (stmt);
-             if (SSA_NAME_IDENTIFIER (ref))
-               {
-                 ref = SSA_NAME_IDENTIFIER (ref);
-                 const char *id = IDENTIFIER_POINTER (ref);
-                 size_t len = strcspn (id, ".$");
-                 if (!len)
-                   len = strlen (id);
-                 ref = get_identifier_with_length (id, len);
-               }
-           }
-         else
-           {
-             /* Except for VLAs, retrieve the allocation function.  */
-             allocfn = gimple_call_fndecl (stmt);
-             if (!allocfn)
-               allocfn = gimple_call_fn (stmt);
-             if (TREE_CODE (allocfn) == SSA_NAME)
-               {
-                 /* For an ALLOC_CALL via a function pointer make a small
-                    effort to determine the destination of the pointer.  */
-                 gimple *def = SSA_NAME_DEF_STMT (allocfn);
-                 if (gimple_assign_single_p (def))
-                   {
-                     tree rhs = gimple_assign_rhs1 (def);
-                     if (DECL_P (rhs))
-                       allocfn = rhs;
-                     else if (TREE_CODE (rhs) == COMPONENT_REF)
-                       allocfn = TREE_OPERAND (rhs, 1);
-                   }
-               }
-           }
-       }
-      else if (gimple_nop_p (stmt))
-       /* Handle DECL_PARM below.  */
-       ref = SSA_NAME_VAR (ref);
-    }
+/* Expand a call EXP to the memcpy builtin.
+   Return NULL_RTX if we failed, the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
 
-  if (DECL_P (ref))
-    loc = DECL_SOURCE_LOCATION (ref);
-  else if (EXPR_P (ref) && EXPR_HAS_LOCATION (ref))
-    loc = EXPR_LOCATION (ref);
-  else if (TREE_CODE (ref) != IDENTIFIER_NODE
-          && TREE_CODE (ref) != SSA_NAME)
-    return;
+static rtx
+expand_builtin_memcpy (tree exp, rtx target)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  if (mode == access_read_write || mode == access_write_only)
-    {
-      if (allocfn == NULL_TREE)
-       {
-         if (*offstr)
-           inform (loc, "at offset %s into destination object %qE of size %s",
-                   offstr, ref, sizestr);
-         else
-           inform (loc, "destination object %qE of size %s", ref, sizestr);
-         return;
-       }
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-      if (*offstr)
-       inform (loc,
-               "at offset %s into destination object of size %s "
-               "allocated by %qE", offstr, sizestr, allocfn);
-      else
-       inform (loc, "destination object of size %s allocated by %qE",
-               sizestr, allocfn);
-      return;
-    }
+  check_memop_access (exp, dest, src, len);
 
-  if (mode == access_read_only)
-    {
-      if (allocfn == NULL_TREE)
-       {
-         if (*offstr)
-           inform (loc, "at offset %s into source object %qE of size %s",
-                   offstr, ref, sizestr);
-         else
-           inform (loc, "source object %qE of size %s", ref, sizestr);
+  return expand_builtin_memory_copy_args (dest, src, len, target, exp,
+                                         /*retmode=*/ RETURN_BEGIN, false);
+}
 
-         return;
-       }
+/* Check a call EXP to the memmove built-in for validity.
+   Return NULL_RTX on both success and failure.  */
 
-      if (*offstr)
-       inform (loc,
-               "at offset %s into source object of size %s allocated by %qE",
-               offstr, sizestr, allocfn);
-      else
-       inform (loc, "source object of size %s allocated by %qE",
-               sizestr, allocfn);
-      return;
-    }
+static rtx
+expand_builtin_memmove (tree exp, rtx target)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  if (allocfn == NULL_TREE)
-    {
-      if (*offstr)
-       inform (loc, "at offset %s into object %qE of size %s",
-               offstr, ref, sizestr);
-      else
-       inform (loc, "object %qE of size %s", ref, sizestr);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-      return;
-    }
+  check_memop_access (exp, dest, src, len);
 
-  if (*offstr)
-    inform (loc,
-           "at offset %s into object of size %s allocated by %qE",
-           offstr, sizestr, allocfn);
-  else
-    inform (loc, "object of size %s allocated by %qE",
-           sizestr, allocfn);
+  return expand_builtin_memory_copy_args (dest, src, len, target, exp,
+                                         /*retmode=*/ RETURN_BEGIN, true);
 }
 
-/* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
-   by BNDRNG if nonnull and valid.  */
+/* Expand a call EXP to the mempcpy builtin.
+   Return NULL_RTX if we failed; the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
 
-static void
-get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
+static rtx
+expand_builtin_mempcpy (tree exp, rtx target)
 {
-  if (bound)
-    get_size_range (bound, range);
-
-  if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
-    return;
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  if (range[0] && TREE_CODE (range[0]) == INTEGER_CST)
-    {
-      offset_int r[] =
-       { wi::to_offset (range[0]), wi::to_offset (range[1]) };
-      if (r[0] < bndrng[0])
-       range[0] = wide_int_to_tree (sizetype, bndrng[0]);
-      if (bndrng[1] < r[1])
-       range[1] = wide_int_to_tree (sizetype, bndrng[1]);
-    }
-  else
-    {
-      range[0] = wide_int_to_tree (sizetype, bndrng[0]);
-      range[1] = wide_int_to_tree (sizetype, bndrng[1]);
-    }
-}
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-/* Try to verify that the sizes and lengths of the arguments to a string
-   manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow or read past the end.
-   Arguments other than EXP may be null.  When non-null, the arguments
-   have the following meaning:
-   DST is the destination of a copy call or NULL otherwise.
-   SRC is the source of a copy call or NULL otherwise.
-   DSTWRITE is the number of bytes written into the destination obtained
-   from the user-supplied size argument to the function (such as in
-   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
-   MAXREAD is the user-supplied bound on the length of the source sequence
-   (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.  If NULL, it's taken to be the same as DSTWRITE.
-   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
-   expression EXP is a string function call (as opposed to a memory call
-   like memcpy).  As an exception, SRCSTR can also be an integer denoting
-   the precomputed size of the source string or object (for functions like
-   memcpy).
-   DSTSIZE is the size of the destination object.
+  /* Policy does not generally allow using compute_objsize (which
+     is used internally by check_memop_size) to change code generation
+     or drive optimization decisions.
 
-   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
-   SIZE_MAX.
+     In this instance it is safe because the code we generate has
+     the same semantics regardless of the return value of
+     check_memop_sizes.   Exactly the same amount of data is copied
+     and the return value is exactly the same in both cases.
 
-   WRITE is true for write accesses, READ is true for reads.  Both are
-   false for simple size checks in calls to functions that neither read
-   from nor write to the region.
+     Furthermore, check_memop_size always uses mode 0 for the call to
+     compute_objsize, so the imprecise nature of compute_objsize is
+     avoided.  */
 
-   When nonnull, PAD points to a more detailed description of the access.
+  /* Avoid expanding mempcpy into memcpy when the call is determined
+     to overflow the buffer.  This also prevents the same overflow
+     from being diagnosed again when expanding memcpy.  */
+  if (!check_memop_access (exp, dest, src, len))
+    return NULL_RTX;
 
-   If the call is successfully verified as safe return true, otherwise
-   return false.  */
+  return expand_builtin_mempcpy_args (dest, src, len,
+                                     target, exp, /*retmode=*/ RETURN_END);
+}
 
-bool
-check_access (tree exp, tree dstwrite,
-             tree maxread, tree srcstr, tree dstsize,
-             access_mode mode, const access_data *pad /* = NULL */)
-{
-  /* The size of the largest object is half the address space, or
-     PTRDIFF_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = max_object_size ();
-
-  /* Either an approximate/minimum the length of the source string for
-     string functions or the size of the source object for raw memory
-     functions.  */
-  tree slen = NULL_TREE;
-
-  /* The range of the access in bytes; first set to the write access
-     for functions that write and then read for those that also (or
-     just) read.  */
-  tree range[2] = { NULL_TREE, NULL_TREE };
-
-  /* Set to true when the exact number of bytes written by a string
-     function like strcpy is not known and the only thing that is
-     known is that it must be at least one (for the terminating nul).  */
-  bool at_least_one = false;
-  if (srcstr)
-    {
-      /* SRCSTR is normally a pointer to string but as a special case
-        it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
-       {
-         if (!check_nul_terminated_array (exp, srcstr, maxread))
-           return false;
-         /* Try to determine the range of lengths the source string
-            refers to.  If it can be determined and is less than
-            the upper bound given by MAXREAD add one to it for
-            the terminating nul.  Otherwise, set it to one for
-            the same reason, or to MAXREAD as appropriate.  */
-         c_strlen_data lendata = { };
-         get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
-         range[0] = lendata.minlen;
-         range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
-         if (range[0]
-             && TREE_CODE (range[0]) == INTEGER_CST
-             && TREE_CODE (range[1]) == INTEGER_CST
-             && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
-           {
-             if (maxread && tree_int_cst_le (maxread, range[0]))
-               range[0] = range[1] = maxread;
-             else
-               range[0] = fold_build2 (PLUS_EXPR, size_type_node,
-                                       range[0], size_one_node);
+/* Helper function to do the actual work for expand of memory copy family
+   functions (memcpy, mempcpy, stpcpy).  Expansing should assign LEN bytes
+   of memory from SRC to DEST and assign to TARGET if convenient.  Return
+   value is based on RETMODE argument.  */
 
-             if (maxread && tree_int_cst_le (maxread, range[1]))
-               range[1] = maxread;
-             else if (!integer_all_onesp (range[1]))
-               range[1] = fold_build2 (PLUS_EXPR, size_type_node,
-                                       range[1], size_one_node);
+static rtx
+expand_builtin_memory_copy_args (tree dest, tree src, tree len,
+                                rtx target, tree exp, memop_ret retmode,
+                                bool might_overlap)
+{
+  unsigned int src_align = get_pointer_alignment (src);
+  unsigned int dest_align = get_pointer_alignment (dest);
+  rtx dest_mem, src_mem, dest_addr, len_rtx;
+  HOST_WIDE_INT expected_size = -1;
+  unsigned int expected_align = 0;
+  unsigned HOST_WIDE_INT min_size;
+  unsigned HOST_WIDE_INT max_size;
+  unsigned HOST_WIDE_INT probable_max_size;
 
-             slen = range[0];
-           }
-         else
-           {
-             at_least_one = true;
-             slen = size_one_node;
-           }
-       }
-      else
-       slen = srcstr;
-    }
+  bool is_move_done;
 
-  if (!dstwrite && !maxread)
-    {
-      /* When the only available piece of data is the object size
-        there is nothing to do.  */
-      if (!slen)
-       return true;
+  /* If DEST is not a pointer type, call the normal function.  */
+  if (dest_align == 0)
+    return NULL_RTX;
 
-      /* Otherwise, when the length of the source sequence is known
-        (as with strlen), set DSTWRITE to it.  */
-      if (!range[0])
-       dstwrite = slen;
-    }
+  /* If either SRC is not a pointer type, don't do this
+     operation in-line.  */
+  if (src_align == 0)
+    return NULL_RTX;
 
-  if (!dstsize)
-    dstsize = maxobjsize;
+  if (currently_expanding_gimple_stmt)
+    stringop_block_profile (currently_expanding_gimple_stmt,
+                           &expected_align, &expected_size);
 
-  /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
-     if valid.  */
-  get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL);
+  if (expected_align < dest_align)
+    expected_align = dest_align;
+  dest_mem = get_memory_rtx (dest, len);
+  set_mem_align (dest_mem, dest_align);
+  len_rtx = expand_normal (len);
+  determine_block_size (len, len_rtx, &min_size, &max_size,
+                       &probable_max_size);
 
-  tree func = get_callee_fndecl (exp);
-  /* Read vs write access by built-ins can be determined from the const
-     qualifiers on the pointer argument.  In the absence of attribute
-     access, non-const qualified pointer arguments to user-defined
-     functions are assumed to both read and write the objects.  */
-  const bool builtin = func ? fndecl_built_in_p (func) : false;
+  /* Try to get the byte representation of the constant SRC points to,
+     with its byte size in NBYTES.  */
+  unsigned HOST_WIDE_INT nbytes;
+  const char *rep = getbyterep (src, &nbytes);
 
-  /* First check the number of bytes to be written against the maximum
-     object size.  */
-  if (range[0]
-      && TREE_CODE (range[0]) == INTEGER_CST
-      && tree_int_cst_lt (maxobjsize, range[0]))
-    {
-      location_t loc = EXPR_LOCATION (exp);
-      maybe_warn_for_bound (OPT_Wstringop_overflow_, loc, exp, func, range,
-                           NULL_TREE, pad);
-      return false;
-    }
-
-  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
-     constant, and in range of unsigned HOST_WIDE_INT.  */
-  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
-
-  /* Next check the number of bytes to be written against the destination
-     object size.  */
-  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
+  /* If the function's constant bound LEN_RTX is less than or equal
+     to the byte size of the representation of the constant argument,
+     and if block move would be done by pieces, we can avoid loading
+     the bytes from memory and only store the computed constant.
+     This works in the overlap (memmove) case as well because
+     store_by_pieces just generates a series of stores of constants
+     from the representation returned by getbyterep().  */
+  if (rep
+      && CONST_INT_P (len_rtx)
+      && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= nbytes
+      && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
+                             CONST_CAST (char *, rep),
+                             dest_align, false))
     {
-      if (range[0]
-         && TREE_CODE (range[0]) == INTEGER_CST
-         && ((tree_fits_uhwi_p (dstsize)
-              && tree_int_cst_lt (dstsize, range[0]))
-             || (dstwrite
-                 && tree_fits_uhwi_p (dstwrite)
-                 && tree_int_cst_lt (dstwrite, range[0]))))
-       {
-         const opt_code opt = OPT_Wstringop_overflow_;
-         if (warning_suppressed_p (exp, opt)
-             || (pad && pad->dst.ref
-                 && warning_suppressed_p (pad->dst.ref, opt)))
-           return false;
-
-         location_t loc = EXPR_LOCATION (exp);
-         bool warned = false;
-         if (dstwrite == slen && at_least_one)
-           {
-             /* This is a call to strcpy with a destination of 0 size
-                and a source of unknown length.  The call will write
-                at least one byte past the end of the destination.  */
-             warned = (func
-                       ? warning_at (loc, opt,
-                                     "%qD writing %E or more bytes into "
-                                     "a region of size %E overflows "
-                                     "the destination",
-                                     func, range[0], dstsize)
-                       : warning_at (loc, opt,
-                                     "writing %E or more bytes into "
-                                     "a region of size %E overflows "
-                                     "the destination",
-                                     range[0], dstsize));
-           }
-         else
-           {
-             const bool read
-               = mode == access_read_only || mode == access_read_write;
-             const bool write
-               = mode == access_write_only || mode == access_read_write;
-             const bool maybe = pad && pad->dst.parmarray;
-             warned = warn_for_access (loc, func, exp,
-                                       OPT_Wstringop_overflow_,
-                                       range, dstsize,
-                                       write, read && !builtin, maybe);
-           }
-
-         if (warned)
-           {
-             suppress_warning (exp, OPT_Wstringop_overflow_);
-             if (pad)
-               pad->dst.inform_access (pad->mode);
-           }
-
-         /* Return error when an overflow has been detected.  */
-         return false;
-       }
+      dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
+                                 builtin_memcpy_read_str,
+                                 CONST_CAST (char *, rep),
+                                 dest_align, false, retmode);
+      dest_mem = force_operand (XEXP (dest_mem, 0), target);
+      dest_mem = convert_memory_address (ptr_mode, dest_mem);
+      return dest_mem;
     }
 
-  /* Check the maximum length of the source sequence against the size
-     of the destination object if known, or against the maximum size
-     of an object.  */
-  if (maxread)
-    {
-      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
-        PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
+  src_mem = get_memory_rtx (src, len);
+  set_mem_align (src_mem, src_align);
 
-      location_t loc = EXPR_LOCATION (exp);
-      tree size = dstsize;
-      if (pad && pad->mode == access_read_only)
-       size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
+  /* Copy word part most expediently.  */
+  enum block_op_methods method = BLOCK_OP_NORMAL;
+  if (CALL_EXPR_TAILCALL (exp)
+      && (retmode == RETURN_BEGIN || target == const0_rtx))
+    method = BLOCK_OP_TAILCALL;
+  bool use_mempcpy_call = (targetm.libc_has_fast_function (BUILT_IN_MEMPCPY)
+                          && retmode == RETURN_END
+                          && !might_overlap
+                          && target != const0_rtx);
+  if (use_mempcpy_call)
+    method = BLOCK_OP_NO_LIBCALL_RET;
+  dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, method,
+                                    expected_align, expected_size,
+                                    min_size, max_size, probable_max_size,
+                                    use_mempcpy_call, &is_move_done,
+                                    might_overlap);
 
-      if (range[0] && maxread && tree_fits_uhwi_p (size))
-       {
-         if (tree_int_cst_lt (maxobjsize, range[0]))
-           {
-             maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
-                                   range, size, pad);
-             return false;
-           }
+  /* Bail out when a mempcpy call would be expanded as libcall and when
+     we have a target that provides a fast implementation
+     of mempcpy routine.  */
+  if (!is_move_done)
+    return NULL_RTX;
 
-         if (size != maxobjsize && tree_int_cst_lt (size, range[0]))
-           {
-             opt_code opt = (dstwrite || mode != access_read_only
-                             ? OPT_Wstringop_overflow_
-                             : OPT_Wstringop_overread);
-             maybe_warn_for_bound (opt, loc, exp, func, range, size, pad);
-             return false;
-           }
-       }
+  if (dest_addr == pc_rtx)
+    return NULL_RTX;
 
-      maybe_warn_nonstring_arg (func, exp);
-    }
-
-  /* Check for reading past the end of SRC.  */
-  bool overread = (slen
-                  && slen == srcstr
-                  && dstwrite
-                  && range[0]
-                  && TREE_CODE (slen) == INTEGER_CST
-                  && tree_int_cst_lt (slen, range[0]));
-  /* If none is determined try to get a better answer based on the details
-     in PAD.  */
-  if (!overread
-      && pad
-      && pad->src.sizrng[1] >= 0
-      && pad->src.offrng[0] >= 0
-      && (pad->src.offrng[1] < 0
-         || pad->src.offrng[0] <= pad->src.offrng[1]))
-    {
-      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
-        PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
-      /* Set OVERREAD for reads starting just past the end of an object.  */
-      overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
-      range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
-      slen = size_zero_node;
-    }
-
-  if (overread)
-    {
-      const opt_code opt = OPT_Wstringop_overread;
-      if (warning_suppressed_p (exp, opt)
-         || (srcstr && warning_suppressed_p (srcstr, opt))
-         || (pad && pad->src.ref
-             && warning_suppressed_p (pad->src.ref, opt)))
-       return false;
+  if (dest_addr == 0)
+    {
+      dest_addr = force_operand (XEXP (dest_mem, 0), target);
+      dest_addr = convert_memory_address (ptr_mode, dest_addr);
+    }
 
-      location_t loc = EXPR_LOCATION (exp);
-      const bool read
-       = mode == access_read_only || mode == access_read_write;
-      const bool maybe = pad && pad->dst.parmarray;
-      if (warn_for_access (loc, func, exp, opt, range, slen, false, read,
-                          maybe))
-       {
-         suppress_warning (exp, opt);
-         if (pad)
-           pad->src.inform_access (access_read_only);
-       }
-      return false;
+  if (retmode != RETURN_BEGIN && target != const0_rtx)
+    {
+      dest_addr = gen_rtx_PLUS (ptr_mode, dest_addr, len_rtx);
+      /* stpcpy pointer to last byte.  */
+      if (retmode == RETURN_END_MINUS_ONE)
+       dest_addr = gen_rtx_MINUS (ptr_mode, dest_addr, const1_rtx);
     }
 
-  return true;
+  return dest_addr;
 }
 
-/* A convenience wrapper for check_access above to check access
-   by a read-only function like puts.  */
-
-static bool
-check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
-                  int ost /* = 1 */)
+static rtx
+expand_builtin_mempcpy_args (tree dest, tree src, tree len,
+                            rtx target, tree orig_exp, memop_ret retmode)
 {
-  if (!warn_stringop_overread)
-    return true;
-
-  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
-    bound = fold_convert (size_type_node, bound);
-  access_data data (exp, access_read_only, NULL_TREE, false, bound, true);
-  compute_objsize (src, ost, &data.src);
-  return check_access (exp, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
-                      /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
-                      &data);
+  return expand_builtin_memory_copy_args (dest, src, len, target, orig_exp,
+                                         retmode, false);
 }
 
-/* If STMT is a call to an allocation function, returns the constant
-   maximum size of the object allocated by the call represented as
-   sizetype.  If nonnull, sets RNG1[] to the range of the size.
-   When nonnull, uses RVALS for range information, otherwise gets global
-   range info.
-   Returns null when STMT is not a call to a valid allocation function.  */
+/* Expand into a movstr instruction, if one is available.  Return NULL_RTX if
+   we failed, the caller should emit a normal call, otherwise try to
+   get the result in TARGET, if convenient.
+   Return value is based on RETMODE argument.  */
 
-tree
-gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
-                       range_query * /* = NULL */)
+static rtx
+expand_movstr (tree dest, tree src, rtx target, memop_ret retmode)
 {
-  if (!stmt || !is_gimple_call (stmt))
-    return NULL_TREE;
-
-  tree allocfntype;
-  if (tree fndecl = gimple_call_fndecl (stmt))
-    allocfntype = TREE_TYPE (fndecl);
-  else
-    allocfntype = gimple_call_fntype (stmt);
+  class expand_operand ops[3];
+  rtx dest_mem;
+  rtx src_mem;
 
-  if (!allocfntype)
-    return NULL_TREE;
+  if (!targetm.have_movstr ())
+    return NULL_RTX;
 
-  unsigned argidx1 = UINT_MAX, argidx2 = UINT_MAX;
-  tree at = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (allocfntype));
-  if (!at)
+  dest_mem = get_memory_rtx (dest, NULL);
+  src_mem = get_memory_rtx (src, NULL);
+  if (retmode == RETURN_BEGIN)
     {
-      if (!gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
-       return NULL_TREE;
-
-      argidx1 = 0;
+      target = force_reg (Pmode, XEXP (dest_mem, 0));
+      dest_mem = replace_equiv_address (dest_mem, target);
     }
 
-  unsigned nargs = gimple_call_num_args (stmt);
+  create_output_operand (&ops[0],
+                        retmode != RETURN_BEGIN ? target : NULL_RTX, Pmode);
+  create_fixed_operand (&ops[1], dest_mem);
+  create_fixed_operand (&ops[2], src_mem);
+  if (!maybe_expand_insn (targetm.code_for_movstr, 3, ops))
+    return NULL_RTX;
 
-  if (argidx1 == UINT_MAX)
+  if (retmode != RETURN_BEGIN && target != const0_rtx)
     {
-      tree atval = TREE_VALUE (at);
-      if (!atval)
-       return NULL_TREE;
-
-      argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
-      if (nargs <= argidx1)
-       return NULL_TREE;
-
-      atval = TREE_CHAIN (atval);
-      if (atval)
+      target = ops[0].value;
+      /* movstr is supposed to set end to the address of the NUL
+        terminator.  If the caller requested a mempcpy-like return value,
+        adjust it.  */
+      if (retmode == RETURN_END)
        {
-         argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
-         if (nargs <= argidx2)
-           return NULL_TREE;
+         rtx tem = plus_constant (GET_MODE (target),
+                                  gen_lowpart (GET_MODE (target), target), 1);
+         emit_move_insn (target, force_operand (tem, NULL_RTX));
        }
     }
+  return target;
+}
 
-  tree size = gimple_call_arg (stmt, argidx1);
-
-  wide_int rng1_buf[2];
-  /* If RNG1 is not set, use the buffer.  */
-  if (!rng1)
-    rng1 = rng1_buf;
-
-  /* Use maximum precision to avoid overflow below.  */
-  const int prec = ADDR_MAX_PRECISION;
-
-  {
-    tree r[2];
-    /* Determine the largest valid range size, including zero.  */
-    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
-      return NULL_TREE;
-    rng1[0] = wi::to_wide (r[0], prec);
-    rng1[1] = wi::to_wide (r[1], prec);
-  }
+/* Do some very basic size validation of a call to the strcpy builtin
+   given by EXP.  Return NULL_RTX to have the built-in expand to a call
+   to the library function.  */
 
-  if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
-    return fold_convert (sizetype, size);
+static rtx
+expand_builtin_strcat (tree exp)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
 
-  /* To handle ranges do the math in wide_int and return the product
-     of the upper bounds as a constant.  Ignore anti-ranges.  */
-  tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
-  wide_int rng2[2];
-  {
-    tree r[2];
-      /* As above, use the full non-negative range on failure.  */
-    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
-      return NULL_TREE;
-    rng2[0] = wi::to_wide (r[0], prec);
-    rng2[1] = wi::to_wide (r[1], prec);
-  }
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
 
-  /* Compute products of both bounds for the caller but return the lesser
-     of SIZE_MAX and the product of the upper bounds as a constant.  */
-  rng1[0] = rng1[0] * rng2[0];
-  rng1[1] = rng1[1] * rng2[1];
+  /* There is no way here to determine the length of the string in
+     the destination to which the SRC string is being appended so
+     just diagnose cases when the souce string is longer than
+     the destination object.  */
+  access_data data (exp, access_read_write, NULL_TREE, true,
+                   NULL_TREE, true);
+  const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+  compute_objsize (src, ost, &data.src);
+  tree destsize = compute_objsize (dest, ost, &data.dst);
 
-  const tree size_max = TYPE_MAX_VALUE (sizetype);
-  if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
-    {
-      rng1[1] = wi::to_wide (size_max, prec);
-      return size_max;
-    }
+  check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
+               src, destsize, data.mode, &data);
 
-  return wide_int_to_tree (sizetype, rng1[1]);
+  return NULL_RTX;
 }
 
-/* For an access to an object referenced to by the function parameter PTR
-   of pointer type, and set RNG[] to the range of sizes of the object
-   obtainedfrom the attribute access specification for the current function.
-   Set STATIC_ARRAY if the array parameter has been declared [static].
-   Return the function parameter on success and null otherwise.  */
-
-tree
-gimple_parm_array_size (tree ptr, wide_int rng[2],
-                       bool *static_array /* = NULL */)
-{
-  /* For a function argument try to determine the byte size of the array
-     from the current function declaratation (e.g., attribute access or
-     related).  */
-  tree var = SSA_NAME_VAR (ptr);
-  if (TREE_CODE (var) != PARM_DECL)
-    return NULL_TREE;
+/* Expand expression EXP, which is a call to the strcpy builtin.  Return
+   NULL_RTX if we failed the caller should emit a normal call, otherwise
+   try to get the result in TARGET, if convenient (and in mode MODE if that's
+   convenient).  */
 
-  const unsigned prec = TYPE_PRECISION (sizetype);
+static rtx
+expand_builtin_strcpy (tree exp, rtx target)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  rdwr_map rdwr_idx;
-  attr_access *access = get_parm_access (rdwr_idx, var);
-  if (!access)
-    return NULL_TREE;
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (access->sizarg != UINT_MAX)
+  if (warn_stringop_overflow)
     {
-      /* TODO: Try to extract the range from the argument based on
-        those of subsequent assertions or based on known calls to
-        the current function.  */
-      return NULL_TREE;
+      access_data data (exp, access_read_write, NULL_TREE, true,
+                       NULL_TREE, true);
+      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+      compute_objsize (src, ost, &data.src);
+      tree dstsize = compute_objsize (dest, ost, &data.dst);
+      check_access (exp, /*dstwrite=*/ NULL_TREE,
+                   /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
+                   dstsize, data.mode, &data);
     }
 
-  if (!access->minsize)
-    return NULL_TREE;
-
-  /* Only consider ordinary array bound at level 2 (or above if it's
-     ever added).  */
-  if (warn_array_parameter < 2 && !access->static_p)
-    return NULL_TREE;
-
-  if (static_array)
-    *static_array = access->static_p;
-
-  rng[0] = wi::zero (prec);
-  rng[1] = wi::uhwi (access->minsize, prec);
-  /* Multiply the array bound encoded in the attribute by the size
-     of what the pointer argument to which it decays points to.  */
-  tree eltype = TREE_TYPE (TREE_TYPE (ptr));
-  tree size = TYPE_SIZE_UNIT (eltype);
-  if (!size || TREE_CODE (size) != INTEGER_CST)
-    return NULL_TREE;
+  if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
+    {
+      /* Check to see if the argument was declared attribute nonstring
+        and if so, issue a warning since at this point it's not known
+        to be nul-terminated.  */
+      tree fndecl = get_callee_fndecl (exp);
+      maybe_warn_nonstring_arg (fndecl, exp);
+      return ret;
+    }
 
-  rng[1] *= wi::to_wide (size, prec);
-  return var;
+  return NULL_RTX;
 }
 
-/* Wrapper around the wide_int overload of get_range that accepts
-   offset_int instead.  For middle end expressions returns the same
-   result.  For a subset of nonconstamt expressions emitted by the front
-   end determines a more precise range than would be possible otherwise.  */
+/* Helper function to do the actual work for expand_builtin_strcpy.  The
+   arguments to the builtin_strcpy call DEST and SRC are broken out
+   so that this can also be called without constructing an actual CALL_EXPR.
+   The other arguments and return value are the same as for
+   expand_builtin_strcpy.  */
 
-static bool
-get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
+static rtx
+expand_builtin_strcpy_args (tree exp, tree dest, tree src, rtx target)
 {
-  offset_int add = 0;
-  if (TREE_CODE (x) == PLUS_EXPR)
+  /* Detect strcpy calls with unterminated arrays..  */
+  tree size;
+  bool exact;
+  if (tree nonstr = unterminated_array (src, &size, &exact))
     {
-      /* Handle constant offsets in pointer addition expressions seen
-        n the front end IL.  */
-      tree op = TREE_OPERAND (x, 1);
-      if (TREE_CODE (op) == INTEGER_CST)
-       {
-         op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
-         add = wi::to_offset (op);
-         x = TREE_OPERAND (x, 0);
-       }
+      /* NONSTR refers to the non-nul terminated constant array.  */
+      warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, nonstr,
+                         size, exact);
+      return NULL_RTX;
     }
 
-  if (TREE_CODE (x) == NOP_EXPR)
-    /* Also handle conversions to sizetype seen in the front end IL.  */
-    x = TREE_OPERAND (x, 0);
+  return expand_movstr (dest, src, target, /*retmode=*/ RETURN_BEGIN);
+}
 
-  tree type = TREE_TYPE (x);
-  if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type))
-    return false;
+/* Expand a call EXP to the stpcpy builtin.
+   Return NULL_RTX if we failed the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
 
-   if (TREE_CODE (x) != INTEGER_CST
-      && TREE_CODE (x) != SSA_NAME)
-    {
-      if (TYPE_UNSIGNED (type)
-         && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
-       type = signed_type_for (type);
+static rtx
+expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
+{
+  tree dst, src;
+  location_t loc = EXPR_LOCATION (exp);
 
-      r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
-      r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
-      return x;
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  dst = CALL_EXPR_ARG (exp, 0);
+  src = CALL_EXPR_ARG (exp, 1);
+
+  if (warn_stringop_overflow)
+    {
+      access_data data (exp, access_read_write);
+      tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
+                                      &data.dst);
+      check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
+                   src, destsize, data.mode, &data);
     }
 
-  wide_int wr[2];
-  if (!get_range (x, stmt, wr, rvals))
-    return false;
+  /* If return value is ignored, transform stpcpy into strcpy.  */
+  if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY))
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      tree result = build_call_nofold_loc (loc, fn, 2, dst, src);
+      return expand_expr (result, target, mode, EXPAND_NORMAL);
+    }
+  else
+    {
+      tree len, lenp1;
+      rtx ret;
 
-  signop sgn = SIGNED;
-  /* Only convert signed integers or unsigned sizetype to a signed
-     offset and avoid converting large positive values in narrower
-     types to negative offsets.  */
-  if (TYPE_UNSIGNED (type)
-      && wr[0].get_precision () < TYPE_PRECISION (sizetype))
-    sgn = UNSIGNED;
+      /* Ensure we get an actual string whose length can be evaluated at
+        compile-time, not an expression containing a string.  This is
+        because the latter will potentially produce pessimized code
+        when used to produce the return value.  */
+      c_strlen_data lendata = { };
+      if (!c_getstr (src)
+         || !(len = c_strlen (src, 0, &lendata, 1)))
+       return expand_movstr (dst, src, target,
+                             /*retmode=*/ RETURN_END_MINUS_ONE);
 
-  r[0] = offset_int::from (wr[0], sgn);
-  r[1] = offset_int::from (wr[1], sgn);
-  return true;
-}
+      if (lendata.decl)
+       warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, lendata.decl);
 
-/* Return the argument that the call STMT to a built-in function returns
-   or null if it doesn't.  On success, set OFFRNG[] to the range of offsets
-   from the argument reflected in the value returned by the built-in if it
-   can be determined, otherwise to 0 and HWI_M1U respectively.  Set
-   *PAST_END for functions like mempcpy that might return a past the end
-   pointer (most functions return a dereferenceable pointer to an existing
-   element of an array).  */
+      lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
+      ret = expand_builtin_mempcpy_args (dst, src, lenp1,
+                                        target, exp,
+                                        /*retmode=*/ RETURN_END_MINUS_ONE);
 
-static tree
-gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
-                         range_query *rvals)
-{
-  /* Clear and set below for the rare function(s) that might return
-     a past-the-end pointer.  */
-  *past_end = false;
+      if (ret)
+       return ret;
 
-  {
-    /* Check for attribute fn spec to see if the function returns one
-       of its arguments.  */
-    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
-    unsigned int argno;
-    if (fnspec.returns_arg (&argno))
-      {
-       /* Functions return the first argument (not a range).  */
-       offrng[0] = offrng[1] = 0;
-       return gimple_call_arg (stmt, argno);
-      }
-  }
+      if (TREE_CODE (len) == INTEGER_CST)
+       {
+         rtx len_rtx = expand_normal (len);
 
-  if (gimple_call_num_args (stmt) < 1)
-    return NULL_TREE;
+         if (CONST_INT_P (len_rtx))
+           {
+             ret = expand_builtin_strcpy_args (exp, dst, src, target);
 
-  tree fn = gimple_call_fndecl (stmt);
-  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
-    {
-      /* See if this is a call to placement new.  */
-      if (!fn
-         || !DECL_IS_OPERATOR_NEW_P (fn)
-         || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
-       return NULL_TREE;
+             if (ret)
+               {
+                 if (! target)
+                   {
+                     if (mode != VOIDmode)
+                       target = gen_reg_rtx (mode);
+                     else
+                       target = gen_reg_rtx (GET_MODE (ret));
+                   }
+                 if (GET_MODE (target) != GET_MODE (ret))
+                   ret = gen_lowpart (GET_MODE (target), ret);
 
-      /* Check the mangling, keeping in mind that operator new takes
-        a size_t which could be unsigned int or unsigned long.  */
-      tree fname = DECL_ASSEMBLER_NAME (fn);
-      if (!id_equal (fname, "_ZnwjPv")       // ordinary form
-         && !id_equal (fname, "_ZnwmPv")    // ordinary form
-         && !id_equal (fname, "_ZnajPv")    // array form
-         && !id_equal (fname, "_ZnamPv"))   // array form
-       return NULL_TREE;
+                 ret = plus_constant (GET_MODE (ret), ret, INTVAL (len_rtx));
+                 ret = emit_move_insn (target, force_operand (ret, NULL_RTX));
+                 gcc_assert (ret);
 
-      if (gimple_call_num_args (stmt) != 2)
-       return NULL_TREE;
+                 return target;
+               }
+           }
+       }
 
-      /* Allocation functions return a pointer to the beginning.  */
-      offrng[0] = offrng[1] = 0;
-      return gimple_call_arg (stmt, 1);
+      return expand_movstr (dst, src, target,
+                           /*retmode=*/ RETURN_END_MINUS_ONE);
     }
+}
 
-  switch (DECL_FUNCTION_CODE (fn))
-    {
-    case BUILT_IN_MEMCPY:
-    case BUILT_IN_MEMCPY_CHK:
-    case BUILT_IN_MEMMOVE:
-    case BUILT_IN_MEMMOVE_CHK:
-    case BUILT_IN_MEMSET:
-    case BUILT_IN_STRCAT:
-    case BUILT_IN_STRCAT_CHK:
-    case BUILT_IN_STRCPY:
-    case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STRNCAT:
-    case BUILT_IN_STRNCAT_CHK:
-    case BUILT_IN_STRNCPY:
-    case BUILT_IN_STRNCPY_CHK:
-      /* Functions return the first argument (not a range).  */
-      offrng[0] = offrng[1] = 0;
-      return gimple_call_arg (stmt, 0);
+/* Expand a call EXP to the stpcpy builtin and diagnose uses of nonstring
+   arguments while being careful to avoid duplicate warnings (which could
+   be issued if the expander were to expand the call, resulting in it
+   being emitted in expand_call().  */
 
-    case BUILT_IN_MEMPCPY:
-    case BUILT_IN_MEMPCPY_CHK:
-      {
-       /* The returned pointer is in a range constrained by the smaller
-          of the upper bound of the size argument and the source object
-          size.  */
-       offrng[0] = 0;
-       offrng[1] = HOST_WIDE_INT_M1U;
-       tree off = gimple_call_arg (stmt, 2);
-       bool off_valid = get_offset_range (off, stmt, offrng, rvals);
-       if (!off_valid || offrng[0] != offrng[1])
-         {
-           /* If the offset is either indeterminate or in some range,
-              try to constrain its upper bound to at most the size
-              of the source object.  */
-           access_ref aref;
-           tree src = gimple_call_arg (stmt, 1);
-           if (compute_objsize (src, 1, &aref, rvals)
-               && aref.sizrng[1] < offrng[1])
-             offrng[1] = aref.sizrng[1];
-         }
+static rtx
+expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
+{
+  if (rtx ret = expand_builtin_stpcpy_1 (exp, target, mode))
+    {
+      /* The call has been successfully expanded.  Check for nonstring
+        arguments and issue warnings as appropriate.  */
+      maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp);
+      return ret;
+    }
 
-       /* Mempcpy may return a past-the-end pointer.  */
-       *past_end = true;
-       return gimple_call_arg (stmt, 0);
-      }
+  return NULL_RTX;
+}
 
-    case BUILT_IN_MEMCHR:
-      {
-       tree off = gimple_call_arg (stmt, 2);
-       if (get_offset_range (off, stmt, offrng, rvals))
-         offrng[1] -= 1;
-       else
-         offrng[1] = HOST_WIDE_INT_M1U;
+/* Check a call EXP to the stpncpy built-in for validity.
+   Return NULL_RTX on both success and failure.  */
 
-       offrng[0] = 0;
-       return gimple_call_arg (stmt, 0);
-      }
+static rtx
+expand_builtin_stpncpy (tree exp, rtx)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
 
-    case BUILT_IN_STRCHR:
-    case BUILT_IN_STRRCHR:
-    case BUILT_IN_STRSTR:
-      offrng[0] = 0;
-      offrng[1] = HOST_WIDE_INT_M1U;
-      return gimple_call_arg (stmt, 0);
+  /* The source and destination of the call.  */
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
 
-    case BUILT_IN_STPCPY:
-    case BUILT_IN_STPCPY_CHK:
-      {
-       access_ref aref;
-       tree src = gimple_call_arg (stmt, 1);
-       if (compute_objsize (src, 1, &aref, rvals))
-         offrng[1] = aref.sizrng[1] - 1;
-       else
-         offrng[1] = HOST_WIDE_INT_M1U;
-       
-       offrng[0] = 0;
-       return gimple_call_arg (stmt, 0);
-      }
+  /* The exact number of bytes to write (not the maximum).  */
+  tree len = CALL_EXPR_ARG (exp, 2);
+  access_data data (exp, access_read_write);
+  /* The size of the destination object.  */
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+  check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data);
+  return NULL_RTX;
+}
 
-    case BUILT_IN_STPNCPY:
-    case BUILT_IN_STPNCPY_CHK:
-      {
-       /* The returned pointer is in a range between the first argument
-          and it plus the smaller of the upper bound of the size argument
-          and the source object size.  */
-       offrng[1] = HOST_WIDE_INT_M1U;
-       tree off = gimple_call_arg (stmt, 2);
-       if (!get_offset_range (off, stmt, offrng, rvals)
-           || offrng[0] != offrng[1])
-         {
-           /* If the offset is either indeterminate or in some range,
-              try to constrain its upper bound to at most the size
-              of the source object.  */
-           access_ref aref;
-           tree src = gimple_call_arg (stmt, 1);
-           if (compute_objsize (src, 1, &aref, rvals)
-               && aref.sizrng[1] < offrng[1])
-             offrng[1] = aref.sizrng[1];
-         }
+/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
+   bytes from constant string DATA + OFFSET and return it as target
+   constant.  */
 
-       /* When the source is the empty string the returned pointer is
-          a copy of the argument.  Otherwise stpcpy can also return
-          a past-the-end pointer.  */
-       offrng[0] = 0;
-       *past_end = true;
-       return gimple_call_arg (stmt, 0);
-      }
+rtx
+builtin_strncpy_read_str (void *data, void *, HOST_WIDE_INT offset,
+                         scalar_int_mode mode)
+{
+  const char *str = (const char *) data;
 
-    default:
-      break;
-    }
+  if ((unsigned HOST_WIDE_INT) offset > strlen (str))
+    return const0_rtx;
 
-  return NULL_TREE;
+  return c_readstr (str + offset, mode);
 }
 
-/* A helper of compute_objsize_r() to determine the size from an assignment
-   statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
+/* Helper to check the sizes of sequences and the destination of calls
+   to __builtin_strncat and __builtin___strncat_chk.  Returns true on
+   success (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
-                    ssa_name_limit_t &snlim, pointer_query *qry)
+check_strncat_sizes (tree exp, tree objsize)
 {
-  tree_code code = gimple_assign_rhs_code (stmt);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
-  tree ptr = gimple_assign_rhs1 (stmt);
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  c_strlen_data lendata = { };
+  get_range_strlen (src, &lendata, /* eltsize = */ 1);
 
-  /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
-     Determine the size/offset of each and use the one with more or less
-     space remaining, respectively.  If either fails, use the information
-     determined from the other instead, adjusted up or down as appropriate
-     for the expression.  */
-  access_ref aref[2] = { *pref, *pref };
-  if (!compute_objsize_r (ptr, ostype, &aref[0], snlim, qry))
-    {
-      aref[0].base0 = false;
-      aref[0].offrng[0] = aref[0].offrng[1] = 0;
-      aref[0].add_max_offset ();
-      aref[0].set_max_size_range ();
-    }
+  /* Try to verify that the destination is big enough for the shortest
+     string.  */
 
-  ptr = gimple_assign_rhs2 (stmt);
-  if (!compute_objsize_r (ptr, ostype, &aref[1], snlim, qry))
+  access_data data (exp, access_read_write, maxread, true);
+  if (!objsize && warn_stringop_overflow)
     {
-      aref[1].base0 = false;
-      aref[1].offrng[0] = aref[1].offrng[1] = 0;
-      aref[1].add_max_offset ();
-      aref[1].set_max_size_range ();
+      /* If it hasn't been provided by __strncat_chk, try to determine
+        the size of the destination object into which the source is
+        being copied.  */
+      objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
     }
 
-  if (!aref[0].ref && !aref[1].ref)
-    /* Fail if the identity of neither argument could be determined.  */
-    return false;
+  /* Add one for the terminating nul.  */
+  tree srclen = (lendata.minlen
+                ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
+                               size_one_node)
+                : NULL_TREE);
 
-  bool i0 = false;
-  if (aref[0].ref && aref[0].base0)
+  /* The strncat function copies at most MAXREAD bytes and always appends
+     the terminating nul so the specified upper bound should never be equal
+     to (or greater than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
-      if (aref[1].ref && aref[1].base0)
-       {
-         /* If the object referenced by both arguments has been determined
-            set *PREF to the one with more or less space remainng, whichever
-            is appopriate for CODE.
-            TODO: Indicate when the objects are distinct so it can be
-            diagnosed.  */
-         i0 = code == MAX_EXPR;
-         const bool i1 = !i0;
-
-         if (aref[i0].size_remaining () < aref[i1].size_remaining ())
-           *pref = aref[i1];
-         else
-           *pref = aref[i0];
-         return true;
-       }
+      location_t loc = EXPR_LOCATION (exp);
+      warning_at (loc, OPT_Wstringop_overflow_,
+                 "%qD specified bound %E equals destination size",
+                 get_callee_fndecl (exp), maxread);
 
-      /* If only the object referenced by one of the arguments could be
-        determined, use it and...  */
-      *pref = aref[0];
-      i0 = true;
+      return false;
     }
-  else
-    *pref = aref[1];
 
-  const bool i1 = !i0;
-  /* ...see if the offset obtained from the other pointer can be used
-     to tighten up the bound on the offset obtained from the first.  */
-  if ((code == MAX_EXPR && aref[i1].offrng[1] < aref[i0].offrng[0])
-      || (code == MIN_EXPR && aref[i0].offrng[0] < aref[i1].offrng[1]))
-    {
-      pref->offrng[0] = aref[i0].offrng[0];
-      pref->offrng[1] = aref[i0].offrng[1];
-    }
-  return true;
+  if (!srclen
+      || (maxread && tree_fits_uhwi_p (maxread)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
+
+  /* The number of bytes to write is LEN but check_access will alsoa
+     check SRCLEN if LEN's value isn't known.  */
+  return check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
+                      objsize, data.mode, &data);
 }
 
-/* A helper of compute_objsize_r() to determine the size from ARRAY_REF
-   AREF.  ADDR is true if PTR is the operand of ADDR_EXPR.  Return true
-   on success and false on failure.  */
+/* Similar to expand_builtin_strcat, do some very basic size validation
+   of a call to the strcpy builtin given by EXP.  Return NULL_RTX to have
+   the built-in expand to a call to the library function.  */
 
-static bool
-handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
-                 ssa_name_limit_t &snlim, pointer_query *qry)
+static rtx
+expand_builtin_strncat (tree exp, rtx)
 {
-  gcc_assert (TREE_CODE (aref) == ARRAY_REF);
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
 
-  ++pref->deref;
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  /* The upper bound on the number of bytes to write.  */
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
-  tree arefop = TREE_OPERAND (aref, 0);
-  tree reftype = TREE_TYPE (arefop);
-  if (!addr && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
-    /* Avoid arrays of pointers.  FIXME: Hande pointers to arrays
-       of known bound.  */
-    return false;
+  /* Detect unterminated source (only).  */
+  if (!check_nul_terminated_array (exp, src, maxread))
+    return NULL_RTX;
 
-  if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
-    return false;
+  /* The length of the source sequence.  */
+  tree slen = c_strlen (src, 1);
 
-  offset_int orng[2];
-  tree off = pref->eval (TREE_OPERAND (aref, 1));
-  range_query *const rvals = qry ? qry->rvals : NULL;
-  if (!get_offset_range (off, NULL, orng, rvals))
+  /* Try to determine the range of lengths that the source expression
+     refers to.  Since the lengths are only used for warning and not
+     for code generation disable strict mode below.  */
+  tree maxlen = slen;
+  if (!maxlen)
     {
-      /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
-      orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-      orng[0] = -orng[1] - 1;
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */ 1);
+      maxlen = lendata.maxbound;
     }
 
-  /* Convert the array index range determined above to a byte
-     offset.  */
-  tree lowbnd = array_ref_low_bound (aref);
-  if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
-    {
-      /* Adjust the index by the low bound of the array domain
-        (normally zero but 1 in Fortran).  */
-      unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
-      orng[0] -= lb;
-      orng[1] -= lb;
-    }
+  access_data data (exp, access_read_write);
+  /* Try to verify that the destination is big enough for the shortest
+     string.  First try to determine the size of the destination object
+     into which the source is being copied.  */
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+
+  /* Add one for the terminating nul.  */
+  tree srclen = (maxlen
+                ? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
+                               size_one_node)
+                : NULL_TREE);
 
-  tree eltype = TREE_TYPE (aref);
-  tree tpsize = TYPE_SIZE_UNIT (eltype);
-  if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
+  /* The strncat function copies at most MAXREAD bytes and always appends
+     the terminating nul so the specified upper bound should never be equal
+     to (or greater than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
-      pref->add_max_offset ();
-      return true;
+      location_t loc = EXPR_LOCATION (exp);
+      warning_at (loc, OPT_Wstringop_overflow_,
+                 "%qD specified bound %E equals destination size",
+                 get_callee_fndecl (exp), maxread);
+
+      return NULL_RTX;
     }
 
-  offset_int sz = wi::to_offset (tpsize);
-  orng[0] *= sz;
-  orng[1] *= sz;
+  if (!srclen
+      || (maxread && tree_fits_uhwi_p (maxread)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  if (ostype && TREE_CODE (eltype) == ARRAY_TYPE)
-    {
-      /* Except for the permissive raw memory functions which use
-        the size of the whole object determined above, use the size
-        of the referenced array.  Because the overall offset is from
-        the beginning of the complete array object add this overall
-        offset to the size of array.  */
-      offset_int sizrng[2] =
-       {
-        pref->offrng[0] + orng[0] + sz,
-        pref->offrng[1] + orng[1] + sz
-       };
-      if (sizrng[1] < sizrng[0])
-       std::swap (sizrng[0], sizrng[1]);
-      if (sizrng[0] >= 0 && sizrng[0] <= pref->sizrng[0])
-       pref->sizrng[0] = sizrng[0];
-      if (sizrng[1] >= 0 && sizrng[1] <= pref->sizrng[1])
-       pref->sizrng[1] = sizrng[1];
-    }
-
-  pref->add_offset (orng[0], orng[1]);
-  return true;
+  check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
+               destsize, data.mode, &data);
+  return NULL_RTX;
 }
 
-/* A helper of compute_objsize_r() to determine the size from MEM_REF
-   MREF.  Return true on success and false on failure.  */
+/* Expand expression EXP, which is a call to the strncpy builtin.  Return
+   NULL_RTX if we failed the caller should emit a normal call.  */
 
-static bool
-handle_mem_ref (tree mref, int ostype, access_ref *pref,
-               ssa_name_limit_t &snlim, pointer_query *qry)
+static rtx
+expand_builtin_strncpy (tree exp, rtx target)
 {
-  gcc_assert (TREE_CODE (mref) == MEM_REF);
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  /* The number of bytes to write (not the maximum).  */
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-  ++pref->deref;
+  /* The length of the source sequence.  */
+  tree slen = c_strlen (src, 1);
 
-  if (VECTOR_TYPE_P (TREE_TYPE (mref)))
+  if (warn_stringop_overflow)
     {
-      /* Hack: Handle MEM_REFs of vector types as those to complete
-        objects; those may be synthesized from multiple assignments
-        to consecutive data members (see PR 93200 and 96963).
-        FIXME: Vectorized assignments should only be present after
-        vectorization so this hack is only necessary after it has
-        run and could be avoided in calls from prior passes (e.g.,
-        tree-ssa-strlen.c).
-        FIXME: Deal with this more generally, e.g., by marking up
-        such MEM_REFs at the time they're created.  */
-      ostype = 0;
+      access_data data (exp, access_read_write, len, true, len, true);
+      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+      compute_objsize (src, ost, &data.src);
+      tree dstsize = compute_objsize (dest, ost, &data.dst);
+      /* The number of bytes to write is LEN but check_access will also
+        check SLEN if LEN's value isn't known.  */
+      check_access (exp, /*dstwrite=*/len,
+                   /*maxread=*/len, src, dstsize, data.mode, &data);
     }
 
-  tree mrefop = TREE_OPERAND (mref, 0);
-  if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
-    return false;
+  /* We must be passed a constant len and src parameter.  */
+  if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
+    return NULL_RTX;
 
-  offset_int orng[2];
-  tree off = pref->eval (TREE_OPERAND (mref, 1));
-  range_query *const rvals = qry ? qry->rvals : NULL;
-  if (!get_offset_range (off, NULL, orng, rvals))
+  slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+
+  /* We're required to pad with trailing zeros if the requested
+     len is greater than strlen(s2)+1.  In that case try to
+     use store_by_pieces, if it fails, punt.  */
+  if (tree_int_cst_lt (slen, len))
     {
-      /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
-      orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-      orng[0] = -orng[1] - 1;
+      unsigned int dest_align = get_pointer_alignment (dest);
+      const char *p = c_getstr (src);
+      rtx dest_mem;
+
+      if (!p || dest_align == 0 || !tree_fits_uhwi_p (len)
+         || !can_store_by_pieces (tree_to_uhwi (len),
+                                  builtin_strncpy_read_str,
+                                  CONST_CAST (char *, p),
+                                  dest_align, false))
+       return NULL_RTX;
+
+      dest_mem = get_memory_rtx (dest, len);
+      store_by_pieces (dest_mem, tree_to_uhwi (len),
+                      builtin_strncpy_read_str,
+                      CONST_CAST (char *, p), dest_align, false,
+                      RETURN_BEGIN);
+      dest_mem = force_operand (XEXP (dest_mem, 0), target);
+      dest_mem = convert_memory_address (ptr_mode, dest_mem);
+      return dest_mem;
     }
 
-  pref->add_offset (orng[0], orng[1]);
-  return true;
+  return NULL_RTX;
 }
 
-/* Helper to compute the size of the object referenced by the PTR
-   expression which must have pointer type, using Object Size type
-   OSTYPE (only the least significant 2 bits are used).
-   On success, sets PREF->REF to the DECL of the referenced object
-   if it's unique, otherwise to null, PREF->OFFRNG to the range of
-   offsets into it, and PREF->SIZRNG to the range of sizes of
-   the object(s).
-   SNLIM is used to avoid visiting the same PHI operand multiple
-   times, and, when nonnull, RVALS to determine range information.
-   Returns true on success, false when a meaningful size (or range)
-   cannot be determined.
-
-   The function is intended for diagnostics and should not be used
-   to influence code generation or optimization.  */
+/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
+   bytes from constant string DATA + OFFSET and return it as target
+   constant.  If PREV isn't nullptr, it has the RTL info from the
+   previous iteration.  */
 
-static bool
-compute_objsize_r (tree ptr, int ostype, access_ref *pref,
-                  ssa_name_limit_t &snlim, pointer_query *qry)
+rtx
+builtin_memset_read_str (void *data, void *prevp,
+                        HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
+                        scalar_int_mode mode)
 {
-  STRIP_NOPS (ptr);
-
-  const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
-  if (addr)
+  by_pieces_prev *prev = (by_pieces_prev *) prevp;
+  if (prev != nullptr && prev->data != nullptr)
     {
-      --pref->deref;
-      ptr = TREE_OPERAND (ptr, 0);
+      /* Use the previous data in the same mode.  */
+      if (prev->mode == mode)
+       return prev->data;
     }
 
-  if (DECL_P (ptr))
-    {
-      pref->ref = ptr;
+  const char *c = (const char *) data;
+  char *p = XALLOCAVEC (char, GET_MODE_SIZE (mode));
 
-      if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
-       {
-         /* Set the maximum size if the reference is to the pointer
-            itself (as opposed to what it points to), and clear
-            BASE0 since the offset isn't necessarily zero-based.  */
-         pref->set_max_size_range ();
-         pref->base0 = false;
-         return true;
-       }
+  memset (p, *c, GET_MODE_SIZE (mode));
 
-      if (tree size = decl_init_size (ptr, false))
-       if (TREE_CODE (size) == INTEGER_CST)
-         {
-           pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
-           return true;
-         }
+  return c_readstr (p, mode);
+}
 
-      pref->set_max_size_range ();
-      return true;
-    }
+/* Callback routine for store_by_pieces.  Return the RTL of a register
+   containing GET_MODE_SIZE (MODE) consecutive copies of the unsigned
+   char value given in the RTL register data.  For example, if mode is
+   4 bytes wide, return the RTL for 0x01010101*data.  If PREV isn't
+   nullptr, it has the RTL info from the previous iteration.  */
 
-  const tree_code code = TREE_CODE (ptr);
-  range_query *const rvals = qry ? qry->rvals : NULL;
+static rtx
+builtin_memset_gen_str (void *data, void *prevp,
+                       HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
+                       scalar_int_mode mode)
+{
+  rtx target, coeff;
+  size_t size;
+  char *p;
 
-  if (code == BIT_FIELD_REF)
+  by_pieces_prev *prev = (by_pieces_prev *) prevp;
+  if (prev != nullptr && prev->data != nullptr)
     {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
-       return false;
+      /* Use the previous data in the same mode.  */
+      if (prev->mode == mode)
+       return prev->data;
 
-      offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
-      pref->add_offset (off / BITS_PER_UNIT);
-      return true;
+      target = simplify_gen_subreg (mode, prev->data, prev->mode, 0);
+      if (target != nullptr)
+       return target;
     }
 
-  if (code == COMPONENT_REF)
-    {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
-       /* In accesses through union types consider the entire unions
-          rather than just their members.  */
-       ostype = 0;
-      tree field = TREE_OPERAND (ptr, 1);
+  size = GET_MODE_SIZE (mode);
+  if (size == 1)
+    return (rtx) data;
 
-      if (ostype == 0)
-       {
-         /* In OSTYPE zero (for raw memory functions like memcpy), use
-            the maximum size instead if the identity of the enclosing
-            object cannot be determined.  */
-         if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
-           return false;
-
-         /* Otherwise, use the size of the enclosing object and add
-            the offset of the member to the offset computed so far.  */
-         tree offset = byte_position (field);
-         if (TREE_CODE (offset) == INTEGER_CST)
-           pref->add_offset (wi::to_offset (offset));
-         else
-           pref->add_max_offset ();
-
-         if (!pref->ref)
-           /* REF may have been already set to an SSA_NAME earlier
-              to provide better context for diagnostics.  In that case,
-              leave it unchanged.  */
-           pref->ref = ref;
-         return true;
-       }
+  p = XALLOCAVEC (char, size);
+  memset (p, 1, size);
+  coeff = c_readstr (p, mode);
 
-      pref->ref = field;
+  target = convert_to_mode (mode, (rtx) data, 1);
+  target = expand_mult (mode, target, coeff, NULL_RTX, 1);
+  return force_reg (mode, target);
+}
 
-      if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
-       {
-         /* Set maximum size if the reference is to the pointer member
-            itself (as opposed to what it points to).  */
-         pref->set_max_size_range ();
-         return true;
-       }
+/* Expand expression EXP, which is a call to the memset builtin.  Return
+   NULL_RTX if we failed the caller should emit a normal call, otherwise
+   try to get the result in TARGET, if convenient (and in mode MODE if that's
+   convenient).  */
 
-      /* SAM is set for array members that might need special treatment.  */
-      special_array_member sam;
-      tree size = component_ref_size (ptr, &sam);
-      if (sam == special_array_member::int_0)
-       pref->sizrng[0] = pref->sizrng[1] = 0;
-      else if (!pref->trail1special && sam == special_array_member::trail_1)
-       pref->sizrng[0] = pref->sizrng[1] = 1;
-      else if (size && TREE_CODE (size) == INTEGER_CST)
-       pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
-      else
-       {
-         /* When the size of the member is unknown it's either a flexible
-            array member or a trailing special array member (either zero
-            length or one-element).  Set the size to the maximum minus
-            the constant size of the type.  */
-         pref->sizrng[0] = 0;
-         pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-         if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref)))
-           if (TREE_CODE (recsize) == INTEGER_CST)
-             pref->sizrng[1] -= wi::to_offset (recsize);
-       }
-      return true;
-    }
+static rtx
+expand_builtin_memset (tree exp, rtx target, machine_mode mode)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  if (code == ARRAY_REF)
-    return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree val = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
 
-  if (code == MEM_REF)
-    return handle_mem_ref (ptr, ostype, pref, snlim, qry);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
-  if (code == TARGET_MEM_REF)
-    {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
-       return false;
+  return expand_builtin_memset_args (dest, val, len, target, mode, exp);
+}
 
-      /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
-      pref->ref = ptr;
-      pref->add_max_offset ();
-      return true;
-    }
+/* Try to store VAL (or, if NULL_RTX, VALC) in LEN bytes starting at TO.
+   Return TRUE if successful, FALSE otherwise.  TO is assumed to be
+   aligned at an ALIGN-bits boundary.  LEN must be a multiple of
+   1<<CTZ_LEN between MIN_LEN and MAX_LEN.
 
-  if (code == INTEGER_CST)
-    {
-      /* Pointer constants other than null are most likely the result
-        of erroneous null pointer addition/subtraction.  Set size to
-        zero.  For null pointers, set size to the maximum for now
-        since those may be the result of jump threading.  */
-      if (integer_zerop (ptr))
-       pref->set_max_size_range ();
-      else
-       pref->sizrng[0] = pref->sizrng[1] = 0;
-      pref->ref = ptr;
+   The strategy is to issue one store_by_pieces for each power of two,
+   from most to least significant, guarded by a test on whether there
+   are at least that many bytes left to copy in LEN.
 
-      return true;
-    }
+   ??? Should we skip some powers of two in favor of loops?  Maybe start
+   at the max of TO/LEN/word alignment, at least when optimizing for
+   size, instead of ensuring O(log len) dynamic compares?  */
 
-  if (code == STRING_CST)
-    {
-      pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
-      pref->ref = ptr;
-      return true;
-    }
+bool
+try_store_by_multiple_pieces (rtx to, rtx len, unsigned int ctz_len,
+                             unsigned HOST_WIDE_INT min_len,
+                             unsigned HOST_WIDE_INT max_len,
+                             rtx val, char valc, unsigned int align)
+{
+  int max_bits = floor_log2 (max_len);
+  int min_bits = floor_log2 (min_len);
+  int sctz_len = ctz_len;
 
-  if (code == POINTER_PLUS_EXPR)
-    {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
-       return false;
+  gcc_checking_assert (sctz_len >= 0);
 
-      /* Clear DEREF since the offset is being applied to the target
-        of the dereference.  */
-      pref->deref = 0;
+  if (val)
+    valc = 1;
 
-      offset_int orng[2];
-      tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (get_offset_range (off, NULL, orng, rvals))
-       pref->add_offset (orng[0], orng[1]);
-      else
-       pref->add_max_offset ();
-      return true;
-    }
+  /* Bits more significant than TST_BITS are part of the shared prefix
+     in the binary representation of both min_len and max_len.  Since
+     they're identical, we don't need to test them in the loop.  */
+  int tst_bits = (max_bits != min_bits ? max_bits
+                 : floor_log2 (max_len ^ min_len));
 
-  if (code == VIEW_CONVERT_EXPR)
+  /* Check whether it's profitable to start by storing a fixed BLKSIZE
+     bytes, to lower max_bits.  In the unlikely case of a constant LEN
+     (implied by identical MAX_LEN and MIN_LEN), we want to issue a
+     single store_by_pieces, but otherwise, select the minimum multiple
+     of the ALIGN (in bytes) and of the MCD of the possible LENs, that
+     brings MAX_LEN below TST_BITS, if that's lower than min_len.  */
+  unsigned HOST_WIDE_INT blksize;
+  if (max_len > min_len)
     {
-      ptr = TREE_OPERAND (ptr, 0);
-      return compute_objsize_r (ptr, ostype, pref, snlim, qry);
+      unsigned HOST_WIDE_INT alrng = MAX (HOST_WIDE_INT_1U << ctz_len,
+                                         align / BITS_PER_UNIT);
+      blksize = max_len - (HOST_WIDE_INT_1U << tst_bits) + alrng;
+      blksize &= ~(alrng - 1);
     }
-
-  if (code == SSA_NAME)
+  else if (max_len == min_len)
+    blksize = max_len;
+  else
+    gcc_unreachable ();
+  if (min_len >= blksize)
     {
-      if (!snlim.next ())
-       return false;
+      min_len -= blksize;
+      min_bits = floor_log2 (min_len);
+      max_len -= blksize;
+      max_bits = floor_log2 (max_len);
 
-      /* Only process an SSA_NAME if the recursion limit has not yet
-        been reached.  */
-      if (qry)
-       {
-         if (++qry->depth)
-           qry->max_depth = qry->depth;
-         if (const access_ref *cache_ref = qry->get_ref (ptr))
-           {
-             /* If the pointer is in the cache set *PREF to what it refers
-                to and return success.  */
-             *pref = *cache_ref;
-             return true;
-           }
-       }
+      tst_bits = (max_bits != min_bits ? max_bits
+                : floor_log2 (max_len ^ min_len));
+    }
+  else
+    blksize = 0;
 
-      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
-      if (is_gimple_call (stmt))
-       {
-         /* If STMT is a call to an allocation function get the size
-            from its argument(s).  If successful, also set *PREF->REF
-            to PTR for the caller to include in diagnostics.  */
-         wide_int wr[2];
-         if (gimple_call_alloc_size (stmt, wr, rvals))
-           {
-             pref->ref = ptr;
-             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-             /* Constrain both bounds to a valid size.  */
-             offset_int maxsize = wi::to_offset (max_object_size ());
-             if (pref->sizrng[0] > maxsize)
-               pref->sizrng[0] = maxsize;
-             if (pref->sizrng[1] > maxsize)
-               pref->sizrng[1] = maxsize;
-           }
-         else
-           {
-             /* For functions known to return one of their pointer arguments
-                try to determine what the returned pointer points to, and on
-                success add OFFRNG which was set to the offset added by
-                the function (e.g., memchr or stpcpy) to the overall offset.
-             */
-             bool past_end;
-             offset_int offrng[2];
-             if (tree ret = gimple_call_return_array (stmt, offrng,
-                                                      &past_end, rvals))
-               {
-                 if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
-                   return false;
-
-                 /* Cap OFFRNG[1] to at most the remaining size of
-                    the object.  */
-                 offset_int remrng[2];
-                 remrng[1] = pref->size_remaining (remrng);
-                 if (remrng[1] != 0 && !past_end)
-                   /* Decrement the size for functions that never return
-                      a past-the-end pointer.  */
-                   remrng[1] -= 1;
-
-                 if (remrng[1] < offrng[1])
-                   offrng[1] = remrng[1];
-                 pref->add_offset (offrng[0], offrng[1]);
-               }
-             else
-               {
-                 /* For other calls that might return arbitrary pointers
-                    including into the middle of objects set the size
-                    range to maximum, clear PREF->BASE0, and also set
-                    PREF->REF to include in diagnostics.  */
-                 pref->set_max_size_range ();
-                 pref->base0 = false;
-                 pref->ref = ptr;
-               }
-           }
-         qry->put_ref (ptr, *pref);
-         return true;
-       }
+  /* Check that we can use store by pieces for the maximum store count
+     we may issue (initial fixed-size block, plus conditional
+     power-of-two-sized from max_bits to ctz_len.  */
+  unsigned HOST_WIDE_INT xlenest = blksize;
+  if (max_bits >= 0)
+    xlenest += ((HOST_WIDE_INT_1U << max_bits) * 2
+               - (HOST_WIDE_INT_1U << ctz_len));
+  if (!can_store_by_pieces (xlenest, builtin_memset_read_str,
+                           &valc, align, true))
+    return false;
 
-      if (gimple_nop_p (stmt))
-       {
-         /* For a function argument try to determine the byte size
-            of the array from the current function declaratation
-            (e.g., attribute access or related).  */
-         wide_int wr[2];
-         bool static_array = false;
-         if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
-           {
-             pref->parmarray = !static_array;
-             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-             pref->ref = ref;
-             qry->put_ref (ptr, *pref);
-             return true;
-           }
+  rtx (*constfun) (void *, void *, HOST_WIDE_INT, scalar_int_mode);
+  void *constfundata;
+  if (val)
+    {
+      constfun = builtin_memset_gen_str;
+      constfundata = val = force_reg (TYPE_MODE (unsigned_char_type_node),
+                                     val);
+    }
+  else
+    {
+      constfun = builtin_memset_read_str;
+      constfundata = &valc;
+    }
 
-         pref->set_max_size_range ();
-         pref->base0 = false;
-         pref->ref = ptr;
-         qry->put_ref (ptr, *pref);
-         return true;
-       }
+  rtx ptr = copy_addr_to_reg (convert_to_mode (ptr_mode, XEXP (to, 0), 0));
+  rtx rem = copy_to_mode_reg (ptr_mode, convert_to_mode (ptr_mode, len, 0));
+  to = replace_equiv_address (to, ptr);
+  set_mem_align (to, align);
 
-      if (gimple_code (stmt) == GIMPLE_PHI)
-       {
-         pref->ref = ptr;
-         access_ref phi_ref = *pref;
-         if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry))
-           return false;
-         *pref = phi_ref;
-         pref->ref = ptr;
-         qry->put_ref (ptr, *pref);
-         return true;
-       }
+  if (blksize)
+    {
+      to = store_by_pieces (to, blksize,
+                           constfun, constfundata,
+                           align, true,
+                           max_len != 0 ? RETURN_END : RETURN_BEGIN);
+      if (max_len == 0)
+       return true;
 
-      if (!is_gimple_assign (stmt))
-       {
-         /* Clear BASE0 since the assigned pointer might point into
-            the middle of the object, set the maximum size range and,
-            if the SSA_NAME refers to a function argumnent, set
-            PREF->REF to it.  */
-         pref->base0 = false;
-         pref->set_max_size_range ();
-         pref->ref = ptr;
-         return true;
-       }
+      /* Adjust PTR, TO and REM.  Since TO's address is likely
+        PTR+offset, we have to replace it.  */
+      emit_move_insn (ptr, force_operand (XEXP (to, 0), NULL_RTX));
+      to = replace_equiv_address (to, ptr);
+      rtx rem_minus_blksize = plus_constant (ptr_mode, rem, -blksize);
+      emit_move_insn (rem, force_operand (rem_minus_blksize, NULL_RTX));
+    }
 
-      tree_code code = gimple_assign_rhs_code (stmt);
+  /* Iterate over power-of-two block sizes from the maximum length to
+     the least significant bit possibly set in the length.  */
+  for (int i = max_bits; i >= sctz_len; i--)
+    {
+      rtx_code_label *label = NULL;
+      blksize = HOST_WIDE_INT_1U << i;
 
-      if (code == MAX_EXPR || code == MIN_EXPR)
+      /* If we're past the bits shared between min_ and max_len, expand
+        a test on the dynamic length, comparing it with the
+        BLKSIZE.  */
+      if (i <= tst_bits)
        {
-         if (!handle_min_max_size (stmt, ostype, pref, snlim, qry))
-           return false;
-         qry->put_ref (ptr, *pref);
-         return true;
+         label = gen_label_rtx ();
+         emit_cmp_and_jump_insns (rem, GEN_INT (blksize), LT, NULL,
+                                  ptr_mode, 1, label,
+                                  profile_probability::even ());
        }
+      /* If we are at a bit that is in the prefix shared by min_ and
+        max_len, skip this BLKSIZE if the bit is clear.  */
+      else if ((max_len & blksize) == 0)
+       continue;
 
-      tree rhs = gimple_assign_rhs1 (stmt);
+      /* Issue a store of BLKSIZE bytes.  */
+      to = store_by_pieces (to, blksize,
+                           constfun, constfundata,
+                           align, true,
+                           i != sctz_len ? RETURN_END : RETURN_BEGIN);
 
-      if (code == ASSERT_EXPR)
+      /* Adjust REM and PTR, unless this is the last iteration.  */
+      if (i != sctz_len)
        {
-         rhs = TREE_OPERAND (rhs, 0);
-         return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+         emit_move_insn (ptr, force_operand (XEXP (to, 0), NULL_RTX));
+         to = replace_equiv_address (to, ptr);
+         rtx rem_minus_blksize = plus_constant (ptr_mode, rem, -blksize);
+         emit_move_insn (rem, force_operand (rem_minus_blksize, NULL_RTX));
        }
 
-      if (code == POINTER_PLUS_EXPR
-         && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
+      if (label)
        {
-         /* Compute the size of the object first. */
-         if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
-           return false;
-
-         offset_int orng[2];
-         tree off = gimple_assign_rhs2 (stmt);
-         if (get_offset_range (off, stmt, orng, rvals))
-           pref->add_offset (orng[0], orng[1]);
-         else
-           pref->add_max_offset ();
-         qry->put_ref (ptr, *pref);
-         return true;
-       }
-
-      if (code == ADDR_EXPR
-         || code == SSA_NAME)
-       return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+         emit_label (label);
 
-      /* (This could also be an assignment from a nonlocal pointer.)  Save
-        PTR to mention in diagnostics but otherwise treat it as a pointer
-        to an unknown object.  */
-      pref->ref = rhs;
-      pref->base0 = false;
-      pref->set_max_size_range ();
-      return true;
+         /* Given conditional stores, the offset can no longer be
+            known, so clear it.  */
+         clear_mem_offset (to);
+       }
     }
 
-  /* Assume all other expressions point into an unknown object
-     of the maximum valid size.  */
-  pref->ref = ptr;
-  pref->base0 = false;
-  pref->set_max_size_range ();
-  if (TREE_CODE (ptr) == SSA_NAME)
-    qry->put_ref (ptr, *pref);
   return true;
 }
 
-/* A "public" wrapper around the above.  Clients should use this overload
-   instead.  */
+/* Helper function to do the actual work for expand_builtin_memset.  The
+   arguments to the builtin_memset call DEST, VAL, and LEN are broken out
+   so that this can also be called without constructing an actual CALL_EXPR.
+   The other arguments and return value are the same as for
+   expand_builtin_memset.  */
 
-tree
-compute_objsize (tree ptr, int ostype, access_ref *pref,
-                range_query *rvals /* = NULL */)
+static rtx
+expand_builtin_memset_args (tree dest, tree val, tree len,
+                           rtx target, machine_mode mode, tree orig_exp)
 {
-  pointer_query qry;
-  qry.rvals = rvals;
-  ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
-    return NULL_TREE;
-
-  offset_int maxsize = pref->size_remaining ();
-  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
-    pref->offrng[0] = 0;
-  return wide_int_to_tree (sizetype, maxsize);
-}
+  tree fndecl, fn;
+  enum built_in_function fcode;
+  machine_mode val_mode;
+  char c;
+  unsigned int dest_align;
+  rtx dest_mem, dest_addr, len_rtx;
+  HOST_WIDE_INT expected_size = -1;
+  unsigned int expected_align = 0;
+  unsigned HOST_WIDE_INT min_size;
+  unsigned HOST_WIDE_INT max_size;
+  unsigned HOST_WIDE_INT probable_max_size;
 
-/* Transitional wrapper.  The function should be removed once callers
-   transition to the pointer_query API.  */
+  dest_align = get_pointer_alignment (dest);
 
-tree
-compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
-{
-  pointer_query qry;
-  if (ptr_qry)
-    ptr_qry->depth = 0;
-  else
-    ptr_qry = &qry;
+  /* If DEST is not a pointer type, don't do this operation in-line.  */
+  if (dest_align == 0)
+    return NULL_RTX;
 
-  ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
-    return NULL_TREE;
+  if (currently_expanding_gimple_stmt)
+    stringop_block_profile (currently_expanding_gimple_stmt,
+                           &expected_align, &expected_size);
 
-  offset_int maxsize = pref->size_remaining ();
-  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
-    pref->offrng[0] = 0;
-  return wide_int_to_tree (sizetype, maxsize);
-}
+  if (expected_align < dest_align)
+    expected_align = dest_align;
 
-/* Legacy wrapper around the above.  The function should be removed
-   once callers transition to one of the two above.  */
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    {
+      /* Evaluate and ignore VAL in case it has side-effects.  */
+      expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      return expand_expr (dest, target, mode, EXPAND_NORMAL);
+    }
 
-tree
-compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
-                tree *poff /* = NULL */, range_query *rvals /* = NULL */)
-{
-  /* Set the initial offsets to zero and size to negative to indicate
-     none has been computed yet.  */
-  access_ref ref;
-  tree size = compute_objsize (ptr, ostype, &ref, rvals);
-  if (!size || !ref.base0)
-    return NULL_TREE;
+  /* Stabilize the arguments in case we fail.  */
+  dest = builtin_save_expr (dest);
+  val = builtin_save_expr (val);
+  len = builtin_save_expr (len);
 
-  if (pdecl)
-    *pdecl = ref.ref;
+  len_rtx = expand_normal (len);
+  determine_block_size (len, len_rtx, &min_size, &max_size,
+                       &probable_max_size);
+  dest_mem = get_memory_rtx (dest, len);
+  val_mode = TYPE_MODE (unsigned_char_type_node);
 
-  if (poff)
-    *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
+  if (TREE_CODE (val) != INTEGER_CST
+      || target_char_cast (val, &c))
+    {
+      rtx val_rtx;
 
-  return size;
-}
+      val_rtx = expand_normal (val);
+      val_rtx = convert_to_mode (val_mode, val_rtx, 0);
 
-/* Helper to determine and check the sizes of the source and the destination
-   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  EXP is the
-   call expression, DEST is the destination argument, SRC is the source
-   argument or null, and LEN is the number of bytes.  Use Object Size type-0
-   regardless of the OPT_Wstringop_overflow_ setting.  Return true on success
-   (no overflow or invalid sizes), false otherwise.  */
+      /* Assume that we can memset by pieces if we can store
+       * the coefficients by pieces (in the required modes).
+       * We can't pass builtin_memset_gen_str as that emits RTL.  */
+      c = 1;
+      if (tree_fits_uhwi_p (len)
+         && can_store_by_pieces (tree_to_uhwi (len),
+                                 builtin_memset_read_str, &c, dest_align,
+                                 true))
+       {
+         val_rtx = force_reg (val_mode, val_rtx);
+         store_by_pieces (dest_mem, tree_to_uhwi (len),
+                          builtin_memset_gen_str, val_rtx, dest_align,
+                          true, RETURN_BEGIN);
+       }
+      else if (!set_storage_via_setmem (dest_mem, len_rtx, val_rtx,
+                                       dest_align, expected_align,
+                                       expected_size, min_size, max_size,
+                                       probable_max_size)
+              && !try_store_by_multiple_pieces (dest_mem, len_rtx,
+                                                tree_ctz (len),
+                                                min_size, max_size,
+                                                val_rtx, 0,
+                                                dest_align))
+       goto do_libcall;
 
-static bool
-check_memop_access (tree exp, tree dest, tree src, tree size)
-{
-  /* For functions like memset and memcpy that operate on raw memory
-     try to determine the size of the largest source and destination
-     object using type-0 Object Size regardless of the object size
-     type specified by the option.  */
-  access_data data (exp, access_read_write);
-  tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0, &data.dst);
+      dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+      dest_mem = convert_memory_address (ptr_mode, dest_mem);
+      return dest_mem;
+    }
 
-  return check_access (exp, size, /*maxread=*/NULL_TREE,
-                      srcsize, dstsize, data.mode, &data);
-}
+  if (c)
+    {
+      if (tree_fits_uhwi_p (len)
+         && can_store_by_pieces (tree_to_uhwi (len),
+                                 builtin_memset_read_str, &c, dest_align,
+                                 true))
+       store_by_pieces (dest_mem, tree_to_uhwi (len),
+                        builtin_memset_read_str, &c, dest_align, true,
+                        RETURN_BEGIN);
+      else if (!set_storage_via_setmem (dest_mem, len_rtx,
+                                       gen_int_mode (c, val_mode),
+                                       dest_align, expected_align,
+                                       expected_size, min_size, max_size,
+                                       probable_max_size)
+              && !try_store_by_multiple_pieces (dest_mem, len_rtx,
+                                                tree_ctz (len),
+                                                min_size, max_size,
+                                                NULL_RTX, c,
+                                                dest_align))
+       goto do_libcall;
 
-/* Validate memchr arguments without performing any expansion.
-   Return NULL_RTX.  */
+      dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+      dest_mem = convert_memory_address (ptr_mode, dest_mem);
+      return dest_mem;
+    }
 
-static rtx
-expand_builtin_memchr (tree exp, rtx)
-{
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  set_mem_align (dest_mem, dest_align);
+  dest_addr = clear_storage_hints (dest_mem, len_rtx,
+                                  CALL_EXPR_TAILCALL (orig_exp)
+                                  ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL,
+                                  expected_align, expected_size,
+                                  min_size, max_size,
+                                  probable_max_size, tree_ctz (len));
 
-  tree arg1 = CALL_EXPR_ARG (exp, 0);
-  tree len = CALL_EXPR_ARG (exp, 2);
+  if (dest_addr == 0)
+    {
+      dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+      dest_addr = convert_memory_address (ptr_mode, dest_addr);
+    }
 
-  check_read_access (exp, arg1, len, 0);
+  return dest_addr;
 
-  return NULL_RTX;
+ do_libcall:
+  fndecl = get_callee_fndecl (orig_exp);
+  fcode = DECL_FUNCTION_CODE (fndecl);
+  if (fcode == BUILT_IN_MEMSET)
+    fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 3,
+                               dest, val, len);
+  else if (fcode == BUILT_IN_BZERO)
+    fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 2,
+                               dest, len);
+  else
+    gcc_unreachable ();
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
+  return expand_call (fn, target, target == const0_rtx);
 }
 
-/* Expand a call EXP to the memcpy builtin.
-   Return NULL_RTX if we failed, the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient (and in
-   mode MODE if that's convenient).  */
+/* Expand expression EXP, which is a call to the bzero builtin.  Return
+   NULL_RTX if we failed the caller should emit a normal call.  */
 
 static rtx
-expand_builtin_memcpy (tree exp, rtx target)
+expand_builtin_bzero (tree exp)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+  if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
   tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_access (exp, dest, src, len);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
-  return expand_builtin_memory_copy_args (dest, src, len, target, exp,
-                                         /*retmode=*/ RETURN_BEGIN, false);
+  /* New argument list transforming bzero(ptr x, int y) to
+     memset(ptr x, int 0, size_t y).   This is done this way
+     so that if it isn't expanded inline, we fallback to
+     calling bzero instead of memset.  */
+
+  location_t loc = EXPR_LOCATION (exp);
+
+  return expand_builtin_memset_args (dest, integer_zero_node,
+                                    fold_convert_loc (loc,
+                                                      size_type_node, size),
+                                    const0_rtx, VOIDmode, exp);
 }
 
-/* Check a call EXP to the memmove built-in for validity.
-   Return NULL_RTX on both success and failure.  */
+/* Try to expand cmpstr operation ICODE with the given operands.
+   Return the result rtx on success, otherwise return null.  */
 
 static rtx
-expand_builtin_memmove (tree exp, rtx target)
+expand_cmpstr (insn_code icode, rtx target, rtx arg1_rtx, rtx arg2_rtx,
+              HOST_WIDE_INT align)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
-
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  tree len = CALL_EXPR_ARG (exp, 2);
+  machine_mode insn_mode = insn_data[icode].operand[0].mode;
 
-  check_memop_access (exp, dest, src, len);
+  if (target && (!REG_P (target) || HARD_REGISTER_P (target)))
+    target = NULL_RTX;
 
-  return expand_builtin_memory_copy_args (dest, src, len, target, exp,
-                                         /*retmode=*/ RETURN_BEGIN, true);
+  class expand_operand ops[4];
+  create_output_operand (&ops[0], target, insn_mode);
+  create_fixed_operand (&ops[1], arg1_rtx);
+  create_fixed_operand (&ops[2], arg2_rtx);
+  create_integer_operand (&ops[3], align);
+  if (maybe_expand_insn (icode, 4, ops))
+    return ops[0].value;
+  return NULL_RTX;
 }
 
-/* Expand a call EXP to the mempcpy builtin.
-   Return NULL_RTX if we failed; the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient (and in
-   mode MODE if that's convenient).  */
+/* Expand expression EXP, which is a call to the memcmp built-in function.
+   Return NULL_RTX if we failed and the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient.
+   RESULT_EQ is true if we can relax the returned value to be either zero
+   or nonzero, without caring about the sign.  */
 
 static rtx
-expand_builtin_mempcpy (tree exp, rtx target)
+expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
 {
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  /* Policy does not generally allow using compute_objsize (which
-     is used internally by check_memop_size) to change code generation
-     or drive optimization decisions.
-
-     In this instance it is safe because the code we generate has
-     the same semantics regardless of the return value of
-     check_memop_sizes.   Exactly the same amount of data is copied
-     and the return value is exactly the same in both cases.
-
-     Furthermore, check_memop_size always uses mode 0 for the call to
-     compute_objsize, so the imprecise nature of compute_objsize is
-     avoided.  */
-
-  /* Avoid expanding mempcpy into memcpy when the call is determined
-     to overflow the buffer.  This also prevents the same overflow
-     from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_access (exp, dest, src, len))
+  /* Diagnose calls where the specified length exceeds the size of either
+     object.  */
+  if (!check_read_access (exp, arg1, len, 0)
+      || !check_read_access (exp, arg2, len, 0))
     return NULL_RTX;
 
-  return expand_builtin_mempcpy_args (dest, src, len,
-                                     target, exp, /*retmode=*/ RETURN_END);
-}
-
-/* Helper function to do the actual work for expand of memory copy family
-   functions (memcpy, mempcpy, stpcpy).  Expansing should assign LEN bytes
-   of memory from SRC to DEST and assign to TARGET if convenient.  Return
-   value is based on RETMODE argument.  */
+  /* Due to the performance benefit, always inline the calls first
+     when result_eq is false.  */
+  rtx result = NULL_RTX;
+  enum built_in_function fcode = DECL_FUNCTION_CODE (get_callee_fndecl (exp));
+  if (!result_eq && fcode != BUILT_IN_BCMP)
+    {
+      result = inline_expand_builtin_bytecmp (exp, target);
+      if (result)
+       return result;
+    }
 
-static rtx
-expand_builtin_memory_copy_args (tree dest, tree src, tree len,
-                                rtx target, tree exp, memop_ret retmode,
-                                bool might_overlap)
-{
-  unsigned int src_align = get_pointer_alignment (src);
-  unsigned int dest_align = get_pointer_alignment (dest);
-  rtx dest_mem, src_mem, dest_addr, len_rtx;
-  HOST_WIDE_INT expected_size = -1;
-  unsigned int expected_align = 0;
-  unsigned HOST_WIDE_INT min_size;
-  unsigned HOST_WIDE_INT max_size;
-  unsigned HOST_WIDE_INT probable_max_size;
+  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+  location_t loc = EXPR_LOCATION (exp);
 
-  bool is_move_done;
+  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
+  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
 
-  /* If DEST is not a pointer type, call the normal function.  */
-  if (dest_align == 0)
+  /* If we don't have POINTER_TYPE, call the function.  */
+  if (arg1_align == 0 || arg2_align == 0)
     return NULL_RTX;
 
-  /* If either SRC is not a pointer type, don't do this
-     operation in-line.  */
-  if (src_align == 0)
-    return NULL_RTX;
+  rtx arg1_rtx = get_memory_rtx (arg1, len);
+  rtx arg2_rtx = get_memory_rtx (arg2, len);
+  rtx len_rtx = expand_normal (fold_convert_loc (loc, sizetype, len));
 
-  if (currently_expanding_gimple_stmt)
-    stringop_block_profile (currently_expanding_gimple_stmt,
-                           &expected_align, &expected_size);
+  /* Set MEM_SIZE as appropriate.  */
+  if (CONST_INT_P (len_rtx))
+    {
+      set_mem_size (arg1_rtx, INTVAL (len_rtx));
+      set_mem_size (arg2_rtx, INTVAL (len_rtx));
+    }
 
-  if (expected_align < dest_align)
-    expected_align = dest_align;
-  dest_mem = get_memory_rtx (dest, len);
-  set_mem_align (dest_mem, dest_align);
-  len_rtx = expand_normal (len);
-  determine_block_size (len, len_rtx, &min_size, &max_size,
-                       &probable_max_size);
+  by_pieces_constfn constfn = NULL;
 
-  /* Try to get the byte representation of the constant SRC points to,
-     with its byte size in NBYTES.  */
+  /* Try to get the byte representation of the constant ARG2 (or, only
+     when the function's result is used for equality to zero, ARG1)
+     points to, with its byte size in NBYTES.  */
   unsigned HOST_WIDE_INT nbytes;
-  const char *rep = getbyterep (src, &nbytes);
+  const char *rep = getbyterep (arg2, &nbytes);
+  if (result_eq && rep == NULL)
+    {
+      /* For equality to zero the arguments are interchangeable.  */
+      rep = getbyterep (arg1, &nbytes);
+      if (rep != NULL)
+       std::swap (arg1_rtx, arg2_rtx);
+    }
 
   /* If the function's constant bound LEN_RTX is less than or equal
      to the byte size of the representation of the constant argument,
      and if block move would be done by pieces, we can avoid loading
-     the bytes from memory and only store the computed constant.
-     This works in the overlap (memmove) case as well because
-     store_by_pieces just generates a series of stores of constants
-     from the representation returned by getbyterep().  */
+     the bytes from memory and only store the computed constant result.  */
   if (rep
       && CONST_INT_P (len_rtx)
-      && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= nbytes
-      && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
-                             CONST_CAST (char *, rep),
-                             dest_align, false))
-    {
-      dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
-                                 builtin_memcpy_read_str,
-                                 CONST_CAST (char *, rep),
-                                 dest_align, false, retmode);
-      dest_mem = force_operand (XEXP (dest_mem, 0), target);
-      dest_mem = convert_memory_address (ptr_mode, dest_mem);
-      return dest_mem;
-    }
+      && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= nbytes)
+    constfn = builtin_memcpy_read_str;
 
-  src_mem = get_memory_rtx (src, len);
-  set_mem_align (src_mem, src_align);
+  result = emit_block_cmp_hints (arg1_rtx, arg2_rtx, len_rtx,
+                                TREE_TYPE (len), target,
+                                result_eq, constfn,
+                                CONST_CAST (char *, rep));
 
-  /* Copy word part most expediently.  */
-  enum block_op_methods method = BLOCK_OP_NORMAL;
-  if (CALL_EXPR_TAILCALL (exp)
-      && (retmode == RETURN_BEGIN || target == const0_rtx))
-    method = BLOCK_OP_TAILCALL;
-  bool use_mempcpy_call = (targetm.libc_has_fast_function (BUILT_IN_MEMPCPY)
-                          && retmode == RETURN_END
-                          && !might_overlap
-                          && target != const0_rtx);
-  if (use_mempcpy_call)
-    method = BLOCK_OP_NO_LIBCALL_RET;
-  dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, method,
-                                    expected_align, expected_size,
-                                    min_size, max_size, probable_max_size,
-                                    use_mempcpy_call, &is_move_done,
-                                    might_overlap);
+  if (result)
+    {
+      /* Return the value in the proper mode for this function.  */
+      if (GET_MODE (result) == mode)
+       return result;
 
-  /* Bail out when a mempcpy call would be expanded as libcall and when
-     we have a target that provides a fast implementation
-     of mempcpy routine.  */
-  if (!is_move_done)
-    return NULL_RTX;
+      if (target != 0)
+       {
+         convert_move (target, result, 0);
+         return target;
+       }
 
-  if (dest_addr == pc_rtx)
-    return NULL_RTX;
+      return convert_to_mode (mode, result, 0);
+    }
 
-  if (dest_addr == 0)
-    {
-      dest_addr = force_operand (XEXP (dest_mem, 0), target);
-      dest_addr = convert_memory_address (ptr_mode, dest_addr);
-    }
-
-  if (retmode != RETURN_BEGIN && target != const0_rtx)
-    {
-      dest_addr = gen_rtx_PLUS (ptr_mode, dest_addr, len_rtx);
-      /* stpcpy pointer to last byte.  */
-      if (retmode == RETURN_END_MINUS_ONE)
-       dest_addr = gen_rtx_MINUS (ptr_mode, dest_addr, const1_rtx);
-    }
-
-  return dest_addr;
-}
-
-static rtx
-expand_builtin_mempcpy_args (tree dest, tree src, tree len,
-                            rtx target, tree orig_exp, memop_ret retmode)
-{
-  return expand_builtin_memory_copy_args (dest, src, len, target, orig_exp,
-                                         retmode, false);
+  return NULL_RTX;
 }
 
-/* Expand into a movstr instruction, if one is available.  Return NULL_RTX if
-   we failed, the caller should emit a normal call, otherwise try to
-   get the result in TARGET, if convenient.
-   Return value is based on RETMODE argument.  */
+/* Expand expression EXP, which is a call to the strcmp builtin.  Return NULL_RTX
+   if we failed the caller should emit a normal call, otherwise try to get
+   the result in TARGET, if convenient.  */
 
 static rtx
-expand_movstr (tree dest, tree src, rtx target, memop_ret retmode)
+expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
 {
-  class expand_operand ops[3];
-  rtx dest_mem;
-  rtx src_mem;
-
-  if (!targetm.have_movstr ())
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest_mem = get_memory_rtx (dest, NULL);
-  src_mem = get_memory_rtx (src, NULL);
-  if (retmode == RETURN_BEGIN)
-    {
-      target = force_reg (Pmode, XEXP (dest_mem, 0));
-      dest_mem = replace_equiv_address (dest_mem, target);
-    }
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
 
-  create_output_operand (&ops[0],
-                        retmode != RETURN_BEGIN ? target : NULL_RTX, Pmode);
-  create_fixed_operand (&ops[1], dest_mem);
-  create_fixed_operand (&ops[2], src_mem);
-  if (!maybe_expand_insn (targetm.code_for_movstr, 3, ops))
+  if (!check_read_access (exp, arg1)
+      || !check_read_access (exp, arg2))
     return NULL_RTX;
 
-  if (retmode != RETURN_BEGIN && target != const0_rtx)
-    {
-      target = ops[0].value;
-      /* movstr is supposed to set end to the address of the NUL
-        terminator.  If the caller requested a mempcpy-like return value,
-        adjust it.  */
-      if (retmode == RETURN_END)
-       {
-         rtx tem = plus_constant (GET_MODE (target),
-                                  gen_lowpart (GET_MODE (target), target), 1);
-         emit_move_insn (target, force_operand (tem, NULL_RTX));
-       }
-    }
-  return target;
-}
+  /* Due to the performance benefit, always inline the calls first.  */
+  rtx result = NULL_RTX;
+  result = inline_expand_builtin_bytecmp (exp, target);
+  if (result)
+    return result;
 
-/* Do some very basic size validation of a call to the strcpy builtin
-   given by EXP.  Return NULL_RTX to have the built-in expand to a call
-   to the library function.  */
+  insn_code cmpstr_icode = direct_optab_handler (cmpstr_optab, SImode);
+  insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
+  if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing)
+    return NULL_RTX;
 
-static rtx
-expand_builtin_strcat (tree exp)
-{
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
-      || !warn_stringop_overflow)
+  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
+  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
+
+  /* If we don't have POINTER_TYPE, call the function.  */
+  if (arg1_align == 0 || arg2_align == 0)
     return NULL_RTX;
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
+  /* Stabilize the arguments in case gen_cmpstr(n)si fail.  */
+  arg1 = builtin_save_expr (arg1);
+  arg2 = builtin_save_expr (arg2);
 
-  /* There is no way here to determine the length of the string in
-     the destination to which the SRC string is being appended so
-     just diagnose cases when the souce string is longer than
-     the destination object.  */
-  access_data data (exp, access_read_write, NULL_TREE, true,
-                   NULL_TREE, true);
-  const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src);
-  tree destsize = compute_objsize (dest, ost, &data.dst);
+  rtx arg1_rtx = get_memory_rtx (arg1, NULL);
+  rtx arg2_rtx = get_memory_rtx (arg2, NULL);
 
-  check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
-               src, destsize, data.mode, &data);
+  /* Try to call cmpstrsi.  */
+  if (cmpstr_icode != CODE_FOR_nothing)
+    result = expand_cmpstr (cmpstr_icode, target, arg1_rtx, arg2_rtx,
+                           MIN (arg1_align, arg2_align));
 
-  return NULL_RTX;
-}
+  /* Try to determine at least one length and call cmpstrnsi.  */
+  if (!result && cmpstrn_icode != CODE_FOR_nothing)
+    {
+      tree len;
+      rtx arg3_rtx;
 
-/* Expand expression EXP, which is a call to the strcpy builtin.  Return
-   NULL_RTX if we failed the caller should emit a normal call, otherwise
-   try to get the result in TARGET, if convenient (and in mode MODE if that's
-   convenient).  */
+      tree len1 = c_strlen (arg1, 1);
+      tree len2 = c_strlen (arg2, 1);
 
-static rtx
-expand_builtin_strcpy (tree exp, rtx target)
-{
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+      if (len1)
+       len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
+      if (len2)
+       len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
+      /* If we don't have a constant length for the first, use the length
+        of the second, if we know it.  We don't require a constant for
+        this case; some cost analysis could be done if both are available
+        but neither is constant.  For now, assume they're equally cheap,
+        unless one has side effects.  If both strings have constant lengths,
+        use the smaller.  */
 
-  if (warn_stringop_overflow)
-    {
-      access_data data (exp, access_read_write, NULL_TREE, true,
-                       NULL_TREE, true);
-      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-      compute_objsize (src, ost, &data.src);
-      tree dstsize = compute_objsize (dest, ost, &data.dst);
-      check_access (exp, /*dstwrite=*/ NULL_TREE,
-                   /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
-                   dstsize, data.mode, &data);
+      if (!len1)
+       len = len2;
+      else if (!len2)
+       len = len1;
+      else if (TREE_SIDE_EFFECTS (len1))
+       len = len2;
+      else if (TREE_SIDE_EFFECTS (len2))
+       len = len1;
+      else if (TREE_CODE (len1) != INTEGER_CST)
+       len = len2;
+      else if (TREE_CODE (len2) != INTEGER_CST)
+       len = len1;
+      else if (tree_int_cst_lt (len1, len2))
+       len = len1;
+      else
+       len = len2;
+
+      /* If both arguments have side effects, we cannot optimize.  */
+      if (len && !TREE_SIDE_EFFECTS (len))
+       {
+         arg3_rtx = expand_normal (len);
+         result = expand_cmpstrn_or_cmpmem
+           (cmpstrn_icode, target, arg1_rtx, arg2_rtx, TREE_TYPE (len),
+            arg3_rtx, MIN (arg1_align, arg2_align));
+       }
     }
 
-  if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
+  tree fndecl = get_callee_fndecl (exp);
+  if (result)
     {
       /* Check to see if the argument was declared attribute nonstring
         and if so, issue a warning since at this point it's not known
         to be nul-terminated.  */
-      tree fndecl = get_callee_fndecl (exp);
       maybe_warn_nonstring_arg (fndecl, exp);
-      return ret;
-    }
-
-  return NULL_RTX;
-}
-
-/* Helper function to do the actual work for expand_builtin_strcpy.  The
-   arguments to the builtin_strcpy call DEST and SRC are broken out
-   so that this can also be called without constructing an actual CALL_EXPR.
-   The other arguments and return value are the same as for
-   expand_builtin_strcpy.  */
 
-static rtx
-expand_builtin_strcpy_args (tree exp, tree dest, tree src, rtx target)
-{
-  /* Detect strcpy calls with unterminated arrays..  */
-  tree size;
-  bool exact;
-  if (tree nonstr = unterminated_array (src, &size, &exact))
-    {
-      /* NONSTR refers to the non-nul terminated constant array.  */
-      warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, nonstr,
-                         size, exact);
-      return NULL_RTX;
+      /* Return the value in the proper mode for this function.  */
+      machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+      if (GET_MODE (result) == mode)
+       return result;
+      if (target == 0)
+       return convert_to_mode (mode, result, 0);
+      convert_move (target, result, 0);
+      return target;
     }
 
-  return expand_movstr (dest, src, target, /*retmode=*/ RETURN_BEGIN);
+  /* Expand the library call ourselves using a stabilized argument
+     list to avoid re-evaluating the function's arguments twice.  */
+  tree fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg1, arg2);
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (fn, target, target == const0_rtx);
 }
 
-/* Expand a call EXP to the stpcpy builtin.
-   Return NULL_RTX if we failed the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient (and in
-   mode MODE if that's convenient).  */
+/* Expand expression EXP, which is a call to the strncmp builtin. Return
+   NULL_RTX if we failed the caller should emit a normal call, otherwise
+   try to get the result in TARGET, if convenient.  */
 
 static rtx
-expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
+expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
+                       ATTRIBUTE_UNUSED machine_mode mode)
 {
-  tree dst, src;
-  location_t loc = EXPR_LOCATION (exp);
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
+  tree arg3 = CALL_EXPR_ARG (exp, 2);
+
+  if (!check_nul_terminated_array (exp, arg1, arg3)
+      || !check_nul_terminated_array (exp, arg2, arg3))
     return NULL_RTX;
 
-  dst = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
+  location_t loc = EXPR_LOCATION (exp);
+  tree len1 = c_strlen (arg1, 1);
+  tree len2 = c_strlen (arg2, 1);
 
-  if (warn_stringop_overflow)
+  if (!len1 || !len2)
     {
-      access_data data (exp, access_read_write);
-      tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
-                                      &data.dst);
-      check_access (exp, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
-                   src, destsize, data.mode, &data);
-    }
-
-  /* If return value is ignored, transform stpcpy into strcpy.  */
-  if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY))
-    {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
-      tree result = build_call_nofold_loc (loc, fn, 2, dst, src);
-      return expand_expr (result, target, mode, EXPAND_NORMAL);
-    }
-  else
-    {
-      tree len, lenp1;
-      rtx ret;
-
-      /* Ensure we get an actual string whose length can be evaluated at
-        compile-time, not an expression containing a string.  This is
-        because the latter will potentially produce pessimized code
-        when used to produce the return value.  */
-      c_strlen_data lendata = { };
-      if (!c_getstr (src)
-         || !(len = c_strlen (src, 0, &lendata, 1)))
-       return expand_movstr (dst, src, target,
-                             /*retmode=*/ RETURN_END_MINUS_ONE);
-
-      if (lendata.decl)
-       warn_string_no_nul (EXPR_LOCATION (exp), exp, NULL, src, lendata.decl);
+      /* Check to see if the argument was declared attribute nonstring
+        and if so, issue a warning since at this point it's not known
+        to be nul-terminated.  */
+      if (!maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp)
+         && !len1 && !len2)
+       {
+         /* A strncmp read is constrained not just by the bound but
+            also by the length of the shorter string.  Specifying
+            a bound that's larger than the size of either array makes
+            no sense and is likely a bug.  When the length of neither
+            of the two strings is known but the sizes of both of
+            the arrays they are stored in is, issue a warning if
+            the bound is larger than than the size of the larger
+            of the two arrays.  */
 
-      lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
-      ret = expand_builtin_mempcpy_args (dst, src, lenp1,
-                                        target, exp,
-                                        /*retmode=*/ RETURN_END_MINUS_ONE);
+         access_ref ref1 (arg3, true);
+         access_ref ref2 (arg3, true);
 
-      if (ret)
-       return ret;
+         tree bndrng[2] = { NULL_TREE, NULL_TREE };
+         get_size_range (arg3, bndrng, ref1.bndrng);
 
-      if (TREE_CODE (len) == INTEGER_CST)
-       {
-         rtx len_rtx = expand_normal (len);
+         tree size1 = compute_objsize (arg1, 1, &ref1);
+         tree size2 = compute_objsize (arg2, 1, &ref2);
+         tree func = get_callee_fndecl (exp);
 
-         if (CONST_INT_P (len_rtx))
+         if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
            {
-             ret = expand_builtin_strcpy_args (exp, dst, src, target);
-
-             if (ret)
+             offset_int rem1 = ref1.size_remaining ();
+             offset_int rem2 = ref2.size_remaining ();
+             if (rem1 == 0 || rem2 == 0)
+               maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+                                     bndrng, integer_zero_node);
+             else
                {
-                 if (! target)
-                   {
-                     if (mode != VOIDmode)
-                       target = gen_reg_rtx (mode);
-                     else
-                       target = gen_reg_rtx (GET_MODE (ret));
-                   }
-                 if (GET_MODE (target) != GET_MODE (ret))
-                   ret = gen_lowpart (GET_MODE (target), ret);
-
-                 ret = plus_constant (GET_MODE (ret), ret, INTVAL (len_rtx));
-                 ret = emit_move_insn (target, force_operand (ret, NULL_RTX));
-                 gcc_assert (ret);
-
-                 return target;
+                 offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
+                 if (maxrem < wi::to_offset (bndrng[0]))
+                   maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
+                                         func, bndrng,
+                                         wide_int_to_tree (sizetype, maxrem));
                }
            }
+         else if (bndrng[0]
+                  && !integer_zerop (bndrng[0])
+                  && ((size1 && integer_zerop (size1))
+                      || (size2 && integer_zerop (size2))))
+           maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+                                 bndrng, integer_zero_node);
        }
-
-      return expand_movstr (dst, src, target,
-                           /*retmode=*/ RETURN_END_MINUS_ONE);
     }
-}
 
-/* Expand a call EXP to the stpcpy builtin and diagnose uses of nonstring
-   arguments while being careful to avoid duplicate warnings (which could
-   be issued if the expander were to expand the call, resulting in it
-   being emitted in expand_call().  */
+  /* Due to the performance benefit, always inline the calls first.  */
+  rtx result = NULL_RTX;
+  result = inline_expand_builtin_bytecmp (exp, target);
+  if (result)
+    return result;
 
-static rtx
-expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
-{
-  if (rtx ret = expand_builtin_stpcpy_1 (exp, target, mode))
-    {
-      /* The call has been successfully expanded.  Check for nonstring
-        arguments and issue warnings as appropriate.  */
-      maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp);
-      return ret;
-    }
+  /* If c_strlen can determine an expression for one of the string
+     lengths, and it doesn't have side effects, then emit cmpstrnsi
+     using length MIN(strlen(string)+1, arg3).  */
+  insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
+  if (cmpstrn_icode == CODE_FOR_nothing)
+    return NULL_RTX;
 
-  return NULL_RTX;
-}
+  tree len;
 
-/* Check a call EXP to the stpncpy built-in for validity.
-   Return NULL_RTX on both success and failure.  */
+  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
+  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
 
-static rtx
-expand_builtin_stpncpy (tree exp, rtx)
-{
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
-      || !warn_stringop_overflow)
-    return NULL_RTX;
+  if (len1)
+    len1 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len1);
+  if (len2)
+    len2 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len2);
 
-  /* The source and destination of the call.  */
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len3 = fold_convert_loc (loc, sizetype, arg3);
 
-  /* The exact number of bytes to write (not the maximum).  */
-  tree len = CALL_EXPR_ARG (exp, 2);
-  access_data data (exp, access_read_write);
-  /* The size of the destination object.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
-  check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data);
-  return NULL_RTX;
-}
+  /* If we don't have a constant length for the first, use the length
+     of the second, if we know it.  If neither string is constant length,
+     use the given length argument.  We don't require a constant for
+     this case; some cost analysis could be done if both are available
+     but neither is constant.  For now, assume they're equally cheap,
+     unless one has side effects.  If both strings have constant lengths,
+     use the smaller.  */
 
-/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
-   bytes from constant string DATA + OFFSET and return it as target
-   constant.  */
+  if (!len1 && !len2)
+    len = len3;
+  else if (!len1)
+    len = len2;
+  else if (!len2)
+    len = len1;
+  else if (TREE_SIDE_EFFECTS (len1))
+    len = len2;
+  else if (TREE_SIDE_EFFECTS (len2))
+    len = len1;
+  else if (TREE_CODE (len1) != INTEGER_CST)
+    len = len2;
+  else if (TREE_CODE (len2) != INTEGER_CST)
+    len = len1;
+  else if (tree_int_cst_lt (len1, len2))
+    len = len1;
+  else
+    len = len2;
 
-rtx
-builtin_strncpy_read_str (void *data, void *, HOST_WIDE_INT offset,
-                         scalar_int_mode mode)
-{
-  const char *str = (const char *) data;
+  /* If we are not using the given length, we must incorporate it here.
+     The actual new length parameter will be MIN(len,arg3) in this case.  */
+  if (len != len3)
+    {
+      len = fold_convert_loc (loc, sizetype, len);
+      len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
+    }
+  rtx arg1_rtx = get_memory_rtx (arg1, len);
+  rtx arg2_rtx = get_memory_rtx (arg2, len);
+  rtx arg3_rtx = expand_normal (len);
+  result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
+                                    arg2_rtx, TREE_TYPE (len), arg3_rtx,
+                                    MIN (arg1_align, arg2_align));
 
-  if ((unsigned HOST_WIDE_INT) offset > strlen (str))
-    return const0_rtx;
+  tree fndecl = get_callee_fndecl (exp);
+  if (result)
+    {
+      /* Return the value in the proper mode for this function.  */
+      mode = TYPE_MODE (TREE_TYPE (exp));
+      if (GET_MODE (result) == mode)
+       return result;
+      if (target == 0)
+       return convert_to_mode (mode, result, 0);
+      convert_move (target, result, 0);
+      return target;
+    }
 
-  return c_readstr (str + offset, mode);
+  /* Expand the library call ourselves using a stabilized argument
+     list to avoid re-evaluating the function's arguments twice.  */
+  tree call = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
+  copy_warning (call, exp);
+  gcc_assert (TREE_CODE (call) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (call) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (call, target, target == const0_rtx);
 }
 
-/* Helper to check the sizes of sequences and the destination of calls
-   to __builtin_strncat and __builtin___strncat_chk.  Returns true on
-   success (no overflow or invalid sizes), false otherwise.  */
+/* Expand a call to __builtin_saveregs, generating the result in TARGET,
+   if that's convenient.  */
 
-static bool
-check_strncat_sizes (tree exp, tree objsize)
+rtx
+expand_builtin_saveregs (void)
 {
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxread = CALL_EXPR_ARG (exp, 2);
+  rtx val;
+  rtx_insn *seq;
 
-  /* Try to determine the range of lengths that the source expression
-     refers to.  */
-  c_strlen_data lendata = { };
-  get_range_strlen (src, &lendata, /* eltsize = */ 1);
+  /* Don't do __builtin_saveregs more than once in a function.
+     Save the result of the first call and reuse it.  */
+  if (saveregs_value != 0)
+    return saveregs_value;
 
-  /* Try to verify that the destination is big enough for the shortest
-     string.  */
+  /* When this function is called, it means that registers must be
+     saved on entry to this function.  So we migrate the call to the
+     first insn of this function.  */
 
-  access_data data (exp, access_read_write, maxread, true);
-  if (!objsize && warn_stringop_overflow)
-    {
-      /* If it hasn't been provided by __strncat_chk, try to determine
-        the size of the destination object into which the source is
-        being copied.  */
-      objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
-    }
+  start_sequence ();
 
-  /* Add one for the terminating nul.  */
-  tree srclen = (lendata.minlen
-                ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
-                               size_one_node)
-                : NULL_TREE);
+  /* Do whatever the machine needs done in this case.  */
+  val = targetm.calls.expand_builtin_saveregs ();
 
-  /* The strncat function copies at most MAXREAD bytes and always appends
-     the terminating nul so the specified upper bound should never be equal
-     to (or greater than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxread))
-    {
-      location_t loc = EXPR_LOCATION (exp);
-      warning_at (loc, OPT_Wstringop_overflow_,
-                 "%qD specified bound %E equals destination size",
-                 get_callee_fndecl (exp), maxread);
+  seq = get_insns ();
+  end_sequence ();
 
-      return false;
-    }
+  saveregs_value = val;
 
-  if (!srclen
-      || (maxread && tree_fits_uhwi_p (maxread)
-         && tree_fits_uhwi_p (srclen)
-         && tree_int_cst_lt (maxread, srclen)))
-    srclen = maxread;
+  /* Put the insns after the NOTE that starts the function.  If this
+     is inside a start_sequence, make the outer-level insn chain current, so
+     the code is placed at the start of the function.  */
+  push_topmost_sequence ();
+  emit_insn_after (seq, entry_of_function ());
+  pop_topmost_sequence ();
 
-  /* The number of bytes to write is LEN but check_access will alsoa
-     check SRCLEN if LEN's value isn't known.  */
-  return check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
-                      objsize, data.mode, &data);
+  return val;
 }
 
-/* Similar to expand_builtin_strcat, do some very basic size validation
-   of a call to the strcpy builtin given by EXP.  Return NULL_RTX to have
-   the built-in expand to a call to the library function.  */
+/* Expand a call to __builtin_next_arg.  */
 
 static rtx
-expand_builtin_strncat (tree exp, rtx)
+expand_builtin_next_arg (void)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
-      || !warn_stringop_overflow)
-    return NULL_RTX;
+  /* Checking arguments is already done in fold_builtin_next_arg
+     that must be called before this function.  */
+  return expand_binop (ptr_mode, add_optab,
+                      crtl->args.internal_arg_pointer,
+                      crtl->args.arg_offset_rtx,
+                      NULL_RTX, 0, OPTAB_LIB_WIDEN);
+}
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  /* The upper bound on the number of bytes to write.  */
-  tree maxread = CALL_EXPR_ARG (exp, 2);
+/* Make it easier for the backends by protecting the valist argument
+   from multiple evaluations.  */
 
-  /* Detect unterminated source (only).  */
-  if (!check_nul_terminated_array (exp, src, maxread))
-    return NULL_RTX;
+static tree
+stabilize_va_list_loc (location_t loc, tree valist, int needs_lvalue)
+{
+  tree vatype = targetm.canonical_va_list_type (TREE_TYPE (valist));
 
-  /* The length of the source sequence.  */
-  tree slen = c_strlen (src, 1);
+  /* The current way of determining the type of valist is completely
+     bogus.  We should have the information on the va builtin instead.  */
+  if (!vatype)
+    vatype = targetm.fn_abi_va_list (cfun->decl);
 
-  /* Try to determine the range of lengths that the source expression
-     refers to.  Since the lengths are only used for warning and not
-     for code generation disable strict mode below.  */
-  tree maxlen = slen;
-  if (!maxlen)
+  if (TREE_CODE (vatype) == ARRAY_TYPE)
     {
-      c_strlen_data lendata = { };
-      get_range_strlen (src, &lendata, /* eltsize = */ 1);
-      maxlen = lendata.maxbound;
-    }
+      if (TREE_SIDE_EFFECTS (valist))
+       valist = save_expr (valist);
 
-  access_data data (exp, access_read_write);
-  /* Try to verify that the destination is big enough for the shortest
-     string.  First try to determine the size of the destination object
-     into which the source is being copied.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+      /* For this case, the backends will be expecting a pointer to
+        vatype, but it's possible we've actually been given an array
+        (an actual TARGET_CANONICAL_VA_LIST_TYPE (valist)).
+        So fix it.  */
+      if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
+       {
+         tree p1 = build_pointer_type (TREE_TYPE (vatype));
+         valist = build_fold_addr_expr_with_type_loc (loc, valist, p1);
+       }
+    }
+  else
+    {
+      tree pt = build_pointer_type (vatype);
 
-  /* Add one for the terminating nul.  */
-  tree srclen = (maxlen
-                ? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
-                               size_one_node)
-                : NULL_TREE);
+      if (! needs_lvalue)
+       {
+         if (! TREE_SIDE_EFFECTS (valist))
+           return valist;
 
-  /* The strncat function copies at most MAXREAD bytes and always appends
-     the terminating nul so the specified upper bound should never be equal
-     to (or greater than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxread))
-    {
-      location_t loc = EXPR_LOCATION (exp);
-      warning_at (loc, OPT_Wstringop_overflow_,
-                 "%qD specified bound %E equals destination size",
-                 get_callee_fndecl (exp), maxread);
+         valist = fold_build1_loc (loc, ADDR_EXPR, pt, valist);
+         TREE_SIDE_EFFECTS (valist) = 1;
+       }
 
-      return NULL_RTX;
+      if (TREE_SIDE_EFFECTS (valist))
+       valist = save_expr (valist);
+      valist = fold_build2_loc (loc, MEM_REF,
+                               vatype, valist, build_int_cst (pt, 0));
     }
 
-  if (!srclen
-      || (maxread && tree_fits_uhwi_p (maxread)
-         && tree_fits_uhwi_p (srclen)
-         && tree_int_cst_lt (maxread, srclen)))
-    srclen = maxread;
-
-  check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
-               destsize, data.mode, &data);
-  return NULL_RTX;
+  return valist;
 }
 
-/* Expand expression EXP, which is a call to the strncpy builtin.  Return
-   NULL_RTX if we failed the caller should emit a normal call.  */
+/* The "standard" definition of va_list is void*.  */
 
-static rtx
-expand_builtin_strncpy (tree exp, rtx target)
+tree
+std_build_builtin_va_list (void)
 {
-  location_t loc = EXPR_LOCATION (exp);
+  return ptr_type_node;
+}
 
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  /* The number of bytes to write (not the maximum).  */
-  tree len = CALL_EXPR_ARG (exp, 2);
+/* The "standard" abi va_list is va_list_type_node.  */
 
-  /* The length of the source sequence.  */
-  tree slen = c_strlen (src, 1);
+tree
+std_fn_abi_va_list (tree fndecl ATTRIBUTE_UNUSED)
+{
+  return va_list_type_node;
+}
 
-  if (warn_stringop_overflow)
-    {
-      access_data data (exp, access_read_write, len, true, len, true);
-      const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-      compute_objsize (src, ost, &data.src);
-      tree dstsize = compute_objsize (dest, ost, &data.dst);
-      /* The number of bytes to write is LEN but check_access will also
-        check SLEN if LEN's value isn't known.  */
-      check_access (exp, /*dstwrite=*/len,
-                   /*maxread=*/len, src, dstsize, data.mode, &data);
-    }
+/* The "standard" type of va_list is va_list_type_node.  */
 
-  /* We must be passed a constant len and src parameter.  */
-  if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
-    return NULL_RTX;
+tree
+std_canonical_va_list_type (tree type)
+{
+  tree wtype, htype;
 
-  slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+  wtype = va_list_type_node;
+  htype = type;
 
-  /* We're required to pad with trailing zeros if the requested
-     len is greater than strlen(s2)+1.  In that case try to
-     use store_by_pieces, if it fails, punt.  */
-  if (tree_int_cst_lt (slen, len))
+  if (TREE_CODE (wtype) == ARRAY_TYPE)
     {
-      unsigned int dest_align = get_pointer_alignment (dest);
-      const char *p = c_getstr (src);
-      rtx dest_mem;
+      /* If va_list is an array type, the argument may have decayed
+        to a pointer type, e.g. by being passed to another function.
+        In that case, unwrap both types so that we can compare the
+        underlying records.  */
+      if (TREE_CODE (htype) == ARRAY_TYPE
+         || POINTER_TYPE_P (htype))
+       {
+         wtype = TREE_TYPE (wtype);
+         htype = TREE_TYPE (htype);
+       }
+    }
+  if (TYPE_MAIN_VARIANT (wtype) == TYPE_MAIN_VARIANT (htype))
+    return va_list_type_node;
 
-      if (!p || dest_align == 0 || !tree_fits_uhwi_p (len)
-         || !can_store_by_pieces (tree_to_uhwi (len),
-                                  builtin_strncpy_read_str,
-                                  CONST_CAST (char *, p),
-                                  dest_align, false))
-       return NULL_RTX;
+  return NULL_TREE;
+}
 
-      dest_mem = get_memory_rtx (dest, len);
-      store_by_pieces (dest_mem, tree_to_uhwi (len),
-                      builtin_strncpy_read_str,
-                      CONST_CAST (char *, p), dest_align, false,
-                      RETURN_BEGIN);
-      dest_mem = force_operand (XEXP (dest_mem, 0), target);
-      dest_mem = convert_memory_address (ptr_mode, dest_mem);
-      return dest_mem;
-    }
+/* The "standard" implementation of va_start: just assign `nextarg' to
+   the variable.  */
 
-  return NULL_RTX;
+void
+std_expand_builtin_va_start (tree valist, rtx nextarg)
+{
+  rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  convert_move (va_r, nextarg, 0);
 }
 
-/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
-   bytes from constant string DATA + OFFSET and return it as target
-   constant.  If PREV isn't nullptr, it has the RTL info from the
-   previous iteration.  */
+/* Expand EXP, a call to __builtin_va_start.  */
 
-rtx
-builtin_memset_read_str (void *data, void *prevp,
-                        HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
-                        scalar_int_mode mode)
+static rtx
+expand_builtin_va_start (tree exp)
 {
-  by_pieces_prev *prev = (by_pieces_prev *) prevp;
-  if (prev != nullptr && prev->data != nullptr)
+  rtx nextarg;
+  tree valist;
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (call_expr_nargs (exp) < 2)
     {
-      /* Use the previous data in the same mode.  */
-      if (prev->mode == mode)
-       return prev->data;
+      error_at (loc, "too few arguments to function %<va_start%>");
+      return const0_rtx;
     }
 
-  const char *c = (const char *) data;
-  char *p = XALLOCAVEC (char, GET_MODE_SIZE (mode));
+  if (fold_builtin_next_arg (exp, true))
+    return const0_rtx;
 
-  memset (p, *c, GET_MODE_SIZE (mode));
+  nextarg = expand_builtin_next_arg ();
+  valist = stabilize_va_list_loc (loc, CALL_EXPR_ARG (exp, 0), 1);
 
-  return c_readstr (p, mode);
+  if (targetm.expand_builtin_va_start)
+    targetm.expand_builtin_va_start (valist, nextarg);
+  else
+    std_expand_builtin_va_start (valist, nextarg);
+
+  return const0_rtx;
 }
 
-/* Callback routine for store_by_pieces.  Return the RTL of a register
-   containing GET_MODE_SIZE (MODE) consecutive copies of the unsigned
-   char value given in the RTL register data.  For example, if mode is
-   4 bytes wide, return the RTL for 0x01010101*data.  If PREV isn't
-   nullptr, it has the RTL info from the previous iteration.  */
+/* Expand EXP, a call to __builtin_va_end.  */
 
 static rtx
-builtin_memset_gen_str (void *data, void *prevp,
-                       HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
-                       scalar_int_mode mode)
+expand_builtin_va_end (tree exp)
 {
-  rtx target, coeff;
-  size_t size;
-  char *p;
-
-  by_pieces_prev *prev = (by_pieces_prev *) prevp;
-  if (prev != nullptr && prev->data != nullptr)
-    {
-      /* Use the previous data in the same mode.  */
-      if (prev->mode == mode)
-       return prev->data;
-
-      target = simplify_gen_subreg (mode, prev->data, prev->mode, 0);
-      if (target != nullptr)
-       return target;
-    }
-
-  size = GET_MODE_SIZE (mode);
-  if (size == 1)
-    return (rtx) data;
+  tree valist = CALL_EXPR_ARG (exp, 0);
 
-  p = XALLOCAVEC (char, size);
-  memset (p, 1, size);
-  coeff = c_readstr (p, mode);
+  /* Evaluate for side effects, if needed.  I hate macros that don't
+     do that.  */
+  if (TREE_SIDE_EFFECTS (valist))
+    expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  target = convert_to_mode (mode, (rtx) data, 1);
-  target = expand_mult (mode, target, coeff, NULL_RTX, 1);
-  return force_reg (mode, target);
+  return const0_rtx;
 }
 
-/* Expand expression EXP, which is a call to the memset builtin.  Return
-   NULL_RTX if we failed the caller should emit a normal call, otherwise
-   try to get the result in TARGET, if convenient (and in mode MODE if that's
-   convenient).  */
+/* Expand EXP, a call to __builtin_va_copy.  We do this as a
+   builtin rather than just as an assignment in stdarg.h because of the
+   nastiness of array-type va_list types.  */
 
 static rtx
-expand_builtin_memset (tree exp, rtx target, machine_mode mode)
+expand_builtin_va_copy (tree exp)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  tree dst, src, t;
+  location_t loc = EXPR_LOCATION (exp);
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree val = CALL_EXPR_ARG (exp, 1);
-  tree len = CALL_EXPR_ARG (exp, 2);
+  dst = CALL_EXPR_ARG (exp, 0);
+  src = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_access (exp, dest, NULL_TREE, len);
+  dst = stabilize_va_list_loc (loc, dst, 1);
+  src = stabilize_va_list_loc (loc, src, 0);
 
-  return expand_builtin_memset_args (dest, val, len, target, mode, exp);
-}
+  gcc_assert (cfun != NULL && cfun->decl != NULL_TREE);
 
-/* Try to store VAL (or, if NULL_RTX, VALC) in LEN bytes starting at TO.
-   Return TRUE if successful, FALSE otherwise.  TO is assumed to be
-   aligned at an ALIGN-bits boundary.  LEN must be a multiple of
-   1<<CTZ_LEN between MIN_LEN and MAX_LEN.
+  if (TREE_CODE (targetm.fn_abi_va_list (cfun->decl)) != ARRAY_TYPE)
+    {
+      t = build2 (MODIFY_EXPR, targetm.fn_abi_va_list (cfun->decl), dst, src);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+  else
+    {
+      rtx dstb, srcb, size;
 
-   The strategy is to issue one store_by_pieces for each power of two,
-   from most to least significant, guarded by a test on whether there
-   are at least that many bytes left to copy in LEN.
+      /* Evaluate to pointers.  */
+      dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL);
+      srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL);
+      size = expand_expr (TYPE_SIZE_UNIT (targetm.fn_abi_va_list (cfun->decl)),
+                 NULL_RTX, VOIDmode, EXPAND_NORMAL);
 
-   ??? Should we skip some powers of two in favor of loops?  Maybe start
-   at the max of TO/LEN/word alignment, at least when optimizing for
-   size, instead of ensuring O(log len) dynamic compares?  */
+      dstb = convert_memory_address (Pmode, dstb);
+      srcb = convert_memory_address (Pmode, srcb);
 
-bool
-try_store_by_multiple_pieces (rtx to, rtx len, unsigned int ctz_len,
-                             unsigned HOST_WIDE_INT min_len,
-                             unsigned HOST_WIDE_INT max_len,
-                             rtx val, char valc, unsigned int align)
-{
-  int max_bits = floor_log2 (max_len);
-  int min_bits = floor_log2 (min_len);
-  int sctz_len = ctz_len;
+      /* "Dereference" to BLKmode memories.  */
+      dstb = gen_rtx_MEM (BLKmode, dstb);
+      set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst))));
+      set_mem_align (dstb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
+      srcb = gen_rtx_MEM (BLKmode, srcb);
+      set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src))));
+      set_mem_align (srcb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
 
-  gcc_checking_assert (sctz_len >= 0);
+      /* Copy.  */
+      emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL);
+    }
 
-  if (val)
-    valc = 1;
+  return const0_rtx;
+}
 
-  /* Bits more significant than TST_BITS are part of the shared prefix
-     in the binary representation of both min_len and max_len.  Since
-     they're identical, we don't need to test them in the loop.  */
-  int tst_bits = (max_bits != min_bits ? max_bits
-                 : floor_log2 (max_len ^ min_len));
+/* Expand a call to one of the builtin functions __builtin_frame_address or
+   __builtin_return_address.  */
 
-  /* Check whether it's profitable to start by storing a fixed BLKSIZE
-     bytes, to lower max_bits.  In the unlikely case of a constant LEN
-     (implied by identical MAX_LEN and MIN_LEN), we want to issue a
-     single store_by_pieces, but otherwise, select the minimum multiple
-     of the ALIGN (in bytes) and of the MCD of the possible LENs, that
-     brings MAX_LEN below TST_BITS, if that's lower than min_len.  */
-  unsigned HOST_WIDE_INT blksize;
-  if (max_len > min_len)
+static rtx
+expand_builtin_frame_address (tree fndecl, tree exp)
+{
+  /* The argument must be a nonnegative integer constant.
+     It counts the number of frames to scan up the stack.
+     The value is either the frame pointer value or the return
+     address saved in that frame.  */
+  if (call_expr_nargs (exp) == 0)
+    /* Warning about missing arg was already issued.  */
+    return const0_rtx;
+  else if (! tree_fits_uhwi_p (CALL_EXPR_ARG (exp, 0)))
     {
-      unsigned HOST_WIDE_INT alrng = MAX (HOST_WIDE_INT_1U << ctz_len,
-                                         align / BITS_PER_UNIT);
-      blksize = max_len - (HOST_WIDE_INT_1U << tst_bits) + alrng;
-      blksize &= ~(alrng - 1);
+      error ("invalid argument to %qD", fndecl);
+      return const0_rtx;
     }
-  else if (max_len == min_len)
-    blksize = max_len;
   else
-    gcc_unreachable ();
-  if (min_len >= blksize)
     {
-      min_len -= blksize;
-      min_bits = floor_log2 (min_len);
-      max_len -= blksize;
-      max_bits = floor_log2 (max_len);
-
-      tst_bits = (max_bits != min_bits ? max_bits
-                : floor_log2 (max_len ^ min_len));
-    }
-  else
-    blksize = 0;
+      /* Number of frames to scan up the stack.  */
+      unsigned HOST_WIDE_INT count = tree_to_uhwi (CALL_EXPR_ARG (exp, 0));
 
-  /* Check that we can use store by pieces for the maximum store count
-     we may issue (initial fixed-size block, plus conditional
-     power-of-two-sized from max_bits to ctz_len.  */
-  unsigned HOST_WIDE_INT xlenest = blksize;
-  if (max_bits >= 0)
-    xlenest += ((HOST_WIDE_INT_1U << max_bits) * 2
-               - (HOST_WIDE_INT_1U << ctz_len));
-  if (!can_store_by_pieces (xlenest, builtin_memset_read_str,
-                           &valc, align, true))
-    return false;
+      rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), count);
 
-  rtx (*constfun) (void *, void *, HOST_WIDE_INT, scalar_int_mode);
-  void *constfundata;
-  if (val)
-    {
-      constfun = builtin_memset_gen_str;
-      constfundata = val = force_reg (TYPE_MODE (unsigned_char_type_node),
-                                     val);
-    }
-  else
-    {
-      constfun = builtin_memset_read_str;
-      constfundata = &valc;
-    }
+      /* Some ports cannot access arbitrary stack frames.  */
+      if (tem == NULL)
+       {
+         warning (0, "unsupported argument to %qD", fndecl);
+         return const0_rtx;
+       }
 
-  rtx ptr = copy_addr_to_reg (convert_to_mode (ptr_mode, XEXP (to, 0), 0));
-  rtx rem = copy_to_mode_reg (ptr_mode, convert_to_mode (ptr_mode, len, 0));
-  to = replace_equiv_address (to, ptr);
-  set_mem_align (to, align);
+      if (count)
+       {
+         /* Warn since no effort is made to ensure that any frame
+            beyond the current one exists or can be safely reached.  */
+         warning (OPT_Wframe_address, "calling %qD with "
+                  "a nonzero argument is unsafe", fndecl);
+       }
 
-  if (blksize)
-    {
-      to = store_by_pieces (to, blksize,
-                           constfun, constfundata,
-                           align, true,
-                           max_len != 0 ? RETURN_END : RETURN_BEGIN);
-      if (max_len == 0)
-       return true;
+      /* For __builtin_frame_address, return what we've got.  */
+      if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
+       return tem;
 
-      /* Adjust PTR, TO and REM.  Since TO's address is likely
-        PTR+offset, we have to replace it.  */
-      emit_move_insn (ptr, force_operand (XEXP (to, 0), NULL_RTX));
-      to = replace_equiv_address (to, ptr);
-      rtx rem_minus_blksize = plus_constant (ptr_mode, rem, -blksize);
-      emit_move_insn (rem, force_operand (rem_minus_blksize, NULL_RTX));
+      if (!REG_P (tem)
+         && ! CONSTANT_P (tem))
+       tem = copy_addr_to_reg (tem);
+      return tem;
     }
+}
 
-  /* Iterate over power-of-two block sizes from the maximum length to
-     the least significant bit possibly set in the length.  */
-  for (int i = max_bits; i >= sctz_len; i--)
-    {
-      rtx_code_label *label = NULL;
-      blksize = HOST_WIDE_INT_1U << i;
+/* Expand EXP, a call to the alloca builtin.  Return NULL_RTX if we
+   failed and the caller should emit a normal call.  */
 
-      /* If we're past the bits shared between min_ and max_len, expand
-        a test on the dynamic length, comparing it with the
-        BLKSIZE.  */
-      if (i <= tst_bits)
-       {
-         label = gen_label_rtx ();
-         emit_cmp_and_jump_insns (rem, GEN_INT (blksize), LT, NULL,
-                                  ptr_mode, 1, label,
-                                  profile_probability::even ());
-       }
-      /* If we are at a bit that is in the prefix shared by min_ and
-        max_len, skip this BLKSIZE if the bit is clear.  */
-      else if ((max_len & blksize) == 0)
-       continue;
+static rtx
+expand_builtin_alloca (tree exp)
+{
+  rtx op0;
+  rtx result;
+  unsigned int align;
+  tree fndecl = get_callee_fndecl (exp);
+  HOST_WIDE_INT max_size;
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  bool alloca_for_var = CALL_ALLOCA_FOR_VAR_P (exp);
+  bool valid_arglist
+    = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
+       ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, INTEGER_TYPE,
+                          VOID_TYPE)
+       : fcode == BUILT_IN_ALLOCA_WITH_ALIGN
+        ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
+        : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
 
-      /* Issue a store of BLKSIZE bytes.  */
-      to = store_by_pieces (to, blksize,
-                           constfun, constfundata,
-                           align, true,
-                           i != sctz_len ? RETURN_END : RETURN_BEGIN);
+  if (!valid_arglist)
+    return NULL_RTX;
 
-      /* Adjust REM and PTR, unless this is the last iteration.  */
-      if (i != sctz_len)
-       {
-         emit_move_insn (ptr, force_operand (XEXP (to, 0), NULL_RTX));
-         to = replace_equiv_address (to, ptr);
-         rtx rem_minus_blksize = plus_constant (ptr_mode, rem, -blksize);
-         emit_move_insn (rem, force_operand (rem_minus_blksize, NULL_RTX));
-       }
+  if ((alloca_for_var
+       && warn_vla_limit >= HOST_WIDE_INT_MAX
+       && warn_alloc_size_limit < warn_vla_limit)
+      || (!alloca_for_var
+         && warn_alloca_limit >= HOST_WIDE_INT_MAX
+         && warn_alloc_size_limit < warn_alloca_limit
+         ))
+    {
+      /* -Walloca-larger-than and -Wvla-larger-than settings of
+        less than HOST_WIDE_INT_MAX override the more general
+        -Walloc-size-larger-than so unless either of the former
+        options is smaller than the last one (wchich would imply
+        that the call was already checked), check the alloca
+        arguments for overflow.  */
+      tree args[] = { CALL_EXPR_ARG (exp, 0), NULL_TREE };
+      int idx[] = { 0, -1 };
+      maybe_warn_alloc_args_overflow (fndecl, exp, args, idx);
+    }
 
-      if (label)
-       {
-         emit_label (label);
+  /* Compute the argument.  */
+  op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
 
-         /* Given conditional stores, the offset can no longer be
-            known, so clear it.  */
-         clear_mem_offset (to);
-       }
-    }
+  /* Compute the alignment.  */
+  align = (fcode == BUILT_IN_ALLOCA
+          ? BIGGEST_ALIGNMENT
+          : TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1)));
 
-  return true;
+  /* Compute the maximum size.  */
+  max_size = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
+              ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 2))
+              : -1);
+
+  /* Allocate the desired space.  If the allocation stems from the declaration
+     of a variable-sized object, it cannot accumulate.  */
+  result
+    = allocate_dynamic_stack_space (op0, 0, align, max_size, alloca_for_var);
+  result = convert_memory_address (ptr_mode, result);
+
+  /* Dynamic allocations for variables are recorded during gimplification.  */
+  if (!alloca_for_var && (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC))
+    record_dynamic_alloc (exp);
+
+  return result;
 }
 
-/* Helper function to do the actual work for expand_builtin_memset.  The
-   arguments to the builtin_memset call DEST, VAL, and LEN are broken out
-   so that this can also be called without constructing an actual CALL_EXPR.
-   The other arguments and return value are the same as for
-   expand_builtin_memset.  */
+/* Emit a call to __asan_allocas_unpoison call in EXP.  Add to second argument
+   of the call virtual_stack_dynamic_rtx - stack_pointer_rtx, which is the
+   STACK_DYNAMIC_OFFSET value.  See motivation for this in comment to
+   handle_builtin_stack_restore function.  */
 
 static rtx
-expand_builtin_memset_args (tree dest, tree val, tree len,
-                           rtx target, machine_mode mode, tree orig_exp)
+expand_asan_emit_allocas_unpoison (tree exp)
 {
-  tree fndecl, fn;
-  enum built_in_function fcode;
-  machine_mode val_mode;
-  char c;
-  unsigned int dest_align;
-  rtx dest_mem, dest_addr, len_rtx;
-  HOST_WIDE_INT expected_size = -1;
-  unsigned int expected_align = 0;
-  unsigned HOST_WIDE_INT min_size;
-  unsigned HOST_WIDE_INT max_size;
-  unsigned HOST_WIDE_INT probable_max_size;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  rtx top = expand_expr (arg0, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+  rtx bot = expand_expr (arg1, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+  rtx off = expand_simple_binop (Pmode, MINUS, virtual_stack_dynamic_rtx,
+                                stack_pointer_rtx, NULL_RTX, 0,
+                                OPTAB_LIB_WIDEN);
+  off = convert_modes (ptr_mode, Pmode, off, 0);
+  bot = expand_simple_binop (ptr_mode, PLUS, bot, off, NULL_RTX, 0,
+                            OPTAB_LIB_WIDEN);
+  rtx ret = init_one_libfunc ("__asan_allocas_unpoison");
+  ret = emit_library_call_value (ret, NULL_RTX, LCT_NORMAL, ptr_mode,
+                                top, ptr_mode, bot, ptr_mode);
+  return ret;
+}
 
-  dest_align = get_pointer_alignment (dest);
+/* Expand a call to bswap builtin in EXP.
+   Return NULL_RTX if a normal call should be emitted rather than expanding the
+   function in-line.  If convenient, the result should be placed in TARGET.
+   SUBTARGET may be used as the target for computing one of EXP's operands.  */
 
-  /* If DEST is not a pointer type, don't do this operation in-line.  */
-  if (dest_align == 0)
+static rtx
+expand_builtin_bswap (machine_mode target_mode, tree exp, rtx target,
+                     rtx subtarget)
+{
+  tree arg;
+  rtx op0;
+
+  if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  if (currently_expanding_gimple_stmt)
-    stringop_block_profile (currently_expanding_gimple_stmt,
-                           &expected_align, &expected_size);
+  arg = CALL_EXPR_ARG (exp, 0);
+  op0 = expand_expr (arg,
+                    subtarget && GET_MODE (subtarget) == target_mode
+                    ? subtarget : NULL_RTX,
+                    target_mode, EXPAND_NORMAL);
+  if (GET_MODE (op0) != target_mode)
+    op0 = convert_to_mode (target_mode, op0, 1);
 
-  if (expected_align < dest_align)
-    expected_align = dest_align;
+  target = expand_unop (target_mode, bswap_optab, op0, target, 1);
 
-  /* If the LEN parameter is zero, return DEST.  */
-  if (integer_zerop (len))
-    {
-      /* Evaluate and ignore VAL in case it has side-effects.  */
-      expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
-      return expand_expr (dest, target, mode, EXPAND_NORMAL);
-    }
+  gcc_assert (target);
 
-  /* Stabilize the arguments in case we fail.  */
-  dest = builtin_save_expr (dest);
-  val = builtin_save_expr (val);
-  len = builtin_save_expr (len);
+  return convert_to_mode (target_mode, target, 1);
+}
 
-  len_rtx = expand_normal (len);
-  determine_block_size (len, len_rtx, &min_size, &max_size,
-                       &probable_max_size);
-  dest_mem = get_memory_rtx (dest, len);
-  val_mode = TYPE_MODE (unsigned_char_type_node);
+/* Expand a call to a unary builtin in EXP.
+   Return NULL_RTX if a normal call should be emitted rather than expanding the
+   function in-line.  If convenient, the result should be placed in TARGET.
+   SUBTARGET may be used as the target for computing one of EXP's operands.  */
 
-  if (TREE_CODE (val) != INTEGER_CST
-      || target_char_cast (val, &c))
-    {
-      rtx val_rtx;
+static rtx
+expand_builtin_unop (machine_mode target_mode, tree exp, rtx target,
+                    rtx subtarget, optab op_optab)
+{
+  rtx op0;
 
-      val_rtx = expand_normal (val);
-      val_rtx = convert_to_mode (val_mode, val_rtx, 0);
+  if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-      /* Assume that we can memset by pieces if we can store
-       * the coefficients by pieces (in the required modes).
-       * We can't pass builtin_memset_gen_str as that emits RTL.  */
-      c = 1;
-      if (tree_fits_uhwi_p (len)
-         && can_store_by_pieces (tree_to_uhwi (len),
-                                 builtin_memset_read_str, &c, dest_align,
-                                 true))
-       {
-         val_rtx = force_reg (val_mode, val_rtx);
-         store_by_pieces (dest_mem, tree_to_uhwi (len),
-                          builtin_memset_gen_str, val_rtx, dest_align,
-                          true, RETURN_BEGIN);
-       }
-      else if (!set_storage_via_setmem (dest_mem, len_rtx, val_rtx,
-                                       dest_align, expected_align,
-                                       expected_size, min_size, max_size,
-                                       probable_max_size)
-              && !try_store_by_multiple_pieces (dest_mem, len_rtx,
-                                                tree_ctz (len),
-                                                min_size, max_size,
-                                                val_rtx, 0,
-                                                dest_align))
-       goto do_libcall;
+  /* Compute the argument.  */
+  op0 = expand_expr (CALL_EXPR_ARG (exp, 0),
+                    (subtarget
+                     && (TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 0)))
+                         == GET_MODE (subtarget))) ? subtarget : NULL_RTX,
+                    VOIDmode, EXPAND_NORMAL);
+  /* Compute op, into TARGET if possible.
+     Set TARGET to wherever the result comes back.  */
+  target = expand_unop (TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 0))),
+                       op_optab, op0, target, op_optab != clrsb_optab);
+  gcc_assert (target);
 
-      dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-      dest_mem = convert_memory_address (ptr_mode, dest_mem);
-      return dest_mem;
-    }
+  return convert_to_mode (target_mode, target, 0);
+}
 
-  if (c)
-    {
-      if (tree_fits_uhwi_p (len)
-         && can_store_by_pieces (tree_to_uhwi (len),
-                                 builtin_memset_read_str, &c, dest_align,
-                                 true))
-       store_by_pieces (dest_mem, tree_to_uhwi (len),
-                        builtin_memset_read_str, &c, dest_align, true,
-                        RETURN_BEGIN);
-      else if (!set_storage_via_setmem (dest_mem, len_rtx,
-                                       gen_int_mode (c, val_mode),
-                                       dest_align, expected_align,
-                                       expected_size, min_size, max_size,
-                                       probable_max_size)
-              && !try_store_by_multiple_pieces (dest_mem, len_rtx,
-                                                tree_ctz (len),
-                                                min_size, max_size,
-                                                NULL_RTX, c,
-                                                dest_align))
-       goto do_libcall;
+/* Expand a call to __builtin_expect.  We just return our argument
+   as the builtin_expect semantic should've been already executed by
+   tree branch prediction pass. */
 
-      dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-      dest_mem = convert_memory_address (ptr_mode, dest_mem);
-      return dest_mem;
-    }
+static rtx
+expand_builtin_expect (tree exp, rtx target)
+{
+  tree arg;
 
-  set_mem_align (dest_mem, dest_align);
-  dest_addr = clear_storage_hints (dest_mem, len_rtx,
-                                  CALL_EXPR_TAILCALL (orig_exp)
-                                  ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL,
-                                  expected_align, expected_size,
-                                  min_size, max_size,
-                                  probable_max_size, tree_ctz (len));
+  if (call_expr_nargs (exp) < 2)
+    return const0_rtx;
+  arg = CALL_EXPR_ARG (exp, 0);
 
-  if (dest_addr == 0)
-    {
-      dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-      dest_addr = convert_memory_address (ptr_mode, dest_addr);
-    }
+  target = expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
+  /* When guessing was done, the hints should be already stripped away.  */
+  gcc_assert (!flag_guess_branch_prob
+             || optimize == 0 || seen_error ());
+  return target;
+}
 
-  return dest_addr;
+/* Expand a call to __builtin_expect_with_probability.  We just return our
+   argument as the builtin_expect semantic should've been already executed by
+   tree branch prediction pass.  */
 
- do_libcall:
-  fndecl = get_callee_fndecl (orig_exp);
-  fcode = DECL_FUNCTION_CODE (fndecl);
-  if (fcode == BUILT_IN_MEMSET)
-    fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 3,
-                               dest, val, len);
-  else if (fcode == BUILT_IN_BZERO)
-    fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 2,
-                               dest, len);
-  else
-    gcc_unreachable ();
-  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
-  return expand_call (fn, target, target == const0_rtx);
+static rtx
+expand_builtin_expect_with_probability (tree exp, rtx target)
+{
+  tree arg;
+
+  if (call_expr_nargs (exp) < 3)
+    return const0_rtx;
+  arg = CALL_EXPR_ARG (exp, 0);
+
+  target = expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
+  /* When guessing was done, the hints should be already stripped away.  */
+  gcc_assert (!flag_guess_branch_prob
+             || optimize == 0 || seen_error ());
+  return target;
 }
 
-/* Expand expression EXP, which is a call to the bzero builtin.  Return
-   NULL_RTX if we failed the caller should emit a normal call.  */
+
+/* Expand a call to __builtin_assume_aligned.  We just return our first
+   argument as the builtin_assume_aligned semantic should've been already
+   executed by CCP.  */
 
 static rtx
-expand_builtin_bzero (tree exp)
+expand_builtin_assume_aligned (tree exp, rtx target)
 {
-  if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
-
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree size = CALL_EXPR_ARG (exp, 1);
+  if (call_expr_nargs (exp) < 2)
+    return const0_rtx;
+  target = expand_expr (CALL_EXPR_ARG (exp, 0), target, VOIDmode,
+                       EXPAND_NORMAL);
+  gcc_assert (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 1))
+             && (call_expr_nargs (exp) < 3
+                 || !TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 2))));
+  return target;
+}
 
-  check_memop_access (exp, dest, NULL_TREE, size);
+void
+expand_builtin_trap (void)
+{
+  if (targetm.have_trap ())
+    {
+      rtx_insn *insn = emit_insn (targetm.gen_trap ());
+      /* For trap insns when not accumulating outgoing args force
+        REG_ARGS_SIZE note to prevent crossjumping of calls with
+        different args sizes.  */
+      if (!ACCUMULATE_OUTGOING_ARGS)
+       add_args_size_note (insn, stack_pointer_delta);
+    }
+  else
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_ABORT);
+      tree call_expr = build_call_expr (fn, 0);
+      expand_call (call_expr, NULL_RTX, false);
+    }
 
-  /* New argument list transforming bzero(ptr x, int y) to
-     memset(ptr x, int 0, size_t y).   This is done this way
-     so that if it isn't expanded inline, we fallback to
-     calling bzero instead of memset.  */
+  emit_barrier ();
+}
 
-  location_t loc = EXPR_LOCATION (exp);
+/* Expand a call to __builtin_unreachable.  We do nothing except emit
+   a barrier saying that control flow will not pass here.
 
-  return expand_builtin_memset_args (dest, integer_zero_node,
-                                    fold_convert_loc (loc,
-                                                      size_type_node, size),
-                                    const0_rtx, VOIDmode, exp);
+   It is the responsibility of the program being compiled to ensure
+   that control flow does never reach __builtin_unreachable.  */
+static void
+expand_builtin_unreachable (void)
+{
+  emit_barrier ();
 }
 
-/* Try to expand cmpstr operation ICODE with the given operands.
-   Return the result rtx on success, otherwise return null.  */
+/* Expand EXP, a call to fabs, fabsf or fabsl.
+   Return NULL_RTX if a normal call should be emitted rather than expanding
+   the function inline.  If convenient, the result should be placed
+   in TARGET.  SUBTARGET may be used as the target for computing
+   the operand.  */
 
 static rtx
-expand_cmpstr (insn_code icode, rtx target, rtx arg1_rtx, rtx arg2_rtx,
-              HOST_WIDE_INT align)
+expand_builtin_fabs (tree exp, rtx target, rtx subtarget)
 {
-  machine_mode insn_mode = insn_data[icode].operand[0].mode;
+  machine_mode mode;
+  tree arg;
+  rtx op0;
 
-  if (target && (!REG_P (target) || HARD_REGISTER_P (target)))
-    target = NULL_RTX;
+  if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  class expand_operand ops[4];
-  create_output_operand (&ops[0], target, insn_mode);
-  create_fixed_operand (&ops[1], arg1_rtx);
-  create_fixed_operand (&ops[2], arg2_rtx);
-  create_integer_operand (&ops[3], align);
-  if (maybe_expand_insn (icode, 4, ops))
-    return ops[0].value;
-  return NULL_RTX;
+  arg = CALL_EXPR_ARG (exp, 0);
+  CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
+  mode = TYPE_MODE (TREE_TYPE (arg));
+  op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
+  return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
 }
 
-/* Expand expression EXP, which is a call to the memcmp built-in function.
-   Return NULL_RTX if we failed and the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient.
-   RESULT_EQ is true if we can relax the returned value to be either zero
-   or nonzero, without caring about the sign.  */
+/* Expand EXP, a call to copysign, copysignf, or copysignl.
+   Return NULL is a normal call should be emitted rather than expanding the
+   function inline.  If convenient, the result should be placed in TARGET.
+   SUBTARGET may be used as the target for computing the operand.  */
 
 static rtx
-expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
+expand_builtin_copysign (tree exp, rtx target, rtx subtarget)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+  rtx op0, op1;
+  tree arg;
+
+  if (!validate_arglist (exp, REAL_TYPE, REAL_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  tree arg1 = CALL_EXPR_ARG (exp, 0);
-  tree arg2 = CALL_EXPR_ARG (exp, 1);
-  tree len = CALL_EXPR_ARG (exp, 2);
+  arg = CALL_EXPR_ARG (exp, 0);
+  op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
 
-  /* Diagnose calls where the specified length exceeds the size of either
-     object.  */
-  if (!check_read_access (exp, arg1, len, 0)
-      || !check_read_access (exp, arg2, len, 0))
-    return NULL_RTX;
+  arg = CALL_EXPR_ARG (exp, 1);
+  op1 = expand_normal (arg);
 
-  /* Due to the performance benefit, always inline the calls first
-     when result_eq is false.  */
-  rtx result = NULL_RTX;
-  enum built_in_function fcode = DECL_FUNCTION_CODE (get_callee_fndecl (exp));
-  if (!result_eq && fcode != BUILT_IN_BCMP)
-    {
-      result = inline_expand_builtin_bytecmp (exp, target);
-      if (result)
-       return result;
-    }
+  return expand_copysign (op0, op1, target);
+}
 
-  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-  location_t loc = EXPR_LOCATION (exp);
+/* Emit a call to __builtin___clear_cache.  */
 
-  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
-  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
+void
+default_emit_call_builtin___clear_cache (rtx begin, rtx end)
+{
+  rtx callee = gen_rtx_SYMBOL_REF (Pmode,
+                                  BUILTIN_ASM_NAME_PTR
+                                  (BUILT_IN_CLEAR_CACHE));
 
-  /* If we don't have POINTER_TYPE, call the function.  */
-  if (arg1_align == 0 || arg2_align == 0)
-    return NULL_RTX;
+  emit_library_call (callee,
+                    LCT_NORMAL, VOIDmode,
+                    convert_memory_address (ptr_mode, begin), ptr_mode,
+                    convert_memory_address (ptr_mode, end), ptr_mode);
+}
 
-  rtx arg1_rtx = get_memory_rtx (arg1, len);
-  rtx arg2_rtx = get_memory_rtx (arg2, len);
-  rtx len_rtx = expand_normal (fold_convert_loc (loc, sizetype, len));
+/* Emit a call to __builtin___clear_cache, unless the target specifies
+   it as do-nothing.  This function can be used by trampoline
+   finalizers to duplicate the effects of expanding a call to the
+   clear_cache builtin.  */
 
-  /* Set MEM_SIZE as appropriate.  */
-  if (CONST_INT_P (len_rtx))
+void
+maybe_emit_call_builtin___clear_cache (rtx begin, rtx end)
+{
+  if ((GET_MODE (begin) != ptr_mode && GET_MODE (begin) != Pmode)
+      || (GET_MODE (end) != ptr_mode && GET_MODE (end) != Pmode))
     {
-      set_mem_size (arg1_rtx, INTVAL (len_rtx));
-      set_mem_size (arg2_rtx, INTVAL (len_rtx));
+      error ("both arguments to %<__builtin___clear_cache%> must be pointers");
+      return;
     }
 
-  by_pieces_constfn constfn = NULL;
+  if (targetm.have_clear_cache ())
+    {
+      /* We have a "clear_cache" insn, and it will handle everything.  */
+      class expand_operand ops[2];
 
-  /* Try to get the byte representation of the constant ARG2 (or, only
-     when the function's result is used for equality to zero, ARG1)
-     points to, with its byte size in NBYTES.  */
-  unsigned HOST_WIDE_INT nbytes;
-  const char *rep = getbyterep (arg2, &nbytes);
-  if (result_eq && rep == NULL)
+      create_address_operand (&ops[0], begin);
+      create_address_operand (&ops[1], end);
+
+      if (maybe_expand_insn (targetm.code_for_clear_cache, 2, ops))
+       return;
+    }
+  else
     {
-      /* For equality to zero the arguments are interchangeable.  */
-      rep = getbyterep (arg1, &nbytes);
-      if (rep != NULL)
-       std::swap (arg1_rtx, arg2_rtx);
+#ifndef CLEAR_INSN_CACHE
+      /* There is no "clear_cache" insn, and __clear_cache() in libgcc
+        does nothing.  There is no need to call it.  Do nothing.  */
+      return;
+#endif /* CLEAR_INSN_CACHE */
     }
 
-  /* If the function's constant bound LEN_RTX is less than or equal
-     to the byte size of the representation of the constant argument,
-     and if block move would be done by pieces, we can avoid loading
-     the bytes from memory and only store the computed constant result.  */
-  if (rep
-      && CONST_INT_P (len_rtx)
-      && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= nbytes)
-    constfn = builtin_memcpy_read_str;
+  targetm.calls.emit_call_builtin___clear_cache (begin, end);
+}
 
-  result = emit_block_cmp_hints (arg1_rtx, arg2_rtx, len_rtx,
-                                TREE_TYPE (len), target,
-                                result_eq, constfn,
-                                CONST_CAST (char *, rep));
+/* Expand a call to __builtin___clear_cache.  */
 
-  if (result)
+static void
+expand_builtin___clear_cache (tree exp)
+{
+  tree begin, end;
+  rtx begin_rtx, end_rtx;
+
+  /* We must not expand to a library call.  If we did, any
+     fallback library function in libgcc that might contain a call to
+     __builtin___clear_cache() would recurse infinitely.  */
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     {
-      /* Return the value in the proper mode for this function.  */
-      if (GET_MODE (result) == mode)
-       return result;
+      error ("both arguments to %<__builtin___clear_cache%> must be pointers");
+      return;
+    }
 
-      if (target != 0)
-       {
-         convert_move (target, result, 0);
-         return target;
-       }
+  begin = CALL_EXPR_ARG (exp, 0);
+  begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL);
 
-      return convert_to_mode (mode, result, 0);
-    }
+  end = CALL_EXPR_ARG (exp, 1);
+  end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL);
 
-  return NULL_RTX;
+  maybe_emit_call_builtin___clear_cache (begin_rtx, end_rtx);
 }
 
-/* Expand expression EXP, which is a call to the strcmp builtin.  Return NULL_RTX
-   if we failed the caller should emit a normal call, otherwise try to get
-   the result in TARGET, if convenient.  */
+/* Given a trampoline address, make sure it satisfies TRAMPOLINE_ALIGNMENT.  */
 
 static rtx
-expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
+round_trampoline_addr (rtx tramp)
 {
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  rtx temp, addend, mask;
 
-  tree arg1 = CALL_EXPR_ARG (exp, 0);
-  tree arg2 = CALL_EXPR_ARG (exp, 1);
+  /* If we don't need too much alignment, we'll have been guaranteed
+     proper alignment by get_trampoline_type.  */
+  if (TRAMPOLINE_ALIGNMENT <= STACK_BOUNDARY)
+    return tramp;
 
-  if (!check_read_access (exp, arg1)
-      || !check_read_access (exp, arg2))
-    return NULL_RTX;
+  /* Round address up to desired boundary.  */
+  temp = gen_reg_rtx (Pmode);
+  addend = gen_int_mode (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1, Pmode);
+  mask = gen_int_mode (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT, Pmode);
 
-  /* Due to the performance benefit, always inline the calls first.  */
-  rtx result = NULL_RTX;
-  result = inline_expand_builtin_bytecmp (exp, target);
-  if (result)
-    return result;
+  temp  = expand_simple_binop (Pmode, PLUS, tramp, addend,
+                              temp, 0, OPTAB_LIB_WIDEN);
+  tramp = expand_simple_binop (Pmode, AND, temp, mask,
+                              temp, 0, OPTAB_LIB_WIDEN);
 
-  insn_code cmpstr_icode = direct_optab_handler (cmpstr_optab, SImode);
-  insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
-  if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing)
-    return NULL_RTX;
+  return tramp;
+}
 
-  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
-  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
+static rtx
+expand_builtin_init_trampoline (tree exp, bool onstack)
+{
+  tree t_tramp, t_func, t_chain;
+  rtx m_tramp, r_tramp, r_chain, tmp;
 
-  /* If we don't have POINTER_TYPE, call the function.  */
-  if (arg1_align == 0 || arg2_align == 0)
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE,
+                        POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  /* Stabilize the arguments in case gen_cmpstr(n)si fail.  */
-  arg1 = builtin_save_expr (arg1);
-  arg2 = builtin_save_expr (arg2);
+  t_tramp = CALL_EXPR_ARG (exp, 0);
+  t_func = CALL_EXPR_ARG (exp, 1);
+  t_chain = CALL_EXPR_ARG (exp, 2);
 
-  rtx arg1_rtx = get_memory_rtx (arg1, NULL);
-  rtx arg2_rtx = get_memory_rtx (arg2, NULL);
+  r_tramp = expand_normal (t_tramp);
+  m_tramp = gen_rtx_MEM (BLKmode, r_tramp);
+  MEM_NOTRAP_P (m_tramp) = 1;
 
-  /* Try to call cmpstrsi.  */
-  if (cmpstr_icode != CODE_FOR_nothing)
-    result = expand_cmpstr (cmpstr_icode, target, arg1_rtx, arg2_rtx,
-                           MIN (arg1_align, arg2_align));
+  /* If ONSTACK, the TRAMP argument should be the address of a field
+     within the local function's FRAME decl.  Either way, let's see if
+     we can fill in the MEM_ATTRs for this memory.  */
+  if (TREE_CODE (t_tramp) == ADDR_EXPR)
+    set_mem_attributes (m_tramp, TREE_OPERAND (t_tramp, 0), true);
 
-  /* Try to determine at least one length and call cmpstrnsi.  */
-  if (!result && cmpstrn_icode != CODE_FOR_nothing)
+  /* Creator of a heap trampoline is responsible for making sure the
+     address is aligned to at least STACK_BOUNDARY.  Normally malloc
+     will ensure this anyhow.  */
+  tmp = round_trampoline_addr (r_tramp);
+  if (tmp != r_tramp)
     {
-      tree len;
-      rtx arg3_rtx;
-
-      tree len1 = c_strlen (arg1, 1);
-      tree len2 = c_strlen (arg2, 1);
-
-      if (len1)
-       len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
-      if (len2)
-       len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
+      m_tramp = change_address (m_tramp, BLKmode, tmp);
+      set_mem_align (m_tramp, TRAMPOLINE_ALIGNMENT);
+      set_mem_size (m_tramp, TRAMPOLINE_SIZE);
+    }
 
-      /* If we don't have a constant length for the first, use the length
-        of the second, if we know it.  We don't require a constant for
-        this case; some cost analysis could be done if both are available
-        but neither is constant.  For now, assume they're equally cheap,
-        unless one has side effects.  If both strings have constant lengths,
-        use the smaller.  */
+  /* The FUNC argument should be the address of the nested function.
+     Extract the actual function decl to pass to the hook.  */
+  gcc_assert (TREE_CODE (t_func) == ADDR_EXPR);
+  t_func = TREE_OPERAND (t_func, 0);
+  gcc_assert (TREE_CODE (t_func) == FUNCTION_DECL);
 
-      if (!len1)
-       len = len2;
-      else if (!len2)
-       len = len1;
-      else if (TREE_SIDE_EFFECTS (len1))
-       len = len2;
-      else if (TREE_SIDE_EFFECTS (len2))
-       len = len1;
-      else if (TREE_CODE (len1) != INTEGER_CST)
-       len = len2;
-      else if (TREE_CODE (len2) != INTEGER_CST)
-       len = len1;
-      else if (tree_int_cst_lt (len1, len2))
-       len = len1;
-      else
-       len = len2;
+  r_chain = expand_normal (t_chain);
 
-      /* If both arguments have side effects, we cannot optimize.  */
-      if (len && !TREE_SIDE_EFFECTS (len))
-       {
-         arg3_rtx = expand_normal (len);
-         result = expand_cmpstrn_or_cmpmem
-           (cmpstrn_icode, target, arg1_rtx, arg2_rtx, TREE_TYPE (len),
-            arg3_rtx, MIN (arg1_align, arg2_align));
-       }
-    }
+  /* Generate insns to initialize the trampoline.  */
+  targetm.calls.trampoline_init (m_tramp, t_func, r_chain);
 
-  tree fndecl = get_callee_fndecl (exp);
-  if (result)
+  if (onstack)
     {
-      /* Check to see if the argument was declared attribute nonstring
-        and if so, issue a warning since at this point it's not known
-        to be nul-terminated.  */
-      maybe_warn_nonstring_arg (fndecl, exp);
+      trampolines_created = 1;
 
-      /* Return the value in the proper mode for this function.  */
-      machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-      if (GET_MODE (result) == mode)
-       return result;
-      if (target == 0)
-       return convert_to_mode (mode, result, 0);
-      convert_move (target, result, 0);
-      return target;
+      if (targetm.calls.custom_function_descriptors != 0)
+       warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
+                   "trampoline generated for nested function %qD", t_func);
     }
 
-  /* Expand the library call ourselves using a stabilized argument
-     list to avoid re-evaluating the function's arguments twice.  */
-  tree fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg1, arg2);
-  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-  return expand_call (fn, target, target == const0_rtx);
+  return const0_rtx;
 }
 
-/* Expand expression EXP, which is a call to the strncmp builtin. Return
-   NULL_RTX if we failed the caller should emit a normal call, otherwise
-   try to get the result in TARGET, if convenient.  */
-
 static rtx
-expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
-                       ATTRIBUTE_UNUSED machine_mode mode)
+expand_builtin_adjust_trampoline (tree exp)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
-
-  tree arg1 = CALL_EXPR_ARG (exp, 0);
-  tree arg2 = CALL_EXPR_ARG (exp, 1);
-  tree arg3 = CALL_EXPR_ARG (exp, 2);
+  rtx tramp;
 
-  if (!check_nul_terminated_array (exp, arg1, arg3)
-      || !check_nul_terminated_array (exp, arg2, arg3))
+  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  location_t loc = EXPR_LOCATION (exp);
-  tree len1 = c_strlen (arg1, 1);
-  tree len2 = c_strlen (arg2, 1);
-
-  if (!len1 || !len2)
-    {
-      /* Check to see if the argument was declared attribute nonstring
-        and if so, issue a warning since at this point it's not known
-        to be nul-terminated.  */
-      if (!maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp)
-         && !len1 && !len2)
-       {
-         /* A strncmp read is constrained not just by the bound but
-            also by the length of the shorter string.  Specifying
-            a bound that's larger than the size of either array makes
-            no sense and is likely a bug.  When the length of neither
-            of the two strings is known but the sizes of both of
-            the arrays they are stored in is, issue a warning if
-            the bound is larger than than the size of the larger
-            of the two arrays.  */
+  tramp = expand_normal (CALL_EXPR_ARG (exp, 0));
+  tramp = round_trampoline_addr (tramp);
+  if (targetm.calls.trampoline_adjust_address)
+    tramp = targetm.calls.trampoline_adjust_address (tramp);
 
-         access_ref ref1 (arg3, true);
-         access_ref ref2 (arg3, true);
+  return tramp;
+}
 
-         tree bndrng[2] = { NULL_TREE, NULL_TREE };
-         get_size_range (arg3, bndrng, ref1.bndrng);
+/* Expand a call to the builtin descriptor initialization routine.
+   A descriptor is made up of a couple of pointers to the static
+   chain and the code entry in this order.  */
 
-         tree size1 = compute_objsize (arg1, 1, &ref1);
-         tree size2 = compute_objsize (arg2, 1, &ref2);
-         tree func = get_callee_fndecl (exp);
+static rtx
+expand_builtin_init_descriptor (tree exp)
+{
+  tree t_descr, t_func, t_chain;
+  rtx m_descr, r_descr, r_func, r_chain;
 
-         if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
-           {
-             offset_int rem1 = ref1.size_remaining ();
-             offset_int rem2 = ref2.size_remaining ();
-             if (rem1 == 0 || rem2 == 0)
-               maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
-                                     bndrng, integer_zero_node);
-             else
-               {
-                 offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
-                 if (maxrem < wi::to_offset (bndrng[0]))
-                   maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
-                                         func, bndrng,
-                                         wide_int_to_tree (sizetype, maxrem));
-               }
-           }
-         else if (bndrng[0]
-                  && !integer_zerop (bndrng[0])
-                  && ((size1 && integer_zerop (size1))
-                      || (size2 && integer_zerop (size2))))
-           maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
-                                 bndrng, integer_zero_node);
-       }
-    }
-
-  /* Due to the performance benefit, always inline the calls first.  */
-  rtx result = NULL_RTX;
-  result = inline_expand_builtin_bytecmp (exp, target);
-  if (result)
-    return result;
-
-  /* If c_strlen can determine an expression for one of the string
-     lengths, and it doesn't have side effects, then emit cmpstrnsi
-     using length MIN(strlen(string)+1, arg3).  */
-  insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
-  if (cmpstrn_icode == CODE_FOR_nothing)
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
+                        VOID_TYPE))
     return NULL_RTX;
 
-  tree len;
-
-  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
-  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
-
-  if (len1)
-    len1 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len1);
-  if (len2)
-    len2 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len2);
-
-  tree len3 = fold_convert_loc (loc, sizetype, arg3);
-
-  /* If we don't have a constant length for the first, use the length
-     of the second, if we know it.  If neither string is constant length,
-     use the given length argument.  We don't require a constant for
-     this case; some cost analysis could be done if both are available
-     but neither is constant.  For now, assume they're equally cheap,
-     unless one has side effects.  If both strings have constant lengths,
-     use the smaller.  */
+  t_descr = CALL_EXPR_ARG (exp, 0);
+  t_func = CALL_EXPR_ARG (exp, 1);
+  t_chain = CALL_EXPR_ARG (exp, 2);
 
-  if (!len1 && !len2)
-    len = len3;
-  else if (!len1)
-    len = len2;
-  else if (!len2)
-    len = len1;
-  else if (TREE_SIDE_EFFECTS (len1))
-    len = len2;
-  else if (TREE_SIDE_EFFECTS (len2))
-    len = len1;
-  else if (TREE_CODE (len1) != INTEGER_CST)
-    len = len2;
-  else if (TREE_CODE (len2) != INTEGER_CST)
-    len = len1;
-  else if (tree_int_cst_lt (len1, len2))
-    len = len1;
-  else
-    len = len2;
+  r_descr = expand_normal (t_descr);
+  m_descr = gen_rtx_MEM (BLKmode, r_descr);
+  MEM_NOTRAP_P (m_descr) = 1;
+  set_mem_align (m_descr, GET_MODE_ALIGNMENT (ptr_mode));
 
-  /* If we are not using the given length, we must incorporate it here.
-     The actual new length parameter will be MIN(len,arg3) in this case.  */
-  if (len != len3)
-    {
-      len = fold_convert_loc (loc, sizetype, len);
-      len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
-    }
-  rtx arg1_rtx = get_memory_rtx (arg1, len);
-  rtx arg2_rtx = get_memory_rtx (arg2, len);
-  rtx arg3_rtx = expand_normal (len);
-  result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
-                                    arg2_rtx, TREE_TYPE (len), arg3_rtx,
-                                    MIN (arg1_align, arg2_align));
+  r_func = expand_normal (t_func);
+  r_chain = expand_normal (t_chain);
 
-  tree fndecl = get_callee_fndecl (exp);
-  if (result)
-    {
-      /* Return the value in the proper mode for this function.  */
-      mode = TYPE_MODE (TREE_TYPE (exp));
-      if (GET_MODE (result) == mode)
-       return result;
-      if (target == 0)
-       return convert_to_mode (mode, result, 0);
-      convert_move (target, result, 0);
-      return target;
-    }
+  /* Generate insns to initialize the descriptor.  */
+  emit_move_insn (adjust_address_nv (m_descr, ptr_mode, 0), r_chain);
+  emit_move_insn (adjust_address_nv (m_descr, ptr_mode,
+                                    POINTER_SIZE / BITS_PER_UNIT), r_func);
 
-  /* Expand the library call ourselves using a stabilized argument
-     list to avoid re-evaluating the function's arguments twice.  */
-  tree call = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
-  copy_warning (call, exp);
-  gcc_assert (TREE_CODE (call) == CALL_EXPR);
-  CALL_EXPR_TAILCALL (call) = CALL_EXPR_TAILCALL (exp);
-  return expand_call (call, target, target == const0_rtx);
+  return const0_rtx;
 }
 
-/* Expand a call to __builtin_saveregs, generating the result in TARGET,
-   if that's convenient.  */
+/* Expand a call to the builtin descriptor adjustment routine.  */
 
-rtx
-expand_builtin_saveregs (void)
+static rtx
+expand_builtin_adjust_descriptor (tree exp)
 {
-  rtx val;
-  rtx_insn *seq;
-
-  /* Don't do __builtin_saveregs more than once in a function.
-     Save the result of the first call and reuse it.  */
-  if (saveregs_value != 0)
-    return saveregs_value;
-
-  /* When this function is called, it means that registers must be
-     saved on entry to this function.  So we migrate the call to the
-     first insn of this function.  */
-
-  start_sequence ();
-
-  /* Do whatever the machine needs done in this case.  */
-  val = targetm.calls.expand_builtin_saveregs ();
+  rtx tramp;
 
-  seq = get_insns ();
-  end_sequence ();
+  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  saveregs_value = val;
+  tramp = expand_normal (CALL_EXPR_ARG (exp, 0));
 
-  /* Put the insns after the NOTE that starts the function.  If this
-     is inside a start_sequence, make the outer-level insn chain current, so
-     the code is placed at the start of the function.  */
-  push_topmost_sequence ();
-  emit_insn_after (seq, entry_of_function ());
-  pop_topmost_sequence ();
+  /* Unalign the descriptor to allow runtime identification.  */
+  tramp = plus_constant (ptr_mode, tramp,
+                        targetm.calls.custom_function_descriptors);
 
-  return val;
+  return force_operand (tramp, NULL_RTX);
 }
 
-/* Expand a call to __builtin_next_arg.  */
-
+/* Expand the call EXP to the built-in signbit, signbitf or signbitl
+   function.  The function first checks whether the back end provides
+   an insn to implement signbit for the respective mode.  If not, it
+   checks whether the floating point format of the value is such that
+   the sign bit can be extracted.  If that is not the case, error out.
+   EXP is the expression that is a call to the builtin function; if
+   convenient, the result should be placed in TARGET.  */
 static rtx
-expand_builtin_next_arg (void)
+expand_builtin_signbit (tree exp, rtx target)
 {
-  /* Checking arguments is already done in fold_builtin_next_arg
-     that must be called before this function.  */
-  return expand_binop (ptr_mode, add_optab,
-                      crtl->args.internal_arg_pointer,
-                      crtl->args.arg_offset_rtx,
-                      NULL_RTX, 0, OPTAB_LIB_WIDEN);
-}
+  const struct real_format *fmt;
+  scalar_float_mode fmode;
+  scalar_int_mode rmode, imode;
+  tree arg;
+  int word, bitpos;
+  enum insn_code icode;
+  rtx temp;
+  location_t loc = EXPR_LOCATION (exp);
 
-/* Make it easier for the backends by protecting the valist argument
-   from multiple evaluations.  */
+  if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-static tree
-stabilize_va_list_loc (location_t loc, tree valist, int needs_lvalue)
-{
-  tree vatype = targetm.canonical_va_list_type (TREE_TYPE (valist));
+  arg = CALL_EXPR_ARG (exp, 0);
+  fmode = SCALAR_FLOAT_TYPE_MODE (TREE_TYPE (arg));
+  rmode = SCALAR_INT_TYPE_MODE (TREE_TYPE (exp));
+  fmt = REAL_MODE_FORMAT (fmode);
 
-  /* The current way of determining the type of valist is completely
-     bogus.  We should have the information on the va builtin instead.  */
-  if (!vatype)
-    vatype = targetm.fn_abi_va_list (cfun->decl);
+  arg = builtin_save_expr (arg);
 
-  if (TREE_CODE (vatype) == ARRAY_TYPE)
+  /* Expand the argument yielding a RTX expression. */
+  temp = expand_normal (arg);
+
+  /* Check if the back end provides an insn that handles signbit for the
+     argument's mode. */
+  icode = optab_handler (signbit_optab, fmode);
+  if (icode != CODE_FOR_nothing)
     {
-      if (TREE_SIDE_EFFECTS (valist))
-       valist = save_expr (valist);
+      rtx_insn *last = get_last_insn ();
+      target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+      if (maybe_emit_unop_insn (icode, target, temp, UNKNOWN))
+       return target;
+      delete_insns_since (last);
+    }
 
-      /* For this case, the backends will be expecting a pointer to
-        vatype, but it's possible we've actually been given an array
-        (an actual TARGET_CANONICAL_VA_LIST_TYPE (valist)).
-        So fix it.  */
-      if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
-       {
-         tree p1 = build_pointer_type (TREE_TYPE (vatype));
-         valist = build_fold_addr_expr_with_type_loc (loc, valist, p1);
-       }
+  /* For floating point formats without a sign bit, implement signbit
+     as "ARG < 0.0".  */
+  bitpos = fmt->signbit_ro;
+  if (bitpos < 0)
+  {
+    /* But we can't do this if the format supports signed zero.  */
+    gcc_assert (!fmt->has_signed_zero || !HONOR_SIGNED_ZEROS (fmode));
+
+    arg = fold_build2_loc (loc, LT_EXPR, TREE_TYPE (exp), arg,
+                      build_real (TREE_TYPE (arg), dconst0));
+    return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
+  }
+
+  if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (fmode).require ();
+      temp = gen_lowpart (imode, temp);
     }
   else
     {
-      tree pt = build_pointer_type (vatype);
+      imode = word_mode;
+      /* Handle targets with different FP word orders.  */
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      temp = operand_subword_force (temp, word, fmode);
+      bitpos = bitpos % BITS_PER_WORD;
+    }
 
-      if (! needs_lvalue)
-       {
-         if (! TREE_SIDE_EFFECTS (valist))
-           return valist;
+  /* Force the intermediate word_mode (or narrower) result into a
+     register.  This avoids attempting to create paradoxical SUBREGs
+     of floating point modes below.  */
+  temp = force_reg (imode, temp);
 
-         valist = fold_build1_loc (loc, ADDR_EXPR, pt, valist);
-         TREE_SIDE_EFFECTS (valist) = 1;
-       }
+  /* If the bitpos is within the "result mode" lowpart, the operation
+     can be implement with a single bitwise AND.  Otherwise, we need
+     a right shift and an AND.  */
 
-      if (TREE_SIDE_EFFECTS (valist))
-       valist = save_expr (valist);
-      valist = fold_build2_loc (loc, MEM_REF,
-                               vatype, valist, build_int_cst (pt, 0));
+  if (bitpos < GET_MODE_BITSIZE (rmode))
+    {
+      wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (rmode));
+
+      if (GET_MODE_SIZE (imode) > GET_MODE_SIZE (rmode))
+       temp = gen_lowpart (rmode, temp);
+      temp = expand_binop (rmode, and_optab, temp,
+                          immed_wide_int_const (mask, rmode),
+                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
+    }
+  else
+    {
+      /* Perform a logical right shift to place the signbit in the least
+        significant bit, then truncate the result to the desired mode
+        and mask just this bit.  */
+      temp = expand_shift (RSHIFT_EXPR, imode, temp, bitpos, NULL_RTX, 1);
+      temp = gen_lowpart (rmode, temp);
+      temp = expand_binop (rmode, and_optab, temp, const1_rtx,
+                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
     }
 
-  return valist;
+  return temp;
 }
 
-/* The "standard" definition of va_list is void*.  */
+/* Expand fork or exec calls.  TARGET is the desired target of the
+   call.  EXP is the call. FN is the
+   identificator of the actual function.  IGNORE is nonzero if the
+   value is to be ignored.  */
 
-tree
-std_build_builtin_va_list (void)
+static rtx
+expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
 {
-  return ptr_type_node;
-}
+  tree id, decl;
+  tree call;
 
-/* The "standard" abi va_list is va_list_type_node.  */
+  if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
+    {
+      tree path = CALL_EXPR_ARG (exp, 0);
+      /* Detect unterminated path.  */
+      if (!check_read_access (exp, path))
+       return NULL_RTX;
 
-tree
-std_fn_abi_va_list (tree fndecl ATTRIBUTE_UNUSED)
-{
-  return va_list_type_node;
-}
+      /* Also detect unterminated first argument.  */
+      switch (DECL_FUNCTION_CODE (fn))
+       {
+       case BUILT_IN_EXECL:
+       case BUILT_IN_EXECLE:
+       case BUILT_IN_EXECLP:
+         if (!check_read_access (exp, path))
+           return NULL_RTX;
+       default:
+         break;
+       }
+    }
 
-/* The "standard" type of va_list is va_list_type_node.  */
 
-tree
-std_canonical_va_list_type (tree type)
-{
-  tree wtype, htype;
+  /* If we are not profiling, just call the function.  */
+  if (!profile_arc_flag)
+    return NULL_RTX;
 
-  wtype = va_list_type_node;
-  htype = type;
+  /* Otherwise call the wrapper.  This should be equivalent for the rest of
+     compiler, so the code does not diverge, and the wrapper may run the
+     code necessary for keeping the profiling sane.  */
 
-  if (TREE_CODE (wtype) == ARRAY_TYPE)
+  switch (DECL_FUNCTION_CODE (fn))
     {
-      /* If va_list is an array type, the argument may have decayed
-        to a pointer type, e.g. by being passed to another function.
-        In that case, unwrap both types so that we can compare the
-        underlying records.  */
-      if (TREE_CODE (htype) == ARRAY_TYPE
-         || POINTER_TYPE_P (htype))
-       {
-         wtype = TREE_TYPE (wtype);
-         htype = TREE_TYPE (htype);
-       }
-    }
-  if (TYPE_MAIN_VARIANT (wtype) == TYPE_MAIN_VARIANT (htype))
-    return va_list_type_node;
+    case BUILT_IN_FORK:
+      id = get_identifier ("__gcov_fork");
+      break;
 
-  return NULL_TREE;
-}
+    case BUILT_IN_EXECL:
+      id = get_identifier ("__gcov_execl");
+      break;
 
-/* The "standard" implementation of va_start: just assign `nextarg' to
-   the variable.  */
+    case BUILT_IN_EXECV:
+      id = get_identifier ("__gcov_execv");
+      break;
 
-void
-std_expand_builtin_va_start (tree valist, rtx nextarg)
-{
-  rtx va_r = expand_expr (valist, NULL_RTX, VOIDmode, EXPAND_WRITE);
-  convert_move (va_r, nextarg, 0);
-}
+    case BUILT_IN_EXECLP:
+      id = get_identifier ("__gcov_execlp");
+      break;
 
-/* Expand EXP, a call to __builtin_va_start.  */
+    case BUILT_IN_EXECLE:
+      id = get_identifier ("__gcov_execle");
+      break;
 
-static rtx
-expand_builtin_va_start (tree exp)
-{
-  rtx nextarg;
-  tree valist;
-  location_t loc = EXPR_LOCATION (exp);
+    case BUILT_IN_EXECVP:
+      id = get_identifier ("__gcov_execvp");
+      break;
 
-  if (call_expr_nargs (exp) < 2)
-    {
-      error_at (loc, "too few arguments to function %<va_start%>");
-      return const0_rtx;
-    }
+    case BUILT_IN_EXECVE:
+      id = get_identifier ("__gcov_execve");
+      break;
 
-  if (fold_builtin_next_arg (exp, true))
-    return const0_rtx;
+    default:
+      gcc_unreachable ();
+    }
 
-  nextarg = expand_builtin_next_arg ();
-  valist = stabilize_va_list_loc (loc, CALL_EXPR_ARG (exp, 0), 1);
+  decl = build_decl (DECL_SOURCE_LOCATION (fn),
+                    FUNCTION_DECL, id, TREE_TYPE (fn));
+  DECL_EXTERNAL (decl) = 1;
+  TREE_PUBLIC (decl) = 1;
+  DECL_ARTIFICIAL (decl) = 1;
+  TREE_NOTHROW (decl) = 1;
+  DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+  call = rewrite_call_expr (EXPR_LOCATION (exp), exp, 0, decl, 0);
+  return expand_call (call, target, ignore);
+ }
 
-  if (targetm.expand_builtin_va_start)
-    targetm.expand_builtin_va_start (valist, nextarg);
-  else
-    std_expand_builtin_va_start (valist, nextarg);
 
-  return const0_rtx;
-}
+\f
+/* Reconstitute a mode for a __sync intrinsic operation.  Since the type of
+   the pointer in these functions is void*, the tree optimizers may remove
+   casts.  The mode computed in expand_builtin isn't reliable either, due
+   to __sync_bool_compare_and_swap.
 
-/* Expand EXP, a call to __builtin_va_end.  */
+   FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the
+   group of builtins.  This gives us log2 of the mode size.  */
 
-static rtx
-expand_builtin_va_end (tree exp)
+static inline machine_mode
+get_builtin_sync_mode (int fcode_diff)
 {
-  tree valist = CALL_EXPR_ARG (exp, 0);
-
-  /* Evaluate for side effects, if needed.  I hate macros that don't
-     do that.  */
-  if (TREE_SIDE_EFFECTS (valist))
-    expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-  return const0_rtx;
+  /* The size is not negotiable, so ask not to get BLKmode in return
+     if the target indicates that a smaller size would be better.  */
+  return int_mode_for_size (BITS_PER_UNIT << fcode_diff, 0).require ();
 }
 
-/* Expand EXP, a call to __builtin_va_copy.  We do this as a
-   builtin rather than just as an assignment in stdarg.h because of the
-   nastiness of array-type va_list types.  */
+/* Expand the memory expression LOC and return the appropriate memory operand
+   for the builtin_sync operations.  */
 
 static rtx
-expand_builtin_va_copy (tree exp)
+get_builtin_sync_mem (tree loc, machine_mode mode)
 {
-  tree dst, src, t;
-  location_t loc = EXPR_LOCATION (exp);
+  rtx addr, mem;
+  int addr_space = TYPE_ADDR_SPACE (POINTER_TYPE_P (TREE_TYPE (loc))
+                                   ? TREE_TYPE (TREE_TYPE (loc))
+                                   : TREE_TYPE (loc));
+  scalar_int_mode addr_mode = targetm.addr_space.address_mode (addr_space);
 
-  dst = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
+  addr = expand_expr (loc, NULL_RTX, addr_mode, EXPAND_SUM);
+  addr = convert_memory_address (addr_mode, addr);
 
-  dst = stabilize_va_list_loc (loc, dst, 1);
-  src = stabilize_va_list_loc (loc, src, 0);
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the full barrier semantics of the intrinsic.  */
+  mem = gen_rtx_MEM (mode, addr);
 
-  gcc_assert (cfun != NULL && cfun->decl != NULL_TREE);
+  set_mem_addr_space (mem, addr_space);
 
-  if (TREE_CODE (targetm.fn_abi_va_list (cfun->decl)) != ARRAY_TYPE)
-    {
-      t = build2 (MODIFY_EXPR, targetm.fn_abi_va_list (cfun->decl), dst, src);
-      TREE_SIDE_EFFECTS (t) = 1;
-      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-    }
-  else
-    {
-      rtx dstb, srcb, size;
+  mem = validize_mem (mem);
 
-      /* Evaluate to pointers.  */
-      dstb = expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL);
-      srcb = expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL);
-      size = expand_expr (TYPE_SIZE_UNIT (targetm.fn_abi_va_list (cfun->decl)),
-                 NULL_RTX, VOIDmode, EXPAND_NORMAL);
+  /* The alignment needs to be at least according to that of the mode.  */
+  set_mem_align (mem, MAX (GET_MODE_ALIGNMENT (mode),
+                          get_pointer_alignment (loc)));
+  set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER);
+  MEM_VOLATILE_P (mem) = 1;
 
-      dstb = convert_memory_address (Pmode, dstb);
-      srcb = convert_memory_address (Pmode, srcb);
+  return mem;
+}
 
-      /* "Dereference" to BLKmode memories.  */
-      dstb = gen_rtx_MEM (BLKmode, dstb);
-      set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst))));
-      set_mem_align (dstb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
-      srcb = gen_rtx_MEM (BLKmode, srcb);
-      set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src))));
-      set_mem_align (srcb, TYPE_ALIGN (targetm.fn_abi_va_list (cfun->decl)));
+/* Make sure an argument is in the right mode.
+   EXP is the tree argument. 
+   MODE is the mode it should be in.  */
 
-      /* Copy.  */
-      emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL);
+static rtx
+expand_expr_force_mode (tree exp, machine_mode mode)
+{
+  rtx val;
+  machine_mode old_mode;
+
+  if (TREE_CODE (exp) == SSA_NAME
+      && TYPE_MODE (TREE_TYPE (exp)) != mode)
+    {
+      /* Undo argument promotion if possible, as combine might not
+        be able to do it later due to MEM_VOLATILE_P uses in the
+        patterns.  */
+      gimple *g = get_gimple_for_ssa_name (exp);
+      if (g && gimple_assign_cast_p (g))
+       {
+         tree rhs = gimple_assign_rhs1 (g);
+         tree_code code = gimple_assign_rhs_code (g);
+         if (CONVERT_EXPR_CODE_P (code)
+             && TYPE_MODE (TREE_TYPE (rhs)) == mode
+             && INTEGRAL_TYPE_P (TREE_TYPE (exp))
+             && INTEGRAL_TYPE_P (TREE_TYPE (rhs))
+             && (TYPE_PRECISION (TREE_TYPE (exp))
+                 > TYPE_PRECISION (TREE_TYPE (rhs))))
+           exp = rhs;
+       }
     }
 
-  return const0_rtx;
+  val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL);
+  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
+     of CONST_INTs, where we know the old_mode only from the call argument.  */
+
+  old_mode = GET_MODE (val);
+  if (old_mode == VOIDmode)
+    old_mode = TYPE_MODE (TREE_TYPE (exp));
+  val = convert_modes (mode, old_mode, val, 1);
+  return val;
 }
 
-/* Expand a call to one of the builtin functions __builtin_frame_address or
-   __builtin_return_address.  */
+
+/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
+   EXP is the CALL_EXPR.  CODE is the rtx code
+   that corresponds to the arithmetic or logical operation from the name;
+   an exception here is that NOT actually means NAND.  TARGET is an optional
+   place for us to store the results; AFTER is true if this is the
+   fetch_and_xxx form.  */
 
 static rtx
-expand_builtin_frame_address (tree fndecl, tree exp)
+expand_builtin_sync_operation (machine_mode mode, tree exp,
+                              enum rtx_code code, bool after,
+                              rtx target)
 {
-  /* The argument must be a nonnegative integer constant.
-     It counts the number of frames to scan up the stack.
-     The value is either the frame pointer value or the return
-     address saved in that frame.  */
-  if (call_expr_nargs (exp) == 0)
-    /* Warning about missing arg was already issued.  */
-    return const0_rtx;
-  else if (! tree_fits_uhwi_p (CALL_EXPR_ARG (exp, 0)))
-    {
-      error ("invalid argument to %qD", fndecl);
-      return const0_rtx;
-    }
-  else
+  rtx val, mem;
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (code == NOT && warn_sync_nand)
     {
-      /* Number of frames to scan up the stack.  */
-      unsigned HOST_WIDE_INT count = tree_to_uhwi (CALL_EXPR_ARG (exp, 0));
+      tree fndecl = get_callee_fndecl (exp);
+      enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-      rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl), count);
+      static bool warned_f_a_n, warned_n_a_f;
 
-      /* Some ports cannot access arbitrary stack frames.  */
-      if (tem == NULL)
+      switch (fcode)
        {
-         warning (0, "unsupported argument to %qD", fndecl);
-         return const0_rtx;
-       }
+       case BUILT_IN_SYNC_FETCH_AND_NAND_1:
+       case BUILT_IN_SYNC_FETCH_AND_NAND_2:
+       case BUILT_IN_SYNC_FETCH_AND_NAND_4:
+       case BUILT_IN_SYNC_FETCH_AND_NAND_8:
+       case BUILT_IN_SYNC_FETCH_AND_NAND_16:
+         if (warned_f_a_n)
+           break;
 
-      if (count)
-       {
-         /* Warn since no effort is made to ensure that any frame
-            beyond the current one exists or can be safely reached.  */
-         warning (OPT_Wframe_address, "calling %qD with "
-                  "a nonzero argument is unsafe", fndecl);
-       }
+         fndecl = builtin_decl_implicit (BUILT_IN_SYNC_FETCH_AND_NAND_N);
+         inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
+         warned_f_a_n = true;
+         break;
 
-      /* For __builtin_frame_address, return what we've got.  */
-      if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
-       return tem;
+       case BUILT_IN_SYNC_NAND_AND_FETCH_1:
+       case BUILT_IN_SYNC_NAND_AND_FETCH_2:
+       case BUILT_IN_SYNC_NAND_AND_FETCH_4:
+       case BUILT_IN_SYNC_NAND_AND_FETCH_8:
+       case BUILT_IN_SYNC_NAND_AND_FETCH_16:
+         if (warned_n_a_f)
+           break;
 
-      if (!REG_P (tem)
-         && ! CONSTANT_P (tem))
-       tem = copy_addr_to_reg (tem);
-      return tem;
+        fndecl = builtin_decl_implicit (BUILT_IN_SYNC_NAND_AND_FETCH_N);
+         inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
+         warned_n_a_f = true;
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
     }
+
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+
+  return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SYNC_SEQ_CST,
+                                after);
 }
 
-/* Expand EXP, a call to the alloca builtin.  Return NULL_RTX if we
-   failed and the caller should emit a normal call.  */
+/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
+   intrinsics. EXP is the CALL_EXPR.  IS_BOOL is
+   true if this is the boolean form.  TARGET is a place for us to store the
+   results; this is NOT optional if IS_BOOL is true.  */
 
 static rtx
-expand_builtin_alloca (tree exp)
+expand_builtin_compare_and_swap (machine_mode mode, tree exp,
+                                bool is_bool, rtx target)
 {
-  rtx op0;
-  rtx result;
-  unsigned int align;
-  tree fndecl = get_callee_fndecl (exp);
-  HOST_WIDE_INT max_size;
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-  bool alloca_for_var = CALL_ALLOCA_FOR_VAR_P (exp);
-  bool valid_arglist
-    = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
-       ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, INTEGER_TYPE,
-                          VOID_TYPE)
-       : fcode == BUILT_IN_ALLOCA_WITH_ALIGN
-        ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
-        : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
+  rtx old_val, new_val, mem;
+  rtx *pbool, *poval;
 
-  if (!valid_arglist)
-    return NULL_RTX;
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  old_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+  new_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
 
-  if ((alloca_for_var
-       && warn_vla_limit >= HOST_WIDE_INT_MAX
-       && warn_alloc_size_limit < warn_vla_limit)
-      || (!alloca_for_var
-         && warn_alloca_limit >= HOST_WIDE_INT_MAX
-         && warn_alloc_size_limit < warn_alloca_limit
-         ))
+  pbool = poval = NULL;
+  if (target != const0_rtx)
     {
-      /* -Walloca-larger-than and -Wvla-larger-than settings of
-        less than HOST_WIDE_INT_MAX override the more general
-        -Walloc-size-larger-than so unless either of the former
-        options is smaller than the last one (wchich would imply
-        that the call was already checked), check the alloca
-        arguments for overflow.  */
-      tree args[] = { CALL_EXPR_ARG (exp, 0), NULL_TREE };
-      int idx[] = { 0, -1 };
-      maybe_warn_alloc_args_overflow (fndecl, exp, args, idx);
+      if (is_bool)
+       pbool = &target;
+      else
+       poval = &target;
     }
+  if (!expand_atomic_compare_and_swap (pbool, poval, mem, old_val, new_val,
+                                      false, MEMMODEL_SYNC_SEQ_CST,
+                                      MEMMODEL_SYNC_SEQ_CST))
+    return NULL_RTX;
 
-  /* Compute the argument.  */
-  op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-
-  /* Compute the alignment.  */
-  align = (fcode == BUILT_IN_ALLOCA
-          ? BIGGEST_ALIGNMENT
-          : TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1)));
-
-  /* Compute the maximum size.  */
-  max_size = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
-              ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 2))
-              : -1);
-
-  /* Allocate the desired space.  If the allocation stems from the declaration
-     of a variable-sized object, it cannot accumulate.  */
-  result
-    = allocate_dynamic_stack_space (op0, 0, align, max_size, alloca_for_var);
-  result = convert_memory_address (ptr_mode, result);
-
-  /* Dynamic allocations for variables are recorded during gimplification.  */
-  if (!alloca_for_var && (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC))
-    record_dynamic_alloc (exp);
-
-  return result;
+  return target;
 }
 
-/* Emit a call to __asan_allocas_unpoison call in EXP.  Add to second argument
-   of the call virtual_stack_dynamic_rtx - stack_pointer_rtx, which is the
-   STACK_DYNAMIC_OFFSET value.  See motivation for this in comment to
-   handle_builtin_stack_restore function.  */
+/* Expand the __sync_lock_test_and_set intrinsic.  Note that the most
+   general form is actually an atomic exchange, and some targets only
+   support a reduced form with the second argument being a constant 1.
+   EXP is the CALL_EXPR; TARGET is an optional place for us to store
+   the results.  */
 
 static rtx
-expand_asan_emit_allocas_unpoison (tree exp)
+expand_builtin_sync_lock_test_and_set (machine_mode mode, tree exp,
+                                      rtx target)
 {
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-  tree arg1 = CALL_EXPR_ARG (exp, 1);
-  rtx top = expand_expr (arg0, NULL_RTX, ptr_mode, EXPAND_NORMAL);
-  rtx bot = expand_expr (arg1, NULL_RTX, ptr_mode, EXPAND_NORMAL);
-  rtx off = expand_simple_binop (Pmode, MINUS, virtual_stack_dynamic_rtx,
-                                stack_pointer_rtx, NULL_RTX, 0,
-                                OPTAB_LIB_WIDEN);
-  off = convert_modes (ptr_mode, Pmode, off, 0);
-  bot = expand_simple_binop (ptr_mode, PLUS, bot, off, NULL_RTX, 0,
-                            OPTAB_LIB_WIDEN);
-  rtx ret = init_one_libfunc ("__asan_allocas_unpoison");
-  ret = emit_library_call_value (ret, NULL_RTX, LCT_NORMAL, ptr_mode,
-                                top, ptr_mode, bot, ptr_mode);
-  return ret;
+  rtx val, mem;
+
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+
+  return expand_sync_lock_test_and_set (target, mem, val);
 }
 
-/* Expand a call to bswap builtin in EXP.
-   Return NULL_RTX if a normal call should be emitted rather than expanding the
-   function in-line.  If convenient, the result should be placed in TARGET.
-   SUBTARGET may be used as the target for computing one of EXP's operands.  */
+/* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
 
-static rtx
-expand_builtin_bswap (machine_mode target_mode, tree exp, rtx target,
-                     rtx subtarget)
+static void
+expand_builtin_sync_lock_release (machine_mode mode, tree exp)
 {
-  tree arg;
-  rtx op0;
+  rtx mem;
 
-  if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
 
-  arg = CALL_EXPR_ARG (exp, 0);
-  op0 = expand_expr (arg,
-                    subtarget && GET_MODE (subtarget) == target_mode
-                    ? subtarget : NULL_RTX,
-                    target_mode, EXPAND_NORMAL);
-  if (GET_MODE (op0) != target_mode)
-    op0 = convert_to_mode (target_mode, op0, 1);
-
-  target = expand_unop (target_mode, bswap_optab, op0, target, 1);
-
-  gcc_assert (target);
-
-  return convert_to_mode (target_mode, target, 1);
+  expand_atomic_store (mem, const0_rtx, MEMMODEL_SYNC_RELEASE, true);
 }
 
-/* Expand a call to a unary builtin in EXP.
-   Return NULL_RTX if a normal call should be emitted rather than expanding the
-   function in-line.  If convenient, the result should be placed in TARGET.
-   SUBTARGET may be used as the target for computing one of EXP's operands.  */
+/* Given an integer representing an ``enum memmodel'', verify its
+   correctness and return the memory model enum.  */
 
-static rtx
-expand_builtin_unop (machine_mode target_mode, tree exp, rtx target,
-                    rtx subtarget, optab op_optab)
+static enum memmodel
+get_memmodel (tree exp)
 {
-  rtx op0;
-
-  if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  rtx op;
+  unsigned HOST_WIDE_INT val;
+  location_t loc
+    = expansion_point_location_if_in_system_header (input_location);
 
-  /* Compute the argument.  */
-  op0 = expand_expr (CALL_EXPR_ARG (exp, 0),
-                    (subtarget
-                     && (TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 0)))
-                         == GET_MODE (subtarget))) ? subtarget : NULL_RTX,
-                    VOIDmode, EXPAND_NORMAL);
-  /* Compute op, into TARGET if possible.
-     Set TARGET to wherever the result comes back.  */
-  target = expand_unop (TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 0))),
-                       op_optab, op0, target, op_optab != clrsb_optab);
-  gcc_assert (target);
+  /* If the parameter is not a constant, it's a run time value so we'll just
+     convert it to MEMMODEL_SEQ_CST to avoid annoying runtime checking.  */
+  if (TREE_CODE (exp) != INTEGER_CST)
+    return MEMMODEL_SEQ_CST;
 
-  return convert_to_mode (target_mode, target, 0);
-}
+  op = expand_normal (exp);
 
-/* Expand a call to __builtin_expect.  We just return our argument
-   as the builtin_expect semantic should've been already executed by
-   tree branch prediction pass. */
+  val = INTVAL (op);
+  if (targetm.memmodel_check)
+    val = targetm.memmodel_check (val);
+  else if (val & ~MEMMODEL_MASK)
+    {
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "unknown architecture specifier in memory model to builtin");
+      return MEMMODEL_SEQ_CST;
+    }
 
-static rtx
-expand_builtin_expect (tree exp, rtx target)
-{
-  tree arg;
+  /* Should never see a user explicit SYNC memodel model, so >= LAST works. */
+  if (memmodel_base (val) >= MEMMODEL_LAST)
+    {
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model argument to builtin");
+      return MEMMODEL_SEQ_CST;
+    }
 
-  if (call_expr_nargs (exp) < 2)
-    return const0_rtx;
-  arg = CALL_EXPR_ARG (exp, 0);
+  /* Workaround for Bugzilla 59448. GCC doesn't track consume properly, so
+     be conservative and promote consume to acquire.  */
+  if (val == MEMMODEL_CONSUME)
+    val = MEMMODEL_ACQUIRE;
 
-  target = expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
-  /* When guessing was done, the hints should be already stripped away.  */
-  gcc_assert (!flag_guess_branch_prob
-             || optimize == 0 || seen_error ());
-  return target;
+  return (enum memmodel) val;
 }
 
-/* Expand a call to __builtin_expect_with_probability.  We just return our
-   argument as the builtin_expect semantic should've been already executed by
-   tree branch prediction pass.  */
+/* Expand the __atomic_exchange intrinsic:
+       TYPE __atomic_exchange (TYPE *object, TYPE desired, enum memmodel)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.  */
 
 static rtx
-expand_builtin_expect_with_probability (tree exp, rtx target)
+expand_builtin_atomic_exchange (machine_mode mode, tree exp, rtx target)
 {
-  tree arg;
+  rtx val, mem;
+  enum memmodel model;
 
-  if (call_expr_nargs (exp) < 3)
-    return const0_rtx;
-  arg = CALL_EXPR_ARG (exp, 0);
+  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
 
-  target = expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
-  /* When guessing was done, the hints should be already stripped away.  */
-  gcc_assert (!flag_guess_branch_prob
-             || optimize == 0 || seen_error ());
-  return target;
-}
+  if (!flag_inline_atomics)
+    return NULL_RTX;
+
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
+  return expand_atomic_exchange (target, mem, val, model);
+}
 
-/* Expand a call to __builtin_assume_aligned.  We just return our first
-   argument as the builtin_assume_aligned semantic should've been already
-   executed by CCP.  */
+/* Expand the __atomic_compare_exchange intrinsic:
+       bool __atomic_compare_exchange (TYPE *object, TYPE *expect, 
+                                       TYPE desired, BOOL weak, 
+                                       enum memmodel success,
+                                       enum memmodel failure)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.  */
 
 static rtx
-expand_builtin_assume_aligned (tree exp, rtx target)
+expand_builtin_atomic_compare_exchange (machine_mode mode, tree exp, 
+                                       rtx target)
 {
-  if (call_expr_nargs (exp) < 2)
-    return const0_rtx;
-  target = expand_expr (CALL_EXPR_ARG (exp, 0), target, VOIDmode,
-                       EXPAND_NORMAL);
-  gcc_assert (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 1))
-             && (call_expr_nargs (exp) < 3
-                 || !TREE_SIDE_EFFECTS (CALL_EXPR_ARG (exp, 2))));
-  return target;
-}
+  rtx expect, desired, mem, oldval;
+  rtx_code_label *label;
+  enum memmodel success, failure;
+  tree weak;
+  bool is_weak;
+  location_t loc
+    = expansion_point_location_if_in_system_header (input_location);
 
-void
-expand_builtin_trap (void)
-{
-  if (targetm.have_trap ())
+  success = get_memmodel (CALL_EXPR_ARG (exp, 4));
+  failure = get_memmodel (CALL_EXPR_ARG (exp, 5));
+
+  if (failure > success)
     {
-      rtx_insn *insn = emit_insn (targetm.gen_trap ());
-      /* For trap insns when not accumulating outgoing args force
-        REG_ARGS_SIZE note to prevent crossjumping of calls with
-        different args sizes.  */
-      if (!ACCUMULATE_OUTGOING_ARGS)
-       add_args_size_note (insn, stack_pointer_delta);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "failure memory model cannot be stronger than success "
+                 "memory model for %<__atomic_compare_exchange%>");
+      success = MEMMODEL_SEQ_CST;
     }
-  else
+  if (is_mm_release (failure) || is_mm_acq_rel (failure))
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_ABORT);
-      tree call_expr = build_call_expr (fn, 0);
-      expand_call (call_expr, NULL_RTX, false);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid failure memory model for "
+                 "%<__atomic_compare_exchange%>");
+      failure = MEMMODEL_SEQ_CST;
+      success = MEMMODEL_SEQ_CST;
     }
 
-  emit_barrier ();
-}
+  if (!flag_inline_atomics)
+    return NULL_RTX;
 
-/* Expand a call to __builtin_unreachable.  We do nothing except emit
-   a barrier saying that control flow will not pass here.
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
 
-   It is the responsibility of the program being compiled to ensure
-   that control flow does never reach __builtin_unreachable.  */
-static void
-expand_builtin_unreachable (void)
-{
-  emit_barrier ();
-}
+  expect = expand_normal (CALL_EXPR_ARG (exp, 1));
+  expect = convert_memory_address (Pmode, expect);
+  expect = gen_rtx_MEM (mode, expect);
+  desired = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
 
-/* Expand EXP, a call to fabs, fabsf or fabsl.
-   Return NULL_RTX if a normal call should be emitted rather than expanding
-   the function inline.  If convenient, the result should be placed
-   in TARGET.  SUBTARGET may be used as the target for computing
-   the operand.  */
+  weak = CALL_EXPR_ARG (exp, 3);
+  is_weak = false;
+  if (tree_fits_shwi_p (weak) && tree_to_shwi (weak) != 0)
+    is_weak = true;
 
-static rtx
-expand_builtin_fabs (tree exp, rtx target, rtx subtarget)
-{
-  machine_mode mode;
-  tree arg;
-  rtx op0;
+  if (target == const0_rtx)
+    target = NULL;
 
-  if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
+  /* Lest the rtl backend create a race condition with an imporoper store
+     to memory, always create a new pseudo for OLDVAL.  */
+  oldval = NULL;
+
+  if (!expand_atomic_compare_and_swap (&target, &oldval, mem, expect, desired,
+                                      is_weak, success, failure))
     return NULL_RTX;
 
-  arg = CALL_EXPR_ARG (exp, 0);
-  CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
-  mode = TYPE_MODE (TREE_TYPE (arg));
-  op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
-  return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
+  /* Conditionally store back to EXPECT, lest we create a race condition
+     with an improper store to memory.  */
+  /* ??? With a rearrangement of atomics at the gimple level, we can handle
+     the normal case where EXPECT is totally private, i.e. a register.  At
+     which point the store can be unconditional.  */
+  label = gen_label_rtx ();
+  emit_cmp_and_jump_insns (target, const0_rtx, NE, NULL,
+                          GET_MODE (target), 1, label);
+  emit_move_insn (expect, oldval);
+  emit_label (label);
+
+  return target;
 }
 
-/* Expand EXP, a call to copysign, copysignf, or copysignl.
-   Return NULL is a normal call should be emitted rather than expanding the
-   function inline.  If convenient, the result should be placed in TARGET.
-   SUBTARGET may be used as the target for computing the operand.  */
+/* Helper function for expand_ifn_atomic_compare_exchange - expand
+   internal ATOMIC_COMPARE_EXCHANGE call into __atomic_compare_exchange_N
+   call.  The weak parameter must be dropped to match the expected parameter
+   list and the expected argument changed from value to pointer to memory
+   slot.  */
 
-static rtx
-expand_builtin_copysign (tree exp, rtx target, rtx subtarget)
+static void
+expand_ifn_atomic_compare_exchange_into_call (gcall *call, machine_mode mode)
 {
-  rtx op0, op1;
-  tree arg;
-
-  if (!validate_arglist (exp, REAL_TYPE, REAL_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  unsigned int z;
+  vec<tree, va_gc> *vec;
 
-  arg = CALL_EXPR_ARG (exp, 0);
-  op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
-
-  arg = CALL_EXPR_ARG (exp, 1);
-  op1 = expand_normal (arg);
-
-  return expand_copysign (op0, op1, target);
+  vec_alloc (vec, 5);
+  vec->quick_push (gimple_call_arg (call, 0));
+  tree expected = gimple_call_arg (call, 1);
+  rtx x = assign_stack_temp_for_type (mode, GET_MODE_SIZE (mode),
+                                     TREE_TYPE (expected));
+  rtx expd = expand_expr (expected, x, mode, EXPAND_NORMAL);
+  if (expd != x)
+    emit_move_insn (x, expd);
+  tree v = make_tree (TREE_TYPE (expected), x);
+  vec->quick_push (build1 (ADDR_EXPR,
+                          build_pointer_type (TREE_TYPE (expected)), v));
+  vec->quick_push (gimple_call_arg (call, 2));
+  /* Skip the boolean weak parameter.  */
+  for (z = 4; z < 6; z++)
+    vec->quick_push (gimple_call_arg (call, z));
+  /* At present we only have BUILT_IN_ATOMIC_COMPARE_EXCHANGE_{1,2,4,8,16}.  */
+  unsigned int bytes_log2 = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
+  gcc_assert (bytes_log2 < 5);
+  built_in_function fncode
+    = (built_in_function) ((int) BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1
+                          + bytes_log2);
+  tree fndecl = builtin_decl_explicit (fncode);
+  tree fn = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fndecl)),
+                   fndecl);
+  tree exp = build_call_vec (boolean_type_node, fn, vec);
+  tree lhs = gimple_call_lhs (call);
+  rtx boolret = expand_call (exp, NULL_RTX, lhs == NULL_TREE);
+  if (lhs)
+    {
+      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+      if (GET_MODE (boolret) != mode)
+       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
+      x = force_reg (mode, x);
+      write_complex_part (target, boolret, true);
+      write_complex_part (target, x, false);
+    }
 }
 
-/* Emit a call to __builtin___clear_cache.  */
+/* Expand IFN_ATOMIC_COMPARE_EXCHANGE internal function.  */
 
 void
-default_emit_call_builtin___clear_cache (rtx begin, rtx end)
+expand_ifn_atomic_compare_exchange (gcall *call)
 {
-  rtx callee = gen_rtx_SYMBOL_REF (Pmode,
-                                  BUILTIN_ASM_NAME_PTR
-                                  (BUILT_IN_CLEAR_CACHE));
+  int size = tree_to_shwi (gimple_call_arg (call, 3)) & 255;
+  gcc_assert (size == 1 || size == 2 || size == 4 || size == 8 || size == 16);
+  machine_mode mode = int_mode_for_size (BITS_PER_UNIT * size, 0).require ();
+  rtx expect, desired, mem, oldval, boolret;
+  enum memmodel success, failure;
+  tree lhs;
+  bool is_weak;
+  location_t loc
+    = expansion_point_location_if_in_system_header (gimple_location (call));
 
-  emit_library_call (callee,
-                    LCT_NORMAL, VOIDmode,
-                    convert_memory_address (ptr_mode, begin), ptr_mode,
-                    convert_memory_address (ptr_mode, end), ptr_mode);
-}
+  success = get_memmodel (gimple_call_arg (call, 4));
+  failure = get_memmodel (gimple_call_arg (call, 5));
 
-/* Emit a call to __builtin___clear_cache, unless the target specifies
-   it as do-nothing.  This function can be used by trampoline
-   finalizers to duplicate the effects of expanding a call to the
-   clear_cache builtin.  */
+  if (failure > success)
+    {
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "failure memory model cannot be stronger than success "
+                 "memory model for %<__atomic_compare_exchange%>");
+      success = MEMMODEL_SEQ_CST;
+    }
 
-void
-maybe_emit_call_builtin___clear_cache (rtx begin, rtx end)
-{
-  if ((GET_MODE (begin) != ptr_mode && GET_MODE (begin) != Pmode)
-      || (GET_MODE (end) != ptr_mode && GET_MODE (end) != Pmode))
+  if (is_mm_release (failure) || is_mm_acq_rel (failure))
     {
-      error ("both arguments to %<__builtin___clear_cache%> must be pointers");
-      return;
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid failure memory model for "
+                 "%<__atomic_compare_exchange%>");
+      failure = MEMMODEL_SEQ_CST;
+      success = MEMMODEL_SEQ_CST;
     }
 
-  if (targetm.have_clear_cache ())
+  if (!flag_inline_atomics)
     {
-      /* We have a "clear_cache" insn, and it will handle everything.  */
-      class expand_operand ops[2];
+      expand_ifn_atomic_compare_exchange_into_call (call, mode);
+      return;
+    }
 
-      create_address_operand (&ops[0], begin);
-      create_address_operand (&ops[1], end);
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (gimple_call_arg (call, 0), mode);
 
-      if (maybe_expand_insn (targetm.code_for_clear_cache, 2, ops))
-       return;
-    }
-  else
+  expect = expand_expr_force_mode (gimple_call_arg (call, 1), mode);
+  desired = expand_expr_force_mode (gimple_call_arg (call, 2), mode);
+
+  is_weak = (tree_to_shwi (gimple_call_arg (call, 3)) & 256) != 0;
+
+  boolret = NULL;
+  oldval = NULL;
+
+  if (!expand_atomic_compare_and_swap (&boolret, &oldval, mem, expect, desired,
+                                      is_weak, success, failure))
     {
-#ifndef CLEAR_INSN_CACHE
-      /* There is no "clear_cache" insn, and __clear_cache() in libgcc
-        does nothing.  There is no need to call it.  Do nothing.  */
+      expand_ifn_atomic_compare_exchange_into_call (call, mode);
       return;
-#endif /* CLEAR_INSN_CACHE */
     }
 
-  targetm.calls.emit_call_builtin___clear_cache (begin, end);
+  lhs = gimple_call_lhs (call);
+  if (lhs)
+    {
+      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+      if (GET_MODE (boolret) != mode)
+       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
+      write_complex_part (target, boolret, true);
+      write_complex_part (target, oldval, false);
+    }
 }
 
-/* Expand a call to __builtin___clear_cache.  */
+/* Expand the __atomic_load intrinsic:
+       TYPE __atomic_load (TYPE *object, enum memmodel)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.  */
 
-static void
-expand_builtin___clear_cache (tree exp)
+static rtx
+expand_builtin_atomic_load (machine_mode mode, tree exp, rtx target)
 {
-  tree begin, end;
-  rtx begin_rtx, end_rtx;
+  rtx mem;
+  enum memmodel model;
 
-  /* We must not expand to a library call.  If we did, any
-     fallback library function in libgcc that might contain a call to
-     __builtin___clear_cache() would recurse infinitely.  */
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+  if (is_mm_release (model) || is_mm_acq_rel (model))
     {
-      error ("both arguments to %<__builtin___clear_cache%> must be pointers");
-      return;
+      location_t loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_load%>");
+      model = MEMMODEL_SEQ_CST;
     }
 
-  begin = CALL_EXPR_ARG (exp, 0);
-  begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL);
+  if (!flag_inline_atomics)
+    return NULL_RTX;
 
-  end = CALL_EXPR_ARG (exp, 1);
-  end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL);
+  /* Expand the operand.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
 
-  maybe_emit_call_builtin___clear_cache (begin_rtx, end_rtx);
+  return expand_atomic_load (target, mem, model);
 }
 
-/* Given a trampoline address, make sure it satisfies TRAMPOLINE_ALIGNMENT.  */
+
+/* Expand the __atomic_store intrinsic:
+       void __atomic_store (TYPE *object, TYPE desired, enum memmodel)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.  */
 
 static rtx
-round_trampoline_addr (rtx tramp)
+expand_builtin_atomic_store (machine_mode mode, tree exp)
 {
-  rtx temp, addend, mask;
+  rtx mem, val;
+  enum memmodel model;
 
-  /* If we don't need too much alignment, we'll have been guaranteed
-     proper alignment by get_trampoline_type.  */
-  if (TRAMPOLINE_ALIGNMENT <= STACK_BOUNDARY)
-    return tramp;
+  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
+  if (!(is_mm_relaxed (model) || is_mm_seq_cst (model)
+       || is_mm_release (model)))
+    {
+      location_t loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_store%>");
+      model = MEMMODEL_SEQ_CST;
+    }
 
-  /* Round address up to desired boundary.  */
-  temp = gen_reg_rtx (Pmode);
-  addend = gen_int_mode (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1, Pmode);
-  mask = gen_int_mode (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT, Pmode);
+  if (!flag_inline_atomics)
+    return NULL_RTX;
 
-  temp  = expand_simple_binop (Pmode, PLUS, tramp, addend,
-                              temp, 0, OPTAB_LIB_WIDEN);
-  tramp = expand_simple_binop (Pmode, AND, temp, mask,
-                              temp, 0, OPTAB_LIB_WIDEN);
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
-  return tramp;
+  return expand_atomic_store (mem, val, model, false);
 }
 
+/* Expand the __atomic_fetch_XXX intrinsic:
+       TYPE __atomic_fetch_XXX (TYPE *object, TYPE val, enum memmodel)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.
+   CODE is the operation, PLUS, MINUS, ADD, XOR, or IOR.
+   FETCH_AFTER is true if returning the result of the operation.
+   FETCH_AFTER is false if returning the value before the operation.
+   IGNORE is true if the result is not used.
+   EXT_CALL is the correct builtin for an external call if this cannot be
+   resolved to an instruction sequence.  */
+
 static rtx
-expand_builtin_init_trampoline (tree exp, bool onstack)
+expand_builtin_atomic_fetch_op (machine_mode mode, tree exp, rtx target,
+                               enum rtx_code code, bool fetch_after,
+                               bool ignore, enum built_in_function ext_call)
 {
-  tree t_tramp, t_func, t_chain;
-  rtx m_tramp, r_tramp, r_chain, tmp;
+  rtx val, mem, ret;
+  enum memmodel model;
+  tree fndecl;
+  tree addr;
 
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE,
-                        POINTER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
 
-  t_tramp = CALL_EXPR_ARG (exp, 0);
-  t_func = CALL_EXPR_ARG (exp, 1);
-  t_chain = CALL_EXPR_ARG (exp, 2);
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
-  r_tramp = expand_normal (t_tramp);
-  m_tramp = gen_rtx_MEM (BLKmode, r_tramp);
-  MEM_NOTRAP_P (m_tramp) = 1;
+  /* Only try generating instructions if inlining is turned on.  */
+  if (flag_inline_atomics)
+    {
+      ret = expand_atomic_fetch_op (target, mem, val, code, model, fetch_after);
+      if (ret)
+       return ret;
+    }
 
-  /* If ONSTACK, the TRAMP argument should be the address of a field
-     within the local function's FRAME decl.  Either way, let's see if
-     we can fill in the MEM_ATTRs for this memory.  */
-  if (TREE_CODE (t_tramp) == ADDR_EXPR)
-    set_mem_attributes (m_tramp, TREE_OPERAND (t_tramp, 0), true);
+  /* Return if a different routine isn't needed for the library call.  */
+  if (ext_call == BUILT_IN_NONE)
+    return NULL_RTX;
 
-  /* Creator of a heap trampoline is responsible for making sure the
-     address is aligned to at least STACK_BOUNDARY.  Normally malloc
-     will ensure this anyhow.  */
-  tmp = round_trampoline_addr (r_tramp);
-  if (tmp != r_tramp)
-    {
-      m_tramp = change_address (m_tramp, BLKmode, tmp);
-      set_mem_align (m_tramp, TRAMPOLINE_ALIGNMENT);
-      set_mem_size (m_tramp, TRAMPOLINE_SIZE);
-    }
+  /* Change the call to the specified function.  */
+  fndecl = get_callee_fndecl (exp);
+  addr = CALL_EXPR_FN (exp);
+  STRIP_NOPS (addr);
 
-  /* The FUNC argument should be the address of the nested function.
-     Extract the actual function decl to pass to the hook.  */
-  gcc_assert (TREE_CODE (t_func) == ADDR_EXPR);
-  t_func = TREE_OPERAND (t_func, 0);
-  gcc_assert (TREE_CODE (t_func) == FUNCTION_DECL);
+  gcc_assert (TREE_OPERAND (addr, 0) == fndecl);
+  TREE_OPERAND (addr, 0) = builtin_decl_explicit (ext_call);
 
-  r_chain = expand_normal (t_chain);
+  /* If we will emit code after the call, the call cannot be a tail call.
+     If it is emitted as a tail call, a barrier is emitted after it, and
+     then all trailing code is removed.  */
+  if (!ignore)
+    CALL_EXPR_TAILCALL (exp) = 0;
 
-  /* Generate insns to initialize the trampoline.  */
-  targetm.calls.trampoline_init (m_tramp, t_func, r_chain);
+  /* Expand the call here so we can emit trailing code.  */
+  ret = expand_call (exp, target, ignore);
 
-  if (onstack)
-    {
-      trampolines_created = 1;
+  /* Replace the original function just in case it matters.  */
+  TREE_OPERAND (addr, 0) = fndecl;
 
-      if (targetm.calls.custom_function_descriptors != 0)
-       warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
-                   "trampoline generated for nested function %qD", t_func);
+  /* Then issue the arithmetic correction to return the right result.  */
+  if (!ignore)
+    {
+      if (code == NOT)
+       {
+         ret = expand_simple_binop (mode, AND, ret, val, NULL_RTX, true,
+                                    OPTAB_LIB_WIDEN);
+         ret = expand_simple_unop (mode, NOT, ret, target, true);
+       }
+      else
+       ret = expand_simple_binop (mode, code, ret, val, target, true,
+                                  OPTAB_LIB_WIDEN);
     }
-
-  return const0_rtx;
+  return ret;
 }
 
-static rtx
-expand_builtin_adjust_trampoline (tree exp)
+/* Expand IFN_ATOMIC_BIT_TEST_AND_* internal function.  */
+
+void
+expand_ifn_atomic_bit_test_and (gcall *call)
 {
-  rtx tramp;
+  tree ptr = gimple_call_arg (call, 0);
+  tree bit = gimple_call_arg (call, 1);
+  tree flag = gimple_call_arg (call, 2);
+  tree lhs = gimple_call_lhs (call);
+  enum memmodel model = MEMMODEL_SYNC_SEQ_CST;
+  machine_mode mode = TYPE_MODE (TREE_TYPE (flag));
+  enum rtx_code code;
+  optab optab;
+  class expand_operand ops[5];
 
-  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  gcc_assert (flag_inline_atomics);
 
-  tramp = expand_normal (CALL_EXPR_ARG (exp, 0));
-  tramp = round_trampoline_addr (tramp);
-  if (targetm.calls.trampoline_adjust_address)
-    tramp = targetm.calls.trampoline_adjust_address (tramp);
+  if (gimple_call_num_args (call) == 4)
+    model = get_memmodel (gimple_call_arg (call, 3));
 
-  return tramp;
-}
+  rtx mem = get_builtin_sync_mem (ptr, mode);
+  rtx val = expand_expr_force_mode (bit, mode);
 
-/* Expand a call to the builtin descriptor initialization routine.
-   A descriptor is made up of a couple of pointers to the static
-   chain and the code entry in this order.  */
+  switch (gimple_call_internal_fn (call))
+    {
+    case IFN_ATOMIC_BIT_TEST_AND_SET:
+      code = IOR;
+      optab = atomic_bit_test_and_set_optab;
+      break;
+    case IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT:
+      code = XOR;
+      optab = atomic_bit_test_and_complement_optab;
+      break;
+    case IFN_ATOMIC_BIT_TEST_AND_RESET:
+      code = AND;
+      optab = atomic_bit_test_and_reset_optab;
+      break;
+    default:
+      gcc_unreachable ();
+    }
 
-static rtx
-expand_builtin_init_descriptor (tree exp)
-{
-  tree t_descr, t_func, t_chain;
-  rtx m_descr, r_descr, r_func, r_chain;
+  if (lhs == NULL_TREE)
+    {
+      val = expand_simple_binop (mode, ASHIFT, const1_rtx,
+                                val, NULL_RTX, true, OPTAB_DIRECT);
+      if (code == AND)
+       val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+      expand_atomic_fetch_op (const0_rtx, mem, val, code, model, false);
+      return;
+    }
 
-  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
-                        VOID_TYPE))
-    return NULL_RTX;
+  rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  enum insn_code icode = direct_optab_handler (optab, mode);
+  gcc_assert (icode != CODE_FOR_nothing);
+  create_output_operand (&ops[0], target, mode);
+  create_fixed_operand (&ops[1], mem);
+  create_convert_operand_to (&ops[2], val, mode, true);
+  create_integer_operand (&ops[3], model);
+  create_integer_operand (&ops[4], integer_onep (flag));
+  if (maybe_expand_insn (icode, 5, ops))
+    return;
 
-  t_descr = CALL_EXPR_ARG (exp, 0);
-  t_func = CALL_EXPR_ARG (exp, 1);
-  t_chain = CALL_EXPR_ARG (exp, 2);
+  rtx bitval = val;
+  val = expand_simple_binop (mode, ASHIFT, const1_rtx,
+                            val, NULL_RTX, true, OPTAB_DIRECT);
+  rtx maskval = val;
+  if (code == AND)
+    val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+  rtx result = expand_atomic_fetch_op (gen_reg_rtx (mode), mem, val,
+                                      code, model, false);
+  if (integer_onep (flag))
+    {
+      result = expand_simple_binop (mode, ASHIFTRT, result, bitval,
+                                   NULL_RTX, true, OPTAB_DIRECT);
+      result = expand_simple_binop (mode, AND, result, const1_rtx, target,
+                                   true, OPTAB_DIRECT);
+    }
+  else
+    result = expand_simple_binop (mode, AND, result, maskval, target, true,
+                                 OPTAB_DIRECT);
+  if (result != target)
+    emit_move_insn (target, result);
+}
 
-  r_descr = expand_normal (t_descr);
-  m_descr = gen_rtx_MEM (BLKmode, r_descr);
-  MEM_NOTRAP_P (m_descr) = 1;
-  set_mem_align (m_descr, GET_MODE_ALIGNMENT (ptr_mode));
+/* Expand an atomic clear operation.
+       void _atomic_clear (BOOL *obj, enum memmodel)
+   EXP is the call expression.  */
 
-  r_func = expand_normal (t_func);
-  r_chain = expand_normal (t_chain);
+static rtx
+expand_builtin_atomic_clear (tree exp) 
+{
+  machine_mode mode;
+  rtx mem, ret;
+  enum memmodel model;
 
-  /* Generate insns to initialize the descriptor.  */
-  emit_move_insn (adjust_address_nv (m_descr, ptr_mode, 0), r_chain);
-  emit_move_insn (adjust_address_nv (m_descr, ptr_mode,
-                                    POINTER_SIZE / BITS_PER_UNIT), r_func);
+  mode = int_mode_for_size (BOOL_TYPE_SIZE, 0).require ();
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+
+  if (is_mm_consume (model) || is_mm_acquire (model) || is_mm_acq_rel (model))
+    {
+      location_t loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_store%>");
+      model = MEMMODEL_SEQ_CST;
+    }
 
+  /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
+     Failing that, a store is issued by __atomic_store.  The only way this can
+     fail is if the bool type is larger than a word size.  Unlikely, but
+     handle it anyway for completeness.  Assume a single threaded model since
+     there is no atomic support in this case, and no barriers are required.  */
+  ret = expand_atomic_store (mem, const0_rtx, model, true);
+  if (!ret)
+    emit_move_insn (mem, const0_rtx);
   return const0_rtx;
 }
 
-/* Expand a call to the builtin descriptor adjustment routine.  */
+/* Expand an atomic test_and_set operation.
+       bool _atomic_test_and_set (BOOL *obj, enum memmodel)
+   EXP is the call expression.  */
 
 static rtx
-expand_builtin_adjust_descriptor (tree exp)
+expand_builtin_atomic_test_and_set (tree exp, rtx target)
 {
-  rtx tramp;
+  rtx mem;
+  enum memmodel model;
+  machine_mode mode;
 
-  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  mode = int_mode_for_size (BOOL_TYPE_SIZE, 0).require ();
+  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
 
-  tramp = expand_normal (CALL_EXPR_ARG (exp, 0));
+  return expand_atomic_test_and_set (target, mem, model);
+}
 
-  /* Unalign the descriptor to allow runtime identification.  */
-  tramp = plus_constant (ptr_mode, tramp,
-                        targetm.calls.custom_function_descriptors);
 
-  return force_operand (tramp, NULL_RTX);
-}
+/* Return true if (optional) argument ARG1 of size ARG0 is always lock free on
+   this architecture.  If ARG1 is NULL, use typical alignment for size ARG0.  */
 
-/* Expand the call EXP to the built-in signbit, signbitf or signbitl
-   function.  The function first checks whether the back end provides
-   an insn to implement signbit for the respective mode.  If not, it
-   checks whether the floating point format of the value is such that
-   the sign bit can be extracted.  If that is not the case, error out.
-   EXP is the expression that is a call to the builtin function; if
-   convenient, the result should be placed in TARGET.  */
-static rtx
-expand_builtin_signbit (tree exp, rtx target)
+static tree
+fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
 {
-  const struct real_format *fmt;
-  scalar_float_mode fmode;
-  scalar_int_mode rmode, imode;
-  tree arg;
-  int word, bitpos;
-  enum insn_code icode;
-  rtx temp;
-  location_t loc = EXPR_LOCATION (exp);
-
-  if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  int size;
+  machine_mode mode;
+  unsigned int mode_align, type_align;
 
-  arg = CALL_EXPR_ARG (exp, 0);
-  fmode = SCALAR_FLOAT_TYPE_MODE (TREE_TYPE (arg));
-  rmode = SCALAR_INT_TYPE_MODE (TREE_TYPE (exp));
-  fmt = REAL_MODE_FORMAT (fmode);
+  if (TREE_CODE (arg0) != INTEGER_CST)
+    return NULL_TREE;
 
-  arg = builtin_save_expr (arg);
+  /* We need a corresponding integer mode for the access to be lock-free.  */
+  size = INTVAL (expand_normal (arg0)) * BITS_PER_UNIT;
+  if (!int_mode_for_size (size, 0).exists (&mode))
+    return boolean_false_node;
 
-  /* Expand the argument yielding a RTX expression. */
-  temp = expand_normal (arg);
+  mode_align = GET_MODE_ALIGNMENT (mode);
 
-  /* Check if the back end provides an insn that handles signbit for the
-     argument's mode. */
-  icode = optab_handler (signbit_optab, fmode);
-  if (icode != CODE_FOR_nothing)
+  if (TREE_CODE (arg1) == INTEGER_CST)
     {
-      rtx_insn *last = get_last_insn ();
-      target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-      if (maybe_emit_unop_insn (icode, target, temp, UNKNOWN))
-       return target;
-      delete_insns_since (last);
-    }
-
-  /* For floating point formats without a sign bit, implement signbit
-     as "ARG < 0.0".  */
-  bitpos = fmt->signbit_ro;
-  if (bitpos < 0)
-  {
-    /* But we can't do this if the format supports signed zero.  */
-    gcc_assert (!fmt->has_signed_zero || !HONOR_SIGNED_ZEROS (fmode));
+      unsigned HOST_WIDE_INT val = UINTVAL (expand_normal (arg1));
 
-    arg = fold_build2_loc (loc, LT_EXPR, TREE_TYPE (exp), arg,
-                      build_real (TREE_TYPE (arg), dconst0));
-    return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
-  }
+      /* Either this argument is null, or it's a fake pointer encoding
+         the alignment of the object.  */
+      val = least_bit_hwi (val);
+      val *= BITS_PER_UNIT;
 
-  if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
-    {
-      imode = int_mode_for_mode (fmode).require ();
-      temp = gen_lowpart (imode, temp);
+      if (val == 0 || mode_align < val)
+        type_align = mode_align;
+      else
+        type_align = val;
     }
   else
     {
-      imode = word_mode;
-      /* Handle targets with different FP word orders.  */
-      if (FLOAT_WORDS_BIG_ENDIAN)
-       word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD;
-      else
-       word = bitpos / BITS_PER_WORD;
-      temp = operand_subword_force (temp, word, fmode);
-      bitpos = bitpos % BITS_PER_WORD;
-    }
-
-  /* Force the intermediate word_mode (or narrower) result into a
-     register.  This avoids attempting to create paradoxical SUBREGs
-     of floating point modes below.  */
-  temp = force_reg (imode, temp);
+      tree ttype = TREE_TYPE (arg1);
 
-  /* If the bitpos is within the "result mode" lowpart, the operation
-     can be implement with a single bitwise AND.  Otherwise, we need
-     a right shift and an AND.  */
+      /* This function is usually invoked and folded immediately by the front
+        end before anything else has a chance to look at it.  The pointer
+        parameter at this point is usually cast to a void *, so check for that
+        and look past the cast.  */
+      if (CONVERT_EXPR_P (arg1)
+         && POINTER_TYPE_P (ttype)
+         && VOID_TYPE_P (TREE_TYPE (ttype))
+         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (arg1, 0))))
+       arg1 = TREE_OPERAND (arg1, 0);
 
-  if (bitpos < GET_MODE_BITSIZE (rmode))
-    {
-      wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (rmode));
+      ttype = TREE_TYPE (arg1);
+      gcc_assert (POINTER_TYPE_P (ttype));
 
-      if (GET_MODE_SIZE (imode) > GET_MODE_SIZE (rmode))
-       temp = gen_lowpart (rmode, temp);
-      temp = expand_binop (rmode, and_optab, temp,
-                          immed_wide_int_const (mask, rmode),
-                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
-    }
-  else
-    {
-      /* Perform a logical right shift to place the signbit in the least
-        significant bit, then truncate the result to the desired mode
-        and mask just this bit.  */
-      temp = expand_shift (RSHIFT_EXPR, imode, temp, bitpos, NULL_RTX, 1);
-      temp = gen_lowpart (rmode, temp);
-      temp = expand_binop (rmode, and_optab, temp, const1_rtx,
-                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
+      /* Get the underlying type of the object.  */
+      ttype = TREE_TYPE (ttype);
+      type_align = TYPE_ALIGN (ttype);
     }
 
-  return temp;
+  /* If the object has smaller alignment, the lock free routines cannot
+     be used.  */
+  if (type_align < mode_align)
+    return boolean_false_node;
+
+  /* Check if a compare_and_swap pattern exists for the mode which represents
+     the required size.  The pattern is not allowed to fail, so the existence
+     of the pattern indicates support is present.  Also require that an
+     atomic load exists for the required size.  */
+  if (can_compare_and_swap_p (mode, true) && can_atomic_load_p (mode))
+    return boolean_true_node;
+  else
+    return boolean_false_node;
 }
 
-/* Expand fork or exec calls.  TARGET is the desired target of the
-   call.  EXP is the call. FN is the
-   identificator of the actual function.  IGNORE is nonzero if the
-   value is to be ignored.  */
+/* Return true if the parameters to call EXP represent an object which will
+   always generate lock free instructions.  The first argument represents the
+   size of the object, and the second parameter is a pointer to the object 
+   itself.  If NULL is passed for the object, then the result is based on 
+   typical alignment for an object of the specified size.  Otherwise return 
+   false.  */
 
 static rtx
-expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
+expand_builtin_atomic_always_lock_free (tree exp)
 {
-  tree id, decl;
-  tree call;
+  tree size;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
 
-  if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
+  if (TREE_CODE (arg0) != INTEGER_CST)
     {
-      tree path = CALL_EXPR_ARG (exp, 0);
-      /* Detect unterminated path.  */
-      if (!check_read_access (exp, path))
-       return NULL_RTX;
-
-      /* Also detect unterminated first argument.  */
-      switch (DECL_FUNCTION_CODE (fn))
-       {
-       case BUILT_IN_EXECL:
-       case BUILT_IN_EXECLE:
-       case BUILT_IN_EXECLP:
-         if (!check_read_access (exp, path))
-           return NULL_RTX;
-       default:
-         break;
-       }
+      error ("non-constant argument 1 to %qs", "__atomic_always_lock_free");
+      return const0_rtx;
     }
 
+  size = fold_builtin_atomic_always_lock_free (arg0, arg1);
+  if (size == boolean_true_node)
+    return const1_rtx;
+  return const0_rtx;
+}
 
-  /* If we are not profiling, just call the function.  */
-  if (!profile_arc_flag)
-    return NULL_RTX;
-
-  /* Otherwise call the wrapper.  This should be equivalent for the rest of
-     compiler, so the code does not diverge, and the wrapper may run the
-     code necessary for keeping the profiling sane.  */
-
-  switch (DECL_FUNCTION_CODE (fn))
-    {
-    case BUILT_IN_FORK:
-      id = get_identifier ("__gcov_fork");
-      break;
-
-    case BUILT_IN_EXECL:
-      id = get_identifier ("__gcov_execl");
-      break;
-
-    case BUILT_IN_EXECV:
-      id = get_identifier ("__gcov_execv");
-      break;
+/* Return a one or zero if it can be determined that object ARG1 of size ARG 
+   is lock free on this architecture.  */
 
-    case BUILT_IN_EXECLP:
-      id = get_identifier ("__gcov_execlp");
-      break;
+static tree
+fold_builtin_atomic_is_lock_free (tree arg0, tree arg1)
+{
+  if (!flag_inline_atomics)
+    return NULL_TREE;
+  
+  /* If it isn't always lock free, don't generate a result.  */
+  if (fold_builtin_atomic_always_lock_free (arg0, arg1) == boolean_true_node)
+    return boolean_true_node;
 
-    case BUILT_IN_EXECLE:
-      id = get_identifier ("__gcov_execle");
-      break;
+  return NULL_TREE;
+}
 
-    case BUILT_IN_EXECVP:
-      id = get_identifier ("__gcov_execvp");
-      break;
+/* Return true if the parameters to call EXP represent an object which will
+   always generate lock free instructions.  The first argument represents the
+   size of the object, and the second parameter is a pointer to the object 
+   itself.  If NULL is passed for the object, then the result is based on 
+   typical alignment for an object of the specified size.  Otherwise return 
+   NULL*/
 
-    case BUILT_IN_EXECVE:
-      id = get_identifier ("__gcov_execve");
-      break;
+static rtx
+expand_builtin_atomic_is_lock_free (tree exp)
+{
+  tree size;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
 
-    default:
-      gcc_unreachable ();
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
+    {
+      error ("non-integer argument 1 to %qs", "__atomic_is_lock_free");
+      return NULL_RTX;
     }
 
-  decl = build_decl (DECL_SOURCE_LOCATION (fn),
-                    FUNCTION_DECL, id, TREE_TYPE (fn));
-  DECL_EXTERNAL (decl) = 1;
-  TREE_PUBLIC (decl) = 1;
-  DECL_ARTIFICIAL (decl) = 1;
-  TREE_NOTHROW (decl) = 1;
-  DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
-  DECL_VISIBILITY_SPECIFIED (decl) = 1;
-  call = rewrite_call_expr (EXPR_LOCATION (exp), exp, 0, decl, 0);
-  return expand_call (call, target, ignore);
- }
+  if (!flag_inline_atomics)
+    return NULL_RTX; 
 
+  /* If the value is known at compile time, return the RTX for it.  */
+  size = fold_builtin_atomic_is_lock_free (arg0, arg1);
+  if (size == boolean_true_node)
+    return const1_rtx;
 
-\f
-/* Reconstitute a mode for a __sync intrinsic operation.  Since the type of
-   the pointer in these functions is void*, the tree optimizers may remove
-   casts.  The mode computed in expand_builtin isn't reliable either, due
-   to __sync_bool_compare_and_swap.
+  return NULL_RTX;
+}
 
-   FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the
-   group of builtins.  This gives us log2 of the mode size.  */
+/* Expand the __atomic_thread_fence intrinsic:
+       void __atomic_thread_fence (enum memmodel)
+   EXP is the CALL_EXPR.  */
 
-static inline machine_mode
-get_builtin_sync_mode (int fcode_diff)
+static void
+expand_builtin_atomic_thread_fence (tree exp)
 {
-  /* The size is not negotiable, so ask not to get BLKmode in return
-     if the target indicates that a smaller size would be better.  */
-  return int_mode_for_size (BITS_PER_UNIT << fcode_diff, 0).require ();
+  enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
+  expand_mem_thread_fence (model);
 }
 
-/* Expand the memory expression LOC and return the appropriate memory operand
-   for the builtin_sync operations.  */
+/* Expand the __atomic_signal_fence intrinsic:
+       void __atomic_signal_fence (enum memmodel)
+   EXP is the CALL_EXPR.  */
 
-static rtx
-get_builtin_sync_mem (tree loc, machine_mode mode)
+static void
+expand_builtin_atomic_signal_fence (tree exp)
 {
-  rtx addr, mem;
-  int addr_space = TYPE_ADDR_SPACE (POINTER_TYPE_P (TREE_TYPE (loc))
-                                   ? TREE_TYPE (TREE_TYPE (loc))
-                                   : TREE_TYPE (loc));
-  scalar_int_mode addr_mode = targetm.addr_space.address_mode (addr_space);
-
-  addr = expand_expr (loc, NULL_RTX, addr_mode, EXPAND_SUM);
-  addr = convert_memory_address (addr_mode, addr);
-
-  /* Note that we explicitly do not want any alias information for this
-     memory, so that we kill all other live memories.  Otherwise we don't
-     satisfy the full barrier semantics of the intrinsic.  */
-  mem = gen_rtx_MEM (mode, addr);
-
-  set_mem_addr_space (mem, addr_space);
-
-  mem = validize_mem (mem);
+  enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
+  expand_mem_signal_fence (model);
+}
 
-  /* The alignment needs to be at least according to that of the mode.  */
-  set_mem_align (mem, MAX (GET_MODE_ALIGNMENT (mode),
-                          get_pointer_alignment (loc)));
-  set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER);
-  MEM_VOLATILE_P (mem) = 1;
+/* Expand the __sync_synchronize intrinsic.  */
 
-  return mem;
+static void
+expand_builtin_sync_synchronize (void)
+{
+  expand_mem_thread_fence (MEMMODEL_SYNC_SEQ_CST);
 }
 
-/* Make sure an argument is in the right mode.
-   EXP is the tree argument. 
-   MODE is the mode it should be in.  */
-
 static rtx
-expand_expr_force_mode (tree exp, machine_mode mode)
+expand_builtin_thread_pointer (tree exp, rtx target)
 {
-  rtx val;
-  machine_mode old_mode;
-
-  if (TREE_CODE (exp) == SSA_NAME
-      && TYPE_MODE (TREE_TYPE (exp)) != mode)
+  enum insn_code icode;
+  if (!validate_arglist (exp, VOID_TYPE))
+    return const0_rtx;
+  icode = direct_optab_handler (get_thread_pointer_optab, Pmode);
+  if (icode != CODE_FOR_nothing)
     {
-      /* Undo argument promotion if possible, as combine might not
-        be able to do it later due to MEM_VOLATILE_P uses in the
-        patterns.  */
-      gimple *g = get_gimple_for_ssa_name (exp);
-      if (g && gimple_assign_cast_p (g))
-       {
-         tree rhs = gimple_assign_rhs1 (g);
-         tree_code code = gimple_assign_rhs_code (g);
-         if (CONVERT_EXPR_CODE_P (code)
-             && TYPE_MODE (TREE_TYPE (rhs)) == mode
-             && INTEGRAL_TYPE_P (TREE_TYPE (exp))
-             && INTEGRAL_TYPE_P (TREE_TYPE (rhs))
-             && (TYPE_PRECISION (TREE_TYPE (exp))
-                 > TYPE_PRECISION (TREE_TYPE (rhs))))
-           exp = rhs;
-       }
+      class expand_operand op;
+      /* If the target is not sutitable then create a new target. */
+      if (target == NULL_RTX
+         || !REG_P (target)
+         || GET_MODE (target) != Pmode)
+       target = gen_reg_rtx (Pmode);
+      create_output_operand (&op, target, Pmode);
+      expand_insn (icode, 1, &op);
+      return target;
     }
-
-  val = expand_expr (exp, NULL_RTX, mode, EXPAND_NORMAL);
-  /* If VAL is promoted to a wider mode, convert it back to MODE.  Take care
-     of CONST_INTs, where we know the old_mode only from the call argument.  */
-
-  old_mode = GET_MODE (val);
-  if (old_mode == VOIDmode)
-    old_mode = TYPE_MODE (TREE_TYPE (exp));
-  val = convert_modes (mode, old_mode, val, 1);
-  return val;
+  error ("%<__builtin_thread_pointer%> is not supported on this target");
+  return const0_rtx;
 }
 
-
-/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
-   EXP is the CALL_EXPR.  CODE is the rtx code
-   that corresponds to the arithmetic or logical operation from the name;
-   an exception here is that NOT actually means NAND.  TARGET is an optional
-   place for us to store the results; AFTER is true if this is the
-   fetch_and_xxx form.  */
-
-static rtx
-expand_builtin_sync_operation (machine_mode mode, tree exp,
-                              enum rtx_code code, bool after,
-                              rtx target)
+static void
+expand_builtin_set_thread_pointer (tree exp)
 {
-  rtx val, mem;
-  location_t loc = EXPR_LOCATION (exp);
-
-  if (code == NOT && warn_sync_nand)
+  enum insn_code icode;
+  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+    return;
+  icode = direct_optab_handler (set_thread_pointer_optab, Pmode);
+  if (icode != CODE_FOR_nothing)
     {
-      tree fndecl = get_callee_fndecl (exp);
-      enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+      class expand_operand op;
+      rtx val = expand_expr (CALL_EXPR_ARG (exp, 0), NULL_RTX,
+                            Pmode, EXPAND_NORMAL);      
+      create_input_operand (&op, val, Pmode);
+      expand_insn (icode, 1, &op);
+      return;
+    }
+  error ("%<__builtin_set_thread_pointer%> is not supported on this target");
+}
 
-      static bool warned_f_a_n, warned_n_a_f;
+\f
+/* Emit code to restore the current value of stack.  */
 
-      switch (fcode)
-       {
-       case BUILT_IN_SYNC_FETCH_AND_NAND_1:
-       case BUILT_IN_SYNC_FETCH_AND_NAND_2:
-       case BUILT_IN_SYNC_FETCH_AND_NAND_4:
-       case BUILT_IN_SYNC_FETCH_AND_NAND_8:
-       case BUILT_IN_SYNC_FETCH_AND_NAND_16:
-         if (warned_f_a_n)
-           break;
+static void
+expand_stack_restore (tree var)
+{
+  rtx_insn *prev;
+  rtx sa = expand_normal (var);
 
-         fndecl = builtin_decl_implicit (BUILT_IN_SYNC_FETCH_AND_NAND_N);
-         inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
-         warned_f_a_n = true;
-         break;
+  sa = convert_memory_address (Pmode, sa);
 
-       case BUILT_IN_SYNC_NAND_AND_FETCH_1:
-       case BUILT_IN_SYNC_NAND_AND_FETCH_2:
-       case BUILT_IN_SYNC_NAND_AND_FETCH_4:
-       case BUILT_IN_SYNC_NAND_AND_FETCH_8:
-       case BUILT_IN_SYNC_NAND_AND_FETCH_16:
-         if (warned_n_a_f)
-           break;
+  prev = get_last_insn ();
+  emit_stack_restore (SAVE_BLOCK, sa);
 
-        fndecl = builtin_decl_implicit (BUILT_IN_SYNC_NAND_AND_FETCH_N);
-         inform (loc, "%qD changed semantics in GCC 4.4", fndecl);
-         warned_n_a_f = true;
-         break;
+  record_new_stack_level ();
 
-       default:
-         gcc_unreachable ();
-       }
-    }
+  fixup_args_size_notes (prev, get_last_insn (), 0);
+}
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+/* Emit code to save the current value of stack.  */
 
-  return expand_atomic_fetch_op (target, mem, val, code, MEMMODEL_SYNC_SEQ_CST,
-                                after);
+static rtx
+expand_stack_save (void)
+{
+  rtx ret = NULL_RTX;
+
+  emit_stack_save (SAVE_BLOCK, &ret);
+  return ret;
 }
 
-/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
-   intrinsics. EXP is the CALL_EXPR.  IS_BOOL is
-   true if this is the boolean form.  TARGET is a place for us to store the
-   results; this is NOT optional if IS_BOOL is true.  */
+/* Emit code to get the openacc gang, worker or vector id or size.  */
 
 static rtx
-expand_builtin_compare_and_swap (machine_mode mode, tree exp,
-                                bool is_bool, rtx target)
+expand_builtin_goacc_parlevel_id_size (tree exp, rtx target, int ignore)
 {
-  rtx old_val, new_val, mem;
-  rtx *pbool, *poval;
+  const char *name;
+  rtx fallback_retval;
+  rtx_insn *(*gen_fn) (rtx, rtx);
+  switch (DECL_FUNCTION_CODE (get_callee_fndecl (exp)))
+    {
+    case BUILT_IN_GOACC_PARLEVEL_ID:
+      name = "__builtin_goacc_parlevel_id";
+      fallback_retval = const0_rtx;
+      gen_fn = targetm.gen_oacc_dim_pos;
+      break;
+    case BUILT_IN_GOACC_PARLEVEL_SIZE:
+      name = "__builtin_goacc_parlevel_size";
+      fallback_retval = const1_rtx;
+      gen_fn = targetm.gen_oacc_dim_size;
+      break;
+    default:
+      gcc_unreachable ();
+    }
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  old_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
-  new_val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
+  if (oacc_get_fn_attrib (current_function_decl) == NULL_TREE)
+    {
+      error ("%qs only supported in OpenACC code", name);
+      return const0_rtx;
+    }
 
-  pbool = poval = NULL;
-  if (target != const0_rtx)
+  tree arg = CALL_EXPR_ARG (exp, 0);
+  if (TREE_CODE (arg) != INTEGER_CST)
     {
-      if (is_bool)
-       pbool = &target;
-      else
-       poval = &target;
+      error ("non-constant argument 0 to %qs", name);
+      return const0_rtx;
     }
-  if (!expand_atomic_compare_and_swap (pbool, poval, mem, old_val, new_val,
-                                      false, MEMMODEL_SYNC_SEQ_CST,
-                                      MEMMODEL_SYNC_SEQ_CST))
-    return NULL_RTX;
 
-  return target;
-}
+  int dim = TREE_INT_CST_LOW (arg);
+  switch (dim)
+    {
+    case GOMP_DIM_GANG:
+    case GOMP_DIM_WORKER:
+    case GOMP_DIM_VECTOR:
+      break;
+    default:
+      error ("illegal argument 0 to %qs", name);
+      return const0_rtx;
+    }
 
-/* Expand the __sync_lock_test_and_set intrinsic.  Note that the most
-   general form is actually an atomic exchange, and some targets only
-   support a reduced form with the second argument being a constant 1.
-   EXP is the CALL_EXPR; TARGET is an optional place for us to store
-   the results.  */
+  if (ignore)
+    return target;
 
-static rtx
-expand_builtin_sync_lock_test_and_set (machine_mode mode, tree exp,
-                                      rtx target)
-{
-  rtx val, mem;
+  if (target == NULL_RTX)
+    target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+  if (!targetm.have_oacc_dim_size ())
+    {
+      emit_move_insn (target, fallback_retval);
+      return target;
+    }
 
-  return expand_sync_lock_test_and_set (target, mem, val);
-}
-
-/* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
+  rtx reg = MEM_P (target) ? gen_reg_rtx (GET_MODE (target)) : target;
+  emit_insn (gen_fn (reg, GEN_INT (dim)));
+  if (reg != target)
+    emit_move_insn (target, reg);
 
-static void
-expand_builtin_sync_lock_release (machine_mode mode, tree exp)
-{
-  rtx mem;
+  return target;
+}
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+/* Expand a string compare operation using a sequence of char comparison
+   to get rid of the calling overhead, with result going to TARGET if
+   that's convenient.
 
-  expand_atomic_store (mem, const0_rtx, MEMMODEL_SYNC_RELEASE, true);
-}
+   VAR_STR is the variable string source;
+   CONST_STR is the constant string source;
+   LENGTH is the number of chars to compare;
+   CONST_STR_N indicates which source string is the constant string;
+   IS_MEMCMP indicates whether it's a memcmp or strcmp.
+  
+   to: (assume const_str_n is 2, i.e., arg2 is a constant string)
 
-/* Given an integer representing an ``enum memmodel'', verify its
-   correctness and return the memory model enum.  */
+   target = (int) (unsigned char) var_str[0]
+           - (int) (unsigned char) const_str[0];
+   if (target != 0)
+     goto ne_label;
+     ...
+   target = (int) (unsigned char) var_str[length - 2]
+           - (int) (unsigned char) const_str[length - 2];
+   if (target != 0)
+     goto ne_label;
+   target = (int) (unsigned char) var_str[length - 1]
+           - (int) (unsigned char) const_str[length - 1];
+   ne_label:
+  */
 
-static enum memmodel
-get_memmodel (tree exp)
+static rtx
+inline_string_cmp (rtx target, tree var_str, const char *const_str,
+                  unsigned HOST_WIDE_INT length,
+                  int const_str_n, machine_mode mode)
 {
-  rtx op;
-  unsigned HOST_WIDE_INT val;
-  location_t loc
-    = expansion_point_location_if_in_system_header (input_location);
-
-  /* If the parameter is not a constant, it's a run time value so we'll just
-     convert it to MEMMODEL_SEQ_CST to avoid annoying runtime checking.  */
-  if (TREE_CODE (exp) != INTEGER_CST)
-    return MEMMODEL_SEQ_CST;
+  HOST_WIDE_INT offset = 0;
+  rtx var_rtx_array
+    = get_memory_rtx (var_str, build_int_cst (unsigned_type_node,length));
+  rtx var_rtx = NULL_RTX;
+  rtx const_rtx = NULL_RTX;
+  rtx result = target ? target : gen_reg_rtx (mode);
+  rtx_code_label *ne_label = gen_label_rtx ();
+  tree unit_type_node = unsigned_char_type_node;
+  scalar_int_mode unit_mode
+    = as_a <scalar_int_mode> TYPE_MODE (unit_type_node);
 
-  op = expand_normal (exp);
+  start_sequence ();
 
-  val = INTVAL (op);
-  if (targetm.memmodel_check)
-    val = targetm.memmodel_check (val);
-  else if (val & ~MEMMODEL_MASK)
+  for (unsigned HOST_WIDE_INT i = 0; i < length; i++)
     {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "unknown architecture specifier in memory model to builtin");
-      return MEMMODEL_SEQ_CST;
-    }
+      var_rtx
+       = adjust_address (var_rtx_array, TYPE_MODE (unit_type_node), offset);
+      const_rtx = c_readstr (const_str + offset, unit_mode);
+      rtx op0 = (const_str_n == 1) ? const_rtx : var_rtx;
+      rtx op1 = (const_str_n == 1) ? var_rtx : const_rtx;
 
-  /* Should never see a user explicit SYNC memodel model, so >= LAST works. */
-  if (memmodel_base (val) >= MEMMODEL_LAST)
-    {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid memory model argument to builtin");
-      return MEMMODEL_SEQ_CST;
+      op0 = convert_modes (mode, unit_mode, op0, 1);
+      op1 = convert_modes (mode, unit_mode, op1, 1);
+      result = expand_simple_binop (mode, MINUS, op0, op1,
+                                   result, 1, OPTAB_WIDEN);
+      if (i < length - 1)
+       emit_cmp_and_jump_insns (result, CONST0_RTX (mode), NE, NULL_RTX,
+                                mode, true, ne_label);
+      offset += GET_MODE_SIZE (unit_mode);
     }
 
-  /* Workaround for Bugzilla 59448. GCC doesn't track consume properly, so
-     be conservative and promote consume to acquire.  */
-  if (val == MEMMODEL_CONSUME)
-    val = MEMMODEL_ACQUIRE;
+  emit_label (ne_label);
+  rtx_insn *insns = get_insns ();
+  end_sequence ();
+  emit_insn (insns);
 
-  return (enum memmodel) val;
+  return result;
 }
 
-/* Expand the __atomic_exchange intrinsic:
-       TYPE __atomic_exchange (TYPE *object, TYPE desired, enum memmodel)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.  */
+/* Inline expansion of a call to str(n)cmp and memcmp, with result going
+   to TARGET if that's convenient.
+   If the call is not been inlined, return NULL_RTX.  */
 
 static rtx
-expand_builtin_atomic_exchange (machine_mode mode, tree exp, rtx target)
+inline_expand_builtin_bytecmp (tree exp, rtx target)
 {
-  rtx val, mem;
-  enum memmodel model;
-
-  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
+  tree fndecl = get_callee_fndecl (exp);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  bool is_ncmp = (fcode == BUILT_IN_STRNCMP || fcode == BUILT_IN_MEMCMP);
 
-  if (!flag_inline_atomics)
+  /* Do NOT apply this inlining expansion when optimizing for size or
+     optimization level below 2.  */
+  if (optimize < 2 || optimize_insn_for_size_p ())
     return NULL_RTX;
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+  gcc_checking_assert (fcode == BUILT_IN_STRCMP
+                      || fcode == BUILT_IN_STRNCMP
+                      || fcode == BUILT_IN_MEMCMP);
 
-  return expand_atomic_exchange (target, mem, val, model);
-}
+  /* On a target where the type of the call (int) has same or narrower presicion
+     than unsigned char, give up the inlining expansion.  */
+  if (TYPE_PRECISION (unsigned_char_type_node)
+      >= TYPE_PRECISION (TREE_TYPE (exp)))
+    return NULL_RTX;
 
-/* Expand the __atomic_compare_exchange intrinsic:
-       bool __atomic_compare_exchange (TYPE *object, TYPE *expect, 
-                                       TYPE desired, BOOL weak, 
-                                       enum memmodel success,
-                                       enum memmodel failure)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.  */
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
+  tree len3_tree = is_ncmp ? CALL_EXPR_ARG (exp, 2) : NULL_TREE;
 
-static rtx
-expand_builtin_atomic_compare_exchange (machine_mode mode, tree exp, 
-                                       rtx target)
-{
-  rtx expect, desired, mem, oldval;
-  rtx_code_label *label;
-  enum memmodel success, failure;
-  tree weak;
-  bool is_weak;
-  location_t loc
-    = expansion_point_location_if_in_system_header (input_location);
+  unsigned HOST_WIDE_INT len1 = 0;
+  unsigned HOST_WIDE_INT len2 = 0;
+  unsigned HOST_WIDE_INT len3 = 0;
 
-  success = get_memmodel (CALL_EXPR_ARG (exp, 4));
-  failure = get_memmodel (CALL_EXPR_ARG (exp, 5));
+  /* Get the object representation of the initializers of ARG1 and ARG2
+     as strings, provided they refer to constant objects, with their byte
+     sizes in LEN1 and LEN2, respectively.  */
+  const char *bytes1 = getbyterep (arg1, &len1);
+  const char *bytes2 = getbyterep (arg2, &len2);
 
-  if (failure > success)
+  /* Fail if neither argument refers to an initialized constant.  */
+  if (!bytes1 && !bytes2)
+    return NULL_RTX;
+
+  if (is_ncmp)
     {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "failure memory model cannot be stronger than success "
-                 "memory model for %<__atomic_compare_exchange%>");
-      success = MEMMODEL_SEQ_CST;
+      /* Fail if the memcmp/strncmp bound is not a constant.  */
+      if (!tree_fits_uhwi_p (len3_tree))
+       return NULL_RTX;
+
+      len3 = tree_to_uhwi (len3_tree);
+
+      if (fcode == BUILT_IN_MEMCMP)
+       {
+         /* Fail if the memcmp bound is greater than the size of either
+            of the two constant objects.  */
+         if ((bytes1 && len1 < len3)
+             || (bytes2 && len2 < len3))
+           return NULL_RTX;
+       }
     }
-  if (is_mm_release (failure) || is_mm_acq_rel (failure))
+
+  if (fcode != BUILT_IN_MEMCMP)
     {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid failure memory model for "
-                 "%<__atomic_compare_exchange%>");
-      failure = MEMMODEL_SEQ_CST;
-      success = MEMMODEL_SEQ_CST;
+      /* For string functions (i.e., strcmp and strncmp) reduce LEN1
+        and LEN2 to the length of the nul-terminated string stored
+        in each.  */
+      if (bytes1 != NULL)
+       len1 = strnlen (bytes1, len1) + 1;
+      if (bytes2 != NULL)
+       len2 = strnlen (bytes2, len2) + 1;
     }
 
-  if (!flag_inline_atomics)
-    return NULL_RTX;
+  /* See inline_string_cmp.  */
+  int const_str_n;
+  if (!len1)
+    const_str_n = 2;
+  else if (!len2)
+    const_str_n = 1;
+  else if (len2 > len1)
+    const_str_n = 1;
+  else
+    const_str_n = 2;
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  /* For strncmp only, compute the new bound as the smallest of
+     the lengths of the two strings (plus 1) and the bound provided
+     to the function.  */
+  unsigned HOST_WIDE_INT bound = (const_str_n == 1) ? len1 : len2;
+  if (is_ncmp && len3 < bound)
+    bound = len3;
 
-  expect = expand_normal (CALL_EXPR_ARG (exp, 1));
-  expect = convert_memory_address (Pmode, expect);
-  expect = gen_rtx_MEM (mode, expect);
-  desired = expand_expr_force_mode (CALL_EXPR_ARG (exp, 2), mode);
+  /* If the bound of the comparison is larger than the threshold,
+     do nothing.  */
+  if (bound > (unsigned HOST_WIDE_INT) param_builtin_string_cmp_inline_length)
+    return NULL_RTX;
 
-  weak = CALL_EXPR_ARG (exp, 3);
-  is_weak = false;
-  if (tree_fits_shwi_p (weak) && tree_to_shwi (weak) != 0)
-    is_weak = true;
+  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
 
-  if (target == const0_rtx)
-    target = NULL;
+  /* Now, start inline expansion the call.  */
+  return inline_string_cmp (target, (const_str_n == 1) ? arg2 : arg1,
+                           (const_str_n == 1) ? bytes1 : bytes2, bound,
+                           const_str_n, mode);
+}
 
-  /* Lest the rtl backend create a race condition with an imporoper store
-     to memory, always create a new pseudo for OLDVAL.  */
-  oldval = NULL;
-
-  if (!expand_atomic_compare_and_swap (&target, &oldval, mem, expect, desired,
-                                      is_weak, success, failure))
-    return NULL_RTX;
-
-  /* Conditionally store back to EXPECT, lest we create a race condition
-     with an improper store to memory.  */
-  /* ??? With a rearrangement of atomics at the gimple level, we can handle
-     the normal case where EXPECT is totally private, i.e. a register.  At
-     which point the store can be unconditional.  */
-  label = gen_label_rtx ();
-  emit_cmp_and_jump_insns (target, const0_rtx, NE, NULL,
-                          GET_MODE (target), 1, label);
-  emit_move_insn (expect, oldval);
-  emit_label (label);
-
-  return target;
-}
-
-/* Helper function for expand_ifn_atomic_compare_exchange - expand
-   internal ATOMIC_COMPARE_EXCHANGE call into __atomic_compare_exchange_N
-   call.  The weak parameter must be dropped to match the expected parameter
-   list and the expected argument changed from value to pointer to memory
-   slot.  */
-
-static void
-expand_ifn_atomic_compare_exchange_into_call (gcall *call, machine_mode mode)
-{
-  unsigned int z;
-  vec<tree, va_gc> *vec;
-
-  vec_alloc (vec, 5);
-  vec->quick_push (gimple_call_arg (call, 0));
-  tree expected = gimple_call_arg (call, 1);
-  rtx x = assign_stack_temp_for_type (mode, GET_MODE_SIZE (mode),
-                                     TREE_TYPE (expected));
-  rtx expd = expand_expr (expected, x, mode, EXPAND_NORMAL);
-  if (expd != x)
-    emit_move_insn (x, expd);
-  tree v = make_tree (TREE_TYPE (expected), x);
-  vec->quick_push (build1 (ADDR_EXPR,
-                          build_pointer_type (TREE_TYPE (expected)), v));
-  vec->quick_push (gimple_call_arg (call, 2));
-  /* Skip the boolean weak parameter.  */
-  for (z = 4; z < 6; z++)
-    vec->quick_push (gimple_call_arg (call, z));
-  /* At present we only have BUILT_IN_ATOMIC_COMPARE_EXCHANGE_{1,2,4,8,16}.  */
-  unsigned int bytes_log2 = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
-  gcc_assert (bytes_log2 < 5);
-  built_in_function fncode
-    = (built_in_function) ((int) BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1
-                          + bytes_log2);
-  tree fndecl = builtin_decl_explicit (fncode);
-  tree fn = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fndecl)),
-                   fndecl);
-  tree exp = build_call_vec (boolean_type_node, fn, vec);
-  tree lhs = gimple_call_lhs (call);
-  rtx boolret = expand_call (exp, NULL_RTX, lhs == NULL_TREE);
-  if (lhs)
-    {
-      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
-      if (GET_MODE (boolret) != mode)
-       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
-      x = force_reg (mode, x);
-      write_complex_part (target, boolret, true);
-      write_complex_part (target, x, false);
-    }
-}
-
-/* Expand IFN_ATOMIC_COMPARE_EXCHANGE internal function.  */
-
-void
-expand_ifn_atomic_compare_exchange (gcall *call)
+/* Expand a call to __builtin_speculation_safe_value_<N>.  MODE
+   represents the size of the first argument to that call, or VOIDmode
+   if the argument is a pointer.  IGNORE will be true if the result
+   isn't used.  */
+static rtx
+expand_speculation_safe_value (machine_mode mode, tree exp, rtx target,
+                              bool ignore)
 {
-  int size = tree_to_shwi (gimple_call_arg (call, 3)) & 255;
-  gcc_assert (size == 1 || size == 2 || size == 4 || size == 8 || size == 16);
-  machine_mode mode = int_mode_for_size (BITS_PER_UNIT * size, 0).require ();
-  rtx expect, desired, mem, oldval, boolret;
-  enum memmodel success, failure;
-  tree lhs;
-  bool is_weak;
-  location_t loc
-    = expansion_point_location_if_in_system_header (gimple_location (call));
+  rtx val, failsafe;
+  unsigned nargs = call_expr_nargs (exp);
 
-  success = get_memmodel (gimple_call_arg (call, 4));
-  failure = get_memmodel (gimple_call_arg (call, 5));
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
 
-  if (failure > success)
+  if (mode == VOIDmode)
     {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "failure memory model cannot be stronger than success "
-                 "memory model for %<__atomic_compare_exchange%>");
-      success = MEMMODEL_SEQ_CST;
+      mode = TYPE_MODE (TREE_TYPE (arg0));
+      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT);
     }
 
-  if (is_mm_release (failure) || is_mm_acq_rel (failure))
-    {
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid failure memory model for "
-                 "%<__atomic_compare_exchange%>");
-      failure = MEMMODEL_SEQ_CST;
-      success = MEMMODEL_SEQ_CST;
-    }
+  val = expand_expr (arg0, NULL_RTX, mode, EXPAND_NORMAL);
 
-  if (!flag_inline_atomics)
+  /* An optional second argument can be used as a failsafe value on
+     some machines.  If it isn't present, then the failsafe value is
+     assumed to be 0.  */
+  if (nargs > 1)
     {
-      expand_ifn_atomic_compare_exchange_into_call (call, mode);
-      return;
+      tree arg1 = CALL_EXPR_ARG (exp, 1);
+      failsafe = expand_expr (arg1, NULL_RTX, mode, EXPAND_NORMAL);
     }
+  else
+    failsafe = const0_rtx;
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (gimple_call_arg (call, 0), mode);
-
-  expect = expand_expr_force_mode (gimple_call_arg (call, 1), mode);
-  desired = expand_expr_force_mode (gimple_call_arg (call, 2), mode);
-
-  is_weak = (tree_to_shwi (gimple_call_arg (call, 3)) & 256) != 0;
+  /* If the result isn't used, the behavior is undefined.  It would be
+     nice to emit a warning here, but path splitting means this might
+     happen with legitimate code.  So simply drop the builtin
+     expansion in that case; we've handled any side-effects above.  */
+  if (ignore)
+    return const0_rtx;
 
-  boolret = NULL;
-  oldval = NULL;
+  /* If we don't have a suitable target, create one to hold the result.  */
+  if (target == NULL || GET_MODE (target) != mode)
+    target = gen_reg_rtx (mode);
 
-  if (!expand_atomic_compare_and_swap (&boolret, &oldval, mem, expect, desired,
-                                      is_weak, success, failure))
-    {
-      expand_ifn_atomic_compare_exchange_into_call (call, mode);
-      return;
-    }
+  if (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)
+    val = convert_modes (mode, VOIDmode, val, false);
 
-  lhs = gimple_call_lhs (call);
-  if (lhs)
-    {
-      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
-      if (GET_MODE (boolret) != mode)
-       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
-      write_complex_part (target, boolret, true);
-      write_complex_part (target, oldval, false);
-    }
+  return targetm.speculation_safe_value (mode, target, val, failsafe);
 }
 
-/* Expand the __atomic_load intrinsic:
-       TYPE __atomic_load (TYPE *object, enum memmodel)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.  */
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
 
-static rtx
-expand_builtin_atomic_load (machine_mode mode, tree exp, rtx target)
+rtx
+expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
+               int ignore)
 {
-  rtx mem;
-  enum memmodel model;
-
-  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
-  if (is_mm_release (model) || is_mm_acq_rel (model))
-    {
-      location_t loc
-       = expansion_point_location_if_in_system_header (input_location);
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid memory model for %<__atomic_load%>");
-      model = MEMMODEL_SEQ_CST;
-    }
-
-  if (!flag_inline_atomics)
-    return NULL_RTX;
+  tree fndecl = get_callee_fndecl (exp);
+  machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
+  int flags;
 
-  /* Expand the operand.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
 
-  return expand_atomic_load (target, mem, model);
-}
+  /* When ASan is enabled, we don't want to expand some memory/string
+     builtins and rely on libsanitizer's hooks.  This allows us to avoid
+     redundant checks and be sure, that possible overflow will be detected
+     by ASan.  */
 
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  if ((flag_sanitize & SANITIZE_ADDRESS) && asan_intercepted_p (fcode))
+    return expand_call (exp, target, ignore);
 
-/* Expand the __atomic_store intrinsic:
-       void __atomic_store (TYPE *object, TYPE desired, enum memmodel)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.  */
+  /* When not optimizing, generate calls to library functions for a certain
+     set of builtins.  */
+  if (!optimize
+      && !called_as_built_in (fndecl)
+      && fcode != BUILT_IN_FORK
+      && fcode != BUILT_IN_EXECL
+      && fcode != BUILT_IN_EXECV
+      && fcode != BUILT_IN_EXECLP
+      && fcode != BUILT_IN_EXECLE
+      && fcode != BUILT_IN_EXECVP
+      && fcode != BUILT_IN_EXECVE
+      && fcode != BUILT_IN_CLEAR_CACHE
+      && !ALLOCA_FUNCTION_CODE_P (fcode)
+      && fcode != BUILT_IN_FREE)
+    return expand_call (exp, target, ignore);
 
-static rtx
-expand_builtin_atomic_store (machine_mode mode, tree exp)
-{
-  rtx mem, val;
-  enum memmodel model;
+  /* The built-in function expanders test for target == const0_rtx
+     to determine whether the function's result will be ignored.  */
+  if (ignore)
+    target = const0_rtx;
 
-  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
-  if (!(is_mm_relaxed (model) || is_mm_seq_cst (model)
-       || is_mm_release (model)))
+  /* If the result of a pure or const built-in function is ignored, and
+     none of its arguments are volatile, we can avoid expanding the
+     built-in call and just evaluate the arguments for side-effects.  */
+  if (target == const0_rtx
+      && ((flags = flags_from_decl_or_type (fndecl)) & (ECF_CONST | ECF_PURE))
+      && !(flags & ECF_LOOPING_CONST_OR_PURE))
     {
-      location_t loc
-       = expansion_point_location_if_in_system_header (input_location);
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid memory model for %<__atomic_store%>");
-      model = MEMMODEL_SEQ_CST;
-    }
-
-  if (!flag_inline_atomics)
-    return NULL_RTX;
-
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+      bool volatilep = false;
+      tree arg;
+      call_expr_arg_iterator iter;
 
-  return expand_atomic_store (mem, val, model, false);
-}
+      FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+       if (TREE_THIS_VOLATILE (arg))
+         {
+           volatilep = true;
+           break;
+         }
 
-/* Expand the __atomic_fetch_XXX intrinsic:
-       TYPE __atomic_fetch_XXX (TYPE *object, TYPE val, enum memmodel)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.
-   CODE is the operation, PLUS, MINUS, ADD, XOR, or IOR.
-   FETCH_AFTER is true if returning the result of the operation.
-   FETCH_AFTER is false if returning the value before the operation.
-   IGNORE is true if the result is not used.
-   EXT_CALL is the correct builtin for an external call if this cannot be
-   resolved to an instruction sequence.  */
+      if (! volatilep)
+       {
+         FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+           expand_expr (arg, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
+    }
 
-static rtx
-expand_builtin_atomic_fetch_op (machine_mode mode, tree exp, rtx target,
-                               enum rtx_code code, bool fetch_after,
-                               bool ignore, enum built_in_function ext_call)
-{
-  rtx val, mem, ret;
-  enum memmodel model;
-  tree fndecl;
-  tree addr;
+  switch (fcode)
+    {
+    CASE_FLT_FN (BUILT_IN_FABS):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
+    case BUILT_IN_FABSD32:
+    case BUILT_IN_FABSD64:
+    case BUILT_IN_FABSD128:
+      target = expand_builtin_fabs (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
 
-  model = get_memmodel (CALL_EXPR_ARG (exp, 2));
+    CASE_FLT_FN (BUILT_IN_COPYSIGN):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_COPYSIGN):
+      target = expand_builtin_copysign (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
 
-  /* Expand the operands.  */
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
+      /* Just do a normal library call if we were unable to fold
+        the values.  */
+    CASE_FLT_FN (BUILT_IN_CABS):
+      break;
 
-  /* Only try generating instructions if inlining is turned on.  */
-  if (flag_inline_atomics)
-    {
-      ret = expand_atomic_fetch_op (target, mem, val, code, model, fetch_after);
-      if (ret)
-       return ret;
-    }
+    CASE_FLT_FN (BUILT_IN_FMA):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FMA):
+      target = expand_builtin_mathfn_ternary (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
 
-  /* Return if a different routine isn't needed for the library call.  */
-  if (ext_call == BUILT_IN_NONE)
-    return NULL_RTX;
+    CASE_FLT_FN (BUILT_IN_ILOGB):
+      if (! flag_unsafe_math_optimizations)
+       break;
+      gcc_fallthrough ();
+    CASE_FLT_FN (BUILT_IN_ISINF):
+    CASE_FLT_FN (BUILT_IN_FINITE):
+    case BUILT_IN_ISFINITE:
+    case BUILT_IN_ISNORMAL:
+      target = expand_builtin_interclass_mathfn (exp, target);
+      if (target)
+       return target;
+      break;
 
-  /* Change the call to the specified function.  */
-  fndecl = get_callee_fndecl (exp);
-  addr = CALL_EXPR_FN (exp);
-  STRIP_NOPS (addr);
+    CASE_FLT_FN (BUILT_IN_ICEIL):
+    CASE_FLT_FN (BUILT_IN_LCEIL):
+    CASE_FLT_FN (BUILT_IN_LLCEIL):
+    CASE_FLT_FN (BUILT_IN_LFLOOR):
+    CASE_FLT_FN (BUILT_IN_IFLOOR):
+    CASE_FLT_FN (BUILT_IN_LLFLOOR):
+      target = expand_builtin_int_roundingfn (exp, target);
+      if (target)
+       return target;
+      break;
 
-  gcc_assert (TREE_OPERAND (addr, 0) == fndecl);
-  TREE_OPERAND (addr, 0) = builtin_decl_explicit (ext_call);
+    CASE_FLT_FN (BUILT_IN_IRINT):
+    CASE_FLT_FN (BUILT_IN_LRINT):
+    CASE_FLT_FN (BUILT_IN_LLRINT):
+    CASE_FLT_FN (BUILT_IN_IROUND):
+    CASE_FLT_FN (BUILT_IN_LROUND):
+    CASE_FLT_FN (BUILT_IN_LLROUND):
+      target = expand_builtin_int_roundingfn_2 (exp, target);
+      if (target)
+       return target;
+      break;
 
-  /* If we will emit code after the call, the call cannot be a tail call.
-     If it is emitted as a tail call, a barrier is emitted after it, and
-     then all trailing code is removed.  */
-  if (!ignore)
-    CALL_EXPR_TAILCALL (exp) = 0;
+    CASE_FLT_FN (BUILT_IN_POWI):
+      target = expand_builtin_powi (exp, target);
+      if (target)
+       return target;
+      break;
 
-  /* Expand the call here so we can emit trailing code.  */
-  ret = expand_call (exp, target, ignore);
+    CASE_FLT_FN (BUILT_IN_CEXPI):
+      target = expand_builtin_cexpi (exp, target);
+      gcc_assert (target);
+      return target;
 
-  /* Replace the original function just in case it matters.  */
-  TREE_OPERAND (addr, 0) = fndecl;
+    CASE_FLT_FN (BUILT_IN_SIN):
+    CASE_FLT_FN (BUILT_IN_COS):
+      if (! flag_unsafe_math_optimizations)
+       break;
+      target = expand_builtin_mathfn_3 (exp, target, subtarget);
+      if (target)
+       return target;
+      break;
 
-  /* Then issue the arithmetic correction to return the right result.  */
-  if (!ignore)
-    {
-      if (code == NOT)
-       {
-         ret = expand_simple_binop (mode, AND, ret, val, NULL_RTX, true,
-                                    OPTAB_LIB_WIDEN);
-         ret = expand_simple_unop (mode, NOT, ret, target, true);
-       }
+    CASE_FLT_FN (BUILT_IN_SINCOS):
+      if (! flag_unsafe_math_optimizations)
+       break;
+      target = expand_builtin_sincos (exp);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_APPLY_ARGS:
+      return expand_builtin_apply_args ();
+
+      /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
+        FUNCTION with a copy of the parameters described by
+        ARGUMENTS, and ARGSIZE.  It returns a block of memory
+        allocated on the stack into which is stored all the registers
+        that might possibly be used for returning the result of a
+        function.  ARGUMENTS is the value returned by
+        __builtin_apply_args.  ARGSIZE is the number of bytes of
+        arguments that must be copied.  ??? How should this value be
+        computed?  We'll also need a safe worst case value for varargs
+        functions.  */
+    case BUILT_IN_APPLY:
+      if (!validate_arglist (exp, POINTER_TYPE,
+                            POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+         && !validate_arglist (exp, REFERENCE_TYPE,
+                               POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+       return const0_rtx;
       else
-       ret = expand_simple_binop (mode, code, ret, val, target, true,
-                                  OPTAB_LIB_WIDEN);
-    }
-  return ret;
-}
+       {
+         rtx ops[3];
 
-/* Expand IFN_ATOMIC_BIT_TEST_AND_* internal function.  */
+         ops[0] = expand_normal (CALL_EXPR_ARG (exp, 0));
+         ops[1] = expand_normal (CALL_EXPR_ARG (exp, 1));
+         ops[2] = expand_normal (CALL_EXPR_ARG (exp, 2));
 
-void
-expand_ifn_atomic_bit_test_and (gcall *call)
-{
-  tree ptr = gimple_call_arg (call, 0);
-  tree bit = gimple_call_arg (call, 1);
-  tree flag = gimple_call_arg (call, 2);
-  tree lhs = gimple_call_lhs (call);
-  enum memmodel model = MEMMODEL_SYNC_SEQ_CST;
-  machine_mode mode = TYPE_MODE (TREE_TYPE (flag));
-  enum rtx_code code;
-  optab optab;
-  class expand_operand ops[5];
+         return expand_builtin_apply (ops[0], ops[1], ops[2]);
+       }
 
-  gcc_assert (flag_inline_atomics);
+      /* __builtin_return (RESULT) causes the function to return the
+        value described by RESULT.  RESULT is address of the block of
+        memory returned by __builtin_apply.  */
+    case BUILT_IN_RETURN:
+      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+       expand_builtin_return (expand_normal (CALL_EXPR_ARG (exp, 0)));
+      return const0_rtx;
 
-  if (gimple_call_num_args (call) == 4)
-    model = get_memmodel (gimple_call_arg (call, 3));
+    case BUILT_IN_SAVEREGS:
+      return expand_builtin_saveregs ();
 
-  rtx mem = get_builtin_sync_mem (ptr, mode);
-  rtx val = expand_expr_force_mode (bit, mode);
+    case BUILT_IN_VA_ARG_PACK:
+      /* All valid uses of __builtin_va_arg_pack () are removed during
+        inlining.  */
+      error ("invalid use of %<__builtin_va_arg_pack ()%>");
+      return const0_rtx;
 
-  switch (gimple_call_internal_fn (call))
-    {
-    case IFN_ATOMIC_BIT_TEST_AND_SET:
-      code = IOR;
-      optab = atomic_bit_test_and_set_optab;
-      break;
-    case IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT:
-      code = XOR;
-      optab = atomic_bit_test_and_complement_optab;
-      break;
-    case IFN_ATOMIC_BIT_TEST_AND_RESET:
-      code = AND;
-      optab = atomic_bit_test_and_reset_optab;
-      break;
-    default:
-      gcc_unreachable ();
-    }
+    case BUILT_IN_VA_ARG_PACK_LEN:
+      /* All valid uses of __builtin_va_arg_pack_len () are removed during
+        inlining.  */
+      error ("invalid use of %<__builtin_va_arg_pack_len ()%>");
+      return const0_rtx;
 
-  if (lhs == NULL_TREE)
-    {
-      val = expand_simple_binop (mode, ASHIFT, const1_rtx,
-                                val, NULL_RTX, true, OPTAB_DIRECT);
-      if (code == AND)
-       val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
-      expand_atomic_fetch_op (const0_rtx, mem, val, code, model, false);
-      return;
-    }
+      /* Return the address of the first anonymous stack arg.  */
+    case BUILT_IN_NEXT_ARG:
+      if (fold_builtin_next_arg (exp, false))
+       return const0_rtx;
+      return expand_builtin_next_arg ();
 
-  rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
-  enum insn_code icode = direct_optab_handler (optab, mode);
-  gcc_assert (icode != CODE_FOR_nothing);
-  create_output_operand (&ops[0], target, mode);
-  create_fixed_operand (&ops[1], mem);
-  create_convert_operand_to (&ops[2], val, mode, true);
-  create_integer_operand (&ops[3], model);
-  create_integer_operand (&ops[4], integer_onep (flag));
-  if (maybe_expand_insn (icode, 5, ops))
-    return;
+    case BUILT_IN_CLEAR_CACHE:
+      expand_builtin___clear_cache (exp);
+      return const0_rtx;
 
-  rtx bitval = val;
-  val = expand_simple_binop (mode, ASHIFT, const1_rtx,
-                            val, NULL_RTX, true, OPTAB_DIRECT);
-  rtx maskval = val;
-  if (code == AND)
-    val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
-  rtx result = expand_atomic_fetch_op (gen_reg_rtx (mode), mem, val,
-                                      code, model, false);
-  if (integer_onep (flag))
-    {
-      result = expand_simple_binop (mode, ASHIFTRT, result, bitval,
-                                   NULL_RTX, true, OPTAB_DIRECT);
-      result = expand_simple_binop (mode, AND, result, const1_rtx, target,
-                                   true, OPTAB_DIRECT);
-    }
-  else
-    result = expand_simple_binop (mode, AND, result, maskval, target, true,
-                                 OPTAB_DIRECT);
-  if (result != target)
-    emit_move_insn (target, result);
-}
+    case BUILT_IN_CLASSIFY_TYPE:
+      return expand_builtin_classify_type (exp);
 
-/* Expand an atomic clear operation.
-       void _atomic_clear (BOOL *obj, enum memmodel)
-   EXP is the call expression.  */
+    case BUILT_IN_CONSTANT_P:
+      return const0_rtx;
 
-static rtx
-expand_builtin_atomic_clear (tree exp) 
-{
-  machine_mode mode;
-  rtx mem, ret;
-  enum memmodel model;
+    case BUILT_IN_FRAME_ADDRESS:
+    case BUILT_IN_RETURN_ADDRESS:
+      return expand_builtin_frame_address (fndecl, exp);
 
-  mode = int_mode_for_size (BOOL_TYPE_SIZE, 0).require ();
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+    /* Returns the address of the area where the structure is returned.
+       0 otherwise.  */
+    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
+      if (call_expr_nargs (exp) != 0
+         || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
+         || !MEM_P (DECL_RTL (DECL_RESULT (current_function_decl))))
+       return const0_rtx;
+      else
+       return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
 
-  if (is_mm_consume (model) || is_mm_acquire (model) || is_mm_acq_rel (model))
-    {
-      location_t loc
-       = expansion_point_location_if_in_system_header (input_location);
-      warning_at (loc, OPT_Winvalid_memory_model,
-                 "invalid memory model for %<__atomic_store%>");
-      model = MEMMODEL_SEQ_CST;
-    }
+    CASE_BUILT_IN_ALLOCA:
+      target = expand_builtin_alloca (exp);
+      if (target)
+       return target;
+      break;
 
-  /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
-     Failing that, a store is issued by __atomic_store.  The only way this can
-     fail is if the bool type is larger than a word size.  Unlikely, but
-     handle it anyway for completeness.  Assume a single threaded model since
-     there is no atomic support in this case, and no barriers are required.  */
-  ret = expand_atomic_store (mem, const0_rtx, model, true);
-  if (!ret)
-    emit_move_insn (mem, const0_rtx);
-  return const0_rtx;
-}
+    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
+      return expand_asan_emit_allocas_unpoison (exp);
 
-/* Expand an atomic test_and_set operation.
-       bool _atomic_test_and_set (BOOL *obj, enum memmodel)
-   EXP is the call expression.  */
+    case BUILT_IN_STACK_SAVE:
+      return expand_stack_save ();
 
-static rtx
-expand_builtin_atomic_test_and_set (tree exp, rtx target)
-{
-  rtx mem;
-  enum memmodel model;
-  machine_mode mode;
+    case BUILT_IN_STACK_RESTORE:
+      expand_stack_restore (CALL_EXPR_ARG (exp, 0));
+      return const0_rtx;
 
-  mode = int_mode_for_size (BOOL_TYPE_SIZE, 0).require ();
-  mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
-  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+    case BUILT_IN_BSWAP16:
+    case BUILT_IN_BSWAP32:
+    case BUILT_IN_BSWAP64:
+    case BUILT_IN_BSWAP128:
+      target = expand_builtin_bswap (target_mode, exp, target, subtarget);
+      if (target)
+       return target;
+      break;
 
-  return expand_atomic_test_and_set (target, mem, model);
-}
+    CASE_INT_FN (BUILT_IN_FFS):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, ffs_optab);
+      if (target)
+       return target;
+      break;
 
+    CASE_INT_FN (BUILT_IN_CLZ):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, clz_optab);
+      if (target)
+       return target;
+      break;
 
-/* Return true if (optional) argument ARG1 of size ARG0 is always lock free on
-   this architecture.  If ARG1 is NULL, use typical alignment for size ARG0.  */
+    CASE_INT_FN (BUILT_IN_CTZ):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, ctz_optab);
+      if (target)
+       return target;
+      break;
 
-static tree
-fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
-{
-  int size;
-  machine_mode mode;
-  unsigned int mode_align, type_align;
+    CASE_INT_FN (BUILT_IN_CLRSB):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, clrsb_optab);
+      if (target)
+       return target;
+      break;
 
-  if (TREE_CODE (arg0) != INTEGER_CST)
-    return NULL_TREE;
+    CASE_INT_FN (BUILT_IN_POPCOUNT):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, popcount_optab);
+      if (target)
+       return target;
+      break;
 
-  /* We need a corresponding integer mode for the access to be lock-free.  */
-  size = INTVAL (expand_normal (arg0)) * BITS_PER_UNIT;
-  if (!int_mode_for_size (size, 0).exists (&mode))
-    return boolean_false_node;
+    CASE_INT_FN (BUILT_IN_PARITY):
+      target = expand_builtin_unop (target_mode, exp, target,
+                                   subtarget, parity_optab);
+      if (target)
+       return target;
+      break;
 
-  mode_align = GET_MODE_ALIGNMENT (mode);
+    case BUILT_IN_STRLEN:
+      target = expand_builtin_strlen (exp, target, target_mode);
+      if (target)
+       return target;
+      break;
 
-  if (TREE_CODE (arg1) == INTEGER_CST)
-    {
-      unsigned HOST_WIDE_INT val = UINTVAL (expand_normal (arg1));
+    case BUILT_IN_STRNLEN:
+      target = expand_builtin_strnlen (exp, target, target_mode);
+      if (target)
+       return target;
+      break;
 
-      /* Either this argument is null, or it's a fake pointer encoding
-         the alignment of the object.  */
-      val = least_bit_hwi (val);
-      val *= BITS_PER_UNIT;
+    case BUILT_IN_STRCAT:
+      target = expand_builtin_strcat (exp);
+      if (target)
+       return target;
+      break;
 
-      if (val == 0 || mode_align < val)
-        type_align = mode_align;
-      else
-        type_align = val;
-    }
-  else
-    {
-      tree ttype = TREE_TYPE (arg1);
+    case BUILT_IN_GETTEXT:
+    case BUILT_IN_PUTS:
+    case BUILT_IN_PUTS_UNLOCKED:
+    case BUILT_IN_STRDUP:
+      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
+      break;
 
-      /* This function is usually invoked and folded immediately by the front
-        end before anything else has a chance to look at it.  The pointer
-        parameter at this point is usually cast to a void *, so check for that
-        and look past the cast.  */
-      if (CONVERT_EXPR_P (arg1)
-         && POINTER_TYPE_P (ttype)
-         && VOID_TYPE_P (TREE_TYPE (ttype))
-         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (arg1, 0))))
-       arg1 = TREE_OPERAND (arg1, 0);
+    case BUILT_IN_INDEX:
+    case BUILT_IN_RINDEX:
+    case BUILT_IN_STRCHR:
+    case BUILT_IN_STRRCHR:
+      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
+      break;
 
-      ttype = TREE_TYPE (arg1);
-      gcc_assert (POINTER_TYPE_P (ttype));
+    case BUILT_IN_FPUTS:
+    case BUILT_IN_FPUTS_UNLOCKED:
+      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
+      break;
 
-      /* Get the underlying type of the object.  */
-      ttype = TREE_TYPE (ttype);
-      type_align = TYPE_ALIGN (ttype);
-    }
+    case BUILT_IN_STRNDUP:
+      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+       check_read_access (exp, CALL_EXPR_ARG (exp, 0), CALL_EXPR_ARG (exp, 1));
+      break;
 
-  /* If the object has smaller alignment, the lock free routines cannot
-     be used.  */
-  if (type_align < mode_align)
-    return boolean_false_node;
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRPBRK:
+    case BUILT_IN_STRSPN:
+    case BUILT_IN_STRCSPN:
+    case BUILT_IN_STRSTR:
+      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+       {
+         check_read_access (exp, CALL_EXPR_ARG (exp, 0));
+         check_read_access (exp, CALL_EXPR_ARG (exp, 1));
+       }
+      break;
 
-  /* Check if a compare_and_swap pattern exists for the mode which represents
-     the required size.  The pattern is not allowed to fail, so the existence
-     of the pattern indicates support is present.  Also require that an
-     atomic load exists for the required size.  */
-  if (can_compare_and_swap_p (mode, true) && can_atomic_load_p (mode))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
-}
-
-/* Return true if the parameters to call EXP represent an object which will
-   always generate lock free instructions.  The first argument represents the
-   size of the object, and the second parameter is a pointer to the object 
-   itself.  If NULL is passed for the object, then the result is based on 
-   typical alignment for an object of the specified size.  Otherwise return 
-   false.  */
+    case BUILT_IN_STRCPY:
+      target = expand_builtin_strcpy (exp, target);
+      if (target)
+       return target;
+      break;
 
-static rtx
-expand_builtin_atomic_always_lock_free (tree exp)
-{
-  tree size;
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-  tree arg1 = CALL_EXPR_ARG (exp, 1);
+    case BUILT_IN_STRNCAT:
+      target = expand_builtin_strncat (exp, target);
+      if (target)
+       return target;
+      break;
 
-  if (TREE_CODE (arg0) != INTEGER_CST)
-    {
-      error ("non-constant argument 1 to %qs", "__atomic_always_lock_free");
-      return const0_rtx;
-    }
+    case BUILT_IN_STRNCPY:
+      target = expand_builtin_strncpy (exp, target);
+      if (target)
+       return target;
+      break;
 
-  size = fold_builtin_atomic_always_lock_free (arg0, arg1);
-  if (size == boolean_true_node)
-    return const1_rtx;
-  return const0_rtx;
-}
+    case BUILT_IN_STPCPY:
+      target = expand_builtin_stpcpy (exp, target, mode);
+      if (target)
+       return target;
+      break;
 
-/* Return a one or zero if it can be determined that object ARG1 of size ARG 
-   is lock free on this architecture.  */
+    case BUILT_IN_STPNCPY:
+      target = expand_builtin_stpncpy (exp, target);
+      if (target)
+       return target;
+      break;
 
-static tree
-fold_builtin_atomic_is_lock_free (tree arg0, tree arg1)
-{
-  if (!flag_inline_atomics)
-    return NULL_TREE;
-  
-  /* If it isn't always lock free, don't generate a result.  */
-  if (fold_builtin_atomic_always_lock_free (arg0, arg1) == boolean_true_node)
-    return boolean_true_node;
+    case BUILT_IN_MEMCHR:
+      target = expand_builtin_memchr (exp, target);
+      if (target)
+       return target;
+      break;
 
-  return NULL_TREE;
-}
+    case BUILT_IN_MEMCPY:
+      target = expand_builtin_memcpy (exp, target);
+      if (target)
+       return target;
+      break;
 
-/* Return true if the parameters to call EXP represent an object which will
-   always generate lock free instructions.  The first argument represents the
-   size of the object, and the second parameter is a pointer to the object 
-   itself.  If NULL is passed for the object, then the result is based on 
-   typical alignment for an object of the specified size.  Otherwise return 
-   NULL*/
+    case BUILT_IN_MEMMOVE:
+      target = expand_builtin_memmove (exp, target);
+      if (target)
+       return target;
+      break;
 
-static rtx
-expand_builtin_atomic_is_lock_free (tree exp)
-{
-  tree size;
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-  tree arg1 = CALL_EXPR_ARG (exp, 1);
+    case BUILT_IN_MEMPCPY:
+      target = expand_builtin_mempcpy (exp, target);
+      if (target)
+       return target;
+      break;
 
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
-    {
-      error ("non-integer argument 1 to %qs", "__atomic_is_lock_free");
-      return NULL_RTX;
-    }
+    case BUILT_IN_MEMSET:
+      target = expand_builtin_memset (exp, target, mode);
+      if (target)
+       return target;
+      break;
 
-  if (!flag_inline_atomics)
-    return NULL_RTX; 
+    case BUILT_IN_BZERO:
+      target = expand_builtin_bzero (exp);
+      if (target)
+       return target;
+      break;
 
-  /* If the value is known at compile time, return the RTX for it.  */
-  size = fold_builtin_atomic_is_lock_free (arg0, arg1);
-  if (size == boolean_true_node)
-    return const1_rtx;
+    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
+       back to a BUILT_IN_STRCMP. Remember to delete the 3rd parameter
+       when changing it to a strcmp call.  */
+    case BUILT_IN_STRCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, true);
+      if (target)
+       return target;
 
-  return NULL_RTX;
-}
+      /* Change this call back to a BUILT_IN_STRCMP.  */
+      TREE_OPERAND (exp, 1)
+       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRCMP));
 
-/* Expand the __atomic_thread_fence intrinsic:
-       void __atomic_thread_fence (enum memmodel)
-   EXP is the CALL_EXPR.  */
+      /* Delete the last parameter.  */
+      unsigned int i;
+      vec<tree, va_gc> *arg_vec;
+      vec_alloc (arg_vec, 2);
+      for (i = 0; i < 2; i++)
+       arg_vec->quick_push (CALL_EXPR_ARG (exp, i));
+      exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), arg_vec);
+      /* FALLTHROUGH */
 
-static void
-expand_builtin_atomic_thread_fence (tree exp)
-{
-  enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
-  expand_mem_thread_fence (model);
-}
+    case BUILT_IN_STRCMP:
+      target = expand_builtin_strcmp (exp, target);
+      if (target)
+       return target;
+      break;
 
-/* Expand the __atomic_signal_fence intrinsic:
-       void __atomic_signal_fence (enum memmodel)
-   EXP is the CALL_EXPR.  */
+    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
+       back to a BUILT_IN_STRNCMP.  */
+    case BUILT_IN_STRNCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, true);
+      if (target)
+       return target;
 
-static void
-expand_builtin_atomic_signal_fence (tree exp)
-{
-  enum memmodel model = get_memmodel (CALL_EXPR_ARG (exp, 0));
-  expand_mem_signal_fence (model);
-}
+      /* Change it back to a BUILT_IN_STRNCMP.  */
+      TREE_OPERAND (exp, 1)
+       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRNCMP));
+      /* FALLTHROUGH */
 
-/* Expand the __sync_synchronize intrinsic.  */
+    case BUILT_IN_STRNCMP:
+      target = expand_builtin_strncmp (exp, target, mode);
+      if (target)
+       return target;
+      break;
 
-static void
-expand_builtin_sync_synchronize (void)
-{
-  expand_mem_thread_fence (MEMMODEL_SYNC_SEQ_CST);
-}
+    case BUILT_IN_BCMP:
+    case BUILT_IN_MEMCMP:
+    case BUILT_IN_MEMCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, fcode == BUILT_IN_MEMCMP_EQ);
+      if (target)
+       return target;
+      if (fcode == BUILT_IN_MEMCMP_EQ)
+       {
+         tree newdecl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+         TREE_OPERAND (exp, 1) = build_fold_addr_expr (newdecl);
+       }
+      break;
 
-static rtx
-expand_builtin_thread_pointer (tree exp, rtx target)
-{
-  enum insn_code icode;
-  if (!validate_arglist (exp, VOID_TYPE))
-    return const0_rtx;
-  icode = direct_optab_handler (get_thread_pointer_optab, Pmode);
-  if (icode != CODE_FOR_nothing)
-    {
-      class expand_operand op;
-      /* If the target is not sutitable then create a new target. */
-      if (target == NULL_RTX
-         || !REG_P (target)
-         || GET_MODE (target) != Pmode)
-       target = gen_reg_rtx (Pmode);
-      create_output_operand (&op, target, Pmode);
-      expand_insn (icode, 1, &op);
-      return target;
-    }
-  error ("%<__builtin_thread_pointer%> is not supported on this target");
-  return const0_rtx;
-}
+    case BUILT_IN_SETJMP:
+      /* This should have been lowered to the builtins below.  */
+      gcc_unreachable ();
 
-static void
-expand_builtin_set_thread_pointer (tree exp)
-{
-  enum insn_code icode;
-  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-    return;
-  icode = direct_optab_handler (set_thread_pointer_optab, Pmode);
-  if (icode != CODE_FOR_nothing)
-    {
-      class expand_operand op;
-      rtx val = expand_expr (CALL_EXPR_ARG (exp, 0), NULL_RTX,
-                            Pmode, EXPAND_NORMAL);      
-      create_input_operand (&op, val, Pmode);
-      expand_insn (icode, 1, &op);
-      return;
-    }
-  error ("%<__builtin_set_thread_pointer%> is not supported on this target");
-}
+    case BUILT_IN_SETJMP_SETUP:
+      /* __builtin_setjmp_setup is passed a pointer to an array of five words
+          and the receiver label.  */
+      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+       {
+         rtx buf_addr = expand_expr (CALL_EXPR_ARG (exp, 0), subtarget,
+                                     VOIDmode, EXPAND_NORMAL);
+         tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 1), 0);
+         rtx_insn *label_r = label_rtx (label);
 
-\f
-/* Emit code to restore the current value of stack.  */
+         /* This is copied from the handling of non-local gotos.  */
+         expand_builtin_setjmp_setup (buf_addr, label_r);
+         nonlocal_goto_handler_labels
+           = gen_rtx_INSN_LIST (VOIDmode, label_r,
+                                nonlocal_goto_handler_labels);
+         /* ??? Do not let expand_label treat us as such since we would
+            not want to be both on the list of non-local labels and on
+            the list of forced labels.  */
+         FORCED_LABEL (label) = 0;
+         return const0_rtx;
+       }
+      break;
 
-static void
-expand_stack_restore (tree var)
-{
-  rtx_insn *prev;
-  rtx sa = expand_normal (var);
-
-  sa = convert_memory_address (Pmode, sa);
-
-  prev = get_last_insn ();
-  emit_stack_restore (SAVE_BLOCK, sa);
+    case BUILT_IN_SETJMP_RECEIVER:
+       /* __builtin_setjmp_receiver is passed the receiver label.  */
+      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+       {
+         tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 0), 0);
+         rtx_insn *label_r = label_rtx (label);
 
-  record_new_stack_level ();
+         expand_builtin_setjmp_receiver (label_r);
+         return const0_rtx;
+       }
+      break;
 
-  fixup_args_size_notes (prev, get_last_insn (), 0);
-}
+      /* __builtin_longjmp is passed a pointer to an array of five words.
+        It's similar to the C library longjmp function but works with
+        __builtin_setjmp above.  */
+    case BUILT_IN_LONGJMP:
+      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+       {
+         rtx buf_addr = expand_expr (CALL_EXPR_ARG (exp, 0), subtarget,
+                                     VOIDmode, EXPAND_NORMAL);
+         rtx value = expand_normal (CALL_EXPR_ARG (exp, 1));
 
-/* Emit code to save the current value of stack.  */
+         if (value != const1_rtx)
+           {
+             error ("%<__builtin_longjmp%> second argument must be 1");
+             return const0_rtx;
+           }
 
-static rtx
-expand_stack_save (void)
-{
-  rtx ret = NULL_RTX;
+         expand_builtin_longjmp (buf_addr, value);
+         return const0_rtx;
+       }
+      break;
 
-  emit_stack_save (SAVE_BLOCK, &ret);
-  return ret;
-}
+    case BUILT_IN_NONLOCAL_GOTO:
+      target = expand_builtin_nonlocal_goto (exp);
+      if (target)
+       return target;
+      break;
 
-/* Emit code to get the openacc gang, worker or vector id or size.  */
+      /* This updates the setjmp buffer that is its argument with the value
+        of the current stack pointer.  */
+    case BUILT_IN_UPDATE_SETJMP_BUF:
+      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+       {
+         rtx buf_addr
+           = expand_normal (CALL_EXPR_ARG (exp, 0));
 
-static rtx
-expand_builtin_goacc_parlevel_id_size (tree exp, rtx target, int ignore)
-{
-  const char *name;
-  rtx fallback_retval;
-  rtx_insn *(*gen_fn) (rtx, rtx);
-  switch (DECL_FUNCTION_CODE (get_callee_fndecl (exp)))
-    {
-    case BUILT_IN_GOACC_PARLEVEL_ID:
-      name = "__builtin_goacc_parlevel_id";
-      fallback_retval = const0_rtx;
-      gen_fn = targetm.gen_oacc_dim_pos;
-      break;
-    case BUILT_IN_GOACC_PARLEVEL_SIZE:
-      name = "__builtin_goacc_parlevel_size";
-      fallback_retval = const1_rtx;
-      gen_fn = targetm.gen_oacc_dim_size;
+         expand_builtin_update_setjmp_buf (buf_addr);
+         return const0_rtx;
+       }
       break;
-    default:
-      gcc_unreachable ();
-    }
 
-  if (oacc_get_fn_attrib (current_function_decl) == NULL_TREE)
-    {
-      error ("%qs only supported in OpenACC code", name);
+    case BUILT_IN_TRAP:
+      expand_builtin_trap ();
       return const0_rtx;
-    }
 
-  tree arg = CALL_EXPR_ARG (exp, 0);
-  if (TREE_CODE (arg) != INTEGER_CST)
-    {
-      error ("non-constant argument 0 to %qs", name);
+    case BUILT_IN_UNREACHABLE:
+      expand_builtin_unreachable ();
       return const0_rtx;
-    }
 
-  int dim = TREE_INT_CST_LOW (arg);
-  switch (dim)
-    {
-    case GOMP_DIM_GANG:
-    case GOMP_DIM_WORKER:
-    case GOMP_DIM_VECTOR:
+    CASE_FLT_FN (BUILT_IN_SIGNBIT):
+    case BUILT_IN_SIGNBITD32:
+    case BUILT_IN_SIGNBITD64:
+    case BUILT_IN_SIGNBITD128:
+      target = expand_builtin_signbit (exp, target);
+      if (target)
+       return target;
       break;
-    default:
-      error ("illegal argument 0 to %qs", name);
-      return const0_rtx;
-    }
 
-  if (ignore)
-    return target;
+      /* Various hooks for the DWARF 2 __throw routine.  */
+    case BUILT_IN_UNWIND_INIT:
+      expand_builtin_unwind_init ();
+      return const0_rtx;
+    case BUILT_IN_DWARF_CFA:
+      return virtual_cfa_rtx;
+#ifdef DWARF2_UNWIND_INFO
+    case BUILT_IN_DWARF_SP_COLUMN:
+      return expand_builtin_dwarf_sp_column ();
+    case BUILT_IN_INIT_DWARF_REG_SIZES:
+      expand_builtin_init_dwarf_reg_sizes (CALL_EXPR_ARG (exp, 0));
+      return const0_rtx;
+#endif
+    case BUILT_IN_FROB_RETURN_ADDR:
+      return expand_builtin_frob_return_addr (CALL_EXPR_ARG (exp, 0));
+    case BUILT_IN_EXTRACT_RETURN_ADDR:
+      return expand_builtin_extract_return_addr (CALL_EXPR_ARG (exp, 0));
+    case BUILT_IN_EH_RETURN:
+      expand_builtin_eh_return (CALL_EXPR_ARG (exp, 0),
+                               CALL_EXPR_ARG (exp, 1));
+      return const0_rtx;
+    case BUILT_IN_EH_RETURN_DATA_REGNO:
+      return expand_builtin_eh_return_data_regno (exp);
+    case BUILT_IN_EXTEND_POINTER:
+      return expand_builtin_extend_pointer (CALL_EXPR_ARG (exp, 0));
+    case BUILT_IN_EH_POINTER:
+      return expand_builtin_eh_pointer (exp);
+    case BUILT_IN_EH_FILTER:
+      return expand_builtin_eh_filter (exp);
+    case BUILT_IN_EH_COPY_VALUES:
+      return expand_builtin_eh_copy_values (exp);
 
-  if (target == NULL_RTX)
-    target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+    case BUILT_IN_VA_START:
+      return expand_builtin_va_start (exp);
+    case BUILT_IN_VA_END:
+      return expand_builtin_va_end (exp);
+    case BUILT_IN_VA_COPY:
+      return expand_builtin_va_copy (exp);
+    case BUILT_IN_EXPECT:
+      return expand_builtin_expect (exp, target);
+    case BUILT_IN_EXPECT_WITH_PROBABILITY:
+      return expand_builtin_expect_with_probability (exp, target);
+    case BUILT_IN_ASSUME_ALIGNED:
+      return expand_builtin_assume_aligned (exp, target);
+    case BUILT_IN_PREFETCH:
+      expand_builtin_prefetch (exp);
+      return const0_rtx;
 
-  if (!targetm.have_oacc_dim_size ())
-    {
-      emit_move_insn (target, fallback_retval);
-      return target;
-    }
+    case BUILT_IN_INIT_TRAMPOLINE:
+      return expand_builtin_init_trampoline (exp, true);
+    case BUILT_IN_INIT_HEAP_TRAMPOLINE:
+      return expand_builtin_init_trampoline (exp, false);
+    case BUILT_IN_ADJUST_TRAMPOLINE:
+      return expand_builtin_adjust_trampoline (exp);
 
-  rtx reg = MEM_P (target) ? gen_reg_rtx (GET_MODE (target)) : target;
-  emit_insn (gen_fn (reg, GEN_INT (dim)));
-  if (reg != target)
-    emit_move_insn (target, reg);
+    case BUILT_IN_INIT_DESCRIPTOR:
+      return expand_builtin_init_descriptor (exp);
+    case BUILT_IN_ADJUST_DESCRIPTOR:
+      return expand_builtin_adjust_descriptor (exp);
 
-  return target;
-}
+    case BUILT_IN_FORK:
+    case BUILT_IN_EXECL:
+    case BUILT_IN_EXECV:
+    case BUILT_IN_EXECLP:
+    case BUILT_IN_EXECLE:
+    case BUILT_IN_EXECVP:
+    case BUILT_IN_EXECVE:
+      target = expand_builtin_fork_or_exec (fndecl, exp, target, ignore);
+      if (target)
+       return target;
+      break;
 
-/* Expand a string compare operation using a sequence of char comparison
-   to get rid of the calling overhead, with result going to TARGET if
-   that's convenient.
+    case BUILT_IN_SYNC_FETCH_AND_ADD_1:
+    case BUILT_IN_SYNC_FETCH_AND_ADD_2:
+    case BUILT_IN_SYNC_FETCH_AND_ADD_4:
+    case BUILT_IN_SYNC_FETCH_AND_ADD_8:
+    case BUILT_IN_SYNC_FETCH_AND_ADD_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_ADD_1);
+      target = expand_builtin_sync_operation (mode, exp, PLUS, false, target);
+      if (target)
+       return target;
+      break;
 
-   VAR_STR is the variable string source;
-   CONST_STR is the constant string source;
-   LENGTH is the number of chars to compare;
-   CONST_STR_N indicates which source string is the constant string;
-   IS_MEMCMP indicates whether it's a memcmp or strcmp.
-  
-   to: (assume const_str_n is 2, i.e., arg2 is a constant string)
+    case BUILT_IN_SYNC_FETCH_AND_SUB_1:
+    case BUILT_IN_SYNC_FETCH_AND_SUB_2:
+    case BUILT_IN_SYNC_FETCH_AND_SUB_4:
+    case BUILT_IN_SYNC_FETCH_AND_SUB_8:
+    case BUILT_IN_SYNC_FETCH_AND_SUB_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_SUB_1);
+      target = expand_builtin_sync_operation (mode, exp, MINUS, false, target);
+      if (target)
+       return target;
+      break;
 
-   target = (int) (unsigned char) var_str[0]
-           - (int) (unsigned char) const_str[0];
-   if (target != 0)
-     goto ne_label;
-     ...
-   target = (int) (unsigned char) var_str[length - 2]
-           - (int) (unsigned char) const_str[length - 2];
-   if (target != 0)
-     goto ne_label;
-   target = (int) (unsigned char) var_str[length - 1]
-           - (int) (unsigned char) const_str[length - 1];
-   ne_label:
-  */
+    case BUILT_IN_SYNC_FETCH_AND_OR_1:
+    case BUILT_IN_SYNC_FETCH_AND_OR_2:
+    case BUILT_IN_SYNC_FETCH_AND_OR_4:
+    case BUILT_IN_SYNC_FETCH_AND_OR_8:
+    case BUILT_IN_SYNC_FETCH_AND_OR_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_OR_1);
+      target = expand_builtin_sync_operation (mode, exp, IOR, false, target);
+      if (target)
+       return target;
+      break;
 
-static rtx
-inline_string_cmp (rtx target, tree var_str, const char *const_str,
-                  unsigned HOST_WIDE_INT length,
-                  int const_str_n, machine_mode mode)
-{
-  HOST_WIDE_INT offset = 0;
-  rtx var_rtx_array
-    = get_memory_rtx (var_str, build_int_cst (unsigned_type_node,length));
-  rtx var_rtx = NULL_RTX;
-  rtx const_rtx = NULL_RTX;
-  rtx result = target ? target : gen_reg_rtx (mode);
-  rtx_code_label *ne_label = gen_label_rtx ();
-  tree unit_type_node = unsigned_char_type_node;
-  scalar_int_mode unit_mode
-    = as_a <scalar_int_mode> TYPE_MODE (unit_type_node);
-
-  start_sequence ();
+    case BUILT_IN_SYNC_FETCH_AND_AND_1:
+    case BUILT_IN_SYNC_FETCH_AND_AND_2:
+    case BUILT_IN_SYNC_FETCH_AND_AND_4:
+    case BUILT_IN_SYNC_FETCH_AND_AND_8:
+    case BUILT_IN_SYNC_FETCH_AND_AND_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_AND_1);
+      target = expand_builtin_sync_operation (mode, exp, AND, false, target);
+      if (target)
+       return target;
+      break;
 
-  for (unsigned HOST_WIDE_INT i = 0; i < length; i++)
-    {
-      var_rtx
-       = adjust_address (var_rtx_array, TYPE_MODE (unit_type_node), offset);
-      const_rtx = c_readstr (const_str + offset, unit_mode);
-      rtx op0 = (const_str_n == 1) ? const_rtx : var_rtx;
-      rtx op1 = (const_str_n == 1) ? var_rtx : const_rtx;
+    case BUILT_IN_SYNC_FETCH_AND_XOR_1:
+    case BUILT_IN_SYNC_FETCH_AND_XOR_2:
+    case BUILT_IN_SYNC_FETCH_AND_XOR_4:
+    case BUILT_IN_SYNC_FETCH_AND_XOR_8:
+    case BUILT_IN_SYNC_FETCH_AND_XOR_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_XOR_1);
+      target = expand_builtin_sync_operation (mode, exp, XOR, false, target);
+      if (target)
+       return target;
+      break;
 
-      op0 = convert_modes (mode, unit_mode, op0, 1);
-      op1 = convert_modes (mode, unit_mode, op1, 1);
-      result = expand_simple_binop (mode, MINUS, op0, op1,
-                                   result, 1, OPTAB_WIDEN);
-      if (i < length - 1)
-       emit_cmp_and_jump_insns (result, CONST0_RTX (mode), NE, NULL_RTX,
-                                mode, true, ne_label);
-      offset += GET_MODE_SIZE (unit_mode);
-    }
+    case BUILT_IN_SYNC_FETCH_AND_NAND_1:
+    case BUILT_IN_SYNC_FETCH_AND_NAND_2:
+    case BUILT_IN_SYNC_FETCH_AND_NAND_4:
+    case BUILT_IN_SYNC_FETCH_AND_NAND_8:
+    case BUILT_IN_SYNC_FETCH_AND_NAND_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_NAND_1);
+      target = expand_builtin_sync_operation (mode, exp, NOT, false, target);
+      if (target)
+       return target;
+      break;
 
-  emit_label (ne_label);
-  rtx_insn *insns = get_insns ();
-  end_sequence ();
-  emit_insn (insns);
+    case BUILT_IN_SYNC_ADD_AND_FETCH_1:
+    case BUILT_IN_SYNC_ADD_AND_FETCH_2:
+    case BUILT_IN_SYNC_ADD_AND_FETCH_4:
+    case BUILT_IN_SYNC_ADD_AND_FETCH_8:
+    case BUILT_IN_SYNC_ADD_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_ADD_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, PLUS, true, target);
+      if (target)
+       return target;
+      break;
 
-  return result;
-}
+    case BUILT_IN_SYNC_SUB_AND_FETCH_1:
+    case BUILT_IN_SYNC_SUB_AND_FETCH_2:
+    case BUILT_IN_SYNC_SUB_AND_FETCH_4:
+    case BUILT_IN_SYNC_SUB_AND_FETCH_8:
+    case BUILT_IN_SYNC_SUB_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_SUB_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, MINUS, true, target);
+      if (target)
+       return target;
+      break;
 
-/* Inline expansion of a call to str(n)cmp and memcmp, with result going
-   to TARGET if that's convenient.
-   If the call is not been inlined, return NULL_RTX.  */
+    case BUILT_IN_SYNC_OR_AND_FETCH_1:
+    case BUILT_IN_SYNC_OR_AND_FETCH_2:
+    case BUILT_IN_SYNC_OR_AND_FETCH_4:
+    case BUILT_IN_SYNC_OR_AND_FETCH_8:
+    case BUILT_IN_SYNC_OR_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_OR_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, IOR, true, target);
+      if (target)
+       return target;
+      break;
 
-static rtx
-inline_expand_builtin_bytecmp (tree exp, rtx target)
-{
-  tree fndecl = get_callee_fndecl (exp);
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-  bool is_ncmp = (fcode == BUILT_IN_STRNCMP || fcode == BUILT_IN_MEMCMP);
+    case BUILT_IN_SYNC_AND_AND_FETCH_1:
+    case BUILT_IN_SYNC_AND_AND_FETCH_2:
+    case BUILT_IN_SYNC_AND_AND_FETCH_4:
+    case BUILT_IN_SYNC_AND_AND_FETCH_8:
+    case BUILT_IN_SYNC_AND_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_AND_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, AND, true, target);
+      if (target)
+       return target;
+      break;
 
-  /* Do NOT apply this inlining expansion when optimizing for size or
-     optimization level below 2.  */
-  if (optimize < 2 || optimize_insn_for_size_p ())
-    return NULL_RTX;
+    case BUILT_IN_SYNC_XOR_AND_FETCH_1:
+    case BUILT_IN_SYNC_XOR_AND_FETCH_2:
+    case BUILT_IN_SYNC_XOR_AND_FETCH_4:
+    case BUILT_IN_SYNC_XOR_AND_FETCH_8:
+    case BUILT_IN_SYNC_XOR_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_XOR_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, XOR, true, target);
+      if (target)
+       return target;
+      break;
 
-  gcc_checking_assert (fcode == BUILT_IN_STRCMP
-                      || fcode == BUILT_IN_STRNCMP
-                      || fcode == BUILT_IN_MEMCMP);
+    case BUILT_IN_SYNC_NAND_AND_FETCH_1:
+    case BUILT_IN_SYNC_NAND_AND_FETCH_2:
+    case BUILT_IN_SYNC_NAND_AND_FETCH_4:
+    case BUILT_IN_SYNC_NAND_AND_FETCH_8:
+    case BUILT_IN_SYNC_NAND_AND_FETCH_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_NAND_AND_FETCH_1);
+      target = expand_builtin_sync_operation (mode, exp, NOT, true, target);
+      if (target)
+       return target;
+      break;
 
-  /* On a target where the type of the call (int) has same or narrower presicion
-     than unsigned char, give up the inlining expansion.  */
-  if (TYPE_PRECISION (unsigned_char_type_node)
-      >= TYPE_PRECISION (TREE_TYPE (exp)))
-    return NULL_RTX;
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_8:
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_16:
+      if (mode == VOIDmode)
+       mode = TYPE_MODE (boolean_type_node);
+      if (!target || !register_operand (target, mode))
+       target = gen_reg_rtx (mode);
 
-  tree arg1 = CALL_EXPR_ARG (exp, 0);
-  tree arg2 = CALL_EXPR_ARG (exp, 1);
-  tree len3_tree = is_ncmp ? CALL_EXPR_ARG (exp, 2) : NULL_TREE;
+      mode = get_builtin_sync_mode 
+                               (fcode - BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1);
+      target = expand_builtin_compare_and_swap (mode, exp, true, target);
+      if (target)
+       return target;
+      break;
 
-  unsigned HOST_WIDE_INT len1 = 0;
-  unsigned HOST_WIDE_INT len2 = 0;
-  unsigned HOST_WIDE_INT len3 = 0;
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_8:
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_16:
+      mode = get_builtin_sync_mode 
+                               (fcode - BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1);
+      target = expand_builtin_compare_and_swap (mode, exp, false, target);
+      if (target)
+       return target;
+      break;
 
-  /* Get the object representation of the initializers of ARG1 and ARG2
-     as strings, provided they refer to constant objects, with their byte
-     sizes in LEN1 and LEN2, respectively.  */
-  const char *bytes1 = getbyterep (arg1, &len1);
-  const char *bytes2 = getbyterep (arg2, &len2);
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_1:
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_2:
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_4:
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_8:
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_TEST_AND_SET_1);
+      target = expand_builtin_sync_lock_test_and_set (mode, exp, target);
+      if (target)
+       return target;
+      break;
 
-  /* Fail if neither argument refers to an initialized constant.  */
-  if (!bytes1 && !bytes2)
-    return NULL_RTX;
+    case BUILT_IN_SYNC_LOCK_RELEASE_1:
+    case BUILT_IN_SYNC_LOCK_RELEASE_2:
+    case BUILT_IN_SYNC_LOCK_RELEASE_4:
+    case BUILT_IN_SYNC_LOCK_RELEASE_8:
+    case BUILT_IN_SYNC_LOCK_RELEASE_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_RELEASE_1);
+      expand_builtin_sync_lock_release (mode, exp);
+      return const0_rtx;
 
-  if (is_ncmp)
-    {
-      /* Fail if the memcmp/strncmp bound is not a constant.  */
-      if (!tree_fits_uhwi_p (len3_tree))
-       return NULL_RTX;
+    case BUILT_IN_SYNC_SYNCHRONIZE:
+      expand_builtin_sync_synchronize ();
+      return const0_rtx;
 
-      len3 = tree_to_uhwi (len3_tree);
+    case BUILT_IN_ATOMIC_EXCHANGE_1:
+    case BUILT_IN_ATOMIC_EXCHANGE_2:
+    case BUILT_IN_ATOMIC_EXCHANGE_4:
+    case BUILT_IN_ATOMIC_EXCHANGE_8:
+    case BUILT_IN_ATOMIC_EXCHANGE_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_EXCHANGE_1);
+      target = expand_builtin_atomic_exchange (mode, exp, target);
+      if (target)
+       return target;
+      break;
 
-      if (fcode == BUILT_IN_MEMCMP)
-       {
-         /* Fail if the memcmp bound is greater than the size of either
-            of the two constant objects.  */
-         if ((bytes1 && len1 < len3)
-             || (bytes2 && len2 < len3))
-           return NULL_RTX;
-       }
-    }
+    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:
+      {
+       unsigned int nargs, z;
+       vec<tree, va_gc> *vec;
 
-  if (fcode != BUILT_IN_MEMCMP)
-    {
-      /* For string functions (i.e., strcmp and strncmp) reduce LEN1
-        and LEN2 to the length of the nul-terminated string stored
-        in each.  */
-      if (bytes1 != NULL)
-       len1 = strnlen (bytes1, len1) + 1;
-      if (bytes2 != NULL)
-       len2 = strnlen (bytes2, len2) + 1;
-    }
+       mode = 
+           get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1);
+       target = expand_builtin_atomic_compare_exchange (mode, exp, target);
+       if (target)
+         return target;
 
-  /* See inline_string_cmp.  */
-  int const_str_n;
-  if (!len1)
-    const_str_n = 2;
-  else if (!len2)
-    const_str_n = 1;
-  else if (len2 > len1)
-    const_str_n = 1;
-  else
-    const_str_n = 2;
-
-  /* For strncmp only, compute the new bound as the smallest of
-     the lengths of the two strings (plus 1) and the bound provided
-     to the function.  */
-  unsigned HOST_WIDE_INT bound = (const_str_n == 1) ? len1 : len2;
-  if (is_ncmp && len3 < bound)
-    bound = len3;
-
-  /* If the bound of the comparison is larger than the threshold,
-     do nothing.  */
-  if (bound > (unsigned HOST_WIDE_INT) param_builtin_string_cmp_inline_length)
-    return NULL_RTX;
-
-  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-
-  /* Now, start inline expansion the call.  */
-  return inline_string_cmp (target, (const_str_n == 1) ? arg2 : arg1,
-                           (const_str_n == 1) ? bytes1 : bytes2, bound,
-                           const_str_n, mode);
-}
-
-/* Expand a call to __builtin_speculation_safe_value_<N>.  MODE
-   represents the size of the first argument to that call, or VOIDmode
-   if the argument is a pointer.  IGNORE will be true if the result
-   isn't used.  */
-static rtx
-expand_speculation_safe_value (machine_mode mode, tree exp, rtx target,
-                              bool ignore)
-{
-  rtx val, failsafe;
-  unsigned nargs = call_expr_nargs (exp);
-
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-
-  if (mode == VOIDmode)
-    {
-      mode = TYPE_MODE (TREE_TYPE (arg0));
-      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT);
-    }
-
-  val = expand_expr (arg0, NULL_RTX, mode, EXPAND_NORMAL);
-
-  /* An optional second argument can be used as a failsafe value on
-     some machines.  If it isn't present, then the failsafe value is
-     assumed to be 0.  */
-  if (nargs > 1)
-    {
-      tree arg1 = CALL_EXPR_ARG (exp, 1);
-      failsafe = expand_expr (arg1, NULL_RTX, mode, EXPAND_NORMAL);
-    }
-  else
-    failsafe = const0_rtx;
-
-  /* If the result isn't used, the behavior is undefined.  It would be
-     nice to emit a warning here, but path splitting means this might
-     happen with legitimate code.  So simply drop the builtin
-     expansion in that case; we've handled any side-effects above.  */
-  if (ignore)
-    return const0_rtx;
-
-  /* If we don't have a suitable target, create one to hold the result.  */
-  if (target == NULL || GET_MODE (target) != mode)
-    target = gen_reg_rtx (mode);
-
-  if (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)
-    val = convert_modes (mode, VOIDmode, val, false);
-
-  return targetm.speculation_safe_value (mode, target, val, failsafe);
-}
-
-/* Expand an expression EXP that calls a built-in function,
-   with result going to TARGET if that's convenient
-   (and in mode MODE if that's convenient).
-   SUBTARGET may be used as the target for computing one of EXP's operands.
-   IGNORE is nonzero if the value is to be ignored.  */
-
-rtx
-expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
-               int ignore)
-{
-  tree fndecl = get_callee_fndecl (exp);
-  machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
-  int flags;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-    return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
-
-  /* When ASan is enabled, we don't want to expand some memory/string
-     builtins and rely on libsanitizer's hooks.  This allows us to avoid
-     redundant checks and be sure, that possible overflow will be detected
-     by ASan.  */
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-  if ((flag_sanitize & SANITIZE_ADDRESS) && asan_intercepted_p (fcode))
-    return expand_call (exp, target, ignore);
-
-  /* When not optimizing, generate calls to library functions for a certain
-     set of builtins.  */
-  if (!optimize
-      && !called_as_built_in (fndecl)
-      && fcode != BUILT_IN_FORK
-      && fcode != BUILT_IN_EXECL
-      && fcode != BUILT_IN_EXECV
-      && fcode != BUILT_IN_EXECLP
-      && fcode != BUILT_IN_EXECLE
-      && fcode != BUILT_IN_EXECVP
-      && fcode != BUILT_IN_EXECVE
-      && fcode != BUILT_IN_CLEAR_CACHE
-      && !ALLOCA_FUNCTION_CODE_P (fcode)
-      && fcode != BUILT_IN_FREE)
-    return expand_call (exp, target, ignore);
-
-  /* The built-in function expanders test for target == const0_rtx
-     to determine whether the function's result will be ignored.  */
-  if (ignore)
-    target = const0_rtx;
-
-  /* If the result of a pure or const built-in function is ignored, and
-     none of its arguments are volatile, we can avoid expanding the
-     built-in call and just evaluate the arguments for side-effects.  */
-  if (target == const0_rtx
-      && ((flags = flags_from_decl_or_type (fndecl)) & (ECF_CONST | ECF_PURE))
-      && !(flags & ECF_LOOPING_CONST_OR_PURE))
-    {
-      bool volatilep = false;
-      tree arg;
-      call_expr_arg_iterator iter;
-
-      FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
-       if (TREE_THIS_VOLATILE (arg))
-         {
-           volatilep = true;
-           break;
-         }
-
-      if (! volatilep)
-       {
-         FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
-           expand_expr (arg, const0_rtx, VOIDmode, EXPAND_NORMAL);
-         return const0_rtx;
-       }
-    }
-
-  switch (fcode)
-    {
-    CASE_FLT_FN (BUILT_IN_FABS):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
-    case BUILT_IN_FABSD32:
-    case BUILT_IN_FABSD64:
-    case BUILT_IN_FABSD128:
-      target = expand_builtin_fabs (exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
-    CASE_FLT_FN (BUILT_IN_COPYSIGN):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_COPYSIGN):
-      target = expand_builtin_copysign (exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
-      /* Just do a normal library call if we were unable to fold
-        the values.  */
-    CASE_FLT_FN (BUILT_IN_CABS):
-      break;
-
-    CASE_FLT_FN (BUILT_IN_FMA):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FMA):
-      target = expand_builtin_mathfn_ternary (exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
-    CASE_FLT_FN (BUILT_IN_ILOGB):
-      if (! flag_unsafe_math_optimizations)
+       /* If this is turned into an external library call, the weak parameter
+          must be dropped to match the expected parameter list.  */
+       nargs = call_expr_nargs (exp);
+       vec_alloc (vec, nargs - 1);
+       for (z = 0; z < 3; z++)
+         vec->quick_push (CALL_EXPR_ARG (exp, z));
+       /* Skip the boolean weak parameter.  */
+       for (z = 4; z < 6; z++)
+         vec->quick_push (CALL_EXPR_ARG (exp, z));
+       exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), vec);
        break;
-      gcc_fallthrough ();
-    CASE_FLT_FN (BUILT_IN_ISINF):
-    CASE_FLT_FN (BUILT_IN_FINITE):
-    case BUILT_IN_ISFINITE:
-    case BUILT_IN_ISNORMAL:
-      target = expand_builtin_interclass_mathfn (exp, target);
-      if (target)
-       return target;
-      break;
-
-    CASE_FLT_FN (BUILT_IN_ICEIL):
-    CASE_FLT_FN (BUILT_IN_LCEIL):
-    CASE_FLT_FN (BUILT_IN_LLCEIL):
-    CASE_FLT_FN (BUILT_IN_LFLOOR):
-    CASE_FLT_FN (BUILT_IN_IFLOOR):
-    CASE_FLT_FN (BUILT_IN_LLFLOOR):
-      target = expand_builtin_int_roundingfn (exp, target);
-      if (target)
-       return target;
-      break;
-
-    CASE_FLT_FN (BUILT_IN_IRINT):
-    CASE_FLT_FN (BUILT_IN_LRINT):
-    CASE_FLT_FN (BUILT_IN_LLRINT):
-    CASE_FLT_FN (BUILT_IN_IROUND):
-    CASE_FLT_FN (BUILT_IN_LROUND):
-    CASE_FLT_FN (BUILT_IN_LLROUND):
-      target = expand_builtin_int_roundingfn_2 (exp, target);
-      if (target)
-       return target;
-      break;
+      }
 
-    CASE_FLT_FN (BUILT_IN_POWI):
-      target = expand_builtin_powi (exp, target);
+    case BUILT_IN_ATOMIC_LOAD_1:
+    case BUILT_IN_ATOMIC_LOAD_2:
+    case BUILT_IN_ATOMIC_LOAD_4:
+    case BUILT_IN_ATOMIC_LOAD_8:
+    case BUILT_IN_ATOMIC_LOAD_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_LOAD_1);
+      target = expand_builtin_atomic_load (mode, exp, target);
       if (target)
        return target;
       break;
 
-    CASE_FLT_FN (BUILT_IN_CEXPI):
-      target = expand_builtin_cexpi (exp, target);
-      gcc_assert (target);
-      return target;
-
-    CASE_FLT_FN (BUILT_IN_SIN):
-    CASE_FLT_FN (BUILT_IN_COS):
-      if (! flag_unsafe_math_optimizations)
-       break;
-      target = expand_builtin_mathfn_3 (exp, target, subtarget);
+    case BUILT_IN_ATOMIC_STORE_1:
+    case BUILT_IN_ATOMIC_STORE_2:
+    case BUILT_IN_ATOMIC_STORE_4:
+    case BUILT_IN_ATOMIC_STORE_8:
+    case BUILT_IN_ATOMIC_STORE_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_STORE_1);
+      target = expand_builtin_atomic_store (mode, exp);
       if (target)
-       return target;
+       return const0_rtx;
       break;
 
-    CASE_FLT_FN (BUILT_IN_SINCOS):
-      if (! flag_unsafe_math_optimizations)
-       break;
-      target = expand_builtin_sincos (exp);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_APPLY_ARGS:
-      return expand_builtin_apply_args ();
-
-      /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
-        FUNCTION with a copy of the parameters described by
-        ARGUMENTS, and ARGSIZE.  It returns a block of memory
-        allocated on the stack into which is stored all the registers
-        that might possibly be used for returning the result of a
-        function.  ARGUMENTS is the value returned by
-        __builtin_apply_args.  ARGSIZE is the number of bytes of
-        arguments that must be copied.  ??? How should this value be
-        computed?  We'll also need a safe worst case value for varargs
-        functions.  */
-    case BUILT_IN_APPLY:
-      if (!validate_arglist (exp, POINTER_TYPE,
-                            POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
-         && !validate_arglist (exp, REFERENCE_TYPE,
-                               POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-       return const0_rtx;
-      else
-       {
-         rtx ops[3];
-
-         ops[0] = expand_normal (CALL_EXPR_ARG (exp, 0));
-         ops[1] = expand_normal (CALL_EXPR_ARG (exp, 1));
-         ops[2] = expand_normal (CALL_EXPR_ARG (exp, 2));
-
-         return expand_builtin_apply (ops[0], ops[1], ops[2]);
-       }
-
-      /* __builtin_return (RESULT) causes the function to return the
-        value described by RESULT.  RESULT is address of the block of
-        memory returned by __builtin_apply.  */
-    case BUILT_IN_RETURN:
-      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-       expand_builtin_return (expand_normal (CALL_EXPR_ARG (exp, 0)));
-      return const0_rtx;
-
-    case BUILT_IN_SAVEREGS:
-      return expand_builtin_saveregs ();
-
-    case BUILT_IN_VA_ARG_PACK:
-      /* All valid uses of __builtin_va_arg_pack () are removed during
-        inlining.  */
-      error ("invalid use of %<__builtin_va_arg_pack ()%>");
-      return const0_rtx;
-
-    case BUILT_IN_VA_ARG_PACK_LEN:
-      /* All valid uses of __builtin_va_arg_pack_len () are removed during
-        inlining.  */
-      error ("invalid use of %<__builtin_va_arg_pack_len ()%>");
-      return const0_rtx;
-
-      /* Return the address of the first anonymous stack arg.  */
-    case BUILT_IN_NEXT_ARG:
-      if (fold_builtin_next_arg (exp, false))
-       return const0_rtx;
-      return expand_builtin_next_arg ();
-
-    case BUILT_IN_CLEAR_CACHE:
-      expand_builtin___clear_cache (exp);
-      return const0_rtx;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-      return expand_builtin_classify_type (exp);
-
-    case BUILT_IN_CONSTANT_P:
-      return const0_rtx;
-
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_RETURN_ADDRESS:
-      return expand_builtin_frame_address (fndecl, exp);
-
-    /* Returns the address of the area where the structure is returned.
-       0 otherwise.  */
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      if (call_expr_nargs (exp) != 0
-         || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
-         || !MEM_P (DECL_RTL (DECL_RESULT (current_function_decl))))
-       return const0_rtx;
-      else
-       return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
-
-    CASE_BUILT_IN_ALLOCA:
-      target = expand_builtin_alloca (exp);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return expand_asan_emit_allocas_unpoison (exp);
-
-    case BUILT_IN_STACK_SAVE:
-      return expand_stack_save ();
-
-    case BUILT_IN_STACK_RESTORE:
-      expand_stack_restore (CALL_EXPR_ARG (exp, 0));
-      return const0_rtx;
-
-    case BUILT_IN_BSWAP16:
-    case BUILT_IN_BSWAP32:
-    case BUILT_IN_BSWAP64:
-    case BUILT_IN_BSWAP128:
-      target = expand_builtin_bswap (target_mode, exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_FFS):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, ffs_optab);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_CLZ):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, clz_optab);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_CTZ):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, ctz_optab);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_CLRSB):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, clrsb_optab);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_POPCOUNT):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, popcount_optab);
-      if (target)
-       return target;
-      break;
-
-    CASE_INT_FN (BUILT_IN_PARITY):
-      target = expand_builtin_unop (target_mode, exp, target,
-                                   subtarget, parity_optab);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRLEN:
-      target = expand_builtin_strlen (exp, target, target_mode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRNLEN:
-      target = expand_builtin_strnlen (exp, target, target_mode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRCAT:
-      target = expand_builtin_strcat (exp);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_GETTEXT:
-    case BUILT_IN_PUTS:
-    case BUILT_IN_PUTS_UNLOCKED:
-    case BUILT_IN_STRDUP:
-      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
-      break;
-
-    case BUILT_IN_INDEX:
-    case BUILT_IN_RINDEX:
-    case BUILT_IN_STRCHR:
-    case BUILT_IN_STRRCHR:
-      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
-      break;
-
-    case BUILT_IN_FPUTS:
-    case BUILT_IN_FPUTS_UNLOCKED:
-      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-       check_read_access (exp, CALL_EXPR_ARG (exp, 0));
-      break;
-
-    case BUILT_IN_STRNDUP:
-      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-       check_read_access (exp, CALL_EXPR_ARG (exp, 0), CALL_EXPR_ARG (exp, 1));
-      break;
-
-    case BUILT_IN_STRCASECMP:
-    case BUILT_IN_STRPBRK:
-    case BUILT_IN_STRSPN:
-    case BUILT_IN_STRCSPN:
-    case BUILT_IN_STRSTR:
-      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-       {
-         check_read_access (exp, CALL_EXPR_ARG (exp, 0));
-         check_read_access (exp, CALL_EXPR_ARG (exp, 1));
-       }
-      break;
-
-    case BUILT_IN_STRCPY:
-      target = expand_builtin_strcpy (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRNCAT:
-      target = expand_builtin_strncat (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRNCPY:
-      target = expand_builtin_strncpy (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STPCPY:
-      target = expand_builtin_stpcpy (exp, target, mode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STPNCPY:
-      target = expand_builtin_stpncpy (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_MEMCHR:
-      target = expand_builtin_memchr (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_MEMCPY:
-      target = expand_builtin_memcpy (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_MEMMOVE:
-      target = expand_builtin_memmove (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_MEMPCPY:
-      target = expand_builtin_mempcpy (exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_MEMSET:
-      target = expand_builtin_memset (exp, target, mode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_BZERO:
-      target = expand_builtin_bzero (exp);
-      if (target)
-       return target;
-      break;
-
-    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
-       back to a BUILT_IN_STRCMP. Remember to delete the 3rd parameter
-       when changing it to a strcmp call.  */
-    case BUILT_IN_STRCMP_EQ:
-      target = expand_builtin_memcmp (exp, target, true);
-      if (target)
-       return target;
-
-      /* Change this call back to a BUILT_IN_STRCMP.  */
-      TREE_OPERAND (exp, 1)
-       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRCMP));
-
-      /* Delete the last parameter.  */
-      unsigned int i;
-      vec<tree, va_gc> *arg_vec;
-      vec_alloc (arg_vec, 2);
-      for (i = 0; i < 2; i++)
-       arg_vec->quick_push (CALL_EXPR_ARG (exp, i));
-      exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), arg_vec);
-      /* FALLTHROUGH */
-
-    case BUILT_IN_STRCMP:
-      target = expand_builtin_strcmp (exp, target);
-      if (target)
-       return target;
-      break;
-
-    /* Expand it as BUILT_IN_MEMCMP_EQ first. If not successful, change it
-       back to a BUILT_IN_STRNCMP.  */
-    case BUILT_IN_STRNCMP_EQ:
-      target = expand_builtin_memcmp (exp, target, true);
-      if (target)
-       return target;
-
-      /* Change it back to a BUILT_IN_STRNCMP.  */
-      TREE_OPERAND (exp, 1)
-       = build_fold_addr_expr (builtin_decl_explicit (BUILT_IN_STRNCMP));
-      /* FALLTHROUGH */
-
-    case BUILT_IN_STRNCMP:
-      target = expand_builtin_strncmp (exp, target, mode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_BCMP:
-    case BUILT_IN_MEMCMP:
-    case BUILT_IN_MEMCMP_EQ:
-      target = expand_builtin_memcmp (exp, target, fcode == BUILT_IN_MEMCMP_EQ);
-      if (target)
-       return target;
-      if (fcode == BUILT_IN_MEMCMP_EQ)
-       {
-         tree newdecl = builtin_decl_explicit (BUILT_IN_MEMCMP);
-         TREE_OPERAND (exp, 1) = build_fold_addr_expr (newdecl);
-       }
-      break;
-
-    case BUILT_IN_SETJMP:
-      /* This should have been lowered to the builtins below.  */
-      gcc_unreachable ();
-
-    case BUILT_IN_SETJMP_SETUP:
-      /* __builtin_setjmp_setup is passed a pointer to an array of five words
-          and the receiver label.  */
-      if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-       {
-         rtx buf_addr = expand_expr (CALL_EXPR_ARG (exp, 0), subtarget,
-                                     VOIDmode, EXPAND_NORMAL);
-         tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 1), 0);
-         rtx_insn *label_r = label_rtx (label);
-
-         /* This is copied from the handling of non-local gotos.  */
-         expand_builtin_setjmp_setup (buf_addr, label_r);
-         nonlocal_goto_handler_labels
-           = gen_rtx_INSN_LIST (VOIDmode, label_r,
-                                nonlocal_goto_handler_labels);
-         /* ??? Do not let expand_label treat us as such since we would
-            not want to be both on the list of non-local labels and on
-            the list of forced labels.  */
-         FORCED_LABEL (label) = 0;
-         return const0_rtx;
-       }
-      break;
-
-    case BUILT_IN_SETJMP_RECEIVER:
-       /* __builtin_setjmp_receiver is passed the receiver label.  */
-      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-       {
-         tree label = TREE_OPERAND (CALL_EXPR_ARG (exp, 0), 0);
-         rtx_insn *label_r = label_rtx (label);
-
-         expand_builtin_setjmp_receiver (label_r);
-         return const0_rtx;
-       }
-      break;
-
-      /* __builtin_longjmp is passed a pointer to an array of five words.
-        It's similar to the C library longjmp function but works with
-        __builtin_setjmp above.  */
-    case BUILT_IN_LONGJMP:
-      if (validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-       {
-         rtx buf_addr = expand_expr (CALL_EXPR_ARG (exp, 0), subtarget,
-                                     VOIDmode, EXPAND_NORMAL);
-         rtx value = expand_normal (CALL_EXPR_ARG (exp, 1));
-
-         if (value != const1_rtx)
-           {
-             error ("%<__builtin_longjmp%> second argument must be 1");
-             return const0_rtx;
-           }
-
-         expand_builtin_longjmp (buf_addr, value);
-         return const0_rtx;
-       }
-      break;
-
-    case BUILT_IN_NONLOCAL_GOTO:
-      target = expand_builtin_nonlocal_goto (exp);
-      if (target)
-       return target;
-      break;
-
-      /* This updates the setjmp buffer that is its argument with the value
-        of the current stack pointer.  */
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-      if (validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
-       {
-         rtx buf_addr
-           = expand_normal (CALL_EXPR_ARG (exp, 0));
-
-         expand_builtin_update_setjmp_buf (buf_addr);
-         return const0_rtx;
-       }
-      break;
-
-    case BUILT_IN_TRAP:
-      expand_builtin_trap ();
-      return const0_rtx;
-
-    case BUILT_IN_UNREACHABLE:
-      expand_builtin_unreachable ();
-      return const0_rtx;
-
-    CASE_FLT_FN (BUILT_IN_SIGNBIT):
-    case BUILT_IN_SIGNBITD32:
-    case BUILT_IN_SIGNBITD64:
-    case BUILT_IN_SIGNBITD128:
-      target = expand_builtin_signbit (exp, target);
-      if (target)
-       return target;
-      break;
-
-      /* Various hooks for the DWARF 2 __throw routine.  */
-    case BUILT_IN_UNWIND_INIT:
-      expand_builtin_unwind_init ();
-      return const0_rtx;
-    case BUILT_IN_DWARF_CFA:
-      return virtual_cfa_rtx;
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-      return expand_builtin_dwarf_sp_column ();
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-      expand_builtin_init_dwarf_reg_sizes (CALL_EXPR_ARG (exp, 0));
-      return const0_rtx;
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-      return expand_builtin_frob_return_addr (CALL_EXPR_ARG (exp, 0));
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-      return expand_builtin_extract_return_addr (CALL_EXPR_ARG (exp, 0));
-    case BUILT_IN_EH_RETURN:
-      expand_builtin_eh_return (CALL_EXPR_ARG (exp, 0),
-                               CALL_EXPR_ARG (exp, 1));
-      return const0_rtx;
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-      return expand_builtin_eh_return_data_regno (exp);
-    case BUILT_IN_EXTEND_POINTER:
-      return expand_builtin_extend_pointer (CALL_EXPR_ARG (exp, 0));
-    case BUILT_IN_EH_POINTER:
-      return expand_builtin_eh_pointer (exp);
-    case BUILT_IN_EH_FILTER:
-      return expand_builtin_eh_filter (exp);
-    case BUILT_IN_EH_COPY_VALUES:
-      return expand_builtin_eh_copy_values (exp);
-
-    case BUILT_IN_VA_START:
-      return expand_builtin_va_start (exp);
-    case BUILT_IN_VA_END:
-      return expand_builtin_va_end (exp);
-    case BUILT_IN_VA_COPY:
-      return expand_builtin_va_copy (exp);
-    case BUILT_IN_EXPECT:
-      return expand_builtin_expect (exp, target);
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-      return expand_builtin_expect_with_probability (exp, target);
-    case BUILT_IN_ASSUME_ALIGNED:
-      return expand_builtin_assume_aligned (exp, target);
-    case BUILT_IN_PREFETCH:
-      expand_builtin_prefetch (exp);
-      return const0_rtx;
-
-    case BUILT_IN_INIT_TRAMPOLINE:
-      return expand_builtin_init_trampoline (exp, true);
-    case BUILT_IN_INIT_HEAP_TRAMPOLINE:
-      return expand_builtin_init_trampoline (exp, false);
-    case BUILT_IN_ADJUST_TRAMPOLINE:
-      return expand_builtin_adjust_trampoline (exp);
-
-    case BUILT_IN_INIT_DESCRIPTOR:
-      return expand_builtin_init_descriptor (exp);
-    case BUILT_IN_ADJUST_DESCRIPTOR:
-      return expand_builtin_adjust_descriptor (exp);
-
-    case BUILT_IN_FORK:
-    case BUILT_IN_EXECL:
-    case BUILT_IN_EXECV:
-    case BUILT_IN_EXECLP:
-    case BUILT_IN_EXECLE:
-    case BUILT_IN_EXECVP:
-    case BUILT_IN_EXECVE:
-      target = expand_builtin_fork_or_exec (fndecl, exp, target, ignore);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_ADD_1:
-    case BUILT_IN_SYNC_FETCH_AND_ADD_2:
-    case BUILT_IN_SYNC_FETCH_AND_ADD_4:
-    case BUILT_IN_SYNC_FETCH_AND_ADD_8:
-    case BUILT_IN_SYNC_FETCH_AND_ADD_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_ADD_1);
-      target = expand_builtin_sync_operation (mode, exp, PLUS, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_SUB_1:
-    case BUILT_IN_SYNC_FETCH_AND_SUB_2:
-    case BUILT_IN_SYNC_FETCH_AND_SUB_4:
-    case BUILT_IN_SYNC_FETCH_AND_SUB_8:
-    case BUILT_IN_SYNC_FETCH_AND_SUB_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_SUB_1);
-      target = expand_builtin_sync_operation (mode, exp, MINUS, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_OR_1:
-    case BUILT_IN_SYNC_FETCH_AND_OR_2:
-    case BUILT_IN_SYNC_FETCH_AND_OR_4:
-    case BUILT_IN_SYNC_FETCH_AND_OR_8:
-    case BUILT_IN_SYNC_FETCH_AND_OR_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_OR_1);
-      target = expand_builtin_sync_operation (mode, exp, IOR, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_AND_1:
-    case BUILT_IN_SYNC_FETCH_AND_AND_2:
-    case BUILT_IN_SYNC_FETCH_AND_AND_4:
-    case BUILT_IN_SYNC_FETCH_AND_AND_8:
-    case BUILT_IN_SYNC_FETCH_AND_AND_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_AND_1);
-      target = expand_builtin_sync_operation (mode, exp, AND, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_XOR_1:
-    case BUILT_IN_SYNC_FETCH_AND_XOR_2:
-    case BUILT_IN_SYNC_FETCH_AND_XOR_4:
-    case BUILT_IN_SYNC_FETCH_AND_XOR_8:
-    case BUILT_IN_SYNC_FETCH_AND_XOR_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_XOR_1);
-      target = expand_builtin_sync_operation (mode, exp, XOR, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_FETCH_AND_NAND_1:
-    case BUILT_IN_SYNC_FETCH_AND_NAND_2:
-    case BUILT_IN_SYNC_FETCH_AND_NAND_4:
-    case BUILT_IN_SYNC_FETCH_AND_NAND_8:
-    case BUILT_IN_SYNC_FETCH_AND_NAND_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_FETCH_AND_NAND_1);
-      target = expand_builtin_sync_operation (mode, exp, NOT, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_ADD_AND_FETCH_1:
-    case BUILT_IN_SYNC_ADD_AND_FETCH_2:
-    case BUILT_IN_SYNC_ADD_AND_FETCH_4:
-    case BUILT_IN_SYNC_ADD_AND_FETCH_8:
-    case BUILT_IN_SYNC_ADD_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_ADD_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, PLUS, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_SUB_AND_FETCH_1:
-    case BUILT_IN_SYNC_SUB_AND_FETCH_2:
-    case BUILT_IN_SYNC_SUB_AND_FETCH_4:
-    case BUILT_IN_SYNC_SUB_AND_FETCH_8:
-    case BUILT_IN_SYNC_SUB_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_SUB_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, MINUS, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_OR_AND_FETCH_1:
-    case BUILT_IN_SYNC_OR_AND_FETCH_2:
-    case BUILT_IN_SYNC_OR_AND_FETCH_4:
-    case BUILT_IN_SYNC_OR_AND_FETCH_8:
-    case BUILT_IN_SYNC_OR_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_OR_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, IOR, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_AND_AND_FETCH_1:
-    case BUILT_IN_SYNC_AND_AND_FETCH_2:
-    case BUILT_IN_SYNC_AND_AND_FETCH_4:
-    case BUILT_IN_SYNC_AND_AND_FETCH_8:
-    case BUILT_IN_SYNC_AND_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_AND_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, AND, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_XOR_AND_FETCH_1:
-    case BUILT_IN_SYNC_XOR_AND_FETCH_2:
-    case BUILT_IN_SYNC_XOR_AND_FETCH_4:
-    case BUILT_IN_SYNC_XOR_AND_FETCH_8:
-    case BUILT_IN_SYNC_XOR_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_XOR_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, XOR, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_NAND_AND_FETCH_1:
-    case BUILT_IN_SYNC_NAND_AND_FETCH_2:
-    case BUILT_IN_SYNC_NAND_AND_FETCH_4:
-    case BUILT_IN_SYNC_NAND_AND_FETCH_8:
-    case BUILT_IN_SYNC_NAND_AND_FETCH_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_NAND_AND_FETCH_1);
-      target = expand_builtin_sync_operation (mode, exp, NOT, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1:
-    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_2:
-    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_4:
-    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_8:
-    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_16:
-      if (mode == VOIDmode)
-       mode = TYPE_MODE (boolean_type_node);
-      if (!target || !register_operand (target, mode))
-       target = gen_reg_rtx (mode);
-
-      mode = get_builtin_sync_mode 
-                               (fcode - BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_1);
-      target = expand_builtin_compare_and_swap (mode, exp, true, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1:
-    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_2:
-    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_4:
-    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_8:
-    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_16:
-      mode = get_builtin_sync_mode 
-                               (fcode - BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1);
-      target = expand_builtin_compare_and_swap (mode, exp, false, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_1:
-    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_2:
-    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_4:
-    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_8:
-    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_TEST_AND_SET_1);
-      target = expand_builtin_sync_lock_test_and_set (mode, exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_SYNC_LOCK_RELEASE_1:
-    case BUILT_IN_SYNC_LOCK_RELEASE_2:
-    case BUILT_IN_SYNC_LOCK_RELEASE_4:
-    case BUILT_IN_SYNC_LOCK_RELEASE_8:
-    case BUILT_IN_SYNC_LOCK_RELEASE_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SYNC_LOCK_RELEASE_1);
-      expand_builtin_sync_lock_release (mode, exp);
-      return const0_rtx;
-
-    case BUILT_IN_SYNC_SYNCHRONIZE:
-      expand_builtin_sync_synchronize ();
-      return const0_rtx;
-
-    case BUILT_IN_ATOMIC_EXCHANGE_1:
-    case BUILT_IN_ATOMIC_EXCHANGE_2:
-    case BUILT_IN_ATOMIC_EXCHANGE_4:
-    case BUILT_IN_ATOMIC_EXCHANGE_8:
-    case BUILT_IN_ATOMIC_EXCHANGE_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_EXCHANGE_1);
-      target = expand_builtin_atomic_exchange (mode, exp, target);
-      if (target)
-       return target;
-      break;
-
-    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:
-      {
-       unsigned int nargs, z;
-       vec<tree, va_gc> *vec;
-
-       mode = 
-           get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1);
-       target = expand_builtin_atomic_compare_exchange (mode, exp, target);
-       if (target)
-         return target;
-
-       /* If this is turned into an external library call, the weak parameter
-          must be dropped to match the expected parameter list.  */
-       nargs = call_expr_nargs (exp);
-       vec_alloc (vec, nargs - 1);
-       for (z = 0; z < 3; z++)
-         vec->quick_push (CALL_EXPR_ARG (exp, z));
-       /* Skip the boolean weak parameter.  */
-       for (z = 4; z < 6; z++)
-         vec->quick_push (CALL_EXPR_ARG (exp, z));
-       exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), vec);
-       break;
-      }
-
-    case BUILT_IN_ATOMIC_LOAD_1:
-    case BUILT_IN_ATOMIC_LOAD_2:
-    case BUILT_IN_ATOMIC_LOAD_4:
-    case BUILT_IN_ATOMIC_LOAD_8:
-    case BUILT_IN_ATOMIC_LOAD_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_LOAD_1);
-      target = expand_builtin_atomic_load (mode, exp, target);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_ATOMIC_STORE_1:
-    case BUILT_IN_ATOMIC_STORE_2:
-    case BUILT_IN_ATOMIC_STORE_4:
-    case BUILT_IN_ATOMIC_STORE_8:
-    case BUILT_IN_ATOMIC_STORE_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_STORE_1);
-      target = expand_builtin_atomic_store (mode, exp);
-      if (target)
-       return const0_rtx;
-      break;
-
-    case BUILT_IN_ATOMIC_ADD_FETCH_1:
-    case BUILT_IN_ATOMIC_ADD_FETCH_2:
-    case BUILT_IN_ATOMIC_ADD_FETCH_4:
-    case BUILT_IN_ATOMIC_ADD_FETCH_8:
-    case BUILT_IN_ATOMIC_ADD_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_ADD_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_SUB_FETCH_1:
-    case BUILT_IN_ATOMIC_SUB_FETCH_2:
-    case BUILT_IN_ATOMIC_SUB_FETCH_4:
-    case BUILT_IN_ATOMIC_SUB_FETCH_8:
-    case BUILT_IN_ATOMIC_SUB_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_SUB_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_AND_FETCH_1:
-    case BUILT_IN_ATOMIC_AND_FETCH_2:
-    case BUILT_IN_ATOMIC_AND_FETCH_4:
-    case BUILT_IN_ATOMIC_AND_FETCH_8:
-    case BUILT_IN_ATOMIC_AND_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_AND_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_AND_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_AND_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_NAND_FETCH_1:
-    case BUILT_IN_ATOMIC_NAND_FETCH_2:
-    case BUILT_IN_ATOMIC_NAND_FETCH_4:
-    case BUILT_IN_ATOMIC_NAND_FETCH_8:
-    case BUILT_IN_ATOMIC_NAND_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_NAND_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_XOR_FETCH_1:
-    case BUILT_IN_ATOMIC_XOR_FETCH_2:
-    case BUILT_IN_ATOMIC_XOR_FETCH_4:
-    case BUILT_IN_ATOMIC_XOR_FETCH_8:
-    case BUILT_IN_ATOMIC_XOR_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_XOR_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_OR_FETCH_1:
-    case BUILT_IN_ATOMIC_OR_FETCH_2:
-    case BUILT_IN_ATOMIC_OR_FETCH_4:
-    case BUILT_IN_ATOMIC_OR_FETCH_8:
-    case BUILT_IN_ATOMIC_OR_FETCH_16:
-      {
-       enum built_in_function lib;
-       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_OR_FETCH_1);
-       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_OR_1 + 
-                                      (fcode - BUILT_IN_ATOMIC_OR_FETCH_1));
-       target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, true,
-                                                ignore, lib);
-       if (target)
-         return target;
-       break;
-      }
-    case BUILT_IN_ATOMIC_FETCH_ADD_1:
-    case BUILT_IN_ATOMIC_FETCH_ADD_2:
-    case BUILT_IN_ATOMIC_FETCH_ADD_4:
-    case BUILT_IN_ATOMIC_FETCH_ADD_8:
-    case BUILT_IN_ATOMIC_FETCH_ADD_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_ADD_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-    case BUILT_IN_ATOMIC_FETCH_SUB_1:
-    case BUILT_IN_ATOMIC_FETCH_SUB_2:
-    case BUILT_IN_ATOMIC_FETCH_SUB_4:
-    case BUILT_IN_ATOMIC_FETCH_SUB_8:
-    case BUILT_IN_ATOMIC_FETCH_SUB_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_SUB_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_ATOMIC_FETCH_AND_1:
-    case BUILT_IN_ATOMIC_FETCH_AND_2:
-    case BUILT_IN_ATOMIC_FETCH_AND_4:
-    case BUILT_IN_ATOMIC_FETCH_AND_8:
-    case BUILT_IN_ATOMIC_FETCH_AND_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_AND_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-  
-    case BUILT_IN_ATOMIC_FETCH_NAND_1:
-    case BUILT_IN_ATOMIC_FETCH_NAND_2:
-    case BUILT_IN_ATOMIC_FETCH_NAND_4:
-    case BUILT_IN_ATOMIC_FETCH_NAND_8:
-    case BUILT_IN_ATOMIC_FETCH_NAND_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_NAND_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-    case BUILT_IN_ATOMIC_FETCH_XOR_1:
-    case BUILT_IN_ATOMIC_FETCH_XOR_2:
-    case BUILT_IN_ATOMIC_FETCH_XOR_4:
-    case BUILT_IN_ATOMIC_FETCH_XOR_8:
-    case BUILT_IN_ATOMIC_FETCH_XOR_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_XOR_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-    case BUILT_IN_ATOMIC_FETCH_OR_1:
-    case BUILT_IN_ATOMIC_FETCH_OR_2:
-    case BUILT_IN_ATOMIC_FETCH_OR_4:
-    case BUILT_IN_ATOMIC_FETCH_OR_8:
-    case BUILT_IN_ATOMIC_FETCH_OR_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_OR_1);
-      target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, false,
-                                              ignore, BUILT_IN_NONE);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_ATOMIC_TEST_AND_SET:
-      return expand_builtin_atomic_test_and_set (exp, target);
-
-    case BUILT_IN_ATOMIC_CLEAR:
-      return expand_builtin_atomic_clear (exp);
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-      return expand_builtin_atomic_always_lock_free (exp);
-
-    case BUILT_IN_ATOMIC_IS_LOCK_FREE:
-      target = expand_builtin_atomic_is_lock_free (exp);
-      if (target)
-        return target;
-      break;
-
-    case BUILT_IN_ATOMIC_THREAD_FENCE:
-      expand_builtin_atomic_thread_fence (exp);
-      return const0_rtx;
-
-    case BUILT_IN_ATOMIC_SIGNAL_FENCE:
-      expand_builtin_atomic_signal_fence (exp);
-      return const0_rtx;
-
-    case BUILT_IN_OBJECT_SIZE:
-      return expand_builtin_object_size (exp);
-
-    case BUILT_IN_MEMCPY_CHK:
-    case BUILT_IN_MEMPCPY_CHK:
-    case BUILT_IN_MEMMOVE_CHK:
-    case BUILT_IN_MEMSET_CHK:
-      target = expand_builtin_memory_chk (exp, target, mode, fcode);
-      if (target)
-       return target;
-      break;
-
-    case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STPCPY_CHK:
-    case BUILT_IN_STRNCPY_CHK:
-    case BUILT_IN_STPNCPY_CHK:
-    case BUILT_IN_STRCAT_CHK:
-    case BUILT_IN_STRNCAT_CHK:
-    case BUILT_IN_SNPRINTF_CHK:
-    case BUILT_IN_VSNPRINTF_CHK:
-      maybe_emit_chk_warning (exp, fcode);
-      break;
-
-    case BUILT_IN_SPRINTF_CHK:
-    case BUILT_IN_VSPRINTF_CHK:
-      maybe_emit_sprintf_chk_warning (exp, fcode);
-      break;
-
-    case BUILT_IN_THREAD_POINTER:
-      return expand_builtin_thread_pointer (exp, target);
-
-    case BUILT_IN_SET_THREAD_POINTER:
-      expand_builtin_set_thread_pointer (exp);
-      return const0_rtx;
-
-    case BUILT_IN_ACC_ON_DEVICE:
-      /* Do library call, if we failed to expand the builtin when
-        folding.  */
-      break;
-
-    case BUILT_IN_GOACC_PARLEVEL_ID:
-    case BUILT_IN_GOACC_PARLEVEL_SIZE:
-      return expand_builtin_goacc_parlevel_id_size (exp, target, ignore);
-
-    case BUILT_IN_SPECULATION_SAFE_VALUE_PTR:
-      return expand_speculation_safe_value (VOIDmode, exp, target, ignore);
-
-    case BUILT_IN_SPECULATION_SAFE_VALUE_1:
-    case BUILT_IN_SPECULATION_SAFE_VALUE_2:
-    case BUILT_IN_SPECULATION_SAFE_VALUE_4:
-    case BUILT_IN_SPECULATION_SAFE_VALUE_8:
-    case BUILT_IN_SPECULATION_SAFE_VALUE_16:
-      mode = get_builtin_sync_mode (fcode - BUILT_IN_SPECULATION_SAFE_VALUE_1);
-      return expand_speculation_safe_value (mode, exp, target, ignore);
-
-    default:   /* just do library call, if unknown builtin */
-      break;
-    }
-
-  /* The switch statement above can drop through to cause the function
-     to be called normally.  */
-  return expand_call (exp, target, ignore);
-}
-
-/* Determine whether a tree node represents a call to a built-in
-   function.  If the tree T is a call to a built-in function with
-   the right number of arguments of the appropriate types, return
-   the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
-   Otherwise the return value is END_BUILTINS.  */
-
-enum built_in_function
-builtin_mathfn_code (const_tree t)
-{
-  const_tree fndecl, arg, parmlist;
-  const_tree argtype, parmtype;
-  const_call_expr_arg_iterator iter;
-
-  if (TREE_CODE (t) != CALL_EXPR)
-    return END_BUILTINS;
-
-  fndecl = get_callee_fndecl (t);
-  if (fndecl == NULL_TREE || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
-      return END_BUILTINS;
-
-  parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-  init_const_call_expr_arg_iterator (t, &iter);
-  for (; parmlist; parmlist = TREE_CHAIN (parmlist))
-    {
-      /* If a function doesn't take a variable number of arguments,
-        the last element in the list will have type `void'.  */
-      parmtype = TREE_VALUE (parmlist);
-      if (VOID_TYPE_P (parmtype))
-       {
-         if (more_const_call_expr_args_p (&iter))
-           return END_BUILTINS;
-         return DECL_FUNCTION_CODE (fndecl);
-       }
-
-      if (! more_const_call_expr_args_p (&iter))
-       return END_BUILTINS;
-
-      arg = next_const_call_expr_arg (&iter);
-      argtype = TREE_TYPE (arg);
-
-      if (SCALAR_FLOAT_TYPE_P (parmtype))
-       {
-         if (! SCALAR_FLOAT_TYPE_P (argtype))
-           return END_BUILTINS;
-       }
-      else if (COMPLEX_FLOAT_TYPE_P (parmtype))
-       {
-         if (! COMPLEX_FLOAT_TYPE_P (argtype))
-           return END_BUILTINS;
-       }
-      else if (POINTER_TYPE_P (parmtype))
-       {
-         if (! POINTER_TYPE_P (argtype))
-           return END_BUILTINS;
-       }
-      else if (INTEGRAL_TYPE_P (parmtype))
-       {
-         if (! INTEGRAL_TYPE_P (argtype))
-           return END_BUILTINS;
-       }
-      else
-       return END_BUILTINS;
-    }
-
-  /* Variable-length argument list.  */
-  return DECL_FUNCTION_CODE (fndecl);
-}
-
-/* Fold a call to __builtin_constant_p, if we know its argument ARG will
-   evaluate to a constant.  */
-
-static tree
-fold_builtin_constant_p (tree arg)
-{
-  /* We return 1 for a numeric type that's known to be a constant
-     value at compile-time or for an aggregate type that's a
-     literal constant.  */
-  STRIP_NOPS (arg);
-
-  /* If we know this is a constant, emit the constant of one.  */
-  if (CONSTANT_CLASS_P (arg)
-      || (TREE_CODE (arg) == CONSTRUCTOR
-         && TREE_CONSTANT (arg)))
-    return integer_one_node;
-  if (TREE_CODE (arg) == ADDR_EXPR)
-    {
-       tree op = TREE_OPERAND (arg, 0);
-       if (TREE_CODE (op) == STRING_CST
-          || (TREE_CODE (op) == ARRAY_REF
-              && integer_zerop (TREE_OPERAND (op, 1))
-              && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST))
-        return integer_one_node;
-    }
-
-  /* If this expression has side effects, show we don't know it to be a
-     constant.  Likewise if it's a pointer or aggregate type since in
-     those case we only want literals, since those are only optimized
-     when generating RTL, not later.
-     And finally, if we are compiling an initializer, not code, we
-     need to return a definite result now; there's not going to be any
-     more optimization done.  */
-  if (TREE_SIDE_EFFECTS (arg)
-      || AGGREGATE_TYPE_P (TREE_TYPE (arg))
-      || POINTER_TYPE_P (TREE_TYPE (arg))
-      || cfun == 0
-      || folding_initializer
-      || force_folding_builtin_constant_p)
-    return integer_zero_node;
-
-  return NULL_TREE;
-}
-
-/* Create builtin_expect or builtin_expect_with_probability
-   with PRED and EXPECTED as its arguments and return it as a truthvalue.
-   Fortran FE can also produce builtin_expect with PREDICTOR as third argument.
-   builtin_expect_with_probability instead uses third argument as PROBABILITY
-   value.  */
-
-static tree
-build_builtin_expect_predicate (location_t loc, tree pred, tree expected,
-                               tree predictor, tree probability)
-{
-  tree fn, arg_types, pred_type, expected_type, call_expr, ret_type;
-
-  fn = builtin_decl_explicit (probability == NULL_TREE ? BUILT_IN_EXPECT
-                             : BUILT_IN_EXPECT_WITH_PROBABILITY);
-  arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  ret_type = TREE_TYPE (TREE_TYPE (fn));
-  pred_type = TREE_VALUE (arg_types);
-  expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
-
-  pred = fold_convert_loc (loc, pred_type, pred);
-  expected = fold_convert_loc (loc, expected_type, expected);
-
-  if (probability)
-    call_expr = build_call_expr_loc (loc, fn, 3, pred, expected, probability);
-  else
-    call_expr = build_call_expr_loc (loc, fn, predictor ? 3 : 2, pred, expected,
-                                    predictor);
-
-  return build2 (NE_EXPR, TREE_TYPE (pred), call_expr,
-                build_int_cst (ret_type, 0));
-}
-
-/* Fold a call to builtin_expect with arguments ARG0, ARG1, ARG2, ARG3.  Return
-   NULL_TREE if no simplification is possible.  */
-
-tree
-fold_builtin_expect (location_t loc, tree arg0, tree arg1, tree arg2,
-                    tree arg3)
-{
-  tree inner, fndecl, inner_arg0;
-  enum tree_code code;
-
-  /* Distribute the expected value over short-circuiting operators.
-     See through the cast from truthvalue_type_node to long.  */
-  inner_arg0 = arg0;
-  while (CONVERT_EXPR_P (inner_arg0)
-        && INTEGRAL_TYPE_P (TREE_TYPE (inner_arg0))
-        && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner_arg0, 0))))
-    inner_arg0 = TREE_OPERAND (inner_arg0, 0);
-
-  /* If this is a builtin_expect within a builtin_expect keep the
-     inner one.  See through a comparison against a constant.  It
-     might have been added to create a thruthvalue.  */
-  inner = inner_arg0;
-
-  if (COMPARISON_CLASS_P (inner)
-      && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST)
-    inner = TREE_OPERAND (inner, 0);
-
-  if (TREE_CODE (inner) == CALL_EXPR
-      && (fndecl = get_callee_fndecl (inner))
-      && (fndecl_built_in_p (fndecl, BUILT_IN_EXPECT)
-         || fndecl_built_in_p (fndecl, BUILT_IN_EXPECT_WITH_PROBABILITY)))
-    return arg0;
-
-  inner = inner_arg0;
-  code = TREE_CODE (inner);
-  if (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
-    {
-      tree op0 = TREE_OPERAND (inner, 0);
-      tree op1 = TREE_OPERAND (inner, 1);
-      arg1 = save_expr (arg1);
-
-      op0 = build_builtin_expect_predicate (loc, op0, arg1, arg2, arg3);
-      op1 = build_builtin_expect_predicate (loc, op1, arg1, arg2, arg3);
-      inner = build2 (code, TREE_TYPE (inner), op0, op1);
-
-      return fold_convert_loc (loc, TREE_TYPE (arg0), inner);
-    }
-
-  /* If the argument isn't invariant then there's nothing else we can do.  */
-  if (!TREE_CONSTANT (inner_arg0))
-    return NULL_TREE;
-
-  /* If we expect that a comparison against the argument will fold to
-     a constant return the constant.  In practice, this means a true
-     constant or the address of a non-weak symbol.  */
-  inner = inner_arg0;
-  STRIP_NOPS (inner);
-  if (TREE_CODE (inner) == ADDR_EXPR)
-    {
-      do
-       {
-         inner = TREE_OPERAND (inner, 0);
-       }
-      while (TREE_CODE (inner) == COMPONENT_REF
-            || TREE_CODE (inner) == ARRAY_REF);
-      if (VAR_OR_FUNCTION_DECL_P (inner) && DECL_WEAK (inner))
-       return NULL_TREE;
-    }
-
-  /* Otherwise, ARG0 already has the proper type for the return value.  */
-  return arg0;
-}
-
-/* Fold a call to __builtin_classify_type with argument ARG.  */
-
-static tree
-fold_builtin_classify_type (tree arg)
-{
-  if (arg == 0)
-    return build_int_cst (integer_type_node, no_type_class);
-
-  return build_int_cst (integer_type_node, type_to_class (TREE_TYPE (arg)));
-}
-
-/* Fold a call EXPR (which may be null) to __builtin_strlen with argument
-   ARG.  */
-
-static tree
-fold_builtin_strlen (location_t loc, tree expr, tree type, tree arg)
-{
-  if (!validate_arg (arg, POINTER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      c_strlen_data lendata = { };
-      tree len = c_strlen (arg, 0, &lendata);
-
-      if (len)
-       return fold_convert_loc (loc, type, len);
-
-      if (!lendata.decl)
-       c_strlen (arg, 1, &lendata);
-
-      if (lendata.decl)
-       {
-         if (EXPR_HAS_LOCATION (arg))
-           loc = EXPR_LOCATION (arg);
-         else if (loc == UNKNOWN_LOCATION)
-           loc = input_location;
-         warn_string_no_nul (loc, expr, "strlen", arg, lendata.decl);
-       }
-
-      return NULL_TREE;
-    }
-}
-
-/* Fold a call to __builtin_inf or __builtin_huge_val.  */
-
-static tree
-fold_builtin_inf (location_t loc, tree type, int warn)
-{
-  REAL_VALUE_TYPE real;
-
-  /* __builtin_inff is intended to be usable to define INFINITY on all
-     targets.  If an infinity is not available, INFINITY expands "to a
-     positive constant of type float that overflows at translation
-     time", footnote "In this case, using INFINITY will violate the
-     constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4).
-     Thus we pedwarn to ensure this constraint violation is
-     diagnosed.  */
-  if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
-    pedwarn (loc, 0, "target format does not support infinity");
-
-  real_inf (&real);
-  return build_real (type, real);
-}
-
-/* Fold function call to builtin sincos, sincosf, or sincosl.  Return
-   NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_sincos (location_t loc,
-                    tree arg0, tree arg1, tree arg2)
-{
-  tree type;
-  tree fndecl, call = NULL_TREE;
-
-  if (!validate_arg (arg0, REAL_TYPE)
-      || !validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  type = TREE_TYPE (arg0);
-
-  /* Calculate the result when the argument is a constant.  */
-  built_in_function fn = mathfn_built_in_2 (type, CFN_BUILT_IN_CEXPI);
-  if (fn == END_BUILTINS)
-    return NULL_TREE;
-
-  /* Canonicalize sincos to cexpi.  */
-  if (TREE_CODE (arg0) == REAL_CST)
-    {
-      tree complex_type = build_complex_type (type);
-      call = fold_const_call (as_combined_fn (fn), complex_type, arg0);
-    }
-  if (!call)
-    {
-      if (!targetm.libc_has_function (function_c99_math_complex, type)
-         || !builtin_decl_implicit_p (fn))
-       return NULL_TREE;
-      fndecl = builtin_decl_explicit (fn);
-      call = build_call_expr_loc (loc, fndecl, 1, arg0);
-      call = builtin_save_expr (call);
-    }
-
-  tree ptype = build_pointer_type (type);
-  arg1 = fold_convert (ptype, arg1);
-  arg2 = fold_convert (ptype, arg2);
-  return build2 (COMPOUND_EXPR, void_type_node,
-                build2 (MODIFY_EXPR, void_type_node,
-                        build_fold_indirect_ref_loc (loc, arg1),
-                        fold_build1_loc (loc, IMAGPART_EXPR, type, call)),
-                build2 (MODIFY_EXPR, void_type_node,
-                        build_fold_indirect_ref_loc (loc, arg2),
-                        fold_build1_loc (loc, REALPART_EXPR, type, call)));
-}
-
-/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-                             arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      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 ind1
-       = fold_convert_loc (loc, integer_type_node,
-                           build1 (INDIRECT_REF, cst_uchar_node,
-                                   fold_convert_loc (loc,
-                                                     cst_uchar_ptr_node,
-                                                     arg1)));
-      tree ind2
-       = fold_convert_loc (loc, integer_type_node,
-                           build1 (INDIRECT_REF, cst_uchar_node,
-                                   fold_convert_loc (loc,
-                                                     cst_uchar_ptr_node,
-                                                     arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin isascii with argument ARG.  */
-
-static tree
-fold_builtin_isascii (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      /* Transform isascii(c) -> ((c & ~0x7f) == 0).  */
-      arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
-                        build_int_cst (integer_type_node,
-                                       ~ (unsigned HOST_WIDE_INT) 0x7f));
-      return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
-                             arg, integer_zero_node);
-    }
-}
-
-/* Fold a call to builtin toascii with argument ARG.  */
-
-static tree
-fold_builtin_toascii (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* Transform toascii(c) -> (c & 0x7f).  */
-  return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
-                         build_int_cst (integer_type_node, 0x7f));
-}
-
-/* Fold a call to builtin isdigit with argument ARG.  */
-
-static tree
-fold_builtin_isdigit (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
-      /* According to the C standard, isdigit is unaffected by locale.
-        However, it definitely is affected by the target character set.  */
-      unsigned HOST_WIDE_INT target_digit0
-       = lang_hooks.to_target_charset ('0');
-
-      if (target_digit0 == 0)
-       return NULL_TREE;
-
-      arg = fold_convert_loc (loc, unsigned_type_node, arg);
-      arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
-                        build_int_cst (unsigned_type_node, target_digit0));
-      return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
-                         build_int_cst (unsigned_type_node, 9));
-    }
-}
-
-/* Fold a call to fabs, fabsf or fabsl with argument ARG.  */
-
-static tree
-fold_builtin_fabs (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  arg = fold_convert_loc (loc, type, arg);
-  return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
-
-/* Fold a call to abs, labs, llabs or imaxabs with argument ARG.  */
-
-static tree
-fold_builtin_abs (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  arg = fold_convert_loc (loc, type, arg);
-  return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
-
-/* Fold a call to builtin carg(a+bi) -> atan2(b,a).  */
-
-static tree
-fold_builtin_carg (location_t loc, tree arg, tree type)
-{
-  if (validate_arg (arg, COMPLEX_TYPE)
-      && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE)
-    {
-      tree atan2_fn = mathfn_built_in (type, BUILT_IN_ATAN2);
-
-      if (atan2_fn)
-        {
-         tree new_arg = builtin_save_expr (arg);
-         tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
-         tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
-         return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin frexp, we can assume the base is 2.  */
-
-static tree
-fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
-{
-  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
-    return NULL_TREE;
-
-  STRIP_NOPS (arg0);
-
-  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
-    return NULL_TREE;
-
-  arg1 = build_fold_indirect_ref_loc (loc, arg1);
-
-  /* Proceed if a valid pointer type was passed in.  */
-  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == integer_type_node)
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
-      tree frac, exp;
-
-      switch (value->cl)
+    case BUILT_IN_ATOMIC_ADD_FETCH_1:
+    case BUILT_IN_ATOMIC_ADD_FETCH_2:
+    case BUILT_IN_ATOMIC_ADD_FETCH_4:
+    case BUILT_IN_ATOMIC_ADD_FETCH_8:
+    case BUILT_IN_ATOMIC_ADD_FETCH_16:
+      {
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_ADD_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_ADD_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, true,
+                                                ignore, lib);
+       if (target)
+         return target;
+       break;
+      }
+    case BUILT_IN_ATOMIC_SUB_FETCH_1:
+    case BUILT_IN_ATOMIC_SUB_FETCH_2:
+    case BUILT_IN_ATOMIC_SUB_FETCH_4:
+    case BUILT_IN_ATOMIC_SUB_FETCH_8:
+    case BUILT_IN_ATOMIC_SUB_FETCH_16:
       {
-      case rvc_zero:
-       /* For +-0, return (*exp = 0, +-0).  */
-       exp = integer_zero_node;
-       frac = arg0;
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_SUB_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_SUB_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, true,
+                                                ignore, lib);
+       if (target)
+         return target;
        break;
-      case rvc_nan:
-      case rvc_inf:
-       /* For +-NaN or +-Inf, *exp is unspecified, return arg0.  */
-       return omit_one_operand_loc (loc, rettype, arg0, arg1);
-      case rvc_normal:
-       {
-         /* Since the frexp function always expects base 2, and in
-            GCC normalized significands are already in the range
-            [0.5, 1.0), we have exactly what frexp wants.  */
-         REAL_VALUE_TYPE frac_rvt = *value;
-         SET_REAL_EXP (&frac_rvt, 0);
-         frac = build_real (rettype, frac_rvt);
-         exp = build_int_cst (integer_type_node, REAL_EXP (value));
-       }
+      }
+    case BUILT_IN_ATOMIC_AND_FETCH_1:
+    case BUILT_IN_ATOMIC_AND_FETCH_2:
+    case BUILT_IN_ATOMIC_AND_FETCH_4:
+    case BUILT_IN_ATOMIC_AND_FETCH_8:
+    case BUILT_IN_ATOMIC_AND_FETCH_16:
+      {
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_AND_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_AND_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_AND_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, true,
+                                                ignore, lib);
+       if (target)
+         return target;
+       break;
+      }
+    case BUILT_IN_ATOMIC_NAND_FETCH_1:
+    case BUILT_IN_ATOMIC_NAND_FETCH_2:
+    case BUILT_IN_ATOMIC_NAND_FETCH_4:
+    case BUILT_IN_ATOMIC_NAND_FETCH_8:
+    case BUILT_IN_ATOMIC_NAND_FETCH_16:
+      {
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_NAND_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_NAND_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, true,
+                                                ignore, lib);
+       if (target)
+         return target;
+       break;
+      }
+    case BUILT_IN_ATOMIC_XOR_FETCH_1:
+    case BUILT_IN_ATOMIC_XOR_FETCH_2:
+    case BUILT_IN_ATOMIC_XOR_FETCH_4:
+    case BUILT_IN_ATOMIC_XOR_FETCH_8:
+    case BUILT_IN_ATOMIC_XOR_FETCH_16:
+      {
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_XOR_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_XOR_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, true,
+                                                ignore, lib);
+       if (target)
+         return target;
+       break;
+      }
+    case BUILT_IN_ATOMIC_OR_FETCH_1:
+    case BUILT_IN_ATOMIC_OR_FETCH_2:
+    case BUILT_IN_ATOMIC_OR_FETCH_4:
+    case BUILT_IN_ATOMIC_OR_FETCH_8:
+    case BUILT_IN_ATOMIC_OR_FETCH_16:
+      {
+       enum built_in_function lib;
+       mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_OR_FETCH_1);
+       lib = (enum built_in_function)((int)BUILT_IN_ATOMIC_FETCH_OR_1 + 
+                                      (fcode - BUILT_IN_ATOMIC_OR_FETCH_1));
+       target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, true,
+                                                ignore, lib);
+       if (target)
+         return target;
        break;
-      default:
-       gcc_unreachable ();
       }
+    case BUILT_IN_ATOMIC_FETCH_ADD_1:
+    case BUILT_IN_ATOMIC_FETCH_ADD_2:
+    case BUILT_IN_ATOMIC_FETCH_ADD_4:
+    case BUILT_IN_ATOMIC_FETCH_ADD_8:
+    case BUILT_IN_ATOMIC_FETCH_ADD_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_ADD_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, PLUS, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_SUB_1:
+    case BUILT_IN_ATOMIC_FETCH_SUB_2:
+    case BUILT_IN_ATOMIC_FETCH_SUB_4:
+    case BUILT_IN_ATOMIC_FETCH_SUB_8:
+    case BUILT_IN_ATOMIC_FETCH_SUB_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_SUB_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, MINUS, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
 
-      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
-      arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1, exp);
-      TREE_SIDE_EFFECTS (arg1) = 1;
-      return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1, frac);
-    }
+    case BUILT_IN_ATOMIC_FETCH_AND_1:
+    case BUILT_IN_ATOMIC_FETCH_AND_2:
+    case BUILT_IN_ATOMIC_FETCH_AND_4:
+    case BUILT_IN_ATOMIC_FETCH_AND_8:
+    case BUILT_IN_ATOMIC_FETCH_AND_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_AND_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, AND, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
+  
+    case BUILT_IN_ATOMIC_FETCH_NAND_1:
+    case BUILT_IN_ATOMIC_FETCH_NAND_2:
+    case BUILT_IN_ATOMIC_FETCH_NAND_4:
+    case BUILT_IN_ATOMIC_FETCH_NAND_8:
+    case BUILT_IN_ATOMIC_FETCH_NAND_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_NAND_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, NOT, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_XOR_1:
+    case BUILT_IN_ATOMIC_FETCH_XOR_2:
+    case BUILT_IN_ATOMIC_FETCH_XOR_4:
+    case BUILT_IN_ATOMIC_FETCH_XOR_8:
+    case BUILT_IN_ATOMIC_FETCH_XOR_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_XOR_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, XOR, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
+    case BUILT_IN_ATOMIC_FETCH_OR_1:
+    case BUILT_IN_ATOMIC_FETCH_OR_2:
+    case BUILT_IN_ATOMIC_FETCH_OR_4:
+    case BUILT_IN_ATOMIC_FETCH_OR_8:
+    case BUILT_IN_ATOMIC_FETCH_OR_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_FETCH_OR_1);
+      target = expand_builtin_atomic_fetch_op (mode, exp, target, IOR, false,
+                                              ignore, BUILT_IN_NONE);
+      if (target)
+       return target;
+      break;
 
-  return NULL_TREE;
-}
+    case BUILT_IN_ATOMIC_TEST_AND_SET:
+      return expand_builtin_atomic_test_and_set (exp, target);
 
-/* Fold a call to builtin modf.  */
+    case BUILT_IN_ATOMIC_CLEAR:
+      return expand_builtin_atomic_clear (exp);
+    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+      return expand_builtin_atomic_always_lock_free (exp);
 
-static tree
-fold_builtin_modf (location_t loc, tree arg0, tree arg1, tree rettype)
-{
-  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
-    return NULL_TREE;
+    case BUILT_IN_ATOMIC_IS_LOCK_FREE:
+      target = expand_builtin_atomic_is_lock_free (exp);
+      if (target)
+        return target;
+      break;
 
-  STRIP_NOPS (arg0);
+    case BUILT_IN_ATOMIC_THREAD_FENCE:
+      expand_builtin_atomic_thread_fence (exp);
+      return const0_rtx;
 
-  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
-    return NULL_TREE;
+    case BUILT_IN_ATOMIC_SIGNAL_FENCE:
+      expand_builtin_atomic_signal_fence (exp);
+      return const0_rtx;
 
-  arg1 = build_fold_indirect_ref_loc (loc, arg1);
+    case BUILT_IN_OBJECT_SIZE:
+      return expand_builtin_object_size (exp);
 
-  /* Proceed if a valid pointer type was passed in.  */
-  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == TYPE_MAIN_VARIANT (rettype))
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
-      REAL_VALUE_TYPE trunc, frac;
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET_CHK:
+      target = expand_builtin_memory_chk (exp, target, mode, fcode);
+      if (target)
+       return target;
+      break;
 
-      switch (value->cl)
-      {
-      case rvc_nan:
-      case rvc_zero:
-       /* For +-NaN or +-0, return (*arg1 = arg0, arg0).  */
-       trunc = frac = *value;
-       break;
-      case rvc_inf:
-       /* For +-Inf, return (*arg1 = arg0, +-0).  */
-       frac = dconst0;
-       frac.sign = value->sign;
-       trunc = *value;
-       break;
-      case rvc_normal:
-       /* Return (*arg1 = trunc(arg0), arg0-trunc(arg0)).  */
-       real_trunc (&trunc, VOIDmode, value);
-       real_arithmetic (&frac, MINUS_EXPR, value, &trunc);
-       /* If the original number was negative and already
-          integral, then the fractional part is -0.0.  */
-       if (value->sign && frac.cl == rvc_zero)
-         frac.sign = value->sign;
-       break;
-      }
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      maybe_emit_chk_warning (exp, fcode);
+      break;
+
+    case BUILT_IN_SPRINTF_CHK:
+    case BUILT_IN_VSPRINTF_CHK:
+      maybe_emit_sprintf_chk_warning (exp, fcode);
+      break;
 
-      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
-      arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1,
-                         build_real (rettype, trunc));
-      TREE_SIDE_EFFECTS (arg1) = 1;
-      return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1,
-                         build_real (rettype, frac));
+    case BUILT_IN_THREAD_POINTER:
+      return expand_builtin_thread_pointer (exp, target);
+
+    case BUILT_IN_SET_THREAD_POINTER:
+      expand_builtin_set_thread_pointer (exp);
+      return const0_rtx;
+
+    case BUILT_IN_ACC_ON_DEVICE:
+      /* Do library call, if we failed to expand the builtin when
+        folding.  */
+      break;
+
+    case BUILT_IN_GOACC_PARLEVEL_ID:
+    case BUILT_IN_GOACC_PARLEVEL_SIZE:
+      return expand_builtin_goacc_parlevel_id_size (exp, target, ignore);
+
+    case BUILT_IN_SPECULATION_SAFE_VALUE_PTR:
+      return expand_speculation_safe_value (VOIDmode, exp, target, ignore);
+
+    case BUILT_IN_SPECULATION_SAFE_VALUE_1:
+    case BUILT_IN_SPECULATION_SAFE_VALUE_2:
+    case BUILT_IN_SPECULATION_SAFE_VALUE_4:
+    case BUILT_IN_SPECULATION_SAFE_VALUE_8:
+    case BUILT_IN_SPECULATION_SAFE_VALUE_16:
+      mode = get_builtin_sync_mode (fcode - BUILT_IN_SPECULATION_SAFE_VALUE_1);
+      return expand_speculation_safe_value (mode, exp, target, ignore);
+
+    default:   /* just do library call, if unknown builtin */
+      break;
     }
 
-  return NULL_TREE;
+  /* The switch statement above can drop through to cause the function
+     to be called normally.  */
+  return expand_call (exp, target, ignore);
 }
 
-/* Given a location LOC, an interclass builtin function decl FNDECL
-   and its single argument ARG, return an folded expression computing
-   the same, or NULL_TREE if we either couldn't or didn't want to fold
-   (the latter happen if there's an RTL instruction available).  */
+/* Determine whether a tree node represents a call to a built-in
+   function.  If the tree T is a call to a built-in function with
+   the right number of arguments of the appropriate types, return
+   the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
+   Otherwise the return value is END_BUILTINS.  */
 
-static tree
-fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
+enum built_in_function
+builtin_mathfn_code (const_tree t)
 {
-  machine_mode mode;
-
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  if (interclass_mathfn_icode (arg, fndecl) != CODE_FOR_nothing)
-    return NULL_TREE;
+  const_tree fndecl, arg, parmlist;
+  const_tree argtype, parmtype;
+  const_call_expr_arg_iterator iter;
 
-  mode = TYPE_MODE (TREE_TYPE (arg));
+  if (TREE_CODE (t) != CALL_EXPR)
+    return END_BUILTINS;
 
-  bool is_ibm_extended = MODE_COMPOSITE_P (mode);
+  fndecl = get_callee_fndecl (t);
+  if (fndecl == NULL_TREE || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+      return END_BUILTINS;
 
-  /* If there is no optab, try generic code.  */
-  switch (DECL_FUNCTION_CODE (fndecl))
+  parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  init_const_call_expr_arg_iterator (t, &iter);
+  for (; parmlist; parmlist = TREE_CHAIN (parmlist))
     {
-      tree result;
+      /* If a function doesn't take a variable number of arguments,
+        the last element in the list will have type `void'.  */
+      parmtype = TREE_VALUE (parmlist);
+      if (VOID_TYPE_P (parmtype))
+       {
+         if (more_const_call_expr_args_p (&iter))
+           return END_BUILTINS;
+         return DECL_FUNCTION_CODE (fndecl);
+       }
 
-    CASE_FLT_FN (BUILT_IN_ISINF):
-      {
-       /* isinf(x) -> isgreater(fabs(x),DBL_MAX).  */
-       tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
-       tree type = TREE_TYPE (arg);
-       REAL_VALUE_TYPE r;
-       char buf[128];
+      if (! more_const_call_expr_args_p (&iter))
+       return END_BUILTINS;
 
-       if (is_ibm_extended)
-         {
-           /* NaN and Inf are encoded in the high-order double value
-              only.  The low-order value is not significant.  */
-           type = double_type_node;
-           mode = DFmode;
-           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
-         }
-       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
-       real_from_string (&r, buf);
-       result = build_call_expr (isgr_fn, 2,
-                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
-                                 build_real (type, r));
-       return result;
-      }
-    CASE_FLT_FN (BUILT_IN_FINITE):
-    case BUILT_IN_ISFINITE:
-      {
-       /* isfinite(x) -> islessequal(fabs(x),DBL_MAX).  */
-       tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
-       tree type = TREE_TYPE (arg);
-       REAL_VALUE_TYPE r;
-       char buf[128];
+      arg = next_const_call_expr_arg (&iter);
+      argtype = TREE_TYPE (arg);
 
-       if (is_ibm_extended)
-         {
-           /* NaN and Inf are encoded in the high-order double value
-              only.  The low-order value is not significant.  */
-           type = double_type_node;
-           mode = DFmode;
-           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
-         }
-       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
-       real_from_string (&r, buf);
-       result = build_call_expr (isle_fn, 2,
-                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
-                                 build_real (type, r));
-       /*result = fold_build2_loc (loc, UNGT_EXPR,
-                                 TREE_TYPE (TREE_TYPE (fndecl)),
-                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
-                                 build_real (type, r));
-       result = fold_build1_loc (loc, TRUTH_NOT_EXPR,
-                                 TREE_TYPE (TREE_TYPE (fndecl)),
-                                 result);*/
-       return result;
-      }
-    case BUILT_IN_ISNORMAL:
-      {
-       /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
-          islessequal(fabs(x),DBL_MAX).  */
-       tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
-       tree type = TREE_TYPE (arg);
-       tree orig_arg, max_exp, min_exp;
-       machine_mode orig_mode = mode;
-       REAL_VALUE_TYPE rmax, rmin;
-       char buf[128];
+      if (SCALAR_FLOAT_TYPE_P (parmtype))
+       {
+         if (! SCALAR_FLOAT_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (COMPLEX_FLOAT_TYPE_P (parmtype))
+       {
+         if (! COMPLEX_FLOAT_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (POINTER_TYPE_P (parmtype))
+       {
+         if (! POINTER_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else if (INTEGRAL_TYPE_P (parmtype))
+       {
+         if (! INTEGRAL_TYPE_P (argtype))
+           return END_BUILTINS;
+       }
+      else
+       return END_BUILTINS;
+    }
 
-       orig_arg = arg = builtin_save_expr (arg);
-       if (is_ibm_extended)
-         {
-           /* Use double to test the normal range of IBM extended
-              precision.  Emin for IBM extended precision is
-              different to emin for IEEE double, being 53 higher
-              since the low double exponent is at least 53 lower
-              than the high double exponent.  */
-           type = double_type_node;
-           mode = DFmode;
-           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
-         }
-       arg = fold_build1_loc (loc, ABS_EXPR, type, arg);
+  /* Variable-length argument list.  */
+  return DECL_FUNCTION_CODE (fndecl);
+}
 
-       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
-       real_from_string (&rmax, buf);
-       sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (orig_mode)->emin - 1);
-       real_from_string (&rmin, buf);
-       max_exp = build_real (type, rmax);
-       min_exp = build_real (type, rmin);
+/* Fold a call to __builtin_constant_p, if we know its argument ARG will
+   evaluate to a constant.  */
 
-       max_exp = build_call_expr (isle_fn, 2, arg, max_exp);
-       if (is_ibm_extended)
-         {
-           /* Testing the high end of the range is done just using
-              the high double, using the same test as isfinite().
-              For the subnormal end of the range we first test the
-              high double, then if its magnitude is equal to the
-              limit of 0x1p-969, we test whether the low double is
-              non-zero and opposite sign to the high double.  */
-           tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
-           tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
-           tree gt_min = build_call_expr (isgt_fn, 2, arg, min_exp);
-           tree eq_min = fold_build2 (EQ_EXPR, integer_type_node,
-                                      arg, min_exp);
-           tree as_complex = build1 (VIEW_CONVERT_EXPR,
-                                     complex_double_type_node, orig_arg);
-           tree hi_dbl = build1 (REALPART_EXPR, type, as_complex);
-           tree lo_dbl = build1 (IMAGPART_EXPR, type, as_complex);
-           tree zero = build_real (type, dconst0);
-           tree hilt = build_call_expr (islt_fn, 2, hi_dbl, zero);
-           tree lolt = build_call_expr (islt_fn, 2, lo_dbl, zero);
-           tree logt = build_call_expr (isgt_fn, 2, lo_dbl, zero);
-           tree ok_lo = fold_build1 (TRUTH_NOT_EXPR, integer_type_node,
-                                     fold_build3 (COND_EXPR,
-                                                  integer_type_node,
-                                                  hilt, logt, lolt));
-           eq_min = fold_build2 (TRUTH_ANDIF_EXPR, integer_type_node,
-                                 eq_min, ok_lo);
-           min_exp = fold_build2 (TRUTH_ORIF_EXPR, integer_type_node,
-                                  gt_min, eq_min);
-         }
-       else
-         {
-           tree const isge_fn
-             = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
-           min_exp = build_call_expr (isge_fn, 2, arg, min_exp);
-         }
-       result = fold_build2 (BIT_AND_EXPR, integer_type_node,
-                             max_exp, min_exp);
-       return result;
-      }
-    default:
-      break;
+static tree
+fold_builtin_constant_p (tree arg)
+{
+  /* We return 1 for a numeric type that's known to be a constant
+     value at compile-time or for an aggregate type that's a
+     literal constant.  */
+  STRIP_NOPS (arg);
+
+  /* If we know this is a constant, emit the constant of one.  */
+  if (CONSTANT_CLASS_P (arg)
+      || (TREE_CODE (arg) == CONSTRUCTOR
+         && TREE_CONSTANT (arg)))
+    return integer_one_node;
+  if (TREE_CODE (arg) == ADDR_EXPR)
+    {
+       tree op = TREE_OPERAND (arg, 0);
+       if (TREE_CODE (op) == STRING_CST
+          || (TREE_CODE (op) == ARRAY_REF
+              && integer_zerop (TREE_OPERAND (op, 1))
+              && TREE_CODE (TREE_OPERAND (op, 0)) == STRING_CST))
+        return integer_one_node;
     }
 
+  /* If this expression has side effects, show we don't know it to be a
+     constant.  Likewise if it's a pointer or aggregate type since in
+     those case we only want literals, since those are only optimized
+     when generating RTL, not later.
+     And finally, if we are compiling an initializer, not code, we
+     need to return a definite result now; there's not going to be any
+     more optimization done.  */
+  if (TREE_SIDE_EFFECTS (arg)
+      || AGGREGATE_TYPE_P (TREE_TYPE (arg))
+      || POINTER_TYPE_P (TREE_TYPE (arg))
+      || cfun == 0
+      || folding_initializer
+      || force_folding_builtin_constant_p)
+    return integer_zero_node;
+
   return NULL_TREE;
 }
 
-/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
-   ARG is the argument for the call.  */
+/* Create builtin_expect or builtin_expect_with_probability
+   with PRED and EXPECTED as its arguments and return it as a truthvalue.
+   Fortran FE can also produce builtin_expect with PREDICTOR as third argument.
+   builtin_expect_with_probability instead uses third argument as PROBABILITY
+   value.  */
 
 static tree
-fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
+build_builtin_expect_predicate (location_t loc, tree pred, tree expected,
+                               tree predictor, tree probability)
 {
-  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  tree fn, arg_types, pred_type, expected_type, call_expr, ret_type;
 
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
+  fn = builtin_decl_explicit (probability == NULL_TREE ? BUILT_IN_EXPECT
+                             : BUILT_IN_EXPECT_WITH_PROBABILITY);
+  arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+  ret_type = TREE_TYPE (TREE_TYPE (fn));
+  pred_type = TREE_VALUE (arg_types);
+  expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
 
-  switch (builtin_index)
-    {
-    case BUILT_IN_ISINF:
-      if (tree_expr_infinite_p (arg))
-       return omit_one_operand_loc (loc, type, integer_one_node, arg);
-      if (!tree_expr_maybe_infinite_p (arg))
-       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-      return NULL_TREE;
+  pred = fold_convert_loc (loc, pred_type, pred);
+  expected = fold_convert_loc (loc, expected_type, expected);
 
-    case BUILT_IN_ISINF_SIGN:
-      {
-       /* isinf_sign(x) -> isinf(x) ? (signbit(x) ? -1 : 1) : 0 */
-       /* In a boolean context, GCC will fold the inner COND_EXPR to
-          1.  So e.g. "if (isinf_sign(x))" would be folded to just
-          "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */
-       tree signbit_fn = builtin_decl_explicit (BUILT_IN_SIGNBIT);
-       tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
-       tree tmp = NULL_TREE;
+  if (probability)
+    call_expr = build_call_expr_loc (loc, fn, 3, pred, expected, probability);
+  else
+    call_expr = build_call_expr_loc (loc, fn, predictor ? 3 : 2, pred, expected,
+                                    predictor);
 
-       arg = builtin_save_expr (arg);
+  return build2 (NE_EXPR, TREE_TYPE (pred), call_expr,
+                build_int_cst (ret_type, 0));
+}
 
-       if (signbit_fn && isinf_fn)
-         {
-           tree signbit_call = build_call_expr_loc (loc, signbit_fn, 1, arg);
-           tree isinf_call = build_call_expr_loc (loc, isinf_fn, 1, arg);
+/* Fold a call to builtin_expect with arguments ARG0, ARG1, ARG2, ARG3.  Return
+   NULL_TREE if no simplification is possible.  */
 
-           signbit_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
-                                       signbit_call, integer_zero_node);
-           isinf_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
-                                     isinf_call, integer_zero_node);
+tree
+fold_builtin_expect (location_t loc, tree arg0, tree arg1, tree arg2,
+                    tree arg3)
+{
+  tree inner, fndecl, inner_arg0;
+  enum tree_code code;
 
-           tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node, signbit_call,
-                              integer_minus_one_node, integer_one_node);
-           tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node,
-                              isinf_call, tmp,
-                              integer_zero_node);
-         }
+  /* Distribute the expected value over short-circuiting operators.
+     See through the cast from truthvalue_type_node to long.  */
+  inner_arg0 = arg0;
+  while (CONVERT_EXPR_P (inner_arg0)
+        && INTEGRAL_TYPE_P (TREE_TYPE (inner_arg0))
+        && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (inner_arg0, 0))))
+    inner_arg0 = TREE_OPERAND (inner_arg0, 0);
 
-       return tmp;
-      }
+  /* If this is a builtin_expect within a builtin_expect keep the
+     inner one.  See through a comparison against a constant.  It
+     might have been added to create a thruthvalue.  */
+  inner = inner_arg0;
 
-    case BUILT_IN_ISFINITE:
-      if (tree_expr_finite_p (arg))
-       return omit_one_operand_loc (loc, type, integer_one_node, arg);
-      if (tree_expr_nan_p (arg) || tree_expr_infinite_p (arg))
-       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-      return NULL_TREE;
+  if (COMPARISON_CLASS_P (inner)
+      && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST)
+    inner = TREE_OPERAND (inner, 0);
 
-    case BUILT_IN_ISNAN:
-      if (tree_expr_nan_p (arg))
-       return omit_one_operand_loc (loc, type, integer_one_node, arg);
-      if (!tree_expr_maybe_nan_p (arg))
-       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
+  if (TREE_CODE (inner) == CALL_EXPR
+      && (fndecl = get_callee_fndecl (inner))
+      && (fndecl_built_in_p (fndecl, BUILT_IN_EXPECT)
+         || fndecl_built_in_p (fndecl, BUILT_IN_EXPECT_WITH_PROBABILITY)))
+    return arg0;
 
-      {
-       bool is_ibm_extended = MODE_COMPOSITE_P (TYPE_MODE (TREE_TYPE (arg)));
-       if (is_ibm_extended)
-         {
-           /* NaN and Inf are encoded in the high-order double value
-              only.  The low-order value is not significant.  */
-           arg = fold_build1_loc (loc, NOP_EXPR, double_type_node, arg);
-         }
-      }
-      arg = builtin_save_expr (arg);
-      return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
+  inner = inner_arg0;
+  code = TREE_CODE (inner);
+  if (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
+    {
+      tree op0 = TREE_OPERAND (inner, 0);
+      tree op1 = TREE_OPERAND (inner, 1);
+      arg1 = save_expr (arg1);
 
-    default:
-      gcc_unreachable ();
+      op0 = build_builtin_expect_predicate (loc, op0, arg1, arg2, arg3);
+      op1 = build_builtin_expect_predicate (loc, op1, arg1, arg2, arg3);
+      inner = build2 (code, TREE_TYPE (inner), op0, op1);
+
+      return fold_convert_loc (loc, TREE_TYPE (arg0), inner);
+    }
+
+  /* If the argument isn't invariant then there's nothing else we can do.  */
+  if (!TREE_CONSTANT (inner_arg0))
+    return NULL_TREE;
+
+  /* If we expect that a comparison against the argument will fold to
+     a constant return the constant.  In practice, this means a true
+     constant or the address of a non-weak symbol.  */
+  inner = inner_arg0;
+  STRIP_NOPS (inner);
+  if (TREE_CODE (inner) == ADDR_EXPR)
+    {
+      do
+       {
+         inner = TREE_OPERAND (inner, 0);
+       }
+      while (TREE_CODE (inner) == COMPONENT_REF
+            || TREE_CODE (inner) == ARRAY_REF);
+      if (VAR_OR_FUNCTION_DECL_P (inner) && DECL_WEAK (inner))
+       return NULL_TREE;
     }
+
+  /* Otherwise, ARG0 already has the proper type for the return value.  */
+  return arg0;
 }
 
-/* Fold a call to __builtin_fpclassify(int, int, int, int, int, ...).
-   This builtin will generate code to return the appropriate floating
-   point classification depending on the value of the floating point
-   number passed in.  The possible return values must be supplied as
-   int arguments to the call in the following order: FP_NAN, FP_INFINITE,
-   FP_NORMAL, FP_SUBNORMAL and FP_ZERO.  The ellipses is for exactly
-   one floating point argument which is "type generic".  */
+/* Fold a call to __builtin_classify_type with argument ARG.  */
 
 static tree
-fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
+fold_builtin_classify_type (tree arg)
 {
-  tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero,
-    arg, type, res, tmp;
-  machine_mode mode;
-  REAL_VALUE_TYPE r;
-  char buf[128];
+  if (arg == 0)
+    return build_int_cst (integer_type_node, no_type_class);
 
-  /* Verify the required arguments in the original call.  */
-  if (nargs != 6
-      || !validate_arg (args[0], INTEGER_TYPE)
-      || !validate_arg (args[1], INTEGER_TYPE)
-      || !validate_arg (args[2], INTEGER_TYPE)
-      || !validate_arg (args[3], INTEGER_TYPE)
-      || !validate_arg (args[4], INTEGER_TYPE)
-      || !validate_arg (args[5], REAL_TYPE))
+  return build_int_cst (integer_type_node, type_to_class (TREE_TYPE (arg)));
+}
+
+/* Fold a call EXPR (which may be null) to __builtin_strlen with argument
+   ARG.  */
+
+static tree
+fold_builtin_strlen (location_t loc, tree expr, tree type, tree arg)
+{
+  if (!validate_arg (arg, POINTER_TYPE))
     return NULL_TREE;
+  else
+    {
+      c_strlen_data lendata = { };
+      tree len = c_strlen (arg, 0, &lendata);
 
-  fp_nan = args[0];
-  fp_infinite = args[1];
-  fp_normal = args[2];
-  fp_subnormal = args[3];
-  fp_zero = args[4];
-  arg = args[5];
-  type = TREE_TYPE (arg);
-  mode = TYPE_MODE (type);
-  arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
+      if (len)
+       return fold_convert_loc (loc, type, len);
+
+      if (!lendata.decl)
+       c_strlen (arg, 1, &lendata);
+
+      if (lendata.decl)
+       {
+         if (EXPR_HAS_LOCATION (arg))
+           loc = EXPR_LOCATION (arg);
+         else if (loc == UNKNOWN_LOCATION)
+           loc = input_location;
+         warn_string_no_nul (loc, expr, "strlen", arg, lendata.decl);
+       }
+
+      return NULL_TREE;
+    }
+}
+
+/* Fold a call to __builtin_inf or __builtin_huge_val.  */
+
+static tree
+fold_builtin_inf (location_t loc, tree type, int warn)
+{
+  REAL_VALUE_TYPE real;
+
+  /* __builtin_inff is intended to be usable to define INFINITY on all
+     targets.  If an infinity is not available, INFINITY expands "to a
+     positive constant of type float that overflows at translation
+     time", footnote "In this case, using INFINITY will violate the
+     constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4).
+     Thus we pedwarn to ensure this constraint violation is
+     diagnosed.  */
+  if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
+    pedwarn (loc, 0, "target format does not support infinity");
 
-  /* fpclassify(x) ->
-       isnan(x) ? FP_NAN :
-         (fabs(x) == Inf ? FP_INFINITE :
-          (fabs(x) >= DBL_MIN ? FP_NORMAL :
-            (x == 0 ? FP_ZERO : FP_SUBNORMAL))).  */
+  real_inf (&real);
+  return build_real (type, real);
+}
 
-  tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
-                    build_real (type, dconst0));
-  res = fold_build3_loc (loc, COND_EXPR, integer_type_node,
-                    tmp, fp_zero, fp_subnormal);
+/* Fold function call to builtin sincos, sincosf, or sincosl.  Return
+   NULL_TREE if no simplification can be made.  */
 
-  sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
-  real_from_string (&r, buf);
-  tmp = fold_build2_loc (loc, GE_EXPR, integer_type_node,
-                    arg, build_real (type, r));
-  res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
+static tree
+fold_builtin_sincos (location_t loc,
+                    tree arg0, tree arg1, tree arg2)
+{
+  tree type;
+  tree fndecl, call = NULL_TREE;
 
-  if (tree_expr_maybe_infinite_p (arg))
+  if (!validate_arg (arg0, REAL_TYPE)
+      || !validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE))
+    return NULL_TREE;
+
+  type = TREE_TYPE (arg0);
+
+  /* Calculate the result when the argument is a constant.  */
+  built_in_function fn = mathfn_built_in_2 (type, CFN_BUILT_IN_CEXPI);
+  if (fn == END_BUILTINS)
+    return NULL_TREE;
+
+  /* Canonicalize sincos to cexpi.  */
+  if (TREE_CODE (arg0) == REAL_CST)
     {
-      real_inf (&r);
-      tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
-                        build_real (type, r));
-      res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp,
-                        fp_infinite, res);
+      tree complex_type = build_complex_type (type);
+      call = fold_const_call (as_combined_fn (fn), complex_type, arg0);
     }
-
-  if (tree_expr_maybe_nan_p (arg))
+  if (!call)
     {
-      tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
-      res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
+      if (!targetm.libc_has_function (function_c99_math_complex, type)
+         || !builtin_decl_implicit_p (fn))
+       return NULL_TREE;
+      fndecl = builtin_decl_explicit (fn);
+      call = build_call_expr_loc (loc, fndecl, 1, arg0);
+      call = builtin_save_expr (call);
     }
 
-  return res;
+  tree ptype = build_pointer_type (type);
+  arg1 = fold_convert (ptype, arg1);
+  arg2 = fold_convert (ptype, arg2);
+  return build2 (COMPOUND_EXPR, void_type_node,
+                build2 (MODIFY_EXPR, void_type_node,
+                        build_fold_indirect_ref_loc (loc, arg1),
+                        fold_build1_loc (loc, IMAGPART_EXPR, type, call)),
+                build2 (MODIFY_EXPR, void_type_node,
+                        build_fold_indirect_ref_loc (loc, arg2),
+                        fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
-/* Fold a call to an unordered comparison function such as
-   __builtin_isgreater().  FNDECL is the FUNCTION_DECL for the function
-   being called and ARG0 and ARG1 are the arguments for the call.
-   UNORDERED_CODE and ORDERED_CODE are comparison codes that give
-   the opposite of the desired result.  UNORDERED_CODE is used
-   for modes that can hold NaNs and ORDERED_CODE is used for
-   the rest.  */
+/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
+   Return NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
-                           enum tree_code unordered_code,
-                           enum tree_code ordered_code)
+fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
 {
-  tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  enum tree_code code;
-  tree type0, type1;
-  enum tree_code code0, code1;
-  tree cmp_type = NULL_TREE;
-
-  type0 = TREE_TYPE (arg0);
-  type1 = TREE_TYPE (arg1);
-
-  code0 = TREE_CODE (type0);
-  code1 = TREE_CODE (type1);
+  if (!validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
 
-  if (code0 == REAL_TYPE && code1 == REAL_TYPE)
-    /* Choose the wider of two real types.  */
-    cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
-      ? type0 : type1;
-  else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
-    cmp_type = type0;
-  else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
-    cmp_type = type1;
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
+                             arg1, arg2);
 
-  arg0 = fold_convert_loc (loc, cmp_type, arg0);
-  arg1 = fold_convert_loc (loc, cmp_type, arg1);
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
 
-  if (unordered_code == UNORDERED_EXPR)
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
+  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
     {
-      if (tree_expr_nan_p (arg0) || tree_expr_nan_p (arg1))
-       return omit_two_operands_loc (loc, type, integer_one_node, arg0, arg1);
-      if (!tree_expr_maybe_nan_p (arg0) && !tree_expr_maybe_nan_p (arg1))
-       return omit_two_operands_loc (loc, type, integer_zero_node, arg0, arg1);
-      return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1);
+      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 ind1
+       = fold_convert_loc (loc, integer_type_node,
+                           build1 (INDIRECT_REF, cst_uchar_node,
+                                   fold_convert_loc (loc,
+                                                     cst_uchar_ptr_node,
+                                                     arg1)));
+      tree ind2
+       = fold_convert_loc (loc, integer_type_node,
+                           build1 (INDIRECT_REF, cst_uchar_node,
+                                   fold_convert_loc (loc,
+                                                     cst_uchar_ptr_node,
+                                                     arg2)));
+      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
     }
 
-  code = (tree_expr_maybe_nan_p (arg0) || tree_expr_maybe_nan_p (arg1))
-        ? unordered_code : ordered_code;
-  return fold_build1_loc (loc, TRUTH_NOT_EXPR, type,
-                     fold_build2_loc (loc, code, type, arg0, arg1));
+  return NULL_TREE;
 }
 
-/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
-   arithmetics if it can never overflow, or into internal functions that
-   return both result of arithmetics and overflowed boolean flag in
-   a complex integer result, or some other check for overflow.
-   Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
-   checking part of that.  */
+/* Fold a call to builtin isascii with argument ARG.  */
 
 static tree
-fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
-                            tree arg0, tree arg1, tree arg2)
+fold_builtin_isascii (location_t loc, tree arg)
 {
-  enum internal_fn ifn = IFN_LAST;
-  /* The code of the expression corresponding to the built-in.  */
-  enum tree_code opcode = ERROR_MARK;
-  bool ovf_only = false;
-
-  switch (fcode)
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
+  else
     {
-    case BUILT_IN_ADD_OVERFLOW_P:
-      ovf_only = true;
-      /* FALLTHRU */
-    case BUILT_IN_ADD_OVERFLOW:
-    case BUILT_IN_SADD_OVERFLOW:
-    case BUILT_IN_SADDL_OVERFLOW:
-    case BUILT_IN_SADDLL_OVERFLOW:
-    case BUILT_IN_UADD_OVERFLOW:
-    case BUILT_IN_UADDL_OVERFLOW:
-    case BUILT_IN_UADDLL_OVERFLOW:
-      opcode = PLUS_EXPR;
-      ifn = IFN_ADD_OVERFLOW;
-      break;
-    case BUILT_IN_SUB_OVERFLOW_P:
-      ovf_only = true;
-      /* FALLTHRU */
-    case BUILT_IN_SUB_OVERFLOW:
-    case BUILT_IN_SSUB_OVERFLOW:
-    case BUILT_IN_SSUBL_OVERFLOW:
-    case BUILT_IN_SSUBLL_OVERFLOW:
-    case BUILT_IN_USUB_OVERFLOW:
-    case BUILT_IN_USUBL_OVERFLOW:
-    case BUILT_IN_USUBLL_OVERFLOW:
-      opcode = MINUS_EXPR;
-      ifn = IFN_SUB_OVERFLOW;
-      break;
-    case BUILT_IN_MUL_OVERFLOW_P:
-      ovf_only = true;
-      /* FALLTHRU */
-    case BUILT_IN_MUL_OVERFLOW:
-    case BUILT_IN_SMUL_OVERFLOW:
-    case BUILT_IN_SMULL_OVERFLOW:
-    case BUILT_IN_SMULLL_OVERFLOW:
-    case BUILT_IN_UMUL_OVERFLOW:
-    case BUILT_IN_UMULL_OVERFLOW:
-    case BUILT_IN_UMULLL_OVERFLOW:
-      opcode = MULT_EXPR;
-      ifn = IFN_MUL_OVERFLOW;
-      break;
-    default:
-      gcc_unreachable ();
+      /* Transform isascii(c) -> ((c & ~0x7f) == 0).  */
+      arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
+                        build_int_cst (integer_type_node,
+                                       ~ (unsigned HOST_WIDE_INT) 0x7f));
+      return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
+                             arg, integer_zero_node);
     }
+}
 
-  /* For the "generic" overloads, the first two arguments can have different
-     types and the last argument determines the target type to use to check
-     for overflow.  The arguments of the other overloads all have the same
-     type.  */
-  tree type = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
+/* Fold a call to builtin toascii with argument ARG.  */
 
-  /* For the __builtin_{add,sub,mul}_overflow_p builtins, when the first two
-     arguments are constant, attempt to fold the built-in call into a constant
-     expression indicating whether or not it detected an overflow.  */
-  if (ovf_only
-      && TREE_CODE (arg0) == INTEGER_CST
-      && TREE_CODE (arg1) == INTEGER_CST)
-    /* Perform the computation in the target type and check for overflow.  */
-    return omit_one_operand_loc (loc, boolean_type_node,
-                                arith_overflowed_p (opcode, type, arg0, arg1)
-                                ? boolean_true_node : boolean_false_node,
-                                arg2);
+static tree
+fold_builtin_toascii (location_t loc, tree arg)
+{
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
 
-  tree intres, ovfres;
-  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
-    {
-      intres = fold_binary_loc (loc, opcode, type,
-                               fold_convert_loc (loc, type, arg0),
-                               fold_convert_loc (loc, type, arg1));
-      if (TREE_OVERFLOW (intres))
-       intres = drop_tree_overflow (intres);
-      ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
-               ? boolean_true_node : boolean_false_node);
-    }
+  /* Transform toascii(c) -> (c & 0x7f).  */
+  return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
+                         build_int_cst (integer_type_node, 0x7f));
+}
+
+/* Fold a call to builtin isdigit with argument ARG.  */
+
+static tree
+fold_builtin_isdigit (location_t loc, tree arg)
+{
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
   else
     {
-      tree ctype = build_complex_type (type);
-      tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
-                                               arg0, arg1);
-      tree tgt = save_expr (call);
-      intres = build1_loc (loc, REALPART_EXPR, type, tgt);
-      ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
-      ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
-    }
+      /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
+      /* According to the C standard, isdigit is unaffected by locale.
+        However, it definitely is affected by the target character set.  */
+      unsigned HOST_WIDE_INT target_digit0
+       = lang_hooks.to_target_charset ('0');
 
-  if (ovf_only)
-    return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
+      if (target_digit0 == 0)
+       return NULL_TREE;
 
-  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
-  tree store
-    = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
-  return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
+      arg = fold_convert_loc (loc, unsigned_type_node, arg);
+      arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
+                        build_int_cst (unsigned_type_node, target_digit0));
+      return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
+                         build_int_cst (unsigned_type_node, 9));
+    }
 }
 
-/* Fold a call to __builtin_FILE to a constant string.  */
+/* Fold a call to fabs, fabsf or fabsl with argument ARG.  */
 
-static inline tree
-fold_builtin_FILE (location_t loc)
+static tree
+fold_builtin_fabs (location_t loc, tree arg, tree type)
 {
-  if (const char *fname = LOCATION_FILE (loc))
-    {
-      /* The documentation says this builtin is equivalent to the preprocessor
-        __FILE__ macro so it appears appropriate to use the same file prefix
-        mappings.  */
-      fname = remap_macro_filename (fname);
-    return build_string_literal (strlen (fname) + 1, fname);
-    }
+  if (!validate_arg (arg, REAL_TYPE))
+    return NULL_TREE;
 
-  return build_string_literal (1, "");
+  arg = fold_convert_loc (loc, type, arg);
+  return fold_build1_loc (loc, ABS_EXPR, type, arg);
 }
 
-/* Fold a call to __builtin_FUNCTION to a constant string.  */
+/* Fold a call to abs, labs, llabs or imaxabs with argument ARG.  */
 
-static inline tree
-fold_builtin_FUNCTION ()
+static tree
+fold_builtin_abs (location_t loc, tree arg, tree type)
 {
-  const char *name = "";
-
-  if (current_function_decl)
-    name = lang_hooks.decl_printable_name (current_function_decl, 0);
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
 
-  return build_string_literal (strlen (name) + 1, name);
+  arg = fold_convert_loc (loc, type, arg);
+  return fold_build1_loc (loc, ABS_EXPR, type, arg);
 }
 
-/* Fold a call to __builtin_LINE to an integer constant.  */
+/* Fold a call to builtin carg(a+bi) -> atan2(b,a).  */
 
-static inline tree
-fold_builtin_LINE (location_t loc, tree type)
+static tree
+fold_builtin_carg (location_t loc, tree arg, tree type)
 {
-  return build_int_cst (type, LOCATION_LINE (loc));
+  if (validate_arg (arg, COMPLEX_TYPE)
+      && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE)
+    {
+      tree atan2_fn = mathfn_built_in (type, BUILT_IN_ATAN2);
+
+      if (atan2_fn)
+        {
+         tree new_arg = builtin_save_expr (arg);
+         tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
+         tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
+         return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
+       }
+    }
+
+  return NULL_TREE;
 }
 
-/* Fold a call to built-in function FNDECL with 0 arguments.
-   This function returns NULL_TREE if no simplification was possible.  */
+/* Fold a call to builtin frexp, we can assume the base is 2.  */
 
 static tree
-fold_builtin_0 (location_t loc, tree fndecl)
+fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
 {
-  tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-  switch (fcode)
-    {
-    case BUILT_IN_FILE:
-      return fold_builtin_FILE (loc);
+  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
+    return NULL_TREE;
 
-    case BUILT_IN_FUNCTION:
-      return fold_builtin_FUNCTION ();
+  STRIP_NOPS (arg0);
 
-    case BUILT_IN_LINE:
-      return fold_builtin_LINE (loc, type);
+  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
+    return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_INF):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_INF):
-    case BUILT_IN_INFD32:
-    case BUILT_IN_INFD64:
-    case BUILT_IN_INFD128:
-      return fold_builtin_inf (loc, type, true);
+  arg1 = build_fold_indirect_ref_loc (loc, arg1);
 
-    CASE_FLT_FN (BUILT_IN_HUGE_VAL):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_HUGE_VAL):
-      return fold_builtin_inf (loc, type, false);
+  /* Proceed if a valid pointer type was passed in.  */
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == integer_type_node)
+    {
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+      tree frac, exp;
 
-    case BUILT_IN_CLASSIFY_TYPE:
-      return fold_builtin_classify_type (NULL_TREE);
+      switch (value->cl)
+      {
+      case rvc_zero:
+       /* For +-0, return (*exp = 0, +-0).  */
+       exp = integer_zero_node;
+       frac = arg0;
+       break;
+      case rvc_nan:
+      case rvc_inf:
+       /* For +-NaN or +-Inf, *exp is unspecified, return arg0.  */
+       return omit_one_operand_loc (loc, rettype, arg0, arg1);
+      case rvc_normal:
+       {
+         /* Since the frexp function always expects base 2, and in
+            GCC normalized significands are already in the range
+            [0.5, 1.0), we have exactly what frexp wants.  */
+         REAL_VALUE_TYPE frac_rvt = *value;
+         SET_REAL_EXP (&frac_rvt, 0);
+         frac = build_real (rettype, frac_rvt);
+         exp = build_int_cst (integer_type_node, REAL_EXP (value));
+       }
+       break;
+      default:
+       gcc_unreachable ();
+      }
 
-    default:
-      break;
+      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+      arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1, exp);
+      TREE_SIDE_EFFECTS (arg1) = 1;
+      return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1, frac);
     }
+
   return NULL_TREE;
 }
 
-/* Fold a call to built-in function FNDECL with 1 argument, ARG0.
-   This function returns NULL_TREE if no simplification was possible.  */
+/* Fold a call to builtin modf.  */
 
 static tree
-fold_builtin_1 (location_t loc, tree expr, tree fndecl, tree arg0)
+fold_builtin_modf (location_t loc, tree arg0, tree arg1, tree rettype)
 {
-  tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  if (! validate_arg (arg0, REAL_TYPE) || ! validate_arg (arg1, POINTER_TYPE))
+    return NULL_TREE;
 
-  if (TREE_CODE (arg0) == ERROR_MARK)
+  STRIP_NOPS (arg0);
+
+  if (!(TREE_CODE (arg0) == REAL_CST && ! TREE_OVERFLOW (arg0)))
     return NULL_TREE;
 
-  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0))
-    return ret;
+  arg1 = build_fold_indirect_ref_loc (loc, arg1);
 
-  switch (fcode)
+  /* Proceed if a valid pointer type was passed in.  */
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (arg1)) == TYPE_MAIN_VARIANT (rettype))
     {
-    case BUILT_IN_CONSTANT_P:
-      {
-       tree val = fold_builtin_constant_p (arg0);
-
-       /* Gimplification will pull the CALL_EXPR for the builtin out of
-          an if condition.  When not optimizing, we'll not CSE it back.
-          To avoid link error types of regressions, return false now.  */
-       if (!val && !optimize)
-         val = integer_zero_node;
+      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg0);
+      REAL_VALUE_TYPE trunc, frac;
 
-       return val;
+      switch (value->cl)
+      {
+      case rvc_nan:
+      case rvc_zero:
+       /* For +-NaN or +-0, return (*arg1 = arg0, arg0).  */
+       trunc = frac = *value;
+       break;
+      case rvc_inf:
+       /* For +-Inf, return (*arg1 = arg0, +-0).  */
+       frac = dconst0;
+       frac.sign = value->sign;
+       trunc = *value;
+       break;
+      case rvc_normal:
+       /* Return (*arg1 = trunc(arg0), arg0-trunc(arg0)).  */
+       real_trunc (&trunc, VOIDmode, value);
+       real_arithmetic (&frac, MINUS_EXPR, value, &trunc);
+       /* If the original number was negative and already
+          integral, then the fractional part is -0.0.  */
+       if (value->sign && frac.cl == rvc_zero)
+         frac.sign = value->sign;
+       break;
       }
 
-    case BUILT_IN_CLASSIFY_TYPE:
-      return fold_builtin_classify_type (arg0);
-
-    case BUILT_IN_STRLEN:
-      return fold_builtin_strlen (loc, expr, type, arg0);
+      /* Create the COMPOUND_EXPR (*arg1 = trunc, frac). */
+      arg1 = fold_build2_loc (loc, MODIFY_EXPR, rettype, arg1,
+                         build_real (rettype, trunc));
+      TREE_SIDE_EFFECTS (arg1) = 1;
+      return fold_build2_loc (loc, COMPOUND_EXPR, rettype, arg1,
+                         build_real (rettype, frac));
+    }
 
-    CASE_FLT_FN (BUILT_IN_FABS):
-    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
-    case BUILT_IN_FABSD32:
-    case BUILT_IN_FABSD64:
-    case BUILT_IN_FABSD128:
-      return fold_builtin_fabs (loc, arg0, type);
+  return NULL_TREE;
+}
 
-    case BUILT_IN_ABS:
-    case BUILT_IN_LABS:
-    case BUILT_IN_LLABS:
-    case BUILT_IN_IMAXABS:
-      return fold_builtin_abs (loc, arg0, type);
+/* Given a location LOC, an interclass builtin function decl FNDECL
+   and its single argument ARG, return an folded expression computing
+   the same, or NULL_TREE if we either couldn't or didn't want to fold
+   (the latter happen if there's an RTL instruction available).  */
 
-    CASE_FLT_FN (BUILT_IN_CONJ):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return fold_build1_loc (loc, CONJ_EXPR, type, arg0);
-    break;
+static tree
+fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
+{
+  machine_mode mode;
 
-    CASE_FLT_FN (BUILT_IN_CREAL):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
-    break;
+  if (!validate_arg (arg, REAL_TYPE))
+    return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_CIMAG):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return non_lvalue_loc (loc, fold_build1_loc (loc, IMAGPART_EXPR, type, arg0));
-    break;
+  if (interclass_mathfn_icode (arg, fndecl) != CODE_FOR_nothing)
+    return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_CARG):
-      return fold_builtin_carg (loc, arg0, type);
+  mode = TYPE_MODE (TREE_TYPE (arg));
 
-    case BUILT_IN_ISASCII:
-      return fold_builtin_isascii (loc, arg0);
+  bool is_ibm_extended = MODE_COMPOSITE_P (mode);
 
-    case BUILT_IN_TOASCII:
-      return fold_builtin_toascii (loc, arg0);
+  /* If there is no optab, try generic code.  */
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+      tree result;
 
-    case BUILT_IN_ISDIGIT:
-      return fold_builtin_isdigit (loc, arg0);
+    CASE_FLT_FN (BUILT_IN_ISINF):
+      {
+       /* isinf(x) -> isgreater(fabs(x),DBL_MAX).  */
+       tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
+       tree type = TREE_TYPE (arg);
+       REAL_VALUE_TYPE r;
+       char buf[128];
 
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
+       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
+       real_from_string (&r, buf);
+       result = build_call_expr (isgr_fn, 2,
+                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
+                                 build_real (type, r));
+       return result;
+      }
     CASE_FLT_FN (BUILT_IN_FINITE):
-    case BUILT_IN_FINITED32:
-    case BUILT_IN_FINITED64:
-    case BUILT_IN_FINITED128:
     case BUILT_IN_ISFINITE:
       {
-       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISFINITE);
-       if (ret)
-         return ret;
-       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
-      }
+       /* isfinite(x) -> islessequal(fabs(x),DBL_MAX).  */
+       tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
+       tree type = TREE_TYPE (arg);
+       REAL_VALUE_TYPE r;
+       char buf[128];
 
-    CASE_FLT_FN (BUILT_IN_ISINF):
-    case BUILT_IN_ISINFD32:
-    case BUILT_IN_ISINFD64:
-    case BUILT_IN_ISINFD128:
-      {
-       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF);
-       if (ret)
-         return ret;
-       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
+       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
+       real_from_string (&r, buf);
+       result = build_call_expr (isle_fn, 2,
+                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
+                                 build_real (type, r));
+       /*result = fold_build2_loc (loc, UNGT_EXPR,
+                                 TREE_TYPE (TREE_TYPE (fndecl)),
+                                 fold_build1_loc (loc, ABS_EXPR, type, arg),
+                                 build_real (type, r));
+       result = fold_build1_loc (loc, TRUTH_NOT_EXPR,
+                                 TREE_TYPE (TREE_TYPE (fndecl)),
+                                 result);*/
+       return result;
       }
-
     case BUILT_IN_ISNORMAL:
-      return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
-
-    case BUILT_IN_ISINF_SIGN:
-      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF_SIGN);
+      {
+       /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
+          islessequal(fabs(x),DBL_MAX).  */
+       tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
+       tree type = TREE_TYPE (arg);
+       tree orig_arg, max_exp, min_exp;
+       machine_mode orig_mode = mode;
+       REAL_VALUE_TYPE rmax, rmin;
+       char buf[128];
 
-    CASE_FLT_FN (BUILT_IN_ISNAN):
-    case BUILT_IN_ISNAND32:
-    case BUILT_IN_ISNAND64:
-    case BUILT_IN_ISNAND128:
-      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN);
+       orig_arg = arg = builtin_save_expr (arg);
+       if (is_ibm_extended)
+         {
+           /* Use double to test the normal range of IBM extended
+              precision.  Emin for IBM extended precision is
+              different to emin for IEEE double, being 53 higher
+              since the low double exponent is at least 53 lower
+              than the high double exponent.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
+       arg = fold_build1_loc (loc, ABS_EXPR, type, arg);
 
-    case BUILT_IN_FREE:
-      if (integer_zerop (arg0))
-       return build_empty_stmt (loc);
-      break;
+       get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf), false);
+       real_from_string (&rmax, buf);
+       sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (orig_mode)->emin - 1);
+       real_from_string (&rmin, buf);
+       max_exp = build_real (type, rmax);
+       min_exp = build_real (type, rmin);
 
+       max_exp = build_call_expr (isle_fn, 2, arg, max_exp);
+       if (is_ibm_extended)
+         {
+           /* Testing the high end of the range is done just using
+              the high double, using the same test as isfinite().
+              For the subnormal end of the range we first test the
+              high double, then if its magnitude is equal to the
+              limit of 0x1p-969, we test whether the low double is
+              non-zero and opposite sign to the high double.  */
+           tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
+           tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
+           tree gt_min = build_call_expr (isgt_fn, 2, arg, min_exp);
+           tree eq_min = fold_build2 (EQ_EXPR, integer_type_node,
+                                      arg, min_exp);
+           tree as_complex = build1 (VIEW_CONVERT_EXPR,
+                                     complex_double_type_node, orig_arg);
+           tree hi_dbl = build1 (REALPART_EXPR, type, as_complex);
+           tree lo_dbl = build1 (IMAGPART_EXPR, type, as_complex);
+           tree zero = build_real (type, dconst0);
+           tree hilt = build_call_expr (islt_fn, 2, hi_dbl, zero);
+           tree lolt = build_call_expr (islt_fn, 2, lo_dbl, zero);
+           tree logt = build_call_expr (isgt_fn, 2, lo_dbl, zero);
+           tree ok_lo = fold_build1 (TRUTH_NOT_EXPR, integer_type_node,
+                                     fold_build3 (COND_EXPR,
+                                                  integer_type_node,
+                                                  hilt, logt, lolt));
+           eq_min = fold_build2 (TRUTH_ANDIF_EXPR, integer_type_node,
+                                 eq_min, ok_lo);
+           min_exp = fold_build2 (TRUTH_ORIF_EXPR, integer_type_node,
+                                  gt_min, eq_min);
+         }
+       else
+         {
+           tree const isge_fn
+             = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
+           min_exp = build_call_expr (isge_fn, 2, arg, min_exp);
+         }
+       result = fold_build2 (BIT_AND_EXPR, integer_type_node,
+                             max_exp, min_exp);
+       return result;
+      }
     default:
       break;
     }
 
   return NULL_TREE;
-
 }
 
-/* Folds a call EXPR (which may be null) to built-in function FNDECL
-   with 2 arguments, ARG0 and ARG1.  This function returns NULL_TREE
-   if no simplification was possible.  */
+/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
+   ARG is the argument for the call.  */
 
 static tree
-fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
+fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  if (TREE_CODE (arg0) == ERROR_MARK
-      || TREE_CODE (arg1) == ERROR_MARK)
+  if (!validate_arg (arg, REAL_TYPE))
     return NULL_TREE;
 
-  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0, arg1))
-    return ret;
-
-  switch (fcode)
+  switch (builtin_index)
     {
-    CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
-    CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, POINTER_TYPE))
-       return do_mpfr_lgamma_r (arg0, arg1, type);
-    break;
+    case BUILT_IN_ISINF:
+      if (tree_expr_infinite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_one_node, arg);
+      if (!tree_expr_maybe_infinite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
+      return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_FREXP):
-      return fold_builtin_frexp (loc, arg0, arg1, type);
+    case BUILT_IN_ISINF_SIGN:
+      {
+       /* isinf_sign(x) -> isinf(x) ? (signbit(x) ? -1 : 1) : 0 */
+       /* In a boolean context, GCC will fold the inner COND_EXPR to
+          1.  So e.g. "if (isinf_sign(x))" would be folded to just
+          "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */
+       tree signbit_fn = builtin_decl_explicit (BUILT_IN_SIGNBIT);
+       tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
+       tree tmp = NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_MODF):
-      return fold_builtin_modf (loc, arg0, arg1, type);
+       arg = builtin_save_expr (arg);
 
-    case BUILT_IN_STRSPN:
-      return fold_builtin_strspn (loc, expr, arg0, arg1);
+       if (signbit_fn && isinf_fn)
+         {
+           tree signbit_call = build_call_expr_loc (loc, signbit_fn, 1, arg);
+           tree isinf_call = build_call_expr_loc (loc, isinf_fn, 1, arg);
+
+           signbit_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
+                                       signbit_call, integer_zero_node);
+           isinf_call = fold_build2_loc (loc, NE_EXPR, integer_type_node,
+                                     isinf_call, integer_zero_node);
+
+           tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node, signbit_call,
+                              integer_minus_one_node, integer_one_node);
+           tmp = fold_build3_loc (loc, COND_EXPR, integer_type_node,
+                              isinf_call, tmp,
+                              integer_zero_node);
+         }
+
+       return tmp;
+      }
+
+    case BUILT_IN_ISFINITE:
+      if (tree_expr_finite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_one_node, arg);
+      if (tree_expr_nan_p (arg) || tree_expr_infinite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
+      return NULL_TREE;
+
+    case BUILT_IN_ISNAN:
+      if (tree_expr_nan_p (arg))
+       return omit_one_operand_loc (loc, type, integer_one_node, arg);
+      if (!tree_expr_maybe_nan_p (arg))
+       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
+
+      {
+       bool is_ibm_extended = MODE_COMPOSITE_P (TYPE_MODE (TREE_TYPE (arg)));
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           arg = fold_build1_loc (loc, NOP_EXPR, double_type_node, arg);
+         }
+      }
+      arg = builtin_save_expr (arg);
+      return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
+
+    default:
+      gcc_unreachable ();
+    }
+}
 
-    case BUILT_IN_STRCSPN:
-      return fold_builtin_strcspn (loc, expr, arg0, arg1);
+/* Fold a call to __builtin_fpclassify(int, int, int, int, int, ...).
+   This builtin will generate code to return the appropriate floating
+   point classification depending on the value of the floating point
+   number passed in.  The possible return values must be supplied as
+   int arguments to the call in the following order: FP_NAN, FP_INFINITE,
+   FP_NORMAL, FP_SUBNORMAL and FP_ZERO.  The ellipses is for exactly
+   one floating point argument which is "type generic".  */
 
-    case BUILT_IN_STRPBRK:
-      return fold_builtin_strpbrk (loc, expr, arg0, arg1, type);
+static tree
+fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
+{
+  tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero,
+    arg, type, res, tmp;
+  machine_mode mode;
+  REAL_VALUE_TYPE r;
+  char buf[128];
 
-    case BUILT_IN_EXPECT:
-      return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, NULL_TREE);
+  /* Verify the required arguments in the original call.  */
+  if (nargs != 6
+      || !validate_arg (args[0], INTEGER_TYPE)
+      || !validate_arg (args[1], INTEGER_TYPE)
+      || !validate_arg (args[2], INTEGER_TYPE)
+      || !validate_arg (args[3], INTEGER_TYPE)
+      || !validate_arg (args[4], INTEGER_TYPE)
+      || !validate_arg (args[5], REAL_TYPE))
+    return NULL_TREE;
 
-    case BUILT_IN_ISGREATER:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNLE_EXPR, LE_EXPR);
-    case BUILT_IN_ISGREATEREQUAL:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNLT_EXPR, LT_EXPR);
-    case BUILT_IN_ISLESS:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNGE_EXPR, GE_EXPR);
-    case BUILT_IN_ISLESSEQUAL:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNGT_EXPR, GT_EXPR);
-    case BUILT_IN_ISLESSGREATER:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNEQ_EXPR, EQ_EXPR);
-    case BUILT_IN_ISUNORDERED:
-      return fold_builtin_unordered_cmp (loc, fndecl,
-                                        arg0, arg1, UNORDERED_EXPR,
-                                        NOP_EXPR);
+  fp_nan = args[0];
+  fp_infinite = args[1];
+  fp_normal = args[2];
+  fp_subnormal = args[3];
+  fp_zero = args[4];
+  arg = args[5];
+  type = TREE_TYPE (arg);
+  mode = TYPE_MODE (type);
+  arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
 
-      /* We do the folding for va_start in the expander.  */
-    case BUILT_IN_VA_START:
-      break;
+  /* fpclassify(x) ->
+       isnan(x) ? FP_NAN :
+         (fabs(x) == Inf ? FP_INFINITE :
+          (fabs(x) >= DBL_MIN ? FP_NORMAL :
+            (x == 0 ? FP_ZERO : FP_SUBNORMAL))).  */
 
-    case BUILT_IN_OBJECT_SIZE:
-      return fold_builtin_object_size (arg0, arg1);
+  tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
+                    build_real (type, dconst0));
+  res = fold_build3_loc (loc, COND_EXPR, integer_type_node,
+                    tmp, fp_zero, fp_subnormal);
 
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-      return fold_builtin_atomic_always_lock_free (arg0, arg1);
+  sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
+  real_from_string (&r, buf);
+  tmp = fold_build2_loc (loc, GE_EXPR, integer_type_node,
+                    arg, build_real (type, r));
+  res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
 
-    case BUILT_IN_ATOMIC_IS_LOCK_FREE:
-      return fold_builtin_atomic_is_lock_free (arg0, arg1);
+  if (tree_expr_maybe_infinite_p (arg))
+    {
+      real_inf (&r);
+      tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
+                        build_real (type, r));
+      res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp,
+                        fp_infinite, res);
+    }
 
-    default:
-      break;
+  if (tree_expr_maybe_nan_p (arg))
+    {
+      tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
+      res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
     }
-  return NULL_TREE;
+
+  return res;
 }
 
-/* Fold a call to built-in function FNDECL with 3 arguments, ARG0, ARG1,
-   and ARG2.
-   This function returns NULL_TREE if no simplification was possible.  */
+/* Fold a call to an unordered comparison function such as
+   __builtin_isgreater().  FNDECL is the FUNCTION_DECL for the function
+   being called and ARG0 and ARG1 are the arguments for the call.
+   UNORDERED_CODE and ORDERED_CODE are comparison codes that give
+   the opposite of the desired result.  UNORDERED_CODE is used
+   for modes that can hold NaNs and ORDERED_CODE is used for
+   the rest.  */
 
 static tree
-fold_builtin_3 (location_t loc, tree fndecl,
-               tree arg0, tree arg1, tree arg2)
+fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
+                           enum tree_code unordered_code,
+                           enum tree_code ordered_code)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  enum tree_code code;
+  tree type0, type1;
+  enum tree_code code0, code1;
+  tree cmp_type = NULL_TREE;
 
-  if (TREE_CODE (arg0) == ERROR_MARK
-      || TREE_CODE (arg1) == ERROR_MARK
-      || TREE_CODE (arg2) == ERROR_MARK)
-    return NULL_TREE;
+  type0 = TREE_TYPE (arg0);
+  type1 = TREE_TYPE (arg1);
 
-  if (tree ret = fold_const_call (as_combined_fn (fcode), type,
-                                 arg0, arg1, arg2))
-    return ret;
+  code0 = TREE_CODE (type0);
+  code1 = TREE_CODE (type1);
 
-  switch (fcode)
-    {
+  if (code0 == REAL_TYPE && code1 == REAL_TYPE)
+    /* Choose the wider of two real types.  */
+    cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
+      ? type0 : type1;
+  else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
+    cmp_type = type0;
+  else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
+    cmp_type = type1;
 
-    CASE_FLT_FN (BUILT_IN_SINCOS):
-      return fold_builtin_sincos (loc, arg0, arg1, arg2);
+  arg0 = fold_convert_loc (loc, cmp_type, arg0);
+  arg1 = fold_convert_loc (loc, cmp_type, arg1);
 
-    CASE_FLT_FN (BUILT_IN_REMQUO):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE)
-         && validate_arg (arg2, POINTER_TYPE))
-       return do_mpfr_remquo (arg0, arg1, arg2);
-    break;
+  if (unordered_code == UNORDERED_EXPR)
+    {
+      if (tree_expr_nan_p (arg0) || tree_expr_nan_p (arg1))
+       return omit_two_operands_loc (loc, type, integer_one_node, arg0, arg1);
+      if (!tree_expr_maybe_nan_p (arg0) && !tree_expr_maybe_nan_p (arg1))
+       return omit_two_operands_loc (loc, type, integer_zero_node, arg0, arg1);
+      return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1);
+    }
 
-    case BUILT_IN_MEMCMP:
-      return fold_builtin_memcmp (loc, arg0, arg1, arg2);
+  code = (tree_expr_maybe_nan_p (arg0) || tree_expr_maybe_nan_p (arg1))
+        ? unordered_code : ordered_code;
+  return fold_build1_loc (loc, TRUTH_NOT_EXPR, type,
+                     fold_build2_loc (loc, code, type, arg0, arg1));
+}
 
-    case BUILT_IN_EXPECT:
-      return fold_builtin_expect (loc, arg0, arg1, arg2, NULL_TREE);
+/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
+   arithmetics if it can never overflow, or into internal functions that
+   return both result of arithmetics and overflowed boolean flag in
+   a complex integer result, or some other check for overflow.
+   Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
+   checking part of that.  */
 
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-      return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, arg2);
+static tree
+fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
+                            tree arg0, tree arg1, tree arg2)
+{
+  enum internal_fn ifn = IFN_LAST;
+  /* The code of the expression corresponding to the built-in.  */
+  enum tree_code opcode = ERROR_MARK;
+  bool ovf_only = false;
 
-    case BUILT_IN_ADD_OVERFLOW:
-    case BUILT_IN_SUB_OVERFLOW:
-    case BUILT_IN_MUL_OVERFLOW:
+  switch (fcode)
+    {
     case BUILT_IN_ADD_OVERFLOW_P:
-    case BUILT_IN_SUB_OVERFLOW_P:
-    case BUILT_IN_MUL_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
+    case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
-    case BUILT_IN_SSUB_OVERFLOW:
-    case BUILT_IN_SSUBL_OVERFLOW:
-    case BUILT_IN_SSUBLL_OVERFLOW:
-    case BUILT_IN_SMUL_OVERFLOW:
-    case BUILT_IN_SMULL_OVERFLOW:
-    case BUILT_IN_SMULLL_OVERFLOW:
     case BUILT_IN_UADD_OVERFLOW:
     case BUILT_IN_UADDL_OVERFLOW:
     case BUILT_IN_UADDLL_OVERFLOW:
+      opcode = PLUS_EXPR;
+      ifn = IFN_ADD_OVERFLOW;
+      break;
+    case BUILT_IN_SUB_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
+    case BUILT_IN_SUB_OVERFLOW:
+    case BUILT_IN_SSUB_OVERFLOW:
+    case BUILT_IN_SSUBL_OVERFLOW:
+    case BUILT_IN_SSUBLL_OVERFLOW:
     case BUILT_IN_USUB_OVERFLOW:
     case BUILT_IN_USUBL_OVERFLOW:
     case BUILT_IN_USUBLL_OVERFLOW:
+      opcode = MINUS_EXPR;
+      ifn = IFN_SUB_OVERFLOW;
+      break;
+    case BUILT_IN_MUL_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
+    case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_SMUL_OVERFLOW:
+    case BUILT_IN_SMULL_OVERFLOW:
+    case BUILT_IN_SMULLL_OVERFLOW:
     case BUILT_IN_UMUL_OVERFLOW:
     case BUILT_IN_UMULL_OVERFLOW:
     case BUILT_IN_UMULLL_OVERFLOW:
-      return fold_builtin_arith_overflow (loc, fcode, arg0, arg1, arg2);
-
-    default:
+      opcode = MULT_EXPR;
+      ifn = IFN_MUL_OVERFLOW;
       break;
+    default:
+      gcc_unreachable ();
+    }
+
+  /* For the "generic" overloads, the first two arguments can have different
+     types and the last argument determines the target type to use to check
+     for overflow.  The arguments of the other overloads all have the same
+     type.  */
+  tree type = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
+
+  /* For the __builtin_{add,sub,mul}_overflow_p builtins, when the first two
+     arguments are constant, attempt to fold the built-in call into a constant
+     expression indicating whether or not it detected an overflow.  */
+  if (ovf_only
+      && TREE_CODE (arg0) == INTEGER_CST
+      && TREE_CODE (arg1) == INTEGER_CST)
+    /* Perform the computation in the target type and check for overflow.  */
+    return omit_one_operand_loc (loc, boolean_type_node,
+                                arith_overflowed_p (opcode, type, arg0, arg1)
+                                ? boolean_true_node : boolean_false_node,
+                                arg2);
+
+  tree intres, ovfres;
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      intres = fold_binary_loc (loc, opcode, type,
+                               fold_convert_loc (loc, type, arg0),
+                               fold_convert_loc (loc, type, arg1));
+      if (TREE_OVERFLOW (intres))
+       intres = drop_tree_overflow (intres);
+      ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
+               ? boolean_true_node : boolean_false_node);
     }
-  return NULL_TREE;
+  else
+    {
+      tree ctype = build_complex_type (type);
+      tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
+                                               arg0, arg1);
+      tree tgt = save_expr (call);
+      intres = build1_loc (loc, REALPART_EXPR, type, tgt);
+      ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
+      ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+    }
+
+  if (ovf_only)
+    return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
+
+  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
+  tree store
+    = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
+  return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
 }
 
-/* Folds a call EXPR (which may be null) to built-in function FNDECL.
-   ARGS is an array of NARGS arguments.  IGNORE is true if the result
-   of the function call is ignored.  This function returns NULL_TREE
-   if no simplification was possible.  */
+/* Fold a call to __builtin_FILE to a constant string.  */
 
-static tree
-fold_builtin_n (location_t loc, tree expr, tree fndecl, tree *args,
-               int nargs, bool)
+static inline tree
+fold_builtin_FILE (location_t loc)
 {
-  tree ret = NULL_TREE;
-
-  switch (nargs)
-    {
-    case 0:
-      ret = fold_builtin_0 (loc, fndecl);
-      break;
-    case 1:
-      ret = fold_builtin_1 (loc, expr, fndecl, args[0]);
-      break;
-    case 2:
-      ret = fold_builtin_2 (loc, expr, fndecl, args[0], args[1]);
-      break;
-    case 3:
-      ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]);
-      break;
-    default:
-      ret = fold_builtin_varargs (loc, fndecl, args, nargs);
-      break;
-    }
-  if (ret)
+  if (const char *fname = LOCATION_FILE (loc))
     {
-      ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
-      SET_EXPR_LOCATION (ret, loc);
-      return ret;
+      /* The documentation says this builtin is equivalent to the preprocessor
+        __FILE__ macro so it appears appropriate to use the same file prefix
+        mappings.  */
+      fname = remap_macro_filename (fname);
+    return build_string_literal (strlen (fname) + 1, fname);
     }
-  return NULL_TREE;
+
+  return build_string_literal (1, "");
 }
 
-/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
-   list ARGS along with N new arguments in NEWARGS.  SKIP is the number
-   of arguments in ARGS to be omitted.  OLDNARGS is the number of
-   elements in ARGS.  */
+/* Fold a call to __builtin_FUNCTION to a constant string.  */
 
-static tree
-rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args,
-                         int skip, tree fndecl, int n, va_list newargs)
+static inline tree
+fold_builtin_FUNCTION ()
 {
-  int nargs = oldnargs - skip + n;
-  tree *buffer;
-
-  if (n > 0)
-    {
-      int i, j;
+  const char *name = "";
 
-      buffer = XALLOCAVEC (tree, nargs);
-      for (i = 0; i < n; i++)
-       buffer[i] = va_arg (newargs, tree);
-      for (j = skip; j < oldnargs; j++, i++)
-       buffer[i] = args[j];
-    }
-  else
-    buffer = args + skip;
+  if (current_function_decl)
+    name = lang_hooks.decl_printable_name (current_function_decl, 0);
 
-  return build_call_expr_loc_array (loc, fndecl, nargs, buffer);
+  return build_string_literal (strlen (name) + 1, name);
 }
 
-/* Return true if FNDECL shouldn't be folded right now.
-   If a built-in function has an inline attribute always_inline
-   wrapper, defer folding it after always_inline functions have
-   been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking
-   might not be performed.  */
+/* Fold a call to __builtin_LINE to an integer constant.  */
 
-bool
-avoid_folding_inline_builtin (tree fndecl)
+static inline tree
+fold_builtin_LINE (location_t loc, tree type)
 {
-  return (DECL_DECLARED_INLINE_P (fndecl)
-         && DECL_DISREGARD_INLINE_LIMITS (fndecl)
-         && cfun
-         && !cfun->always_inline_functions_inlined
-         && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)));
+  return build_int_cst (type, LOCATION_LINE (loc));
 }
 
-/* A wrapper function for builtin folding that prevents warnings for
-   "statement without effect" and the like, caused by removing the
-   call node earlier than the warning is generated.  */
+/* Fold a call to built-in function FNDECL with 0 arguments.
+   This function returns NULL_TREE if no simplification was possible.  */
 
-tree
-fold_call_expr (location_t loc, tree exp, bool ignore)
+static tree
+fold_builtin_0 (location_t loc, tree fndecl)
 {
-  tree ret = NULL_TREE;
-  tree fndecl = get_callee_fndecl (exp);
-  if (fndecl && fndecl_built_in_p (fndecl)
-      /* If CALL_EXPR_VA_ARG_PACK is set, the arguments aren't finalized
-        yet.  Defer folding until we see all the arguments
-        (after inlining).  */
-      && !CALL_EXPR_VA_ARG_PACK (exp))
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  switch (fcode)
     {
-      int nargs = call_expr_nargs (exp);
+    case BUILT_IN_FILE:
+      return fold_builtin_FILE (loc);
 
-      /* Before gimplification CALL_EXPR_VA_ARG_PACK is not set, but
-        instead last argument is __builtin_va_arg_pack ().  Defer folding
-        even in that case, until arguments are finalized.  */
-      if (nargs && TREE_CODE (CALL_EXPR_ARG (exp, nargs - 1)) == CALL_EXPR)
-       {
-         tree fndecl2 = get_callee_fndecl (CALL_EXPR_ARG (exp, nargs - 1));
-         if (fndecl2 && fndecl_built_in_p (fndecl2, BUILT_IN_VA_ARG_PACK))
-           return NULL_TREE;
-       }
+    case BUILT_IN_FUNCTION:
+      return fold_builtin_FUNCTION ();
 
-      if (avoid_folding_inline_builtin (fndecl))
-       return NULL_TREE;
+    case BUILT_IN_LINE:
+      return fold_builtin_LINE (loc, type);
 
-      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-        return targetm.fold_builtin (fndecl, call_expr_nargs (exp),
-                                    CALL_EXPR_ARGP (exp), ignore);
-      else
-       {
-         tree *args = CALL_EXPR_ARGP (exp);
-         ret = fold_builtin_n (loc, exp, fndecl, args, nargs, ignore);
-         if (ret)
-           return ret;
-       }
-    }
-  return NULL_TREE;
-}
+    CASE_FLT_FN (BUILT_IN_INF):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_INF):
+    case BUILT_IN_INFD32:
+    case BUILT_IN_INFD64:
+    case BUILT_IN_INFD128:
+      return fold_builtin_inf (loc, type, true);
 
-/* Fold a CALL_EXPR with type TYPE with FN as the function expression.
-   N arguments are passed in the array ARGARRAY.  Return a folded
-   expression or NULL_TREE if no simplification was possible.  */
+    CASE_FLT_FN (BUILT_IN_HUGE_VAL):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_HUGE_VAL):
+      return fold_builtin_inf (loc, type, false);
 
-tree
-fold_builtin_call_array (location_t loc, tree,
-                        tree fn,
-                        int n,
-                        tree *argarray)
-{
-  if (TREE_CODE (fn) != ADDR_EXPR)
-    return NULL_TREE;
+    case BUILT_IN_CLASSIFY_TYPE:
+      return fold_builtin_classify_type (NULL_TREE);
 
-  tree fndecl = TREE_OPERAND (fn, 0);
-  if (TREE_CODE (fndecl) == FUNCTION_DECL
-      && fndecl_built_in_p (fndecl))
-    {
-      /* If last argument is __builtin_va_arg_pack (), arguments to this
-        function are not finalized yet.  Defer folding until they are.  */
-      if (n && TREE_CODE (argarray[n - 1]) == CALL_EXPR)
-       {
-         tree fndecl2 = get_callee_fndecl (argarray[n - 1]);
-         if (fndecl2 && fndecl_built_in_p (fndecl2, BUILT_IN_VA_ARG_PACK))
-           return NULL_TREE;
-       }
-      if (avoid_folding_inline_builtin (fndecl))
-       return NULL_TREE;
-      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-       return targetm.fold_builtin (fndecl, n, argarray, false);
-      else
-       return fold_builtin_n (loc, NULL_TREE, fndecl, argarray, n, false);
+    default:
+      break;
     }
-
   return NULL_TREE;
 }
 
-/* Construct a new CALL_EXPR using the tail of the argument list of EXP
-   along with N new arguments specified as the "..." parameters.  SKIP
-   is the number of arguments in EXP to be omitted.  This function is used
-   to do varargs-to-varargs transformations.  */
+/* Fold a call to built-in function FNDECL with 1 argument, ARG0.
+   This function returns NULL_TREE if no simplification was possible.  */
 
 static tree
-rewrite_call_expr (location_t loc, tree exp, int skip, tree fndecl, int n, ...)
+fold_builtin_1 (location_t loc, tree expr, tree fndecl, tree arg0)
 {
-  va_list ap;
-  tree t;
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  va_start (ap, n);
-  t = rewrite_call_expr_valist (loc, call_expr_nargs (exp),
-                               CALL_EXPR_ARGP (exp), skip, fndecl, n, ap);
-  va_end (ap);
+  if (TREE_CODE (arg0) == ERROR_MARK)
+    return NULL_TREE;
 
-  return t;
-}
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0))
+    return ret;
 
-/* Validate a single argument ARG against a tree code CODE representing
-   a type.  Return true when argument is valid.  */
+  switch (fcode)
+    {
+    case BUILT_IN_CONSTANT_P:
+      {
+       tree val = fold_builtin_constant_p (arg0);
 
-static bool
-validate_arg (const_tree arg, enum tree_code code)
-{
-  if (!arg)
-    return false;
-  else if (code == POINTER_TYPE)
-    return POINTER_TYPE_P (TREE_TYPE (arg));
-  else if (code == INTEGER_TYPE)
-    return INTEGRAL_TYPE_P (TREE_TYPE (arg));
-  return code == TREE_CODE (TREE_TYPE (arg));
-}
+       /* Gimplification will pull the CALL_EXPR for the builtin out of
+          an if condition.  When not optimizing, we'll not CSE it back.
+          To avoid link error types of regressions, return false now.  */
+       if (!val && !optimize)
+         val = integer_zero_node;
+
+       return val;
+      }
+
+    case BUILT_IN_CLASSIFY_TYPE:
+      return fold_builtin_classify_type (arg0);
+
+    case BUILT_IN_STRLEN:
+      return fold_builtin_strlen (loc, expr, type, arg0);
+
+    CASE_FLT_FN (BUILT_IN_FABS):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
+    case BUILT_IN_FABSD32:
+    case BUILT_IN_FABSD64:
+    case BUILT_IN_FABSD128:
+      return fold_builtin_fabs (loc, arg0, type);
 
-/* This function validates the types of a function call argument list
-   against a specified list of tree_codes.  If the last specifier is a 0,
-   that represents an ellipses, otherwise the last specifier must be a
-   VOID_TYPE.
+    case BUILT_IN_ABS:
+    case BUILT_IN_LABS:
+    case BUILT_IN_LLABS:
+    case BUILT_IN_IMAXABS:
+      return fold_builtin_abs (loc, arg0, type);
 
-   This is the GIMPLE version of validate_arglist.  Eventually we want to
-   completely convert builtins.c to work from GIMPLEs and the tree based
-   validate_arglist will then be removed.  */
+    CASE_FLT_FN (BUILT_IN_CONJ):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return fold_build1_loc (loc, CONJ_EXPR, type, arg0);
+    break;
 
-bool
-validate_gimple_arglist (const gcall *call, ...)
-{
-  enum tree_code code;
-  bool res = 0;
-  va_list ap;
-  const_tree arg;
-  size_t i;
+    CASE_FLT_FN (BUILT_IN_CREAL):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
+    break;
 
-  va_start (ap, call);
-  i = 0;
+    CASE_FLT_FN (BUILT_IN_CIMAG):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return non_lvalue_loc (loc, fold_build1_loc (loc, IMAGPART_EXPR, type, arg0));
+    break;
 
-  do
-    {
-      code = (enum tree_code) va_arg (ap, int);
-      switch (code)
-       {
-       case 0:
-         /* This signifies an ellipses, any further arguments are all ok.  */
-         res = true;
-         goto end;
-       case VOID_TYPE:
-         /* This signifies an endlink, if no arguments remain, return
-            true, otherwise return false.  */
-         res = (i == gimple_call_num_args (call));
-         goto end;
-       default:
-         /* If no parameters remain or the parameter's code does not
-            match the specified code, return false.  Otherwise continue
-            checking any remaining arguments.  */
-         arg = gimple_call_arg (call, i++);
-         if (!validate_arg (arg, code))
-           goto end;
-         break;
-       }
-    }
-  while (1);
+    CASE_FLT_FN (BUILT_IN_CARG):
+      return fold_builtin_carg (loc, arg0, type);
 
-  /* We need gotos here since we can only have one VA_CLOSE in a
-     function.  */
- end: ;
-  va_end (ap);
+    case BUILT_IN_ISASCII:
+      return fold_builtin_isascii (loc, arg0);
 
-  return res;
-}
+    case BUILT_IN_TOASCII:
+      return fold_builtin_toascii (loc, arg0);
 
-/* Default target-specific builtin expander that does nothing.  */
+    case BUILT_IN_ISDIGIT:
+      return fold_builtin_isdigit (loc, arg0);
 
-rtx
-default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
-                       rtx target ATTRIBUTE_UNUSED,
-                       rtx subtarget ATTRIBUTE_UNUSED,
-                       machine_mode mode ATTRIBUTE_UNUSED,
-                       int ignore ATTRIBUTE_UNUSED)
-{
-  return NULL_RTX;
-}
+    CASE_FLT_FN (BUILT_IN_FINITE):
+    case BUILT_IN_FINITED32:
+    case BUILT_IN_FINITED64:
+    case BUILT_IN_FINITED128:
+    case BUILT_IN_ISFINITE:
+      {
+       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISFINITE);
+       if (ret)
+         return ret;
+       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
+      }
 
-/* Returns true is EXP represents data that would potentially reside
-   in a readonly section.  */
+    CASE_FLT_FN (BUILT_IN_ISINF):
+    case BUILT_IN_ISINFD32:
+    case BUILT_IN_ISINFD64:
+    case BUILT_IN_ISINFD128:
+      {
+       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF);
+       if (ret)
+         return ret;
+       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
+      }
 
-bool
-readonly_data_expr (tree exp)
-{
-  STRIP_NOPS (exp);
+    case BUILT_IN_ISNORMAL:
+      return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
 
-  if (TREE_CODE (exp) != ADDR_EXPR)
-    return false;
+    case BUILT_IN_ISINF_SIGN:
+      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF_SIGN);
 
-  exp = get_base_address (TREE_OPERAND (exp, 0));
-  if (!exp)
-    return false;
+    CASE_FLT_FN (BUILT_IN_ISNAN):
+    case BUILT_IN_ISNAND32:
+    case BUILT_IN_ISNAND64:
+    case BUILT_IN_ISNAND128:
+      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN);
 
-  /* Make sure we call decl_readonly_section only for trees it
-     can handle (since it returns true for everything it doesn't
-     understand).  */
-  if (TREE_CODE (exp) == STRING_CST
-      || TREE_CODE (exp) == CONSTRUCTOR
-      || (VAR_P (exp) && TREE_STATIC (exp)))
-    return decl_readonly_section (exp, 0);
-  else
-    return false;
-}
+    case BUILT_IN_FREE:
+      if (integer_zerop (arg0))
+       return build_empty_stmt (loc);
+      break;
 
-/* Simplify a call to the strpbrk builtin.  S1 and S2 are the arguments
-   to the call, and TYPE is its return type.
+    default:
+      break;
+    }
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
+  return NULL_TREE;
 
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
+}
 
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
+/* Folds a call EXPR (which may be null) to built-in function FNDECL
+   with 2 arguments, ARG0 and ARG1.  This function returns NULL_TREE
+   if no simplification was possible.  */
 
 static tree
-fold_builtin_strpbrk (location_t loc, tree, tree s1, tree s2, tree type)
+fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
 {
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, POINTER_TYPE))
-    return NULL_TREE;
-
-  tree fn;
-  const char *p1, *p2;
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  p2 = c_getstr (s2);
-  if (p2 == NULL)
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK)
     return NULL_TREE;
 
-  p1 = c_getstr (s1);
-  if (p1 != NULL)
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0, arg1))
+    return ret;
+
+  switch (fcode)
     {
-      const char *r = strpbrk (p1, p2);
-      tree tem;
+    CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
+    CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
+      if (validate_arg (arg0, REAL_TYPE)
+         && validate_arg (arg1, POINTER_TYPE))
+       return do_mpfr_lgamma_r (arg0, arg1, type);
+    break;
 
-      if (r == NULL)
-       return build_int_cst (TREE_TYPE (s1), 0);
+    CASE_FLT_FN (BUILT_IN_FREXP):
+      return fold_builtin_frexp (loc, arg0, arg1, type);
 
-      /* Return an offset into the constant string argument.  */
-      tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
-      return fold_convert_loc (loc, type, tem);
-    }
+    CASE_FLT_FN (BUILT_IN_MODF):
+      return fold_builtin_modf (loc, arg0, arg1, type);
 
-  if (p2[0] == '\0')
-    /* strpbrk(x, "") == NULL.
-       Evaluate and ignore s1 in case it had side-effects.  */
-    return omit_one_operand_loc (loc, type, integer_zero_node, s1);
+    case BUILT_IN_STRSPN:
+      return fold_builtin_strspn (loc, expr, arg0, arg1);
 
-  if (p2[1] != '\0')
-    return NULL_TREE;  /* Really call strpbrk.  */
+    case BUILT_IN_STRCSPN:
+      return fold_builtin_strcspn (loc, expr, arg0, arg1);
 
-  fn = builtin_decl_implicit (BUILT_IN_STRCHR);
-  if (!fn)
-    return NULL_TREE;
+    case BUILT_IN_STRPBRK:
+      return fold_builtin_strpbrk (loc, expr, arg0, arg1, type);
 
-  /* New argument list transforming strpbrk(s1, s2) to
-     strchr(s1, s2[0]).  */
-  return build_call_expr_loc (loc, fn, 2, s1,
-                             build_int_cst (integer_type_node, p2[0]));
-}
+    case BUILT_IN_EXPECT:
+      return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, NULL_TREE);
+
+    case BUILT_IN_ISGREATER:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNLE_EXPR, LE_EXPR);
+    case BUILT_IN_ISGREATEREQUAL:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNLT_EXPR, LT_EXPR);
+    case BUILT_IN_ISLESS:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNGE_EXPR, GE_EXPR);
+    case BUILT_IN_ISLESSEQUAL:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNGT_EXPR, GT_EXPR);
+    case BUILT_IN_ISLESSGREATER:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNEQ_EXPR, EQ_EXPR);
+    case BUILT_IN_ISUNORDERED:
+      return fold_builtin_unordered_cmp (loc, fndecl,
+                                        arg0, arg1, UNORDERED_EXPR,
+                                        NOP_EXPR);
+
+      /* We do the folding for va_start in the expander.  */
+    case BUILT_IN_VA_START:
+      break;
+
+    case BUILT_IN_OBJECT_SIZE:
+      return fold_builtin_object_size (arg0, arg1);
 
-/* Simplify a call to the strspn builtin.  S1 and S2 are the arguments
-   to the call.
+    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+      return fold_builtin_atomic_always_lock_free (arg0, arg1);
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
+    case BUILT_IN_ATOMIC_IS_LOCK_FREE:
+      return fold_builtin_atomic_is_lock_free (arg0, arg1);
 
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
+    default:
+      break;
+    }
+  return NULL_TREE;
+}
 
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
+/* Fold a call to built-in function FNDECL with 3 arguments, ARG0, ARG1,
+   and ARG2.
+   This function returns NULL_TREE if no simplification was possible.  */
 
 static tree
-fold_builtin_strspn (location_t loc, tree expr, tree s1, tree s2)
+fold_builtin_3 (location_t loc, tree fndecl,
+               tree arg0, tree arg1, tree arg2)
 {
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, POINTER_TYPE))
-    return NULL_TREE;
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  if (!check_nul_terminated_array (expr, s1)
-      || !check_nul_terminated_array (expr, s2))
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK
+      || TREE_CODE (arg2) == ERROR_MARK)
     return NULL_TREE;
 
-  const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type,
+                                 arg0, arg1, arg2))
+    return ret;
 
-  /* If either argument is "", return NULL_TREE.  */
-  if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
-    /* Evaluate and ignore both arguments in case either one has
-       side-effects.  */
-    return omit_two_operands_loc (loc, size_type_node, size_zero_node,
-                                 s1, s2);
-  return NULL_TREE;
-}
+  switch (fcode)
+    {
 
-/* Simplify a call to the strcspn builtin.  S1 and S2 are the arguments
-   to the call.
+    CASE_FLT_FN (BUILT_IN_SINCOS):
+      return fold_builtin_sincos (loc, arg0, arg1, arg2);
 
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
+    CASE_FLT_FN (BUILT_IN_REMQUO):
+      if (validate_arg (arg0, REAL_TYPE)
+         && validate_arg (arg1, REAL_TYPE)
+         && validate_arg (arg2, POINTER_TYPE))
+       return do_mpfr_remquo (arg0, arg1, arg2);
+    break;
 
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
+    case BUILT_IN_MEMCMP:
+      return fold_builtin_memcmp (loc, arg0, arg1, arg2);
 
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
+    case BUILT_IN_EXPECT:
+      return fold_builtin_expect (loc, arg0, arg1, arg2, NULL_TREE);
 
-static tree
-fold_builtin_strcspn (location_t loc, tree expr, tree s1, tree s2)
-{
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, POINTER_TYPE))
-    return NULL_TREE;
+    case BUILT_IN_EXPECT_WITH_PROBABILITY:
+      return fold_builtin_expect (loc, arg0, arg1, NULL_TREE, arg2);
 
-  if (!check_nul_terminated_array (expr, s1)
-      || !check_nul_terminated_array (expr, s2))
-    return NULL_TREE;
+    case BUILT_IN_ADD_OVERFLOW:
+    case BUILT_IN_SUB_OVERFLOW:
+    case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+    case BUILT_IN_SADD_OVERFLOW:
+    case BUILT_IN_SADDL_OVERFLOW:
+    case BUILT_IN_SADDLL_OVERFLOW:
+    case BUILT_IN_SSUB_OVERFLOW:
+    case BUILT_IN_SSUBL_OVERFLOW:
+    case BUILT_IN_SSUBLL_OVERFLOW:
+    case BUILT_IN_SMUL_OVERFLOW:
+    case BUILT_IN_SMULL_OVERFLOW:
+    case BUILT_IN_SMULLL_OVERFLOW:
+    case BUILT_IN_UADD_OVERFLOW:
+    case BUILT_IN_UADDL_OVERFLOW:
+    case BUILT_IN_UADDLL_OVERFLOW:
+    case BUILT_IN_USUB_OVERFLOW:
+    case BUILT_IN_USUBL_OVERFLOW:
+    case BUILT_IN_USUBLL_OVERFLOW:
+    case BUILT_IN_UMUL_OVERFLOW:
+    case BUILT_IN_UMULL_OVERFLOW:
+    case BUILT_IN_UMULLL_OVERFLOW:
+      return fold_builtin_arith_overflow (loc, fcode, arg0, arg1, arg2);
 
-  /* If the first argument is "", return NULL_TREE.  */
-  const char *p1 = c_getstr (s1);
-  if (p1 && *p1 == '\0')
-    {
-      /* Evaluate and ignore argument s2 in case it has
-        side-effects.  */
-      return omit_one_operand_loc (loc, size_type_node,
-                                  size_zero_node, s2);
+    default:
+      break;
     }
+  return NULL_TREE;
+}
 
-  /* If the second argument is "", return __builtin_strlen(s1).  */
-  const char *p2 = c_getstr (s2);
-  if (p2 && *p2 == '\0')
-    {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+/* Folds a call EXPR (which may be null) to built-in function FNDECL.
+   ARGS is an array of NARGS arguments.  IGNORE is true if the result
+   of the function call is ignored.  This function returns NULL_TREE
+   if no simplification was possible.  */
 
-      /* If the replacement _DECL isn't initialized, don't do the
-        transformation.  */
-      if (!fn)
-       return NULL_TREE;
+static tree
+fold_builtin_n (location_t loc, tree expr, tree fndecl, tree *args,
+               int nargs, bool)
+{
+  tree ret = NULL_TREE;
 
-      return build_call_expr_loc (loc, fn, 1, s1);
+  switch (nargs)
+    {
+    case 0:
+      ret = fold_builtin_0 (loc, fndecl);
+      break;
+    case 1:
+      ret = fold_builtin_1 (loc, expr, fndecl, args[0]);
+      break;
+    case 2:
+      ret = fold_builtin_2 (loc, expr, fndecl, args[0], args[1]);
+      break;
+    case 3:
+      ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]);
+      break;
+    default:
+      ret = fold_builtin_varargs (loc, fndecl, args, nargs);
+      break;
+    }
+  if (ret)
+    {
+      ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
+      SET_EXPR_LOCATION (ret, loc);
+      return ret;
     }
   return NULL_TREE;
 }
 
-/* Fold the next_arg or va_start call EXP. Returns true if there was an error
-   produced.  False otherwise.  This is done so that we don't output the error
-   or warning twice or three times.  */
+/* Construct a new CALL_EXPR to FNDECL using the tail of the argument
+   list ARGS along with N new arguments in NEWARGS.  SKIP is the number
+   of arguments in ARGS to be omitted.  OLDNARGS is the number of
+   elements in ARGS.  */
 
-bool
-fold_builtin_next_arg (tree exp, bool va_start_p)
+static tree
+rewrite_call_expr_valist (location_t loc, int oldnargs, tree *args,
+                         int skip, tree fndecl, int n, va_list newargs)
 {
-  tree fntype = TREE_TYPE (current_function_decl);
-  int nargs = call_expr_nargs (exp);
-  tree arg;
-  /* There is good chance the current input_location points inside the
-     definition of the va_start macro (perhaps on the token for
-     builtin) in a system header, so warnings will not be emitted.
-     Use the location in real source code.  */
-  location_t current_location =
-    linemap_unwind_to_first_non_reserved_loc (line_table, input_location,
-                                             NULL);
+  int nargs = oldnargs - skip + n;
+  tree *buffer;
 
-  if (!stdarg_p (fntype))
+  if (n > 0)
     {
-      error ("%<va_start%> used in function with fixed arguments");
-      return true;
-    }
+      int i, j;
 
-  if (va_start_p)
-    {
-      if (va_start_p && (nargs != 2))
-       {
-         error ("wrong number of arguments to function %<va_start%>");
-         return true;
-       }
-      arg = CALL_EXPR_ARG (exp, 1);
+      buffer = XALLOCAVEC (tree, nargs);
+      for (i = 0; i < n; i++)
+       buffer[i] = va_arg (newargs, tree);
+      for (j = skip; j < oldnargs; j++, i++)
+       buffer[i] = args[j];
     }
-  /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
-     when we checked the arguments and if needed issued a warning.  */
   else
-    {
-      if (nargs == 0)
-       {
-         /* Evidently an out of date version of <stdarg.h>; can't validate
-            va_start's second argument, but can still work as intended.  */
-         warning_at (current_location,
-                     OPT_Wvarargs,
-                  "%<__builtin_next_arg%> called without an argument");
-         return true;
-       }
-      else if (nargs > 1)
-       {
-         error ("wrong number of arguments to function %<__builtin_next_arg%>");
-         return true;
-       }
-      arg = CALL_EXPR_ARG (exp, 0);
-    }
+    buffer = args + skip;
 
-  if (TREE_CODE (arg) == SSA_NAME
-      && SSA_NAME_VAR (arg))
-    arg = SSA_NAME_VAR (arg);
+  return build_call_expr_loc_array (loc, fndecl, nargs, buffer);
+}
+
+/* Return true if FNDECL shouldn't be folded right now.
+   If a built-in function has an inline attribute always_inline
+   wrapper, defer folding it after always_inline functions have
+   been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking
+   might not be performed.  */
+
+bool
+avoid_folding_inline_builtin (tree fndecl)
+{
+  return (DECL_DECLARED_INLINE_P (fndecl)
+         && DECL_DISREGARD_INLINE_LIMITS (fndecl)
+         && cfun
+         && !cfun->always_inline_functions_inlined
+         && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)));
+}
+
+/* A wrapper function for builtin folding that prevents warnings for
+   "statement without effect" and the like, caused by removing the
+   call node earlier than the warning is generated.  */
 
-  /* We destructively modify the call to be __builtin_va_start (ap, 0)
-     or __builtin_next_arg (0) the first time we see it, after checking
-     the arguments and if needed issuing a warning.  */
-  if (!integer_zerop (arg))
+tree
+fold_call_expr (location_t loc, tree exp, bool ignore)
+{
+  tree ret = NULL_TREE;
+  tree fndecl = get_callee_fndecl (exp);
+  if (fndecl && fndecl_built_in_p (fndecl)
+      /* If CALL_EXPR_VA_ARG_PACK is set, the arguments aren't finalized
+        yet.  Defer folding until we see all the arguments
+        (after inlining).  */
+      && !CALL_EXPR_VA_ARG_PACK (exp))
     {
-      tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
+      int nargs = call_expr_nargs (exp);
 
-      /* Strip off all nops for the sake of the comparison.  This
-        is not quite the same as STRIP_NOPS.  It does more.
-        We must also strip off INDIRECT_EXPR for C++ reference
-        parameters.  */
-      while (CONVERT_EXPR_P (arg)
-            || TREE_CODE (arg) == INDIRECT_REF)
-       arg = TREE_OPERAND (arg, 0);
-      if (arg != last_parm)
+      /* Before gimplification CALL_EXPR_VA_ARG_PACK is not set, but
+        instead last argument is __builtin_va_arg_pack ().  Defer folding
+        even in that case, until arguments are finalized.  */
+      if (nargs && TREE_CODE (CALL_EXPR_ARG (exp, nargs - 1)) == CALL_EXPR)
        {
-         /* FIXME: Sometimes with the tree optimizers we can get the
-            not the last argument even though the user used the last
-            argument.  We just warn and set the arg to be the last
-            argument so that we will get wrong-code because of
-            it.  */
-         warning_at (current_location,
-                     OPT_Wvarargs,
-                     "second parameter of %<va_start%> not last named argument");
+         tree fndecl2 = get_callee_fndecl (CALL_EXPR_ARG (exp, nargs - 1));
+         if (fndecl2 && fndecl_built_in_p (fndecl2, BUILT_IN_VA_ARG_PACK))
+           return NULL_TREE;
        }
 
-      /* Undefined by C99 7.15.1.4p4 (va_start):
-         "If the parameter parmN is declared with the register storage
-         class, with a function or array type, or with a type that is
-         not compatible with the type that results after application of
-         the default argument promotions, the behavior is undefined."
-      */
-      else if (DECL_REGISTER (arg))
-       {
-         warning_at (current_location,
-                     OPT_Wvarargs,
-                     "undefined behavior when second parameter of "
-                     "%<va_start%> is declared with %<register%> storage");
-       }
+      if (avoid_folding_inline_builtin (fndecl))
+       return NULL_TREE;
 
-      /* We want to verify the second parameter just once before the tree
-        optimizers are run and then avoid keeping it in the tree,
-        as otherwise we could warn even for correct code like:
-        void foo (int i, ...)
-        { va_list ap; i++; va_start (ap, i); va_end (ap); }  */
-      if (va_start_p)
-       CALL_EXPR_ARG (exp, 1) = integer_zero_node;
+      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+        return targetm.fold_builtin (fndecl, call_expr_nargs (exp),
+                                    CALL_EXPR_ARGP (exp), ignore);
       else
-       CALL_EXPR_ARG (exp, 0) = integer_zero_node;
+       {
+         tree *args = CALL_EXPR_ARGP (exp);
+         ret = fold_builtin_n (loc, exp, fndecl, args, nargs, ignore);
+         if (ret)
+           return ret;
+       }
     }
-  return false;
+  return NULL_TREE;
 }
 
+/* Fold a CALL_EXPR with type TYPE with FN as the function expression.
+   N arguments are passed in the array ARGARRAY.  Return a folded
+   expression or NULL_TREE if no simplification was possible.  */
 
-/* Expand a call EXP to __builtin_object_size.  */
-
-static rtx
-expand_builtin_object_size (tree exp)
+tree
+fold_builtin_call_array (location_t loc, tree,
+                        tree fn,
+                        int n,
+                        tree *argarray)
 {
-  tree ost;
-  int object_size_type;
-  tree fndecl = get_callee_fndecl (exp);
+  if (TREE_CODE (fn) != ADDR_EXPR)
+    return NULL_TREE;
 
-  if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+  tree fndecl = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fndecl) == FUNCTION_DECL
+      && fndecl_built_in_p (fndecl))
     {
-      error ("first argument of %qD must be a pointer, second integer constant",
-            fndecl);
-      expand_builtin_trap ();
-      return const0_rtx;
+      /* If last argument is __builtin_va_arg_pack (), arguments to this
+        function are not finalized yet.  Defer folding until they are.  */
+      if (n && TREE_CODE (argarray[n - 1]) == CALL_EXPR)
+       {
+         tree fndecl2 = get_callee_fndecl (argarray[n - 1]);
+         if (fndecl2 && fndecl_built_in_p (fndecl2, BUILT_IN_VA_ARG_PACK))
+           return NULL_TREE;
+       }
+      if (avoid_folding_inline_builtin (fndecl))
+       return NULL_TREE;
+      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+       return targetm.fold_builtin (fndecl, n, argarray, false);
+      else
+       return fold_builtin_n (loc, NULL_TREE, fndecl, argarray, n, false);
     }
 
-  ost = CALL_EXPR_ARG (exp, 1);
-  STRIP_NOPS (ost);
+  return NULL_TREE;
+}
 
-  if (TREE_CODE (ost) != INTEGER_CST
-      || tree_int_cst_sgn (ost) < 0
-      || compare_tree_int (ost, 3) > 0)
-    {
-      error ("last argument of %qD is not integer constant between 0 and 3",
-             fndecl);
-      expand_builtin_trap ();
-      return const0_rtx;
-    }
+/* Construct a new CALL_EXPR using the tail of the argument list of EXP
+   along with N new arguments specified as the "..." parameters.  SKIP
+   is the number of arguments in EXP to be omitted.  This function is used
+   to do varargs-to-varargs transformations.  */
 
-  object_size_type = tree_to_shwi (ost);
+static tree
+rewrite_call_expr (location_t loc, tree exp, int skip, tree fndecl, int n, ...)
+{
+  va_list ap;
+  tree t;
 
-  return object_size_type < 2 ? constm1_rtx : const0_rtx;
+  va_start (ap, n);
+  t = rewrite_call_expr_valist (loc, call_expr_nargs (exp),
+                               CALL_EXPR_ARGP (exp), skip, fndecl, n, ap);
+  va_end (ap);
+
+  return t;
 }
 
-/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
-   FCODE is the BUILT_IN_* to use.
-   Return NULL_RTX if we failed; the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient (and in
-   mode MODE if that's convenient).  */
+/* Validate a single argument ARG against a tree code CODE representing
+   a type.  Return true when argument is valid.  */
 
-static rtx
-expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
-                          enum built_in_function fcode)
+static bool
+validate_arg (const_tree arg, enum tree_code code)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE,
-                        fcode == BUILT_IN_MEMSET_CHK
-                        ? INTEGER_TYPE : POINTER_TYPE,
-                        INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
+  if (!arg)
+    return false;
+  else if (code == POINTER_TYPE)
+    return POINTER_TYPE_P (TREE_TYPE (arg));
+  else if (code == INTEGER_TYPE)
+    return INTEGRAL_TYPE_P (TREE_TYPE (arg));
+  return code == TREE_CODE (TREE_TYPE (arg));
+}
 
-  tree dest = CALL_EXPR_ARG (exp, 0);
-  tree src = CALL_EXPR_ARG (exp, 1);
-  tree len = CALL_EXPR_ARG (exp, 2);
-  tree size = CALL_EXPR_ARG (exp, 3);
+/* This function validates the types of a function call argument list
+   against a specified list of tree_codes.  If the last specifier is a 0,
+   that represents an ellipses, otherwise the last specifier must be a
+   VOID_TYPE.
 
-  /* FIXME: Set access mode to write only for memset et al.  */
-  bool sizes_ok = check_access (exp, len, /*maxread=*/NULL_TREE,
-                               /*srcstr=*/NULL_TREE, size, access_read_write);
+   This is the GIMPLE version of validate_arglist.  Eventually we want to
+   completely convert builtins.c to work from GIMPLEs and the tree based
+   validate_arglist will then be removed.  */
 
-  if (!tree_fits_uhwi_p (size))
-    return NULL_RTX;
+bool
+validate_gimple_arglist (const gcall *call, ...)
+{
+  enum tree_code code;
+  bool res = 0;
+  va_list ap;
+  const_tree arg;
+  size_t i;
 
-  if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
-    {
-      /* Avoid transforming the checking call to an ordinary one when
-        an overflow has been detected or when the call couldn't be
-        validated because the size is not constant.  */
-      if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
-       return NULL_RTX;
+  va_start (ap, call);
+  i = 0;
 
-      tree fn = NULL_TREE;
-      /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
-        mem{cpy,pcpy,move,set} is available.  */
-      switch (fcode)
+  do
+    {
+      code = (enum tree_code) va_arg (ap, int);
+      switch (code)
        {
-       case BUILT_IN_MEMCPY_CHK:
-         fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
-         break;
-       case BUILT_IN_MEMPCPY_CHK:
-         fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
-         break;
-       case BUILT_IN_MEMMOVE_CHK:
-         fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
-         break;
-       case BUILT_IN_MEMSET_CHK:
-         fn = builtin_decl_explicit (BUILT_IN_MEMSET);
-         break;
+       case 0:
+         /* This signifies an ellipses, any further arguments are all ok.  */
+         res = true;
+         goto end;
+       case VOID_TYPE:
+         /* This signifies an endlink, if no arguments remain, return
+            true, otherwise return false.  */
+         res = (i == gimple_call_num_args (call));
+         goto end;
        default:
+         /* If no parameters remain or the parameter's code does not
+            match the specified code, return false.  Otherwise continue
+            checking any remaining arguments.  */
+         arg = gimple_call_arg (call, i++);
+         if (!validate_arg (arg, code))
+           goto end;
          break;
        }
+    }
+  while (1);
 
-      if (! fn)
-       return NULL_RTX;
+  /* We need gotos here since we can only have one VA_CLOSE in a
+     function.  */
+ end: ;
+  va_end (ap);
 
-      fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 3, dest, src, len);
-      gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-      return expand_expr (fn, target, mode, EXPAND_NORMAL);
-    }
-  else if (fcode == BUILT_IN_MEMSET_CHK)
-    return NULL_RTX;
-  else
-    {
-      unsigned int dest_align = get_pointer_alignment (dest);
+  return res;
+}
 
-      /* If DEST is not a pointer type, call the normal function.  */
-      if (dest_align == 0)
-       return NULL_RTX;
+/* Default target-specific builtin expander that does nothing.  */
 
-      /* If SRC and DEST are the same (and not volatile), do nothing.  */
-      if (operand_equal_p (src, dest, 0))
-       {
-         tree expr;
+rtx
+default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
+                       rtx target ATTRIBUTE_UNUSED,
+                       rtx subtarget ATTRIBUTE_UNUSED,
+                       machine_mode mode ATTRIBUTE_UNUSED,
+                       int ignore ATTRIBUTE_UNUSED)
+{
+  return NULL_RTX;
+}
 
-         if (fcode != BUILT_IN_MEMPCPY_CHK)
-           {
-             /* Evaluate and ignore LEN in case it has side-effects.  */
-             expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
-             return expand_expr (dest, target, mode, EXPAND_NORMAL);
-           }
+/* Returns true is EXP represents data that would potentially reside
+   in a readonly section.  */
 
-         expr = fold_build_pointer_plus (dest, len);
-         return expand_expr (expr, target, mode, EXPAND_NORMAL);
-       }
+bool
+readonly_data_expr (tree exp)
+{
+  STRIP_NOPS (exp);
 
-      /* __memmove_chk special case.  */
-      if (fcode == BUILT_IN_MEMMOVE_CHK)
-       {
-         unsigned int src_align = get_pointer_alignment (src);
+  if (TREE_CODE (exp) != ADDR_EXPR)
+    return false;
 
-         if (src_align == 0)
-           return NULL_RTX;
+  exp = get_base_address (TREE_OPERAND (exp, 0));
+  if (!exp)
+    return false;
 
-         /* If src is categorized for a readonly section we can use
-            normal __memcpy_chk.  */
-         if (readonly_data_expr (src))
-           {
-             tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
-             if (!fn)
-               return NULL_RTX;
-             fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4,
-                                         dest, src, len, size);
-             gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-             CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-             return expand_expr (fn, target, mode, EXPAND_NORMAL);
-           }
-       }
-      return NULL_RTX;
-    }
+  /* Make sure we call decl_readonly_section only for trees it
+     can handle (since it returns true for everything it doesn't
+     understand).  */
+  if (TREE_CODE (exp) == STRING_CST
+      || TREE_CODE (exp) == CONSTRUCTOR
+      || (VAR_P (exp) && TREE_STATIC (exp)))
+    return decl_readonly_section (exp, 0);
+  else
+    return false;
 }
 
-/* Emit warning if a buffer overflow is detected at compile time.  */
+/* Simplify a call to the strpbrk builtin.  S1 and S2 are the arguments
+   to the call, and TYPE is its return type.
 
-static void
-maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
-{
-  /* The source string.  */
-  tree srcstr = NULL_TREE;
-  /* The size of the destination object returned by __builtin_object_size.  */
-  tree objsize = NULL_TREE;
-  /* The string that is being concatenated with (as in __strcat_chk)
-     or null if it isn't.  */
-  tree catstr = NULL_TREE;
-  /* The maximum length of the source sequence in a bounded operation
-     (such as __strncat_chk) or null if the operation isn't bounded
-     (such as __strcat_chk).  */
-  tree maxread = NULL_TREE;
-  /* The exact size of the access (such as in __strncpy_chk).  */
-  tree size = NULL_TREE;
-  /* The access by the function that's checked.  Except for snprintf
-     both writing and reading is checked.  */
-  access_mode mode = access_read_write;
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.
 
-  switch (fcode)
-    {
-    case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STPCPY_CHK:
-      srcstr = CALL_EXPR_ARG (exp, 1);
-      objsize = CALL_EXPR_ARG (exp, 2);
-      break;
+   The simplified form may be a constant or other expression which
+   computes the same value, but in a more efficient manner (including
+   calls to other builtin functions).
 
-    case BUILT_IN_STRCAT_CHK:
-      /* For __strcat_chk the warning will be emitted only if overflowing
-        by at least strlen (dest) + 1 bytes.  */
-      catstr = CALL_EXPR_ARG (exp, 0);
-      srcstr = CALL_EXPR_ARG (exp, 1);
-      objsize = CALL_EXPR_ARG (exp, 2);
-      break;
+   The call may contain arguments which need to be evaluated, but
+   which are not useful to determine the result of the call.  In
+   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
+   COMPOUND_EXPR will be an argument which must be evaluated.
+   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
+   COMPOUND_EXPR in the chain will contain the tree for the simplified
+   form of the builtin function call.  */
 
-    case BUILT_IN_STRNCAT_CHK:
-      catstr = CALL_EXPR_ARG (exp, 0);
-      srcstr = CALL_EXPR_ARG (exp, 1);
-      maxread = CALL_EXPR_ARG (exp, 2);
-      objsize = CALL_EXPR_ARG (exp, 3);
-      break;
+static tree
+fold_builtin_strpbrk (location_t loc, tree, tree s1, tree s2, tree type)
+{
+  if (!validate_arg (s1, POINTER_TYPE)
+      || !validate_arg (s2, POINTER_TYPE))
+    return NULL_TREE;
 
-    case BUILT_IN_STRNCPY_CHK:
-    case BUILT_IN_STPNCPY_CHK:
-      srcstr = CALL_EXPR_ARG (exp, 1);
-      size = CALL_EXPR_ARG (exp, 2);
-      objsize = CALL_EXPR_ARG (exp, 3);
-      break;
+  tree fn;
+  const char *p1, *p2;
 
-    case BUILT_IN_SNPRINTF_CHK:
-    case BUILT_IN_VSNPRINTF_CHK:
-      maxread = CALL_EXPR_ARG (exp, 1);
-      objsize = CALL_EXPR_ARG (exp, 3);
-      /* The only checked access the write to the destination.  */
-      mode = access_write_only;
-      break;
-    default:
-      gcc_unreachable ();
-    }
+  p2 = c_getstr (s2);
+  if (p2 == NULL)
+    return NULL_TREE;
 
-  if (catstr && maxread)
+  p1 = c_getstr (s1);
+  if (p1 != NULL)
     {
-      /* Check __strncat_chk.  There is no way to determine the length
-        of the string to which the source string is being appended so
-        just warn when the length of the source string is not known.  */
-      check_strncat_sizes (exp, objsize);
-      return;
+      const char *r = strpbrk (p1, p2);
+      tree tem;
+
+      if (r == NULL)
+       return build_int_cst (TREE_TYPE (s1), 0);
+
+      /* Return an offset into the constant string argument.  */
+      tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
+      return fold_convert_loc (loc, type, tem);
     }
 
-  check_access (exp, size, maxread, srcstr, objsize, mode);
-}
+  if (p2[0] == '\0')
+    /* strpbrk(x, "") == NULL.
+       Evaluate and ignore s1 in case it had side-effects.  */
+    return omit_one_operand_loc (loc, type, integer_zero_node, s1);
 
-/* Emit warning if a buffer overflow is detected at compile time
-   in __sprintf_chk/__vsprintf_chk calls.  */
+  if (p2[1] != '\0')
+    return NULL_TREE;  /* Really call strpbrk.  */
 
-static void
-maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
-{
-  tree size, len, fmt;
-  const char *fmt_str;
-  int nargs = call_expr_nargs (exp);
+  fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+  if (!fn)
+    return NULL_TREE;
 
-  /* Verify the required arguments in the original call.  */
+  /* New argument list transforming strpbrk(s1, s2) to
+     strchr(s1, s2[0]).  */
+  return build_call_expr_loc (loc, fn, 2, s1,
+                             build_int_cst (integer_type_node, p2[0]));
+}
 
-  if (nargs < 4)
-    return;
-  size = CALL_EXPR_ARG (exp, 2);
-  fmt = CALL_EXPR_ARG (exp, 3);
+/* Simplify a call to the strspn builtin.  S1 and S2 are the arguments
+   to the call.
 
-  if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
-    return;
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.
 
-  /* Check whether the format is a literal string constant.  */
-  fmt_str = c_getstr (fmt);
-  if (fmt_str == NULL)
-    return;
+   The simplified form may be a constant or other expression which
+   computes the same value, but in a more efficient manner (including
+   calls to other builtin functions).
 
-  if (!init_target_chars ())
-    return;
+   The call may contain arguments which need to be evaluated, but
+   which are not useful to determine the result of the call.  In
+   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
+   COMPOUND_EXPR will be an argument which must be evaluated.
+   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
+   COMPOUND_EXPR in the chain will contain the tree for the simplified
+   form of the builtin function call.  */
 
-  /* If the format doesn't contain % args or %%, we know its size.  */
-  if (strchr (fmt_str, target_percent) == 0)
-    len = build_int_cstu (size_type_node, strlen (fmt_str));
-  /* If the format is "%s" and first ... argument is a string literal,
-     we know it too.  */
-  else if (fcode == BUILT_IN_SPRINTF_CHK
-          && strcmp (fmt_str, target_percent_s) == 0)
-    {
-      tree arg;
+static tree
+fold_builtin_strspn (location_t loc, tree expr, tree s1, tree s2)
+{
+  if (!validate_arg (s1, POINTER_TYPE)
+      || !validate_arg (s2, POINTER_TYPE))
+    return NULL_TREE;
 
-      if (nargs < 5)
-       return;
-      arg = CALL_EXPR_ARG (exp, 4);
-      if (! POINTER_TYPE_P (TREE_TYPE (arg)))
-       return;
+  if (!check_nul_terminated_array (expr, s1)
+      || !check_nul_terminated_array (expr, s2))
+    return NULL_TREE;
 
-      len = c_strlen (arg, 1);
-      if (!len || ! tree_fits_uhwi_p (len))
-       return;
-    }
-  else
-    return;
+  const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+
+  /* If either argument is "", return NULL_TREE.  */
+  if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+    /* Evaluate and ignore both arguments in case either one has
+       side-effects.  */
+    return omit_two_operands_loc (loc, size_type_node, size_zero_node,
+                                 s1, s2);
+  return NULL_TREE;
+}
 
-  /* Add one for the terminating nul.  */
-  len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+/* Simplify a call to the strcspn builtin.  S1 and S2 are the arguments
+   to the call.
 
-  check_access (exp, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, len, size,
-               access_write_only);
-}
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.
 
-/* Return true if STMT is a call to an allocation function.  Unless
-   ALL_ALLOC is set, consider only functions that return dynmamically
-   allocated objects.  Otherwise return true even for all forms of
-   alloca (including VLA).  */
+   The simplified form may be a constant or other expression which
+   computes the same value, but in a more efficient manner (including
+   calls to other builtin functions).
 
-static bool
-fndecl_alloc_p (tree fndecl, bool all_alloc)
+   The call may contain arguments which need to be evaluated, but
+   which are not useful to determine the result of the call.  In
+   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
+   COMPOUND_EXPR will be an argument which must be evaluated.
+   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
+   COMPOUND_EXPR in the chain will contain the tree for the simplified
+   form of the builtin function call.  */
+
+static tree
+fold_builtin_strcspn (location_t loc, tree expr, tree s1, tree s2)
 {
-  if (!fndecl)
-    return false;
+  if (!validate_arg (s1, POINTER_TYPE)
+      || !validate_arg (s2, POINTER_TYPE))
+    return NULL_TREE;
 
-  /* A call to operator new isn't recognized as one to a built-in.  */
-  if (DECL_IS_OPERATOR_NEW_P (fndecl))
-    return true;
+  if (!check_nul_terminated_array (expr, s1)
+      || !check_nul_terminated_array (expr, s2))
+    return NULL_TREE;
 
-  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+  /* If the first argument is "", return NULL_TREE.  */
+  const char *p1 = c_getstr (s1);
+  if (p1 && *p1 == '\0')
     {
-      switch (DECL_FUNCTION_CODE (fndecl))
-       {
-       case BUILT_IN_ALLOCA:
-       case BUILT_IN_ALLOCA_WITH_ALIGN:
-         return all_alloc;
-       case BUILT_IN_ALIGNED_ALLOC:
-       case BUILT_IN_CALLOC:
-       case BUILT_IN_GOMP_ALLOC:
-       case BUILT_IN_MALLOC:
-       case BUILT_IN_REALLOC:
-       case BUILT_IN_STRDUP:
-       case BUILT_IN_STRNDUP:
-         return true;
-       default:
-         break;
-       }
+      /* Evaluate and ignore argument s2 in case it has
+        side-effects.  */
+      return omit_one_operand_loc (loc, size_type_node,
+                                  size_zero_node, s2);
     }
 
-  /* A function is considered an allocation function if it's declared
-     with attribute malloc with an argument naming its associated
-     deallocation function.  */
-  tree attrs = DECL_ATTRIBUTES (fndecl);
-  if (!attrs)
-    return false;
-
-  for (tree allocs = attrs;
-       (allocs = lookup_attribute ("malloc", allocs));
-       allocs = TREE_CHAIN (allocs))
+  /* If the second argument is "", return __builtin_strlen(s1).  */
+  const char *p2 = c_getstr (s2);
+  if (p2 && *p2 == '\0')
     {
-      tree args = TREE_VALUE (allocs);
-      if (!args)
-       continue;
-
-      if (TREE_VALUE (args))
-       return true;
-    }
-
-  return false;
-}
+      tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
 
-/* Return true if STMT is a call to an allocation function.  A wrapper
-   around fndecl_alloc_p.  */
+      /* If the replacement _DECL isn't initialized, don't do the
+        transformation.  */
+      if (!fn)
+       return NULL_TREE;
 
-static bool
-gimple_call_alloc_p (gimple *stmt, bool all_alloc = false)
-{
-  return fndecl_alloc_p (gimple_call_fndecl (stmt), all_alloc);
+      return build_call_expr_loc (loc, fn, 1, s1);
+    }
+  return NULL_TREE;
 }
 
-/* Return the zero-based number corresponding to the argument being
-   deallocated if STMT is a call to a deallocation function or UINT_MAX
-   if it isn't.  */
+/* Fold the next_arg or va_start call EXP. Returns true if there was an error
+   produced.  False otherwise.  This is done so that we don't output the error
+   or warning twice or three times.  */
 
-static unsigned
-call_dealloc_argno (tree exp)
+bool
+fold_builtin_next_arg (tree exp, bool va_start_p)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  if (!fndecl)
-    return UINT_MAX;
-
-  return fndecl_dealloc_argno (fndecl);
-}
-
-/* Return the zero-based number corresponding to the argument being
-   deallocated if FNDECL is a deallocation function or UINT_MAX
-   if it isn't.  */
+  tree fntype = TREE_TYPE (current_function_decl);
+  int nargs = call_expr_nargs (exp);
+  tree arg;
+  /* There is good chance the current input_location points inside the
+     definition of the va_start macro (perhaps on the token for
+     builtin) in a system header, so warnings will not be emitted.
+     Use the location in real source code.  */
+  location_t current_location =
+    linemap_unwind_to_first_non_reserved_loc (line_table, input_location,
+                                             NULL);
 
-unsigned
-fndecl_dealloc_argno (tree fndecl)
-{
-  /* A call to operator delete isn't recognized as one to a built-in.  */
-  if (DECL_IS_OPERATOR_DELETE_P (fndecl))
+  if (!stdarg_p (fntype))
     {
-      if (DECL_IS_REPLACEABLE_OPERATOR (fndecl))
-       return 0;
-
-      /* Avoid placement delete that's not been inlined.  */
-      tree fname = DECL_ASSEMBLER_NAME (fndecl);
-      if (id_equal (fname, "_ZdlPvS_")       // ordinary form
-         || id_equal (fname, "_ZdaPvS_"))   // array form
-       return UINT_MAX;
-      return 0;
+      error ("%<va_start%> used in function with fixed arguments");
+      return true;
     }
 
-  /* TODO: Handle user-defined functions with attribute malloc?  Handle
-     known non-built-ins like fopen?  */
-  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+  if (va_start_p)
     {
-      switch (DECL_FUNCTION_CODE (fndecl))
+      if (va_start_p && (nargs != 2))
        {
-       case BUILT_IN_FREE:
-       case BUILT_IN_REALLOC:
-         return 0;
-       default:
-         break;
+         error ("wrong number of arguments to function %<va_start%>");
+         return true;
        }
-      return UINT_MAX;
+      arg = CALL_EXPR_ARG (exp, 1);
     }
-
-  tree attrs = DECL_ATTRIBUTES (fndecl);
-  if (!attrs)
-    return UINT_MAX;
-
-  for (tree atfree = attrs;
-       (atfree = lookup_attribute ("*dealloc", atfree));
-       atfree = TREE_CHAIN (atfree))
+  /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
+     when we checked the arguments and if needed issued a warning.  */
+  else
     {
-      tree alloc = TREE_VALUE (atfree);
-      if (!alloc)
-       continue;
-
-      tree pos = TREE_CHAIN (alloc);
-      if (!pos)
-       return 0;
-
-      pos = TREE_VALUE (pos);
-      return TREE_INT_CST_LOW (pos) - 1;
+      if (nargs == 0)
+       {
+         /* Evidently an out of date version of <stdarg.h>; can't validate
+            va_start's second argument, but can still work as intended.  */
+         warning_at (current_location,
+                     OPT_Wvarargs,
+                  "%<__builtin_next_arg%> called without an argument");
+         return true;
+       }
+      else if (nargs > 1)
+       {
+         error ("wrong number of arguments to function %<__builtin_next_arg%>");
+         return true;
+       }
+      arg = CALL_EXPR_ARG (exp, 0);
     }
 
-  return UINT_MAX;
-}
-
-/* Return true if DELC doesn't refer to an operator delete that's
-   suitable to call with a pointer returned from the operator new
-   described by NEWC.  */
-
-static bool
-new_delete_mismatch_p (const demangle_component &newc,
-                      const demangle_component &delc)
-{
-  if (newc.type != delc.type)
-    return true;
+  if (TREE_CODE (arg) == SSA_NAME
+      && SSA_NAME_VAR (arg))
+    arg = SSA_NAME_VAR (arg);
 
-  switch (newc.type)
+  /* We destructively modify the call to be __builtin_va_start (ap, 0)
+     or __builtin_next_arg (0) the first time we see it, after checking
+     the arguments and if needed issuing a warning.  */
+  if (!integer_zerop (arg))
     {
-    case DEMANGLE_COMPONENT_NAME:
-      {
-       int len = newc.u.s_name.len;
-       const char *news = newc.u.s_name.s;
-       const char *dels = delc.u.s_name.s;
-       if (len != delc.u.s_name.len || memcmp (news, dels, len))
-         return true;
-
-       if (news[len] == 'n')
-         {
-           if (news[len + 1] == 'a')
-             return dels[len] != 'd' || dels[len + 1] != 'a';
-           if (news[len + 1] == 'w')
-             return dels[len] != 'd' || dels[len + 1] != 'l';
-         }
-       return false;
-      }
-
-    case DEMANGLE_COMPONENT_OPERATOR:
-      /* Operator mismatches are handled above.  */
-      return false;
-
-    case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
-      if (newc.u.s_extended_operator.args != delc.u.s_extended_operator.args)
-       return true;
-      return new_delete_mismatch_p (*newc.u.s_extended_operator.name,
-                                   *delc.u.s_extended_operator.name);
-
-    case DEMANGLE_COMPONENT_FIXED_TYPE:
-      if (newc.u.s_fixed.accum != delc.u.s_fixed.accum
-         || newc.u.s_fixed.sat != delc.u.s_fixed.sat)
-       return true;
-      return new_delete_mismatch_p (*newc.u.s_fixed.length,
-                                   *delc.u.s_fixed.length);
+      tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
 
-    case DEMANGLE_COMPONENT_CTOR:
-      if (newc.u.s_ctor.kind != delc.u.s_ctor.kind)
-       return true;
-      return new_delete_mismatch_p (*newc.u.s_ctor.name,
-                                   *delc.u.s_ctor.name);
+      /* Strip off all nops for the sake of the comparison.  This
+        is not quite the same as STRIP_NOPS.  It does more.
+        We must also strip off INDIRECT_EXPR for C++ reference
+        parameters.  */
+      while (CONVERT_EXPR_P (arg)
+            || TREE_CODE (arg) == INDIRECT_REF)
+       arg = TREE_OPERAND (arg, 0);
+      if (arg != last_parm)
+       {
+         /* FIXME: Sometimes with the tree optimizers we can get the
+            not the last argument even though the user used the last
+            argument.  We just warn and set the arg to be the last
+            argument so that we will get wrong-code because of
+            it.  */
+         warning_at (current_location,
+                     OPT_Wvarargs,
+                     "second parameter of %<va_start%> not last named argument");
+       }
 
-    case DEMANGLE_COMPONENT_DTOR:
-      if (newc.u.s_dtor.kind != delc.u.s_dtor.kind)
-       return true;
-      return new_delete_mismatch_p (*newc.u.s_dtor.name,
-                                   *delc.u.s_dtor.name);
+      /* Undefined by C99 7.15.1.4p4 (va_start):
+         "If the parameter parmN is declared with the register storage
+         class, with a function or array type, or with a type that is
+         not compatible with the type that results after application of
+         the default argument promotions, the behavior is undefined."
+      */
+      else if (DECL_REGISTER (arg))
+       {
+         warning_at (current_location,
+                     OPT_Wvarargs,
+                     "undefined behavior when second parameter of "
+                     "%<va_start%> is declared with %<register%> storage");
+       }
 
-    case DEMANGLE_COMPONENT_BUILTIN_TYPE:
-      {
-       /* The demangler API provides no better way to compare built-in
-          types except to by comparing their demangled names. */
-       size_t nsz, dsz;
-       demangle_component *pnc = const_cast<demangle_component *>(&newc);
-       demangle_component *pdc = const_cast<demangle_component *>(&delc);
-       char *nts = cplus_demangle_print (0, pnc, 16, &nsz);
-       char *dts = cplus_demangle_print (0, pdc, 16, &dsz);
-       if (!nts != !dts)
-         return true;
-       bool mismatch = strcmp (nts, dts);
-       free (nts);
-       free (dts);
-       return mismatch;
-      }
+      /* We want to verify the second parameter just once before the tree
+        optimizers are run and then avoid keeping it in the tree,
+        as otherwise we could warn even for correct code like:
+        void foo (int i, ...)
+        { va_list ap; i++; va_start (ap, i); va_end (ap); }  */
+      if (va_start_p)
+       CALL_EXPR_ARG (exp, 1) = integer_zero_node;
+      else
+       CALL_EXPR_ARG (exp, 0) = integer_zero_node;
+    }
+  return false;
+}
 
-    case DEMANGLE_COMPONENT_SUB_STD:
-      if (newc.u.s_string.len != delc.u.s_string.len)
-       return true;
-      return memcmp (newc.u.s_string.string, delc.u.s_string.string,
-                    newc.u.s_string.len);
 
-    case DEMANGLE_COMPONENT_FUNCTION_PARAM:
-    case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
-      return newc.u.s_number.number != delc.u.s_number.number;
+/* Expand a call EXP to __builtin_object_size.  */
 
-    case DEMANGLE_COMPONENT_CHARACTER:
-      return newc.u.s_character.character != delc.u.s_character.character;
+static rtx
+expand_builtin_object_size (tree exp)
+{
+  tree ost;
+  int object_size_type;
+  tree fndecl = get_callee_fndecl (exp);
 
-    case DEMANGLE_COMPONENT_DEFAULT_ARG:
-    case DEMANGLE_COMPONENT_LAMBDA:
-      if (newc.u.s_unary_num.num != delc.u.s_unary_num.num)
-       return true;
-      return new_delete_mismatch_p (*newc.u.s_unary_num.sub,
-                                   *delc.u.s_unary_num.sub);
-    default:
-      break;
+  if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    {
+      error ("first argument of %qD must be a pointer, second integer constant",
+            fndecl);
+      expand_builtin_trap ();
+      return const0_rtx;
     }
 
-  if (!newc.u.s_binary.left != !delc.u.s_binary.left)
-    return true;
+  ost = CALL_EXPR_ARG (exp, 1);
+  STRIP_NOPS (ost);
 
-  if (!newc.u.s_binary.left)
-    return false;
+  if (TREE_CODE (ost) != INTEGER_CST
+      || tree_int_cst_sgn (ost) < 0
+      || compare_tree_int (ost, 3) > 0)
+    {
+      error ("last argument of %qD is not integer constant between 0 and 3",
+             fndecl);
+      expand_builtin_trap ();
+      return const0_rtx;
+    }
 
-  if (new_delete_mismatch_p (*newc.u.s_binary.left, *delc.u.s_binary.left)
-      || !newc.u.s_binary.right != !delc.u.s_binary.right)
-    return true;
+  object_size_type = tree_to_shwi (ost);
 
-  if (newc.u.s_binary.right)
-    return new_delete_mismatch_p (*newc.u.s_binary.right,
-                                 *delc.u.s_binary.right);
-  return false;
+  return object_size_type < 2 ? constm1_rtx : const0_rtx;
 }
 
-/* Return true if DELETE_DECL is an operator delete that's not suitable
-   to call with a pointer returned fron NEW_DECL.  */
+/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+   FCODE is the BUILT_IN_* to use.
+   Return NULL_RTX if we failed; the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
 
-static bool
-new_delete_mismatch_p (tree new_decl, tree delete_decl)
+static rtx
+expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
+                          enum built_in_function fcode)
 {
-  tree new_name = DECL_ASSEMBLER_NAME (new_decl);
-  tree delete_name = DECL_ASSEMBLER_NAME (delete_decl);
-
-  /* valid_new_delete_pair_p() returns a conservative result (currently
-     it only handles global operators).  A true result is reliable but
-     a false result doesn't necessarily mean the operators don't match.  */
-  if (valid_new_delete_pair_p (new_name, delete_name))
-    return false;
-
-  /* For anything not handled by valid_new_delete_pair_p() such as member
-     operators compare the individual demangled components of the mangled
-     name.  */
-  const char *new_str = IDENTIFIER_POINTER (new_name);
-  const char *del_str = IDENTIFIER_POINTER (delete_name);
+  if (!validate_arglist (exp,
+                        POINTER_TYPE,
+                        fcode == BUILT_IN_MEMSET_CHK
+                        ? INTEGER_TYPE : POINTER_TYPE,
+                        INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
 
-  void *np = NULL, *dp = NULL;
-  demangle_component *ndc = cplus_demangle_v3_components (new_str, 0, &np);
-  demangle_component *ddc = cplus_demangle_v3_components (del_str, 0, &dp);
-  bool mismatch = new_delete_mismatch_p (*ndc, *ddc);
-  free (np);
-  free (dp);
-  return mismatch;
-}
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
-   functions.  Return true if the latter is suitable to deallocate objects
-   allocated by calls to the former.  */
+  /* FIXME: Set access mode to write only for memset et al.  */
+  bool sizes_ok = check_access (exp, len, /*maxread=*/NULL_TREE,
+                               /*srcstr=*/NULL_TREE, size, access_read_write);
 
-static bool
-matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
-{
-  /* Set to alloc_kind_t::builtin if ALLOC_DECL is associated with
-     a built-in deallocator.  */
-  enum class alloc_kind_t { none, builtin, user }
-  alloc_dealloc_kind = alloc_kind_t::none;
+  if (!tree_fits_uhwi_p (size))
+    return NULL_RTX;
 
-  if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
+  if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
     {
-      if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
-       /* Return true iff both functions are of the same array or
-          singleton form and false otherwise.  */
-       return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
+      /* Avoid transforming the checking call to an ordinary one when
+        an overflow has been detected or when the call couldn't be
+        validated because the size is not constant.  */
+      if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
+       return NULL_RTX;
 
-      /* Return false for deallocation functions that are known not
-        to match.  */
-      if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
-         || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
-       return false;
-      /* Otherwise proceed below to check the deallocation function's
-        "*dealloc" attributes to look for one that mentions this operator
-        new.  */
-    }
-  else if (fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL))
-    {
-      switch (DECL_FUNCTION_CODE (alloc_decl))
+      tree fn = NULL_TREE;
+      /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+        mem{cpy,pcpy,move,set} is available.  */
+      switch (fcode)
        {
-       case BUILT_IN_ALLOCA:
-       case BUILT_IN_ALLOCA_WITH_ALIGN:
-         return false;
-
-       case BUILT_IN_ALIGNED_ALLOC:
-       case BUILT_IN_CALLOC:
-       case BUILT_IN_GOMP_ALLOC:
-       case BUILT_IN_MALLOC:
-       case BUILT_IN_REALLOC:
-       case BUILT_IN_STRDUP:
-       case BUILT_IN_STRNDUP:
-         if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
-           return false;
-
-         if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
-             || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
-           return true;
-
-         alloc_dealloc_kind = alloc_kind_t::builtin;
+       case BUILT_IN_MEMCPY_CHK:
+         fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
+         break;
+       case BUILT_IN_MEMPCPY_CHK:
+         fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
+         break;
+       case BUILT_IN_MEMMOVE_CHK:
+         fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
+         break;
+       case BUILT_IN_MEMSET_CHK:
+         fn = builtin_decl_explicit (BUILT_IN_MEMSET);
          break;
-
        default:
          break;
        }
-    }
-
-  /* Set if DEALLOC_DECL both allocates and deallocates.  */
-  alloc_kind_t realloc_kind = alloc_kind_t::none;
-
-  if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
-    {
-      built_in_function dealloc_code = DECL_FUNCTION_CODE (dealloc_decl);
-      if (dealloc_code == BUILT_IN_REALLOC)
-       realloc_kind = alloc_kind_t::builtin;
-
-      for (tree amats = DECL_ATTRIBUTES (alloc_decl);
-          (amats = lookup_attribute ("malloc", amats));
-          amats = TREE_CHAIN (amats))
-       {
-         tree args = TREE_VALUE (amats);
-         if (!args)
-           continue;
 
-         tree fndecl = TREE_VALUE (args);
-         if (!fndecl || !DECL_P (fndecl))
-           continue;
+      if (! fn)
+       return NULL_RTX;
 
-         if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
-             && dealloc_code == DECL_FUNCTION_CODE (fndecl))
-           return true;
-       }
+      fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 3, dest, src, len);
+      gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+      return expand_expr (fn, target, mode, EXPAND_NORMAL);
     }
-
-  const bool alloc_builtin = fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL);
-  alloc_kind_t realloc_dealloc_kind = alloc_kind_t::none;
-
-  /* If DEALLOC_DECL has an internal "*dealloc" attribute scan the list
-     of its associated allocation functions for ALLOC_DECL.
-     If the corresponding ALLOC_DECL is found they're a matching pair,
-     otherwise they're not.
-     With DDATS set to the Deallocator's *Dealloc ATtributes...  */
-  for (tree ddats = DECL_ATTRIBUTES (dealloc_decl);
-       (ddats = lookup_attribute ("*dealloc", ddats));
-       ddats = TREE_CHAIN (ddats))
+  else if (fcode == BUILT_IN_MEMSET_CHK)
+    return NULL_RTX;
+  else
     {
-      tree args = TREE_VALUE (ddats);
-      if (!args)
-       continue;
-
-      tree alloc = TREE_VALUE (args);
-      if (!alloc)
-       continue;
+      unsigned int dest_align = get_pointer_alignment (dest);
 
-      if (alloc == DECL_NAME (dealloc_decl))
-       realloc_kind = alloc_kind_t::user;
+      /* If DEST is not a pointer type, call the normal function.  */
+      if (dest_align == 0)
+       return NULL_RTX;
 
-      if (DECL_P (alloc))
+      /* If SRC and DEST are the same (and not volatile), do nothing.  */
+      if (operand_equal_p (src, dest, 0))
        {
-         gcc_checking_assert (fndecl_built_in_p (alloc, BUILT_IN_NORMAL));
+         tree expr;
 
-         switch (DECL_FUNCTION_CODE (alloc))
+         if (fcode != BUILT_IN_MEMPCPY_CHK)
            {
-           case BUILT_IN_ALIGNED_ALLOC:
-           case BUILT_IN_CALLOC:
-           case BUILT_IN_GOMP_ALLOC:
-           case BUILT_IN_MALLOC:
-           case BUILT_IN_REALLOC:
-           case BUILT_IN_STRDUP:
-           case BUILT_IN_STRNDUP:
-             realloc_dealloc_kind = alloc_kind_t::builtin;
-             break;
-           default:
-             break;
+             /* Evaluate and ignore LEN in case it has side-effects.  */
+             expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+             return expand_expr (dest, target, mode, EXPAND_NORMAL);
            }
 
-         if (!alloc_builtin)
-           continue;
-
-         if (DECL_FUNCTION_CODE (alloc) != DECL_FUNCTION_CODE (alloc_decl))
-           continue;
-
-         return true;
+         expr = fold_build_pointer_plus (dest, len);
+         return expand_expr (expr, target, mode, EXPAND_NORMAL);
        }
 
-      if (alloc == DECL_NAME (alloc_decl))
-       return true;
-    }
-
-  if (realloc_kind == alloc_kind_t::none)
-    return false;
+      /* __memmove_chk special case.  */
+      if (fcode == BUILT_IN_MEMMOVE_CHK)
+       {
+         unsigned int src_align = get_pointer_alignment (src);
 
-  hash_set<tree> common_deallocs;
-  /* Special handling for deallocators.  Iterate over both the allocator's
-     and the reallocator's associated deallocator functions looking for
-     the first one in common.  If one is found, the de/reallocator is
-     a match for the allocator even though the latter isn't directly
-     associated with the former.  This simplifies declarations in system
-     headers.
-     With AMATS set to the Allocator's Malloc ATtributes,
-     and  RMATS set to Reallocator's Malloc ATtributes...  */
-  for (tree amats = DECL_ATTRIBUTES (alloc_decl),
-        rmats = DECL_ATTRIBUTES (dealloc_decl);
-       (amats = lookup_attribute ("malloc", amats))
-        || (rmats = lookup_attribute ("malloc", rmats));
-       amats = amats ? TREE_CHAIN (amats) : NULL_TREE,
-        rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
-    {
-      if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
-       if (tree adealloc = TREE_VALUE (args))
-         {
-           if (DECL_P (adealloc)
-               && fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
-             {
-               built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
-               if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
-                 {
-                   if (realloc_kind == alloc_kind_t::builtin)
-                     return true;
-                   alloc_dealloc_kind = alloc_kind_t::builtin;
-                 }
-               continue;
-             }
-
-           common_deallocs.add (adealloc);
-         }
+         if (src_align == 0)
+           return NULL_RTX;
 
-      if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
-       if (tree ddealloc = TREE_VALUE (args))
-         {
-           if (DECL_P (ddealloc)
-               && fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
-             {
-               built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
-               if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
-                 {
-                   if (alloc_dealloc_kind == alloc_kind_t::builtin)
-                     return true;
-                   realloc_dealloc_kind = alloc_kind_t::builtin;
-                 }
-               continue;
-             }
-
-           if (common_deallocs.add (ddealloc))
-             return true;
-         }
+         /* If src is categorized for a readonly section we can use
+            normal __memcpy_chk.  */
+         if (readonly_data_expr (src))
+           {
+             tree fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+             if (!fn)
+               return NULL_RTX;
+             fn = build_call_nofold_loc (EXPR_LOCATION (exp), fn, 4,
+                                         dest, src, len, size);
+             gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+             CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+             return expand_expr (fn, target, mode, EXPAND_NORMAL);
+           }
+       }
+      return NULL_RTX;
     }
-
-  /* Succeed only if ALLOC_DECL and the reallocator DEALLOC_DECL share
-     a built-in deallocator.  */
-  return  (alloc_dealloc_kind == alloc_kind_t::builtin
-          && realloc_dealloc_kind == alloc_kind_t::builtin);
 }
 
-/* Return true if DEALLOC_DECL is a function suitable to deallocate
-   objectes allocated by the ALLOC call.  */
+/* Emit warning if a buffer overflow is detected at compile time.  */
 
-static bool
-matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
+static void
+maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
 {
-  tree alloc_decl = gimple_call_fndecl (alloc);
-  if (!alloc_decl)
-    return true;
-
-  return matching_alloc_calls_p (alloc_decl, dealloc_decl);
-}
+  /* The source string.  */
+  tree srcstr = NULL_TREE;
+  /* The size of the destination object returned by __builtin_object_size.  */
+  tree objsize = NULL_TREE;
+  /* The string that is being concatenated with (as in __strcat_chk)
+     or null if it isn't.  */
+  tree catstr = NULL_TREE;
+  /* The maximum length of the source sequence in a bounded operation
+     (such as __strncat_chk) or null if the operation isn't bounded
+     (such as __strcat_chk).  */
+  tree maxread = NULL_TREE;
+  /* The exact size of the access (such as in __strncpy_chk).  */
+  tree size = NULL_TREE;
+  /* The access by the function that's checked.  Except for snprintf
+     both writing and reading is checked.  */
+  access_mode mode = access_read_write;
 
-/* Diagnose a call EXP to deallocate a pointer referenced by AREF if it
-   includes a nonzero offset.  Such a pointer cannot refer to the beginning
-   of an allocated object.  A negative offset may refer to it only if
-   the target pointer is unknown.  */
+  switch (fcode)
+    {
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY_CHK:
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
+      break;
 
-static bool
-warn_dealloc_offset (location_t loc, tree exp, const access_ref &aref)
-{
-  if (aref.deref || aref.offrng[0] <= 0 || aref.offrng[1] <= 0)
-    return false;
+    case BUILT_IN_STRCAT_CHK:
+      /* For __strcat_chk the warning will be emitted only if overflowing
+        by at least strlen (dest) + 1 bytes.  */
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
+      break;
 
-  tree dealloc_decl = get_callee_fndecl (exp);
-  if (!dealloc_decl)
-    return false;
+    case BUILT_IN_STRNCAT_CHK:
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
+      break;
 
-  if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
-      && !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
-    {
-      /* A call to a user-defined operator delete with a pointer plus offset
-        may be valid if it's returned from an unknown function (i.e., one
-        that's not operator new).  */
-      if (TREE_CODE (aref.ref) == SSA_NAME)
-       {
-         gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
-         if (is_gimple_call (def_stmt))
-           {
-             tree alloc_decl = gimple_call_fndecl (def_stmt);
-             if (!alloc_decl || !DECL_IS_OPERATOR_NEW_P (alloc_decl))
-               return false;
-           }
-       }
-    }
+    case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      size = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
+      break;
 
-  char offstr[80];
-  offstr[0] = '\0';
-  if (wi::fits_shwi_p (aref.offrng[0]))
-    {
-      if (aref.offrng[0] == aref.offrng[1]
-         || !wi::fits_shwi_p (aref.offrng[1]))
-       sprintf (offstr, " %lli",
-                (long long)aref.offrng[0].to_shwi ());
-      else
-       sprintf (offstr, " [%lli, %lli]",
-                (long long)aref.offrng[0].to_shwi (),
-                (long long)aref.offrng[1].to_shwi ());
+    case BUILT_IN_SNPRINTF_CHK:
+    case BUILT_IN_VSNPRINTF_CHK:
+      maxread = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 3);
+      /* The only checked access the write to the destination.  */
+      mode = access_write_only;
+      break;
+    default:
+      gcc_unreachable ();
     }
 
-  if (!warning_at (loc, OPT_Wfree_nonheap_object,
-                  "%qD called on pointer %qE with nonzero offset%s",
-                  dealloc_decl, aref.ref, offstr))
-    return false;
-
-  if (DECL_P (aref.ref))
-    inform (DECL_SOURCE_LOCATION (aref.ref), "declared here");
-  else if (TREE_CODE (aref.ref) == SSA_NAME)
+  if (catstr && maxread)
     {
-      gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
-      if (is_gimple_call (def_stmt))
-       {
-         location_t def_loc = gimple_location (def_stmt);
-         tree alloc_decl = gimple_call_fndecl (def_stmt);
-         if (alloc_decl)
-           inform (def_loc,
-                   "returned from %qD", alloc_decl);
-         else if (tree alloc_fntype = gimple_call_fntype (def_stmt))
-           inform (def_loc,
-                   "returned from %qT", alloc_fntype);
-         else
-           inform (def_loc,  "obtained here");
-       }
+      /* Check __strncat_chk.  There is no way to determine the length
+        of the string to which the source string is being appended so
+        just warn when the length of the source string is not known.  */
+      check_strncat_sizes (exp, objsize);
+      return;
     }
 
-  return true;
+  check_access (exp, size, maxread, srcstr, objsize, mode);
 }
 
-/* Issue a warning if a deallocation function such as free, realloc,
-   or C++ operator delete is called with an argument not returned by
-   a matching allocation function such as malloc or the corresponding
-   form of C++ operatorn new.  */
+/* Emit warning if a buffer overflow is detected at compile time
+   in __sprintf_chk/__vsprintf_chk calls.  */
 
-void
-maybe_emit_free_warning (tree exp)
+static void
+maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 {
-  tree fndecl = get_callee_fndecl (exp);
-  if (!fndecl)
-    return;
+  tree size, len, fmt;
+  const char *fmt_str;
+  int nargs = call_expr_nargs (exp);
 
-  unsigned argno = call_dealloc_argno (exp);
-  if ((unsigned) call_expr_nargs (exp) <= argno)
-    return;
+  /* Verify the required arguments in the original call.  */
 
-  tree ptr = CALL_EXPR_ARG (exp, argno);
-  if (integer_zerop (ptr))
+  if (nargs < 4)
     return;
+  size = CALL_EXPR_ARG (exp, 2);
+  fmt = CALL_EXPR_ARG (exp, 3);
 
-  access_ref aref;
-  if (!compute_objsize (ptr, 0, &aref))
+  if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
     return;
 
-  tree ref = aref.ref;
-  if (integer_zerop (ref))
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
     return;
 
-  tree dealloc_decl = get_callee_fndecl (exp);
-  location_t loc = EXPR_LOCATION (exp);
+  if (!init_target_chars ())
+    return;
 
-  if (DECL_P (ref) || EXPR_P (ref))
+  /* If the format doesn't contain % args or %%, we know its size.  */
+  if (strchr (fmt_str, target_percent) == 0)
+    len = build_int_cstu (size_type_node, strlen (fmt_str));
+  /* If the format is "%s" and first ... argument is a string literal,
+     we know it too.  */
+  else if (fcode == BUILT_IN_SPRINTF_CHK
+          && strcmp (fmt_str, target_percent_s) == 0)
     {
-      /* Diagnose freeing a declared object.  */
-      if (aref.ref_declared ()
-         && warning_at (loc, OPT_Wfree_nonheap_object,
-                        "%qD called on unallocated object %qD",
-                        dealloc_decl, ref))
-       {
-         loc = (DECL_P (ref)
-                ? DECL_SOURCE_LOCATION (ref)
-                : EXPR_LOCATION (ref));
-         inform (loc, "declared here");
-         return;
-       }
+      tree arg;
 
-      /* Diagnose freeing a pointer that includes a positive offset.
-        Such a pointer cannot refer to the beginning of an allocated
-        object.  A negative offset may refer to it.  */
-      if (aref.sizrng[0] != aref.sizrng[1]
-         && warn_dealloc_offset (loc, exp, aref))
+      if (nargs < 5)
+       return;
+      arg = CALL_EXPR_ARG (exp, 4);
+      if (! POINTER_TYPE_P (TREE_TYPE (arg)))
+       return;
+
+      len = c_strlen (arg, 1);
+      if (!len || ! tree_fits_uhwi_p (len))
        return;
     }
-  else if (CONSTANT_CLASS_P (ref))
-    {
-      if (warning_at (loc, OPT_Wfree_nonheap_object,
-                     "%qD called on a pointer to an unallocated "
-                     "object %qE", dealloc_decl, ref))
-       {
-         if (TREE_CODE (ptr) == SSA_NAME)
-           {
-             gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
-             if (is_gimple_assign (def_stmt))
-               {
-                 location_t loc = gimple_location (def_stmt);
-                 inform (loc, "assigned here");
-               }
-           }
-         return;
-       }
-    }
-  else if (TREE_CODE (ref) == SSA_NAME)
-    {
-      /* Also warn if the pointer argument refers to the result
-        of an allocation call like alloca or VLA.  */
-      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-      if (is_gimple_call (def_stmt))
-       {
-         bool warned = false;
-         if (gimple_call_alloc_p (def_stmt))
-           {
-             if (matching_alloc_calls_p (def_stmt, dealloc_decl))
-               {
-                 if (warn_dealloc_offset (loc, exp, aref))
-                   return;
-               }
-             else
-               {
-                 tree alloc_decl = gimple_call_fndecl (def_stmt);
-                 const opt_code opt =
-                   (DECL_IS_OPERATOR_NEW_P (alloc_decl)
-                    || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
-                    ? OPT_Wmismatched_new_delete
-                    : OPT_Wmismatched_dealloc);
-                 warned = warning_at (loc, opt,
-                                      "%qD called on pointer returned "
-                                      "from a mismatched allocation "
-                                      "function", dealloc_decl);
-               }
-           }
-         else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
-                  || gimple_call_builtin_p (def_stmt,
-                                            BUILT_IN_ALLOCA_WITH_ALIGN))
-           warned = warning_at (loc, OPT_Wfree_nonheap_object,
-                                "%qD called on pointer to "
-                                "an unallocated object",
-                                dealloc_decl);
-         else if (warn_dealloc_offset (loc, exp, aref))
-           return;
-
-         if (warned)
-           {
-             tree fndecl = gimple_call_fndecl (def_stmt);
-             inform (gimple_location (def_stmt),
-                     "returned from %qD", fndecl);
-             return;
-           }
-       }
-      else if (gimple_nop_p (def_stmt))
-       {
-         ref = SSA_NAME_VAR (ref);
-         /* Diagnose freeing a pointer that includes a positive offset.  */
-         if (TREE_CODE (ref) == PARM_DECL
-             && !aref.deref
-             && aref.sizrng[0] != aref.sizrng[1]
-             && aref.offrng[0] > 0 && aref.offrng[1] > 0
-             && warn_dealloc_offset (loc, exp, aref))
-           return;
-       }
-    }
+  else
+    return;
+
+  /* Add one for the terminating nul.  */
+  len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
+  check_access (exp, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, len, size,
+               access_write_only);
 }
 
 /* Fold a call to __builtin_object_size with arguments PTR and OST,
index a64ece3f1cd9aed195cd2e5ec5383d4a96e39bf8..b580635a09f03406b9c0edbf5510e233d3bfa37a 100644 (file)
@@ -149,223 +149,10 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
-extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
-extern void warn_string_no_nul (location_t, tree, const char *, tree,
-                               tree, tree = NULL_TREE, bool = false,
-                               const wide_int[2] = NULL);
-extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
 extern bool builtin_with_linkage_p (tree);
 
-/* Describes recursion limits used by functions that follow use-def
-   chains of SSA_NAMEs.  */
-
-class ssa_name_limit_t
-{
-  bitmap visited;         /* Bitmap of visited SSA_NAMEs.  */
-  unsigned ssa_def_max;   /* Longest chain of SSA_NAMEs to follow.  */
-
-  /* Not copyable or assignable.  */
-  DISABLE_COPY_AND_ASSIGN (ssa_name_limit_t);
-
-public:
-
-  ssa_name_limit_t ()
-    : visited (),
-      ssa_def_max (param_ssa_name_def_chain_limit) { }
-
-  /* Set a bit for the PHI in VISITED and return true if it wasn't
-     already set.  */
-  bool visit_phi (tree);
-  /* Clear a bit for the PHI in VISITED.  */
-  void leave_phi (tree);
-  /* Return false if the SSA_NAME chain length counter has reached
-     the limit, otherwise increment the counter and return true.  */
-  bool next ();
-
-  /* If the SSA_NAME has already been "seen" return a positive value.
-     Otherwise add it to VISITED.  If the SSA_NAME limit has been
-     reached, return a negative value.  Otherwise return zero.  */
-  int next_phi (tree);
-
-  ~ssa_name_limit_t ();
-};
-
-class pointer_query;
-
-/* Describes a reference to an object used in an access.  */
-struct access_ref
-{
-  /* Set the bounds of the reference to at most as many bytes
-     as the first argument or unknown when null, and at least
-     one when the second argument is true unless the first one
-     is a constant zero.  */
-  access_ref (tree = NULL_TREE, bool = false);
-
-  /* Return the PHI node REF refers to or null if it doesn't.  */
-  gphi *phi () const;
-
-  /* Return the object to which REF refers.  */
-  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
-               ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
-
-  /* Return true if OFFRNG is the constant zero.  */
-  bool offset_zero () const
-  {
-    return offrng[0] == 0 && offrng[1] == 0;
-  }
-
-  /* Return true if OFFRNG is bounded to a subrange of offset values
-     valid for the largest possible object.  */
-  bool offset_bounded () const;
-
-  /* Return the maximum amount of space remaining and if non-null, set
-     argument to the minimum.  */
-  offset_int size_remaining (offset_int * = NULL) const;
-
-/* Return true if the offset and object size are in range for SIZE.  */
-  bool offset_in_range (const offset_int &) const;
-
-  /* Return true if *THIS is an access to a declared object.  */
-  bool ref_declared () const
-  {
-    return DECL_P (ref) && base0 && deref < 1;
-  }
-
-  /* Set the size range to the maximum.  */
-  void set_max_size_range ()
-  {
-    sizrng[0] = 0;
-    sizrng[1] = wi::to_offset (max_object_size ());
-  }
-
-  /* Add OFF to the offset range.  */
-  void add_offset (const offset_int &off)
-  {
-    add_offset (off, off);
-  }
-
-  /* Add the range [MIN, MAX] to the offset range.  */
-  void add_offset (const offset_int &, const offset_int &);
-
-  /* Add the maximum representable offset to the offset range.  */
-  void add_max_offset ()
-  {
-    offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-    add_offset (-maxoff - 1, maxoff);
-  }
-
-  /* Issue an informational message describing the target of an access
-     with the given mode.  */
-  void inform_access (access_mode) const;
-
-  /* Reference to the accessed object(s).  */
-  tree ref;
-
-  /* Range of byte offsets into and sizes of the object(s).  */
-  offset_int offrng[2];
-  offset_int sizrng[2];
-  /* The minimum and maximum offset computed.  */
-  offset_int offmax[2];
-  /* Range of the bound of the access: denotes that the access
-     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
-     For string functions the size of the actual access is
-     further constrained by the length of the string.  */
-  offset_int bndrng[2];
-
-  /* Used to fold integer expressions when called from front ends.  */
-  tree (*eval)(tree);
-  /* Positive when REF is dereferenced, negative when its address is
-     taken.  */
-  int deref;
-  /* Set if trailing one-element arrays should be treated as flexible
-     array members.  */
-  bool trail1special;
-  /* Set if valid offsets must start at zero (for declared and allocated
-     objects but not for others referenced by pointers).  */
-  bool base0;
-  /* Set if REF refers to a function array parameter not declared
-     static.  */
-  bool parmarray;
-};
-
-class range_query;
-
-/* Queries and caches compute_objsize results.  */
-class pointer_query
-{
-  DISABLE_COPY_AND_ASSIGN (pointer_query);
-
-public:
-  /* Type of the two-level cache object defined by clients of the class
-     to have pointer SSA_NAMEs cached for speedy access.  */
-  struct cache_type
-  {
-    /* 1-based indices into cache.  */
-    vec<unsigned> indices;
-    /* The cache itself.  */
-    vec<access_ref> access_refs;
-  };
-
-  /* Construct an object with the given Ranger instance and cache.  */
-  explicit pointer_query (range_query * = NULL, cache_type * = NULL);
-
-  /* Retrieve the access_ref for a variable from cache if it's there.  */
-  const access_ref* get_ref (tree, int = 1) const;
-
-  /* Retrieve the access_ref for a variable from cache or compute it.  */
-  bool get_ref (tree, access_ref*, int = 1);
-
-  /* Add an access_ref for the SSA_NAME to the cache.  */
-  void put_ref (tree, const access_ref&, int = 1);
-
-  /* Flush the cache.  */
-  void flush_cache ();
-
-  /* A Ranger instance.  May be null to use global ranges.  */
-  range_query *rvals;
-  /* Cache of SSA_NAMEs.  May be null to disable caching.  */
-  cache_type *var_cache;
-
-  /* Cache performance counters.  */
-  mutable unsigned hits;
-  mutable unsigned misses;
-  mutable unsigned failures;
-  mutable unsigned depth;
-  mutable unsigned max_depth;
-};
-
-/* Describes a pair of references used in an access by built-in
-   functions like memcpy.  */
-struct access_data
-{
-  /* Set the access to at most MAXWRITE and MAXREAD bytes, and
-     at least 1 when MINWRITE or MINREAD, respectively, is set.  */
-  access_data (tree expr, access_mode mode,
-              tree maxwrite = NULL_TREE, bool minwrite = false,
-              tree maxread = NULL_TREE, bool minread = false)
-    : call (expr),
-      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
-
-  /* Built-in function call.  */
-  tree call;
-  /* Destination and source of the access.  */
-  access_ref dst, src;
-  /* Read-only for functions like memcmp or strlen, write-only
-     for memset, read-write for memcpy or strcat.  */
-  access_mode mode;
-};
-
-extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
-                                   range_query * = NULL);
-extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
-
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
-/* Legacy/transitional API.  Should not be used in new code.  */
-extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
-                            range_query * = NULL);
+class access_data;
 extern bool check_access (tree, tree, tree, tree, tree,
                          access_mode, const access_data * = NULL);
-extern void maybe_emit_free_warning (tree);
 
 #endif /* GCC_BUILTINS_H */
index c54c57206c7c61076c84533290eb1e4833bc628f..795d21414c18ac13bebe6fdedf716ab1bbd69c5e 100644 (file)
@@ -60,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "attr-fnspec.h"
 #include "value-query.h"
+#include "pointer-query.h"
 
 #include "tree-pretty-print.h"
 
@@ -2628,10 +2629,6 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 
   /* Check attribute access arguments.  */
   maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, exp);
-
-  /* Check calls to operator new for mismatched forms and attempts
-     to deallocate unallocated objects.  */
-  maybe_emit_free_warning (exp);
 }
 
 /* Update ARGS_SIZE to contain the total size for the argument block.
index d47e405e7459034d1248fb9dbc76aad2f7ae8ec5..229c84e1d74d9e92ea40c52e72e44bdeafffc3d0 100644 (file)
@@ -34,7 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "stor-layout.h"
-#include "builtins.h"
+#include "pointer-query.h"
 
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
index 598c76bf52e916f50a6d915f0b25f2092f44d75d..3a5c2dd0be88b3e08d290a0b7d7913f4ff3408e3 100644 (file)
@@ -37,7 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "domwalk.h"
 #include "tree-cfg.h"
 #include "attribs.h"
-#include "builtins.h"
+#include "pointer-query.h"
 
 // This purposely returns a value_range, not a value_range_equiv, to
 // break the dependency on equivalences for this pass.
index b6f6054354384e4ded7ee3cc46b2e1d10e663f2d..ad7b140173fa5c04ac9fbb1d5d4bd16aaa58baab 100644 (file)
@@ -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-access.h"
 #include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stmt.h"
index f38fb03f068e4aee8c25cd983f9f010e61f18d85..8e90b7cfc43c54d5d29208dd6519bab46aae093e 100644 (file)
@@ -71,6 +71,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "attribs.h"
 #include "builtins.h"
+#include "pointer-query.h"
 #include "stor-layout.h"
 
 #include "realmpfr.h"
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
new file mode 100644 (file)
index 0000000..e4d98b2
--- /dev/null
@@ -0,0 +1,1765 @@
+/* Pass to detect and issue warnings for invalid accesses, including
+   invalid or mismatched allocation/deallocation calls.
+
+   Copyright (C) 2020-2021 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "builtins.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-access.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "tree-cfg.h"
+#include "tree-object-size.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+#include "gimple-range.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "demangle.h"
+#include "pointer-query.h"
+
+/* For a call EXPR at LOC to a function FNAME that expects a string
+   in the argument ARG, issue a diagnostic due to it being a called
+   with an argument that is a character array with no terminating
+   NUL.  SIZE is the EXACT size of the array, and BNDRNG the number
+   of characters in which the NUL is expected.  Either EXPR or FNAME
+   may be null but noth both.  SIZE may be null when BNDRNG is null.  */
+
+void
+warn_string_no_nul (location_t loc, tree expr, const char *fname,
+                   tree arg, tree decl, tree size /* = NULL_TREE */,
+                   bool exact /* = false */,
+                   const wide_int bndrng[2] /* = NULL */)
+{
+  const opt_code opt = OPT_Wstringop_overread;
+  if ((expr && warning_suppressed_p (expr, opt))
+      || warning_suppressed_p (arg, opt))
+    return;
+
+  loc = expansion_point_location_if_in_system_header (loc);
+  bool warned;
+
+  /* Format the bound range as a string to keep the nuber of messages
+     from exploding.  */
+  char bndstr[80];
+  *bndstr = 0;
+  if (bndrng)
+    {
+      if (bndrng[0] == bndrng[1])
+       sprintf (bndstr, "%llu", (unsigned long long) bndrng[0].to_uhwi ());
+      else
+       sprintf (bndstr, "[%llu, %llu]",
+                (unsigned long long) bndrng[0].to_uhwi (),
+                (unsigned long long) bndrng[1].to_uhwi ());
+    }
+
+  const tree maxobjsize = max_object_size ();
+  const wide_int maxsiz = wi::to_wide (maxobjsize);
+  if (expr)
+    {
+      tree func = get_callee_fndecl (expr);
+      if (bndrng)
+       {
+         if (wi::ltu_p (maxsiz, bndrng[0]))
+           warned = warning_at (loc, opt,
+                                "%qD specified bound %s exceeds "
+                                "maximum object size %E",
+                                func, bndstr, maxobjsize);
+         else
+           {
+             bool maybe = wi::to_wide (size) == bndrng[0];
+             warned = warning_at (loc, opt,
+                                  exact
+                                  ? G_("%qD specified bound %s exceeds "
+                                       "the size %E of unterminated array")
+                                  : (maybe
+                                     ? G_("%qD specified bound %s may "
+                                          "exceed the size of at most %E "
+                                          "of unterminated array")
+                                     : G_("%qD specified bound %s exceeds "
+                                          "the size of at most %E "
+                                          "of unterminated array")),
+                                  func, bndstr, size);
+           }
+       }
+      else
+       warned = warning_at (loc, opt,
+                            "%qD argument missing terminating nul",
+                            func);
+    }
+  else
+    {
+      if (bndrng)
+       {
+         if (wi::ltu_p (maxsiz, bndrng[0]))
+           warned = warning_at (loc, opt,
+                                "%qs specified bound %s exceeds "
+                                "maximum object size %E",
+                                fname, bndstr, maxobjsize);
+         else
+           {
+             bool maybe = wi::to_wide (size) == bndrng[0];
+             warned = warning_at (loc, opt,
+                                  exact
+                                  ? G_("%qs specified bound %s exceeds "
+                                       "the size %E of unterminated array")
+                                  : (maybe
+                                     ? G_("%qs specified bound %s may "
+                                          "exceed the size of at most %E "
+                                          "of unterminated array")
+                                     : G_("%qs specified bound %s exceeds "
+                                          "the size of at most %E "
+                                          "of unterminated array")),
+                                  fname, bndstr, size);
+           }
+       }
+      else
+       warned = warning_at (loc, opt,
+                            "%qs argument missing terminating nul",
+                            fname);
+    }
+
+  if (warned)
+    {
+      inform (DECL_SOURCE_LOCATION (decl),
+             "referenced argument declared here");
+      suppress_warning (arg, opt);
+      if (expr)
+       suppress_warning (expr, opt);
+    }
+}
+
+/* For a call EXPR (which may be null) that expects a string argument
+   SRC as an argument, returns false if SRC is a character array with
+   no terminating NUL.  When nonnull, BOUND is the number of characters
+   in which to expect the terminating NUL.  RDONLY is true for read-only
+   accesses such as strcmp, false for read-write such as strcpy.  When
+   EXPR is also issues a warning.  */
+
+bool
+check_nul_terminated_array (tree expr, tree src,
+                           tree bound /* = NULL_TREE */)
+{
+  /* The constant size of the array SRC points to.  The actual size
+     may be less of EXACT is true, but not more.  */
+  tree size;
+  /* True if SRC involves a non-constant offset into the array.  */
+  bool exact;
+  /* The unterminated constant array SRC points to.  */
+  tree nonstr = unterminated_array (src, &size, &exact);
+  if (!nonstr)
+    return true;
+
+  /* NONSTR refers to the non-nul terminated constant array and SIZE
+     is the constant size of the array in bytes.  EXACT is true when
+     SIZE is exact.  */
+
+  wide_int bndrng[2];
+  if (bound)
+    {
+      value_range r;
+
+      get_global_range_query ()->range_of_expr (r, bound);
+
+      if (r.kind () != VR_RANGE)
+       return true;
+
+      bndrng[0] = r.lower_bound ();
+      bndrng[1] = r.upper_bound ();
+
+      if (exact)
+       {
+         if (wi::leu_p (bndrng[0], wi::to_wide (size)))
+           return true;
+       }
+      else if (wi::lt_p (bndrng[0], wi::to_wide (size), UNSIGNED))
+       return true;
+    }
+
+  if (expr)
+    warn_string_no_nul (EXPR_LOCATION (expr), expr, NULL, src, nonstr,
+                       size, exact, bound ? bndrng : NULL);
+
+  return false;
+}
+
+/* If EXP refers to an unterminated constant character array return
+   the declaration of the object of which the array is a member or
+   element and if SIZE is not null, set *SIZE to the size of
+   the unterminated array and set *EXACT if the size is exact or
+   clear it otherwise.  Otherwise return null.  */
+
+tree
+unterminated_array (tree exp, tree *size /* = NULL */, bool *exact /* = NULL */)
+{
+  /* C_STRLEN will return NULL and set DECL in the info
+     structure if EXP references a unterminated array.  */
+  c_strlen_data lendata = { };
+  tree len = c_strlen (exp, 1, &lendata);
+  if (len == NULL_TREE && lendata.minlen && lendata.decl)
+     {
+       if (size)
+       {
+         len = lendata.minlen;
+         if (lendata.off)
+           {
+             /* Constant offsets are already accounted for in LENDATA.MINLEN,
+                but not in a SSA_NAME + CST expression.  */
+             if (TREE_CODE (lendata.off) == INTEGER_CST)
+               *exact = true;
+             else if (TREE_CODE (lendata.off) == PLUS_EXPR
+                      && TREE_CODE (TREE_OPERAND (lendata.off, 1)) == INTEGER_CST)
+               {
+                 /* Subtract the offset from the size of the array.  */
+                 *exact = false;
+                 tree temp = TREE_OPERAND (lendata.off, 1);
+                 temp = fold_convert (ssizetype, temp);
+                 len = fold_build2 (MINUS_EXPR, ssizetype, len, temp);
+               }
+             else
+               *exact = false;
+           }
+         else
+           *exact = true;
+
+         *size = len;
+       }
+       return lendata.decl;
+     }
+
+  return NULL_TREE;
+}
+
+/* Issue a warning OPT for a bounded call EXP with a bound in RANGE
+   accessing an object with SIZE.  */
+
+bool
+maybe_warn_for_bound (opt_code opt, location_t loc, tree exp, tree func,
+                     tree bndrng[2], tree size,
+                     const access_data *pad /* = NULL */)
+{
+  if (!bndrng[0] || warning_suppressed_p (exp, opt))
+    return false;
+
+  tree maxobjsize = max_object_size ();
+
+  bool warned = false;
+
+  if (opt == OPT_Wstringop_overread)
+    {
+      bool maybe = pad && pad->src.phi ();
+
+      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+       {
+         if (bndrng[0] == bndrng[1])
+           warned = (func
+                     ? warning_at (loc, opt,
+                                   (maybe
+                                    ? G_("%qD specified bound %E may "
+                                         "exceed maximum object size %E")
+                                    : G_("%qD specified bound %E "
+                                         "exceeds maximum object size %E")),
+                                   func, bndrng[0], maxobjsize)
+                     : warning_at (loc, opt,
+                                   (maybe
+                                    ? G_("specified bound %E may "
+                                         "exceed maximum object size %E")
+                                    : G_("specified bound %E "
+                                         "exceeds maximum object size %E")),
+                                   bndrng[0], maxobjsize));
+         else
+           warned = (func
+                     ? warning_at (loc, opt,
+                                   (maybe
+                                    ? G_("%qD specified bound [%E, %E] may "
+                                         "exceed maximum object size %E")
+                                    : G_("%qD specified bound [%E, %E] "
+                                         "exceeds maximum object size %E")),
+                                   func,
+                                   bndrng[0], bndrng[1], maxobjsize)
+                     : warning_at (loc, opt,
+                                   (maybe
+                                    ? G_("specified bound [%E, %E] may "
+                                         "exceed maximum object size %E")
+                                    : G_("specified bound [%E, %E] "
+                                         "exceeds maximum object size %E")),
+                                   bndrng[0], bndrng[1], maxobjsize));
+       }
+      else if (!size || tree_int_cst_le (bndrng[0], size))
+       return false;
+      else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD specified bound %E may exceed "
+                                     "source size %E")
+                                : G_("%qD specified bound %E exceeds "
+                                     "source size %E")),
+                               func, bndrng[0], size)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("specified bound %E may exceed "
+                                     "source size %E")
+                                : G_("specified bound %E exceeds "
+                                     "source size %E")),
+                               bndrng[0], size));
+      else
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD specified bound [%E, %E] may "
+                                     "exceed source size %E")
+                                : G_("%qD specified bound [%E, %E] exceeds "
+                                     "source size %E")),
+                               func, bndrng[0], bndrng[1], size)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("specified bound [%E, %E] may exceed "
+                                     "source size %E")
+                                : G_("specified bound [%E, %E] exceeds "
+                                     "source size %E")),
+                               bndrng[0], bndrng[1], size));
+      if (warned)
+       {
+         if (pad && pad->src.ref)
+           {
+             if (DECL_P (pad->src.ref))
+               inform (DECL_SOURCE_LOCATION (pad->src.ref),
+                       "source object declared here");
+             else if (EXPR_HAS_LOCATION (pad->src.ref))
+               inform (EXPR_LOCATION (pad->src.ref),
+                       "source object allocated here");
+           }
+         suppress_warning (exp, opt);
+       }
+
+      return warned;
+    }
+
+  bool maybe = pad && pad->dst.phi ();
+  if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+    {
+      if (bndrng[0] == bndrng[1])
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD specified size %E may "
+                                     "exceed maximum object size %E")
+                                : G_("%qD specified size %E "
+                                     "exceeds maximum object size %E")),
+                               func, bndrng[0], maxobjsize)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("specified size %E may exceed "
+                                     "maximum object size %E")
+                                : G_("specified size %E exceeds "
+                                     "maximum object size %E")),
+                               bndrng[0], maxobjsize));
+      else
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD specified size between %E and %E "
+                                     "may exceed maximum object size %E")
+                                : G_("%qD specified size between %E and %E "
+                                     "exceeds maximum object size %E")),
+                               func, bndrng[0], bndrng[1], maxobjsize)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("specified size between %E and %E "
+                                     "may exceed maximum object size %E")
+                                : G_("specified size between %E and %E "
+                                     "exceeds maximum object size %E")),
+                               bndrng[0], bndrng[1], maxobjsize));
+    }
+  else if (!size || tree_int_cst_le (bndrng[0], size))
+    return false;
+  else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+    warned = (func
+             ? warning_at (loc, opt,
+                           (maybe
+                            ? G_("%qD specified bound %E may exceed "
+                                 "destination size %E")
+                            : G_("%qD specified bound %E exceeds "
+                                 "destination size %E")),
+                           func, bndrng[0], size)
+             : warning_at (loc, opt,
+                           (maybe
+                            ? G_("specified bound %E may exceed "
+                                 "destination size %E")
+                            : G_("specified bound %E exceeds "
+                                 "destination size %E")),
+                           bndrng[0], size));
+  else
+    warned = (func
+             ? warning_at (loc, opt,
+                           (maybe
+                            ? G_("%qD specified bound [%E, %E] may exceed "
+                                 "destination size %E")
+                            : G_("%qD specified bound [%E, %E] exceeds "
+                                 "destination size %E")),
+                           func, bndrng[0], bndrng[1], size)
+             : warning_at (loc, opt,
+                           (maybe
+                            ? G_("specified bound [%E, %E] exceeds "
+                                 "destination size %E")
+                            : G_("specified bound [%E, %E] exceeds "
+                                 "destination size %E")),
+                           bndrng[0], bndrng[1], size));
+
+  if (warned)
+    {
+      if (pad && pad->dst.ref)
+       {
+         if (DECL_P (pad->dst.ref))
+           inform (DECL_SOURCE_LOCATION (pad->dst.ref),
+                   "destination object declared here");
+         else if (EXPR_HAS_LOCATION (pad->dst.ref))
+           inform (EXPR_LOCATION (pad->dst.ref),
+                   "destination object allocated here");
+       }
+      suppress_warning (exp, opt);
+    }
+
+  return warned;
+}
+
+/* For an expression EXP issue an access warning controlled by option OPT
+   with access to a region SIZE bytes in size in the RANGE of sizes.
+   WRITE is true for a write access, READ for a read access, neither for
+   call that may or may not perform an access but for which the range
+   is expected to valid.
+   Returns true when a warning has been issued.  */
+
+static bool
+warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
+                tree size, bool write, bool read, bool maybe)
+{
+  bool warned = false;
+
+  if (write && read)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+       warned = (func
+                 ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("%qD may access %E byte in a region "
+                                    "of size %E")
+                               : G_("%qD accessing %E byte in a region "
+                                    "of size %E")),
+                               (maybe
+                                ? G_ ("%qD may access %E bytes in a region "
+                                      "of size %E")
+                                : G_ ("%qD accessing %E bytes in a region "
+                                      "of size %E")),
+                              func, range[0], size)
+                 : warning_n (loc, opt, tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("may access %E byte in a region "
+                                    "of size %E")
+                               : G_("accessing %E byte in a region "
+                                    "of size %E")),
+                              (maybe
+                               ? G_("may access %E bytes in a region "
+                                    "of size %E")
+                               : G_("accessing %E bytes in a region "
+                                    "of size %E")),
+                              range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+       {
+         /* Avoid printing the upper bound if it's invalid.  */
+         warned = (func
+                   ? warning_at (loc, opt,
+                                 (maybe
+                                  ? G_("%qD may access %E or more bytes "
+                                       "in a region of size %E")
+                                  : G_("%qD accessing %E or more bytes "
+                                       "in a region of size %E")),
+                                 func, range[0], size)
+                   : warning_at (loc, opt,
+                                 (maybe
+                                  ? G_("may access %E or more bytes "
+                                       "in a region of size %E")
+                                  : G_("accessing %E or more bytes "
+                                       "in a region of size %E")),
+                                 range[0], size));
+       }
+      else
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD may access between %E and %E "
+                                     "bytes in a region of size %E")
+                                : G_("%qD accessing between %E and %E "
+                                     "bytes in a region of size %E")),
+                               func, range[0], range[1], size)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("may access between %E and %E bytes "
+                                     "in a region of size %E")
+                                : G_("accessing between %E and %E bytes "
+                                     "in a region of size %E")),
+                               range[0], range[1], size));
+      return warned;
+    }
+
+  if (write)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+       warned = (func
+                 ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("%qD may write %E byte into a region "
+                                    "of size %E")
+                               : G_("%qD writing %E byte into a region "
+                                    "of size %E overflows the destination")),
+                              (maybe
+                               ? G_("%qD may write %E bytes into a region "
+                                    "of size %E")
+                               : G_("%qD writing %E bytes into a region "
+                                    "of size %E overflows the destination")),
+                              func, range[0], size)
+                 : warning_n (loc, opt, tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("may write %E byte into a region "
+                                    "of size %E")
+                               : G_("writing %E byte into a region "
+                                    "of size %E overflows the destination")),
+                              (maybe
+                               ? G_("may write %E bytes into a region "
+                                    "of size %E")
+                               : G_("writing %E bytes into a region "
+                                    "of size %E overflows the destination")),
+                              range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+       {
+         /* Avoid printing the upper bound if it's invalid.  */
+         warned = (func
+                   ? warning_at (loc, opt,
+                                 (maybe
+                                  ? G_("%qD may write %E or more bytes "
+                                       "into a region of size %E")
+                                  : G_("%qD writing %E or more bytes "
+                                       "into a region of size %E overflows "
+                                       "the destination")),
+                                 func, range[0], size)
+                   : warning_at (loc, opt,
+                                 (maybe
+                                  ? G_("may write %E or more bytes into "
+                                       "a region of size %E")
+                                  : G_("writing %E or more bytes into "
+                                       "a region of size %E overflows "
+                                       "the destination")),
+                                 range[0], size));
+       }
+      else
+       warned = (func
+                 ? warning_at (loc, opt,
+                               (maybe
+                                ? G_("%qD may write between %E and %E bytes "
+                                     "into a region of size %E")
+                                : G_("%qD writing between %E and %E bytes "
+                                     "into a region of size %E overflows "
+                                     "the destination")),
+                               func, range[0], range[1], size)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("may write between %E and %E bytes "
+                                     "into a region of size %E")
+                                : G_("writing between %E and %E bytes "
+                                     "into a region of size %E overflows "
+                                     "the destination")),
+                               range[0], range[1], size));
+      return warned;
+    }
+
+  if (read)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+       warned = (func
+                 ? warning_n (loc, OPT_Wstringop_overread,
+                              tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("%qD may read %E byte from a region "
+                                    "of size %E")
+                               : G_("%qD reading %E byte from a region "
+                                    "of size %E")),
+                              (maybe
+                               ? G_("%qD may read %E bytes from a region "
+                                    "of size %E")
+                               : G_("%qD reading %E bytes from a region "
+                                    "of size %E")),
+                              func, range[0], size)
+                 : warning_n (loc, OPT_Wstringop_overread,
+                              tree_to_uhwi (range[0]),
+                              (maybe
+                               ? G_("may read %E byte from a region "
+                                    "of size %E")
+                               : G_("reading %E byte from a region "
+                                    "of size %E")),
+                              (maybe
+                               ? G_("may read %E bytes from a region "
+                                    "of size %E")
+                               : G_("reading %E bytes from a region "
+                                    "of size %E")),
+                              range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+       {
+         /* Avoid printing the upper bound if it's invalid.  */
+         warned = (func
+                   ? warning_at (loc, OPT_Wstringop_overread,
+                                 (maybe
+                                  ? G_("%qD may read %E or more bytes "
+                                       "from a region of size %E")
+                                  : G_("%qD reading %E or more bytes "
+                                       "from a region of size %E")),
+                                 func, range[0], size)
+                   : warning_at (loc, OPT_Wstringop_overread,
+                                 (maybe
+                                  ? G_("may read %E or more bytes "
+                                       "from a region of size %E")
+                                  : G_("reading %E or more bytes "
+                                       "from a region of size %E")),
+                                 range[0], size));
+       }
+      else
+       warned = (func
+                 ? warning_at (loc, OPT_Wstringop_overread,
+                               (maybe
+                                ? G_("%qD may read between %E and %E bytes "
+                                     "from a region of size %E")
+                                : G_("%qD reading between %E and %E bytes "
+                                     "from a region of size %E")),
+                               func, range[0], range[1], size)
+                 : warning_at (loc, opt,
+                               (maybe
+                                ? G_("may read between %E and %E bytes "
+                                     "from a region of size %E")
+                                : G_("reading between %E and %E bytes "
+                                     "from a region of size %E")),
+                               range[0], range[1], size));
+
+      if (warned)
+       suppress_warning (exp, OPT_Wstringop_overread);
+
+      return warned;
+    }
+
+  if (tree_int_cst_equal (range[0], range[1])
+      || tree_int_cst_sign_bit (range[1]))
+    warned = (func
+             ? warning_n (loc, OPT_Wstringop_overread,
+                          tree_to_uhwi (range[0]),
+                          "%qD expecting %E byte in a region of size %E",
+                          "%qD expecting %E bytes in a region of size %E",
+                          func, range[0], size)
+             : warning_n (loc, OPT_Wstringop_overread,
+                          tree_to_uhwi (range[0]),
+                          "expecting %E byte in a region of size %E",
+                          "expecting %E bytes in a region of size %E",
+                          range[0], size));
+  else if (tree_int_cst_sign_bit (range[1]))
+    {
+      /* Avoid printing the upper bound if it's invalid.  */
+      warned = (func
+               ? warning_at (loc, OPT_Wstringop_overread,
+                             "%qD expecting %E or more bytes in a region "
+                             "of size %E",
+                             func, range[0], size)
+               : warning_at (loc, OPT_Wstringop_overread,
+                             "expecting %E or more bytes in a region "
+                             "of size %E",
+                             range[0], size));
+    }
+  else
+    warned = (func
+             ? warning_at (loc, OPT_Wstringop_overread,
+                           "%qD expecting between %E and %E bytes in "
+                           "a region of size %E",
+                           func, range[0], range[1], size)
+             : warning_at (loc, OPT_Wstringop_overread,
+                           "expecting between %E and %E bytes in "
+                           "a region of size %E",
+                           range[0], range[1], size));
+
+  if (warned)
+    suppress_warning (exp, OPT_Wstringop_overread);
+
+  return warned;
+}
+
+/* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
+   by BNDRNG if nonnull and valid.  */
+
+void
+get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
+{
+  if (bound)
+    get_size_range (bound, range);
+
+  if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
+    return;
+
+  if (range[0] && TREE_CODE (range[0]) == INTEGER_CST)
+    {
+      offset_int r[] =
+       { wi::to_offset (range[0]), wi::to_offset (range[1]) };
+      if (r[0] < bndrng[0])
+       range[0] = wide_int_to_tree (sizetype, bndrng[0]);
+      if (bndrng[1] < r[1])
+       range[1] = wide_int_to_tree (sizetype, bndrng[1]);
+    }
+  else
+    {
+      range[0] = wide_int_to_tree (sizetype, bndrng[0]);
+      range[1] = wide_int_to_tree (sizetype, bndrng[1]);
+    }
+}
+
+/* Try to verify that the sizes and lengths of the arguments to a string
+   manipulation function given by EXP are within valid bounds and that
+   the operation does not lead to buffer overflow or read past the end.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or NULL otherwise.
+   SRC is the source of a copy call or NULL otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
+   (such as in strncat(d, s, N).  It specifies the upper limit on the number
+   of bytes to write.  If NULL, it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object.
+
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
+   SIZE_MAX.
+
+   WRITE is true for write accesses, READ is true for reads.  Both are
+   false for simple size checks in calls to functions that neither read
+   from nor write to the region.
+
+   When nonnull, PAD points to a more detailed description of the access.
+
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
+
+bool
+check_access (tree exp, tree dstwrite,
+             tree maxread, tree srcstr, tree dstsize,
+             access_mode mode, const access_data *pad /* = NULL */)
+{
+  /* The size of the largest object is half the address space, or
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
+
+  /* Either an approximate/minimum the length of the source string for
+     string functions or the size of the source object for raw memory
+     functions.  */
+  tree slen = NULL_TREE;
+
+  /* The range of the access in bytes; first set to the write access
+     for functions that write and then read for those that also (or
+     just) read.  */
+  tree range[2] = { NULL_TREE, NULL_TREE };
+
+  /* Set to true when the exact number of bytes written by a string
+     function like strcpy is not known and the only thing that is
+     known is that it must be at least one (for the terminating nul).  */
+  bool at_least_one = false;
+  if (srcstr)
+    {
+      /* SRCSTR is normally a pointer to string but as a special case
+        it can be an integer denoting the length of a string.  */
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
+       {
+         if (!check_nul_terminated_array (exp, srcstr, maxread))
+           return false;
+         /* Try to determine the range of lengths the source string
+            refers to.  If it can be determined and is less than
+            the upper bound given by MAXREAD add one to it for
+            the terminating nul.  Otherwise, set it to one for
+            the same reason, or to MAXREAD as appropriate.  */
+         c_strlen_data lendata = { };
+         get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
+         range[0] = lendata.minlen;
+         range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
+         if (range[0]
+             && TREE_CODE (range[0]) == INTEGER_CST
+             && TREE_CODE (range[1]) == INTEGER_CST
+             && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
+           {
+             if (maxread && tree_int_cst_le (maxread, range[0]))
+               range[0] = range[1] = maxread;
+             else
+               range[0] = fold_build2 (PLUS_EXPR, size_type_node,
+                                       range[0], size_one_node);
+
+             if (maxread && tree_int_cst_le (maxread, range[1]))
+               range[1] = maxread;
+             else if (!integer_all_onesp (range[1]))
+               range[1] = fold_build2 (PLUS_EXPR, size_type_node,
+                                       range[1], size_one_node);
+
+             slen = range[0];
+           }
+         else
+           {
+             at_least_one = true;
+             slen = size_one_node;
+           }
+       }
+      else
+       slen = srcstr;
+    }
+
+  if (!dstwrite && !maxread)
+    {
+      /* When the only available piece of data is the object size
+        there is nothing to do.  */
+      if (!slen)
+       return true;
+
+      /* Otherwise, when the length of the source sequence is known
+        (as with strlen), set DSTWRITE to it.  */
+      if (!range[0])
+       dstwrite = slen;
+    }
+
+  if (!dstsize)
+    dstsize = maxobjsize;
+
+  /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
+     if valid.  */
+  get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL);
+
+  tree func = get_callee_fndecl (exp);
+  /* Read vs write access by built-ins can be determined from the const
+     qualifiers on the pointer argument.  In the absence of attribute
+     access, non-const qualified pointer arguments to user-defined
+     functions are assumed to both read and write the objects.  */
+  const bool builtin = func ? fndecl_built_in_p (func) : false;
+
+  /* First check the number of bytes to be written against the maximum
+     object size.  */
+  if (range[0]
+      && TREE_CODE (range[0]) == INTEGER_CST
+      && tree_int_cst_lt (maxobjsize, range[0]))
+    {
+      location_t loc = EXPR_LOCATION (exp);
+      maybe_warn_for_bound (OPT_Wstringop_overflow_, loc, exp, func, range,
+                           NULL_TREE, pad);
+      return false;
+    }
+
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+  /* Next check the number of bytes to be written against the destination
+     object size.  */
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
+    {
+      if (range[0]
+         && TREE_CODE (range[0]) == INTEGER_CST
+         && ((tree_fits_uhwi_p (dstsize)
+              && tree_int_cst_lt (dstsize, range[0]))
+             || (dstwrite
+                 && tree_fits_uhwi_p (dstwrite)
+                 && tree_int_cst_lt (dstwrite, range[0]))))
+       {
+         const opt_code opt = OPT_Wstringop_overflow_;
+         if (warning_suppressed_p (exp, opt)
+             || (pad && pad->dst.ref
+                 && warning_suppressed_p (pad->dst.ref, opt)))
+           return false;
+
+         location_t loc = EXPR_LOCATION (exp);
+         bool warned = false;
+         if (dstwrite == slen && at_least_one)
+           {
+             /* This is a call to strcpy with a destination of 0 size
+                and a source of unknown length.  The call will write
+                at least one byte past the end of the destination.  */
+             warned = (func
+                       ? warning_at (loc, opt,
+                                     "%qD writing %E or more bytes into "
+                                     "a region of size %E overflows "
+                                     "the destination",
+                                     func, range[0], dstsize)
+                       : warning_at (loc, opt,
+                                     "writing %E or more bytes into "
+                                     "a region of size %E overflows "
+                                     "the destination",
+                                     range[0], dstsize));
+           }
+         else
+           {
+             const bool read
+               = mode == access_read_only || mode == access_read_write;
+             const bool write
+               = mode == access_write_only || mode == access_read_write;
+             const bool maybe = pad && pad->dst.parmarray;
+             warned = warn_for_access (loc, func, exp,
+                                       OPT_Wstringop_overflow_,
+                                       range, dstsize,
+                                       write, read && !builtin, maybe);
+           }
+
+         if (warned)
+           {
+             suppress_warning (exp, OPT_Wstringop_overflow_);
+             if (pad)
+               pad->dst.inform_access (pad->mode);
+           }
+
+         /* Return error when an overflow has been detected.  */
+         return false;
+       }
+    }
+
+  /* Check the maximum length of the source sequence against the size
+     of the destination object if known, or against the maximum size
+     of an object.  */
+  if (maxread)
+    {
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+        PAD is nonnull and BNDRNG is valid.  */
+      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
+
+      location_t loc = EXPR_LOCATION (exp);
+      tree size = dstsize;
+      if (pad && pad->mode == access_read_only)
+       size = wide_int_to_tree (sizetype, pad->src.sizrng[1]);
+
+      if (range[0] && maxread && tree_fits_uhwi_p (size))
+       {
+         if (tree_int_cst_lt (maxobjsize, range[0]))
+           {
+             maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
+                                   range, size, pad);
+             return false;
+           }
+
+         if (size != maxobjsize && tree_int_cst_lt (size, range[0]))
+           {
+             opt_code opt = (dstwrite || mode != access_read_only
+                             ? OPT_Wstringop_overflow_
+                             : OPT_Wstringop_overread);
+             maybe_warn_for_bound (opt, loc, exp, func, range, size, pad);
+             return false;
+           }
+       }
+
+      maybe_warn_nonstring_arg (func, exp);
+    }
+
+  /* Check for reading past the end of SRC.  */
+  bool overread = (slen
+                  && slen == srcstr
+                  && dstwrite
+                  && range[0]
+                  && TREE_CODE (slen) == INTEGER_CST
+                  && tree_int_cst_lt (slen, range[0]));
+  /* If none is determined try to get a better answer based on the details
+     in PAD.  */
+  if (!overread
+      && pad
+      && pad->src.sizrng[1] >= 0
+      && pad->src.offrng[0] >= 0
+      && (pad->src.offrng[1] < 0
+         || pad->src.offrng[0] <= pad->src.offrng[1]))
+    {
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+        PAD is nonnull and BNDRNG is valid.  */
+      get_size_range (maxread, range, pad ? pad->src.bndrng : NULL);
+      /* Set OVERREAD for reads starting just past the end of an object.  */
+      overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
+      range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
+      slen = size_zero_node;
+    }
+
+  if (overread)
+    {
+      const opt_code opt = OPT_Wstringop_overread;
+      if (warning_suppressed_p (exp, opt)
+         || (srcstr && warning_suppressed_p (srcstr, opt))
+         || (pad && pad->src.ref
+             && warning_suppressed_p (pad->src.ref, opt)))
+       return false;
+
+      location_t loc = EXPR_LOCATION (exp);
+      const bool read
+       = mode == access_read_only || mode == access_read_write;
+      const bool maybe = pad && pad->dst.parmarray;
+      if (warn_for_access (loc, func, exp, opt, range, slen, false, read,
+                          maybe))
+       {
+         suppress_warning (exp, opt);
+         if (pad)
+           pad->src.inform_access (access_read_only);
+       }
+      return false;
+    }
+
+  return true;
+}
+
+/* Return true if STMT is a call to an allocation function.  Unless
+   ALL_ALLOC is set, consider only functions that return dynmamically
+   allocated objects.  Otherwise return true even for all forms of
+   alloca (including VLA).  */
+
+static bool
+fndecl_alloc_p (tree fndecl, bool all_alloc)
+{
+  if (!fndecl)
+    return false;
+
+  /* A call to operator new isn't recognized as one to a built-in.  */
+  if (DECL_IS_OPERATOR_NEW_P (fndecl))
+    return true;
+
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       case BUILT_IN_ALLOCA:
+       case BUILT_IN_ALLOCA_WITH_ALIGN:
+         return all_alloc;
+       case BUILT_IN_ALIGNED_ALLOC:
+       case BUILT_IN_CALLOC:
+       case BUILT_IN_GOMP_ALLOC:
+       case BUILT_IN_MALLOC:
+       case BUILT_IN_REALLOC:
+       case BUILT_IN_STRDUP:
+       case BUILT_IN_STRNDUP:
+         return true;
+       default:
+         break;
+       }
+    }
+
+  /* A function is considered an allocation function if it's declared
+     with attribute malloc with an argument naming its associated
+     deallocation function.  */
+  tree attrs = DECL_ATTRIBUTES (fndecl);
+  if (!attrs)
+    return false;
+
+  for (tree allocs = attrs;
+       (allocs = lookup_attribute ("malloc", allocs));
+       allocs = TREE_CHAIN (allocs))
+    {
+      tree args = TREE_VALUE (allocs);
+      if (!args)
+       continue;
+
+      if (TREE_VALUE (args))
+       return true;
+    }
+
+  return false;
+}
+
+/* Return true if STMT is a call to an allocation function.  A wrapper
+   around fndecl_alloc_p.  */
+
+static bool
+gimple_call_alloc_p (gimple *stmt, bool all_alloc = false)
+{
+  return fndecl_alloc_p (gimple_call_fndecl (stmt), all_alloc);
+}
+
+/* Return true if DELC doesn't refer to an operator delete that's
+   suitable to call with a pointer returned from the operator new
+   described by NEWC.  */
+
+static bool
+new_delete_mismatch_p (const demangle_component &newc,
+                      const demangle_component &delc)
+{
+  if (newc.type != delc.type)
+    return true;
+
+  switch (newc.type)
+    {
+    case DEMANGLE_COMPONENT_NAME:
+      {
+       int len = newc.u.s_name.len;
+       const char *news = newc.u.s_name.s;
+       const char *dels = delc.u.s_name.s;
+       if (len != delc.u.s_name.len || memcmp (news, dels, len))
+         return true;
+
+       if (news[len] == 'n')
+         {
+           if (news[len + 1] == 'a')
+             return dels[len] != 'd' || dels[len + 1] != 'a';
+           if (news[len + 1] == 'w')
+             return dels[len] != 'd' || dels[len + 1] != 'l';
+         }
+       return false;
+      }
+
+    case DEMANGLE_COMPONENT_OPERATOR:
+      /* Operator mismatches are handled above.  */
+      return false;
+
+    case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+      if (newc.u.s_extended_operator.args != delc.u.s_extended_operator.args)
+       return true;
+      return new_delete_mismatch_p (*newc.u.s_extended_operator.name,
+                                   *delc.u.s_extended_operator.name);
+
+    case DEMANGLE_COMPONENT_FIXED_TYPE:
+      if (newc.u.s_fixed.accum != delc.u.s_fixed.accum
+         || newc.u.s_fixed.sat != delc.u.s_fixed.sat)
+       return true;
+      return new_delete_mismatch_p (*newc.u.s_fixed.length,
+                                   *delc.u.s_fixed.length);
+
+    case DEMANGLE_COMPONENT_CTOR:
+      if (newc.u.s_ctor.kind != delc.u.s_ctor.kind)
+       return true;
+      return new_delete_mismatch_p (*newc.u.s_ctor.name,
+                                   *delc.u.s_ctor.name);
+
+    case DEMANGLE_COMPONENT_DTOR:
+      if (newc.u.s_dtor.kind != delc.u.s_dtor.kind)
+       return true;
+      return new_delete_mismatch_p (*newc.u.s_dtor.name,
+                                   *delc.u.s_dtor.name);
+
+    case DEMANGLE_COMPONENT_BUILTIN_TYPE:
+      {
+       /* The demangler API provides no better way to compare built-in
+          types except to by comparing their demangled names. */
+       size_t nsz, dsz;
+       demangle_component *pnc = const_cast<demangle_component *>(&newc);
+       demangle_component *pdc = const_cast<demangle_component *>(&delc);
+       char *nts = cplus_demangle_print (0, pnc, 16, &nsz);
+       char *dts = cplus_demangle_print (0, pdc, 16, &dsz);
+       if (!nts != !dts)
+         return true;
+       bool mismatch = strcmp (nts, dts);
+       free (nts);
+       free (dts);
+       return mismatch;
+      }
+
+    case DEMANGLE_COMPONENT_SUB_STD:
+      if (newc.u.s_string.len != delc.u.s_string.len)
+       return true;
+      return memcmp (newc.u.s_string.string, delc.u.s_string.string,
+                    newc.u.s_string.len);
+
+    case DEMANGLE_COMPONENT_FUNCTION_PARAM:
+    case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
+      return newc.u.s_number.number != delc.u.s_number.number;
+
+    case DEMANGLE_COMPONENT_CHARACTER:
+      return newc.u.s_character.character != delc.u.s_character.character;
+
+    case DEMANGLE_COMPONENT_DEFAULT_ARG:
+    case DEMANGLE_COMPONENT_LAMBDA:
+      if (newc.u.s_unary_num.num != delc.u.s_unary_num.num)
+       return true;
+      return new_delete_mismatch_p (*newc.u.s_unary_num.sub,
+                                   *delc.u.s_unary_num.sub);
+    default:
+      break;
+    }
+
+  if (!newc.u.s_binary.left != !delc.u.s_binary.left)
+    return true;
+
+  if (!newc.u.s_binary.left)
+    return false;
+
+  if (new_delete_mismatch_p (*newc.u.s_binary.left, *delc.u.s_binary.left)
+      || !newc.u.s_binary.right != !delc.u.s_binary.right)
+    return true;
+
+  if (newc.u.s_binary.right)
+    return new_delete_mismatch_p (*newc.u.s_binary.right,
+                                 *delc.u.s_binary.right);
+  return false;
+}
+
+/* Return true if DELETE_DECL is an operator delete that's not suitable
+   to call with a pointer returned fron NEW_DECL.  */
+
+static bool
+new_delete_mismatch_p (tree new_decl, tree delete_decl)
+{
+  tree new_name = DECL_ASSEMBLER_NAME (new_decl);
+  tree delete_name = DECL_ASSEMBLER_NAME (delete_decl);
+
+  /* valid_new_delete_pair_p() returns a conservative result (currently
+     it only handles global operators).  A true result is reliable but
+     a false result doesn't necessarily mean the operators don't match.  */
+  if (valid_new_delete_pair_p (new_name, delete_name))
+    return false;
+
+  /* For anything not handled by valid_new_delete_pair_p() such as member
+     operators compare the individual demangled components of the mangled
+     name.  */
+  const char *new_str = IDENTIFIER_POINTER (new_name);
+  const char *del_str = IDENTIFIER_POINTER (delete_name);
+
+  void *np = NULL, *dp = NULL;
+  demangle_component *ndc = cplus_demangle_v3_components (new_str, 0, &np);
+  demangle_component *ddc = cplus_demangle_v3_components (del_str, 0, &dp);
+  bool mismatch = new_delete_mismatch_p (*ndc, *ddc);
+  free (np);
+  free (dp);
+  return mismatch;
+}
+
+/* ALLOC_DECL and DEALLOC_DECL are pair of allocation and deallocation
+   functions.  Return true if the latter is suitable to deallocate objects
+   allocated by calls to the former.  */
+
+static bool
+matching_alloc_calls_p (tree alloc_decl, tree dealloc_decl)
+{
+  /* Set to alloc_kind_t::builtin if ALLOC_DECL is associated with
+     a built-in deallocator.  */
+  enum class alloc_kind_t { none, builtin, user }
+  alloc_dealloc_kind = alloc_kind_t::none;
+
+  if (DECL_IS_OPERATOR_NEW_P (alloc_decl))
+    {
+      if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
+       /* Return true iff both functions are of the same array or
+          singleton form and false otherwise.  */
+       return !new_delete_mismatch_p (alloc_decl, dealloc_decl);
+
+      /* Return false for deallocation functions that are known not
+        to match.  */
+      if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
+         || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
+       return false;
+      /* Otherwise proceed below to check the deallocation function's
+        "*dealloc" attributes to look for one that mentions this operator
+        new.  */
+    }
+  else if (fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (alloc_decl))
+       {
+       case BUILT_IN_ALLOCA:
+       case BUILT_IN_ALLOCA_WITH_ALIGN:
+         return false;
+
+       case BUILT_IN_ALIGNED_ALLOC:
+       case BUILT_IN_CALLOC:
+       case BUILT_IN_GOMP_ALLOC:
+       case BUILT_IN_MALLOC:
+       case BUILT_IN_REALLOC:
+       case BUILT_IN_STRDUP:
+       case BUILT_IN_STRNDUP:
+         if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl))
+           return false;
+
+         if (fndecl_built_in_p (dealloc_decl, BUILT_IN_FREE)
+             || fndecl_built_in_p (dealloc_decl, BUILT_IN_REALLOC))
+           return true;
+
+         alloc_dealloc_kind = alloc_kind_t::builtin;
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  /* Set if DEALLOC_DECL both allocates and deallocates.  */
+  alloc_kind_t realloc_kind = alloc_kind_t::none;
+
+  if (fndecl_built_in_p (dealloc_decl, BUILT_IN_NORMAL))
+    {
+      built_in_function dealloc_code = DECL_FUNCTION_CODE (dealloc_decl);
+      if (dealloc_code == BUILT_IN_REALLOC)
+       realloc_kind = alloc_kind_t::builtin;
+
+      for (tree amats = DECL_ATTRIBUTES (alloc_decl);
+          (amats = lookup_attribute ("malloc", amats));
+          amats = TREE_CHAIN (amats))
+       {
+         tree args = TREE_VALUE (amats);
+         if (!args)
+           continue;
+
+         tree fndecl = TREE_VALUE (args);
+         if (!fndecl || !DECL_P (fndecl))
+           continue;
+
+         if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
+             && dealloc_code == DECL_FUNCTION_CODE (fndecl))
+           return true;
+       }
+    }
+
+  const bool alloc_builtin = fndecl_built_in_p (alloc_decl, BUILT_IN_NORMAL);
+  alloc_kind_t realloc_dealloc_kind = alloc_kind_t::none;
+
+  /* If DEALLOC_DECL has an internal "*dealloc" attribute scan the list
+     of its associated allocation functions for ALLOC_DECL.
+     If the corresponding ALLOC_DECL is found they're a matching pair,
+     otherwise they're not.
+     With DDATS set to the Deallocator's *Dealloc ATtributes...  */
+  for (tree ddats = DECL_ATTRIBUTES (dealloc_decl);
+       (ddats = lookup_attribute ("*dealloc", ddats));
+       ddats = TREE_CHAIN (ddats))
+    {
+      tree args = TREE_VALUE (ddats);
+      if (!args)
+       continue;
+
+      tree alloc = TREE_VALUE (args);
+      if (!alloc)
+       continue;
+
+      if (alloc == DECL_NAME (dealloc_decl))
+       realloc_kind = alloc_kind_t::user;
+
+      if (DECL_P (alloc))
+       {
+         gcc_checking_assert (fndecl_built_in_p (alloc, BUILT_IN_NORMAL));
+
+         switch (DECL_FUNCTION_CODE (alloc))
+           {
+           case BUILT_IN_ALIGNED_ALLOC:
+           case BUILT_IN_CALLOC:
+           case BUILT_IN_GOMP_ALLOC:
+           case BUILT_IN_MALLOC:
+           case BUILT_IN_REALLOC:
+           case BUILT_IN_STRDUP:
+           case BUILT_IN_STRNDUP:
+             realloc_dealloc_kind = alloc_kind_t::builtin;
+             break;
+           default:
+             break;
+           }
+
+         if (!alloc_builtin)
+           continue;
+
+         if (DECL_FUNCTION_CODE (alloc) != DECL_FUNCTION_CODE (alloc_decl))
+           continue;
+
+         return true;
+       }
+
+      if (alloc == DECL_NAME (alloc_decl))
+       return true;
+    }
+
+  if (realloc_kind == alloc_kind_t::none)
+    return false;
+
+  hash_set<tree> common_deallocs;
+  /* Special handling for deallocators.  Iterate over both the allocator's
+     and the reallocator's associated deallocator functions looking for
+     the first one in common.  If one is found, the de/reallocator is
+     a match for the allocator even though the latter isn't directly
+     associated with the former.  This simplifies declarations in system
+     headers.
+     With AMATS set to the Allocator's Malloc ATtributes,
+     and  RMATS set to Reallocator's Malloc ATtributes...  */
+  for (tree amats = DECL_ATTRIBUTES (alloc_decl),
+        rmats = DECL_ATTRIBUTES (dealloc_decl);
+       (amats = lookup_attribute ("malloc", amats))
+        || (rmats = lookup_attribute ("malloc", rmats));
+       amats = amats ? TREE_CHAIN (amats) : NULL_TREE,
+        rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
+    {
+      if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
+       if (tree adealloc = TREE_VALUE (args))
+         {
+           if (DECL_P (adealloc)
+               && fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
+             {
+               built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
+               if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
+                 {
+                   if (realloc_kind == alloc_kind_t::builtin)
+                     return true;
+                   alloc_dealloc_kind = alloc_kind_t::builtin;
+                 }
+               continue;
+             }
+
+           common_deallocs.add (adealloc);
+         }
+
+      if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
+       if (tree ddealloc = TREE_VALUE (args))
+         {
+           if (DECL_P (ddealloc)
+               && fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
+             {
+               built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
+               if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
+                 {
+                   if (alloc_dealloc_kind == alloc_kind_t::builtin)
+                     return true;
+                   realloc_dealloc_kind = alloc_kind_t::builtin;
+                 }
+               continue;
+             }
+
+           if (common_deallocs.add (ddealloc))
+             return true;
+         }
+    }
+
+  /* Succeed only if ALLOC_DECL and the reallocator DEALLOC_DECL share
+     a built-in deallocator.  */
+  return  (alloc_dealloc_kind == alloc_kind_t::builtin
+          && realloc_dealloc_kind == alloc_kind_t::builtin);
+}
+
+/* Return true if DEALLOC_DECL is a function suitable to deallocate
+   objectes allocated by the ALLOC call.  */
+
+static bool
+matching_alloc_calls_p (gimple *alloc, tree dealloc_decl)
+{
+  tree alloc_decl = gimple_call_fndecl (alloc);
+  if (!alloc_decl)
+    return true;
+
+  return matching_alloc_calls_p (alloc_decl, dealloc_decl);
+}
+
+/* Diagnose a call EXP to deallocate a pointer referenced by AREF if it
+   includes a nonzero offset.  Such a pointer cannot refer to the beginning
+   of an allocated object.  A negative offset may refer to it only if
+   the target pointer is unknown.  */
+
+static bool
+warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
+{
+  if (aref.deref || aref.offrng[0] <= 0 || aref.offrng[1] <= 0)
+    return false;
+
+  tree dealloc_decl = gimple_call_fndecl (call);
+  if (!dealloc_decl)
+    return false;
+
+  if (DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+      && !DECL_IS_REPLACEABLE_OPERATOR (dealloc_decl))
+    {
+      /* A call to a user-defined operator delete with a pointer plus offset
+        may be valid if it's returned from an unknown function (i.e., one
+        that's not operator new).  */
+      if (TREE_CODE (aref.ref) == SSA_NAME)
+       {
+         gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
+         if (is_gimple_call (def_stmt))
+           {
+             tree alloc_decl = gimple_call_fndecl (def_stmt);
+             if (!alloc_decl || !DECL_IS_OPERATOR_NEW_P (alloc_decl))
+               return false;
+           }
+       }
+    }
+
+  char offstr[80];
+  offstr[0] = '\0';
+  if (wi::fits_shwi_p (aref.offrng[0]))
+    {
+      if (aref.offrng[0] == aref.offrng[1]
+         || !wi::fits_shwi_p (aref.offrng[1]))
+       sprintf (offstr, " %lli",
+                (long long)aref.offrng[0].to_shwi ());
+      else
+       sprintf (offstr, " [%lli, %lli]",
+                (long long)aref.offrng[0].to_shwi (),
+                (long long)aref.offrng[1].to_shwi ());
+    }
+
+  if (!warning_at (loc, OPT_Wfree_nonheap_object,
+                  "%qD called on pointer %qE with nonzero offset%s",
+                  dealloc_decl, aref.ref, offstr))
+    return false;
+
+  if (DECL_P (aref.ref))
+    inform (DECL_SOURCE_LOCATION (aref.ref), "declared here");
+  else if (TREE_CODE (aref.ref) == SSA_NAME)
+    {
+      gimple *def_stmt = SSA_NAME_DEF_STMT (aref.ref);
+      if (is_gimple_call (def_stmt))
+       {
+         location_t def_loc = gimple_location (def_stmt);
+         tree alloc_decl = gimple_call_fndecl (def_stmt);
+         if (alloc_decl)
+           inform (def_loc,
+                   "returned from %qD", alloc_decl);
+         else if (tree alloc_fntype = gimple_call_fntype (def_stmt))
+           inform (def_loc,
+                   "returned from %qT", alloc_fntype);
+         else
+           inform (def_loc,  "obtained here");
+       }
+    }
+
+  return true;
+}
+
+/* Issue a warning if a deallocation function such as free, realloc,
+   or C++ operator delete is called with an argument not returned by
+   a matching allocation function such as malloc or the corresponding
+   form of C++ operatorn new.  */
+
+void
+maybe_emit_free_warning (gcall *call)
+{
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return;
+
+  unsigned argno = fndecl_dealloc_argno (fndecl);
+  if ((unsigned) gimple_call_num_args (call) <= argno)
+    return;
+
+  tree ptr = gimple_call_arg (call, argno);
+  if (integer_zerop (ptr))
+    return;
+
+  access_ref aref;
+  if (!compute_objsize (ptr, 0, &aref))
+    return;
+
+  tree ref = aref.ref;
+  if (integer_zerop (ref))
+    return;
+
+  tree dealloc_decl = fndecl;
+  location_t loc = gimple_location (call);
+
+  if (DECL_P (ref) || EXPR_P (ref))
+    {
+      /* Diagnose freeing a declared object.  */
+      if (aref.ref_declared ()
+         && warning_at (loc, OPT_Wfree_nonheap_object,
+                        "%qD called on unallocated object %qD",
+                        dealloc_decl, ref))
+       {
+         loc = (DECL_P (ref)
+                ? DECL_SOURCE_LOCATION (ref)
+                : EXPR_LOCATION (ref));
+         inform (loc, "declared here");
+         return;
+       }
+
+      /* Diagnose freeing a pointer that includes a positive offset.
+        Such a pointer cannot refer to the beginning of an allocated
+        object.  A negative offset may refer to it.  */
+      if (aref.sizrng[0] != aref.sizrng[1]
+         && warn_dealloc_offset (loc, call, aref))
+       return;
+    }
+  else if (CONSTANT_CLASS_P (ref))
+    {
+      if (warning_at (loc, OPT_Wfree_nonheap_object,
+                     "%qD called on a pointer to an unallocated "
+                     "object %qE", dealloc_decl, ref))
+       {
+         if (TREE_CODE (ptr) == SSA_NAME)
+           {
+             gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
+             if (is_gimple_assign (def_stmt))
+               {
+                 location_t loc = gimple_location (def_stmt);
+                 inform (loc, "assigned here");
+               }
+           }
+         return;
+       }
+    }
+  else if (TREE_CODE (ref) == SSA_NAME)
+    {
+      /* Also warn if the pointer argument refers to the result
+        of an allocation call like alloca or VLA.  */
+      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+      if (is_gimple_call (def_stmt))
+       {
+         bool warned = false;
+         if (gimple_call_alloc_p (def_stmt))
+           {
+             if (matching_alloc_calls_p (def_stmt, dealloc_decl))
+               {
+                 if (warn_dealloc_offset (loc, call, aref))
+                   return;
+               }
+             else
+               {
+                 tree alloc_decl = gimple_call_fndecl (def_stmt);
+                 const opt_code opt =
+                   (DECL_IS_OPERATOR_NEW_P (alloc_decl)
+                    || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+                    ? OPT_Wmismatched_new_delete
+                    : OPT_Wmismatched_dealloc);
+                 warned = warning_at (loc, opt,
+                                      "%qD called on pointer returned "
+                                      "from a mismatched allocation "
+                                      "function", dealloc_decl);
+               }
+           }
+         else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
+                  || gimple_call_builtin_p (def_stmt,
+                                            BUILT_IN_ALLOCA_WITH_ALIGN))
+           warned = warning_at (loc, OPT_Wfree_nonheap_object,
+                                "%qD called on pointer to "
+                                "an unallocated object",
+                                dealloc_decl);
+         else if (warn_dealloc_offset (loc, call, aref))
+           return;
+
+         if (warned)
+           {
+             tree fndecl = gimple_call_fndecl (def_stmt);
+             inform (gimple_location (def_stmt),
+                     "returned from %qD", fndecl);
+             return;
+           }
+       }
+      else if (gimple_nop_p (def_stmt))
+       {
+         ref = SSA_NAME_VAR (ref);
+         /* Diagnose freeing a pointer that includes a positive offset.  */
+         if (TREE_CODE (ref) == PARM_DECL
+             && !aref.deref
+             && aref.sizrng[0] != aref.sizrng[1]
+             && aref.offrng[0] > 0 && aref.offrng[1] > 0
+             && warn_dealloc_offset (loc, call, aref))
+           return;
+       }
+    }
+}
+
+namespace {
+
+const pass_data pass_data_waccess = {
+  GIMPLE_PASS,
+  "waccess",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg, /* properties_required  */
+  0,       /* properties_provided  */
+  0,       /* properties_destroyed  */
+  0,       /* properties_start */
+  0,       /* properties_finish */
+};
+
+/* Pass to detect invalid accesses.  */
+class pass_waccess : public gimple_opt_pass
+{
+ public:
+  pass_waccess (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_waccess, ctxt), m_ranger ()
+    { }
+
+  opt_pass *clone () { return new pass_waccess (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *);
+
+  void check (basic_block);
+  void check (gcall *);
+
+private:
+  gimple_ranger *m_ranger;
+};
+
+/* Return true when any checks performed by the pass are enabled.  */
+
+bool
+pass_waccess::gate (function *)
+{
+  return (warn_free_nonheap_object
+         || warn_mismatched_alloc
+         || warn_mismatched_new_delete);
+}
+
+/* Check call STMT for invalid accesses.  */
+
+void
+pass_waccess::check (gcall *stmt)
+{
+  maybe_emit_free_warning (stmt);
+}
+
+/* Check basic block BB for invalid accesses.  */
+
+void
+pass_waccess::check (basic_block bb)
+{
+  /* Iterate over statements, looking for function calls.  */
+  for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+    {
+      if (gcall *call = dyn_cast <gcall *> (gsi_stmt (si)))
+       check (call);
+    }
+}
+
+/* Check function FUN for invalid accesses.  */
+
+unsigned
+pass_waccess::execute (function *fun)
+{
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    check (bb);
+
+  return 0;
+}
+
+}   // namespace
+
+/* Return a new instance of the pass.  */
+
+gimple_opt_pass *
+make_pass_warn_access (gcc::context *ctxt)
+{
+  return new pass_waccess (ctxt);
+}
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
new file mode 100644 (file)
index 0000000..6197574
--- /dev/null
@@ -0,0 +1,37 @@
+/* Pass to detect and issue warnings for invalid accesses, including
+   invalid or mismatched allocation/deallocation calls.
+
+   Copyright (C) 2020-2021 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_GIMPLE_SSA_WARN_ACCESS_H
+#define GCC_GIMPLE_SSA_WARN_ACCESS_H
+
+extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
+extern void warn_string_no_nul (location_t, tree, const char *, tree,
+                               tree, tree = NULL_TREE, bool = false,
+                               const wide_int[2] = NULL);
+extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
+extern void get_size_range (tree, tree[2], const offset_int[2]);
+
+class access_data;
+extern bool maybe_warn_for_bound (opt_code, location_t, tree, tree,
+                                 tree[2], tree, const access_data * = NULL);
+
+#endif   // GCC_GIMPLE_SSA_WARN_ACCESS_H
index efb8db98393acb2bbd2a281d8722f5351f90d94c..404acb03195bab76a7976eba7196d8d46669d600 100644 (file)
@@ -26,7 +26,7 @@
 #include "tree.h"
 #include "gimple.h"
 #include "tree-pass.h"
-#include "builtins.h"
+#include "pointer-query.h"
 #include "ssa.h"
 #include "gimple-pretty-print.h"
 #include "gimple-ssa-warn-restrict.h"
index f5d88a61b0e847f58cae9c92ca614f9d9ae250c0..e2858368b7dd73277da1858369bead44574d00b1 100644 (file)
@@ -419,6 +419,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_gimple_isel);
   NEXT_PASS (pass_cleanup_cfg_post_optimizing);
   NEXT_PASS (pass_warn_function_noreturn);
+  NEXT_PASS (pass_warn_access);
 
   NEXT_PASS (pass_expand);
 
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
new file mode 100644 (file)
index 0000000..bc7cac0
--- /dev/null
@@ -0,0 +1,1895 @@
+/* Definitions of the pointer_query and related classes.
+
+   Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "gimple.h"
+#include "predict.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "tree-vrp.h"
+#include "tree-ssanames.h"
+#include "expmed.h"
+#include "optabs.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "alias.h"
+#include "fold-const.h"
+#include "fold-const-call.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "varasm.h"
+#include "tree-object-size.h"
+#include "tree-ssa-strlen.h"
+#include "realmpfr.h"
+#include "cfgrtl.h"
+#include "except.h"
+#include "dojump.h"
+#include "explow.h"
+#include "stmt.h"
+#include "expr.h"
+#include "libfuncs.h"
+#include "output.h"
+#include "typeclass.h"
+#include "langhooks.h"
+#include "value-prof.h"
+#include "builtins.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "asan.h"
+#include "internal-fn.h"
+#include "case-cfn-macros.h"
+#include "gimple-fold.h"
+#include "intl.h"
+#include "tree-dfa.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-ssa-live.h"
+#include "tree-outof-ssa.h"
+#include "attr-fnspec.h"
+#include "gimple-range.h"
+
+#include "pointer-query.h"
+
+static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
+                              pointer_query *);
+
+/* Wrapper around the wide_int overload of get_range that accepts
+   offset_int instead.  For middle end expressions returns the same
+   result.  For a subset of nonconstamt expressions emitted by the front
+   end determines a more precise range than would be possible otherwise.  */
+
+static bool
+get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
+{
+  offset_int add = 0;
+  if (TREE_CODE (x) == PLUS_EXPR)
+    {
+      /* Handle constant offsets in pointer addition expressions seen
+        n the front end IL.  */
+      tree op = TREE_OPERAND (x, 1);
+      if (TREE_CODE (op) == INTEGER_CST)
+       {
+         op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
+         add = wi::to_offset (op);
+         x = TREE_OPERAND (x, 0);
+       }
+    }
+
+  if (TREE_CODE (x) == NOP_EXPR)
+    /* Also handle conversions to sizetype seen in the front end IL.  */
+    x = TREE_OPERAND (x, 0);
+
+  tree type = TREE_TYPE (x);
+  if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type))
+    return false;
+
+   if (TREE_CODE (x) != INTEGER_CST
+      && TREE_CODE (x) != SSA_NAME)
+    {
+      if (TYPE_UNSIGNED (type)
+         && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
+       type = signed_type_for (type);
+
+      r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
+      r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
+      return x;
+    }
+
+  wide_int wr[2];
+  if (!get_range (x, stmt, wr, rvals))
+    return false;
+
+  signop sgn = SIGNED;
+  /* Only convert signed integers or unsigned sizetype to a signed
+     offset and avoid converting large positive values in narrower
+     types to negative offsets.  */
+  if (TYPE_UNSIGNED (type)
+      && wr[0].get_precision () < TYPE_PRECISION (sizetype))
+    sgn = UNSIGNED;
+
+  r[0] = offset_int::from (wr[0], sgn);
+  r[1] = offset_int::from (wr[1], sgn);
+  return true;
+}
+
+/* Return the argument that the call STMT to a built-in function returns
+   or null if it doesn't.  On success, set OFFRNG[] to the range of offsets
+   from the argument reflected in the value returned by the built-in if it
+   can be determined, otherwise to 0 and HWI_M1U respectively.  Set
+   *PAST_END for functions like mempcpy that might return a past the end
+   pointer (most functions return a dereferenceable pointer to an existing
+   element of an array).  */
+
+static tree
+gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
+                         range_query *rvals)
+{
+  /* Clear and set below for the rare function(s) that might return
+     a past-the-end pointer.  */
+  *past_end = false;
+
+  {
+    /* Check for attribute fn spec to see if the function returns one
+       of its arguments.  */
+    attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
+    unsigned int argno;
+    if (fnspec.returns_arg (&argno))
+      {
+       /* Functions return the first argument (not a range).  */
+       offrng[0] = offrng[1] = 0;
+       return gimple_call_arg (stmt, argno);
+      }
+  }
+
+  if (gimple_call_num_args (stmt) < 1)
+    return NULL_TREE;
+
+  tree fn = gimple_call_fndecl (stmt);
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      /* See if this is a call to placement new.  */
+      if (!fn
+         || !DECL_IS_OPERATOR_NEW_P (fn)
+         || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fn))
+       return NULL_TREE;
+
+      /* Check the mangling, keeping in mind that operator new takes
+        a size_t which could be unsigned int or unsigned long.  */
+      tree fname = DECL_ASSEMBLER_NAME (fn);
+      if (!id_equal (fname, "_ZnwjPv")       // ordinary form
+         && !id_equal (fname, "_ZnwmPv")    // ordinary form
+         && !id_equal (fname, "_ZnajPv")    // array form
+         && !id_equal (fname, "_ZnamPv"))   // array form
+       return NULL_TREE;
+
+      if (gimple_call_num_args (stmt) != 2)
+       return NULL_TREE;
+
+      /* Allocation functions return a pointer to the beginning.  */
+      offrng[0] = offrng[1] = 0;
+      return gimple_call_arg (stmt, 1);
+    }
+
+  switch (DECL_FUNCTION_CODE (fn))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      /* Functions return the first argument (not a range).  */
+      offrng[0] = offrng[1] = 0;
+      return gimple_call_arg (stmt, 0);
+
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+      {
+       /* The returned pointer is in a range constrained by the smaller
+          of the upper bound of the size argument and the source object
+          size.  */
+       offrng[0] = 0;
+       offrng[1] = HOST_WIDE_INT_M1U;
+       tree off = gimple_call_arg (stmt, 2);
+       bool off_valid = get_offset_range (off, stmt, offrng, rvals);
+       if (!off_valid || offrng[0] != offrng[1])
+         {
+           /* If the offset is either indeterminate or in some range,
+              try to constrain its upper bound to at most the size
+              of the source object.  */
+           access_ref aref;
+           tree src = gimple_call_arg (stmt, 1);
+           if (compute_objsize (src, 1, &aref, rvals)
+               && aref.sizrng[1] < offrng[1])
+             offrng[1] = aref.sizrng[1];
+         }
+
+       /* Mempcpy may return a past-the-end pointer.  */
+       *past_end = true;
+       return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_MEMCHR:
+      {
+       tree off = gimple_call_arg (stmt, 2);
+       if (get_offset_range (off, stmt, offrng, rvals))
+         offrng[1] -= 1;
+       else
+         offrng[1] = HOST_WIDE_INT_M1U;
+
+       offrng[0] = 0;
+       return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_STRCHR:
+    case BUILT_IN_STRRCHR:
+    case BUILT_IN_STRSTR:
+      offrng[0] = 0;
+      offrng[1] = HOST_WIDE_INT_M1U;
+      return gimple_call_arg (stmt, 0);
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+      {
+       access_ref aref;
+       tree src = gimple_call_arg (stmt, 1);
+       if (compute_objsize (src, 1, &aref, rvals))
+         offrng[1] = aref.sizrng[1] - 1;
+       else
+         offrng[1] = HOST_WIDE_INT_M1U;
+       
+       offrng[0] = 0;
+       return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+      {
+       /* The returned pointer is in a range between the first argument
+          and it plus the smaller of the upper bound of the size argument
+          and the source object size.  */
+       offrng[1] = HOST_WIDE_INT_M1U;
+       tree off = gimple_call_arg (stmt, 2);
+       if (!get_offset_range (off, stmt, offrng, rvals)
+           || offrng[0] != offrng[1])
+         {
+           /* If the offset is either indeterminate or in some range,
+              try to constrain its upper bound to at most the size
+              of the source object.  */
+           access_ref aref;
+           tree src = gimple_call_arg (stmt, 1);
+           if (compute_objsize (src, 1, &aref, rvals)
+               && aref.sizrng[1] < offrng[1])
+             offrng[1] = aref.sizrng[1];
+         }
+
+       /* When the source is the empty string the returned pointer is
+          a copy of the argument.  Otherwise stpcpy can also return
+          a past-the-end pointer.  */
+       offrng[0] = 0;
+       *past_end = true;
+       return gimple_call_arg (stmt, 0);
+      }
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+/* If STMT is a call to an allocation function, returns the constant
+   maximum size of the object allocated by the call represented as
+   sizetype.  If nonnull, sets RNG1[] to the range of the size.
+   When nonnull, uses RVALS for range information, otherwise gets global
+   range info.
+   Returns null when STMT is not a call to a valid allocation function.  */
+
+tree
+gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
+                       range_query * /* = NULL */)
+{
+  if (!stmt || !is_gimple_call (stmt))
+    return NULL_TREE;
+
+  tree allocfntype;
+  if (tree fndecl = gimple_call_fndecl (stmt))
+    allocfntype = TREE_TYPE (fndecl);
+  else
+    allocfntype = gimple_call_fntype (stmt);
+
+  if (!allocfntype)
+    return NULL_TREE;
+
+  unsigned argidx1 = UINT_MAX, argidx2 = UINT_MAX;
+  tree at = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (allocfntype));
+  if (!at)
+    {
+      if (!gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+       return NULL_TREE;
+
+      argidx1 = 0;
+    }
+
+  unsigned nargs = gimple_call_num_args (stmt);
+
+  if (argidx1 == UINT_MAX)
+    {
+      tree atval = TREE_VALUE (at);
+      if (!atval)
+       return NULL_TREE;
+
+      argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
+      if (nargs <= argidx1)
+       return NULL_TREE;
+
+      atval = TREE_CHAIN (atval);
+      if (atval)
+       {
+         argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval)) - 1;
+         if (nargs <= argidx2)
+           return NULL_TREE;
+       }
+    }
+
+  tree size = gimple_call_arg (stmt, argidx1);
+
+  wide_int rng1_buf[2];
+  /* If RNG1 is not set, use the buffer.  */
+  if (!rng1)
+    rng1 = rng1_buf;
+
+  /* Use maximum precision to avoid overflow below.  */
+  const int prec = ADDR_MAX_PRECISION;
+
+  {
+    tree r[2];
+    /* Determine the largest valid range size, including zero.  */
+    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng1[0] = wi::to_wide (r[0], prec);
+    rng1[1] = wi::to_wide (r[1], prec);
+  }
+
+  if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
+    return fold_convert (sizetype, size);
+
+  /* To handle ranges do the math in wide_int and return the product
+     of the upper bounds as a constant.  Ignore anti-ranges.  */
+  tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
+  wide_int rng2[2];
+  {
+    tree r[2];
+      /* As above, use the full non-negative range on failure.  */
+    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng2[0] = wi::to_wide (r[0], prec);
+    rng2[1] = wi::to_wide (r[1], prec);
+  }
+
+  /* Compute products of both bounds for the caller but return the lesser
+     of SIZE_MAX and the product of the upper bounds as a constant.  */
+  rng1[0] = rng1[0] * rng2[0];
+  rng1[1] = rng1[1] * rng2[1];
+
+  const tree size_max = TYPE_MAX_VALUE (sizetype);
+  if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
+    {
+      rng1[1] = wi::to_wide (size_max, prec);
+      return size_max;
+    }
+
+  return wide_int_to_tree (sizetype, rng1[1]);
+}
+
+/* For an access to an object referenced to by the function parameter PTR
+   of pointer type, and set RNG[] to the range of sizes of the object
+   obtainedfrom the attribute access specification for the current function.
+   Set STATIC_ARRAY if the array parameter has been declared [static].
+   Return the function parameter on success and null otherwise.  */
+
+tree
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+                       bool *static_array /* = NULL */)
+{
+  /* For a function argument try to determine the byte size of the array
+     from the current function declaratation (e.g., attribute access or
+     related).  */
+  tree var = SSA_NAME_VAR (ptr);
+  if (TREE_CODE (var) != PARM_DECL)
+    return NULL_TREE;
+
+  const unsigned prec = TYPE_PRECISION (sizetype);
+
+  rdwr_map rdwr_idx;
+  attr_access *access = get_parm_access (rdwr_idx, var);
+  if (!access)
+    return NULL_TREE;
+
+  if (access->sizarg != UINT_MAX)
+    {
+      /* TODO: Try to extract the range from the argument based on
+        those of subsequent assertions or based on known calls to
+        the current function.  */
+      return NULL_TREE;
+    }
+
+  if (!access->minsize)
+    return NULL_TREE;
+
+  /* Only consider ordinary array bound at level 2 (or above if it's
+     ever added).  */
+  if (warn_array_parameter < 2 && !access->static_p)
+    return NULL_TREE;
+
+  if (static_array)
+    *static_array = access->static_p;
+
+  rng[0] = wi::zero (prec);
+  rng[1] = wi::uhwi (access->minsize, prec);
+  /* Multiply the array bound encoded in the attribute by the size
+     of what the pointer argument to which it decays points to.  */
+  tree eltype = TREE_TYPE (TREE_TYPE (ptr));
+  tree size = TYPE_SIZE_UNIT (eltype);
+  if (!size || TREE_CODE (size) != INTEGER_CST)
+    return NULL_TREE;
+
+  rng[1] *= wi::to_wide (size, prec);
+  return var;
+}
+
+access_ref::access_ref (tree bound /* = NULL_TREE */,
+                       bool minaccess /* = false */)
+: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
+  base0 (true), parmarray ()
+{
+  /* Set to valid.  */
+  offrng[0] = offrng[1] = 0;
+  offmax[0] = offmax[1] = 0;
+  /* Invalidate.   */
+  sizrng[0] = sizrng[1] = -1;
+
+  /* Set the default bounds of the access and adjust below.  */
+  bndrng[0] = minaccess ? 1 : 0;
+  bndrng[1] = HOST_WIDE_INT_M1U;
+
+  /* When BOUND is nonnull and a range can be extracted from it,
+     set the bounds of the access to reflect both it and MINACCESS.
+     BNDRNG[0] is the size of the minimum access.  */
+  tree rng[2];
+  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
+    {
+      bndrng[0] = wi::to_offset (rng[0]);
+      bndrng[1] = wi::to_offset (rng[1]);
+      bndrng[0] = bndrng[0] > 0 && minaccess ? 1 : 0;
+    }
+}
+
+/* Return the PHI node REF refers to or null if it doesn't.  */
+
+gphi *
+access_ref::phi () const
+{
+  if (!ref || TREE_CODE (ref) != SSA_NAME)
+    return NULL;
+
+  gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+  if (gimple_code (def_stmt) != GIMPLE_PHI)
+    return NULL;
+
+  return as_a <gphi *> (def_stmt);
+}
+
+/* Determine and return the largest object to which *THIS.  If *THIS
+   refers to a PHI and PREF is nonnull, fill *PREF with the details
+   of the object determined by compute_objsize(ARG, OSTYPE) for each
+   PHI argument ARG.  */
+
+tree
+access_ref::get_ref (vec<access_ref> *all_refs,
+                    access_ref *pref /* = NULL */,
+                    int ostype /* = 1 */,
+                    ssa_name_limit_t *psnlim /* = NULL */,
+                    pointer_query *qry /* = NULL */) const
+{
+  gphi *phi_stmt = this->phi ();
+  if (!phi_stmt)
+    return ref;
+
+  /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might
+     cause unbounded recursion.  */
+  ssa_name_limit_t snlim_buf;
+  if (!psnlim)
+    psnlim = &snlim_buf;
+
+  if (!psnlim->visit_phi (ref))
+    return NULL_TREE;
+
+  /* Reflects the range of offsets of all PHI arguments refer to the same
+     object (i.e., have the same REF).  */
+  access_ref same_ref;
+  /* The conservative result of the PHI reflecting the offset and size
+     of the largest PHI argument, regardless of whether or not they all
+     refer to the same object.  */
+  pointer_query empty_qry;
+  if (!qry)
+    qry = &empty_qry;
+
+  access_ref phi_ref;
+  if (pref)
+    {
+      phi_ref = *pref;
+      same_ref = *pref;
+    }
+
+  /* Set if any argument is a function array (or VLA) parameter not
+     declared [static].  */
+  bool parmarray = false;
+  /* The size of the smallest object referenced by the PHI arguments.  */
+  offset_int minsize = 0;
+  const offset_int maxobjsize = wi::to_offset (max_object_size ());
+  /* The offset of the PHI, not reflecting those of its arguments.  */
+  const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
+
+  const unsigned nargs = gimple_phi_num_args (phi_stmt);
+  for (unsigned i = 0; i < nargs; ++i)
+    {
+      access_ref phi_arg_ref;
+      tree arg = gimple_phi_arg_def (phi_stmt, i);
+      if (!compute_objsize_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
+         || phi_arg_ref.sizrng[0] < 0)
+       /* A PHI with all null pointer arguments.  */
+       return NULL_TREE;
+
+      /* Add PREF's offset to that of the argument.  */
+      phi_arg_ref.add_offset (orng[0], orng[1]);
+      if (TREE_CODE (arg) == SSA_NAME)
+       qry->put_ref (arg, phi_arg_ref);
+
+      if (all_refs)
+       all_refs->safe_push (phi_arg_ref);
+
+      const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
+                                  || phi_arg_ref.sizrng[1] != maxobjsize);
+
+      parmarray |= phi_arg_ref.parmarray;
+
+      const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
+
+      if (phi_ref.sizrng[0] < 0)
+       {
+         if (!nullp)
+           same_ref = phi_arg_ref;
+         phi_ref = phi_arg_ref;
+         if (arg_known_size)
+           minsize = phi_arg_ref.sizrng[0];
+         continue;
+       }
+
+      const bool phi_known_size = (phi_ref.sizrng[0] != 0
+                                  || phi_ref.sizrng[1] != maxobjsize);
+
+      if (phi_known_size && phi_arg_ref.sizrng[0] < minsize)
+       minsize = phi_arg_ref.sizrng[0];
+
+      /* Disregard null pointers in PHIs with two or more arguments.
+        TODO: Handle this better!  */
+      if (nullp)
+       continue;
+
+      /* Determine the amount of remaining space in the argument.  */
+      offset_int argrem[2];
+      argrem[1] = phi_arg_ref.size_remaining (argrem);
+
+      /* Determine the amount of remaining space computed so far and
+        if the remaining space in the argument is more use it instead.  */
+      offset_int phirem[2];
+      phirem[1] = phi_ref.size_remaining (phirem);
+
+      if (phi_arg_ref.ref != same_ref.ref)
+       same_ref.ref = NULL_TREE;
+
+      if (phirem[1] < argrem[1]
+         || (phirem[1] == argrem[1]
+             && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1]))
+       /* Use the argument with the most space remaining as the result,
+          or the larger one if the space is equal.  */
+       phi_ref = phi_arg_ref;
+
+      /* Set SAME_REF.OFFRNG to the maximum range of all arguments.  */
+      if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
+       same_ref.offrng[0] = phi_arg_ref.offrng[0];
+      if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
+       same_ref.offrng[1] = phi_arg_ref.offrng[1];
+    }
+
+  if (!same_ref.ref && same_ref.offrng[0] != 0)
+    /* Clear BASE0 if not all the arguments refer to the same object and
+       if not all their offsets are zero-based.  This allows the final
+       PHI offset to out of bounds for some arguments but not for others
+       (or negative even of all the arguments are BASE0), which is overly
+       permissive.  */
+    phi_ref.base0 = false;
+
+  if (same_ref.ref)
+    phi_ref = same_ref;
+  else
+    {
+      /* Replace the lower bound of the largest argument with the size
+        of the smallest argument, and set PARMARRAY if any argument
+        was one.  */
+      phi_ref.sizrng[0] = minsize;
+      phi_ref.parmarray = parmarray;
+    }
+
+  if (phi_ref.sizrng[0] < 0)
+    {
+      /* Fail if none of the PHI's arguments resulted in updating PHI_REF
+        (perhaps because they have all been already visited by prior
+        recursive calls).  */
+      psnlim->leave_phi (ref);
+      return NULL_TREE;
+    }
+
+  /* Avoid changing *THIS.  */
+  if (pref && pref != this)
+    *pref = phi_ref;
+
+  psnlim->leave_phi (ref);
+
+  return phi_ref.ref;
+}
+
+/* Return the maximum amount of space remaining and if non-null, set
+   argument to the minimum.  */
+
+offset_int
+access_ref::size_remaining (offset_int *pmin /* = NULL */) const
+{
+  offset_int minbuf;
+  if (!pmin)
+    pmin = &minbuf;
+
+  /* add_offset() ensures the offset range isn't inverted.  */
+  gcc_checking_assert (offrng[0] <= offrng[1]);
+
+  if (base0)
+    {
+      /* The offset into referenced object is zero-based (i.e., it's
+        not referenced by a pointer into middle of some unknown object).  */
+      if (offrng[0] < 0 && offrng[1] < 0)
+       {
+         /* If the offset is negative the remaining size is zero.  */
+         *pmin = 0;
+         return 0;
+       }
+
+      if (sizrng[1] <= offrng[0])
+       {
+         /* If the starting offset is greater than or equal to the upper
+            bound on the size of the object, the space remaining is zero.
+            As a special case, if it's equal, set *PMIN to -1 to let
+            the caller know the offset is valid and just past the end.  */
+         *pmin = sizrng[1] == offrng[0] ? -1 : 0;
+         return 0;
+       }
+
+      /* Otherwise return the size minus the lower bound of the offset.  */
+      offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+      *pmin = sizrng[0] - or0;
+      return sizrng[1] - or0;
+    }
+
+  /* The offset to the referenced object isn't zero-based (i.e., it may
+     refer to a byte other than the first.  The size of such an object
+     is constrained only by the size of the address space (the result
+     of max_object_size()).  */
+  if (sizrng[1] <= offrng[0])
+    {
+      *pmin = 0;
+      return 0;
+    }
+
+  offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+  *pmin = sizrng[0] - or0;
+  return sizrng[1] - or0;
+}
+
+/* Return true if the offset and object size are in range for SIZE.  */
+
+bool
+access_ref::offset_in_range (const offset_int &size) const
+{
+  if (size_remaining () < size)
+    return false;
+
+  if (base0)
+    return offmax[0] >= 0 && offmax[1] <= sizrng[1];
+
+  offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+  return offmax[0] > -maxoff && offmax[1] < maxoff;
+}
+
+/* Add the range [MIN, MAX] to the offset range.  For known objects (with
+   zero-based offsets) at least one of whose offset's bounds is in range,
+   constrain the other (or both) to the bounds of the object (i.e., zero
+   and the upper bound of its size).  This improves the quality of
+   diagnostics.  */
+
+void access_ref::add_offset (const offset_int &min, const offset_int &max)
+{
+  if (min <= max)
+    {
+      /* To add an ordinary range just add it to the bounds.  */
+      offrng[0] += min;
+      offrng[1] += max;
+    }
+  else if (!base0)
+    {
+      /* To add an inverted range to an offset to an unknown object
+        expand it to the maximum.  */
+      add_max_offset ();
+      return;
+    }
+  else
+    {
+      /* To add an inverted range to an offset to an known object set
+        the upper bound to the maximum representable offset value
+        (which may be greater than MAX_OBJECT_SIZE).
+        The lower bound is either the sum of the current offset and
+        MIN when abs(MAX) is greater than the former, or zero otherwise.
+        Zero because then then inverted range includes the negative of
+        the lower bound.  */
+      offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      offrng[1] = maxoff;
+
+      if (max >= 0)
+       {
+         offrng[0] = 0;
+         if (offmax[0] > 0)
+           offmax[0] = 0;
+         return;
+       }
+
+      offset_int absmax = wi::abs (max);
+      if (offrng[0] < absmax)
+       {
+         offrng[0] += min;
+         /* Cap the lower bound at the upper (set to MAXOFF above)
+            to avoid inadvertently recreating an inverted range.  */
+         if (offrng[1] < offrng[0])
+           offrng[0] = offrng[1];
+       }
+      else
+       offrng[0] = 0;
+    }
+
+  /* Set the minimum and maximmum computed so far. */
+  if (offrng[1] < 0 && offrng[1] < offmax[0])
+    offmax[0] = offrng[1];
+  if (offrng[0] > 0 && offrng[0] > offmax[1])
+    offmax[1] = offrng[0];
+
+  if (!base0)
+    return;
+
+  /* When referencing a known object check to see if the offset computed
+     so far is in bounds... */
+  offset_int remrng[2];
+  remrng[1] = size_remaining (remrng);
+  if (remrng[1] > 0 || remrng[0] < 0)
+    {
+      /* ...if so, constrain it so that neither bound exceeds the size of
+        the object.  Out of bounds offsets are left unchanged, and, for
+        better or worse, become in bounds later.  They should be detected
+        and diagnosed at the point they first become invalid by
+        -Warray-bounds.  */
+      if (offrng[0] < 0)
+       offrng[0] = 0;
+      if (offrng[1] > sizrng[1])
+       offrng[1] = sizrng[1];
+    }
+}
+
+/* Issue one inform message describing each target of an access REF.
+   WRITE is set for a write access and clear for a read access.  */
+
+void
+access_ref::inform_access (access_mode mode) const
+{
+  const access_ref &aref = *this;
+  if (!aref.ref)
+    return;
+
+  if (aref.phi ())
+    {
+      /* Set MAXREF to refer to the largest object and fill ALL_REFS
+        with data for all objects referenced by the PHI arguments.  */
+      access_ref maxref;
+      auto_vec<access_ref> all_refs;
+      if (!get_ref (&all_refs, &maxref))
+       return;
+
+      /* Except for MAXREF, the rest of the arguments' offsets need not
+        reflect one added to the PHI itself.  Determine the latter from
+        MAXREF on which the result is based.  */
+      const offset_int orng[] =
+       {
+         offrng[0] - maxref.offrng[0],
+         wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
+       };
+
+      /* Add the final PHI's offset to that of each of the arguments
+        and recurse to issue an inform message for it.  */
+      for (unsigned i = 0; i != all_refs.length (); ++i)
+       {
+         /* Skip any PHIs; those could lead to infinite recursion.  */
+         if (all_refs[i].phi ())
+           continue;
+
+         all_refs[i].add_offset (orng[0], orng[1]);
+         all_refs[i].inform_access (mode);
+       }
+      return;
+    }
+
+  /* Convert offset range and avoid including a zero range since it
+     isn't necessarily meaningful.  */
+  HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
+  HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
+  HOST_WIDE_INT minoff;
+  HOST_WIDE_INT maxoff = diff_max;
+  if (wi::fits_shwi_p (aref.offrng[0]))
+    minoff = aref.offrng[0].to_shwi ();
+  else
+    minoff = aref.offrng[0] < 0 ? diff_min : diff_max;
+
+  if (wi::fits_shwi_p (aref.offrng[1]))
+    maxoff = aref.offrng[1].to_shwi ();
+
+  if (maxoff <= diff_min || maxoff >= diff_max)
+    /* Avoid mentioning an upper bound that's equal to or in excess
+       of the maximum of ptrdiff_t.  */
+    maxoff = minoff;
+
+  /* Convert size range and always include it since all sizes are
+     meaningful. */
+  unsigned long long minsize = 0, maxsize = 0;
+  if (wi::fits_shwi_p (aref.sizrng[0])
+      && wi::fits_shwi_p (aref.sizrng[1]))
+    {
+      minsize = aref.sizrng[0].to_shwi ();
+      maxsize = aref.sizrng[1].to_shwi ();
+    }
+
+  /* SIZRNG doesn't necessarily have the same range as the allocation
+     size determined by gimple_call_alloc_size ().  */
+  char sizestr[80];
+  if (minsize == maxsize)
+    sprintf (sizestr, "%llu", minsize);
+  else
+    sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+
+  char offstr[80];
+  if (minoff == 0
+      && (maxoff == 0 || aref.sizrng[1] <= maxoff))
+    offstr[0] = '\0';
+  else if (minoff == maxoff)
+    sprintf (offstr, "%lli", (long long) minoff);
+  else
+    sprintf (offstr, "[%lli, %lli]", (long long) minoff, (long long) maxoff);
+
+  location_t loc = UNKNOWN_LOCATION;
+
+  tree ref = this->ref;
+  tree allocfn = NULL_TREE;
+  if (TREE_CODE (ref) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (ref);
+      if (is_gimple_call (stmt))
+       {
+         loc = gimple_location (stmt);
+         if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+           {
+             /* Strip the SSA_NAME suffix from the variable name and
+                recreate an identifier with the VLA's original name.  */
+             ref = gimple_call_lhs (stmt);
+             if (SSA_NAME_IDENTIFIER (ref))
+               {
+                 ref = SSA_NAME_IDENTIFIER (ref);
+                 const char *id = IDENTIFIER_POINTER (ref);
+                 size_t len = strcspn (id, ".$");
+                 if (!len)
+                   len = strlen (id);
+                 ref = get_identifier_with_length (id, len);
+               }
+           }
+         else
+           {
+             /* Except for VLAs, retrieve the allocation function.  */
+             allocfn = gimple_call_fndecl (stmt);
+             if (!allocfn)
+               allocfn = gimple_call_fn (stmt);
+             if (TREE_CODE (allocfn) == SSA_NAME)
+               {
+                 /* For an ALLOC_CALL via a function pointer make a small
+                    effort to determine the destination of the pointer.  */
+                 gimple *def = SSA_NAME_DEF_STMT (allocfn);
+                 if (gimple_assign_single_p (def))
+                   {
+                     tree rhs = gimple_assign_rhs1 (def);
+                     if (DECL_P (rhs))
+                       allocfn = rhs;
+                     else if (TREE_CODE (rhs) == COMPONENT_REF)
+                       allocfn = TREE_OPERAND (rhs, 1);
+                   }
+               }
+           }
+       }
+      else if (gimple_nop_p (stmt))
+       /* Handle DECL_PARM below.  */
+       ref = SSA_NAME_VAR (ref);
+    }
+
+  if (DECL_P (ref))
+    loc = DECL_SOURCE_LOCATION (ref);
+  else if (EXPR_P (ref) && EXPR_HAS_LOCATION (ref))
+    loc = EXPR_LOCATION (ref);
+  else if (TREE_CODE (ref) != IDENTIFIER_NODE
+          && TREE_CODE (ref) != SSA_NAME)
+    return;
+
+  if (mode == access_read_write || mode == access_write_only)
+    {
+      if (allocfn == NULL_TREE)
+       {
+         if (*offstr)
+           inform (loc, "at offset %s into destination object %qE of size %s",
+                   offstr, ref, sizestr);
+         else
+           inform (loc, "destination object %qE of size %s", ref, sizestr);
+         return;
+       }
+
+      if (*offstr)
+       inform (loc,
+               "at offset %s into destination object of size %s "
+               "allocated by %qE", offstr, sizestr, allocfn);
+      else
+       inform (loc, "destination object of size %s allocated by %qE",
+               sizestr, allocfn);
+      return;
+    }
+
+  if (mode == access_read_only)
+    {
+      if (allocfn == NULL_TREE)
+       {
+         if (*offstr)
+           inform (loc, "at offset %s into source object %qE of size %s",
+                   offstr, ref, sizestr);
+         else
+           inform (loc, "source object %qE of size %s", ref, sizestr);
+
+         return;
+       }
+
+      if (*offstr)
+       inform (loc,
+               "at offset %s into source object of size %s allocated by %qE",
+               offstr, sizestr, allocfn);
+      else
+       inform (loc, "source object of size %s allocated by %qE",
+               sizestr, allocfn);
+      return;
+    }
+
+  if (allocfn == NULL_TREE)
+    {
+      if (*offstr)
+       inform (loc, "at offset %s into object %qE of size %s",
+               offstr, ref, sizestr);
+      else
+       inform (loc, "object %qE of size %s", ref, sizestr);
+
+      return;
+    }
+
+  if (*offstr)
+    inform (loc,
+           "at offset %s into object of size %s allocated by %qE",
+           offstr, sizestr, allocfn);
+  else
+    inform (loc, "object of size %s allocated by %qE",
+           sizestr, allocfn);
+}
+
+/* Set a bit for the PHI in VISITED and return true if it wasn't
+   already set.  */
+
+bool
+ssa_name_limit_t::visit_phi (tree ssa_name)
+{
+  if (!visited)
+    visited = BITMAP_ALLOC (NULL);
+
+  /* Return false if SSA_NAME has already been visited.  */
+  return bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+/* Clear a bit for the PHI in VISITED.  */
+
+void
+ssa_name_limit_t::leave_phi (tree ssa_name)
+{
+  /* Return false if SSA_NAME has already been visited.  */
+  bitmap_clear_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+/* Return false if the SSA_NAME chain length counter has reached
+   the limit, otherwise increment the counter and return true.  */
+
+bool
+ssa_name_limit_t::next ()
+{
+  /* Return a negative value to let caller avoid recursing beyond
+     the specified limit.  */
+  if (ssa_def_max == 0)
+    return false;
+
+  --ssa_def_max;
+  return true;
+}
+
+/* If the SSA_NAME has already been "seen" return a positive value.
+   Otherwise add it to VISITED.  If the SSA_NAME limit has been
+   reached, return a negative value.  Otherwise return zero.  */
+
+int
+ssa_name_limit_t::next_phi (tree ssa_name)
+{
+  {
+    gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
+    /* Return a positive value if the PHI has already been visited.  */
+    if (gimple_code (def_stmt) == GIMPLE_PHI
+       && !visit_phi (ssa_name))
+      return 1;
+  }
+
+  /* Return a negative value to let caller avoid recursing beyond
+     the specified limit.  */
+  if (ssa_def_max == 0)
+    return -1;
+
+  --ssa_def_max;
+
+  return 0;
+}
+
+ssa_name_limit_t::~ssa_name_limit_t ()
+{
+  if (visited)
+    BITMAP_FREE (visited);
+}
+
+/* Default ctor.  Initialize object with pointers to the range_query
+   and cache_type instances to use or null.  */
+
+pointer_query::pointer_query (range_query *qry /* = NULL */,
+                             cache_type *cache /* = NULL */)
+: rvals (qry), var_cache (cache), hits (), misses (),
+  failures (), depth (), max_depth ()
+{
+  /* No op.  */
+}
+
+/* Return a pointer to the cached access_ref instance for the SSA_NAME
+   PTR if it's there or null otherwise.  */
+
+const access_ref *
+pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
+{
+  if (!var_cache)
+    {
+      ++misses;
+      return NULL;
+    }
+
+  unsigned version = SSA_NAME_VERSION (ptr);
+  unsigned idx = version << 1 | (ostype & 1);
+  if (var_cache->indices.length () <= idx)
+    {
+      ++misses;
+      return NULL;
+    }
+
+  unsigned cache_idx = var_cache->indices[idx];
+  if (var_cache->access_refs.length () <= cache_idx)
+    {
+      ++misses;
+      return NULL;
+    }
+
+  access_ref &cache_ref = var_cache->access_refs[cache_idx];
+  if (cache_ref.ref)
+    {
+      ++hits;
+      return &cache_ref;
+    }
+
+  ++misses;
+  return NULL;
+}
+
+/* Retrieve the access_ref instance for a variable from the cache if it's
+   there or compute it and insert it into the cache if it's nonnonull.  */
+
+bool
+pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
+{
+  const unsigned version
+    = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
+
+  if (var_cache && version)
+    {
+      unsigned idx = version << 1 | (ostype & 1);
+      if (idx < var_cache->indices.length ())
+       {
+         unsigned cache_idx = var_cache->indices[idx] - 1;
+         if (cache_idx < var_cache->access_refs.length ()
+             && var_cache->access_refs[cache_idx].ref)
+           {
+             ++hits;
+             *pref = var_cache->access_refs[cache_idx];
+             return true;
+           }
+       }
+
+      ++misses;
+    }
+
+  if (!compute_objsize (ptr, ostype, pref, this))
+    {
+      ++failures;
+      return false;
+    }
+
+  return true;
+}
+
+/* Add a copy of the access_ref REF for the SSA_NAME to the cache if it's
+   nonnull.  */
+
+void
+pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
+{
+  /* Only add populated/valid entries.  */
+  if (!var_cache || !ref.ref || ref.sizrng[0] < 0)
+    return;
+
+  /* Add REF to the two-level cache.  */
+  unsigned version = SSA_NAME_VERSION (ptr);
+  unsigned idx = version << 1 | (ostype & 1);
+
+  /* Grow INDICES if necessary.  An index is valid if it's nonzero.
+     Its value minus one is the index into ACCESS_REFS.  Not all
+     entries are valid.  */
+  if (var_cache->indices.length () <= idx)
+    var_cache->indices.safe_grow_cleared (idx + 1);
+
+  if (!var_cache->indices[idx])
+    var_cache->indices[idx] = var_cache->access_refs.length () + 1;
+
+  /* Grow ACCESS_REF cache if necessary.  An entry is valid if its
+     REF member is nonnull.  All entries except for the last two
+     are valid.  Once nonnull, the REF value must stay unchanged.  */
+  unsigned cache_idx = var_cache->indices[idx];
+  if (var_cache->access_refs.length () <= cache_idx)
+    var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
+
+  access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
+  if (cache_ref.ref)
+  {
+    gcc_checking_assert (cache_ref.ref == ref.ref);
+    return;
+  }
+
+  cache_ref = ref;
+}
+
+/* Flush the cache if it's nonnull.  */
+
+void
+pointer_query::flush_cache ()
+{
+  if (!var_cache)
+    return;
+  var_cache->indices.release ();
+  var_cache->access_refs.release ();
+}
+
+/* A helper of compute_objsize_r() to determine the size from an assignment
+   statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
+
+static bool
+handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
+                    ssa_name_limit_t &snlim, pointer_query *qry)
+{
+  tree_code code = gimple_assign_rhs_code (stmt);
+
+  tree ptr = gimple_assign_rhs1 (stmt);
+
+  /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
+     Determine the size/offset of each and use the one with more or less
+     space remaining, respectively.  If either fails, use the information
+     determined from the other instead, adjusted up or down as appropriate
+     for the expression.  */
+  access_ref aref[2] = { *pref, *pref };
+  if (!compute_objsize_r (ptr, ostype, &aref[0], snlim, qry))
+    {
+      aref[0].base0 = false;
+      aref[0].offrng[0] = aref[0].offrng[1] = 0;
+      aref[0].add_max_offset ();
+      aref[0].set_max_size_range ();
+    }
+
+  ptr = gimple_assign_rhs2 (stmt);
+  if (!compute_objsize_r (ptr, ostype, &aref[1], snlim, qry))
+    {
+      aref[1].base0 = false;
+      aref[1].offrng[0] = aref[1].offrng[1] = 0;
+      aref[1].add_max_offset ();
+      aref[1].set_max_size_range ();
+    }
+
+  if (!aref[0].ref && !aref[1].ref)
+    /* Fail if the identity of neither argument could be determined.  */
+    return false;
+
+  bool i0 = false;
+  if (aref[0].ref && aref[0].base0)
+    {
+      if (aref[1].ref && aref[1].base0)
+       {
+         /* If the object referenced by both arguments has been determined
+            set *PREF to the one with more or less space remainng, whichever
+            is appopriate for CODE.
+            TODO: Indicate when the objects are distinct so it can be
+            diagnosed.  */
+         i0 = code == MAX_EXPR;
+         const bool i1 = !i0;
+
+         if (aref[i0].size_remaining () < aref[i1].size_remaining ())
+           *pref = aref[i1];
+         else
+           *pref = aref[i0];
+         return true;
+       }
+
+      /* If only the object referenced by one of the arguments could be
+        determined, use it and...  */
+      *pref = aref[0];
+      i0 = true;
+    }
+  else
+    *pref = aref[1];
+
+  const bool i1 = !i0;
+  /* ...see if the offset obtained from the other pointer can be used
+     to tighten up the bound on the offset obtained from the first.  */
+  if ((code == MAX_EXPR && aref[i1].offrng[1] < aref[i0].offrng[0])
+      || (code == MIN_EXPR && aref[i0].offrng[0] < aref[i1].offrng[1]))
+    {
+      pref->offrng[0] = aref[i0].offrng[0];
+      pref->offrng[1] = aref[i0].offrng[1];
+    }
+  return true;
+}
+
+/* A helper of compute_objsize_r() to determine the size from ARRAY_REF
+   AREF.  ADDR is true if PTR is the operand of ADDR_EXPR.  Return true
+   on success and false on failure.  */
+
+static bool
+handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
+                 ssa_name_limit_t &snlim, pointer_query *qry)
+{
+  gcc_assert (TREE_CODE (aref) == ARRAY_REF);
+
+  ++pref->deref;
+
+  tree arefop = TREE_OPERAND (aref, 0);
+  tree reftype = TREE_TYPE (arefop);
+  if (!addr && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
+    /* Avoid arrays of pointers.  FIXME: Hande pointers to arrays
+       of known bound.  */
+    return false;
+
+  if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
+    return false;
+
+  offset_int orng[2];
+  tree off = pref->eval (TREE_OPERAND (aref, 1));
+  range_query *const rvals = qry ? qry->rvals : NULL;
+  if (!get_offset_range (off, NULL, orng, rvals))
+    {
+      /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
+      orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      orng[0] = -orng[1] - 1;
+    }
+
+  /* Convert the array index range determined above to a byte
+     offset.  */
+  tree lowbnd = array_ref_low_bound (aref);
+  if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
+    {
+      /* Adjust the index by the low bound of the array domain
+        (normally zero but 1 in Fortran).  */
+      unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
+      orng[0] -= lb;
+      orng[1] -= lb;
+    }
+
+  tree eltype = TREE_TYPE (aref);
+  tree tpsize = TYPE_SIZE_UNIT (eltype);
+  if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
+    {
+      pref->add_max_offset ();
+      return true;
+    }
+
+  offset_int sz = wi::to_offset (tpsize);
+  orng[0] *= sz;
+  orng[1] *= sz;
+
+  if (ostype && TREE_CODE (eltype) == ARRAY_TYPE)
+    {
+      /* Except for the permissive raw memory functions which use
+        the size of the whole object determined above, use the size
+        of the referenced array.  Because the overall offset is from
+        the beginning of the complete array object add this overall
+        offset to the size of array.  */
+      offset_int sizrng[2] =
+       {
+        pref->offrng[0] + orng[0] + sz,
+        pref->offrng[1] + orng[1] + sz
+       };
+      if (sizrng[1] < sizrng[0])
+       std::swap (sizrng[0], sizrng[1]);
+      if (sizrng[0] >= 0 && sizrng[0] <= pref->sizrng[0])
+       pref->sizrng[0] = sizrng[0];
+      if (sizrng[1] >= 0 && sizrng[1] <= pref->sizrng[1])
+       pref->sizrng[1] = sizrng[1];
+    }
+
+  pref->add_offset (orng[0], orng[1]);
+  return true;
+}
+
+/* A helper of compute_objsize_r() to determine the size from MEM_REF
+   MREF.  Return true on success and false on failure.  */
+
+static bool
+handle_mem_ref (tree mref, int ostype, access_ref *pref,
+               ssa_name_limit_t &snlim, pointer_query *qry)
+{
+  gcc_assert (TREE_CODE (mref) == MEM_REF);
+
+  ++pref->deref;
+
+  if (VECTOR_TYPE_P (TREE_TYPE (mref)))
+    {
+      /* Hack: Handle MEM_REFs of vector types as those to complete
+        objects; those may be synthesized from multiple assignments
+        to consecutive data members (see PR 93200 and 96963).
+        FIXME: Vectorized assignments should only be present after
+        vectorization so this hack is only necessary after it has
+        run and could be avoided in calls from prior passes (e.g.,
+        tree-ssa-strlen.c).
+        FIXME: Deal with this more generally, e.g., by marking up
+        such MEM_REFs at the time they're created.  */
+      ostype = 0;
+    }
+
+  tree mrefop = TREE_OPERAND (mref, 0);
+  if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
+    return false;
+
+  offset_int orng[2];
+  tree off = pref->eval (TREE_OPERAND (mref, 1));
+  range_query *const rvals = qry ? qry->rvals : NULL;
+  if (!get_offset_range (off, NULL, orng, rvals))
+    {
+      /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
+      orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      orng[0] = -orng[1] - 1;
+    }
+
+  pref->add_offset (orng[0], orng[1]);
+  return true;
+}
+
+/* Helper to compute the size of the object referenced by the PTR
+   expression which must have pointer type, using Object Size type
+   OSTYPE (only the least significant 2 bits are used).
+   On success, sets PREF->REF to the DECL of the referenced object
+   if it's unique, otherwise to null, PREF->OFFRNG to the range of
+   offsets into it, and PREF->SIZRNG to the range of sizes of
+   the object(s).
+   SNLIM is used to avoid visiting the same PHI operand multiple
+   times, and, when nonnull, RVALS to determine range information.
+   Returns true on success, false when a meaningful size (or range)
+   cannot be determined.
+
+   The function is intended for diagnostics and should not be used
+   to influence code generation or optimization.  */
+
+static bool
+compute_objsize_r (tree ptr, int ostype, access_ref *pref,
+                  ssa_name_limit_t &snlim, pointer_query *qry)
+{
+  STRIP_NOPS (ptr);
+
+  const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
+  if (addr)
+    {
+      --pref->deref;
+      ptr = TREE_OPERAND (ptr, 0);
+    }
+
+  if (DECL_P (ptr))
+    {
+      pref->ref = ptr;
+
+      if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
+       {
+         /* Set the maximum size if the reference is to the pointer
+            itself (as opposed to what it points to), and clear
+            BASE0 since the offset isn't necessarily zero-based.  */
+         pref->set_max_size_range ();
+         pref->base0 = false;
+         return true;
+       }
+
+      if (tree size = decl_init_size (ptr, false))
+       if (TREE_CODE (size) == INTEGER_CST)
+         {
+           pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+           return true;
+         }
+
+      pref->set_max_size_range ();
+      return true;
+    }
+
+  const tree_code code = TREE_CODE (ptr);
+  range_query *const rvals = qry ? qry->rvals : NULL;
+
+  if (code == BIT_FIELD_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+       return false;
+
+      offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
+      pref->add_offset (off / BITS_PER_UNIT);
+      return true;
+    }
+
+  if (code == COMPONENT_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
+       /* In accesses through union types consider the entire unions
+          rather than just their members.  */
+       ostype = 0;
+      tree field = TREE_OPERAND (ptr, 1);
+
+      if (ostype == 0)
+       {
+         /* In OSTYPE zero (for raw memory functions like memcpy), use
+            the maximum size instead if the identity of the enclosing
+            object cannot be determined.  */
+         if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+           return false;
+
+         /* Otherwise, use the size of the enclosing object and add
+            the offset of the member to the offset computed so far.  */
+         tree offset = byte_position (field);
+         if (TREE_CODE (offset) == INTEGER_CST)
+           pref->add_offset (wi::to_offset (offset));
+         else
+           pref->add_max_offset ();
+
+         if (!pref->ref)
+           /* REF may have been already set to an SSA_NAME earlier
+              to provide better context for diagnostics.  In that case,
+              leave it unchanged.  */
+           pref->ref = ref;
+         return true;
+       }
+
+      pref->ref = field;
+
+      if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
+       {
+         /* Set maximum size if the reference is to the pointer member
+            itself (as opposed to what it points to).  */
+         pref->set_max_size_range ();
+         return true;
+       }
+
+      /* SAM is set for array members that might need special treatment.  */
+      special_array_member sam;
+      tree size = component_ref_size (ptr, &sam);
+      if (sam == special_array_member::int_0)
+       pref->sizrng[0] = pref->sizrng[1] = 0;
+      else if (!pref->trail1special && sam == special_array_member::trail_1)
+       pref->sizrng[0] = pref->sizrng[1] = 1;
+      else if (size && TREE_CODE (size) == INTEGER_CST)
+       pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+      else
+       {
+         /* When the size of the member is unknown it's either a flexible
+            array member or a trailing special array member (either zero
+            length or one-element).  Set the size to the maximum minus
+            the constant size of the type.  */
+         pref->sizrng[0] = 0;
+         pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+         if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref)))
+           if (TREE_CODE (recsize) == INTEGER_CST)
+             pref->sizrng[1] -= wi::to_offset (recsize);
+       }
+      return true;
+    }
+
+  if (code == ARRAY_REF)
+    return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
+
+  if (code == MEM_REF)
+    return handle_mem_ref (ptr, ostype, pref, snlim, qry);
+
+  if (code == TARGET_MEM_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+       return false;
+
+      /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
+      pref->ref = ptr;
+      pref->add_max_offset ();
+      return true;
+    }
+
+  if (code == INTEGER_CST)
+    {
+      /* Pointer constants other than null are most likely the result
+        of erroneous null pointer addition/subtraction.  Set size to
+        zero.  For null pointers, set size to the maximum for now
+        since those may be the result of jump threading.  */
+      if (integer_zerop (ptr))
+       pref->set_max_size_range ();
+      else
+       pref->sizrng[0] = pref->sizrng[1] = 0;
+      pref->ref = ptr;
+
+      return true;
+    }
+
+  if (code == STRING_CST)
+    {
+      pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
+      pref->ref = ptr;
+      return true;
+    }
+
+  if (code == POINTER_PLUS_EXPR)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+       return false;
+
+      /* Clear DEREF since the offset is being applied to the target
+        of the dereference.  */
+      pref->deref = 0;
+
+      offset_int orng[2];
+      tree off = pref->eval (TREE_OPERAND (ptr, 1));
+      if (get_offset_range (off, NULL, orng, rvals))
+       pref->add_offset (orng[0], orng[1]);
+      else
+       pref->add_max_offset ();
+      return true;
+    }
+
+  if (code == VIEW_CONVERT_EXPR)
+    {
+      ptr = TREE_OPERAND (ptr, 0);
+      return compute_objsize_r (ptr, ostype, pref, snlim, qry);
+    }
+
+  if (code == SSA_NAME)
+    {
+      if (!snlim.next ())
+       return false;
+
+      /* Only process an SSA_NAME if the recursion limit has not yet
+        been reached.  */
+      if (qry)
+       {
+         if (++qry->depth)
+           qry->max_depth = qry->depth;
+         if (const access_ref *cache_ref = qry->get_ref (ptr))
+           {
+             /* If the pointer is in the cache set *PREF to what it refers
+                to and return success.  */
+             *pref = *cache_ref;
+             return true;
+           }
+       }
+
+      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+      if (is_gimple_call (stmt))
+       {
+         /* If STMT is a call to an allocation function get the size
+            from its argument(s).  If successful, also set *PREF->REF
+            to PTR for the caller to include in diagnostics.  */
+         wide_int wr[2];
+         if (gimple_call_alloc_size (stmt, wr, rvals))
+           {
+             pref->ref = ptr;
+             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+             /* Constrain both bounds to a valid size.  */
+             offset_int maxsize = wi::to_offset (max_object_size ());
+             if (pref->sizrng[0] > maxsize)
+               pref->sizrng[0] = maxsize;
+             if (pref->sizrng[1] > maxsize)
+               pref->sizrng[1] = maxsize;
+           }
+         else
+           {
+             /* For functions known to return one of their pointer arguments
+                try to determine what the returned pointer points to, and on
+                success add OFFRNG which was set to the offset added by
+                the function (e.g., memchr) to the overall offset.  */
+             bool past_end;
+             offset_int offrng[2];
+             if (tree ret = gimple_call_return_array (stmt, offrng,
+                                                      &past_end, rvals))
+               {
+                 if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
+                   return false;
+
+                 /* Cap OFFRNG[1] to at most the remaining size of
+                    the object.  */
+                 offset_int remrng[2];
+                 remrng[1] = pref->size_remaining (remrng);
+                 if (remrng[1] != 0 && !past_end)
+                   /* Decrement the size for functions that never return
+                      a past-the-end pointer.  */
+                   remrng[1] -= 1;
+
+                 if (remrng[1] < offrng[1])
+                   offrng[1] = remrng[1];
+                 pref->add_offset (offrng[0], offrng[1]);
+               }
+             else
+               {
+                 /* For other calls that might return arbitrary pointers
+                    including into the middle of objects set the size
+                    range to maximum, clear PREF->BASE0, and also set
+                    PREF->REF to include in diagnostics.  */
+                 pref->set_max_size_range ();
+                 pref->base0 = false;
+                 pref->ref = ptr;
+               }
+           }
+         qry->put_ref (ptr, *pref);
+         return true;
+       }
+
+      if (gimple_nop_p (stmt))
+       {
+         /* For a function argument try to determine the byte size
+            of the array from the current function declaratation
+            (e.g., attribute access or related).  */
+         wide_int wr[2];
+         bool static_array = false;
+         if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
+           {
+             pref->parmarray = !static_array;
+             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+             pref->ref = ref;
+             qry->put_ref (ptr, *pref);
+             return true;
+           }
+
+         pref->set_max_size_range ();
+         pref->base0 = false;
+         pref->ref = ptr;
+         qry->put_ref (ptr, *pref);
+         return true;
+       }
+
+      if (gimple_code (stmt) == GIMPLE_PHI)
+       {
+         pref->ref = ptr;
+         access_ref phi_ref = *pref;
+         if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry))
+           return false;
+         *pref = phi_ref;
+         pref->ref = ptr;
+         qry->put_ref (ptr, *pref);
+         return true;
+       }
+
+      if (!is_gimple_assign (stmt))
+       {
+         /* Clear BASE0 since the assigned pointer might point into
+            the middle of the object, set the maximum size range and,
+            if the SSA_NAME refers to a function argumnent, set
+            PREF->REF to it.  */
+         pref->base0 = false;
+         pref->set_max_size_range ();
+         pref->ref = ptr;
+         return true;
+       }
+
+      tree_code code = gimple_assign_rhs_code (stmt);
+
+      if (code == MAX_EXPR || code == MIN_EXPR)
+       {
+         if (!handle_min_max_size (stmt, ostype, pref, snlim, qry))
+           return false;
+         qry->put_ref (ptr, *pref);
+         return true;
+       }
+
+      tree rhs = gimple_assign_rhs1 (stmt);
+
+      if (code == ASSERT_EXPR)
+       {
+         rhs = TREE_OPERAND (rhs, 0);
+         return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+       }
+
+      if (code == POINTER_PLUS_EXPR
+         && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
+       {
+         /* Compute the size of the object first. */
+         if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+           return false;
+
+         offset_int orng[2];
+         tree off = gimple_assign_rhs2 (stmt);
+         if (get_offset_range (off, stmt, orng, rvals))
+           pref->add_offset (orng[0], orng[1]);
+         else
+           pref->add_max_offset ();
+         qry->put_ref (ptr, *pref);
+         return true;
+       }
+
+      if (code == ADDR_EXPR
+         || code == SSA_NAME)
+       return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+
+      /* (This could also be an assignment from a nonlocal pointer.)  Save
+        PTR to mention in diagnostics but otherwise treat it as a pointer
+        to an unknown object.  */
+      pref->ref = rhs;
+      pref->base0 = false;
+      pref->set_max_size_range ();
+      return true;
+    }
+
+  /* Assume all other expressions point into an unknown object
+     of the maximum valid size.  */
+  pref->ref = ptr;
+  pref->base0 = false;
+  pref->set_max_size_range ();
+  if (TREE_CODE (ptr) == SSA_NAME)
+    qry->put_ref (ptr, *pref);
+  return true;
+}
+
+/* A "public" wrapper around the above.  Clients should use this overload
+   instead.  */
+
+tree
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+                range_query *rvals /* = NULL */)
+{
+  pointer_query qry;
+  qry.rvals = rvals;
+  ssa_name_limit_t snlim;
+  if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
+    return NULL_TREE;
+
+  offset_int maxsize = pref->size_remaining ();
+  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
+    pref->offrng[0] = 0;
+  return wide_int_to_tree (sizetype, maxsize);
+}
+
+/* Transitional wrapper.  The function should be removed once callers
+   transition to the pointer_query API.  */
+
+tree
+compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
+{
+  pointer_query qry;
+  if (ptr_qry)
+    ptr_qry->depth = 0;
+  else
+    ptr_qry = &qry;
+
+  ssa_name_limit_t snlim;
+  if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
+    return NULL_TREE;
+
+  offset_int maxsize = pref->size_remaining ();
+  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
+    pref->offrng[0] = 0;
+  return wide_int_to_tree (sizetype, maxsize);
+}
+
+/* Legacy wrapper around the above.  The function should be removed
+   once callers transition to one of the two above.  */
+
+tree
+compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
+                tree *poff /* = NULL */, range_query *rvals /* = NULL */)
+{
+  /* Set the initial offsets to zero and size to negative to indicate
+     none has been computed yet.  */
+  access_ref ref;
+  tree size = compute_objsize (ptr, ostype, &ref, rvals);
+  if (!size || !ref.base0)
+    return NULL_TREE;
+
+  if (pdecl)
+    *pdecl = ref.ref;
+
+  if (poff)
+    *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
+
+  return size;
+}
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
new file mode 100644 (file)
index 0000000..6168c80
--- /dev/null
@@ -0,0 +1,234 @@
+/* Definitions of the pointer_query and related classes.
+
+   Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_POINTER_QUERY_H
+#define GCC_POINTER_QUERY_H
+
+/* Describes recursion limits used by functions that follow use-def
+   chains of SSA_NAMEs.  */
+
+class ssa_name_limit_t
+{
+  bitmap visited;         /* Bitmap of visited SSA_NAMEs.  */
+  unsigned ssa_def_max;   /* Longest chain of SSA_NAMEs to follow.  */
+
+  /* Not copyable or assignable.  */
+  DISABLE_COPY_AND_ASSIGN (ssa_name_limit_t);
+
+public:
+
+  ssa_name_limit_t ()
+    : visited (),
+      ssa_def_max (param_ssa_name_def_chain_limit) { }
+
+  /* Set a bit for the PHI in VISITED and return true if it wasn't
+     already set.  */
+  bool visit_phi (tree);
+  /* Clear a bit for the PHI in VISITED.  */
+  void leave_phi (tree);
+  /* Return false if the SSA_NAME chain length counter has reached
+     the limit, otherwise increment the counter and return true.  */
+  bool next ();
+
+  /* If the SSA_NAME has already been "seen" return a positive value.
+     Otherwise add it to VISITED.  If the SSA_NAME limit has been
+     reached, return a negative value.  Otherwise return zero.  */
+  int next_phi (tree);
+
+  ~ssa_name_limit_t ();
+};
+
+class pointer_query;
+
+/* Describes a reference to an object used in an access.  */
+struct access_ref
+{
+  /* Set the bounds of the reference to at most as many bytes
+     as the first argument or unknown when null, and at least
+     one when the second argument is true unless the first one
+     is a constant zero.  */
+  access_ref (tree = NULL_TREE, bool = false);
+
+  /* Return the PHI node REF refers to or null if it doesn't.  */
+  gphi *phi () const;
+
+  /* Return the object to which REF refers.  */
+  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
+               ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
+
+  /* Return true if OFFRNG is the constant zero.  */
+  bool offset_zero () const
+  {
+    return offrng[0] == 0 && offrng[1] == 0;
+  }
+
+  /* Return true if OFFRNG is bounded to a subrange of offset values
+     valid for the largest possible object.  */
+  bool offset_bounded () const;
+
+  /* Return the maximum amount of space remaining and if non-null, set
+     argument to the minimum.  */
+  offset_int size_remaining (offset_int * = NULL) const;
+
+/* Return true if the offset and object size are in range for SIZE.  */
+  bool offset_in_range (const offset_int &) const;
+
+  /* Return true if *THIS is an access to a declared object.  */
+  bool ref_declared () const
+  {
+    return DECL_P (ref) && base0 && deref < 1;
+  }
+
+  /* Set the size range to the maximum.  */
+  void set_max_size_range ()
+  {
+    sizrng[0] = 0;
+    sizrng[1] = wi::to_offset (max_object_size ());
+  }
+
+  /* Add OFF to the offset range.  */
+  void add_offset (const offset_int &off)
+  {
+    add_offset (off, off);
+  }
+
+  /* Add the range [MIN, MAX] to the offset range.  */
+  void add_offset (const offset_int &, const offset_int &);
+
+  /* Add the maximum representable offset to the offset range.  */
+  void add_max_offset ()
+  {
+    offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+    add_offset (-maxoff - 1, maxoff);
+  }
+
+  /* Issue an informational message describing the target of an access
+     with the given mode.  */
+  void inform_access (access_mode) const;
+
+  /* Reference to the accessed object(s).  */
+  tree ref;
+
+  /* Range of byte offsets into and sizes of the object(s).  */
+  offset_int offrng[2];
+  offset_int sizrng[2];
+  /* The minimum and maximum offset computed.  */
+  offset_int offmax[2];
+  /* Range of the bound of the access: denotes that the access
+     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
+     For string functions the size of the actual access is
+     further constrained by the length of the string.  */
+  offset_int bndrng[2];
+
+  /* Used to fold integer expressions when called from front ends.  */
+  tree (*eval)(tree);
+  /* Positive when REF is dereferenced, negative when its address is
+     taken.  */
+  int deref;
+  /* Set if trailing one-element arrays should be treated as flexible
+     array members.  */
+  bool trail1special;
+  /* Set if valid offsets must start at zero (for declared and allocated
+     objects but not for others referenced by pointers).  */
+  bool base0;
+  /* Set if REF refers to a function array parameter not declared
+     static.  */
+  bool parmarray;
+};
+
+class range_query;
+
+/* Queries and caches compute_objsize results.  */
+class pointer_query
+{
+  DISABLE_COPY_AND_ASSIGN (pointer_query);
+
+public:
+  /* Type of the two-level cache object defined by clients of the class
+     to have pointer SSA_NAMEs cached for speedy access.  */
+  struct cache_type
+  {
+    /* 1-based indices into cache.  */
+    vec<unsigned> indices;
+    /* The cache itself.  */
+    vec<access_ref> access_refs;
+  };
+
+  /* Construct an object with the given Ranger instance and cache.  */
+  explicit pointer_query (range_query * = NULL, cache_type * = NULL);
+
+  /* Retrieve the access_ref for a variable from cache if it's there.  */
+  const access_ref* get_ref (tree, int = 1) const;
+
+  /* Retrieve the access_ref for a variable from cache or compute it.  */
+  bool get_ref (tree, access_ref*, int = 1);
+
+  /* Add an access_ref for the SSA_NAME to the cache.  */
+  void put_ref (tree, const access_ref&, int = 1);
+
+  /* Flush the cache.  */
+  void flush_cache ();
+
+  /* A Ranger instance.  May be null to use global ranges.  */
+  range_query *rvals;
+  /* Cache of SSA_NAMEs.  May be null to disable caching.  */
+  cache_type *var_cache;
+
+  /* Cache performance counters.  */
+  mutable unsigned hits;
+  mutable unsigned misses;
+  mutable unsigned failures;
+  mutable unsigned depth;
+  mutable unsigned max_depth;
+};
+
+/* Describes a pair of references used in an access by built-in
+   functions like memcpy.  */
+struct access_data
+{
+  /* Set the access to at most MAXWRITE and MAXREAD bytes, and
+     at least 1 when MINWRITE or MINREAD, respectively, is set.  */
+  access_data (tree expr, access_mode mode,
+              tree maxwrite = NULL_TREE, bool minwrite = false,
+              tree maxread = NULL_TREE, bool minread = false)
+    : call (expr),
+      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+
+  /* Built-in function call.  */
+  tree call;
+  /* Destination and source of the access.  */
+  access_ref dst, src;
+  /* Read-only for functions like memcmp or strlen, write-only
+     for memset, read-write for memcpy or strcat.  */
+  access_mode mode;
+};
+
+class range_query;
+extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
+                                   range_query * = NULL);
+extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
+
+extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
+/* Legacy/transitional API.  Should not be used in new code.  */
+extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
+                            range_query * = NULL);
+
+#endif   // GCC_POINTER_QUERY_H
index aa9757a2fe9924ab991fc2e2f35533733a0916cc..1f5b1370a95a448a2b54fe64c21a00cc1ba04303 100644 (file)
@@ -428,6 +428,7 @@ extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_early_object_sizes (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
index 799c21fddde9757307bba3d7b381bbd9784c5399..15391da81048b148f312d87af2522910282a511b 100644 (file)
@@ -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-access.h"
 #include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stor-layout.h"
@@ -47,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-strlen.h"
 #include "tree-hash-traits.h"
 #include "builtins.h"
+#include "pointer-query.h"
 #include "target.h"
 #include "diagnostic-core.h"
 #include "diagnostic.h"
index c621f870880cb19748b1f04bab016a1f097e5987..e923e67b694287c012d91f4e83bc0af92f03b788 100644 (file)
@@ -14380,6 +14380,65 @@ valid_new_delete_pair_p (tree new_asm, tree delete_asm)
   return false;
 }
 
+/* Return the zero-based number corresponding to the argument being
+   deallocated if FNDECL is a deallocation function or an out-of-bounds
+   value if it isn't.  */
+
+unsigned
+fndecl_dealloc_argno (tree fndecl)
+{
+  /* A call to operator delete isn't recognized as one to a built-in.  */
+  if (DECL_IS_OPERATOR_DELETE_P (fndecl))
+    {
+      if (DECL_IS_REPLACEABLE_OPERATOR (fndecl))
+       return 0;
+
+      /* Avoid placement delete that's not been inlined.  */
+      tree fname = DECL_ASSEMBLER_NAME (fndecl);
+      if (id_equal (fname, "_ZdlPvS_")       // ordinary form
+         || id_equal (fname, "_ZdaPvS_"))   // array form
+       return UINT_MAX;
+      return 0;
+    }
+
+  /* TODO: Handle user-defined functions with attribute malloc?  Handle
+     known non-built-ins like fopen?  */
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    {
+      switch (DECL_FUNCTION_CODE (fndecl))
+       {
+       case BUILT_IN_FREE:
+       case BUILT_IN_REALLOC:
+         return 0;
+       default:
+         break;
+       }
+      return UINT_MAX;
+    }
+
+  tree attrs = DECL_ATTRIBUTES (fndecl);
+  if (!attrs)
+    return UINT_MAX;
+
+  for (tree atfree = attrs;
+       (atfree = lookup_attribute ("*dealloc", atfree));
+       atfree = TREE_CHAIN (atfree))
+    {
+      tree alloc = TREE_VALUE (atfree);
+      if (!alloc)
+       continue;
+
+      tree pos = TREE_CHAIN (alloc);
+      if (!pos)
+       return 0;
+
+      pos = TREE_VALUE (pos);
+      return TREE_INT_CST_LOW (pos) - 1;
+    }
+
+  return UINT_MAX;
+}
+
 #if CHECKING_P
 
 namespace selftest {
index 13c088cee054a641bdc182b1537d8fdb2b38857b..972ceb370f87385a2e3555ab70777453f20d73b5 100644 (file)
@@ -6468,4 +6468,9 @@ extern void suppress_warning (tree, opt_code = all_warnings, bool = true)
 /* Copy warning disposition from one expression to another.  */
 extern void copy_warning (tree, const_tree);
 
+/* Return the zero-based number corresponding to the argument being
+   deallocated if FNDECL is a deallocation function or an out-of-bounds
+   value if it isn't.  */
+extern unsigned fndecl_dealloc_argno (tree);
+
 #endif  /* GCC_TREE_H  */