public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-2976] Move more warning code to gimple-ssa-warn-access etc.
@ 2021-08-17 20:51 Martin Sebor
  0 siblings, 0 replies; only message in thread
From: Martin Sebor @ 2021-08-17 20:51 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:b48d4e6818674898f90d9358378c127511ef0f9f

commit r12-2976-gb48d4e6818674898f90d9358378c127511ef0f9f
Author: Martin Sebor <msebor@redhat.com>
Date:   Tue Aug 17 14:49:05 2021 -0600

    Move more warning code to gimple-ssa-warn-access etc.
    
    Also resolves:
    PR middle-end/101854 - Invalid warning -Wstringop-overflow wrong argument
    
    gcc/ChangeLog:
    
            PR middle-end/101854
            * builtins.c (expand_builtin_alloca): Move warning code to check_alloca
            in gimple-ssa-warn-access.cc.
            * calls.c (alloc_max_size): Move code to check_alloca.
            (get_size_range): Move to pointer-query.cc.
            (maybe_warn_alloc_args_overflow): Move to gimple-ssa-warn-access.cc.
            (get_attr_nonstring_decl): Move to tree.c.
            (fntype_argno_type): Move to gimple-ssa-warn-access.cc.
            (append_attrname): Same.
            (maybe_warn_rdwr_sizes): Same.
            (initialize_argument_information): Move code to
            gimple-ssa-warn-access.cc.
            * calls.h (maybe_warn_alloc_args_overflow): Move to
            gimple-ssa-warn-access.h.
            (get_attr_nonstring_decl): Move to tree.h.
            (maybe_warn_nonstring_arg):  Move to gimple-ssa-warn-access.h.
            (enum size_range_flags): Move to pointer-query.h.
            (get_size_range): Same.
            * gimple-ssa-warn-access.cc (has_location): Remove unused overload
            to avoid Clang -Wunused-function.
            (get_size_range): Declare static.
            (maybe_emit_free_warning): Rename...
            (maybe_check_dealloc_call): ...to this for consistency.
            (class pass_waccess): Add members.
            (pass_waccess::~pass_waccess): Defined.
            (alloc_max_size): Move here from calls.c.
            (maybe_warn_alloc_args_overflow): Same.
            (check_alloca): New function.
            (check_alloc_size_call): New function.
            (check_strncat): Handle another warning flag.
            (pass_waccess::check_builtin): Handle alloca.
            (fntype_argno_type): Move here from calls.c.
            (append_attrname): Same.
            (maybe_warn_rdwr_sizes): Same.
            (pass_waccess::check_call): Define.
            (check_nonstring_args): New function.
            (pass_waccess::check): Call new member functions.
            (pass_waccess::execute): Enable ranger.
            * gimple-ssa-warn-access.h (get_size_range): Move here from calls.h.
            (maybe_warn_nonstring_arg): Same.
            * gimple-ssa-warn-restrict.c: Remove #include.
            * pointer-query.cc (get_size_range): Move here from calls.c.
            * pointer-query.h (enum size_range_flags): Same.
            (get_size_range): Same.
            * tree.c (get_attr_nonstring_decl): Move here from calls.c.
            * tree.h (get_attr_nonstring_decl): Move here from calls.h.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/attr-alloc_size-5.c: Adjust optimization to -O1.
            * gcc.dg/attr-alloc_size-7.c: Use #pragmas to adjust optimization.
            * gcc.dg/attr-alloc_size-8.c: Adjust optimization to -O1.
    
            PR middle-end/101854
            * gcc.dg/Wstringop-overflow-72.c: New test.

Diff:
---
 gcc/builtins.c                               |  22 +-
 gcc/calls.c                                  | 760 ---------------------------
 gcc/calls.h                                  |  15 +-
 gcc/gimple-ssa-warn-access.cc                | 683 +++++++++++++++++++++++-
 gcc/gimple-ssa-warn-access.h                 |   4 +-
 gcc/gimple-ssa-warn-restrict.c               |   1 +
 gcc/pointer-query.cc                         | 195 +++++--
 gcc/pointer-query.h                          |  11 +
 gcc/testsuite/gcc.dg/Wstringop-overflow-72.c |  13 +
 gcc/testsuite/gcc.dg/attr-alloc_size-5.c     |   2 +-
 gcc/testsuite/gcc.dg/attr-alloc_size-7.c     |  45 +-
 gcc/testsuite/gcc.dg/attr-alloc_size-8.c     |   2 +-
 gcc/tree.c                                   |  54 ++
 gcc/tree.h                                   |   6 +
 14 files changed, 952 insertions(+), 861 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d2be807f1d6..99548627761 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -43,7 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
-#include "gimple-ssa-warn-restrict.h"
+#include "gimple-ssa-warn-access.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -81,7 +81,6 @@ along with GCC; see the file COPYING3.  If not see
 #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
@@ -4896,25 +4895,6 @@ expand_builtin_alloca (tree exp)
   if (!valid_arglist)
     return 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);
-    }
-
   /* Compute the argument.  */
   op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
 
diff --git a/gcc/calls.c b/gcc/calls.c
index fcb0d6dec69..e50d3fc3b62 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
-#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -50,7 +49,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "tree-vrp.h"
 #include "tree-ssanames.h"
-#include "tree-ssa-strlen.h"
 #include "intl.h"
 #include "stringpool.h"
 #include "hash-map.h"
@@ -60,8 +58,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "attr-fnspec.h"
 #include "value-query.h"
-#include "pointer-query.h"
-#include "gimple-ssa-warn-access.h"
 #include "tree-pretty-print.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
@@ -1223,397 +1219,6 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
       }
 }
 
