PR tree-optimization/78696 - [7 Regression] -fprintf-return-value misoptimizes %.Ng where N is greater than 10

gcc/ChangeLog:

	PR tree-optimization/78696
	* gimple-ssa-sprintf.c (format_floating): Correct handling of
	precision.  Use MPFR for %f for greater fidelity.  Correct handling
	of %g.
	(pass_sprintf_length::compute_format_length): Set width and precision
	specified by asrerisk to void_node for vararg functions.
	(try_substitute_return_value): Adjust dump output.

gcc/testsuite/ChangeLog:

	PR tree-optimization/78696
	* gcc.dg/tree-ssa/builtin-sprintf-5.c: Remove incorrect test cases.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Correct off-by-1 errors.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.

From-SVN: r244037
This commit is contained in:
Martin Sebor 2017-01-03 16:14:44 -07:00
parent 0f2a6e84c6
commit 5b4f088d0d
7 changed files with 614 additions and 121 deletions

View file

@ -1,3 +1,13 @@
2017-01-03 Martin Sebor <msebor@redhat.com>
PR tree-optimization/78696
* gimple-ssa-sprintf.c (format_floating): Correct handling of
precision. Use MPFR for %f for greater fidelity. Correct handling
of %g.
(pass_sprintf_length::compute_format_length): Set width and precision
specified by asrerisk to void_node for vararg functions.
(try_substitute_return_value): Adjust dump output.
2017-01-03 David Edelsohn <dje.gcc@gmail.com>
* doc/invoke.texi (RS6000 options): LRA is enabled by default.

View file

