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:
parent
954b452ada
commit
a151e93b2b
@ -1,5 +1,12 @@
|
|||||||
2017-01-23 Martin Sebor <msebor@redhat.com>
|
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
|
PR middle-end/78703
|
||||||
* gimple-ssa-sprintf.c (pass_sprintf_length::gate): Adjust formatting.
|
* gimple-ssa-sprintf.c (pass_sprintf_length::gate): Adjust formatting.
|
||||||
(fmtresult::operator+=): Outlined.
|
(fmtresult::operator+=): Outlined.
|
||||||
|
@ -183,12 +183,6 @@ struct format_result
|
|||||||
the return value of a call. */
|
the return value of a call. */
|
||||||
bool knownrange;
|
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
|
/* True if no individual directive resulted in more than 4095 bytes
|
||||||
of output (the total NUMBER_CHARS might be greater). */
|
of output (the total NUMBER_CHARS might be greater). */
|
||||||
bool under4k;
|
bool under4k;
|
||||||
@ -452,7 +446,6 @@ struct fmtresult
|
|||||||
: argmin (), argmax (),
|
: argmin (), argmax (),
|
||||||
knownrange (min < HOST_WIDE_INT_MAX),
|
knownrange (min < HOST_WIDE_INT_MAX),
|
||||||
bounded (),
|
bounded (),
|
||||||
constant (),
|
|
||||||
nullp ()
|
nullp ()
|
||||||
{
|
{
|
||||||
range.min = min;
|
range.min = min;
|
||||||
@ -465,11 +458,10 @@ struct fmtresult
|
|||||||
: argmin (), argmax (),
|
: argmin (), argmax (),
|
||||||
knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
|
knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
|
||||||
bounded (),
|
bounded (),
|
||||||
constant (),
|
|
||||||
nullp ()
|
nullp ()
|
||||||
{
|
{
|
||||||
range.min = min;
|
range.min = min;
|
||||||
range.max = min;
|
range.max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The range a directive's argument is in. */
|
/* The range a directive's argument is in. */
|
||||||
@ -490,12 +482,6 @@ struct fmtresult
|
|||||||
false otherwise. */
|
false otherwise. */
|
||||||
bool bounded;
|
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. */
|
/* True when the argument is a null pointer. */
|
||||||
bool nullp;
|
bool nullp;
|
||||||
};
|
};
|
||||||
@ -818,7 +804,7 @@ static fmtresult
|
|||||||
format_none (const directive &, tree)
|
format_none (const directive &, tree)
|
||||||
{
|
{
|
||||||
fmtresult res (0);
|
fmtresult res (0);
|
||||||
res.bounded = res.constant = true;
|
res.bounded = true;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,7 +814,7 @@ static fmtresult
|
|||||||
format_percent (const directive &, tree)
|
format_percent (const directive &, tree)
|
||||||
{
|
{
|
||||||
fmtresult res (1);
|
fmtresult res (1);
|
||||||
res.bounded = res.constant = true;
|
res.bounded = true;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1095,7 +1081,6 @@ format_integer (const directive &dir, tree arg)
|
|||||||
{
|
{
|
||||||
res.range.max = len;
|
res.range.max = len;
|
||||||
res.bounded = true;
|
res.bounded = true;
|
||||||
res.constant = true;
|
|
||||||
res.knownrange = true;
|
res.knownrange = true;
|
||||||
res.bounded = 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. */
|
/* The minimum and maximum number of bytes produced by the directive. */
|
||||||
fmtresult res;
|
fmtresult res;
|
||||||
res.constant = true;
|
|
||||||
|
|
||||||
/* Get the real type format desription for the target. */
|
/* Get the real type format desription for the target. */
|
||||||
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
|
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
|
||||||
@ -1634,7 +1618,6 @@ get_string_length (tree str)
|
|||||||
fmtresult res;
|
fmtresult res;
|
||||||
res.range.min = res.range.max = tree_to_shwi (slen);
|
res.range.min = res.range.max = tree_to_shwi (slen);
|
||||||
res.bounded = true;
|
res.bounded = true;
|
||||||
res.constant = true;
|
|
||||||
res.knownrange = true;
|
res.knownrange = true;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -1662,11 +1645,6 @@ get_string_length (tree str)
|
|||||||
res.bounded = res.range.max < target_int_max ();
|
res.bounded = res.range.max < target_int_max ();
|
||||||
res.knownrange = res.bounded;
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1713,7 +1691,6 @@ format_character (const directive &dir, tree arg)
|
|||||||
res.range.min = res.range.max = 1;
|
res.range.min = res.range.max = 1;
|
||||||
res.bounded = true;
|
res.bounded = true;
|
||||||
res.knownrange = true;
|
res.knownrange = true;
|
||||||
res.constant = arg && TREE_CODE (arg) == INTEGER_CST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the lengths for field width. */
|
/* 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. */
|
/* Compute the range the argument's length can be in. */
|
||||||
fmtresult slen = get_string_length (arg);
|
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);
|
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
|
precision is either not specified or it is specified and
|
||||||
its value is known. */
|
its value is known. */
|
||||||
res.bounded = true;
|
res.bounded = true;
|
||||||
res.constant = dir.prec != HOST_WIDE_INT_MIN;
|
|
||||||
}
|
}
|
||||||
else if (dir.width == 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. */
|
of bytes is known or contrained to some range. */
|
||||||
res.bounded = 0 <= dir.prec || slen.bounded;
|
res.bounded = 0 <= dir.prec || slen.bounded;
|
||||||
res.knownrange = slen.knownrange;
|
res.knownrange = slen.knownrange;
|
||||||
res.constant = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the lengths for field width. */
|
/* 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
|
/* When precision is specified the range of characters on output
|
||||||
is known to be bounded by it. */
|
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;
|
res.knownrange = true;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -1936,10 +1912,9 @@ format_directive (const pass_sprintf_length::call_info &info,
|
|||||||
/* Compute the (approximate) length of the formatted output. */
|
/* Compute the (approximate) length of the formatted output. */
|
||||||
fmtresult fmtres = dir.fmtfunc (dir, arg);
|
fmtresult fmtres = dir.fmtfunc (dir, arg);
|
||||||
|
|
||||||
/* The overall result is bounded and constant only if the output
|
/* The overall result is bounded only if the output of every directive
|
||||||
of every directive is bounded and constant, respectively. */
|
is bounded. */
|
||||||
res->bounded &= fmtres.bounded;
|
res->bounded &= fmtres.bounded;
|
||||||
res->constant &= fmtres.constant;
|
|
||||||
|
|
||||||
/* Record whether the output of all directives is known to be
|
/* Record whether the output of all directives is known to be
|
||||||
bounded by some maximum, implying that their arguments are
|
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;
|
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
|
/* 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
|
by the known range [0, 0] (with no conversion producing more than
|
||||||
more than 4K bytes) until determined otherwise. */
|
4K bytes) until determined otherwise. */
|
||||||
res->bounded = true;
|
res->bounded = true;
|
||||||
res->knownrange = true;
|
res->knownrange = true;
|
||||||
res->constant = true;
|
|
||||||
res->under4k = true;
|
res->under4k = true;
|
||||||
res->floating = false;
|
res->floating = false;
|
||||||
res->warned = false;
|
res->warned = false;
|
||||||
@ -2877,42 +2851,71 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
|
|||||||
const pass_sprintf_length::call_info &info,
|
const pass_sprintf_length::call_info &info,
|
||||||
const format_result &res)
|
const format_result &res)
|
||||||
{
|
{
|
||||||
|
if (!res.bounded)
|
||||||
|
return false;
|
||||||
|
|
||||||
tree lhs = gimple_get_lhs (info.callstmt);
|
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
|
/* Avoid the return value optimization when the behavior of the call
|
||||||
is undefined either because any directive may have produced 4K or
|
is undefined either because any directive may have produced 4K or
|
||||||
more of output, or the return value exceeds INT_MAX, or because
|
more of output, or the return value exceeds INT_MAX, or because
|
||||||
the output overflows the destination object (but leave it enabled
|
the output overflows the destination object (but leave it enabled
|
||||||
when the function is bounded because then the behavior is well-
|
when the function is bounded because then the behavior is well-
|
||||||
defined). */
|
defined). */
|
||||||
if (lhs
|
if (res.under4k
|
||||||
&& res.bounded
|
&& minretval == maxretval
|
||||||
&& res.under4k
|
&& (info.bounded || minretval < info.objsize)
|
||||||
&& (info.bounded || res.number_chars <= info.objsize)
|
&& minretval <= target_int_max ()
|
||||||
&& res.number_chars - 1 <= target_int_max ()
|
|
||||||
/* Not prepared to handle possibly throwing calls here; they shouldn't
|
/* Not prepared to handle possibly throwing calls here; they shouldn't
|
||||||
appear in non-artificial testcases, except when the __*_chk routines
|
appear in non-artificial testcases, except when the __*_chk routines
|
||||||
are badly declared. */
|
are badly declared. */
|
||||||
&& !stmt_ends_bb_p (info.callstmt))
|
&& !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
|
/* Replace the call to the bounded function with a zero size
|
||||||
(e.g., snprintf(0, 0, "%i", 123) with the constant result
|
(e.g., snprintf(0, 0, "%i", 123) with the constant result
|
||||||
of the function minus 1 for the terminating NUL which
|
of the function. */
|
||||||
the function's return value does not include. */
|
|
||||||
if (!update_call_from_tree (gsi, cst))
|
if (!update_call_from_tree (gsi, cst))
|
||||||
gimplify_and_update_call_from_tree (gsi, cst);
|
gimplify_and_update_call_from_tree (gsi, cst);
|
||||||
gimple *callstmt = gsi_stmt (*gsi);
|
gimple *callstmt = gsi_stmt (*gsi);
|
||||||
update_stmt (callstmt);
|
update_stmt (callstmt);
|
||||||
}
|
}
|
||||||
else
|
else if (lhs)
|
||||||
{
|
{
|
||||||
/* Replace the left-hand side of the call with the constant
|
/* Replace the left-hand side of the call with the constant
|
||||||
result of the formatted function minus 1 for the terminating
|
result of the formatted function. */
|
||||||
NUL which the function's return value does not include. */
|
|
||||||
gimple_call_set_lhs (info.callstmt, NULL_TREE);
|
gimple_call_set_lhs (info.callstmt, NULL_TREE);
|
||||||
gimple *g = gimple_build_assign (lhs, cst);
|
gimple *g = gimple_build_assign (lhs, cst);
|
||||||
gsi_insert_after (gsi, g, GSI_NEW_STMT);
|
gsi_insert_after (gsi, g, GSI_NEW_STMT);
|
||||||
@ -2921,88 +2924,56 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
|
|||||||
|
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
{
|
{
|
||||||
location_t callloc = gimple_location (info.callstmt);
|
if (removed)
|
||||||
fprintf (dump_file, "On line %i substituting ",
|
fprintf (dump_file, " Removing call statement.");
|
||||||
LOCATION_LINE (callloc));
|
else
|
||||||
print_generic_expr (dump_file, cst, dump_flags);
|
{
|
||||||
fprintf (dump_file, " for ");
|
fprintf (dump_file, " Substituting ");
|
||||||
print_generic_expr (dump_file, info.func, dump_flags);
|
print_generic_expr (dump_file, cst, dump_flags);
|
||||||
fprintf (dump_file, " %s (output %s).\n",
|
fprintf (dump_file, " for %s.\n",
|
||||||
info.nowrite ? "call" : "return value",
|
info.nowrite ? "statement" : "return value");
|
||||||
res.constant ? "constant" : "variable");
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lhs == NULL_TREE
|
else if (lhs)
|
||||||
&& info.nowrite
|
|
||||||
&& !stmt_ends_bb_p (info.callstmt))
|
|
||||||
{
|
{
|
||||||
/* Remove the call to the bounded function with a zero size
|
bool setrange = false;
|
||||||
(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;
|
|
||||||
|
|
||||||
if (lhs
|
if ((info.bounded || maxretval < info.objsize)
|
||||||
&& res.bounded
|
&& res.under4k
|
||||||
&& ((maxbytes = res.number_chars - 1) <= target_int_max ()
|
&& (minretval < target_int_max ()
|
||||||
|| (res.number_chars_min - 1 <= target_int_max ()
|
&& maxretval < target_int_max ()))
|
||||||
&& (maxbytes = res.number_chars_max - 1) <= target_int_max ()))
|
|
||||||
&& (info.bounded || maxbytes < info.objsize))
|
|
||||||
{
|
{
|
||||||
/* If the result is in a valid range bounded by the size of
|
/* 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
|
the destination set it so that it can be used for subsequent
|
||||||
optimizations. */
|
optimizations. */
|
||||||
int prec = TYPE_PRECISION (integer_type_node);
|
int prec = TYPE_PRECISION (integer_type_node);
|
||||||
|
|
||||||
if (res.number_chars < target_int_max () && res.under4k)
|
wide_int min = wi::shwi (minretval, prec);
|
||||||
{
|
wide_int max = wi::shwi (maxretval, prec);
|
||||||
wide_int num = wi::shwi (res.number_chars - 1, prec);
|
set_range_info (lhs, VR_RANGE, min, max);
|
||||||
set_range_info (lhs, VR_RANGE, num, num);
|
|
||||||
}
|
setrange = true;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
{
|
{
|
||||||
const char *inbounds
|
const char *inbounds
|
||||||
= (res.number_chars_min <= info.objsize
|
= (minretval < info.objsize
|
||||||
? (res.number_chars_max <= info.objsize
|
? (maxretval < info.objsize
|
||||||
? "in" : "potentially out-of")
|
? "in" : "potentially out-of")
|
||||||
: "out-of");
|
: "out-of");
|
||||||
|
|
||||||
location_t callloc = gimple_location (info.callstmt);
|
const char *what = setrange ? "Setting" : "Discarding";
|
||||||
fprintf (dump_file, "On line %i ", LOCATION_LINE (callloc));
|
if (minretval != maxretval)
|
||||||
print_generic_expr (dump_file, info.func, dump_flags);
|
|
||||||
|
|
||||||
const char *ign = lhs ? "" : " ignored";
|
|
||||||
if (res.number_chars >= HOST_WIDE_INT_MAX)
|
|
||||||
fprintf (dump_file,
|
fprintf (dump_file,
|
||||||
" %s-bounds return value in range [%lu, %lu]%s.\n",
|
" %s %s-bounds return value range [%llu, %llu].\n",
|
||||||
inbounds,
|
what, inbounds,
|
||||||
(unsigned long)res.number_chars_min - 1,
|
(unsigned long long)minretval,
|
||||||
(unsigned long)res.number_chars_max - 1, ign);
|
(unsigned long long)maxretval);
|
||||||
else
|
else
|
||||||
fprintf (dump_file, " %s-bounds return value %lu%s.\n",
|
fprintf (dump_file, " %s %s-bounds return value %llu.\n",
|
||||||
inbounds, (unsigned long)res.number_chars - 1, ign);
|
what, inbounds, (unsigned long long)minretval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user