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:
Martin Sebor 2019-01-02 06:02:37 +00:00 committed by Jeff Law
parent 79b1c2295b
commit 5d6655ebcc
13 changed files with 370 additions and 170 deletions

View File

@ -1,6 +1,27 @@
2019-01-01 Martin Sebor <msebor@redhat.com> 2019-01-01 Martin Sebor <msebor@redhat.com>
Jeff Law <law@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. * gimple-fold.c (get_range_strlen): Update prototype.
(get_range_strlen_tree): Update prototype. Drop minlen/maxlen (get_range_strlen_tree): Update prototype. Drop minlen/maxlen
local variables. Use pdata to return information to caller. local variables. Use pdata to return information to caller.

View File

@ -3341,7 +3341,10 @@ check_access (tree exp, tree, tree, tree dstwrite,
the upper bound given by MAXREAD add one to it for the upper bound given by MAXREAD add one to it for
the terminating nul. Otherwise, set it to one for the terminating nul. Otherwise, set it to one for
the same reason, or to MAXREAD as appropriate. */ 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 (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
{ {
if (maxread && tree_int_cst_le (maxread, range[0])) 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 /* Try to determine the range of lengths that the source expression
refers to. */ refers to. */
tree lenrange[2]; c_strlen_data lendata = { };
get_range_strlen (src, lenrange); get_range_strlen (src, &lendata, /* eltsize = */ 1);
/* Try to verify that the destination is big enough for the shortest /* Try to verify that the destination is big enough for the shortest
string. */ string. */
@ -4224,8 +4227,8 @@ check_strncat_sizes (tree exp, tree objsize)
} }
/* Add one for the terminating nul. */ /* Add one for the terminating nul. */
tree srclen = (lenrange[0] tree srclen = (lendata.minlen
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0], ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
size_one_node) size_one_node)
: NULL_TREE); : NULL_TREE);
@ -4277,12 +4280,15 @@ expand_builtin_strncat (tree exp, rtx)
tree slen = c_strlen (src, 1); tree slen = c_strlen (src, 1);
/* Try to determine the range of lengths that the source expression /* Try to determine the range of lengths that the source expression
refers to. */ refers to. Since the lengths are only used for warning and not
tree lenrange[2]; for code generation disable strict mode below. */
if (slen) tree maxlen = slen;
lenrange[0] = lenrange[1] = slen; if (!maxlen)
else {
get_range_strlen (src, lenrange); 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 /* Try to verify that the destination is big enough for the shortest
string. First try to determine the size of the destination object 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); tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
/* Add one for the terminating nul. */ /* Add one for the terminating nul. */
tree srclen = (lenrange[0] tree srclen = (maxlen
? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0], ? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
size_one_node) size_one_node)
: NULL_TREE); : NULL_TREE);

View File

@ -1569,9 +1569,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
/* The bound argument to a bounded string function like strncpy. */ /* The bound argument to a bounded string function like strncpy. */
tree bound = NULL_TREE; tree bound = NULL_TREE;
/* The range of lengths of a string argument to one of the comparison /* 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. */ functions. If the length is less than the bound it is used instead.
tree lenrng[2] = { NULL_TREE, NULL_TREE }; 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 /* It's safe to call "bounded" string functions with a non-string
argument since the functions provide an explicit bound for this 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. */ and to adjust the range of the bound of the bounded ones. */
for (unsigned argno = 0; for (unsigned argno = 0;
argno < MIN (nargs, 2) 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); tree arg = CALL_EXPR_ARG (exp, argno);
if (!get_attr_nonstring_decl (arg)) 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. */ /* Fall through. */
@ -1616,8 +1622,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
{ {
tree arg = CALL_EXPR_ARG (exp, 0); tree arg = CALL_EXPR_ARG (exp, 0);
if (!get_attr_nonstring_decl (arg)) 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) if (nargs > 1)
bound = CALL_EXPR_ARG (exp, 1); bound = CALL_EXPR_ARG (exp, 1);
break; 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. */ /* Add one for the nul. */
lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]), maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
lenrng[1], size_one_node); size_one_node);
if (!bndrng[0]) if (!bndrng[0])
{ {
/* Conservatively use the upper bound of the lengths for /* Conservatively use the upper bound of the lengths for
both the lower and the upper bound of the operation. */ both the lower and the upper bound of the operation. */
bndrng[0] = lenrng[1]; bndrng[0] = maxlen;
bndrng[1] = lenrng[1]; bndrng[1] = maxlen;
bound = void_type_node; bound = void_type_node;
} }
else else
{ {
/* Replace the bound on the operation with the upper bound /* Replace the bound on the operation with the upper bound
of the length of the string if the latter is smaller. */ of the length of the string if the latter is smaller. */
if (tree_int_cst_lt (lenrng[1], bndrng[0])) if (tree_int_cst_lt (maxlen, bndrng[0]))
bndrng[0] = lenrng[1]; bndrng[0] = maxlen;
else if (tree_int_cst_lt (lenrng[1], bndrng[1])) else if (tree_int_cst_lt (maxlen, bndrng[1]))
bndrng[1] = lenrng[1]; bndrng[1] = maxlen;
} }
} }

