/* 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.
#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 "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)
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;
-
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (!attr || TREE_STRING_LENGTH (attr) < 1)
- return 0;
+ unsigned int arg;
+ if (fnspec.returns_arg (&arg))
+ return ERF_RETURNS_ARG | arg;
- 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 (!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 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);
error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
}
-/* Used to define rdwr_map below. */
-struct rdwr_access_hash: int_hash<int, -1> { };
-
-/* A mapping between argument number corresponding to attribute access
- mode (read_only, write_only, or read_write) and operands. */
-typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
-
-/* Initialize a mapping for a call to function FNDECL declared with
- attribute access. Each attribute positional operand inserts one
- entry into the mapping with the operand number as the key. */
-
-static void
-init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
-{
- if (!fntype)
- return;
-
- for (tree access = TYPE_ATTRIBUTES (fntype);
- (access = lookup_attribute ("access", access));
- access = TREE_CHAIN (access))
- {
- /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
- is the attribute argument's value. */
- tree mode = TREE_VALUE (access);
- gcc_assert (TREE_CODE (mode) == TREE_LIST);
- mode = TREE_VALUE (mode);
- gcc_assert (TREE_CODE (mode) == STRING_CST);
-
- const char *modestr = TREE_STRING_POINTER (mode);
- for (const char *m = modestr; *m; )
- {
- attr_access acc = { };
-
- switch (*m)
- {
- case 'r': acc.mode = acc.read_only; break;
- case 'w': acc.mode = acc.write_only; break;
- default: acc.mode = acc.read_write; break;
- }
-
- char *end;
- acc.ptrarg = strtoul (++m, &end, 10);
- m = end;
- if (*m == ',')
- {
- acc.sizarg = strtoul (++m, &end, 10);
- m = end;
- }
- else
- acc.sizarg = UINT_MAX;
-
- acc.ptr = NULL_TREE;
- acc.size = NULL_TREE;
-
- /* Unconditionally add an entry for the required pointer
- operand of the attribute, and one for the optional size
- operand when it's specified. */
- rwm->put (acc.ptrarg, acc);
- if (acc.sizarg != UINT_MAX)
- rwm->put (acc.sizarg, acc);
- }
- }
-}
-
-/* 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 "rdwr" attribute 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)
-{
- /* Append the relevant attribute to the string. This (deliberately)
- appends the attribute pointer operand even when none was specified. */
- size_t len = strlen (attrstr);
-
- const char *atname
- = (access.second.mode == attr_access::read_only
- ? "read_only"
- : (access.second.mode == attr_access::write_only
- ? "write_only" : "read_write"));
-
- const char *sep = len ? ", " : "";
-
- if (access.second.sizarg == UINT_MAX)
- snprintf (attrstr + len, strsize - len,
- "%s%s (%i)", sep, atname,
- access.second.ptrarg + 1);
- else
- snprintf (attrstr + len, strsize - len,
- "%s%s (%i, %i)", sep, atname,
- access.second.ptrarg + 1, access.second.sizarg + 1);
-}
-
-/* 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 exp)
-{
- tree fndecl = NULL_TREE;
- tree fntype = NULL_TREE;
- if (tree fnaddr = CALL_EXPR_FN (exp))
- {
- if (TREE_CODE (fnaddr) == ADDR_EXPR)
- {
- fndecl = TREE_OPERAND (fnaddr, 0);
- fntype = TREE_TYPE (fndecl);
- }
- else
- fntype = TREE_TYPE (TREE_TYPE (fnaddr));
- }
-
- if (!fntype)
- return;
-
- /* 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 argtype = fntype_argno_type (fntype, ptridx);
- argtype = TREE_TYPE (argtype);
-
- tree size;
- if (sizidx == -1)
- {
- /* If only the pointer attribute operand was specified
- and not size, set SIZE to the 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. */
- size = size_one_node;
- }
- else
- size = rwm->get (sizidx)->size;
-
- tree ptr = access.second.ptr;
- tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
- if (get_size_range (size, sizrng, true)
- && tree_int_cst_sgn (sizrng[0]) < 0
- && tree_int_cst_sgn (sizrng[1]) < 0)
- {
- /* Warn about negative sizes. */
- bool warned = false;
- location_t loc = EXPR_LOCATION (exp);
- if (tree_int_cst_equal (sizrng[0], sizrng[1]))
- warned = warning_at (loc, OPT_Wstringop_overflow_,
- "%Kargument %i value %E is negative",
- exp, sizidx + 1, size);
- else
- warned = warning_at (loc, OPT_Wstringop_overflow_,
- "%Kargument %i range [%E, %E] is negative",
- exp, sizidx + 1, sizrng[0], sizrng[1]);
- if (warned)
- {
- append_attrname (access, attrstr, sizeof attrstr);
- /* Avoid warning again for the same attribute. */
- continue;
- }
- }
-
- if (tree_int_cst_sgn (sizrng[0]) >= 0)
- {
- if (COMPLETE_TYPE_P (argtype))
- {
- /* Multiple SIZE by the size of the type the pointer
- argument points to. If it's incomplete the size
- is used as is. */
- size = NULL_TREE;
- 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);
- size = wide_int_to_tree (sizetype, minsize);
- }
- }
- }
- else
- size = NULL_TREE;
-
- if (sizidx >= 0
- && integer_zerop (ptr)
- && 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. */
- bool warned = false;
- location_t loc = EXPR_LOCATION (exp);
- if (tree_int_cst_equal (sizrng[0], sizrng[1]))
- warned = warning_at (loc, OPT_Wnonnull,
- "%Kargument %i is null but the corresponding "
- "size argument %i value is %E",
- exp, ptridx + 1, sizidx + 1, size);
- else
- warned = warning_at (loc, OPT_Wnonnull,
- "%Kargument %i is null but the corresponding "
- "size argument %i range is [%E, %E]",
- exp, ptridx + 1, sizidx + 1,
- sizrng[0], sizrng[1]);
- if (warned)
- {
- append_attrname (access, attrstr, sizeof attrstr);
- /* Avoid warning again for the same attribute. */
- continue;
- }
- }
-
- tree objsize = compute_objsize (ptr, 0);
-
- tree srcsize;
- if (access.second.mode == attr_access::write_only)
- {
- /* For a write-only argument there is no source. */
- srcsize = NULL_TREE;
- }
- else
- {
- /* For read-only and read-write attributes also set the source
- size. */
- srcsize = objsize;
- if (access.second.mode == attr_access::read_only)
- {
- /* 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. */
- objsize = NULL_TREE;
- }
- }
-
- /* Clear the no-warning bit in case it was set in a prior
- iteration so that accesses via different arguments are
- diagnosed. */
- TREE_NO_WARNING (exp) = false;
- check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
- srcsize, objsize);
-
- if (TREE_NO_WARNING (exp))
- /* If check_access issued a warning above, append the relevant
- attribute to the string. */
- append_attrname (access, attrstr, sizeof attrstr);
- }
-
- if (!*attrstr)
- return;
-
- 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);
-
- /* 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.
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 };
-
- /* Map of attribute read_only, write_only, or read_write specifications
- for function arguments. */
- rdwr_map rdwr_idx;
- init_attr_rdwr_indices (&rdwr_idx, fntype);
-
/* 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;
-
- /* 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;
- gcc_assert (access->size == NULL_TREE);
- }
- 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 read_only, write_only, and read_write arguments. */
- maybe_warn_rdwr_sizes (&rdwr_idx, 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)));
+}