PR tree-optimization/78696 - [7 Regression] -fprintf-return-value misoptimizes %...
authorMartin Sebor <msebor@gcc.gnu.org>
Tue, 3 Jan 2017 23:14:44 +0000 (16:14 -0700)
committerMartin Sebor <msebor@gcc.gnu.org>
Tue, 3 Jan 2017 23:14:44 +0000 (16:14 -0700)
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

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

index 179cd099e12de254e567663fcc15c5e4ecb06168..882856412e2ee2ef1e70f031707b0479d6294b19 100644 (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.
index 5bf0215cf1ec1cfd38803763c885c21095b8e4f8..d468cd7f369d93a8061bbccd330f4e37a5ad928b 100644 (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 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);
        }
     }
 }
index 8155754f847877b23300d8902f8774797dcdc21f..c12b94a85f95dc27c6b89227e7283d99d8c591f6 100644 (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
index c4489acb360d7e184f19921365a7f6e336a34ad2..c915a351f53c53cdcc15289fea5726ce7de92ddf 100644 (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)
index 0069348a75b90c90415a6259631e9793400d0f5d..778725500bd3f1be368ec1a753ef3a5883c00db7 100644 (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" } */
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-9.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-9.c
new file mode 100644 (file)
index 0000000..c8c6405
--- /dev/null
@@ -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" } */
+}
index 35a5bd0f0602aa1697c6ba959158322a896891d9..96892c16425b716f6576930a5fdb3b12256c2150 100644 (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);