public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-2581] Add new gimple-ssa-warn-access pass.
@ 2021-07-28 22:07 Martin Sebor
  0 siblings, 0 replies; only message in thread
From: Martin Sebor @ 2021-07-28 22:07 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2a837de28ee94b4ec201059a9a7aaa852e6808da

commit r12-2581-g2a837de28ee94b4ec201059a9a7aaa852e6808da
Author: Martin Sebor <msebor@redhat.com>
Date:   Wed Jul 28 15:28:10 2021 -0600

    Add new gimple-ssa-warn-access pass.
    
    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.

Diff:
---
 gcc/Makefile.in                |     2 +
 gcc/attribs.h                  |     2 -
 gcc/builtins.c                 | 15209 +++++++++++++++------------------------
 gcc/builtins.h                 |   215 +-
 gcc/calls.c                    |     5 +-
 gcc/cp/init.c                  |     2 +-
 gcc/gimple-array-bounds.cc     |     2 +-
 gcc/gimple-fold.c              |     1 +
 gcc/gimple-ssa-sprintf.c       |     1 +
 gcc/gimple-ssa-warn-access.cc  |  1765 +++++
 gcc/gimple-ssa-warn-access.h   |    37 +
 gcc/gimple-ssa-warn-restrict.c |     2 +-
 gcc/passes.def                 |     1 +
 gcc/pointer-query.cc           |  1895 +++++
 gcc/pointer-query.h            |   234 +
 gcc/tree-pass.h                |     1 +
 gcc/tree-ssa-strlen.c          |     2 +
 gcc/tree.c                     |    59 +
 gcc/tree.h                     |     5 +
 19 files changed, 9855 insertions(+), 9585 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 29bd4edb4b7..98eb479a125 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -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 \
diff --git a/gcc/attribs.h b/gcc/attribs.h
index df78eb152f9..87231b954c6 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -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
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 170d776c410..845a8bb1201 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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%>");
-	  [...]

[diff truncated at 524288 bytes]


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-07-28 22:07 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-28 22:07 [gcc r12-2581] Add new gimple-ssa-warn-access pass Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).