View File

@ -1502,11 +1502,14 @@ get_range_strlen_tree (tree arg, bitmap *visited,
return true; return true;
} }
/* Obtain the minimum and maximum string length or minimum and maximum /* For an ARG referencing one or more strings, try to obtain the range
value of ARG in LENGTH[0] and LENGTH[1], respectively. of their lengths, or the size of the largest array ARG referes to if
If ARG is an SSA name variable, follow its use-def chains. When the range of lengths cannot be determined, and store all in *PDATA.
TYPE == 0, if LENGTH[1] is not equal to the length we determine or For an integer ARG (when RKIND == SRK_INT_VALUE), try to determine
if we are unable to determine the length or value, return false. 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. VISITED is a bitmap of visited variables.
RKIND determines the kind of value or range to obtain (see RKIND determines the kind of value or range to obtain (see
strlen_range_kind). 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. */ Return true if *PDATA was successfully populated and false otherwise. */
static bool static bool
get_range_strlen (tree arg, bitmap *visited, get_range_strlen (tree arg, bitmap *visited, strlen_range_kind rkind,
strlen_range_kind rkind,
c_strlen_data *pdata, bool *flexp, unsigned eltsize) c_strlen_data *pdata, bool *flexp, unsigned eltsize)
{ {
@ -1612,6 +1614,7 @@ get_range_strlen (tree arg, bitmap *visited,
return false; return false;
} }
} }
/* Determine the minimum and maximum value or string length that ARG /* Determine the minimum and maximum value or string length that ARG
refers to and store each in the first two elements of MINMAXLEN. refers to and store each in the first two elements of MINMAXLEN.
For expressions that point to strings of unknown lengths that are 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. */ 4 for wide characer strings. ELTSIZE is by default 1. */
bool bool
get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize, get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize, bool strict)
bool strict, tree *nonstr /* = NULL */)
{ {
bitmap visited = NULL; bitmap visited = NULL;
minmaxlen[0] = NULL_TREE;
minmaxlen[1] = NULL_TREE;
tree nonstrbuf;
if (!nonstr)
nonstr = &nonstrbuf;
*nonstr = NULL_TREE;
bool flexarray = false; bool flexarray = false;
c_strlen_data lendata = { }; if (!get_range_strlen (arg, &visited, strict ? SRK_LENRANGE : SRK_LENRANGE_2, pdata, &flexarray, eltsize))
if (!get_range_strlen (arg, &visited,
strict ? SRK_LENRANGE : SRK_LENRANGE_2,
&lendata, &flexarray, eltsize))
{ {
minmaxlen[0] = NULL_TREE; /* On failure extend the length range to an impossible maximum
minmaxlen[1] = NULL_TREE; (a valid MAXLEN must be less than PTRDIFF_MAX - 1). Other
} members can stay unchanged regardless. */
else pdata->minlen = ssize_int (0);
{ pdata->maxlen = build_all_ones_cst (size_type_node);
minmaxlen[0] = lendata.minlen;
minmaxlen[1] = lendata.maxlen;
} }
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) if (visited)
BITMAP_FREE (visited); BITMAP_FREE (visited);
return flexarray; return flexarray;
} }
/* Return the maximum string length for ARG, counting by TYPE /* Return the maximum value for ARG given RKIND (see strlen_range_kind).
(1, 2 or 4 for normal or wide chars). NONSTR indicates For ARG of pointer types, NONSTR indicates if the caller is prepared
if the caller is prepared to handle unterminated strings. 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 If an unterminated array is discovered and our caller handles
unterminated strings, then bubble up the offending DECL and unterminated arrays, then bubble up the offending DECL and
return the maximum size. Otherwise return NULL. */ return the maximum size. Otherwise return NULL. */
static tree static tree
@ -1692,10 +1687,15 @@ get_maxval_strlen (tree arg, strlen_range_kind rkind, tree *nonstr = NULL)
bitmap visited = NULL; bitmap visited = NULL;
bool dummy; /* Reset DATA.MAXLEN if the call fails or when DATA.MAXLEN
is unbounded. */
c_strlen_data lendata = { }; c_strlen_data lendata = { };
bool dummy;
if (!get_range_strlen (arg, &visited, rkind, &lendata, &dummy, 1)) if (!get_range_strlen (arg, &visited, rkind, &lendata, &dummy, 1))
lendata.maxlen = NULL_TREE; lendata.maxlen = NULL_TREE;
else if (lendata.maxlen && integer_all_onesp (lendata.maxlen))
lendata.maxlen = NULL_TREE;
if (visited) if (visited)
BITMAP_FREE (visited); BITMAP_FREE (visited);
@ -3668,21 +3668,19 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
wide_int minlen; wide_int minlen;
wide_int maxlen; wide_int maxlen;
/* Set to non-null if ARG refers to an unterminated array. */ c_strlen_data lendata = { };
tree nonstr; if (!get_range_strlen (arg, &lendata, /* eltsize = */ 1)
tree lenrange[2]; && !lendata.decl
if (!get_range_strlen (arg, lenrange, 1, true, &nonstr) && lendata.minlen && TREE_CODE (lendata.minlen) == INTEGER_CST
&& !nonstr && lendata.maxlen && TREE_CODE (lendata.maxlen) == INTEGER_CST)
&& lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
&& lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
{ {
/* The range of lengths refers to either a single constant /* The range of lengths refers to either a single constant
string or to the longest and shortest constant string string or to the longest and shortest constant string
referenced by the argument of the strlen() call, or to referenced by the argument of the strlen() call, or to
the strings that can possibly be stored in the arrays the strings that can possibly be stored in the arrays
the argument refers to. */ the argument refers to. */
minlen = wi::to_wide (lenrange[0]); minlen = wi::to_wide (lendata.minlen);
maxlen = wi::to_wide (lenrange[1]); maxlen = wi::to_wide (lendata.maxlen);
} }
else else
{ {
@ -3694,9 +3692,12 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
if (minlen == maxlen) if (minlen == maxlen)
{ {
lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL, /* Fold the strlen call to a constant. */
true, GSI_SAME_STMT); tree type = TREE_TYPE (lendata.minlen);
replace_call_with_value (gsi, lenrange[0]); 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; return true;
} }

View File

@ -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 create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL);
extern tree canonicalize_constructor_val (tree, tree); extern tree canonicalize_constructor_val (tree, tree);
extern tree get_symbol_constant_value (tree); extern tree get_symbol_constant_value (tree);
extern bool get_range_strlen (tree, tree[2], unsigned = 1, struct c_strlen_data;
bool = false, tree * = NULL); 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 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 *);
extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree)); extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));