-/* The limit set by -Walloc-larger-than=.  */
-static GTY(()) tree alloc_object_size_limit;
-
-/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than=
-   setting if the option is specified, or to the maximum object size if it
-   is not.  Return the initialized value.  */
-
-static tree
-alloc_max_size (void)
-{
-  if (alloc_object_size_limit)
-    return alloc_object_size_limit;
-
-  HOST_WIDE_INT limit = warn_alloc_size_limit;
-  if (limit == HOST_WIDE_INT_MAX)
-    limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
-
-  alloc_object_size_limit = build_int_cst (size_type_node, limit);
-
-  return alloc_object_size_limit;
-}
-
-/* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a represents a valid size
-   of object, or a valid size argument to an allocation function declared
-   with attribute alloc_size (whose argument may be signed), or to a string
-   manipulation function like memset.
-   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
-   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
-   a (nearly) invalid argument to allocation functions like malloc but it
-   is a valid argument to functions like memset.
-   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
-   in a multi-range, otherwise to the smallest valid subrange.  */
-
-bool
-get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
-		int flags /* = 0 */)
-{
-  if (!exp)
-    return false;
-
-  if (tree_fits_uhwi_p (exp))
-    {
-      /* EXP is a constant.  */
-      range[0] = range[1] = exp;
-      return true;
-    }
-
-  tree exptype = TREE_TYPE (exp);
-  bool integral = INTEGRAL_TYPE_P (exptype);
-
-  wide_int min, max;
-  enum value_range_kind range_type;
-
-  if (!query)
-    query = get_global_range_query ();
-
-  if (integral)
-    {
-      value_range vr;
-
-      query->range_of_expr (vr, exp, stmt);
-
-      if (vr.undefined_p ())
-	vr.set_varying (TREE_TYPE (exp));
-      range_type = vr.kind ();
-      min = wi::to_wide (vr.min ());
-      max = wi::to_wide (vr.max ());
-    }
-  else
-    range_type = VR_VARYING;
-
-  if (range_type == VR_VARYING)
-    {
-      if (integral)
-	{	
-	  /* Use the full range of the type of the expression when
-	     no value range information is available.  */
-	  range[0] = TYPE_MIN_VALUE (exptype);
-	  range[1] = TYPE_MAX_VALUE (exptype);
-	  return true;
-	}
-
-      range[0] = NULL_TREE;
-      range[1] = NULL_TREE;
-      return false;
-    }
-
-  unsigned expprec = TYPE_PRECISION (exptype);
-
-  bool signed_p = !TYPE_UNSIGNED (exptype);
-
-  if (range_type == VR_ANTI_RANGE)
-    {
-      if (signed_p)
-	{
-	  if (wi::les_p (max, 0))
-	    {
-	      /* EXP is not in a strictly negative range.  That means
-		 it must be in some (not necessarily strictly) positive
-		 range which includes zero.  Since in signed to unsigned
-		 conversions negative values end up converted to large
-		 positive values, and otherwise they are not valid sizes,
-		 the resulting range is in both cases [0, TYPE_MAX].  */
-	      min = wi::zero (expprec);
-	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-	    }
-	  else if (wi::les_p (min - 1, 0))
-	    {
-	      /* EXP is not in a negative-positive range.  That means EXP
-		 is either negative, or greater than max.  Since negative
-		 sizes are invalid make the range [MAX + 1, TYPE_MAX].  */
-	      min = max + 1;
-	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-	    }
-	  else
-	    {
-	      max = min - 1;
-	      min = wi::zero (expprec);
-	    }
-	}
-      else
-	{
-	  wide_int maxsize = wi::to_wide (max_object_size ());
-	  min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
-	  max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
-	  if (wi::eq_p (0, min - 1))
-	    {
-	      /* EXP is unsigned and not in the range [1, MAX].  That means
-		 it's either zero or greater than MAX.  Even though 0 would
-		 normally be detected by -Walloc-zero, unless ALLOW_ZERO
-		 is set, set the range to [MAX, TYPE_MAX] so that when MAX
-		 is greater than the limit the whole range is diagnosed.  */
-	      wide_int maxsize = wi::to_wide (max_object_size ());
-	      if (flags & SR_ALLOW_ZERO)
-		{
-		  if (wi::leu_p (maxsize, max + 1)
-		      || !(flags & SR_USE_LARGEST))
-		    min = max = wi::zero (expprec);
-		  else
-		    {
-		      min = max + 1;
-		      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-		    }
-		}
-	      else
-		{
-		  min = max + 1;
-		  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-		}
-	    }
-	  else if ((flags & SR_USE_LARGEST)
-		   && wi::ltu_p (max + 1, maxsize))
-	    {
-	      /* When USE_LARGEST is set and the larger of the two subranges
-		 is a valid size, use it...  */
-	      min = max + 1;
-	      max = maxsize;
-	    }
-	  else
-	    {
-	      /* ...otherwise use the smaller subrange.  */
-	      max = min - 1;
-	      min = wi::zero (expprec);
-	    }
-	}
-    }
-
-  range[0] = wide_int_to_tree (exptype, min);
-  range[1] = wide_int_to_tree (exptype, max);
-
-  return true;
-}
-
-bool
-get_size_range (tree exp, tree range[2], int flags /* = 0 */)
-{
-  return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags);
-}
-
-/* Diagnose a call EXP to function FN decorated with attribute alloc_size
-   whose argument numbers given by IDX with values given by ARGS exceed
-   the maximum object size or cause an unsigned oveflow (wrapping) when
-   multiplied.  FN is null when EXP is a call via a function pointer.
-   When ARGS[0] is null the function does nothing.  ARGS[1] may be null
-   for functions like malloc, and non-null for those like calloc that
-   are decorated with a two-argument attribute alloc_size.  */
-
-void
-maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
-{
-  /* The range each of the (up to) two arguments is known to be in.  */
-  tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } };
-
-  /* Maximum object size set by -Walloc-size-larger-than= or SIZE_MAX / 2.  */
-  tree maxobjsize = alloc_max_size ();
-
-  location_t loc = EXPR_LOCATION (exp);
-
-  tree fntype = fn ? TREE_TYPE (fn) : TREE_TYPE (TREE_TYPE (exp));
-  bool warned = false;
-
-  /* Validate each argument individually.  */
-  for (unsigned i = 0; i != 2 && args[i]; ++i)
-    {
-      if (TREE_CODE (args[i]) == INTEGER_CST)
-	{
-	  argrange[i][0] = args[i];
-	  argrange[i][1] = args[i];
-
-	  if (tree_int_cst_lt (args[i], integer_zero_node))
-	    {
-	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-				   "argument %i value %qE is negative",
-				   idx[i] + 1, args[i]);
-	    }
-	  else if (integer_zerop (args[i]))
-	    {
-	      /* Avoid issuing -Walloc-zero for allocation functions other
-		 than __builtin_alloca that are declared with attribute
-		 returns_nonnull because there's no portability risk.  This
-		 avoids warning for such calls to libiberty's xmalloc and
-		 friends.
-		 Also avoid issuing the warning for calls to function named
-		 "alloca".  */
-	      if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA)
-		  ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6
-		  : !lookup_attribute ("returns_nonnull",
-				       TYPE_ATTRIBUTES (fntype)))
-		warned = warning_at (loc, OPT_Walloc_zero,
-				     "argument %i value is zero",
-				     idx[i] + 1);
-	    }
-	  else if (tree_int_cst_lt (maxobjsize, args[i]))
-	    {
-	      /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98
-		 mode and with -fno-exceptions as a way to indicate array
-		 size overflow.  There's no good way to detect C++98 here
-		 so avoid diagnosing these calls for all C++ modes.  */
-	      if (i == 0
-		  && fn
-		  && !args[1]
-		  && lang_GNU_CXX ()
-		  && DECL_IS_OPERATOR_NEW_P (fn)
-		  && integer_all_onesp (args[i]))
-		continue;
-
-	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-				   "argument %i value %qE exceeds "
-				   "maximum object size %E",
-				   idx[i] + 1, args[i], maxobjsize);
-	    }
-	}
-      else if (TREE_CODE (args[i]) == SSA_NAME
-	       && get_size_range (args[i], argrange[i]))
-	{
-	  /* Verify that the argument's range is not negative (including
-	     upper bound of zero).  */
-	  if (tree_int_cst_lt (argrange[i][0], integer_zero_node)
-	      && tree_int_cst_le (argrange[i][1], integer_zero_node))
-	    {
-	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-				   "argument %i range [%E, %E] is negative",
-				   idx[i] + 1,
-				   argrange[i][0], argrange[i][1]);
-	    }
-	  else if (tree_int_cst_lt (maxobjsize, argrange[i][0]))
-	    {
-	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-				   "argument %i range [%E, %E] exceeds "
-				   "maximum object size %E",
-				   idx[i] + 1,
-				   argrange[i][0], argrange[i][1],
-				   maxobjsize);
-	    }
-	}
-    }
-
-  if (!argrange[0])
-    return;
-
-  /* For a two-argument alloc_size, validate the product of the two
-     arguments if both of their values or ranges are known.  */
-  if (!warned && tree_fits_uhwi_p (argrange[0][0])
-      && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0])
-      && !integer_onep (argrange[0][0])
-      && !integer_onep (argrange[1][0]))
-    {
-      /* Check for overflow in the product of a function decorated with
-	 attribute alloc_size (X, Y).  */
-      unsigned szprec = TYPE_PRECISION (size_type_node);
-      wide_int x = wi::to_wide (argrange[0][0], szprec);
-      wide_int y = wi::to_wide (argrange[1][0], szprec);
-
-      wi::overflow_type vflow;
-      wide_int prod = wi::umul (x, y, &vflow);
-
-      if (vflow)
-	warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-			     "product %<%E * %E%> of arguments %i and %i "
-			     "exceeds %<SIZE_MAX%>",
-			     argrange[0][0], argrange[1][0],
-			     idx[0] + 1, idx[1] + 1);
-      else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod))
-	warned = warning_at (loc, OPT_Walloc_size_larger_than_,
-			     "product %<%E * %E%> of arguments %i and %i "
-			     "exceeds maximum object size %E",
-			     argrange[0][0], argrange[1][0],
-			     idx[0] + 1, idx[1] + 1,
-			     maxobjsize);
-
-      if (warned)
-	{
-	  /* Print the full range of each of the two arguments to make
-	     it clear when it is, in fact, in a range and not constant.  */
-	  if (argrange[0][0] != argrange [0][1])
-	    inform (loc, "argument %i in the range [%E, %E]",
-		    idx[0] + 1, argrange[0][0], argrange[0][1]);
-	  if (argrange[1][0] != argrange [1][1])
-	    inform (loc, "argument %i in the range [%E, %E]",
-		    idx[1] + 1, argrange[1][0], argrange[1][1]);
-	}
-    }
-
-  if (warned && fn)
-    {
-      location_t fnloc = DECL_SOURCE_LOCATION (fn);
-
-      if (DECL_IS_UNDECLARED_BUILTIN (fn))
-	inform (loc,
-		"in a call to built-in allocation function %qD", fn);
-      else
-	inform (fnloc,
-		"in a call to allocation function %qD declared here", fn);
-    }
-}
-
-/* If EXPR refers to a character array or pointer declared attribute
-   nonstring return a decl for that array or pointer and set *REF to
-   the referenced enclosing object or pointer.  Otherwise returns
-   null.  */
-
-tree
-get_attr_nonstring_decl (tree expr, tree *ref)
-{
-  tree decl = expr;
-  tree var = NULL_TREE;
-  if (TREE_CODE (decl) == SSA_NAME)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (decl);
-
-      if (is_gimple_assign (def))
-	{
-	  tree_code code = gimple_assign_rhs_code (def);
-	  if (code == ADDR_EXPR
-	      || code == COMPONENT_REF
-	      || code == VAR_DECL)
-	    decl = gimple_assign_rhs1 (def);
-	}
-      else
-	var = SSA_NAME_VAR (decl);
-    }
-
-  if (TREE_CODE (decl) == ADDR_EXPR)
-    decl = TREE_OPERAND (decl, 0);
-
-  /* To simplify calling code, store the referenced DECL regardless of
-     the attribute determined below, but avoid storing the SSA_NAME_VAR
-     obtained above (it's not useful for dataflow purposes).  */
-  if (ref)
-    *ref = decl;
-
-  /* Use the SSA_NAME_VAR that was determined above to see if it's
-     declared nonstring.  Otherwise drill down into the referenced
-     DECL.  */
-  if (var)
-    decl = var;
-  else if (TREE_CODE (decl) == ARRAY_REF)
-    decl = TREE_OPERAND (decl, 0);
-  else if (TREE_CODE (decl) == COMPONENT_REF)
-    decl = TREE_OPERAND (decl, 1);
-  else if (TREE_CODE (decl) == MEM_REF)
-    return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
-
-  if (DECL_P (decl)
-      && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
-    return decl;
-
-  return NULL_TREE;
-}
-
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
@@ -1627,310 +1232,6 @@ maybe_complain_about_tail_call (tree call_expr, const char *reason)
   error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
 }
 
