]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/calls.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / calls.c
index 93da3d6256e67a8b4ae427aee85ba59af65c40da..e50d3fc3b62080b0e59630c247f7a55aec17eb56 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989-2020 Free Software Foundation, Inc.
+   Copyright (C) 1989-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -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"
@@ -59,7 +57,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "gimple-fold.h"
 #include "attr-fnspec.h"
-
+#include "value-query.h"
 #include "tree-pretty-print.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
@@ -629,21 +627,32 @@ special_function_p (const_tree fndecl, int flags)
   return flags;
 }
 
+/* Return fnspec for DECL.  */
+
+static attr_fnspec
+decl_fnspec (tree fndecl)
+{
+  tree attr;
+  tree type = TREE_TYPE (fndecl);
+  if (type)
+    {
+      attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
+      if (attr)
+       {
+         return TREE_VALUE (TREE_VALUE (attr));
+       }
+    }
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    return builtin_fnspec (fndecl);
+  return "";
+}
+
 /* Similar to special_function_p; return a set of ERF_ flags for the
    function FNDECL.  */
 static int
 decl_return_flags (tree fndecl)
 {
-  tree attr;
-  tree type = TREE_TYPE (fndecl);
-  if (!type)
-    return 0;
-
-  attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
-  if (!attr)
-    return 0;
-
-  attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (attr)));
+  attr_fnspec fnspec = decl_fnspec (fndecl);
 
   unsigned int arg;
   if (fnspec.returns_arg (&arg))