@ -483,9 +483,11 @@ struct conversion_spec
/* Numeric precision as in "%.32s". */
int precision;
/* Width specified via the '*' character. */
/* Width specified via the '*' character. Need not be INTEGER_CST.
For vararg functions set to void_node. */
tree star_width;
/* Precision specified via the asterisk. */
/* Precision specified via the asterisk. Need not be INTEGER_CST.
For vararg functions set to void_node. */
tree star_precision;
/* Length modifier. */
@ -1246,15 +1248,26 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
fmtstr[len + 5] = spec;
fmtstr[len + 6] = '\0';
/* Avoid passing negative precisions with larger magnitude to MPFR
to avoid exposing its bugs. (A negative precision is supposed
to be ignored.) */
if (prec < 0)
prec = -1;
spec = TOUPPER (spec);
if (spec == 'E' || spec == 'F')
{
/* For %e, specify the precision explicitly since mpfr_sprintf
does its own thing just to be different (see MPFR bug 21088). */
if (prec < 0)
prec = 6;
}
else
{
/* Avoid passing negative precisions with larger magnitude to MPFR
to avoid exposing its bugs. (A negative precision is supposed
to be ignored.) */
if (prec < 0)
prec = -1;
}
HOST_WIDE_INT p = prec;
if (TOUPPER (spec) == 'G')
if (spec == 'G')
{
/* For G/g, precision gives the maximum number of significant
digits which is bounded by LDBL_MAX_10_EXP, or, for a 128
@ -1287,7 +1300,8 @@ get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
}
/* Return the number of bytes to format using the format specifier
SPEC the largest value in the real floating TYPE. */
SPEC and the precision PREC the largest value in the real floating
TYPE. */
static unsigned HOST_WIDE_INT
format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
@ -1329,7 +1343,6 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
HOST_WIDE_INT prec)
{
tree type;
bool ldbl = false;
switch (spec.modifier)
{
@ -1340,12 +1353,10 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
case FMT_LEN_L:
type = long_double_type_node;
ldbl = true;
break;
case FMT_LEN_ll:
type = long_double_type_node;
ldbl = true;
break;
default:
@ -1355,95 +1366,94 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res;
/* Log10 of of the maximum number of exponent digits for the type. */
int logexpdigs = 2;
/* The result is always bounded (though the range may be all of int). */
res.bounded = true;
if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
/* The minimum output as determined by flags. It's always at least 1. */
int flagmin = (1 /* for the first digit */
+ (spec.get_flag ('+') | spec.get_flag (' '))
+ (prec == 0 && spec.get_flag ('#')));
if (width == INT_MIN || prec == INT_MIN)
{
/* The base in which the exponent is represented should always
be 2 in GCC. */
const double log10_2 = .30102999566398119521;
/* Compute T_MAX_EXP for base 2. */
int expdigs = REAL_MODE_FORMAT (TYPE_MODE (type))->emax * log10_2;
logexpdigs = ilog (expdigs, 10);
/* When either width or precision is specified but unknown
the upper bound is the maximum. Otherwise it will be
computed for each directive below. */
res.range.max = HOST_WIDE_INT_MAX;
}
else
res.range.max = HOST_WIDE_INT_M1U;
switch (spec.specifier)
{
case 'A':
case 'a':
{
/* The minimum output is "0x.p+0". */
res.range.min = 6 + (prec > 0 ? prec : 0);
res.range.max = (width == INT_MIN
? HOST_WIDE_INT_MAX
: format_floating_max (type, 'a', prec));
res.range.min = flagmin + 5 + (prec > 0 ? prec + 1 : 0);
if (res.range.max == HOST_WIDE_INT_M1U)
{
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'a', prec);
}
/* The output of "%a" is fully specified only when precision
is explicitly specified and width isn't unknown. */
res.bounded = INT_MIN != width && -1 < prec;
break;
}
case 'E':
case 'e':
{
bool sign = spec.get_flag ('+') || spec.get_flag (' ');
/* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
res.range.min = (sign
+ 1 /* unit */ + (prec < 0 ? 7 : prec ? prec + 1 : 0)
res.range.min = (flagmin
+ (prec == INT_MIN
? 0 : prec < 0 ? 7 : prec ? prec + 1 : 0)
+ 2 /* e+ */ + 2);
/* Unless width is uknown the maximum output is the minimum plus
sign (unless already included), plus the difference between
the minimum exponent of 2 and the maximum exponent for the type. */
res.range.max = (width == INT_MIN
? HOST_WIDE_INT_M1U
: res.range.min + !sign + logexpdigs - 2);
/* "%e" is fully specified and the range of bytes is bounded
unless width is unknown. */
res.bounded = INT_MIN != width;
if (res.range.max == HOST_WIDE_INT_M1U)
{
/* MPFR uses a precision of 16 by default for some reason.
Set it to the C default of 6. */
res.range.max = format_floating_max (type, 'e',
-1 == prec ? 6 : prec);
}
break;
}
case 'F':
case 'f':
{
/* The minimum output is "1.234567" regardless of the value
of the actual argument. */
res.range.min = 2 + (prec < 0 ? 6 : prec);
/* 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). */
res.range.min = (flagmin
+ (prec != INT_MIN) /* for decimal point */
+ (prec == INT_MIN
? 0 : prec < 0 ? 6 : prec ? prec : -1));
/* Compute the maximum just once. */
const HOST_WIDE_INT f_max[] = {
format_floating_max (double_type_node, 'f', prec),
format_floating_max (long_double_type_node, 'f', prec)
};
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl];
/* "%f" is fully specified and the range of bytes is bounded
unless width is unknown. */
res.bounded = INT_MIN != width;
if (res.range.max == HOST_WIDE_INT_M1U)
{
/* Compute the upper bound for -TYPE_MAX. */
res.range.max = format_floating_max (type, 'f', prec);
}
break;
}
case 'G':
case 'g':
{
/* The minimum is the same as for '%F'. */
res.range.min = 1;
/* Compute the maximum just once. */
const HOST_WIDE_INT g_max[] = {
format_floating_max (double_type_node, 'g', prec),
format_floating_max (long_double_type_node, 'g', prec)
};
res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl];
/* "%g" is fully specified and the range of bytes is bounded
unless width is unknown. */
res.bounded = INT_MIN != width;
/* The %g output depends on precision and the exponent of
the argument. Since the value of the argument isn't known
the lower bound on the range of bytes (not counting flags
or width) is 1. */
res.range.min = flagmin;
if (res.range.max == HOST_WIDE_INT_M1U)
{
/* Compute the upper bound for -TYPE_MAX which should be
the lesser of %e and %f. */
res.range.max = format_floating_max (type, 'g', prec);
}
break;
}
@ -1453,6 +1463,7 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
if (width > 0)
{
/* If width has been specified use it to adjust the range. */
if (res.range.min < (unsigned)width)
res.range.min = width;
if (res.range.max < (unsigned)width)
@ -1469,9 +1480,9 @@ format_floating (const conversion_spec &spec, HOST_WIDE_INT width,
static fmtresult
format_floating (const conversion_spec &spec, tree arg)
{
/* Set WIDTH to -1 when it's not specified, to INT_MIN when it is
specified by the asterisk to an unknown value, and otherwise to
a non-negative value corresponding to the specified width. */
/* Set WIDTH to -1 when it's not specified, to HOST_WIDE_INT_MIN when
it is specified by the asterisk to an unknown value, and otherwise
to a non-negative value corresponding to the specified width. */
HOST_WIDE_INT width = -1;
HOST_WIDE_INT prec = -1;
@ -1498,13 +1509,13 @@ format_floating (const conversion_spec &spec, tree arg)
else if (spec.star_precision)
{
if (TREE_CODE (spec.star_precision) == INTEGER_CST)
prec = tree_to_shwi (spec.star_precision);
else
{
/* FIXME: Handle non-constant precision. */
res.range.min = res.range.max = HOST_WIDE_INT_M1U;
return res;
prec = tree_to_shwi (spec.star_precision);
if (prec < 0)
prec = -1;
}
else
prec = INT_MIN;
}
else if (res.constant && TOUPPER (spec.specifier) != 'A')
{
@ -1515,11 +1526,6 @@ format_floating (const conversion_spec &spec, tree arg)
if (res.constant)
{
/* Set up an array to easily iterate over. */
unsigned HOST_WIDE_INT* const minmax[] = {
&res.range.min, &res.range.max
};
/* Get the real type format desription for the target. */
const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)));
@ -1541,26 +1547,42 @@ format_floating (const conversion_spec &spec, tree arg)
*pfmt = '\0';
for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i)
{
/* Set up an array to easily iterate over below. */
unsigned HOST_WIDE_INT* const minmax[] = {
&res.range.min, &res.range.max
};
for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i)
{
/* Use the MPFR rounding specifier to round down in the first
iteration and then up. In most but not all cases this will
result in the same number of bytes. */
char rndspec = "DU"[i];
/* Format it and store the result in the corresponding member
of the result struct. */
unsigned HOST_WIDE_INT len
= get_mpfr_format_length (mpfrval, fmtstr, prec,
spec.specifier, rndspec);
if (0 < width && len < (unsigned)width)
len = width;
*minmax[i] = len;
}
}
/* Make sure the minimum is less than the maximum (MPFR rounding
in the call to mpfr_snprintf can result in the reverse. */
if (res.range.max < res.range.min)
{
/* Use the MPFR rounding specifier to round down in the first
iteration and then up. In most but not all cases this will
result in the same number of bytes. */
char rndspec = "DU"[i];
/* Format it and store the result in the corresponding member
of the result struct. */
unsigned HOST_WIDE_INT len
= get_mpfr_format_length (mpfrval, fmtstr, prec,
spec.specifier, rndspec);
if (0 < width && len < (unsigned)width)
len = width;
*minmax[i] = len;
unsigned HOST_WIDE_INT tmp = res.range.min;
res.range.min = res.range.max;
res.range.max = tmp;
}
/* The range of output is known even if the result isn't bounded. */
if (width == INT_MIN)
if (width == HOST_WIDE_INT_MIN)
{
res.knownrange = false;
res.range.max = HOST_WIDE_INT_MAX;
@ -2420,10 +2442,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
{
/* Similarly to the block above, this could be either a POSIX
positional argument or a width, depending on what follows. */
if (gimple_call_num_args (info.callstmt) <= argno)
return false;
spec.star_width = gimple_call_arg (info.callstmt, argno++);
if (argno < gimple_call_num_args (info.callstmt))
spec.star_width = gimple_call_arg (info.callstmt, argno++);
else
spec.star_width = void_node;
++pf;
}
@ -2499,7 +2521,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
}
else if ('*' == *pf)
{
spec.star_width = gimple_call_arg (info.callstmt, argno++);
if (argno < gimple_call_num_args (info.callstmt))
spec.star_width = gimple_call_arg (info.callstmt, argno++);
else
spec.star_width = void_node;
++pf;
}
else if ('\'' == *pf)
@ -2527,7 +2552,10 @@ pass_sprintf_length::compute_format_length (call_info &info,
}
else if ('*' == *pf)
{
spec.star_precision = gimple_call_arg (info.callstmt, argno++);
if (argno < gimple_call_num_args (info.callstmt))
spec.star_precision = gimple_call_arg (info.callstmt, argno++);
else
spec.star_precision = void_node;
++pf;
}
else
@ -2795,11 +2823,11 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
fprintf (dump_file,
" %s-bounds return value in range [%lu, %lu]%s.\n",
inbounds,
(unsigned long)res.number_chars_min,
(unsigned long)res.number_chars_max, ign);
(unsigned long)res.number_chars_min - 1,
(unsigned long)res.number_chars_max - 1, ign);
else
fprintf (dump_file, " %s-bounds return value %lu%s.\n",
inbounds, (unsigned long)res.number_chars, ign);
inbounds, (unsigned long)res.number_chars - 1, ign);
}
}
}