-/* Returns the type of the argument ARGNO to function with type FNTYPE
-   or null when the typoe cannot be determined or no such argument exists.  */
-
-static tree
-fntype_argno_type (tree fntype, unsigned argno)
-{
-  if (!prototype_p (fntype))
-    return NULL_TREE;
-
-  tree argtype;
-  function_args_iterator it;
-  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
-    if (argno-- == 0)
-      return argtype;
-
-  return NULL_TREE;
-}
-
-/* Helper to append the "human readable" attribute access specification
-   described by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
-   diagnostics.  */
-
-static inline void
-append_attrname (const std::pair<int, attr_access> &access,
-		 char *attrstr, size_t strsize)
-{
-  if (access.second.internal_p)
-    return;
-
-  tree str = access.second.to_external_string ();
-  gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str));
-  strcpy (attrstr, TREE_STRING_POINTER (str));
-}
-
-/* Iterate over attribute access read-only, read-write, and write-only
-   arguments and diagnose past-the-end accesses and related problems
-   in the function call EXP.  */
-
-static void
-maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
-{
-  auto_diagnostic_group adg;
-
-  /* Set if a warning has been issued for any argument (used to decide
-     whether to emit an informational note at the end).  */
-  opt_code opt_warned = N_OPTS;
-
-  /* A string describing the attributes that the warnings issued by this
-     function apply to.  Used to print one informational note per function
-     call, rather than one per warning.  That reduces clutter.  */
-  char attrstr[80];
-  attrstr[0] = 0;
-
-  for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
-    {
-      std::pair<int, attr_access> access = *it;
-
-      /* Get the function call arguments corresponding to the attribute's
-	 positional arguments.  When both arguments have been specified
-	 there will be two entries in *RWM, one for each.  They are
-	 cross-referenced by their respective argument numbers in
-	 ACCESS.PTRARG and ACCESS.SIZARG.  */
-      const int ptridx = access.second.ptrarg;
-      const int sizidx = access.second.sizarg;
-
-      gcc_assert (ptridx != -1);
-      gcc_assert (access.first == ptridx || access.first == sizidx);
-
-      /* The pointer is set to null for the entry corresponding to
-	 the size argument.  Skip it.  It's handled when the entry
-	 corresponding to the pointer argument comes up.  */
-      if (!access.second.ptr)
-	continue;
-
-      tree ptrtype = fntype_argno_type (fntype, ptridx);
-      tree argtype = TREE_TYPE (ptrtype);
-
-      /* The size of the access by the call.  */
-      tree access_size;
-      if (sizidx == -1)
-	{
-	  /* If only the pointer attribute operand was specified and
-	     not size, set SIZE to the greater of MINSIZE or size of
-	     one element of the pointed to type to detect smaller
-	     objects (null pointers are diagnosed in this case only
-	     if the pointer is also declared with attribute nonnull.  */
-	  if (access.second.minsize
-	      && access.second.minsize != HOST_WIDE_INT_M1U)
-	    access_size = build_int_cstu (sizetype, access.second.minsize);
-	  else
-	    access_size = size_one_node;
-	}
-      else
-	access_size = rwm->get (sizidx)->size;
-
-      /* Format the value or range to avoid an explosion of messages.  */
-      char sizstr[80];
-      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (access_size, sizrng, true))
-	{
-	  char *s0 = print_generic_expr_to_str (sizrng[0]);
-	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-	    {
-	      gcc_checking_assert (strlen (s0) < sizeof sizstr);
-	      strcpy (sizstr, s0);
-	    }
-	  else
-	    {
-	      char *s1 = print_generic_expr_to_str (sizrng[1]);
-	      gcc_checking_assert (strlen (s0) + strlen (s1)
-				   < sizeof sizstr - 4);
-	      sprintf (sizstr, "[%s, %s]", s0, s1);
-	      free (s1);
-	    }
-	  free (s0);
-	}
-      else
-	*sizstr = '\0';
-
-      /* Set if a warning has been issued for the current argument.  */
-      opt_code arg_warned = no_warning;
-      location_t loc = EXPR_LOCATION (exp);
-      tree ptr = access.second.ptr;
-      if (*sizstr
-	  && tree_int_cst_sgn (sizrng[0]) < 0
-	  && tree_int_cst_sgn (sizrng[1]) < 0)
-	{
-	  /* Warn about negative sizes.  */
-	  if (access.second.internal_p)
-	    {
-	      const std::string argtypestr
-		= access.second.array_as_string (ptrtype);
-
-	      if (warning_at (loc, OPT_Wstringop_overflow_,
-			      "bound argument %i value %s is "
-			      "negative for a variable length array "
-			      "argument %i of type %s",
-			      sizidx + 1, sizstr,
-			      ptridx + 1, argtypestr.c_str ()))
-		arg_warned = OPT_Wstringop_overflow_;
-	    }
-	  else if (warning_at (loc, OPT_Wstringop_overflow_,
-			       "argument %i value %s is negative",
-			       sizidx + 1, sizstr))
-	    arg_warned = OPT_Wstringop_overflow_;
-
-	  if (arg_warned != no_warning)
-	    {
-	      append_attrname (access, attrstr, sizeof attrstr);
-	      /* Remember a warning has been issued and avoid warning
-		 again below for the same attribute.  */
-	      opt_warned = arg_warned;
-	      continue;
-	    }
-	}
-
-      if (tree_int_cst_sgn (sizrng[0]) >= 0)
-	{
-	  if (COMPLETE_TYPE_P (argtype))
-	    {
-	      /* Multiply ACCESS_SIZE by the size of the type the pointer
-		 argument points to.  If it's incomplete the size is used
-		 as is.  */
-	      if (tree argsize = TYPE_SIZE_UNIT (argtype))
-		if (TREE_CODE (argsize) == INTEGER_CST)
-		  {
-		    const int prec = TYPE_PRECISION (sizetype);
-		    wide_int minsize = wi::to_wide (sizrng[0], prec);
-		    minsize *= wi::to_wide (argsize, prec);
-		    access_size = wide_int_to_tree (sizetype, minsize);
-		  }
-	    }
-	}
-      else
-	access_size = NULL_TREE;
-
-      if (integer_zerop (ptr))
-	{
-	  if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0)
-	    {
-	      /* Warn about null pointers with positive sizes.  This is
-		 different from also declaring the pointer argument with
-		 attribute nonnull when the function accepts null pointers
-		 only when the corresponding size is zero.  */
-	      if (access.second.internal_p)
-		{
-		  const std::string argtypestr
-		    = access.second.array_as_string (ptrtype);
-
-		  if (warning_at (loc, OPT_Wnonnull,
-				  "argument %i of variable length "
-				  "array %s is null but "
-				  "the corresponding bound argument "
-				  "%i value is %s",
-				  ptridx + 1, argtypestr.c_str (),
-				  sizidx + 1, sizstr))
-		    arg_warned = OPT_Wnonnull;
-		}
-	      else if (warning_at (loc, OPT_Wnonnull,
-				   "argument %i is null but "
-				   "the corresponding size argument "
-				   "%i value is %s",
-				   ptridx + 1, sizidx + 1, sizstr))
-		arg_warned = OPT_Wnonnull;
-	    }
-	  else if (access_size && access.second.static_p)
-	    {
-	      /* Warn about null pointers for [static N] array arguments
-		 but do not warn for ordinary (i.e., nonstatic) arrays.  */
-	      if (warning_at (loc, OPT_Wnonnull,
-			      "argument %i to %<%T[static %E]%> "
-			      "is null where non-null expected",
-			      ptridx + 1, argtype, access_size))
-		arg_warned = OPT_Wnonnull;		
-	    }
-
-	  if (arg_warned != no_warning)
-	    {
-	      append_attrname (access, attrstr, sizeof attrstr);
-	      /* Remember a warning has been issued and avoid warning
-		 again below for the same attribute.  */
-	      opt_warned = OPT_Wnonnull;
-	      continue;
-	    }
-	}
-
-      access_data data (ptr, access.second.mode, NULL_TREE, false,
-			NULL_TREE, false);
-      access_ref* const pobj = (access.second.mode == access_write_only
-				? &data.dst : &data.src);
-      tree objsize = compute_objsize (ptr, 1, pobj);
-
-      /* The size of the destination or source object.  */
-      tree dstsize = NULL_TREE, srcsize = NULL_TREE;
-      if (access.second.mode == access_read_only
-	  || access.second.mode == access_none)
-	{
-	  /* For a read-only argument there is no destination.  For
-	     no access, set the source as well and differentiate via
-	     the access flag below.  */
-	  srcsize = objsize;
-	  if (access.second.mode == access_read_only
-	      || access.second.mode == access_none)
-	    {
-	      /* For a read-only attribute there is no destination so
-		 clear OBJSIZE.  This emits "reading N bytes" kind of
-		 diagnostics instead of the "writing N bytes" kind,
-		 unless MODE is none.  */
-	      objsize = NULL_TREE;
-	    }
-	}
-      else
-	dstsize = objsize;
-
-      /* Clear the no-warning bit in case it was set by check_access
-	 in a prior iteration so that accesses via different arguments
-	 are diagnosed.  */
-      suppress_warning (exp, OPT_Wstringop_overflow_, false);
-      access_mode mode = data.mode;
-      if (mode == access_deferred)
-	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
-      check_access (exp, access_size, /*maxread=*/ NULL_TREE, srcsize,
-		    dstsize, mode, &data);
-
-      if (warning_suppressed_p (exp, OPT_Wstringop_overflow_))
-	opt_warned = OPT_Wstringop_overflow_;
-      if (opt_warned != N_OPTS)
-	{
-	  if (access.second.internal_p)
-	    inform (loc, "referencing argument %u of type %qT",
-		    ptridx + 1, ptrtype);
-	  else
-	    /* If check_access issued a warning above, append the relevant
-	       attribute to the string.  */
-	    append_attrname (access, attrstr, sizeof attrstr);
-	}
-    }
-
-  if (*attrstr)
-    {
-      if (fndecl)
-	inform (DECL_SOURCE_LOCATION (fndecl),
-		"in a call to function %qD declared with attribute %qs",
-		fndecl, attrstr);
-      else
-	inform (EXPR_LOCATION (fndecl),
-		"in a call with type %qT and attribute %qs",
-		fntype, attrstr);
-    }
-  else if (opt_warned != N_OPTS)
-    {
-      if (fndecl)
-	inform (DECL_SOURCE_LOCATION (fndecl),
-		"in a call to function %qD", fndecl);
-      else
-	inform (EXPR_LOCATION (fndecl),
-		"in a call with type %qT", fntype);
-    }
-
-  /* Set the bit in case if was cleared and not set above.  */
-  if (opt_warned != N_OPTS)
-    suppress_warning (exp, opt_warned);
-}
-
 /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
    CALL_EXPR EXP.
 
