diff options
author | Martin Sebor <msebor@gcc.gnu.org> | 2017-01-03 16:14:44 -0700 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2017-01-03 16:14:44 -0700 |
commit | 5b4f088d0d203c56898f7957366255d33b6b41a6 (patch) | |
tree | 5331a51b6c1ca01b2f03916a9d7018b572c715ca /gcc/gimple-ssa-sprintf.c | |
parent | 0f2a6e84c6e4edd122228be318a014dd88d1c4f2 (diff) | |
download | gcc-5b4f088d0d203c56898f7957366255d33b6b41a6.zip gcc-5b4f088d0d203c56898f7957366255d33b6b41a6.tar.gz gcc-5b4f088d0d203c56898f7957366255d33b6b41a6.tar.bz2 |
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
Diffstat (limited to 'gcc/gimple-ssa-sprintf.c')
-rw-r--r-- | gcc/gimple-ssa-sprintf.c | 248 |
1 files changed, 138 insertions, 110 deletions
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 5bf0215..d468cd7 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -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 base in which the exponent is represented should always - be 2 in GCC. */ + /* 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 ('#'))); - 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); + 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 = 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)); - - /* 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; + 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); + } + 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); - - /* 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; + /* 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_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); } } } |