View file

@ -1,3 +1,43 @@
2017-01-03 Martin Sebor <msebor@redhat.com>
PR tree-optimization/78696
* gcc.dg/tree-ssa/builtin-sprintf-5.c: Remove incorrect test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Correct off-by-1 errors.
* gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
2017-01-03 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/dg-test-1.c: Add tests of relative line specifications
with more than one digit.
* lib/gcc-dg.exp (process-message): Support more than one digit
in relative line specifications.
2017-01-03 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/78965
* gcc.dg/pr78965.c: New test.
PR middle-end/78901
* g++.dg/opt/pr78901.C: New test.
2017-01-03 Janne Blomqvist <jb@gcc.gnu.org>
PR fortran/78534
PR fortran/66310
* gfortran.dg/dependency_49.f90: Change scan-tree-dump-times
due to gfc_trans_string_copy change to avoid -Wstringop-overflow.
* gfortran.dg/repeat_4.f90: Use integers of kind C_SIZE_T.
* gfortran.dg/repeat_7.f90: New test for PR 66310.
* gfortran.dg/scan_2.f90: Handle potential cast in assignment.
* gfortran.dg/string_1.f90: Limit to ilp32 targets.
* gfortran.dg/string_1_lp64.f90: New test.
* gfortran.dg/string_3.f90: Limit to ilp32 targets.
* gfortran.dg/string_3_lp64.f90: New test.
* gfortran.dg/transfer_intrinsic_1.f90: Change
scan-tree-dump-times due to gfc_trans_string_copy change to
avoid -Wstringop-overflow.
2017-01-02 Uros Bizjak <ubizjak@gmail.com>
PR target/78967

