PR middle-end/78703 - -fprintf-return-value floating point handling incorrect...

PR middle-end/78703 - -fprintf-return-value floating point handling incorrect in locales with a mulltibyte decimal point
	* gimple-ssa-sprintf.c (struct format_result): Remove constant member.
	(struct fmtresult, format_integer, format_floating): Adjust.
	(get_string_length, format_string,format_directive): Same.
	(pass_sprintf_length::compute_format_length): Same.
	(try_substitute_return_value): Simplify slightly.

From-SVN: r244846
This commit is contained in:
Martin Sebor 2017-01-24 01:06:34 +00:00 committed by Martin Sebor
parent 954b452ada
commit a151e93b2b
2 changed files with 88 additions and 110 deletions

View File

@ -1,5 +1,12 @@
2017-01-23 Martin Sebor <msebor@redhat.com>
PR middle-end/78703
* gimple-ssa-sprintf.c (struct format_result): Remove constant member.
(struct fmtresult, format_integer, format_floating): Adjust.
(get_string_length, format_string,format_directive): Same.
(pass_sprintf_length::compute_format_length): Same.
(try_substitute_return_value): Simplify slightly.
PR middle-end/78703
* gimple-ssa-sprintf.c (pass_sprintf_length::gate): Adjust formatting.
(fmtresult::operator+=): Outlined.

View File