@@ -2030,27 +1331,6 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 
   bitmap_obstack_release (NULL);
 
-  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
-  /* Extract attribute alloc_size from the type of the called expression
-     (which could be a function or a function pointer) and if set, store
-     the indices of the corresponding arguments in ALLOC_IDX, and then
-     the actual argument(s) at those indices in ALLOC_ARGS.  */
-  int alloc_idx[2] = { -1, -1 };
-  if (tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs))
-    {
-      tree args = TREE_VALUE (alloc_size);
-      alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
-      if (TREE_CHAIN (args))
-	alloc_idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
-    }
-
-  /* Array for up to the two attribute alloc_size arguments.  */
-  tree alloc_args[] = { NULL_TREE, NULL_TREE };
-
-  /* Map of attribute accewss specifications for function arguments.  */
-  rdwr_map rdwr_idx;
-  init_attr_rdwr_indices (&rdwr_idx, fntypeattrs);
-
   /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
   for (argpos = 0; argpos < num_actuals; i--, argpos++)
     {
@@ -2283,44 +1563,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 	 does pass the promoted mode.  */
       arg.mode = TYPE_MODE (type);
       targetm.calls.function_arg_advance (args_so_far, arg);
-
-      /* Store argument values for functions decorated with attribute
-	 alloc_size.  */
-      if (argpos == alloc_idx[0])
-	alloc_args[0] = args[i].tree_value;
-      else if (argpos == alloc_idx[1])
-	alloc_args[1] = args[i].tree_value;
-
-      /* Save the actual argument that corresponds to the access attribute
-	 operand for later processing.  */
-      if (attr_access *access = rdwr_idx.get (argpos))
-	{
-	  if (POINTER_TYPE_P (type))
-	    {
-	      access->ptr = args[i].tree_value;
-	      // A nonnull ACCESS->SIZE contains VLA bounds.  */
-	    }
-	  else
-	    {
-	      access->size = args[i].tree_value;
-	      gcc_assert (access->ptr == NULL_TREE);
-	    }
-	}
-    }
-
-  if (alloc_args[0])
-    {
-      /* Check the arguments of functions decorated with attribute
-	 alloc_size.  */
-      maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
     }
-
-  /* Detect passing non-string arguments to functions expecting
-     nul-terminated strings.  */
-  maybe_warn_nonstring_arg (fndecl, exp);
-
-  /* Check attribute access arguments.  */
-  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, exp);
 }
 
 /* Update ARGS_SIZE to contain the total size for the argument block.
@@ -6020,6 +5263,3 @@ cxx17_empty_base_field_p (const_tree field)
 	  && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
 	  && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
 }
-
-/* Tell the garbage collector about GTY markers in this source file.  */
-#include "gt-calls.h"
diff --git a/gcc/calls.h b/gcc/calls.h
index 4a018f6ae85..9a98f5de75f 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -130,21 +130,8 @@ extern bool apply_pass_by_reference_rules (CUMULATIVE_ARGS *,
 					   function_arg_info &);
 extern bool reference_callee_copied (CUMULATIVE_ARGS *,
 				     const function_arg_info &);
-extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern tree get_attr_nonstring_decl (tree, tree * = NULL);
-extern bool maybe_warn_nonstring_arg (tree, tree);
 extern void maybe_complain_about_tail_call (tree, const char *);
-enum size_range_flags
-  {
-   /* Set to consider zero a valid range.  */
-   SR_ALLOW_ZERO = 1,
-   /* Set to use the largest subrange of a set of ranges as opposed
-      to the smallest.  */
-   SR_USE_LARGEST = 2
-  };
-extern bool get_size_range (tree, tree[2], int = 0);
-extern bool get_size_range (class range_query *, tree, gimple *,
-			    tree[2], int = 0);
+
 extern rtx rtx_for_static_chain (const_tree, bool);
 extern bool cxx17_empty_base_field_p (const_tree);
 
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 93f43b711e2..f3efe564af0 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -20,6 +20,7 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
+#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -36,6 +37,7 @@
 #include "fold-const.h"
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
+#include "langhooks.h"
 #include "tree-dfa.h"
 #include "tree-ssa.h"
 #include "tree-cfg.h"
@@ -50,14 +52,6 @@
 #include "demangle.h"
 #include "pointer-query.h"
 
-/* Return true if STMT has an associated location.  */
-
-static inline location_t
-has_location (const gimple *stmt)
-{
-  return gimple_has_location (stmt);
-}
-
 /* Return true if tree node X has an associated location.  */
 
 static inline location_t