View file

@ -21,6 +21,7 @@
FAIL (__LINE__)(value); \
} while (0)
/* Verify that EXPECT == snprintf(0, 0, ...). */
#define EQL(expect, ...) \
do { \
int n = __builtin_snprintf (0, 0, __VA_ARGS__); \
@ -140,6 +141,7 @@ void test_arg_multiarg (int i, double d)
EQL (16, "%*i %s", 12, i, "abc");
}
/* Verify that EXPECT == vsnprintf(0, 0, ...). */
#define EQLv(expect, fmt, va) \
do { \
int n = __builtin_vsnprintf (0, 0, fmt, va); \
@ -149,9 +151,7 @@ void test_arg_multiarg (int i, double d)
void test_va_int (__builtin_va_list va)
{
EQLv ( 2, "%02hhx", va);
EQLv ( 2, "%02.*hhx", va);
EQLv ( 4, "%04hx", va);
EQLv ( 4, "%04.*hx", va);
}
void test_va_multiarg (__builtin_va_list va)

View file

@ -63,11 +63,14 @@ void test_floating_a_var (double x)
T (0, "%.*a", INT_MIN, x); /* { dg-warning "writing between 6 and 24 bytes" } */
T (0, "%.*a", INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */
/* Expected output is "0x0." followed by INT_MAX digits followed by
"p+" followed by 1 to four digits, with a byte count in the range
[3 + INT_MAX + 2 + 1, 3 + INT_MAX + 2 + 4]. */
T (0, "%.*a", INT_MAX, x); /* { dg-warning "writing between 2147483654 and 2147483658 bytes" } */
T (0, "%*.*a", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*.*a", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */
T (0, "%*.*a", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483654 and 2147483658 bytes" } */
}
void test_floating_e_cst (void)
@ -75,7 +78,7 @@ void test_floating_e_cst (void)
T (0, "%*e", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*e", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*e", INT_MIN, 0.); /* { dg-warning "writing 5 bytes" } */
T (0, "%.*e", INT_MIN, 0.); /* { dg-warning "writing 12 bytes" } */
T (0, "%.*e", INT_MAX, 0.); /* { dg-warning "writing 2147483653 bytes" } */
@ -103,7 +106,7 @@ void test_floating_f_cst (void)
T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */
T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */
T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 1 byte" } */
T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 8 byte" } */
T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */

View file

@ -0,0 +1,276 @@
/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
Test to verify the correctness of ranges of output computed for floating
point directives.
{ dg-do compile }
{ dg-options "-O2 -Wformat -Wformat-length -ftrack-macro-expansion=0" } */
typedef __builtin_va_list va_list;
char dst[1];
extern void sink (int, void*);
/* Macro to test either width or precision specified by the asterisk
(but not both). */
#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
/* Macro to test both width and precision specified by the asterisk. */
#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
/* Macro to test vsprintf with both width and precision specified by
the asterisk. */
#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
/* Exercise %a. */
void test_a (int w, int p, double x)
{
T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
T1 ("%*.a", w); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%*.0a", w); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%*.1a", w); /* { dg-warning "writing 8 or more bytes" } */
T1 ("%*.2a", w); /* { dg-warning "writing 9 or more bytes" } */
T1 ("%.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%1.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%2.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T1 ("%3.*a", p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */
}
/* Exercise %e. */
void test_e (int w, int p, double x)
{
T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
T1 ("%*.e", w); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%*.0e", w); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%*.1e", w); /* { dg-warning "writing 7 or more bytes" } */
T1 ("%*.2e", w); /* { dg-warning "writing 8 or more bytes" } */
T1 ("%.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%1.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%2.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T1 ("%3.*e", p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */
}
/* Exercise %f. */
void test_f (int w, int p, double x)
{
T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
T1 ("%*.f", w); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%*.0f", w); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%*.1f", w); /* { dg-warning "writing 3 or more bytes" } */
T1 ("%*.2f", w); /* { dg-warning "writing 4 or more bytes" } */
T1 ("%.*f", p); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%1.*f", p); /* { dg-warning "writing 1 or more bytes" } */
T1 ("%2.*f", p); /* { dg-warning "writing 2 or more bytes" } */
T1 ("%3.*f", p); /* { dg-warning "writing 3 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g (double x)
{
T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
}
/* Exercise %a. */
void test_a_va (va_list va)
{
T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
T ("%*.a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%*.0a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%*.1a"); /* { dg-warning "writing 8 or more bytes" } */
T ("%*.2a"); /* { dg-warning "writing 9 or more bytes" } */
T ("%.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%1.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%2.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%6.*a"); /* { dg-warning "writing 6 or more bytes" } */
T ("%9.*a"); /* { dg-warning "writing 9 or more bytes" } */
T ("%*.*a"); /* { dg-warning "writing 6 or more bytes" } */
}
/* Exercise %e. */
void test_e_va (va_list va)
{
T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
T ("%.*e"); /* { dg-warning "writing 5 or more bytes" } */
T ("%1.*e"); /* { dg-warning "writing 5 or more bytes" } */
T ("%6.*e"); /* { dg-warning "writing 6 or more bytes" } */
T ("%9.*e"); /* { dg-warning "writing 9 or more bytes" } */
T ("%*.*e"); /* { dg-warning "writing 5 or more bytes" } */
}
/* Exercise %f. */
void test_f_va (va_list va)
{
T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
T ("%.*f"); /* { dg-warning "writing 1 or more bytes" } */
T ("%1.*f"); /* { dg-warning "writing 1 or more bytes" } */
T ("%3.*f"); /* { dg-warning "writing 3 or more bytes" } */
T ("%*.*f"); /* { dg-warning "writing 1 or more bytes" } */
}
/* Exercise %g. The expected output is the lesser of %e and %f. */
void test_g_va (va_list va)
{
T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */
T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */
T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
T ("%.*g"); /* { dg-warning "writing 1 or more bytes" } */
T ("%1.*g"); /* { dg-warning "writing 1 or more bytes" } */
T ("%4.*g"); /* { dg-warning "writing 4 or more bytes" } */
T ("%*.*g"); /* { dg-warning "writing 1 or more bytes" } */
}

View file

@ -56,9 +56,12 @@ checkv (const char *func, int line, int res, int min, int max,
fail = 1;
}
else
else if (min == max)
__builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n",
func, line, fmt, n, dst);
else
__builtin_printf ("PASS: %s:%i: \"%s\" result %i in [%i, %i]: \"%s\"\n",
func, line, fmt, n, min, max, dst);
}
if (fail)
@ -75,7 +78,7 @@ check (const char *func, int line, int res, int min, int max,
__builtin_va_end (va);
}
char buffer[256];
char buffer[4100];
char* volatile dst = buffer;
char* ptr = buffer;
@ -415,6 +418,8 @@ test_a_double (double d)
EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */
EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */
EQL (11, 12, "%.*a", 4, 6.0); /* 0xc.0000p-1 */
EQL (12, 13, "%.*a", 5, 7.0); /* 0xe.00000p-1 */
/* d is in [ 0, -DBL_MAX ] */
RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */
RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */
@ -432,7 +437,7 @@ test_a_long_double (void)
}
static void __attribute__ ((noinline, noclone))
test_e_double (void)
test_e_double (double d)
{
EQL (12, 13, "%e", 1.0e0);
EQL (13, 14, "%e", -1.0e0);
@ -454,10 +459,34 @@ test_e_double (void)
EQL (12, 13, "%e", 1.0e-1);
EQL (12, 13, "%e", 1.0e-12);
EQL (13, 14, "%e", 1.0e-123);
RNG (12, 14, 15, "%e", d);
RNG ( 5, 7, 8, "%.e", d);
RNG ( 5, 7, 8, "%.0e", d);
RNG ( 7, 9, 10, "%.1e", d);
RNG ( 8, 10, 11, "%.2e", d);
RNG ( 9, 11, 12, "%.3e", d);
RNG (10, 12, 13, "%.4e", d);
RNG (11, 13, 14, "%.5e", d);
RNG (12, 14, 15, "%.6e", d);
RNG (13, 15, 16, "%.7e", d);
RNG (4006, 4008, 4009, "%.4000e", d);
RNG ( 5, 7, 8, "%.*e", 0, d);
RNG ( 7, 9, 10, "%.*e", 1, d);
RNG ( 8, 10, 11, "%.*e", 2, d);
RNG ( 9, 11, 12, "%.*e", 3, d);
RNG (10, 12, 13, "%.*e", 4, d);
RNG (11, 13, 14, "%.*e", 5, d);
RNG (12, 14, 15, "%.*e", 6, d);
RNG (13, 15, 16, "%.*e", 7, d);
RNG (4006, 4008, 4009, "%.*e", 4000, d);
}
static void __attribute__ ((noinline, noclone))
test_e_long_double (void)
test_e_long_double (long double d)
{
EQL (12, 13, "%Le", 1.0e0L);
EQL (13, 14, "%Le", -1.0e0L);
@ -490,10 +519,32 @@ test_e_long_double (void)
EQL ( 8, 9, "%.1Le", 1.0e-111L);
EQL (19, 20, "%.12Le", 1.0e-112L);
EQL (20, 21, "%.13Le", 1.0e-113L);
/* The following correspond to the double results plus 1 for the upper
bound accounting for the four-digit exponent. */
RNG (12, 15, 16, "%Le", d); /* 0.000000e+00 ... -1.189732e+4932 */
RNG ( 5, 8, 9, "%.Le", d);
RNG ( 5, 9, 10, "%.0Le", d);
RNG ( 7, 10, 11, "%.1Le", d); /* 0.0e+00 ... -1.2e+4932 */
RNG ( 8, 11, 12, "%.2Le", d); /* 0.00e+00 ... -1.19e+4932 */
RNG ( 9, 12, 13, "%.3Le", d);
RNG (10, 13, 14, "%.4Le", d);
RNG (11, 14, 15, "%.5Le", d);
RNG (12, 15, 16, "%.6Le", d); /* same as plain "%Le" */
RNG (13, 16, 17, "%.7Le", d); /* 0.0000000e+00 ... -1.1897315e+4932 */
RNG ( 5, 9, 10, "%.*Le", 0, d);
RNG ( 7, 10, 11, "%.*Le", 1, d);
RNG ( 8, 11, 12, "%.*Le", 2, d);
RNG ( 9, 12, 13, "%.*Le", 3, d);
RNG (10, 13, 14, "%.*Le", 4, d);
RNG (11, 14, 15, "%.*Le", 5, d);
RNG (12, 15, 16, "%.*Le", 6, d);
RNG (13, 16, 17, "%.*Le", 7, d);
}
static void __attribute__ ((noinline, noclone))
test_f_double (void)
test_f_double (double d)
{
EQL ( 8, 9, "%f", 0.0e0);
EQL ( 8, 9, "%f", 0.1e0);
@ -511,6 +562,8 @@ test_f_double (void)
EQL ( 8, 9, "%f", 1.0e-1);
EQL ( 8, 9, "%f", 1.0e-12);
EQL ( 8, 9, "%f", 1.0e-123);
RNG ( 8, 317, 318, "%f", d);
}
static void __attribute__ ((noinline, noclone))
@ -534,6 +587,87 @@ test_f_long_double (void)
EQL ( 8, 9, "%Lf", 1.0e-123L);
}
static void __attribute__ ((noinline, noclone))
test_g_double (double d)
{
/* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%g", 0.0);
EQL ( 3, 4, "%g", 1.0 / 2);
EQL ( 4, 5, "%g", 1.0 / 4);
EQL ( 5, 6, "%g", 1.0 / 8);
EQL ( 6, 7, "%g", 1.0 / 16);
EQL ( 7, 8, "%g", 1.0 / 32);
EQL ( 8, 9, "%g", 1.0 / 64);
EQL ( 9, 10, "%g", 1.0 / 128);
EQL ( 10, 11, "%g", 1.0 / 256);
EQL ( 10, 11, "%g", 1.0 / 512);
/* Numbers that are not exactly representable. */
RNG ( 3, 8, 9, "%g", 0.1);
RNG ( 4, 8, 9, "%g", 0.12);
RNG ( 5, 8, 9, "%g", 0.123);
RNG ( 6, 8, 9, "%g", 0.1234);
RNG ( 7, 8, 9, "%g", 0.12345);
RNG ( 8, 8, 9, "%g", 0.123456);
RNG ( 4, 7, 8, "%g", 0.123e+1);
EQL ( 8, 9, "%g", 0.123e+12);
RNG ( 9, 12, 13, "%g", 0.123e+134);
RNG ( 1, 13, 14, "%g", d);
RNG ( 1, 7, 8, "%.g", d);
RNG ( 1, 7, 8, "%.0g", d);
RNG ( 1, 7, 8, "%.1g", d);
RNG ( 1, 9, 10, "%.2g", d);
RNG ( 1, 10, 11, "%.3g", d);
RNG ( 1, 11, 12, "%.4g", d);
RNG ( 1, 12, 13, "%.5g", d);
RNG ( 1, 13, 14, "%.6g", d);
RNG ( 1, 14, 15, "%.7g", d);
RNG ( 1, 15, 16, "%.8g", d);
RNG ( 1,310,311, "%.9999g", d);
RNG ( 1, 7, 8, "%.*g", 0, d);
RNG ( 1, 7, 8, "%.*g", 1, d);
RNG ( 1, 9, 10, "%.*g", 2, d);
RNG ( 1, 10, 11, "%.*g", 3, d);
RNG ( 1, 11, 12, "%.*g", 4, d);
RNG ( 1, 12, 13, "%.*g", 5, d);
RNG ( 1, 13, 14, "%.*g", 6, d);
RNG ( 1, 14, 15, "%.*g", 7, d);
RNG ( 1, 15, 16, "%.*g", 8, d);
RNG ( 1,310,311, "%.*g", 9999, d);
}
static void __attribute__ ((noinline, noclone))
test_g_long_double (void)
{
/* Numbers exactly representable in binary floating point. */
EQL ( 1, 2, "%Lg", 0.0L);
EQL ( 3, 4, "%Lg", 1.0L / 2);
EQL ( 4, 5, "%Lg", 1.0L / 4);
EQL ( 5, 6, "%Lg", 1.0L / 8);
EQL ( 6, 7, "%Lg", 1.0L / 16);
EQL ( 7, 8, "%Lg", 1.0L / 32);
EQL ( 8, 9, "%Lg", 1.0L / 64);
EQL ( 9, 10, "%Lg", 1.0L / 128);
EQL ( 10, 11, "%Lg", 1.0L / 256);
EQL ( 10, 11, "%Lg", 1.0L / 512);
/* Numbers that are not exactly representable. */
RNG ( 3, 8, 9, "%Lg", 0.1L);
RNG ( 4, 8, 9, "%Lg", 0.12L);
RNG ( 5, 8, 9, "%Lg", 0.123L);
RNG ( 6, 8, 9, "%Lg", 0.1234L);
RNG ( 7, 8, 9, "%Lg", 0.12345L);
RNG ( 8, 8, 9, "%Lg", 0.123456L);
RNG ( 4, 7, 8, "%Lg", 0.123e+1L);
EQL ( 8, 9, "%Lg", 0.123e+12L);
RNG ( 9, 12, 13, "%Lg", 0.123e+134L);
}
static void __attribute__ ((noinline, noclone))
test_s (int i)
{
@ -577,12 +711,14 @@ int main (void)
test_x ('?', 0xdead, 0xdeadbeef);
test_a_double (0.0);
test_e_double ();
test_f_double ();
test_e_double (0.0);
test_f_double (0.0);
test_g_double (0.0);
test_a_long_double ();
test_e_long_double ();
test_e_long_double (0.0);
test_f_long_double ();
test_g_long_double ();
test_s (0);