re PR tree-optimization/79800 (wrong snprintf result range with precision in a narrow...
authorMartin Sebor <msebor@redhat.com>
Wed, 15 Mar 2017 04:31:27 +0000 (04:31 +0000)
committerJeff Law <law@gcc.gnu.org>
Wed, 15 Mar 2017 04:31:27 +0000 (22:31 -0600)
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

gcc/ChangeLog
gcc/gimple-ssa-sprintf.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c
gcc/testsuite/gcc.dg/tree-ssa/pr79800.c [new file with mode: 0644]

index 77e6f8c0b68f42af9aaa41d286fbba68bf3993a8..d9f1054b1eea0e9876160170c9460c5446c50c9a 100644 (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".
index 0448b2127bed1d3770fba5b7f01fe03342e97705..2474391cadf6eb328a15f1397be4e14b1031f2c6 100644 (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;
 
index f1ba337149bb80a58be82557f456e74bd8515e01..616d03b59134f4334fee4eb4d1c6bf3cc825baac 100644 (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
index c38a656bc7be6ce6072d123e2be77aae4847ef25..0b863a8aaaffc23a5ed3371bf734e6aba59d1ae5 100644 (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" } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c
new file mode 100644 (file)
index 0000000..180a6e7
--- /dev/null
@@ -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;
+}