@ -183,12 +183,6 @@ struct format_result
the return value of a call. */
bool knownrange;
/* True when the output of the formatted call is constant (and
thus a candidate for string constant folding). This is rare
and typically requires that the arguments of all directives
are also constant. CONSTANT implies BOUNDED. */
bool constant;
/* True if no individual directive resulted in more than 4095 bytes
of output (the total NUMBER_CHARS might be greater). */
bool under4k;
@ -452,7 +446,6 @@ struct fmtresult
: argmin (), argmax (),
knownrange (min < HOST_WIDE_INT_MAX),
bounded (),
constant (),
nullp ()
{
range.min = min;
@ -465,11 +458,10 @@ struct fmtresult
: argmin (), argmax (),
knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
bounded (),
constant (),
nullp ()
{
range.min = min;
range.max = min;
range.max = max;
}
/* The range a directive's argument is in. */
@ -490,12 +482,6 @@ struct fmtresult
false otherwise. */
bool bounded;
/* True when the output of a directive is constant. This is rare
and typically requires that the argument(s) of the directive
are also constant (such as determined by constant propagation,
though not value range propagation). */
bool constant;
/* True when the argument is a null pointer. */
bool nullp;
};
@ -818,7 +804,7 @@ static fmtresult
format_none (const directive &, tree)
{
fmtresult res (0);
res.bounded = res.constant = true;
res.bounded = true;
return res;
}
@ -828,7 +814,7 @@ static fmtresult
format_percent (const directive &, tree)
{
fmtresult res (1);
res.bounded = res.constant = true;
res.bounded = true;
return res;
}
@ -1095,7 +1081,6 @@ format_integer (const directive &dir, tree arg)
{
res.range.max = len;
res.bounded = true;
res.constant = true;
res.knownrange = true;
res.bounded = true;
}
@ -1537,7 +1522,6 @@ format_floating (const directive &dir, tree arg)
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res;
res.constant = true;
/* Get the real type format desription for the target. */
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
@ -1634,7 +1618,6 @@ get_string_length (tree str)
fmtresult res;
res.range.min = res.range.max = tree_to_shwi (slen);
res.bounded = true;
res.constant = true;
res.knownrange = true;
return res;
}
@ -1662,11 +1645,6 @@ get_string_length (tree str)
res.bounded = res.range.max < target_int_max ();
res.knownrange = res.bounded;
/* Set RES.CONSTANT to false even though that may be overly
conservative in rare cases like: 'x ? a : b' where a and
b have the same lengths and consist of the same characters. */
res.constant = false;
return res;
}
@ -1713,7 +1691,6 @@ format_character (const directive &dir, tree arg)
res.range.min = res.range.max = 1;
res.bounded = true;
res.knownrange = true;
res.constant = arg && TREE_CODE (arg) == INTEGER_CST;
}
/* Adjust the lengths for field width. */
@ -1759,7 +1736,8 @@ format_string (const directive &dir, tree arg)
/* Compute the range the argument's length can be in. */
fmtresult slen = get_string_length (arg);
if (slen.constant)
if (slen.range.min == slen.range.max
&& slen.range.min < HOST_WIDE_INT_MAX)
{
gcc_checking_assert (slen.range.min == slen.range.max);
@ -1807,7 +1785,6 @@ format_string (const directive &dir, tree arg)
precision is either not specified or it is specified and
its value is known. */
res.bounded = true;
res.constant = dir.prec != HOST_WIDE_INT_MIN;
}
else if (dir.width == HOST_WIDE_INT_MIN)
{
@ -1868,7 +1845,6 @@ format_string (const directive &dir, tree arg)
of bytes is known or contrained to some range. */
res.bounded = 0 <= dir.prec || slen.bounded;
res.knownrange = slen.knownrange;
res.constant = false;
}
/* Adjust the lengths for field width. */
@ -1888,7 +1864,7 @@ format_string (const directive &dir, tree arg)
/* When precision is specified the range of characters on output
is known to be bounded by it. */
if (HOST_WIDE_INT_MIN != dir.width && HOST_WIDE_INT_MIN != dir.prec)
if (HOST_WIDE_INT_MIN != dir.width && -1 < dir.prec)
res.knownrange = true;
return res;
@ -1936,10 +1912,9 @@ format_directive (const pass_sprintf_length::call_info &info,
/* Compute the (approximate) length of the formatted output. */
fmtresult fmtres = dir.fmtfunc (dir, arg);
/* The overall result is bounded and constant only if the output
of every directive is bounded and constant, respectively. */
/* The overall result is bounded only if the output of every directive
is bounded. */
res->bounded &= fmtres.bounded;
res->constant &= fmtres.constant;
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
@ -2792,11 +2767,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
res->number_chars = res->number_chars_min = res->number_chars_max = 0;
/* No directive has been seen yet so the length of output is bounded
by the known range [0, 0] and constant (with no conversion producing
more than 4K bytes) until determined otherwise. */
by the known range [0, 0] (with no conversion producing more than
4K bytes) until determined otherwise. */
res->bounded = true;
res->knownrange = true;
res->constant = true;
res->under4k = true;
res->floating = false;
res->warned = false;
@ -2877,42 +2851,71 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
const pass_sprintf_length::call_info &info,
const format_result &res)
{
if (!res.bounded)
return false;
tree lhs = gimple_get_lhs (info.callstmt);
/* Set to true when the entire call has been removed. */
bool removed = false;
/* The minumum return value. */
unsigned HOST_WIDE_INT minretval = res.number_chars_min;
/* The maximum return value. */
unsigned HOST_WIDE_INT maxretval = res.number_chars_max;
/* Adjust the number of bytes which includes the terminating nul
to reflect the return value of the function which does not.
Because the valid range of the function is [INT_MIN, INT_MAX],
a valid range before the adjustment below is [0, INT_MAX + 1]
(the functions only return negative values on error or undefined
behavior). */
if (minretval <= target_int_max () + 1)
--minretval;
if (maxretval <= target_int_max () + 1)
--maxretval;
/* Avoid the return value optimization when the behavior of the call
is undefined either because any directive may have produced 4K or
more of output, or the return value exceeds INT_MAX, or because
the output overflows the destination object (but leave it enabled
when the function is bounded because then the behavior is well-
defined). */
if (lhs
&& res.bounded
&& res.under4k
&& (info.bounded || res.number_chars <= info.objsize)
&& res.number_chars - 1 <= target_int_max ()
if (res.under4k
&& minretval == maxretval
&& (info.bounded || minretval < info.objsize)
&& minretval <= target_int_max ()
/* Not prepared to handle possibly throwing calls here; they shouldn't
appear in non-artificial testcases, except when the __*_chk routines
are badly declared. */
&& !stmt_ends_bb_p (info.callstmt))
{
tree cst = build_int_cst (integer_type_node, res.number_chars - 1);
tree cst = build_int_cst (integer_type_node, minretval);
if (info.nowrite)
if (lhs == NULL_TREE
&& info.nowrite)
{
/* Remove the call to the bounded function with a zero size
(e.g., snprintf(0, 0, "%i", 123)) if there is no lhs. */
unlink_stmt_vdef (info.callstmt);
gsi_remove (gsi, true);
removed = true;
}
else if (info.nowrite)
{
/* Replace the call to the bounded function with a zero size
(e.g., snprintf(0, 0, "%i", 123) with the constant result
of the function minus 1 for the terminating NUL which
the function's return value does not include. */
of the function. */
if (!update_call_from_tree (gsi, cst))
gimplify_and_update_call_from_tree (gsi, cst);
gimple *callstmt = gsi_stmt (*gsi);
update_stmt (callstmt);
}
else
else if (lhs)
{
/* Replace the left-hand side of the call with the constant
result of the formatted function minus 1 for the terminating
NUL which the function's return value does not include. */
result of the formatted function. */
gimple_call_set_lhs (info.callstmt, NULL_TREE);
gimple *g = gimple_build_assign (lhs, cst);
gsi_insert_after (gsi, g, GSI_NEW_STMT);
@ -2921,88 +2924,56 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
if (dump_file)
{
location_t callloc = gimple_location (info.callstmt);
fprintf (dump_file, "On line %i substituting ",
LOCATION_LINE (callloc));
print_generic_expr (dump_file, cst, dump_flags);
fprintf (dump_file, " for ");
print_generic_expr (dump_file, info.func, dump_flags);
fprintf (dump_file, " %s (output %s).\n",
info.nowrite ? "call" : "return value",
res.constant ? "constant" : "variable");
if (removed)
fprintf (dump_file, " Removing call statement.");
else
{
fprintf (dump_file, " Substituting ");
print_generic_expr (dump_file, cst, dump_flags);
fprintf (dump_file, " for %s.\n",
info.nowrite ? "statement" : "return value");
}
}
}
else if (lhs == NULL_TREE
&& info.nowrite
&& !stmt_ends_bb_p (info.callstmt))
else if (lhs)
{
/* Remove the call to the bounded function with a zero size
(e.g., snprintf(0, 0, "%i", 123)) if there is no lhs. */
unlink_stmt_vdef (info.callstmt);
gsi_remove (gsi, true);
if (dump_file)
{
location_t callloc = gimple_location (info.callstmt);
fprintf (dump_file, "On line %i removing ",
LOCATION_LINE (callloc));
print_generic_expr (dump_file, info.func, dump_flags);
fprintf (dump_file, " call.\n");
}
return true;
}
else
{
unsigned HOST_WIDE_INT maxbytes;
bool setrange = false;
if (lhs
&& res.bounded
&& ((maxbytes = res.number_chars - 1) <= target_int_max ()
|| (res.number_chars_min - 1 <= target_int_max ()
&& (maxbytes = res.number_chars_max - 1) <= target_int_max ()))
&& (info.bounded || maxbytes < info.objsize))
if ((info.bounded || maxretval < info.objsize)
&& res.under4k
&& (minretval < target_int_max ()
&& maxretval < target_int_max ()))
{
/* If the result is in a valid range bounded by the size of
the destination set it so that it can be used for subsequent
optimizations. */
int prec = TYPE_PRECISION (integer_type_node);
if (res.number_chars < target_int_max () && res.under4k)
{
wide_int num = wi::shwi (res.number_chars - 1, prec);
set_range_info (lhs, VR_RANGE, num, num);
}
else if (res.number_chars_min < target_int_max ()
&& res.number_chars_max < target_int_max ())
{
wide_int min = wi::shwi (res.under4k ? res.number_chars_min - 1
: target_int_min (), prec);
wide_int max = wi::shwi (res.number_chars_max - 1, prec);
set_range_info (lhs, VR_RANGE, min, max);
}
wide_int min = wi::shwi (minretval, prec);
wide_int max = wi::shwi (maxretval, prec);
set_range_info (lhs, VR_RANGE, min, max);
setrange = true;
}
if (dump_file)
{
const char *inbounds
= (res.number_chars_min <= info.objsize
? (res.number_chars_max <= info.objsize
= (minretval < info.objsize
? (maxretval < info.objsize
? "in" : "potentially out-of")
: "out-of");
location_t callloc = gimple_location (info.callstmt);
fprintf (dump_file, "On line %i ", LOCATION_LINE (callloc));
print_generic_expr (dump_file, info.func, dump_flags);
const char *ign = lhs ? "" : " ignored";
if (res.number_chars >= HOST_WIDE_INT_MAX)
const char *what = setrange ? "Setting" : "Discarding";
if (minretval != maxretval)
fprintf (dump_file,
" %s-bounds return value in range [%lu, %lu]%s.\n",
inbounds,
(unsigned long)res.number_chars_min - 1,
(unsigned long)res.number_chars_max - 1, ign);
" %s %s-bounds return value range [%llu, %llu].\n",
what, inbounds,
(unsigned long long)minretval,
(unsigned long long)maxretval);
else
fprintf (dump_file, " %s-bounds return value %lu%s.\n",
inbounds, (unsigned long)res.number_chars - 1, ign);
fprintf (dump_file, " %s %s-bounds return value %llu.\n",
what, inbounds, (unsigned long long)minretval);
}
}