@@ -1177,7 +1171,7 @@ warn_for_access (location_t loc, tree func, tree expr, int opt,
 /* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
    by BNDRNG if nonnull and valid.  */
 
-void
+static void
 get_size_range (tree bound, tree range[2], const offset_int bndrng[2])
 {
   if (bound)
@@ -2105,14 +2099,14 @@ warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
    form of C++ operatorn new.  */
 
 static void
-maybe_emit_free_warning (gcall *call)
+maybe_check_dealloc_call (gcall *call)
 {
   tree fndecl = gimple_call_fndecl (call);
   if (!fndecl)
     return;
 
   unsigned argno = fndecl_dealloc_argno (fndecl);
-  if ((unsigned) gimple_call_num_args (call) <= argno)
+  if ((unsigned) call_nargs (call) <= argno)
     return;
 
   tree ptr = gimple_call_arg (call, argno);
@@ -2246,9 +2240,9 @@ const pass_data pass_data_waccess = {
 class pass_waccess : public gimple_opt_pass
 {
  public:
-  pass_waccess (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_waccess, ctxt), m_ranger ()
-    { }
+  pass_waccess (gcc::context *);
+
+  ~pass_waccess ();
 
   opt_pass *clone () { return new pass_waccess (m_ctxt); }
 
@@ -2258,6 +2252,9 @@ class pass_waccess : public gimple_opt_pass
   /* Check a call to a built-in function.  */
   bool check_builtin (gcall *);
 
+  /* Check a call to an ordinary function.  */
+  bool check_call (gcall *);
+
   /* Check statements in a basic block.  */
   void check (basic_block);
 
@@ -2265,9 +2262,35 @@ class pass_waccess : public gimple_opt_pass
  void check (gcall *);
 
 private:
+  /* Not copyable or assignable.  */
+  pass_waccess (pass_waccess &) = delete;
+  void operator= (pass_waccess &) = delete;
+
+  /* A pointer_query object and its cache to store information about
+     pointers and their targets in.  */
+  pointer_query ptr_qry;
+  pointer_query::cache_type var_cache;
+
   gimple_ranger *m_ranger;
 };
 
+/* Construct the pass.  */
+
+pass_waccess::pass_waccess (gcc::context *ctxt)
+  : gimple_opt_pass (pass_data_waccess, ctxt),
+    ptr_qry (m_ranger, &var_cache),
+    var_cache (),
+    m_ranger ()
+{
+}
+
+/* Release pointer_query cache.  */
+
+pass_waccess::~pass_waccess ()
+{
+  ptr_qry.flush_cache ();
+}
+
 /* Return true when any checks performed by the pass are enabled.  */
 
 bool
@@ -2278,12 +2301,256 @@ pass_waccess::gate (function *)
 	  || warn_mismatched_new_delete);
 }
 
+/* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than=
+   setting if the option is specified, or to the maximum object size if it
+   is not.  Return the initialized value.  */
+
+static tree
+alloc_max_size (void)
+{
+  HOST_WIDE_INT limit = warn_alloc_size_limit;
+  if (limit == HOST_WIDE_INT_MAX)
+    limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
+
+  return build_int_cst (size_type_node, limit);
+}
+
+/* Diagnose a call EXP to function FN decorated with attribute alloc_size
+   whose argument numbers given by IDX with values given by ARGS exceed
+   the maximum object size or cause an unsigned oveflow (wrapping) when
+   multiplied.  FN is null when EXP is a call via a function pointer.
+   When ARGS[0] is null the function does nothing.  ARGS[1] may be null
+   for functions like malloc, and non-null for those like calloc that
+   are decorated with a two-argument attribute alloc_size.  */
+
+void
+maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
+				const int idx[2])
+{
+  /* The range each of the (up to) two arguments is known to be in.  */
+  tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } };
+
+  /* Maximum object size set by -Walloc-size-larger-than= or SIZE_MAX / 2.  */
+  tree maxobjsize = alloc_max_size ();
+
+  location_t loc = get_location (stmt);
+
+  tree fn = gimple_call_fndecl (stmt);
+  tree fntype = fn ? TREE_TYPE (fn) : gimple_call_fntype (stmt);
+  bool warned = false;
+
+  /* Validate each argument individually.  */
+  for (unsigned i = 0; i != 2 && args[i]; ++i)
+    {
+      if (TREE_CODE (args[i]) == INTEGER_CST)
+	{
+	  argrange[i][0] = args[i];
+	  argrange[i][1] = args[i];
+
+	  if (tree_int_cst_lt (args[i], integer_zero_node))
+	    {
+	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+				   "argument %i value %qE is negative",
+				   idx[i] + 1, args[i]);
+	    }
+	  else if (integer_zerop (args[i]))
+	    {
+	      /* Avoid issuing -Walloc-zero for allocation functions other
+		 than __builtin_alloca that are declared with attribute
+		 returns_nonnull because there's no portability risk.  This
+		 avoids warning for such calls to libiberty's xmalloc and
+		 friends.
+		 Also avoid issuing the warning for calls to function named
+		 "alloca".  */
+	      if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA)
+		  ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6
+		  : !lookup_attribute ("returns_nonnull",
+				       TYPE_ATTRIBUTES (fntype)))
+		warned = warning_at (loc, OPT_Walloc_zero,
+				     "argument %i value is zero",
+				     idx[i] + 1);
+	    }
+	  else if (tree_int_cst_lt (maxobjsize, args[i]))
+	    {
+	      /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98
+		 mode and with -fno-exceptions as a way to indicate array
+		 size overflow.  There's no good way to detect C++98 here
+		 so avoid diagnosing these calls for all C++ modes.  */
+	      if (i == 0
+		  && fn
+		  && !args[1]
+		  && lang_GNU_CXX ()
+		  && DECL_IS_OPERATOR_NEW_P (fn)
+		  && integer_all_onesp (args[i]))
+		continue;
+
+	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+				   "argument %i value %qE exceeds "
+				   "maximum object size %E",
+				   idx[i] + 1, args[i], maxobjsize);
+	    }
+	}
+      else if (TREE_CODE (args[i]) == SSA_NAME
+	       && get_size_range (args[i], argrange[i]))
+	{
+	  /* Verify that the argument's range is not negative (including
+	     upper bound of zero).  */
+	  if (tree_int_cst_lt (argrange[i][0], integer_zero_node)
+	      && tree_int_cst_le (argrange[i][1], integer_zero_node))
+	    {
+	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+				   "argument %i range [%E, %E] is negative",
+				   idx[i] + 1,
+				   argrange[i][0], argrange[i][1]);
+	    }
+	  else if (tree_int_cst_lt (maxobjsize, argrange[i][0]))
+	    {
+	      warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+				   "argument %i range [%E, %E] exceeds "
+				   "maximum object size %E",
+				   idx[i] + 1,
+				   argrange[i][0], argrange[i][1],
+				   maxobjsize);
+	    }
+	}
+    }
+
+  if (!argrange[0])
+    return;
+
+  /* For a two-argument alloc_size, validate the product of the two
+     arguments if both of their values or ranges are known.  */
+  if (!warned && tree_fits_uhwi_p (argrange[0][0])
+      && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0])
+      && !integer_onep (argrange[0][0])
+      && !integer_onep (argrange[1][0]))
+    {
+      /* Check for overflow in the product of a function decorated with
+	 attribute alloc_size (X, Y).  */
+      unsigned szprec = TYPE_PRECISION (size_type_node);
+      wide_int x = wi::to_wide (argrange[0][0], szprec);
+      wide_int y = wi::to_wide (argrange[1][0], szprec);
+
+      wi::overflow_type vflow;
+      wide_int prod = wi::umul (x, y, &vflow);
+
+      if (vflow)
+	warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+			     "product %<%E * %E%> of arguments %i and %i "
+			     "exceeds %<SIZE_MAX%>",
+			     argrange[0][0], argrange[1][0],
+			     idx[0] + 1, idx[1] + 1);
+      else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod))
+	warned = warning_at (loc, OPT_Walloc_size_larger_than_,
+			     "product %<%E * %E%> of arguments %i and %i "
+			     "exceeds maximum object size %E",
+			     argrange[0][0], argrange[1][0],
+			     idx[0] + 1, idx[1] + 1,
+			     maxobjsize);
+
+      if (warned)
+	{
+	  /* Print the full range of each of the two arguments to make
+	     it clear when it is, in fact, in a range and not constant.  */
+	  if (argrange[0][0] != argrange [0][1])
+	    inform (loc, "argument %i in the range [%E, %E]",
+		    idx[0] + 1, argrange[0][0], argrange[0][1]);
+	  if (argrange[1][0] != argrange [1][1])
+	    inform (loc, "argument %i in the range [%E, %E]",
+		    idx[1] + 1, argrange[1][0], argrange[1][1]);
+	}
+    }
+
+  if (warned && fn)
+    {
+      location_t fnloc = DECL_SOURCE_LOCATION (fn);
+
+      if (DECL_IS_UNDECLARED_BUILTIN (fn))
+	inform (loc,
+		"in a call to built-in allocation function %qD", fn);
+      else
+	inform (fnloc,
+		"in a call to allocation function %qD declared here", fn);
+    }
+}
+
+/* Check a call to an alloca function for an excessive size.  */
+
+static void
+check_alloca (gimple *stmt)
+{
+  if ((warn_vla_limit >= HOST_WIDE_INT_MAX
+       && warn_alloc_size_limit < warn_vla_limit)
+      || (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  HWI_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.  */
+      const tree alloc_args[] = { call_arg (stmt, 0), NULL_TREE };
+      const int idx[] = { 0, -1 };
+      maybe_warn_alloc_args_overflow (stmt, alloc_args, idx);
+    }
+}
+
+/* Check a call to an allocation function for an excessive size.  */
+
+static void
+check_alloc_size_call (gimple *stmt)
+{
+  if (gimple_call_num_args (stmt) < 1)
+    /* Avoid invalid calls to functions without a prototype.  */
+    return;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  if (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      /* Alloca is handled separately.  */
+      switch (DECL_FUNCTION_CODE (fndecl))
+	{
+	case BUILT_IN_ALLOCA:
+	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
+	  return;
+	default:
+	  break;
+	}
+    }
+
+  tree fntype = gimple_call_fntype (stmt);
+  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
+
+  tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs);
+  if (!alloc_size)
+    return;
+
+  /* Extract attribute alloc_size from the type of the called expression
+     (which could be a function or a function pointer) and if set, store
+     the indices of the corresponding arguments in ALLOC_IDX, and then
+     the actual argument(s) at those indices in ALLOC_ARGS.  */
+  int idx[2] = { -1, -1 };
+  tree alloc_args[] = { NULL_TREE, NULL_TREE };
+
+  tree args = TREE_VALUE (alloc_size);
+  idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+  alloc_args[0] = call_arg (stmt, idx[0]);
+  if (TREE_CHAIN (args))
+    {
+      idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+      alloc_args[1] = call_arg (stmt, idx[1]);
+    }
+
+  maybe_warn_alloc_args_overflow (stmt, alloc_args, idx);
+}
+
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
 static void
 check_strcat (gimple *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
   tree dest = call_arg (stmt, 0);
@@ -2308,7 +2575,7 @@ check_strcat (gimple *stmt)
 static void
 check_strncat (gimple *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
   tree dest = call_arg (stmt, 0);
@@ -2537,6 +2804,12 @@ pass_waccess::check_builtin (gcall *stmt)
 
   switch (DECL_FUNCTION_CODE (callee))
     {
+    case BUILT_IN_ALLOCA:
+    case BUILT_IN_ALLOCA_WITH_ALIGN:
+    case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
+      check_alloca (stmt);
+      return true;
+
     case BUILT_IN_GETTEXT:
     case BUILT_IN_PUTS:
     case BUILT_IN_PUTS_UNLOCKED:
@@ -2639,16 +2912,384 @@ pass_waccess::check_builtin (gcall *stmt)
   return true;
 }
 
+/* Returns the type of the argument ARGNO to function with type FNTYPE
+   or null when the typoe cannot be determined or no such argument exists.  */
+
+static tree
+fntype_argno_type (tree fntype, unsigned argno)
+{
+  if (!prototype_p (fntype))
+    return NULL_TREE;
+
+  tree argtype;
+  function_args_iterator it;
+  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
+    if (argno-- == 0)
+      return argtype;
+
+  return NULL_TREE;
+}
+
+/* Helper to append the "human readable" attribute access specification
+   described by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
+   diagnostics.  */
+
+static inline void
+append_attrname (const std::pair<int, attr_access> &access,
+		 char *attrstr, size_t strsize)
+{
+  if (access.second.internal_p)
+    return;
+
+  tree str = access.second.to_external_string ();
+  gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str));
+  strcpy (attrstr, TREE_STRING_POINTER (str));
+}
+
+/* Iterate over attribute access read-only, read-write, and write-only
+   arguments and diagnose past-the-end accesses and related problems
+   in the function call EXP.  */
+
+static void
+maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
+{
+  auto_diagnostic_group adg;
+
+  /* Set if a warning has been issued for any argument (used to decide
+     whether to emit an informational note at the end).  */
+  opt_code opt_warned = no_warning;
+
+  /* A string describing the attributes that the warnings issued by this
+     function apply to.  Used to print one informational note per function
+     call, rather than one per warning.  That reduces clutter.  */
+  char attrstr[80];
+  attrstr[0] = 0;
+
+  for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
+    {
+      std::pair<int, attr_access> access = *it;
+
+      /* Get the function call arguments corresponding to the attribute's
+	 positional arguments.  When both arguments have been specified
+	 there will be two entries in *RWM, one for each.  They are
+	 cross-referenced by their respective argument numbers in
+	 ACCESS.PTRARG and ACCESS.SIZARG.  */
+      const int ptridx = access.second.ptrarg;
+      const int sizidx = access.second.sizarg;
+
+      gcc_assert (ptridx != -1);
+      gcc_assert (access.first == ptridx || access.first == sizidx);
+
+      /* The pointer is set to null for the entry corresponding to
+	 the size argument.  Skip it.  It's handled when the entry
+	 corresponding to the pointer argument comes up.  */
+      if (!access.second.ptr)
+	continue;
+
+      tree ptrtype = fntype_argno_type (fntype, ptridx);
+      tree argtype = TREE_TYPE (ptrtype);
+
+      /* The size of the access by the call.  */
+      tree access_size;
+      if (sizidx == -1)
+	{
+	  /* If only the pointer attribute operand was specified and
+	     not size, set SIZE to the greater of MINSIZE or size of
+	     one element of the pointed to type to detect smaller
+	     objects (null pointers are diagnosed in this case only
+	     if the pointer is also declared with attribute nonnull.  */
+	  if (access.second.minsize
+	      && access.second.minsize != HOST_WIDE_INT_M1U)
+	    access_size = build_int_cstu (sizetype, access.second.minsize);
+	  else
+	    access_size = size_one_node;
+	}
+      else
+	access_size = rwm->get (sizidx)->size;
+
+      /* Format the value or range to avoid an explosion of messages.  */
+      char sizstr[80];
+      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
+      if (get_size_range (access_size, sizrng, true))
+	{
+	  char *s0 = print_generic_expr_to_str (sizrng[0]);
+	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+	    {
+	      gcc_checking_assert (strlen (s0) < sizeof sizstr);
+	      strcpy (sizstr, s0);
+	    }
+	  else
+	    {
+	      char *s1 = print_generic_expr_to_str (sizrng[1]);
+	      gcc_checking_assert (strlen (s0) + strlen (s1)
+				   < sizeof sizstr - 4);
+	      sprintf (sizstr, "[%s, %s]", s0, s1);
+	      free (s1);
+	    }
+	  free (s0);
+	}
+      else
+	*sizstr = '\0';
+
+      /* Set if a warning has been issued for the current argument.  */
+      opt_code arg_warned = no_warning;
+      location_t loc = get_location (stmt);
+      tree ptr = access.second.ptr;
+      if (*sizstr
+	  && tree_int_cst_sgn (sizrng[0]) < 0
+	  && tree_int_cst_sgn (sizrng[1]) < 0)
+	{
+	  /* Warn about negative sizes.  */
+	  if (access.second.internal_p)
+	    {
+	      const std::string argtypestr
+		= access.second.array_as_string (ptrtype);
+
+	      if (warning_at (loc, OPT_Wstringop_overflow_,
+			      "bound argument %i value %s is "
+			      "negative for a variable length array "
+			      "argument %i of type %s",
+			      sizidx + 1, sizstr,
+			      ptridx + 1, argtypestr.c_str ()))
+		arg_warned = OPT_Wstringop_overflow_;
+	    }
+	  else if (warning_at (loc, OPT_Wstringop_overflow_,
+			       "argument %i value %s is negative",
+			       sizidx + 1, sizstr))
+	    arg_warned = OPT_Wstringop_overflow_;
+
+	  if (arg_warned != no_warning)
+	    {
+	      append_attrname (access, attrstr, sizeof attrstr);
+	      /* Remember a warning has been issued and avoid warning
+		 again below for the same attribute.  */
+	      opt_warned = arg_warned;
+	      continue;
+	    }
+	}
+
+      if (tree_int_cst_sgn (sizrng[0]) >= 0)
+	{
+	  if (COMPLETE_TYPE_P (argtype))
+	    {
+	      /* Multiply ACCESS_SIZE by the size of the type the pointer
+		 argument points to.  If it's incomplete the size is used
+		 as is.  */
+	      if (tree argsize = TYPE_SIZE_UNIT (argtype))
+		if (TREE_CODE (argsize) == INTEGER_CST)
+		  {
+		    const int prec = TYPE_PRECISION (sizetype);
+		    wide_int minsize = wi::to_wide (sizrng[0], prec);
+		    minsize *= wi::to_wide (argsize, prec);
+		    access_size = wide_int_to_tree (sizetype, minsize);
+		  }
+	    }
+	}
+      else
+	access_size = NULL_TREE;
+
+      if (integer_zerop (ptr))
+	{
+	  if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0)
+	    {
+	      /* Warn about null pointers with positive sizes.  This is
+		 different from also declaring the pointer argument with
+		 attribute nonnull when the function accepts null pointers
+		 only when the corresponding size is zero.  */
+	      if (access.second.internal_p)
+		{
+		  const std::string argtypestr
+		    = access.second.array_as_string (ptrtype);
+
+		  if (warning_at (loc, OPT_Wnonnull,
+				  "argument %i of variable length "
+				  "array %s is null but "
+				  "the corresponding bound argument "
+				  "%i value is %s",
+				  ptridx + 1, argtypestr.c_str (),
+				  sizidx + 1, sizstr))
+		    arg_warned = OPT_Wnonnull;
+		}
+	      else if (warning_at (loc, OPT_Wnonnull,
+				   "argument %i is null but "
+				   "the corresponding size argument "
+				   "%i value is %s",
+				   ptridx + 1, sizidx + 1, sizstr))
+		arg_warned = OPT_Wnonnull;
+	    }
+	  else if (access_size && access.second.static_p)
+	    {
+	      /* Warn about null pointers for [static N] array arguments
+		 but do not warn for ordinary (i.e., nonstatic) arrays.  */
+	      if (warning_at (loc, OPT_Wnonnull,
+			      "argument %i to %<%T[static %E]%> "
+			      "is null where non-null expected",
+			      ptridx + 1, argtype, access_size))
+		arg_warned = OPT_Wnonnull;
+	    }
+
+	  if (arg_warned != no_warning)
+	    {
+	      append_attrname (access, attrstr, sizeof attrstr);
+	      /* Remember a warning has been issued and avoid warning
+		 again below for the same attribute.  */
+	      opt_warned = OPT_Wnonnull;
+	      continue;
+	    }
+	}
+
+      access_data data (ptr, access.second.mode, NULL_TREE, false,
+			NULL_TREE, false);
+      access_ref* const pobj = (access.second.mode == access_write_only
+				? &data.dst : &data.src);
+      tree objsize = compute_objsize (ptr, 1, pobj);
+
+      /* The size of the destination or source object.  */
+      tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+      if (access.second.mode == access_read_only
+	  || access.second.mode == access_none)
+	{
+	  /* For a read-only argument there is no destination.  For
+	     no access, set the source as well and differentiate via
+	     the access flag below.  */
+	  srcsize = objsize;
+	  if (access.second.mode == access_read_only
+	      || access.second.mode == access_none)
+	    {
+	      /* For a read-only attribute there is no destination so
+		 clear OBJSIZE.  This emits "reading N bytes" kind of
+		 diagnostics instead of the "writing N bytes" kind,
+		 unless MODE is none.  */
+	      objsize = NULL_TREE;
+	    }
+	}
+      else
+	dstsize = objsize;
+
+      /* Clear the no-warning bit in case it was set by check_access
+	 in a prior iteration so that accesses via different arguments
+	 are diagnosed.  */
+      suppress_warning (stmt, OPT_Wstringop_overflow_, false);
+      access_mode mode = data.mode;
+      if (mode == access_deferred)
+	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
+      check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize,
+		    dstsize, mode, &data);
+
+      if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
+	opt_warned = OPT_Wstringop_overflow_;
+      if (opt_warned != no_warning)
+	{
+	  if (access.second.internal_p)
+	    inform (loc, "referencing argument %u of type %qT",
+		    ptridx + 1, ptrtype);
+	  else
+	    /* If check_access issued a warning above, append the relevant
+	       attribute to the string.  */
+	    append_attrname (access, attrstr, sizeof attrstr);
+	}
+    }
+
+  if (*attrstr)
+    {
+      if (fndecl)
+	inform (get_location (fndecl),
+		"in a call to function %qD declared with attribute %qs",
+		fndecl, attrstr);
+      else
+	inform (get_location (stmt),
+		"in a call with type %qT and attribute %qs",
+		fntype, attrstr);
+    }
+  else if (opt_warned != no_warning)
+    {
+      if (fndecl)
+	inform (get_location (fndecl),
+		"in a call to function %qD", fndecl);
+      else
+	inform (get_location (stmt),
+		"in a call with type %qT", fntype);
+    }
+
+  /* Set the bit in case if was cleared and not set above.  */
+  if (opt_warned != no_warning)
+    suppress_warning (stmt, opt_warned);
+}
+
+/* Check call STMT to an ordinary (non-built-in) function for invalid
+   accesses.  Return true if a call has been handled.  */
+
+bool
+pass_waccess::check_call (gcall *stmt)
+{
+  tree fntype = gimple_call_fntype (stmt);
+  if (!fntype)
+    return false;
+
+  tree fntypeattrs = TYPE_ATTRIBUTES (fntype);
+  if (!fntypeattrs)
+    return false;
+
+  /* Map of attribute accewss specifications for function arguments.  */
+  rdwr_map rdwr_idx;
+  init_attr_rdwr_indices (&rdwr_idx, fntypeattrs);
+
+  unsigned nargs = call_nargs (stmt);
+  for (unsigned i = 0; i != nargs; ++i)
+    {
+      tree arg = call_arg (stmt, i);
+
+      /* Save the actual argument that corresponds to the access attribute
+	 operand for later processing.  */
+      if (attr_access *access = rdwr_idx.get (i))
+	{
+	  if (POINTER_TYPE_P (TREE_TYPE (arg)))
+	    {
+	      access->ptr = arg;
+	      // A nonnull ACCESS->SIZE contains VLA bounds.  */
+	    }
+	  else
+	    {
+	      access->size = arg;
+	      gcc_assert (access->ptr == NULL_TREE);
+	    }
+	}
+    }
+
+  /* Check attribute access arguments.  */
+  tree fndecl = gimple_call_fndecl (stmt);
+  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
+
+  check_alloc_size_call (stmt);
+  return true;
+}
+
+/* Check arguments in a call STMT for attribute nonstring.  */
+
+static void
+check_nonstring_args (gcall *stmt)
+{
+  tree fndecl = gimple_call_fndecl (stmt);
+
+  /* Detect passing non-string arguments to functions expecting
+     nul-terminated strings.  */
+  maybe_warn_nonstring_arg (fndecl, stmt);
+}
+
 /* Check call STMT for invalid accesses.  */
 
 void
 pass_waccess::check (gcall *stmt)
 {
-  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
-      && check_builtin (stmt))
-    return;
+  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    check_builtin (stmt);
 
