gimple-fold.h (get_range_strlen): Update prototype.
* gimple-fold.h (get_range_strlen): Update prototype. * builtins.c (check_access): Update call to get_range_strlen to use c_strlen_data pointer. Change various variable accesses to instead pull data from the c_strlen_data structure. (check_strncat_sizes, expand_builtin_strncat): Likewise. * calls.c (maybe_warn_nonstring_arg): Likewise. * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Likewise. Reset minimum length if maximum lengh is unknown. * gimple-ssa-sprintf.c (get_string_length): Likewise. Drop code that used c_strlen, it's no longer needed. Restructure slightly. (format_string): Set unlikely range appropriately. * gimple-fold.c (get_range_strlen): Update comments. Fix minor formatting issues. (get_range_strlen): Accept c_strlen_data pointer for external call sites as well. Pass through to call to internal get_range_strlen. Adjust minlen, maxlen and maxbound as needed. (get_maxval_strlen): Update comments. (gimple_fold_builtin_strlen): Update call to get_range_strlen to use c_strlen_data pointer. Change variable accesses to instead use c_strlen_data data members. * gcc.dg/strlenopt-40.c: Disable a couple tests. * gcc.dg/strlenopt-48.c: Twiddle test. * gcc.dg/strlenopt-59.c: New test. * gcc.dg/tree-ssa/builtin-snprintf-5.c: New test. * g++.dg/init/strlen.C: New test. Co-Authored-By: Jeff Law <law@redhat.com> From-SVN: r267503
This commit is contained in:
parent
79b1c2295b
commit
5d6655ebcc
@ -1,6 +1,27 @@
|
||||
2019-01-01 Martin Sebor <msebor@redhat.com>
|
||||
Jeff Law <law@redhat.com>
|
||||
|
||||
* gimple-fold.h (get_range_strlen): Update prototype.
|
||||
* builtins.c (check_access): Update call to get_range_strlen to use
|
||||
c_strlen_data pointer. Change various variable accesses to instead
|
||||
pull data from the c_strlen_data structure.
|
||||
(check_strncat_sizes, expand_builtin_strncat): Likewise.
|
||||
* calls.c (maybe_warn_nonstring_arg): Likewise.
|
||||
* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Likewise. Reset
|
||||
minimum length if maximum lengh is unknown.
|
||||
* gimple-ssa-sprintf.c (get_string_length): Likewise. Drop code
|
||||
that used c_strlen, it's no longer needed. Restructure slightly.
|
||||
(format_string): Set unlikely range appropriately.
|
||||
* gimple-fold.c (get_range_strlen): Update comments. Fix minor
|
||||
formatting issues.
|
||||
(get_range_strlen): Accept c_strlen_data pointer for external
|
||||
call sites as well. Pass through to call to internal get_range_strlen.
|
||||
Adjust minlen, maxlen and maxbound as needed.
|
||||
(get_maxval_strlen): Update comments.
|
||||
(gimple_fold_builtin_strlen): Update call to get_range_strlen
|
||||
to use c_strlen_data pointer. Change variable accesses to instead
|
||||
use c_strlen_data data members.
|
||||
|
||||
* gimple-fold.c (get_range_strlen): Update prototype.
|
||||
(get_range_strlen_tree): Update prototype. Drop minlen/maxlen
|
||||
local variables. Use pdata to return information to caller.
|
||||
|
@ -3341,7 +3341,10 @@ check_access (tree exp, tree, tree, tree dstwrite,
|
||||
the upper bound given by MAXREAD add one to it for
|
||||
the terminating nul. Otherwise, set it to one for
|
||||
the same reason, or to MAXREAD as appropriate. */
|
||||
get_range_strlen (srcstr, range);
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
|
||||
range[0] = lendata.minlen;
|
||||
range[1] = lendata.maxbound;
|
||||
if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
|
||||
{
|
||||
if (maxread && tree_int_cst_le (maxread, range[0]))
|
||||
@ -4209,8 +4212,8 @@ check_strncat_sizes (tree exp, tree objsize)
|
||||
|
||||
/* Try to determine the range of lengths that the source expression
|
||||
refers to. */
|
||||
tree lenrange[2];
|
||||
get_range_strlen (src, lenrange);
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (src, &lendata, /* eltsize = */ 1);
|
||||
|
||||
/* Try to verify that the destination is big enough for the shortest
|
||||
string. */
|
||||
@ -4224,8 +4227,8 @@ check_strncat_sizes (tree exp, tree objsize)
|
||||
}
|
||||
|
||||
/* Add one for the terminating nul. */
|
||||
tree srclen = (lenrange[0]
|
||||
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
|
||||
tree srclen = (lendata.minlen
|
||||
? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
|
||||
size_one_node)
|
||||
: NULL_TREE);
|
||||
|
||||
@ -4277,12 +4280,15 @@ expand_builtin_strncat (tree exp, rtx)
|
||||
tree slen = c_strlen (src, 1);
|
||||
|
||||
/* Try to determine the range of lengths that the source expression
|
||||
refers to. */
|
||||
tree lenrange[2];
|
||||
if (slen)
|
||||
lenrange[0] = lenrange[1] = slen;
|
||||
else
|
||||
get_range_strlen (src, lenrange);
|
||||
refers to. Since the lengths are only used for warning and not
|
||||
for code generation disable strict mode below. */
|
||||
tree maxlen = slen;
|
||||
if (!maxlen)
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (src, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
|
||||
/* Try to verify that the destination is big enough for the shortest
|
||||
string. First try to determine the size of the destination object
|
||||
@ -4290,8 +4296,8 @@ expand_builtin_strncat (tree exp, rtx)
|
||||
tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
|
||||
|
||||
/* Add one for the terminating nul. */
|
||||
tree srclen = (lenrange[0]
|
||||
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
|
||||
tree srclen = (maxlen
|
||||
? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
|
||||
size_one_node)
|
||||
: NULL_TREE);
|
||||
|
||||
|
41
gcc/calls.c
41
gcc/calls.c
@ -1569,9 +1569,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
|
||||
/* The bound argument to a bounded string function like strncpy. */
|
||||
tree bound = NULL_TREE;
|
||||
|
||||
/* The range of lengths of a string argument to one of the comparison
|
||||
functions. If the length is less than the bound it is used instead. */
|
||||
tree lenrng[2] = { NULL_TREE, 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
|
||||
@ -1591,11 +1593,15 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
|
||||
and to adjust the range of the bound of the bounded ones. */
|
||||
for (unsigned argno = 0;
|
||||
argno < MIN (nargs, 2)
|
||||
&& !(lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST); argno++)
|
||||
&& !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
|
||||
{
|
||||
tree arg = CALL_EXPR_ARG (exp, argno);
|
||||
if (!get_attr_nonstring_decl (arg))
|
||||
get_range_strlen (arg, lenrng);
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Fall through. */
|
||||
@ -1616,8 +1622,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
|
||||
{
|
||||
tree arg = CALL_EXPR_ARG (exp, 0);
|
||||
if (!get_attr_nonstring_decl (arg))
|
||||
get_range_strlen (arg, lenrng);
|
||||
|
||||
{
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
|
||||
maxlen = lendata.maxbound;
|
||||
}
|
||||
if (nargs > 1)
|
||||
bound = CALL_EXPR_ARG (exp, 1);
|
||||
break;
|
||||
@ -1658,28 +1667,28 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
|
||||
}
|
||||
}
|
||||
|
||||
if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST)
|
||||
if (maxlen && !integer_all_onesp (maxlen))
|
||||
{
|
||||
/* Add one for the nul. */
|
||||
lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
|
||||
lenrng[1], size_one_node);
|
||||
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] = lenrng[1];
|
||||
bndrng[1] = lenrng[1];
|
||||
bndrng[0] = maxlen;
|
||||
bndrng[1] = maxlen;
|
||||
bound = void_type_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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 (lenrng[1], bndrng[0]))
|
||||
bndrng[0] = lenrng[1];
|
||||
else if (tree_int_cst_lt (lenrng[1], bndrng[1]))
|
||||
bndrng[1] = lenrng[1];
|
||||
if (tree_int_cst_lt (maxlen, bndrng[0]))
|
||||
bndrng[0] = maxlen;
|
||||
else if (tree_int_cst_lt (maxlen, bndrng[1]))
|
||||
bndrng[1] = maxlen;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1502,11 +1502,14 @@ get_range_strlen_tree (tree arg, bitmap *visited,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Obtain the minimum and maximum string length or minimum and maximum
|
||||
value of ARG in LENGTH[0] and LENGTH[1], respectively.
|
||||
If ARG is an SSA name variable, follow its use-def chains. When
|
||||
TYPE == 0, if LENGTH[1] is not equal to the length we determine or
|
||||
if we are unable to determine the length or value, return false.
|
||||
/* For an ARG referencing one or more strings, try to obtain the range
|
||||
of their lengths, or the size of the largest array ARG referes to if
|
||||
the range of lengths cannot be determined, and store all in *PDATA.
|
||||
For an integer ARG (when RKIND == SRK_INT_VALUE), try to determine
|
||||
the maximum constant value.
|
||||
If ARG is an SSA_NAME, follow its use-def chains. When RKIND ==
|
||||
SRK_STRLEN, then if PDATA->MAXLEN is not equal to the determined
|
||||
length or if we are unable to determine the length, return false.
|
||||
VISITED is a bitmap of visited variables.
|
||||
RKIND determines the kind of value or range to obtain (see
|
||||
strlen_range_kind).
|
||||
@ -1516,8 +1519,7 @@ get_range_strlen_tree (tree arg, bitmap *visited,
|
||||
Return true if *PDATA was successfully populated and false otherwise. */
|
||||
|
||||
static bool
|
||||
get_range_strlen (tree arg, bitmap *visited,
|
||||
strlen_range_kind rkind,
|
||||
get_range_strlen (tree arg, bitmap *visited, strlen_range_kind rkind,
|
||||
c_strlen_data *pdata, bool *flexp, unsigned eltsize)
|
||||
{
|
||||
|
||||
@ -1612,6 +1614,7 @@ get_range_strlen (tree arg, bitmap *visited,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine the minimum and maximum value or string length that ARG
|
||||
refers to and store each in the first two elements of MINMAXLEN.
|
||||
For expressions that point to strings of unknown lengths that are
|
||||
@ -1638,47 +1641,39 @@ get_range_strlen (tree arg, bitmap *visited,
|
||||
4 for wide characer strings. ELTSIZE is by default 1. */
|
||||
|
||||
bool
|
||||
get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
|
||||
bool strict, tree *nonstr /* = NULL */)
|
||||
get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize, bool strict)
|
||||
{
|
||||
bitmap visited = NULL;
|
||||
|
||||
minmaxlen[0] = NULL_TREE;
|
||||
minmaxlen[1] = NULL_TREE;
|
||||
|
||||
tree nonstrbuf;
|
||||
if (!nonstr)
|
||||
nonstr = &nonstrbuf;
|
||||
*nonstr = NULL_TREE;
|
||||
|
||||
bool flexarray = false;
|
||||
c_strlen_data lendata = { };
|
||||
if (!get_range_strlen (arg, &visited,
|
||||
strict ? SRK_LENRANGE : SRK_LENRANGE_2,
|
||||
&lendata, &flexarray, eltsize))
|
||||
if (!get_range_strlen (arg, &visited, strict ? SRK_LENRANGE : SRK_LENRANGE_2, pdata, &flexarray, eltsize))
|
||||
{
|
||||
minmaxlen[0] = NULL_TREE;
|
||||
minmaxlen[1] = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
minmaxlen[0] = lendata.minlen;
|
||||
minmaxlen[1] = lendata.maxlen;
|
||||
/* On failure extend the length range to an impossible maximum
|
||||
(a valid MAXLEN must be less than PTRDIFF_MAX - 1). Other
|
||||
members can stay unchanged regardless. */
|
||||
pdata->minlen = ssize_int (0);
|
||||
pdata->maxlen = build_all_ones_cst (size_type_node);
|
||||
}
|
||||
else if (!pdata->minlen)
|
||||
pdata->minlen = ssize_int (0);
|
||||
|
||||
/* Unless its null, leave the more conservative MAXBOUND unchanged. */
|
||||
if (!pdata->maxbound)
|
||||
pdata->maxbound = pdata->maxlen;
|
||||
|
||||
*nonstr = lendata.decl;
|
||||
if (visited)
|
||||
BITMAP_FREE (visited);
|
||||
|
||||
return flexarray;
|
||||
}
|
||||
|
||||
/* Return the maximum string length for ARG, counting by TYPE
|
||||
(1, 2 or 4 for normal or wide chars). NONSTR indicates
|
||||
if the caller is prepared to handle unterminated strings.
|
||||
/* Return the maximum value for ARG given RKIND (see strlen_range_kind).
|
||||
For ARG of pointer types, NONSTR indicates if the caller is prepared
|
||||
to handle unterminated strings. For integer ARG and when RKIND ==
|
||||
SRK_INT_VALUE, NONSTR must be null.
|
||||
|
||||
If an unterminated string is discovered and our caller handles
|
||||
unterminated strings, then bubble up the offending DECL and
|
||||
If an unterminated array is discovered and our caller handles
|
||||
unterminated arrays, then bubble up the offending DECL and
|
||||
return the maximum size. Otherwise return NULL. */
|
||||
|
||||
static tree
|
||||
@ -1692,10 +1687,15 @@ get_maxval_strlen (tree arg, strlen_range_kind rkind, tree *nonstr = NULL)
|
||||
|
||||
bitmap visited = NULL;
|
||||
|
||||
bool dummy;
|
||||
/* Reset DATA.MAXLEN if the call fails or when DATA.MAXLEN
|
||||
is unbounded. */
|
||||
c_strlen_data lendata = { };
|
||||
bool dummy;
|
||||
if (!get_range_strlen (arg, &visited, rkind, &lendata, &dummy, 1))
|
||||
lendata.maxlen = NULL_TREE;
|
||||
else if (lendata.maxlen && integer_all_onesp (lendata.maxlen))
|
||||
lendata.maxlen = NULL_TREE;
|
||||
|
||||
if (visited)
|
||||
BITMAP_FREE (visited);
|
||||
|
||||
@ -3668,21 +3668,19 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
|
||||
wide_int minlen;
|
||||
wide_int maxlen;
|
||||
|
||||
/* Set to non-null if ARG refers to an unterminated array. */
|
||||
tree nonstr;
|
||||
tree lenrange[2];
|
||||
if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
|
||||
&& !nonstr
|
||||
&& lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
|
||||
&& lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
|
||||
c_strlen_data lendata = { };
|
||||
if (!get_range_strlen (arg, &lendata, /* eltsize = */ 1)
|
||||
&& !lendata.decl
|
||||
&& lendata.minlen && TREE_CODE (lendata.minlen) == INTEGER_CST
|
||||
&& lendata.maxlen && TREE_CODE (lendata.maxlen) == INTEGER_CST)
|
||||
{
|
||||
/* The range of lengths refers to either a single constant
|
||||
string or to the longest and shortest constant string
|
||||
referenced by the argument of the strlen() call, or to
|
||||
the strings that can possibly be stored in the arrays
|
||||
the argument refers to. */
|
||||
minlen = wi::to_wide (lenrange[0]);
|
||||
maxlen = wi::to_wide (lenrange[1]);
|
||||
minlen = wi::to_wide (lendata.minlen);
|
||||
maxlen = wi::to_wide (lendata.maxlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3694,9 +3692,12 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
|
||||
|
||||
if (minlen == maxlen)
|
||||
{
|
||||
lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
|
||||
true, GSI_SAME_STMT);
|
||||
replace_call_with_value (gsi, lenrange[0]);
|
||||
/* Fold the strlen call to a constant. */
|
||||
tree type = TREE_TYPE (lendata.minlen);
|
||||
tree len = force_gimple_operand_gsi (gsi,
|
||||
wide_int_to_tree (type, minlen),
|
||||
true, NULL, true, GSI_SAME_STMT);
|
||||
replace_call_with_value (gsi, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL);
|
||||
extern tree canonicalize_constructor_val (tree, tree);
|
||||
extern tree get_symbol_constant_value (tree);
|
||||
extern bool get_range_strlen (tree, tree[2], unsigned = 1,
|
||||
bool = false, tree * = NULL);
|
||||
struct c_strlen_data;
|
||||
extern bool get_range_strlen (tree, c_strlen_data *, unsigned eltsize, bool = false);
|
||||
extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
|
||||
extern bool fold_stmt (gimple_stmt_iterator *);
|
||||
extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
|
||||
|
@ -2003,90 +2003,73 @@ get_string_length (tree str, unsigned eltsize)
|
||||
if (!str)
|
||||
return fmtresult ();
|
||||
|
||||
c_strlen_data data = { };
|
||||
tree slen = c_strlen (str, 1, &data, eltsize);
|
||||
if (slen && TREE_CODE (slen) == INTEGER_CST)
|
||||
{
|
||||
/* The string is properly terminated and
|
||||
we know its length. */
|
||||
fmtresult res (tree_to_shwi (slen));
|
||||
res.nonstr = NULL_TREE;
|
||||
return res;
|
||||
}
|
||||
else if (!slen
|
||||
&& data.decl
|
||||
&& data.minlen
|
||||
&& TREE_CODE (data.minlen) == INTEGER_CST)
|
||||
{
|
||||
/* STR was not properly NUL terminated, but we have
|
||||
length information about the unterminated string. */
|
||||
fmtresult res (tree_to_shwi (data.minlen));
|
||||
res.nonstr = data.decl;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Determine the length of the shortest and longest string referenced
|
||||
by STR. Strings of unknown lengths are bounded by the sizes of
|
||||
arrays that subexpressions of STR may refer to. Pointers that
|
||||
aren't known to point any such arrays result in LENRANGE[1] set
|
||||
to SIZE_MAX. NONSTR is set to the declaration of the constant
|
||||
array that is known not to be nul-terminated. */
|
||||
tree lenrange[2];
|
||||
tree nonstr;
|
||||
bool flexarray = get_range_strlen (str, lenrange, eltsize, false, &nonstr);
|
||||
aren't known to point any such arrays result in LENDATA.MAXLEN
|
||||
set to SIZE_MAX. */
|
||||
c_strlen_data lendata = { };
|
||||
bool flexarray = get_range_strlen (str, &lendata, eltsize);
|
||||
|
||||
if (lenrange [0] || lenrange [1])
|
||||
/* Return the default result when nothing is known about the string. */
|
||||
if (integer_all_onesp (lendata.maxbound)
|
||||
&& integer_all_onesp (lendata.maxlen))
|
||||
return fmtresult ();
|
||||
|
||||
HOST_WIDE_INT min
|
||||
= (tree_fits_uhwi_p (lendata.minlen)
|
||||
? tree_to_uhwi (lendata.minlen)
|
||||
: 0);
|
||||
|
||||
HOST_WIDE_INT max
|
||||
= (tree_fits_uhwi_p (lendata.maxbound)
|
||||
? tree_to_uhwi (lendata.maxbound)
|
||||
: HOST_WIDE_INT_M1U);
|
||||
|
||||
const bool unbounded = flexarray || integer_all_onesp (lendata.maxlen);
|
||||
|
||||
/* Set the max/likely counters to unbounded when a minimum is known
|
||||
but the maximum length isn't bounded. This implies that STR is
|
||||
a conditional expression involving a string of known length and
|
||||
and an expression of unknown/unbounded length. */
|
||||
if (min
|
||||
&& (unsigned HOST_WIDE_INT)min < HOST_WIDE_INT_M1U
|
||||
&& unbounded)
|
||||
max = HOST_WIDE_INT_M1U;
|
||||
|
||||
/* get_range_strlen() returns the target value of SIZE_MAX for
|
||||
strings of unknown length. Bump it up to HOST_WIDE_INT_M1U
|
||||
which may be bigger. */
|
||||
if ((unsigned HOST_WIDE_INT)min == target_size_max ())
|
||||
min = HOST_WIDE_INT_M1U;
|
||||
if ((unsigned HOST_WIDE_INT)max == target_size_max ())
|
||||
max = HOST_WIDE_INT_M1U;
|
||||
|
||||
fmtresult res (min, max);
|
||||
res.nonstr = lendata.decl;
|
||||
|
||||
/* Set RES.KNOWNRANGE to true if and only if all strings referenced
|
||||
by STR are known to be bounded (though not necessarily by their
|
||||
actual length but perhaps by their maximum possible length). */
|
||||
if (res.range.max < target_int_max ())
|
||||
{
|
||||
HOST_WIDE_INT min
|
||||
= (tree_fits_uhwi_p (lenrange[0])
|
||||
? tree_to_uhwi (lenrange[0])
|
||||
: 0);
|
||||
|
||||
HOST_WIDE_INT max
|
||||
= (tree_fits_uhwi_p (lenrange[1])
|
||||
? tree_to_uhwi (lenrange[1])
|
||||
: HOST_WIDE_INT_M1U);
|
||||
|
||||
/* get_range_strlen() returns the target value of SIZE_MAX for
|
||||
strings of unknown length. Bump it up to HOST_WIDE_INT_M1U
|
||||
which may be bigger. */
|
||||
if ((unsigned HOST_WIDE_INT)min == target_size_max ())
|
||||
min = HOST_WIDE_INT_M1U;
|
||||
if ((unsigned HOST_WIDE_INT)max == target_size_max ())
|
||||
max = HOST_WIDE_INT_M1U;
|
||||
|
||||
fmtresult res (min, max);
|
||||
res.nonstr = nonstr;
|
||||
|
||||
/* Set RES.KNOWNRANGE to true if and only if all strings referenced
|
||||
by STR are known to be bounded (though not necessarily by their
|
||||
actual length but perhaps by their maximum possible length). */
|
||||
if (res.range.max < target_int_max ())
|
||||
{
|
||||
res.knownrange = true;
|
||||
/* When the the length of the longest string is known and not
|
||||
excessive use it as the likely length of the string(s). */
|
||||
res.range.likely = res.range.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When the upper bound is unknown (it can be zero or excessive)
|
||||
set the likely length to the greater of 1 and the length of
|
||||
the shortest string and reset the lower bound to zero. */
|
||||
res.range.likely = res.range.min ? res.range.min : warn_level > 1;
|
||||
res.range.min = 0;
|
||||
}
|
||||
|
||||
/* If the range of string length has been estimated from the size
|
||||
of an array at the end of a struct assume that it's longer than
|
||||
the array bound says it is in case it's used as a poor man's
|
||||
flexible array member, such as in struct S { char a[4]; }; */
|
||||
res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max;
|
||||
|
||||
return res;
|
||||
res.knownrange = true;
|
||||
/* When the the length of the longest string is known and not
|
||||
excessive use it as the likely length of the string(s). */
|
||||
res.range.likely = res.range.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When the upper bound is unknown (it can be zero or excessive)
|
||||
set the likely length to the greater of 1 and the length of
|
||||
the shortest string and reset the lower bound to zero. */
|
||||
res.range.likely = res.range.min ? res.range.min : warn_level > 1;
|
||||
res.range.min = 0;
|
||||
}
|
||||
|
||||
return fmtresult ();
|
||||
res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Return the minimum and maximum number of characters formatted
|
||||
@ -2326,6 +2309,8 @@ format_string (const directive &dir, tree arg, vr_values *)
|
||||
if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
|
||||
res.range.max = dir.prec[1];
|
||||
res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
|
||||
if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.unlikely)
|
||||
res.range.unlikely = dir.prec[1];
|
||||
}
|
||||
else if (slen.range.min >= target_int_max ())
|
||||
{
|
||||
@ -2335,6 +2320,7 @@ format_string (const directive &dir, tree arg, vr_values *)
|
||||
empty, while at level 1 they are assumed to be one byte
|
||||
long. */
|
||||
res.range.likely = warn_level > 1;
|
||||
res.range.unlikely = HOST_WIDE_INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2344,8 +2330,6 @@ format_string (const directive &dir, tree arg, vr_values *)
|
||||
if (res.range.likely >= target_int_max ())
|
||||
res.range.likely = warn_level > 1;
|
||||
}
|
||||
|
||||
res.range.unlikely = res.range.max;
|
||||
}
|
||||
|
||||
/* If the argument isn't a nul-terminated string and the number
|
||||
|
@ -1,3 +1,12 @@
|
||||
2019-01-01 Martin Sebor <msebor@redhat.com>
|
||||
Jeff Law <law@redhat.com>
|
||||
|
||||
* gcc.dg/strlenopt-40.c: Disable a couple tests.
|
||||
* gcc.dg/strlenopt-48.c: Twiddle test slightly.
|
||||
* gcc.dg/strlenopt-59.c: New test.
|
||||
* gcc.dg/tree-ssa/builtin-snprintf-5.c: New test.
|
||||
* g++.dg/init/strlen.C: New test.
|
||||
|
||||
2019-01-01 Thomas Koenig <tkoenig@gcc.gnu.org>
|
||||
|
||||
PR fortran/82743
|
||||
|
43
gcc/testsuite/g++.dg/init/strlen.C
Normal file
43
gcc/testsuite/g++.dg/init/strlen.C
Normal file
@ -0,0 +1,43 @@
|
||||
// Test to verify that the strlen() optimization doesn't make assumptions
|
||||
// about the static type of the object pointed to by its argument. See
|
||||
// the following thread for background:
|
||||
// https://gcc.gnu.org/ml/gcc-patches/2018-08/msg00260.html
|
||||
|
||||
// { dg-do run }
|
||||
// { dg-options "-O2 -Wall -fdump-tree-optimized" }
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void *operator new[] (size_t, void *p) { return p; }
|
||||
|
||||
struct S { int x; char a[1]; char b[64]; };
|
||||
|
||||
__attribute__ ((noipa)) void
|
||||
init (char *s)
|
||||
{
|
||||
*s++ = '1';
|
||||
*s++ = '\0';
|
||||
}
|
||||
|
||||
__attribute__ ((noipa)) void
|
||||
test_dynamic_type (S *p)
|
||||
{
|
||||
// The placement new call below isn't strictly valid because it
|
||||
// creates an object that is larger than the space of the p->a
|
||||
// subobject in which it is created. However, the corresponding
|
||||
// GIMPLE considers it valid and there's apparently no way to
|
||||
// distinguish invalid cases from ones like it that might be valid.
|
||||
// If/when GIMPLE changes to make this possible this test can be
|
||||
// removed.
|
||||
char *q = new (p->a) char [16];
|
||||
|
||||
init (q);
|
||||
|
||||
if (0 == __builtin_strlen (q))
|
||||
__builtin_abort();
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
test_dynamic_type (new S);
|
||||
}
|
@ -124,8 +124,8 @@ void elim_global_arrays (int i)
|
||||
ELIM_TRUE (strlen (a7) < sizeof a7);
|
||||
|
||||
ELIM_TRUE (strlen (ax) != DIFF_MAX);
|
||||
ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
|
||||
ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
|
||||
/* ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); */
|
||||
/* ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); */
|
||||
}
|
||||
|
||||
void elim_pointer_to_arrays (void)
|
||||
|
@ -11,7 +11,7 @@ void f (void)
|
||||
{
|
||||
extern char a[2][1];
|
||||
int n = strlen (a[1]);
|
||||
if (n)
|
||||
if (n >= sizeof a)
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ void g (void)
|
||||
{
|
||||
extern char b[3][2][1];
|
||||
int n = strlen (b[2][1]);
|
||||
if (n)
|
||||
if (n >= sizeof b)
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ void h (void)
|
||||
{
|
||||
extern char c[4][3][2][1];
|
||||
int n = strlen (c[3][2][1]);
|
||||
if (n)
|
||||
if (n >= sizeof c)
|
||||
abort();
|
||||
}
|
||||
|
||||
|
73
gcc/testsuite/gcc.dg/strlenopt-59.c
Normal file
73
gcc/testsuite/gcc.dg/strlenopt-59.c
Normal file
@ -0,0 +1,73 @@
|
||||
/* Verify that strlen() calls with constant conditional expressions are
|
||||
eliminated as expected.
|
||||
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O1 -fdump-tree-optimized" } */
|
||||
|
||||
extern void abort (void);
|
||||
extern __SIZE_TYPE__ strlen (const char*);
|
||||
|
||||
|
||||
#define CAT(x, y) x ## y
|
||||
#define CONCAT(x, y) CAT (x, y)
|
||||
#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
|
||||
|
||||
#define FAIL(name) do { \
|
||||
extern void FAILNAME (name) (void); \
|
||||
FAILNAME (name)(); \
|
||||
} while (0)
|
||||
|
||||
/* Macros to emit a call to funcation named
|
||||
call_failed_to_be_eliminated_on_line_NNN()
|
||||
for each call that's expected to be eliminated. The dg-final
|
||||
scan-tree-dump-time directive at the bottom of the test verifies
|
||||
that no such call appears in output. */
|
||||
#define ELIM(expr) \
|
||||
if ((expr)) FAIL (test_not_eliminated); else (void)0
|
||||
|
||||
extern char a3[3];
|
||||
extern char a7[7];
|
||||
|
||||
struct MemArrays { char a[7], b[9]; };
|
||||
|
||||
struct MemArrays ma;
|
||||
|
||||
void test_elim_condexpr (int i)
|
||||
{
|
||||
ELIM (6 < strlen (i ? "" : "123456"));
|
||||
ELIM (6 < strlen (i ? "123456" : ""));
|
||||
|
||||
ELIM (4 < strlen (i < 1 ? "a" : i == 1 ? "ab" : "abc"));
|
||||
|
||||
ELIM (3 < strlen (i ? "" : a3));
|
||||
ELIM (3 < strlen (i ? a3 : "1"));
|
||||
|
||||
ELIM (6 < strlen (i ? "12" : a7));
|
||||
ELIM (6 < strlen (i ? a7 : "123"));
|
||||
|
||||
ELIM (6 < strlen (i ? "1234" : a7));
|
||||
ELIM (7 < strlen (i ? a7 : "1234567"));
|
||||
|
||||
ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a3));
|
||||
ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? a3 : "abc"));
|
||||
ELIM (3 < strlen (i < 1 ? a3 : i == 1 ? "a" : "abc"));
|
||||
|
||||
ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a7));
|
||||
ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : "abc"));
|
||||
ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : "abc"));
|
||||
|
||||
ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : a3));
|
||||
ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : a3));
|
||||
|
||||
{
|
||||
enum { maxlen = sizeof ma - 1 };
|
||||
ELIM (maxlen < strlen (ma.a));
|
||||
}
|
||||
|
||||
{
|
||||
enum { maxlen = sizeof ma - __builtin_offsetof (struct MemArrays, b) - 1 };
|
||||
ELIM (maxlen < strlen (ma.b));
|
||||
}
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "test_not_eliminated_" 0 "optimized" } } */
|
51
gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-5.c
Normal file
51
gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-5.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
extern void abort (void);
|
||||
extern int snprintf (char*, size_t, const char*, ...);
|
||||
|
||||
const char s0[] = "";
|
||||
const char s1[] = "a";
|
||||
const char s2[] = "ab";
|
||||
|
||||
extern char ax[];
|
||||
extern const char* const ptr;
|
||||
|
||||
#define CAT(x, y) x ## y
|
||||
#define CONCAT(x, y) CAT (x, y)
|
||||
#define TEST CONCAT (test_on_line_, __LINE__)
|
||||
|
||||
#define KEEP(expr) do { \
|
||||
if ((expr)) { \
|
||||
extern void TEST (void); \
|
||||
TEST (); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
void test_literal (int i)
|
||||
{
|
||||
KEEP (0 < snprintf (0, 0, "%s", i ? "" : ax));
|
||||
KEEP (1 < snprintf (0, 0, "%s", i ? ax : "1"));
|
||||
KEEP (2 < snprintf (0, 0, "%s", i ? "12" : ptr));
|
||||
|
||||
KEEP (1 > snprintf (0, 0, "%s", i ? "" : ax));
|
||||
KEEP (1 > snprintf (0, 0, "%s", i ? ax : "1"));
|
||||
KEEP (2 > snprintf (0, 0, "%s", i ? "12" : ptr));
|
||||
}
|
||||
|
||||
void test_cststr (int i)
|
||||
{
|
||||
KEEP (0 < snprintf (0, 0, "%s", i ? s0 : ax));
|
||||
KEEP (1 < snprintf (0, 0, "%s", i ? ax : s1));
|
||||
KEEP (2 < snprintf (0, 0, "%s", i ? s2 : ptr));
|
||||
|
||||
KEEP (1 > snprintf (0, 0, "%s", i ? s0 : ax));
|
||||
KEEP (1 > snprintf (0, 0, "%s", i ? ax : s1));
|
||||
KEEP (2 > snprintf (0, 0, "%s", i ? s2 : ptr));
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "test_on_line_" 12 "optimized" } } */
|
@ -1989,15 +1989,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
|
||||
lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
|
||||
else
|
||||
{
|
||||
tree range[2];
|
||||
get_range_strlen (src, range);
|
||||
if (range[0] != NULL_TREE
|
||||
&& TREE_CODE (range[0]) == INTEGER_CST
|
||||
&& range[1] != NULL_TREE
|
||||
&& TREE_CODE (range[1]) == INTEGER_CST)
|
||||
c_strlen_data lendata = { };
|
||||
get_range_strlen (src, &lendata, /* eltsize = */1);
|
||||
if (TREE_CODE (lendata.minlen) == INTEGER_CST
|
||||
&& TREE_CODE (lendata.maxbound) == INTEGER_CST)
|
||||
{
|
||||
lenrange[0] = wi::to_wide (range[0], prec);
|
||||
lenrange[1] = wi::to_wide (range[1], prec);
|
||||
/* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
|
||||
which stores the length of the shortest known string. */
|
||||
if (integer_all_onesp (lendata.maxlen))
|
||||
lenrange[0] = wi::shwi (0, prec);
|
||||
else
|
||||
lenrange[0] = wi::to_wide (lendata.minlen, prec);
|
||||
lenrange[1] = wi::to_wide (lendata.maxbound, prec);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user