/* Convert function calls to rtl insns, for GNU C compiler.
- Copyright (C) 1989-2019 Free Software Foundation, Inc.
+ Copyright (C) 1989-2021 Free Software Foundation, Inc.
This file is part of GCC.
#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"
+#include "hash-traits.h"
#include "attribs.h"
#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. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
{
tree name_decl = DECL_NAME (fndecl);
- if (fndecl && name_decl
- && IDENTIFIER_LENGTH (name_decl) <= 11
- /* Exclude functions not at the file scope, or not `extern',
- since they are not the magic functions we would otherwise
- think they are.
- FIXME: this should be handled with attributes, not with this
- hacky imitation of DECL_ASSEMBLER_NAME. It's (also) wrong
- because you can declare fork() inside a function if you
- wish. */
- && (DECL_CONTEXT (fndecl) == NULL_TREE
- || TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
- && TREE_PUBLIC (fndecl))
+ if (maybe_special_function_p (fndecl)
+ && IDENTIFIER_LENGTH (name_decl) <= 11)
{
const char *name = IDENTIFIER_POINTER (name_decl);
const char *tname = name;
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_fnspec fnspec = decl_fnspec (fndecl);
- attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
- if (!attr)
- return 0;
+ unsigned int arg;
+ if (fnspec.returns_arg (&arg))
+ return ERF_RETURNS_ARG | arg;
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (!attr || TREE_STRING_LENGTH (attr) < 1)
- return 0;
-
- switch (TREE_STRING_POINTER (attr)[0])
- {
- case '1':
- case '2':
- case '3':
- case '4':
- return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
-
- case 'm':
- return ERF_NOALIAS;
-
- case '.':
- default:
- return 0;
- }
+ if (fnspec.returns_noalias_p ())
+ return ERF_NOALIAS;
+ return 0;
}
/* Return nonzero when FNDECL represents a call to setjmp. */
/* 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
}
}
-/* 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 (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 function that
- expects a nul-terminated string argument. */
-
-void
-maybe_warn_nonstring_arg (tree fndecl, tree exp)
-{
- if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
- return;
-
- if (TREE_NO_WARNING (exp) || !warn_stringop_overflow)
- return;
-
- /* Avoid clearly invalid calls (more checking done below). */
- unsigned nargs = call_expr_nargs (exp);
- if (!nargs)
- return;
-
- /* 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 the adjustment below and
- regardless of attribute nonstring. */
- tree maxobjsize = max_object_size ();
- if (tree_int_cst_lt (maxobjsize, bndrng[0]))
- {
- if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warning_at (loc, OPT_Wstringop_overflow_,
- "%K%qD specified bound %E "
- "exceeds maximum object size %E",
- exp, fndecl, bndrng[0], maxobjsize);
- else
- warning_at (loc, OPT_Wstringop_overflow_,
- "%K%qD specified bound [%E, %E] "
- "exceeds maximum object size %E",
- exp, fndecl, bndrng[0], bndrng[1], maxobjsize);
- return;
- }
- }
-
- 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;
- }
- }
-
- /* 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_overflow_,
- "%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_overflow_,
- "%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_overflow_,
- "%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_overflow_,
- "%qD argument %i declared attribute %<nonstring%>",
- fndecl, argno + 1);
-
- if (warned)
- inform (DECL_SOURCE_LOCATION (decl),
- "argument %qD declared here", decl);
- }
-}
-
/* 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);
bitmap_obstack_release (NULL);
- /* 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",
- TYPE_ATTRIBUTES (fntype)))
- {
- 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 };
-
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
for (argpos = 0; argpos < num_actuals; i--, argpos++)
{
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
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;
- }
-
- 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);
}
/* Update ARGS_SIZE to contain the total size for the argument block.
{
if (idx >= internal_arg_pointer_exp_state.cache.length ())
internal_arg_pointer_exp_state.cache
- .safe_grow_cleared (idx + 1);
+ .safe_grow_cleared (idx + 1, true);
internal_arg_pointer_exp_state.cache[idx] = val;
}
}
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,
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))
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;
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))
{
#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. */
try_tail_call = can_implement_as_sibling_call_p (exp,
structure_value_addr,
funtype,
- reg_parm_stack_space,
fndecl,
flags, addr, args_size);
}
else
{
- if (!PUSH_ARGS)
+ if (!targetm.calls.push_argument (0))
argblock = push_block (gen_int_mode (args_size.constant, Pmode), 0, 0);
}
return targetm.calls.must_pass_in_stack (arg);
}
-/* Tell the garbage collector about GTY markers in this source file. */
-#include "gt-calls.h"
+/* Return true if FIELD is the C++17 empty base field that should
+ be ignored for ABI calling convention decisions in order to
+ maintain ABI compatibility between C++14 and earlier, which doesn't
+ add this FIELD to classes with empty bases, and C++17 and later
+ which does. */
+
+bool
+cxx17_empty_base_field_p (const_tree field)
+{
+ return (DECL_FIELD_ABI_IGNORED (field)
+ && DECL_ARTIFICIAL (field)
+ && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
+ && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
+}