-  maybe_emit_free_warning (stmt);
+  if (is_gimple_call (stmt))
+    check_call (stmt);
+
+  maybe_check_dealloc_call (stmt);
+
+  check_nonstring_args (stmt);
 }
 
 /* Check basic block BB for invalid accesses.  */
@@ -2669,6 +3310,8 @@ pass_waccess::check (basic_block bb)
 unsigned
 pass_waccess::execute (function *fun)
 {
+  m_ranger = enable_ranger (fun);
+
   basic_block bb;
   FOR_EACH_BB_FN (bb, fun)
     check (bb);
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 8b33ecbd4fb..1cd3a28c421 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -31,7 +31,9 @@ extern void warn_string_no_nul (location_t, tree, const char *, tree,
 				tree, tree = NULL_TREE, bool = false,
 				const wide_int[2] = NULL);
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
-extern void get_size_range (tree, tree[2], const offset_int[2]);
+
+extern bool maybe_warn_nonstring_arg (tree, gimple *);
+extern bool maybe_warn_nonstring_arg (tree, tree);
 
 class access_data;
 extern bool maybe_warn_for_bound (opt_code, location_t, gimple *, tree,
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index 404acb03195..d1df9ca8d4f 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -29,6 +29,7 @@
 #include "pointer-query.h"
 #include "ssa.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-access.h"
 #include "gimple-ssa-warn-restrict.h"
 #include "diagnostic-core.h"
 #include "fold-const.h"
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index bc7cac092f5..99caf78bfa7 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -22,58 +22,21 @@
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "target.h"
-#include "rtl.h"
 #include "tree.h"
-#include "memmodel.h"
 #include "gimple.h"
-#include "predict.h"
-#include "tm_p.h"
 #include "stringpool.h"
 #include "tree-vrp.h"
-#include "tree-ssanames.h"
-#include "expmed.h"
-#include "optabs.h"
-#include "emit-rtl.h"
-#include "recog.h"
 #include "diagnostic-core.h"
-#include "alias.h"
 #include "fold-const.h"
-#include "fold-const-call.h"
-#include "gimple-ssa-warn-restrict.h"
-#include "stor-layout.h"
-#include "calls.h"
-#include "varasm.h"
 #include "tree-object-size.h"
 #include "tree-ssa-strlen.h"
-#include "realmpfr.h"
-#include "cfgrtl.h"
-#include "except.h"
-#include "dojump.h"
-#include "explow.h"
-#include "stmt.h"
-#include "expr.h"
-#include "libfuncs.h"
-#include "output.h"
-#include "typeclass.h"
 #include "langhooks.h"
-#include "value-prof.h"
-#include "builtins.h"
 #include "stringpool.h"
 #include "attribs.h"
-#include "asan.h"
-#include "internal-fn.h"
-#include "case-cfn-macros.h"
 #include "gimple-fold.h"
 #include "intl.h"
-#include "tree-dfa.h"
-#include "gimple-iterator.h"
-#include "gimple-ssa.h"
-#include "tree-ssa-live.h"
-#include "tree-outof-ssa.h"
 #include "attr-fnspec.h"
 #include "gimple-range.h"
-
 #include "pointer-query.h"
 
 static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
@@ -311,6 +274,164 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
   return NULL_TREE;
 }
 
+/* Return true when EXP's range can be determined and set RANGE[] to it
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.
+   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
+   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
+   a (nearly) invalid argument to allocation functions like malloc but it
+   is a valid argument to functions like memset.
+   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
+   in a multi-range, otherwise to the smallest valid subrange.  */
+
+bool
+get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
+		int flags /* = 0 */)
+{
+  if (!exp)
+    return false;
+
+  if (tree_fits_uhwi_p (exp))
+    {
+      /* EXP is a constant.  */
+      range[0] = range[1] = exp;
+      return true;
+    }
+
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
+  wide_int min, max;
+  enum value_range_kind range_type;
+
+  if (!query)
+    query = get_global_range_query ();
+
+  if (integral)
+    {
+      value_range vr;
+
+      query->range_of_expr (vr, exp, stmt);
+
+      if (vr.undefined_p ())
+	vr.set_varying (TREE_TYPE (exp));
+      range_type = vr.kind ();
+      min = wi::to_wide (vr.min ());
+      max = wi::to_wide (vr.max ());
+    }
+  else
+    range_type = VR_VARYING;
+
+  if (range_type == VR_VARYING)
+    {
+      if (integral)
+	{	
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
+      range[0] = NULL_TREE;
+      range[1] = NULL_TREE;
+      return false;
+    }
+
+  unsigned expprec = TYPE_PRECISION (exptype);
+
+  bool signed_p = !TYPE_UNSIGNED (exptype);
+
+  if (range_type == VR_ANTI_RANGE)
+    {
+      if (signed_p)
+	{
+	  if (wi::les_p (max, 0))
+	    {
+	      /* EXP is not in a strictly negative range.  That means
+		 it must be in some (not necessarily strictly) positive
+		 range which includes zero.  Since in signed to unsigned
+		 conversions negative values end up converted to large
+		 positive values, and otherwise they are not valid sizes,
+		 the resulting range is in both cases [0, TYPE_MAX].  */
+	      min = wi::zero (expprec);
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
+	  else if (wi::les_p (min - 1, 0))
+	    {
+	      /* EXP is not in a negative-positive range.  That means EXP
+		 is either negative, or greater than max.  Since negative
+		 sizes are invalid make the range [MAX + 1, TYPE_MAX].  */
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
+	  else
+	    {
+	      max = min - 1;
+	      min = wi::zero (expprec);
+	    }
+	}
+      else
+	{
+	  wide_int maxsize = wi::to_wide (max_object_size ());
+	  min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
+	  max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
+	  if (wi::eq_p (0, min - 1))
+	    {
+	      /* EXP is unsigned and not in the range [1, MAX].  That means
+		 it's either zero or greater than MAX.  Even though 0 would
+		 normally be detected by -Walloc-zero, unless ALLOW_ZERO
+		 is set, set the range to [MAX, TYPE_MAX] so that when MAX
+		 is greater than the limit the whole range is diagnosed.  */
+	      wide_int maxsize = wi::to_wide (max_object_size ());
+	      if (flags & SR_ALLOW_ZERO)
+		{
+		  if (wi::leu_p (maxsize, max + 1)
+		      || !(flags & SR_USE_LARGEST))
+		    min = max = wi::zero (expprec);
+		  else
+		    {
+		      min = max + 1;
+		      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+		    }
+		}
+	      else
+		{
+		  min = max + 1;
+		  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+		}
+	    }
+	  else if ((flags & SR_USE_LARGEST)
+		   && wi::ltu_p (max + 1, maxsize))
+	    {
+	      /* When USE_LARGEST is set and the larger of the two subranges
+		 is a valid size, use it...  */
+	      min = max + 1;
+	      max = maxsize;
+	    }
+	  else
+	    {
+	      /* ...otherwise use the smaller subrange.  */
+	      max = min - 1;
+	      min = wi::zero (expprec);
+	    }
+	}
+    }
+
+  range[0] = wide_int_to_tree (exptype, min);
+  range[1] = wide_int_to_tree (exptype, max);
+
+  return true;
+}
+
+bool
+get_size_range (tree exp, tree range[2], int flags /* = 0 */)
+{
+  return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags);
+}
+
 /* 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.
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index 8bd538a3ac2..eb7e90dde03 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -230,6 +230,17 @@ struct access_data
   access_mode mode;
 };
 
+enum size_range_flags
+  {
+   /* Set to consider zero a valid range.  */
+   SR_ALLOW_ZERO = 1,
+   /* Set to use the largest subrange of a set of ranges as opposed
+      to the smallest.  */
+   SR_USE_LARGEST = 2
+  };
+extern bool get_size_range (tree, tree[2], int = 0);
+extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0);
+
 class range_query;
 extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
 				    range_query * = NULL);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c
