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: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf.c (checkv): Add test cases.
@@ -474,9 +474,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. */
@@ -1135,10 +1137,11 @@ format_integer (const conversion_spec &spec, tree arg)
}
/* 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 int
-format_floating_max (tree type, char spec, int prec = -1)
+format_floating_max (tree type, char spec, int prec)
{
machine_mode mode = TYPE_MODE (type);
@@ -1189,7 +1192,6 @@ static fmtresult
format_floating (const conversion_spec &spec, int width, int prec)
{
tree type;
- bool ldbl = false;
switch (spec.modifier)
{
@@ -1200,12 +1202,10 @@ format_floating (const conversion_spec &spec, int width, int prec)
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:
@@ -1215,19 +1215,20 @@ format_floating (const conversion_spec &spec, int width, int prec)
/* 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 base in which the exponent is represented should always
- be 2 in GCC. */
-
- const double log10_2 = .30102999566398119521;
+ /* 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 ('#')));
- /* Compute T_MAX_EXP for base 2. */
- int expdigs = REAL_MODE_FORMAT (TYPE_MODE (type))->emax * log10_2;
- logexpdigs = ilog (expdigs, 10);
+ if (width == INT_MIN || prec == INT_MIN)
+ {
+ /* 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 = target_int_max ();
}
switch (spec.specifier)
@@ -1235,75 +1236,73 @@ format_floating (const conversion_spec &spec, int width, int prec)
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));
-
- /* 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;
+ /* The minimum output is "0xp+0". */
+ res.range.min = flagmin + 5 + (prec > 0 ? prec + 1 : 0);
+ if (res.range.max == HOST_WIDE_INT_MAX)
+ {
+ /* Compute the upper bound for -TYPE_MAX. */
+ res.range.max = format_floating_max (type, 'a', 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_MAX)
+ {
+ /* 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);
-
- /* Compute the maximum just once. */
- const 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;
+ /* 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));
+
+ if (res.range.max == HOST_WIDE_INT_MAX)
+ {
+ /* 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 = 2 + (prec < 0 ? 6 : prec);
-
- /* Compute the maximum just once. */
- const 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];
+ /* 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;
- /* "%g" 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_MAX)
+ {
+ /* 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;
}
@@ -1313,6 +1312,7 @@ format_floating (const conversion_spec &spec, int width, int prec)
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)
@@ -1360,11 +1360,7 @@ format_floating (const conversion_spec &spec, tree arg)
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 = INT_MIN;
}
else if (res.constant && TOUPPER (spec.specifier) != 'A')
{
@@ -1375,11 +1371,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)));
@@ -1418,16 +1409,32 @@ format_floating (const conversion_spec &spec, tree arg)
*pfmt++ = spec.specifier;
*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. */
+ *rndspec = "DU"[i];
+
+ /* Format it and store the result in the corresponding
+ member of the result struct. */
+ *minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval);
+ }
+ }
+
+ /* 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. */
- *rndspec = "DU"[i];
-
- /* Format it and store the result in the corresponding
- member of the result struct. */
- *minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval);
+ 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. */
@@ -2263,10 +2270,10 @@ pass_sprintf_length::compute_format_length (const 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;
}
@@ -2342,7 +2349,10 @@ pass_sprintf_length::compute_format_length (const 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)
@@ -2370,7 +2380,10 @@ pass_sprintf_length::compute_format_length (const 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
@@ -2629,11 +2642,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);
}
}
}
@@ -19,6 +19,7 @@
FAIL (__LINE__)(value); \
} while (0)
+/* Verify that EXPECT == snprintf(0, 0, ...). */
#define T(expect, ...) \
do { \
int n = __builtin_snprintf (0, 0, __VA_ARGS__); \
@@ -108,6 +109,7 @@ void test_arg_multiarg (int i, double d)
T (16, "%*i %s", 12, i, "abc");
}
+/* Verify that EXPECT == vsnprintf(0, 0, ...). */
#define TV(expect, fmt, va) \
do { \
int n = __builtin_vsnprintf (0, 0, fmt, va); \
@@ -117,9 +119,7 @@ void test_arg_multiarg (int i, double d)
void test_va_int (__builtin_va_list va)
{
TV ( 2, "%02hhx", va);
- TV ( 2, "%02.*hhx", va);
TV ( 4, "%04hx", va);
- TV ( 4, "%04.*hx", va);
}
void test_va_multiarg (__builtin_va_list va)
new file mode 100644
@@ -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" } */
+}
@@ -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;
@@ -368,6 +371,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 */
@@ -385,7 +390,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);
@@ -407,10 +412,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);
@@ -443,10 +472,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);
@@ -464,6 +515,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))
@@ -488,6 +541,87 @@ test_f_long_double (void)
}
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)
{
EQL ( 0, 1, "%s", "");
@@ -530,12 +664,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);