diff options
author | Martin Sebor <msebor@redhat.com> | 2018-07-04 02:19:35 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2018-07-03 20:19:35 -0600 |
commit | 7af7cb515f8ee1af63f7998c2748281263ae6f48 (patch) | |
tree | 798819af04347e5a75794a0189e84ed327e7e467 /gcc/gimple-ssa-sprintf.c | |
parent | 68a8b147efe9b919ace0b05c879697a3314fdca6 (diff) | |
download | gcc-7af7cb515f8ee1af63f7998c2748281263ae6f48.zip gcc-7af7cb515f8ee1af63f7998c2748281263ae6f48.tar.gz gcc-7af7cb515f8ee1af63f7998c2748281263ae6f48.tar.bz2 |
PR tree-optimization/86274 - SEGFAULT when logging std::to_string(NAN)
gcc/ChangeLog:
PR tree-optimization/86274
* gimple-ssa-sprintf.c (fmtresult::type_max_digits): Verify
precondition.
(format_floating): Correct handling of infinities and NaNs.
gcc/testsuite/ChangeLog:
PR tree-optimization/86274
* gcc.dg/tree-ssa/builtin-sprintf-9.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust.
* gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf.c: Same.
* gcc.dg/tree-ssa/pr83198.c: Same.
From-SVN: r262368
Diffstat (limited to 'gcc/gimple-ssa-sprintf.c')
-rw-r--r-- | gcc/gimple-ssa-sprintf.c | 94 |
1 files changed, 66 insertions, 28 deletions
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index ec5e704..bda738d 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -781,15 +781,19 @@ unsigned fmtresult::type_max_digits (tree type, int base) { unsigned prec = TYPE_PRECISION (type); - if (base == 8) - return (prec + 2) / 3; - - if (base == 16) - return prec / 4; + switch (base) + { + case 8: + return (prec + 2) / 3; + case 10: + /* Decimal approximation: yields 3, 5, 10, and 20 for precision + of 8, 16, 32, and 64 bits. */ + return prec * 301 / 1000 + 1; + case 16: + return prec / 4; + } - /* Decimal approximation: yields 3, 5, 10, and 20 for precision - of 8, 16, 32, and 64 bits. */ - return prec * 301 / 1000 + 1; + gcc_unreachable (); } static bool @@ -1759,6 +1763,11 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) unsigned flagmin = (1 /* for the first digit */ + (dir.get_flag ('+') | dir.get_flag (' '))); + /* The minimum is 3 for "inf" and "nan" for all specifiers, plus 1 + for the plus sign/space with the '+' and ' ' flags, respectively, + unless reduced below. */ + res.range.min = 2 + flagmin; + /* When the pound flag is set the decimal point is included in output regardless of precision. Whether or not a decimal point is included otherwise depends on the specification and precision. */ @@ -1775,14 +1784,13 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) else if (dir.prec[0] > 0) minprec = dir.prec[0] + !radix /* decimal point */; - res.range.min = (2 /* 0x */ - + flagmin - + radix - + minprec - + 3 /* p+0 */); + res.range.likely = (2 /* 0x */ + + flagmin + + radix + + minprec + + 3 /* p+0 */); 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. */ @@ -1800,15 +1808,14 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) non-zero, decimal point. */ HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0; - /* The minimum output is "[-+]1.234567e+00" regardless + /* The likely minimum output is "[-+]1.234567e+00" regardless of the value of the actual argument. */ - res.range.min = (flagmin - + radix - + minprec - + 2 /* e+ */ + 2); + res.range.likely = (flagmin + + radix + + minprec + + 2 /* e+ */ + 2); res.range.max = format_floating_max (type, 'e', prec[1]); - res.range.likely = res.range.min; /* The unlikely maximum accounts for the longest multibyte decimal point character. */ @@ -1827,12 +1834,15 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) 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). */ - res.range.min = flagmin + radix + minprec; + /* For finite numbers (i.e., not infinity or NaN) 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). + But in all cases, the lower bound is no greater than 3. */ + unsigned HOST_WIDE_INT min = flagmin + radix + minprec; + if (min < res.range.min) + res.range.min = min; /* Compute the upper bound for -TYPE_MAX. */ res.range.max = format_floating_max (type, 'f', prec[1]); @@ -1842,7 +1852,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) if (dir.prec[0] < 0 && dir.prec[1] > 0) res.range.likely = 3; else - res.range.likely = res.range.min; + res.range.likely = min; /* The unlikely maximum accounts for the longest multibyte decimal point character. */ @@ -1860,7 +1870,9 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) the lower bound on the range of bytes (not counting flags or width) is 1 plus radix (i.e., either "0" or "0." for "%g" and "%#g", respectively, with a zero argument). */ - res.range.min = flagmin + radix; + unsigned HOST_WIDE_INT min = flagmin + radix; + if (min < res.range.min) + res.range.min = min; char spec = 'g'; HOST_WIDE_INT maxprec = dir.prec[1]; @@ -1992,6 +2004,32 @@ format_floating (const directive &dir, tree arg, vr_values *) const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg); const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg))); + if (!real_isfinite (rvp)) + { + /* The format for Infinity and NaN is "[-]inf"/"[-]infinity" + and "[-]nan" with the choice being implementation-defined + but not locale dependent. */ + bool sign = dir.get_flag ('+') || real_isneg (rvp); + res.range.min = 3 + sign; + + res.range.likely = res.range.min; + res.range.max = res.range.min; + /* The inlikely maximum is "[-/+]infinity" or "[-/+]nan". */ + res.range.unlikely = sign + (real_isinf (rvp) ? 8 : 3); + + /* The range for infinity and NaN is known unless either width + or precision is unknown. Width has the same effect regardless + of whether the argument is finite. Precision is either ignored + (e.g., Glibc) or can have an effect on the short vs long format + such as inf/infinity (e.g., Solaris). */ + res.knownrange = dir.known_width_and_precision (); + + /* Adjust the range for width but ignore precision. */ + res.adjust_for_width_or_precision (dir.width); + + return res; + } + char fmtstr [40]; char *pfmt = fmtstr; |