new file mode 100644
index 00000000000..c10773e90a3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-72.c
@@ -0,0 +1,13 @@
+/* PR middle-end/101854 - Invalid warning -Wstringop-overflow wrong argument
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { int a[5]; };
+
+struct A g (int*, int[6][8]);
+
+struct A f (void)
+{
+  int a[2];
+  return g (a, 0);            // { dg-bogus "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-5.c b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
index 7aa7cbf0c72..4eea625a966 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-5.c
@@ -4,7 +4,7 @@
    zero bytes.  For standard allocation functions the return value is
    implementation-defined and so relying on it may be a source of bugs.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Walloc-zero" } */
+/* { dg-options "-O1 -Wall -Walloc-zero" } */
 
 #define SCHAR_MAX  __SCHAR_MAX__
 #define SCHAR_MIN  (-SCHAR_MAX - 1)
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-7.c b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
index 68602ec37d9..3adde5c2270 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-7.c
@@ -4,7 +4,7 @@
    of the maximum specified by -Walloc-size-larger-than=maximum.  */
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-O2 -Wall -Walloc-size-larger-than=12345" } */
+/* { dg-options "-O1 -Wall -Walloc-size-larger-than=12345" } */
 
 #define SIZE_MAX   __SIZE_MAX__
 #define MAXOBJSZ   12345