@@ -990,7 +999,8 @@ precompute_register_parameters (int num_actuals, struct arg_data *args,
        /* If the value is a non-legitimate constant, force it into a
           pseudo now.  TLS symbols sometimes need a call to resolve.  */
        if (CONSTANT_P (args[i].value)
-           && !targetm.legitimate_constant_p (args[i].mode, args[i].value))
+           && (!targetm.legitimate_constant_p (args[i].mode, args[i].value)
+               || targetm.precompute_tls_p (args[i].mode, args[i].value)))
          args[i].value = force_reg (args[i].mode, args[i].value);
 
        /* If we're going to have to load the value by parts, pull the
@@ -1209,662 +1219,10 @@ 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 true, 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.  */
-
-bool
-get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
-{
-  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 (integral)
-    range_type = determine_value_range (exp, &min, &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 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 true, set the range to [MAX, TYPE_MAX] so that when MAX
-            is greater than the limit the whole range is diagnosed.  */
-         if (allow_zero)
-           min = max = wi::zero (expprec);
-         else
-           {
-             min = max + 1;
-             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
-           }
-       }
-      else
-       {
-         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;
-}
-
-/* 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_,
-                                  "%Kargument %i value %qE is negative",
-                                  exp, 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,
-                                    "%Kargument %i value is zero",
-                                    exp, 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_,
-                                  "%Kargument %i value %qE exceeds "
-                                  "maximum object size %E",
-                                  exp, 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_,
-                                  "%Kargument %i range [%E, %E] is negative",
-                                  exp, 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_,
-                                  "%Kargument %i range [%E, %E] exceeds "
-                                  "maximum object size %E",
-                                  exp, 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_,
-                            "%Kproduct %<%E * %E%> of arguments %i and %i "
-                            "exceeds %<SIZE_MAX%>",
-                            exp, 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_,
-                            "%Kproduct %<%E * %E%> of arguments %i and %i "
-                            "exceeds maximum object size %E",
-                            exp, 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_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;
-}
-
-/* Warn about passing a non-string array/pointer to a built-in function
-   that expects a nul-terminated string argument.  Returns true if
-   a warning has been issued.*/
-
-bool
-maybe_warn_nonstring_arg (tree fndecl, tree exp)
-{
-  if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
-    return false;
-
-  if (TREE_NO_WARNING (exp) || !warn_stringop_overread)
-    return false;
-
-  /* Avoid clearly invalid calls (more checking done below).  */
-  unsigned nargs = call_expr_nargs (exp);
-  if (!nargs)
-    return false;
-
-  /* The bound argument to a bounded string function like strncpy.  */
-  tree bound = NULL_TREE;
-
-  /* The longest known or possible string argument to one of the comparison
-     functions.  If the length is less than the bound it is used instead.
-     Since the length is only used for warning and not for code generation
-     disable strict mode in the calls to get_range_strlen below.  */
-  tree maxlen = NULL_TREE;
-
-  /* It's safe to call "bounded" string functions with a non-string
-     argument since the functions provide an explicit bound for this
-     purpose.  The exception is strncat where the bound may refer to
-     either the destination or the source.  */
-  int fncode = DECL_FUNCTION_CODE (fndecl);
-  switch (fncode)
-    {
-    case BUILT_IN_STRCMP:
-    case BUILT_IN_STRNCMP:
-    case BUILT_IN_STRNCASECMP:
-      {
-       /* For these, if one argument refers to one or more of a set
-          of string constants or arrays of known size, determine
-          the range of their known or possible lengths and use it
-          conservatively as the bound for the unbounded function,
-          and to adjust the range of the bound of the bounded ones.  */
-       for (unsigned argno = 0;
-            argno < MIN (nargs, 2)
-              && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
-         {
-           tree arg = CALL_EXPR_ARG (exp, argno);
-           if (!get_attr_nonstring_decl (arg))
-             {
-               c_strlen_data lendata = { };
-               /* Set MAXBOUND to an arbitrary non-null non-integer
-                  node as a request to have it set to the length of
-                  the longest string in a PHI.  */
-               lendata.maxbound = arg;
-               get_range_strlen (arg, &lendata, /* eltsize = */ 1);
-               maxlen = lendata.maxbound;
-             }
-         }
-      }
-      /* Fall through.  */
-
-    case BUILT_IN_STRNCAT:
-    case BUILT_IN_STPNCPY:
-    case BUILT_IN_STRNCPY:
-      if (nargs > 2)
-       bound = CALL_EXPR_ARG (exp, 2);
-      break;
-
-    case BUILT_IN_STRNDUP:
-      if (nargs > 1)
-       bound = CALL_EXPR_ARG (exp, 1);
-      break;
-
-    case BUILT_IN_STRNLEN:
-      {
-       tree arg = CALL_EXPR_ARG (exp, 0);
-       if (!get_attr_nonstring_decl (arg))
-         {
-           c_strlen_data lendata = { };
-           /* Set MAXBOUND to an arbitrary non-null non-integer
-              node as a request to have it set to the length of
-              the longest string in a PHI.  */
-           lendata.maxbound = arg;
-           get_range_strlen (arg, &lendata, /* eltsize = */ 1);
-           maxlen = lendata.maxbound;
-         }
-       if (nargs > 1)
-         bound = CALL_EXPR_ARG (exp, 1);
-       break;
-      }
-
-    default:
-      break;
-    }
-
-  /* Determine the range of the bound argument (if specified).  */
-  tree bndrng[2] = { NULL_TREE, NULL_TREE };
-  if (bound)
-    {
-      STRIP_NOPS (bound);
-      get_size_range (bound, bndrng);
-    }
-
-  location_t loc = EXPR_LOCATION (exp);
-
-  if (bndrng[0])
-    {
-      /* Diagnose excessive bound prior to the adjustment below and
-        regardless of attribute nonstring.  */
-      tree maxobjsize = max_object_size ();
-      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
-       {
-         bool warned = false;
-         if (tree_int_cst_equal (bndrng[0], bndrng[1]))
-           warned = warning_at (loc, OPT_Wstringop_overread,
-                                "%K%qD specified bound %E "
-                                "exceeds maximum object size %E",
-                                exp, fndecl, bndrng[0], maxobjsize);
-         else
-           warned = warning_at (loc, OPT_Wstringop_overread,
-                                "%K%qD specified bound [%E, %E] "
-                                "exceeds maximum object size %E",
-                                exp, fndecl, bndrng[0], bndrng[1],
-                                maxobjsize);
-         if (warned)
-           TREE_NO_WARNING (exp) = true;
-
-         return warned;
-       }
-    }
-
-  if (maxlen && !integer_all_onesp (maxlen))
-    {
-      /* Add one for the nul.  */
-      maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
-                           size_one_node);
-
-      if (!bndrng[0])
-       {
-         /* Conservatively use the upper bound of the lengths for
-            both the lower and the upper bound of the operation.  */
-         bndrng[0] = maxlen;
-         bndrng[1] = maxlen;
-         bound = void_type_node;
-       }
-      else if (maxlen)
-       {
-         /* Replace the bound on the operation with the upper bound
-            of the length of the string if the latter is smaller.  */
-         if (tree_int_cst_lt (maxlen, bndrng[0]))
-           bndrng[0] = maxlen;
-         else if (tree_int_cst_lt (maxlen, bndrng[1]))
-           bndrng[1] = maxlen;
-       }
-    }
-
-  bool any_arg_warned = false;
-  /* Iterate over the built-in function's formal arguments and check
-     each const char* against the actual argument.  If the actual
-     argument is declared attribute non-string issue a warning unless
-     the argument's maximum length is bounded.  */
-  function_args_iterator it;
-  function_args_iter_init (&it, TREE_TYPE (fndecl));
-
-  for (unsigned argno = 0; ; ++argno, function_args_iter_next (&it))
-    {
-      /* Avoid iterating past the declared argument in a call
-        to function declared without a prototype.  */
-      if (argno >= nargs)
-       break;
-
-      tree argtype = function_args_iter_cond (&it);
-      if (!argtype)
-       break;
-
-      if (TREE_CODE (argtype) != POINTER_TYPE)
-       continue;
-
-      argtype = TREE_TYPE (argtype);
-
-      if (TREE_CODE (argtype) != INTEGER_TYPE
-         || !TYPE_READONLY (argtype))
-       continue;
-
-      argtype = TYPE_MAIN_VARIANT (argtype);
-      if (argtype != char_type_node)
-       continue;
-
-      tree callarg = CALL_EXPR_ARG (exp, argno);
-      if (TREE_CODE (callarg) == ADDR_EXPR)
-       callarg = TREE_OPERAND (callarg, 0);
-
-      /* See if the destination is declared with attribute "nonstring".  */
-      tree decl = get_attr_nonstring_decl (callarg);
-      if (!decl)
-       continue;
-
-      /* The maximum number of array elements accessed.  */
-      offset_int wibnd = 0;
-
-      if (argno && fncode == BUILT_IN_STRNCAT)
-       {
-         /* See if the bound in strncat is derived from the length
-            of the strlen of the destination (as it's expected to be).
-            If so, reset BOUND and FNCODE to trigger a warning.  */
-         tree dstarg = CALL_EXPR_ARG (exp, 0);
-         if (is_strlen_related_p (dstarg, bound))
-           {
-             /* The bound applies to the destination, not to the source,
-                so reset these to trigger a warning without mentioning
-                the bound.  */
-             bound = NULL;
-             fncode = 0;
-           }
-         else if (bndrng[1])
-           /* Use the upper bound of the range for strncat.  */
-           wibnd = wi::to_offset (bndrng[1]);
-       }
-      else if (bndrng[0])
-       /* Use the lower bound of the range for functions other than
-          strncat.  */
-       wibnd = wi::to_offset (bndrng[0]);
-
-      /* Determine the size of the argument array if it is one.  */
-      offset_int asize = wibnd;
-      bool known_size = false;
-      tree type = TREE_TYPE (decl);
-
-      /* Determine the array size.  For arrays of unknown bound and
-        pointers reset BOUND to trigger the appropriate warning.  */
-      if (TREE_CODE (type) == ARRAY_TYPE)
-       {
-         if (tree arrbnd = TYPE_DOMAIN (type))
-           {
-             if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
-               {
-                 asize = wi::to_offset (arrbnd) + 1;
-                 known_size = true;
-               }
-           }
-         else if (bound == void_type_node)
-           bound = NULL_TREE;
-       }
-      else if (bound == void_type_node)
-       bound = NULL_TREE;
-
-      /* In a call to strncat with a bound in a range whose lower but
-        not upper bound is less than the array size, reset ASIZE to
-        be the same as the bound and the other variable to trigger
-        the apprpriate warning below.  */
-      if (fncode == BUILT_IN_STRNCAT
-         && bndrng[0] != bndrng[1]
-         && wi::ltu_p (wi::to_offset (bndrng[0]), asize)
-         && (!known_size
-             || wi::ltu_p (asize, wibnd)))
-       {
-         asize = wibnd;
-         bound = NULL_TREE;
-         fncode = 0;
-       }
-
-      bool warned = false;
-
-      auto_diagnostic_group d;
-      if (wi::ltu_p (asize, wibnd))
-       {
-         if (bndrng[0] == bndrng[1])
-           warned = warning_at (loc, OPT_Wstringop_overread,
-                                "%qD argument %i declared attribute "
-                                "%<nonstring%> is smaller than the specified "
-                                "bound %wu",
-                                fndecl, argno + 1, wibnd.to_uhwi ());
-         else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
-           warned = warning_at (loc, OPT_Wstringop_overread,
-                                "%qD argument %i declared attribute "
-                                "%<nonstring%> is smaller than "
-                                "the specified bound [%E, %E]",
-                                fndecl, argno + 1, bndrng[0], bndrng[1]);
-         else
-           warned = warning_at (loc, OPT_Wstringop_overread,
-                                "%qD argument %i declared attribute "
-                                "%<nonstring%> may be smaller than "
-                                "the specified bound [%E, %E]",
-                                fndecl, argno + 1, bndrng[0], bndrng[1]);
-       }
-      else if (fncode == BUILT_IN_STRNCAT)
-       ; /* Avoid warning for calls to strncat() when the bound
-            is equal to the size of the non-string argument.  */
-      else if (!bound)
-       warned = warning_at (loc, OPT_Wstringop_overread,
-                            "%qD argument %i declared attribute %<nonstring%>",
-                            fndecl, argno + 1);
-
-      if (warned)
-       {
-         inform (DECL_SOURCE_LOCATION (decl),
-                 "argument %qD declared here", decl);
-         any_arg_warned = true;
-       }
-    }
-
-  if (any_arg_warned)
-    TREE_NO_WARNING (exp) = true;
-
-  return any_arg_warned;
-}
-
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
-static void
+void
 maybe_complain_about_tail_call (tree call_expr, const char *reason)
 {
   gcc_assert (TREE_CODE (call_expr) == CALL_EXPR);
@@ -1874,306 +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).  */
-  bool any_warned = false;
-
-  /* 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))
-       {
-         const 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
-           {
-             const 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);
-           }
-       }
-      else
-       *sizstr = '\0';
-
-      /* Set if a warning has been issued for the current argument.  */
-      bool arg_warned = false;
-      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);
-
-             arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
-                                      "%Kbound argument %i value %s is "
-                                      "negative for a variable length array "
-                                      "argument %i of type %s",
-                                      exp, sizidx + 1, sizstr,
-                                      ptridx + 1, argtypestr.c_str ());
-           }
-         else
-           arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
-                                    "%Kargument %i value %s is negative",
-                                    exp, sizidx + 1, sizstr);
-
-         if (arg_warned)
-           {
-             append_attrname (access, attrstr, sizeof attrstr);
-             /* Remember a warning has been issued and avoid warning
-                again below for the same attribute.  */
-             any_warned = true;
-             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);
-
-                 arg_warned = warning_at (loc, OPT_Wnonnull,
-                                          "%Kargument %i of variable length "
-                                          "array %s is null but "
-                                          "the corresponding bound argument "
-                                          "%i value is %s",
-                                          exp, sizidx + 1, argtypestr.c_str (),
-                                          ptridx + 1, sizstr);
-               }
-             else
-               arg_warned = warning_at (loc, OPT_Wnonnull,
-                                        "%Kargument %i is null but "
-                                        "the corresponding size argument "
-                                        "%i value is %s",
-                                        exp, ptridx + 1, sizidx + 1,
-                                        sizstr);
-           }
-         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.  */
-             arg_warned = warning_at (loc, OPT_Wnonnull,
-                                      "%Kargument %i to %<%T[static %E]%> "
-                                      "is null where non-null expected",
-                                      exp, ptridx + 1, argtype,
-                                      access_size);
-           }
-
-         if (arg_warned)
-           {
-             append_attrname (access, attrstr, sizeof attrstr);
-             /* Remember a warning has been issued and avoid warning
-                again below for the same attribute.  */
-             any_warned = true;
-             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.  */
-      TREE_NO_WARNING (exp) = 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 (TREE_NO_WARNING (exp))
-       {
-         any_warned = true;
-
-         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 (any_warned)
-    {
-      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.  */
-  TREE_NO_WARNING (exp) = true;
-}
-
 /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
    CALL_EXPR EXP.
 
@@ -2273,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++)
     {
@@ -2328,19 +1365,18 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       function_arg_info arg (type, argpos < n_named_args);
       if (pass_by_reference (args_so_far_pnt, arg))
        {
-         bool callee_copies;
-         tree base = NULL_TREE;
-
-         callee_copies = reference_callee_copied (args_so_far_pnt, arg);
-
-         /* If we're compiling a thunk, pass through invisible references
-            instead of making a copy.  */
-         if (call_from_thunk_p
-             || (callee_copies
-                 && !TREE_ADDRESSABLE (type)
-                 && (base = get_base_address (args[i].tree_value))
-                 && TREE_CODE (base) != SSA_NAME
-                 && (!DECL_P (base) || MEM_P (DECL_RTL (base)))))
+         const bool callee_copies
+           = reference_callee_copied (args_so_far_pnt, arg);
+         tree base;
+
+         /* If we're compiling a thunk, pass directly the address of an object
+            already in memory, instead of making a copy.  Likewise if we want
+            to make the copy in the callee instead of the caller.  */
+         if ((call_from_thunk_p || callee_copies)
+             && TREE_CODE (args[i].tree_value) != WITH_SIZE_EXPR
+             && ((base = get_base_address (args[i].tree_value)), true)
+             && TREE_CODE (base) != SSA_NAME
+             && (!DECL_P (base) || MEM_P (DECL_RTL (base))))
            {
              /* We may have turned the parameter value into an SSA name.
                 Go back to the original parameter so we can take the
@@ -2527,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.
@@ -3467,7 +2466,6 @@ static bool
 can_implement_as_sibling_call_p (tree exp,
                                 rtx structure_value_addr,
                                 tree funtype,
-                                int reg_parm_stack_space ATTRIBUTE_UNUSED,
                                 tree fndecl,
                                 int flags,
                                 tree addr,
@@ -3492,20 +2490,6 @@ can_implement_as_sibling_call_p (tree exp,
       return false;
     }
 
-#ifdef REG_PARM_STACK_SPACE
-  /* If outgoing reg parm stack space changes, we cannot do sibcall.  */
-  if (OUTGOING_REG_PARM_STACK_SPACE (funtype)
-      != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl))
-      || (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl)))
-    {
-      maybe_complain_about_tail_call (exp,
-                                     "inconsistent size of stack space"
-                                     " allocated for arguments which are"
-                                     " passed in registers");
-      return false;
-    }
-#endif
-
   /* Check whether the target is able to optimize the call
      into a sibcall.  */
   if (!targetm.function_ok_for_sibcall (fndecl, exp))
@@ -3679,7 +2663,7 @@ expand_call (tree exp, rtx target, int ignore)
      So the entire argument block must then be preallocated (i.e., we
      ignore PUSH_ROUNDING in that case).  */
 
-  int must_preallocate = !PUSH_ARGS;
+  int must_preallocate = !targetm.calls.push_argument (0);
 
   /* Size of the stack reserved for parameter registers.  */
   int reg_parm_stack_space = 0;
@@ -3760,6 +2744,7 @@ expand_call (tree exp, rtx target, int ignore)
      side-effects.  */
   if ((flags & (ECF_CONST | ECF_PURE))
       && (!(flags & ECF_LOOPING_CONST_OR_PURE))
+      && (flags & ECF_NOTHROW)
       && (ignore || target == const0_rtx
          || TYPE_MODE (rettype) == VOIDmode))
     {
@@ -3787,7 +2772,7 @@ expand_call (tree exp, rtx target, int ignore)
 #endif
 
   if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))
-      && reg_parm_stack_space > 0 && PUSH_ARGS)
+      && reg_parm_stack_space > 0 && targetm.calls.push_argument (0))
     must_preallocate = 1;
 
   /* Set up a place to return a structure.  */
@@ -4030,7 +3015,6 @@ expand_call (tree exp, rtx target, int ignore)
     try_tail_call = can_implement_as_sibling_call_p (exp,
                                                     structure_value_addr,
                                                     funtype,
-                                                    reg_parm_stack_space,
                                                     fndecl,
                                                     flags, addr, args_size);
 
@@ -5429,7 +4413,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
     }
   else
     {
-      if (!PUSH_ARGS)
+      if (!targetm.calls.push_argument (0))
        argblock = push_block (gen_int_mode (args_size.constant, Pmode), 0, 0);
     }
 
@@ -6279,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"