re PR tree-optimization/79800 (wrong snprintf result range with precision in a narrow negative-positive range)

PR tree-optimization/79800
	* gimple-ssa-sprintf.c (format_floating: Add argument.  Handle
	precision in negative-positive range.
	(format_floating): Call non-const overload with adjusted precision.

	PR tree-optimization/79800
	* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
	* gcc.dg/tree-ssa/pr79800.c: New test.

From-SVN: r246151
This commit is contained in:
Martin Sebor 2017-03-15 04:31:27 +00:00 committed by Jeff Law
parent 76ca5f401d
commit a983542b60
5 changed files with 85 additions and 31 deletions

View File

@ -1,3 +1,10 @@
2017-03-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/79800
* gimple-ssa-sprintf.c (format_floating: Add argument. Handle
precision in negative-positive range.
(format_floating): Call non-const overload with adjusted precision.
2017-03-14 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/79947
@ -10,7 +17,6 @@
* builtin-attrs.def (ATTR_ALLOC_SIZE_2_NOTHROW_LIST): New macro.
* builtins.def (aligned_alloc): Use it.
PR c/79936
* Makefile.in (GTFILES): Add calls.c.
* calls.c: Include "gt-calls.h".

View File

@ -1499,11 +1499,13 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
}
/* Return a range representing the minimum and maximum number of bytes
that the directive DIR will output for any argument. This function
is used when the directive argument or its value isn't known. */
that the directive DIR will output for any argument. PREC gives
the adjusted precision range to account for negative precisions
meaning the default 6. This function is used when the directive
argument or its value isn't known. */
static fmtresult
format_floating (const directive &dir)
format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
{
tree type;
@ -1532,8 +1534,8 @@ format_floating (const directive &dir)
/* The minimum output as determined by flags. It's always at least 1.
When plus or space are set the output is preceded by either a sign
or a space. */
int flagmin = (1 /* for the first digit */
+ (dir.get_flag ('+') | dir.get_flag (' ')));
unsigned flagmin = (1 /* for the first digit */
+ (dir.get_flag ('+') | dir.get_flag (' ')));
/* When the pound flag is set the decimal point is included in output
regardless of precision. Whether or not a decimal point is included
@ -1557,14 +1559,13 @@ format_floating (const directive &dir)
+ minprec
+ 3 /* p+0 */);
res.range.max = format_floating_max (type, 'a', dir.prec[1]);
res.range.max = format_floating_max (type, 'a', prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
res.range.unlikely = res.range.max;
if (dir.prec[0] != dir.prec[1]
|| dir.prec[0] == -1 || dir.prec[0] > 0)
if (dir.prec[1] > 0)
res.range.unlikely += target_mb_len_max () - 1;
break;
@ -1573,23 +1574,18 @@ format_floating (const directive &dir)
case 'E':
case 'e':
{
/* Minimum output attributable to precision and, when it's
non-zero, decimal point. */
HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
/* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0)
minprec = 0;
else if (dir.prec[0] > 0)
minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = (flagmin
+ radix
+ minprec
+ 2 /* e+ */ + 2);
/* MPFR uses a precision of 16 by default for some reason.
Set it to the C default of 6. */
int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1];
res.range.max = format_floating_max (type, 'e', maxprec);
res.range.max = format_floating_max (type, 'e', prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
@ -1605,21 +1601,19 @@ format_floating (const directive &dir)
case 'F':
case 'f':
{
/* Minimum output attributable to precision and, when it's non-zero,
decimal point. */
HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
/* The lower bound when precision isn't specified is 8 bytes
("1.23456" since precision is taken to be 6). When precision
is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */
HOST_WIDE_INT minprec = 0;
if (dir.prec[0] < 0)
minprec = dir.prec[1] < 0 ? 6 + !radix /* decimal point */ : 0;
else if (dir.prec[0])
minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = flagmin + radix + minprec;
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'f', dir.prec[1]);
res.range.max = format_floating_max (type, 'f', prec[1]);
/* The minimum output with unknown precision is a single byte
(e.g., "0") but the more likely output is 3 bytes ("0.0"). */
@ -1659,6 +1653,8 @@ format_floating (const directive &dir)
else if (maxprec < 0)
maxprec = 5;
}
else
maxprec = prec[1];
res.range.max = format_floating_max (type, spec, maxprec);
@ -1702,9 +1698,6 @@ format_floating (const directive &dir)
static fmtresult
format_floating (const directive &dir, tree arg)
{
if (!arg || TREE_CODE (arg) != REAL_CST)
return format_floating (dir);
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
/* For an indeterminate precision the lower bound must be assumed
@ -1767,6 +1760,9 @@ format_floating (const directive &dir, tree arg)
}
}
if (!arg || TREE_CODE (arg) != REAL_CST)
return format_floating (dir, prec);
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res;

View File

@ -1,3 +1,9 @@
2017-03-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/79800
* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
* gcc.dg/tree-ssa/pr79800.c: New test.
2017-03-14 Michael Meissner <meissner@linux.vnet.ibm.com>
PR target/79947

View File

@ -113,9 +113,16 @@ void test_unknown_precision_integer (int p, int i, double d)
void test_unknown_precision_floating (int p, double d)
{
T ( 0, "%.*a", R (-1, 0), d); /* { dg-warning "between 6 and 24 " } */
T ( 6, "%.*a", R (-1, 0), d); /* { dg-warning "writing a terminating nul" } */
T ( 7, "%.*a", R (-1, 0), d);
T ( 7, "%.*a", p, d);
T (21, "%.*a", p, 3.141);
T ( 0, "%.*e", R (-1, 0), d); /* { dg-warning "between 5 and 14 " } */
T ( 0, "%.*e", R (-1, 6), d); /* { dg-warning "between 5 and 14 " } */
T ( 5, "%.*e", R (-1, 6), d); /* { dg-warning "writing a terminating nul" } */
T ( 6, "%.*e", R (-1, 6), d);
/* "%.0e", 0.0 results in 5 bytes: "0e+00" */
T ( 5, "%.*e", p, d); /* { dg-warning "writing a terminating nul" } */
/* "%#.0e", 0.0 results in 6 bytes: "0.e+00" */
@ -125,6 +132,10 @@ void test_unknown_precision_floating (int p, double d)
T ( 6, "%#.*e", p, 3.141); /* { dg-warning "writing a terminating nul" } */
T ( 7, "%#.*e", p, 3.141);
T ( 0, "%.*f", R (-1, 0), d); /* { dg-warning "between 1 and 317 " } */
T ( 0, "%.*f", R (-1, 6), d); /* { dg-warning "between 1 and 317 " } */
T ( 3, "%.*f", R (-1, 6), d); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%.*f", R (-1, 6), d);
/* "%.0f", 0.0 results in 1 byte: "0" but precision of at least 1
is likely, resulting in "0.0". */
T ( 3, "%.*f", p, d); /* { dg-warning "may write a terminating nul" } */
@ -138,12 +149,16 @@ void test_unknown_precision_floating (int p, double d)
T ( 3, "%#.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%#.*f", p, 3.141);
T ( 0, "%.*g", R (-1, 0), d); /* { dg-warning "between 1 and 13 " } */
T (12, "%.*g", R (-1, 0), d); /* { dg-warning "may write a terminating nul" } */
T (13, "%.*g", R (-1, 0), d);
T (12, "%.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (12, "%#.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (13, "%.*g", p, d);
T (13, "%#.*g", p, d);
T ( 6, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R (-1, 0), d);
T (12, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */
T (12, "%#.*g", R (-1, 6), d);/* { dg-warning "may write a terminating nul" } */
T (13, "%#.*g", R (-1, 0), d);
T ( 6, "%#.*g", R ( 0, 0), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R ( 0, 0), d);
T ( 6, "%#.*g", R ( 0, 1), d);/* { dg-warning "may write a terminating nul" } */

View File

@ -0,0 +1,31 @@
/* PR 79800 - wrong snprintf result range with precision in a narrow
negative-positive range
{ dg-do "run" }
{ dg-options "-O2 -Wall" } */
#define FMT "%.*a"
char fmt[] = FMT;
volatile double x = 1.23456789;
void f (int p)
{
if (p < -1 || 0 < p)
p = -1;
char d[30];
int n1 = __builtin_sprintf (d, "%.*a", p, x);
const char *s = n1 < 20 ? "< 20" : ">= 20";
if (__builtin_strcmp (s, ">= 20"))
__builtin_abort ();
}
volatile int i = -1;
int main ()
{
f (i);
return 0;
}