@@ -13,15 +13,40 @@ typedef __SIZE_TYPE__ size_t;
 
 void sink (void*);
 
-static size_t maxobjsize (void)
+#pragma GCC push_options
+/* Verify that constant evaluation takes place even at -O0.  */
+#pragma GCC optimize ("0")
+
+void test_cst (void *p)
 {
-  return MAXOBJSZ;
+  enum { max = MAXOBJSZ };
+
+  sink (__builtin_aligned_alloc (1, max));
+  sink (__builtin_aligned_alloc (1, max + 1));   /* { dg-warning "argument 2 value .12346\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_alloca (max));
+  sink (__builtin_alloca (max + 2));   /* { dg-warning "argument 1 value .12347\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_calloc (1, max));
+  sink (__builtin_calloc (max, 1));
+
+  sink (__builtin_calloc (max / 2, 3));   /* { dg-warning "product .6172\[lu\]* \\* 3\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */
+  sink (__builtin_calloc (4, max / 3));   /* { dg-warning "product .4\[lu\]* \\* 4115\[lu\]*. of arguments 1 and 2 exceeds maximum object size 12345" } */
+
+  sink (__builtin_malloc (max));
+  sink (__builtin_malloc (max + 3));   /* { dg-warning "argument 1 value .12348\[lu\]*. exceeds maximum object size 12345" } */
+
+  sink (__builtin_realloc (p, max));
+  sink (__builtin_realloc (p, max + 4));  /* { dg-warning "argument 2 value .12349\[lu\]*. exceeds maximum object size 12345" } */
 }
 
 
-void test_var (void *p)
+/* Variable evaluation needs -O1.  */
+#pragma GCC pop_options
+
+__attribute__ ((noipa)) void test_var (void *p)
 {
-  size_t max = maxobjsize ();
+  size_t max = MAXOBJSZ;
 
   sink (__builtin_aligned_alloc (1, max));
   sink (__builtin_aligned_alloc (1, max + 1));   /* { dg-warning "argument 2 value .12346\[lu\]*. exceeds maximum object size 12345" } */
@@ -43,7 +68,15 @@ void test_var (void *p)
 }
 
 
-void test_range (void *p, size_t range)
+/* Value range evaluation (apparently) needs -O2 here.  */
+#pragma GCC optimize ("2")
+
+static size_t maxobjsize (void)
+{
+  return MAXOBJSZ;
+}
+
+__attribute__ ((noipa)) void test_range (void *p, size_t range)
 {
   /* Make sure the variable is at least as large as the maximum object
      size but also make sure that it's guaranteed not to be too big to
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size-8.c b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
index 91d7eb58e29..7b47b045b11 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size-8.c
@@ -4,7 +4,7 @@
    two more specific options override the more general latter option.  */
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-O2 -Walloc-size-larger-than=123 -Walloca-larger-than=234 -Wvla-larger-than=345" } */
+/* { dg-options "-O -Walloc-size-larger-than=123 -Walloca-larger-than=234 -Wvla-larger-than=345" } */
 
 typedef __SIZE_TYPE__ size_t;
 
diff --git a/gcc/tree.c b/gcc/tree.c
index 6ec8a9738c8..cba3bca41b3 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14464,6 +14464,60 @@ fndecl_dealloc_argno (tree fndecl)
   return UINT_MAX;
 }
 
+/* If EXPR refers to a character array or pointer declared attribute
+   nonstring, return a decl for that array or pointer and set *REF
+   to the referenced enclosing object or pointer.  Otherwise return
+   null.  */
+
+tree
+get_attr_nonstring_decl (tree expr, tree *ref)
+{
+  tree decl = expr;
+  tree var = NULL_TREE;
+  if (TREE_CODE (decl) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (decl);
+
+      if (is_gimple_assign (def))
+	{
+	  tree_code code = gimple_assign_rhs_code (def);
+	  if (code == ADDR_EXPR
+	      || code == COMPONENT_REF
+	      || code == VAR_DECL)
+	    decl = gimple_assign_rhs1 (def);
+	}
+      else
+	var = SSA_NAME_VAR (decl);
+    }
+
+  if (TREE_CODE (decl) == ADDR_EXPR)
+    decl = TREE_OPERAND (decl, 0);
+
+  /* To simplify calling code, store the referenced DECL regardless of
+     the attribute determined below, but avoid storing the SSA_NAME_VAR
+     obtained above (it's not useful for dataflow purposes).  */
+  if (ref)
+    *ref = decl;
+
+  /* Use the SSA_NAME_VAR that was determined above to see if it's
+     declared nonstring.  Otherwise drill down into the referenced
+     DECL.  */
+  if (var)
+    decl = var;
+  else if (TREE_CODE (decl) == ARRAY_REF)
+    decl = TREE_OPERAND (decl, 0);
+  else if (TREE_CODE (decl) == COMPONENT_REF)
+    decl = TREE_OPERAND (decl, 1);
+  else if (TREE_CODE (decl) == MEM_REF)
+    return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
+
+  if (DECL_P (decl)
+      && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
+    return decl;
+
+  return NULL_TREE;
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/tree.h b/gcc/tree.h
index 78d8a049d29..905417fd17b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -6488,4 +6488,10 @@ extern void copy_warning (tree, const_tree);
    value if it isn't.  */
 extern unsigned fndecl_dealloc_argno (tree);
 
+/* If an expression refers to a character array or pointer declared
+   attribute nonstring, return a decl for that array or pointer and
+   if nonnull, set the second argument to the referenced enclosing
+   object or pointer.  Otherwise return null.  */
+extern tree get_attr_nonstring_decl (tree, tree * = NULL);
+
 #endif  /* GCC_TREE_H  */


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

only message in thread, other threads:[~2021-08-17 20:51 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17 20:51 [gcc r12-2976] Move more warning code to gimple-ssa-warn-access etc 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).