View File

@ -2003,90 +2003,73 @@ get_string_length (tree str, unsigned eltsize)
if (!str) if (!str)
return fmtresult (); 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 /* Determine the length of the shortest and longest string referenced
by STR. Strings of unknown lengths are bounded by the sizes of by STR. Strings of unknown lengths are bounded by the sizes of
arrays that subexpressions of STR may refer to. Pointers that arrays that subexpressions of STR may refer to. Pointers that
aren't known to point any such arrays result in LENRANGE[1] set aren't known to point any such arrays result in LENDATA.MAXLEN
to SIZE_MAX. NONSTR is set to the declaration of the constant set to SIZE_MAX. */
array that is known not to be nul-terminated. */ c_strlen_data lendata = { };
tree lenrange[2]; bool flexarray = get_range_strlen (str, &lendata, eltsize);
tree nonstr;
bool flexarray = get_range_strlen (str, lenrange, eltsize, false, &nonstr);
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 res.knownrange = true;
= (tree_fits_uhwi_p (lenrange[0]) /* When the the length of the longest string is known and not
? tree_to_uhwi (lenrange[0]) excessive use it as the likely length of the string(s). */
: 0); res.range.likely = res.range.max;
}
HOST_WIDE_INT max else
= (tree_fits_uhwi_p (lenrange[1]) {
? tree_to_uhwi (lenrange[1]) /* When the upper bound is unknown (it can be zero or excessive)
: HOST_WIDE_INT_M1U); set the likely length to the greater of 1 and the length of
the shortest string and reset the lower bound to zero. */
/* get_range_strlen() returns the target value of SIZE_MAX for res.range.likely = res.range.min ? res.range.min : warn_level > 1;
strings of unknown length. Bump it up to HOST_WIDE_INT_M1U res.range.min = 0;
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;
} }
return fmtresult (); res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
return res;
} }
/* Return the minimum and maximum number of characters formatted /* 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) if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
res.range.max = dir.prec[1]; res.range.max = dir.prec[1];
res.range.likely = dir.prec[1] ? warn_level > 1 : 0; 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 ()) 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 empty, while at level 1 they are assumed to be one byte
long. */ long. */
res.range.likely = warn_level > 1; res.range.likely = warn_level > 1;
res.range.unlikely = HOST_WIDE_INT_MAX;
} }
else else
{ {
@ -2344,8 +2330,6 @@ format_string (const directive &dir, tree arg, vr_values *)
if (res.range.likely >= target_int_max ()) if (res.range.likely >= target_int_max ())
res.range.likely = warn_level > 1; res.range.likely = warn_level > 1;
} }
res.range.unlikely = res.range.max;
} }
/* If the argument isn't a nul-terminated string and the number /* If the argument isn't a nul-terminated string and the number

View File

@ -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> 2019-01-01 Thomas Koenig <tkoenig@gcc.gnu.org>
PR fortran/82743 PR fortran/82743

View 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);
}

View File

@ -124,8 +124,8 @@ void elim_global_arrays (int i)
ELIM_TRUE (strlen (a7) < sizeof a7); ELIM_TRUE (strlen (a7) < sizeof a7);
ELIM_TRUE (strlen (ax) != DIFF_MAX); 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) void elim_pointer_to_arrays (void)

View File

@ -11,7 +11,7 @@ void f (void)
{ {
extern char a[2][1]; extern char a[2][1];
int n = strlen (a[1]); int n = strlen (a[1]);
if (n) if (n >= sizeof a)
abort(); abort();
} }
@ -19,7 +19,7 @@ void g (void)
{ {
extern char b[3][2][1]; extern char b[3][2][1];
int n = strlen (b[2][1]); int n = strlen (b[2][1]);
if (n) if (n >= sizeof b)
abort(); abort();
} }
@ -27,7 +27,7 @@ void h (void)
{ {
extern char c[4][3][2][1]; extern char c[4][3][2][1];
int n = strlen (c[3][2][1]); int n = strlen (c[3][2][1]);
if (n) if (n >= sizeof c)
abort(); abort();
} }

View 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" } } */

View 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" } } */

View File

@ -1989,15 +1989,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec); lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
else else
{ {
tree range[2]; c_strlen_data lendata = { };
get_range_strlen (src, range); get_range_strlen (src, &lendata, /* eltsize = */1);
if (range[0] != NULL_TREE if (TREE_CODE (lendata.minlen) == INTEGER_CST
&& TREE_CODE (range[0]) == INTEGER_CST && TREE_CODE (lendata.maxbound) == INTEGER_CST)
&& range[1] != NULL_TREE
&& TREE_CODE (range[1]) == INTEGER_CST)
{ {
lenrange[0] = wi::to_wide (range[0], prec); /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
lenrange[1] = wi::to_wide (range[1], prec); 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 else
{ {