Add __builtin_tgmath for better tgmath.h implementation (bug 81156).
authorJoseph Myers <joseph@codesourcery.com>
Wed, 15 Nov 2017 01:53:45 +0000 (01:53 +0000)
committerJoseph Myers <jsm28@gcc.gnu.org>
Wed, 15 Nov 2017 01:53:45 +0000 (01:53 +0000)
Various implementations of C99/C11 <tgmath.h> have the property that
their macro expansions contain many copies of the macro arguments, so
resulting in exponential blowup of the size of macro expansions where
a call to such a macro contains other such calls in the macro
arguments.

This patch adds a (C-only) language feature __builtin_tgmath designed
to avoid this problem by implementing the <tgmath.h> function
selection rules directly in the compiler.  The effect is that
type-generic macros can be defined simply as

#define pow(a, b) __builtin_tgmath (powf, pow, powl, \
                                    cpowf, cpow, cpowl, a, b)

as in the example added to the manual, with each macro argument
expanded exactly once.  The details of __builtin_tgmath are as
described in the manual.  This is C-only since C++ uses function
overloading and just defines <ctgmath> to include <ccomplex> and
<cmath>.

__builtin_tgmath handles C99/C11 type-generic macros, and _FloatN,
_FloatNx and decimal floating-point types (following the proposed
resolution to the floating-point TS DR#9 that makes the rules for
finding a common type from arguments to a type-generic macro follow
the usual arithmetic conversions after adjustment of integer arguments
to _Decimal64 or double - or to _Complex double in the case of GNU
complex integer arguments).

Type-generic macros for functions from TS 18661 that round their
results to a narrower type are handled, but there are still some
unresolved questions regarding such macros so further changes in that
regard may be needed in future.  The current implementation follows an
older version of the DR#13 resolution (allowing a function for a
wide-enough argument type to be selected if no exactly-matching
function is available), but with appropriate calls to __builtin_tgmath
is still fully compatible with the latest version of the resolution
(not yet in the DR log), and allowing such not-exactly-matching
argument types to be chosen in that case avoids needing another
special case to treat integers as _Float64 instead of double in
certain cases.

Regarding other possible language/library features, not currently
implemented in GCC:

* Imaginary types could be naturally supported by allowing cases where
  the type-generic type is an imaginary type T and arguments or return
  types may be T (as at present), or the corresponding real type to T
  (as at present), or (new) the corresponding real type if T is real
  or imaginary but T if T is complex.  (tgmath.h would need a series
  of functions such as

  static inline _Imaginary double
  __sin_imag (_Imaginary double __x)
  {
    return _Imaginary_I * sinh (__imag__ __x);
  }

  to be used in __builtin_tgmath calls.)

* __builtin_tgmath would use the constant rounding direction in the
  presence of support for the FENV_ROUND / FENV_DEC_ROUND pragmas.
  Support for those would also require a new __builtin_<something> to
  cause a non-type-generic call to use the constant rounding
  direction (it seems cleaner to add a new __builtin_<something> when
  required than to make __builtin_tgmath handle a non-type-generic
  case with only one function argument).

* TS 18661-5 __STDC_TGMATH_OPERATOR_EVALUATION__ would require new
  __builtin_<something> that evaluates with excess range and precision
  like arithmetic operators do.

* The proposed C bindings for IEEE 754-2018 augmented arithmetic
  operations involve struct return types.  As currently implemented
  __builtin_tgmath does not handle those, but support could be added.

There are many error cases that the implementation diagnoses.  I've
tried to ensure reasonable error messages for erroneous uses of
__builtin_tgmath, but the errors for erroneous uses of the resulting
type-generic macros (that is, when the non-function arguments have
inappropriate types) are more important as they are more likely to be
seen by users.

GCC's own tgmath.h, as used for some targets, is updated in this
patch.  I've tested those changes minimally, via adjusting
gcc.dg/c99-tgmath-* locally to use that tgmath.h version.  I've also
run the glibc testsuite (which has much more thorough tests of
correctness of tgmath.h function selection) with a glibc patch to use
__builtin_tgmath in glibc's tgmath.h.

Bootstrapped with no regressions on x86_64-pc-linux-gnu.

PR c/81156

gcc:
* doc/extend.texi (Other Builtins): Document __builtin_tgmath.
* ginclude/tgmath.h (__tg_cplx, __tg_ldbl, __tg_dbl, __tg_choose)
(__tg_choose_2, __tg_choose_3, __TGMATH_REAL_1_2)
(__TGMATH_REAL_2_3): Remove macros.
(__TGMATH_CPLX, __TGMATH_CPLX_2, __TGMATH_REAL, __TGMATH_REAL_2)
(__TGMATH_REAL_3, __TGMATH_CPLX_ONLY): Define using
__builtin_tgmath.
(frexp, ldexp, nexttoward, scalbn, scalbln): Define using
__TGMATH_REAL_2.
(remquo): Define using __TGMATH_REAL_3.

gcc/c:
* c-parser.c (check_tgmath_function): New function.
(enum tgmath_parm_kind): New enum.
(c_parser_postfix_expression): Handle __builtin_tgmath.

gcc/c-family:
* c-common.c (c_common_reswords): Add __builtin_tgmath.
* c-common.h (enum rid): Add RID_BUILTIN_TGMATH.

gcc/testsuite:
* gcc.dg/builtin-tgmath-1.c, gcc.dg/builtin-tgmath-2.c,
gcc.dg/builtin-tgmath-err-1.c, gcc.dg/builtin-tgmath-err-2.c,
gcc.dg/dfp/builtin-tgmath-dfp-err.c,
gcc.dg/dfp/builtin-tgmath-dfp.c: New tests.

From-SVN: r254749

15 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-parser.c
gcc/doc/extend.texi
gcc/ginclude/tgmath.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtin-tgmath-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-tgmath-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c [new file with mode: 0644]

index 288e16347d555fe868d3467f8a2ae46bdbb4cad6..22431e52f4504e5cf31b8f6453b549435d525d95 100644 (file)
@@ -1,3 +1,17 @@
+2017-11-15  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/81156
+       * doc/extend.texi (Other Builtins): Document __builtin_tgmath.
+       * ginclude/tgmath.h (__tg_cplx, __tg_ldbl, __tg_dbl, __tg_choose)
+       (__tg_choose_2, __tg_choose_3, __TGMATH_REAL_1_2)
+       (__TGMATH_REAL_2_3): Remove macros.
+       (__TGMATH_CPLX, __TGMATH_CPLX_2, __TGMATH_REAL, __TGMATH_REAL_2)
+       (__TGMATH_REAL_3, __TGMATH_CPLX_ONLY): Define using
+       __builtin_tgmath.
+       (frexp, ldexp, nexttoward, scalbn, scalbln): Define using
+       __TGMATH_REAL_2.
+       (remquo): Define using __TGMATH_REAL_3.
+
 2017-11-14  Jeff Law  <law@redhat.com>
 
        * vr-values.c: New file with contents extracted from tree-vrp.c.
index 7e47cb8cf496075cd428217043842b3ce597b460..3127635056ca7c83ba380dd178dd5e4b86341cd0 100644 (file)
@@ -1,3 +1,9 @@
+2017-11-15  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/81156
+       * c-common.c (c_common_reswords): Add __builtin_tgmath.
+       * c-common.h (enum rid): Add RID_BUILTIN_TGMATH.
+
 2017-11-10  Martin Sebor  <msebor@redhat.com>
 
        PR c/81117
index a76fae7f8fc8710619e4a8667eba555f5f64aaf0..65d37c60c48127dc8973cdbeda439b11a4ee7b99 100644 (file)
@@ -376,6 +376,7 @@ const struct c_common_resword c_common_reswords[] =
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
+  { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
   { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
   { "__builtin_va_arg",        RID_VA_ARG,     0 },
index 7e1877e8d164adfef446be519f3ec6d983a235c4..5bb86191d2b78cc6967e590ea6e93f7c264767e7 100644 (file)
@@ -101,6 +101,7 @@ enum rid
   RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,         RID_BUILTIN_SHUFFLE,
+  RID_BUILTIN_TGMATH,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
index 60feeea902276a948f926b29876d44022b511d10..5622c8ae7875256a96dfbe94ec07fb8a84643454 100644 (file)
@@ -1,3 +1,10 @@
+2017-11-15  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/81156
+       * c-parser.c (check_tgmath_function): New function.
+       (enum tgmath_parm_kind): New enum.
+       (c_parser_postfix_expression): Handle __builtin_tgmath.
+
 2017-10-31  David Malcolm  <dmalcolm@redhat.com>
 
        * c-decl.c (implicit_decl_warning): Update for renaming of
index 7bca5f1a2a7cb9df355b267ae94fc76fb47a8f73..3d90e28caad16aab94c8035a8fc94429eb8d098a 100644 (file)
@@ -7829,6 +7829,61 @@ c_parser_generic_selection (c_parser *parser)
   return matched_assoc.expression;
 }
 
+/* Check the validity of a function pointer argument *EXPR (argument
+   position POS) to __builtin_tgmath.  Return the number of function
+   arguments if possibly valid; return 0 having reported an error if
+   not valid.  */
+
+static unsigned int
+check_tgmath_function (c_expr *expr, unsigned int pos)
+{
+  tree type = TREE_TYPE (expr->value);
+  if (!FUNCTION_POINTER_TYPE_P (type))
+    {
+      error_at (expr->get_location (),
+               "argument %u of %<__builtin_tgmath%> is not a function pointer",
+               pos);
+      return 0;
+    }
+  type = TREE_TYPE (type);
+  if (!prototype_p (type))
+    {
+      error_at (expr->get_location (),
+               "argument %u of %<__builtin_tgmath%> is unprototyped", pos);
+      return 0;
+    }
+  if (stdarg_p (type))
+    {
+      error_at (expr->get_location (),
+               "argument %u of %<__builtin_tgmath%> has variable arguments",
+               pos);
+      return 0;
+    }
+  unsigned int nargs = 0;
+  function_args_iterator iter;
+  tree t;
+  FOREACH_FUNCTION_ARGS (type, t, iter)
+    {
+      if (t == void_type_node)
+       break;
+      nargs++;
+    }
+  if (nargs == 0)
+    {
+      error_at (expr->get_location (),
+               "argument %u of %<__builtin_tgmath%> has no arguments", pos);
+      return 0;
+    }
+  return nargs;
+}
+
+/* Ways in which a parameter or return value of a type-generic macro
+   may vary between the different functions the macro may call.  */
+enum tgmath_parm_kind
+  {
+    tgmath_fixed, tgmath_real, tgmath_complex
+  };
+
 /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2,
    C11 6.5.1-6.5.2).  Compound literals aren't handled here; callers have to
    call c_parser_postfix_expression_after_paren_type on encountering them.
@@ -7869,6 +7924,7 @@ c_parser_generic_selection (c_parser *parser)
                             assignment-expression ,
                             assignment-expression )
      __builtin_types_compatible_p ( type-name , type-name )
+     __builtin_tgmath ( expr-list )
      __builtin_complex ( assignment-expression , assignment-expression )
      __builtin_shuffle ( assignment-expression , assignment-expression )
      __builtin_shuffle ( assignment-expression ,
@@ -8295,6 +8351,513 @@ c_parser_postfix_expression (c_parser *parser)
            set_c_expr_source_range (&expr, loc, close_paren_loc);
          }
          break;
+       case RID_BUILTIN_TGMATH:
+         {
+           vec<c_expr_t, va_gc> *cexpr_list;
+           location_t close_paren_loc;
+
+           c_parser_consume_token (parser);
+           if (!c_parser_get_builtin_args (parser,
+                                           "__builtin_tgmath",
+                                           &cexpr_list, false,
+                                           &close_paren_loc))
+             {
+               expr.set_error ();
+               break;
+             }
+
+           if (vec_safe_length (cexpr_list) < 3)
+             {
+               error_at (loc, "too few arguments to %<__builtin_tgmath%>");
+               expr.set_error ();
+               break;
+             }
+
+           unsigned int i;
+           c_expr_t *p;
+           FOR_EACH_VEC_ELT (*cexpr_list, i, p)
+             *p = convert_lvalue_to_rvalue (loc, *p, true, true);
+           unsigned int nargs = check_tgmath_function (&(*cexpr_list)[0], 1);
+           if (nargs == 0)
+             {
+               expr.set_error ();
+               break;
+             }
+           if (vec_safe_length (cexpr_list) < nargs)
+             {
+               error_at (loc, "too few arguments to %<__builtin_tgmath%>");
+               expr.set_error ();
+               break;
+             }
+           unsigned int num_functions = vec_safe_length (cexpr_list) - nargs;
+           if (num_functions < 2)
+             {
+               error_at (loc, "too few arguments to %<__builtin_tgmath%>");
+               expr.set_error ();
+               break;
+             }
+
+           /* The first NUM_FUNCTIONS expressions are the function
+              pointers.  The remaining NARGS expressions are the
+              arguments that are to be passed to one of those
+              functions, chosen following <tgmath.h> rules.  */
+           for (unsigned int j = 1; j < num_functions; j++)
+             {
+               unsigned int this_nargs
+                 = check_tgmath_function (&(*cexpr_list)[j], j + 1);
+               if (this_nargs == 0)
+                 {
+                   expr.set_error ();
+                   goto out;
+                 }
+               if (this_nargs != nargs)
+                 {
+                   error_at ((*cexpr_list)[j].get_location (),
+                             "argument %u of %<__builtin_tgmath%> has "
+                             "wrong number of arguments", j + 1);
+                   expr.set_error ();
+                   goto out;
+                 }
+             }
+
+           /* The functions all have the same number of arguments.
+              Determine whether arguments and return types vary in
+              ways permitted for <tgmath.h> functions.  */
+           /* The first entry in each of these vectors is for the
+              return type, subsequent entries for parameter
+              types.  */
+           auto_vec<enum tgmath_parm_kind> parm_kind (nargs + 1);
+           auto_vec<tree> parm_first (nargs + 1);
+           auto_vec<bool> parm_complex (nargs + 1);
+           auto_vec<bool> parm_varies (nargs + 1);
+           tree first_type = TREE_TYPE (TREE_TYPE ((*cexpr_list)[0].value));
+           tree first_ret = TYPE_MAIN_VARIANT (TREE_TYPE (first_type));
+           parm_first.quick_push (first_ret);
+           parm_complex.quick_push (TREE_CODE (first_ret) == COMPLEX_TYPE);
+           parm_varies.quick_push (false);
+           function_args_iterator iter;
+           tree t;
+           unsigned int argpos;
+           FOREACH_FUNCTION_ARGS (first_type, t, iter)
+             {
+               if (t == void_type_node)
+                 break;
+               parm_first.quick_push (TYPE_MAIN_VARIANT (t));
+               parm_complex.quick_push (TREE_CODE (t) == COMPLEX_TYPE);
+               parm_varies.quick_push (false);
+             }
+           for (unsigned int j = 1; j < num_functions; j++)
+             {
+               tree type = TREE_TYPE (TREE_TYPE ((*cexpr_list)[j].value));
+               tree ret = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+               if (ret != parm_first[0])
+                 {
+                   parm_varies[0] = true;
+                   if (!SCALAR_FLOAT_TYPE_P (parm_first[0])
+                       && !COMPLEX_FLOAT_TYPE_P (parm_first[0]))
+                     {
+                       error_at ((*cexpr_list)[0].get_location (),
+                                 "invalid type-generic return type for "
+                                 "argument %u of %<__builtin_tgmath%>",
+                                 1);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   if (!SCALAR_FLOAT_TYPE_P (ret)
+                       && !COMPLEX_FLOAT_TYPE_P (ret))
+                     {
+                       error_at ((*cexpr_list)[j].get_location (),
+                                 "invalid type-generic return type for "
+                                 "argument %u of %<__builtin_tgmath%>",
+                                 j + 1);
+                       expr.set_error ();
+                       goto out;
+                     }
+                 }
+               if (TREE_CODE (ret) == COMPLEX_TYPE)
+                 parm_complex[0] = true;
+               argpos = 1;
+               FOREACH_FUNCTION_ARGS (type, t, iter)
+                 {
+                   if (t == void_type_node)
+                     break;
+                   t = TYPE_MAIN_VARIANT (t);
+                   if (t != parm_first[argpos])
+                     {
+                       parm_varies[argpos] = true;
+                       if (!SCALAR_FLOAT_TYPE_P (parm_first[argpos])
+                           && !COMPLEX_FLOAT_TYPE_P (parm_first[argpos]))
+                         {
+                           error_at ((*cexpr_list)[0].get_location (),
+                                     "invalid type-generic type for "
+                                     "argument %u of argument %u of "
+                                     "%<__builtin_tgmath%>", argpos, 1);
+                           expr.set_error ();
+                           goto out;
+                         }
+                       if (!SCALAR_FLOAT_TYPE_P (t)
+                           && !COMPLEX_FLOAT_TYPE_P (t))
+                         {
+                           error_at ((*cexpr_list)[j].get_location (),
+                                     "invalid type-generic type for "
+                                     "argument %u of argument %u of "
+                                     "%<__builtin_tgmath%>", argpos, j + 1);
+                           expr.set_error ();
+                           goto out;
+                         }
+                     }
+                   if (TREE_CODE (t) == COMPLEX_TYPE)
+                     parm_complex[argpos] = true;
+                   argpos++;
+                 }
+             }
+           enum tgmath_parm_kind max_variation = tgmath_fixed;
+           for (unsigned int j = 0; j <= nargs; j++)
+             {
+               enum tgmath_parm_kind this_kind;
+               if (parm_varies[j])
+                 {
+                   if (parm_complex[j])
+                     max_variation = this_kind = tgmath_complex;
+                   else
+                     {
+                       this_kind = tgmath_real;
+                       if (max_variation != tgmath_complex)
+                         max_variation = tgmath_real;
+                     }
+                 }
+               else
+                 this_kind = tgmath_fixed;
+               parm_kind.quick_push (this_kind);
+             }
+           if (max_variation == tgmath_fixed)
+             {
+               error_at (loc, "function arguments of %<__builtin_tgmath%> "
+                         "all have the same type");
+               expr.set_error ();
+               break;
+             }
+
+           /* Identify a parameter (not the return type) that varies,
+              including with complex types if any variation includes
+              complex types; there must be at least one such
+              parameter.  */
+           unsigned int tgarg = 0;
+           for (unsigned int j = 1; j <= nargs; j++)
+             if (parm_kind[j] == max_variation)
+               {
+                 tgarg = j;
+                 break;
+               }
+           if (tgarg == 0)
+             {
+               error_at (loc, "function arguments of %<__builtin_tgmath%> "
+                         "lack type-generic parameter");
+               expr.set_error ();
+               break;
+             }
+
+           /* Determine the type of the relevant parameter for each
+              function.  */
+           auto_vec<tree> tg_type (num_functions);
+           for (unsigned int j = 0; j < num_functions; j++)
+             {
+               tree type = TREE_TYPE (TREE_TYPE ((*cexpr_list)[j].value));
+               argpos = 1;
+               FOREACH_FUNCTION_ARGS (type, t, iter)
+                 {
+                   if (argpos == tgarg)
+                     {
+                       tg_type.quick_push (TYPE_MAIN_VARIANT (t));
+                       break;
+                     }
+                   argpos++;
+                 }
+             }
+
+           /* Verify that the corresponding types are different for
+              all the listed functions.  Also determine whether all
+              the types are complex, whether all the types are
+              standard or binary, and whether all the types are
+              decimal.  */
+           bool all_complex = true;
+           bool all_binary = true;
+           bool all_decimal = true;
+           hash_set<tree> tg_types;
+           FOR_EACH_VEC_ELT (tg_type, i, t)
+             {
+               if (TREE_CODE (t) == COMPLEX_TYPE)
+                 all_decimal = false;
+               else
+                 {
+                   all_complex = false;
+                   if (DECIMAL_FLOAT_TYPE_P (t))
+                     all_binary = false;
+                   else
+                     all_decimal = false;
+                 }
+               if (tg_types.add (t))
+                 {
+                   error_at ((*cexpr_list)[i].get_location (),
+                             "duplicate type-generic parameter type for "
+                             "function argument %u of %<__builtin_tgmath%>",
+                             i + 1);
+                   expr.set_error ();
+                   goto out;
+                 }
+             }
+
+           /* Verify that other parameters and the return type whose
+              types vary have their types varying in the correct
+              way.  */
+           for (unsigned int j = 0; j < num_functions; j++)
+             {
+               tree exp_type = tg_type[j];
+               tree exp_real_type = exp_type;
+               if (TREE_CODE (exp_type) == COMPLEX_TYPE)
+                 exp_real_type = TREE_TYPE (exp_type);
+               tree type = TREE_TYPE (TREE_TYPE ((*cexpr_list)[j].value));
+               tree ret = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+               if ((parm_kind[0] == tgmath_complex && ret != exp_type)
+                   || (parm_kind[0] == tgmath_real && ret != exp_real_type))
+                 {
+                   error_at ((*cexpr_list)[j].get_location (),
+                             "bad return type for function argument %u "
+                             "of %<__builtin_tgmath%>", j + 1);
+                   expr.set_error ();
+                   goto out;
+                 }
+               argpos = 1;
+               FOREACH_FUNCTION_ARGS (type, t, iter)
+                 {
+                   if (t == void_type_node)
+                     break;
+                   t = TYPE_MAIN_VARIANT (t);
+                   if ((parm_kind[argpos] == tgmath_complex
+                        && t != exp_type)
+                       || (parm_kind[argpos] == tgmath_real
+                           && t != exp_real_type))
+                     {
+                       error_at ((*cexpr_list)[j].get_location (),
+                                 "bad type for argument %u of "
+                                 "function argument %u of "
+                                 "%<__builtin_tgmath%>", argpos, j + 1);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   argpos++;
+                 }
+             }
+
+           /* The functions listed are a valid set of functions for a
+              <tgmath.h> macro to select between.  Identify the
+              matching function, if any.  First, the argument types
+              must be combined following <tgmath.h> rules.  Integer
+              types are treated as _Decimal64 if any type-generic
+              argument is decimal, or if the only alternatives for
+              type-generic arguments are of decimal types, and are
+              otherwise treated as double (or _Complex double for
+              complex integer types).  After that adjustment, types
+              are combined following the usual arithmetic
+              conversions.  If the function only accepts complex
+              arguments, a complex type is produced.  */
+           bool arg_complex = all_complex;
+           bool arg_binary = all_binary;
+           bool arg_int_decimal = all_decimal;
+           for (unsigned int j = 1; j <= nargs; j++)
+             {
+               if (parm_kind[j] == tgmath_fixed)
+                 continue;
+               c_expr_t *ce = &(*cexpr_list)[num_functions + j - 1];
+               tree type = TREE_TYPE (ce->value);
+               if (!INTEGRAL_TYPE_P (type)
+                   && !SCALAR_FLOAT_TYPE_P (type)
+                   && TREE_CODE (type) != COMPLEX_TYPE)
+                 {
+                   error_at (ce->get_location (),
+                             "invalid type of argument %u of type-generic "
+                             "function", j);
+                   expr.set_error ();
+                   goto out;
+                 }
+               if (DECIMAL_FLOAT_TYPE_P (type))
+                 {
+                   arg_int_decimal = true;
+                   if (all_complex)
+                     {
+                       error_at (ce->get_location (),
+                                 "decimal floating-point argument %u to "
+                                 "complex-only type-generic function", j);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   else if (all_binary)
+                     {
+                       error_at (ce->get_location (),
+                                 "decimal floating-point argument %u to "
+                                 "binary-only type-generic function", j);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   else if (arg_complex)
+                     {
+                       error_at (ce->get_location (),
+                                 "both complex and decimal floating-point "
+                                 "arguments to type-generic function");
+                       expr.set_error ();
+                       goto out;
+                     }
+                   else if (arg_binary)
+                     {
+                       error_at (ce->get_location (),
+                                 "both binary and decimal floating-point "
+                                 "arguments to type-generic function");
+                       expr.set_error ();
+                       goto out;
+                     }
+                 }
+               else if (TREE_CODE (type) == COMPLEX_TYPE)
+                 {
+                   arg_complex = true;
+                   if (COMPLEX_FLOAT_TYPE_P (type))
+                     arg_binary = true;
+                   if (all_decimal)
+                     {
+                       error_at (ce->get_location (),
+                                 "complex argument %u to "
+                                 "decimal-only type-generic function", j);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   else if (arg_int_decimal)
+                     {
+                       error_at (ce->get_location (),
+                                 "both complex and decimal floating-point "
+                                 "arguments to type-generic function");
+                       expr.set_error ();
+                       goto out;
+                     }
+                 }
+               else if (SCALAR_FLOAT_TYPE_P (type))
+                 {
+                   arg_binary = true;
+                   if (all_decimal)
+                     {
+                       error_at (ce->get_location (),
+                                 "binary argument %u to "
+                                 "decimal-only type-generic function", j);
+                       expr.set_error ();
+                       goto out;
+                     }
+                   else if (arg_int_decimal)
+                     {
+                       error_at (ce->get_location (),
+                                 "both binary and decimal floating-point "
+                                 "arguments to type-generic function");
+                       expr.set_error ();
+                       goto out;
+                     }
+                 }
+             }
+           tree arg_real = NULL_TREE;
+           for (unsigned int j = 1; j <= nargs; j++)
+             {
+               if (parm_kind[j] == tgmath_fixed)
+                 continue;
+               c_expr_t *ce = &(*cexpr_list)[num_functions + j - 1];
+               tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ce->value));
+               if (TREE_CODE (type) == COMPLEX_TYPE)
+                 type = TREE_TYPE (type);
+               if (INTEGRAL_TYPE_P (type))
+                 type = (arg_int_decimal
+                         ? dfloat64_type_node
+                         : double_type_node);
+               if (arg_real == NULL_TREE)
+                 arg_real = type;
+               else
+                 arg_real = common_type (arg_real, type);
+               if (arg_real == error_mark_node)
+                 {
+                   expr.set_error ();
+                   goto out;
+                 }
+             }
+           tree arg_type = (arg_complex
+                            ? build_complex_type (arg_real)
+                            : arg_real);
+
+           /* Look for a function to call with type-generic parameter
+              type ARG_TYPE.  */
+           c_expr_t *fn = NULL;
+           for (unsigned int j = 0; j < num_functions; j++)
+             {
+               if (tg_type[j] == arg_type)
+                 {
+                   fn = &(*cexpr_list)[j];
+                   break;
+                 }
+             }
+           if (fn == NULL
+               && parm_kind[0] == tgmath_fixed
+               && SCALAR_FLOAT_TYPE_P (parm_first[0]))
+             {
+               /* Presume this is a macro that rounds its result to a
+                  narrower type, and look for the first function with
+                  at least the range and precision of the argument
+                  type.  */
+               for (unsigned int j = 0; j < num_functions; j++)
+                 {
+                   if (arg_complex
+                       != (TREE_CODE (tg_type[j]) == COMPLEX_TYPE))
+                     continue;
+                   tree real_tg_type = (arg_complex
+                                        ? TREE_TYPE (tg_type[j])
+                                        : tg_type[j]);
+                   if (DECIMAL_FLOAT_TYPE_P (arg_real)
+                       != DECIMAL_FLOAT_TYPE_P (real_tg_type))
+                     continue;
+                   scalar_float_mode arg_mode
+                     = SCALAR_FLOAT_TYPE_MODE (arg_real);
+                   scalar_float_mode tg_mode
+                     = SCALAR_FLOAT_TYPE_MODE (real_tg_type);
+                   const real_format *arg_fmt = REAL_MODE_FORMAT (arg_mode);
+                   const real_format *tg_fmt = REAL_MODE_FORMAT (tg_mode);
+                   if (arg_fmt->b == tg_fmt->b
+                       && arg_fmt->p <= tg_fmt->p
+                       && arg_fmt->emax <= tg_fmt->emax
+                       && (arg_fmt->emin - arg_fmt->p
+                           >= tg_fmt->emin - tg_fmt->p))
+                     {
+                       fn = &(*cexpr_list)[j];
+                       break;
+                     }
+                 }
+             }
+           if (fn == NULL)
+             {
+               error_at (loc, "no matching function for type-generic call");
+               expr.set_error ();
+               break;
+             }
+
+           /* Construct a call to FN.  */
+           vec<tree, va_gc> *args;
+           vec_alloc (args, nargs);
+           vec<tree, va_gc> *origtypes;
+           vec_alloc (origtypes, nargs);
+           auto_vec<location_t> arg_loc (nargs);
+           for (unsigned int j = 0; j < nargs; j++)
+             {
+               c_expr_t *ce = &(*cexpr_list)[num_functions + j];
+               args->quick_push (ce->value);
+               arg_loc.quick_push (ce->get_location ());
+               origtypes->quick_push (ce->original_type);
+             }
+           expr.value = c_build_function_call_vec (loc, arg_loc, fn->value,
+                                                   args, origtypes);
+           set_c_expr_source_range (&expr, loc, close_paren_loc);
+           break;
+         }
        case RID_BUILTIN_CALL_WITH_STATIC_CHAIN:
          {
            vec<c_expr_t, va_gc> *cexpr_list;
@@ -8563,6 +9126,7 @@ c_parser_postfix_expression (c_parser *parser)
       expr.set_error ();
       break;
     }
+ out:
   return c_parser_postfix_expression_after_primary
     (parser, EXPR_LOC_OR_LOC (expr.value, loc), expr);
 }
index c6bdb86a9eab234361ecb300d78f3e134b47f082..711264c132a4b0df2b3d6261bfb282ce235a4219 100644 (file)
@@ -11684,6 +11684,63 @@ future revisions.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} @var{type} __builtin_tgmath (@var{functions}, @var{arguments})
+
+The built-in function @code{__builtin_tgmath}, available only for C
+and Objective-C, calls a function determined according to the rules of
+@code{<tgmath.h>} macros.  It is intended to be used in
+implementations of that header, so that expansions of macros from that
+header only expand each of their arguments once, to avoid problems
+when calls to such macros are nested inside the arguments of other
+calls to such macros; in addition, it results in better diagnostics
+for invalid calls to @code{<tgmath.h>} macros than implementations
+using other GNU C language features.  For example, the @code{pow}
+type-generic macro might be defined as:
+
+@smallexample
+#define pow(a, b) __builtin_tgmath (powf, pow, powl, \
+                                    cpowf, cpow, cpowl, a, b)
+@end smallexample
+
+The arguments to @code{__builtin_tgmath} are at least two pointers to
+functions, followed by the arguments to the type-generic macro (which
+will be passed as arguments to the selected function).  All the
+pointers to functions must be pointers to prototyped functions, none
+of which may have variable arguments, and all of which must have the
+same number of parameters; the number of parameters of the first
+function determines how many arguments to @code{__builtin_tgmath} are
+interpreted as function pointers, and how many as the arguments to the
+called function.
+
+The types of the specified functions must all be different, but
+related to each other in the same way as a set of functions that may
+be selected between by a macro in @code{<tgmath.h>}.  This means that
+the functions are parameterized by a floating-point type @var{t},
+different for each such function.  The function return types may all
+be the same type, or they may be @var{t} for each function, or they
+may be the real type corresponding to @var{t} for each function (if
+some of the types @var{t} are complex).  Likewise, for each parameter
+position, the type of the parameter in that position may always be the
+same type, or may be @var{t} for each function (this case must apply
+for at least one parameter position), or may be the real type
+corresponding to @var{t} for each function.
+
+The standard rules for @code{<tgmath.h>} macros are used to find a
+common type @var{u} from the types of the arguments for parameters
+whose types vary between the functions; complex integer types (a GNU
+extension) are treated like @code{_Complex double} for this purpose.
+If the function return types vary, or are all the same integer type,
+the function called is the one for which @var{t} is @var{u}, and it is
+an error if there is no such function.  If the function return types
+are all the same floating-point type, the type-generic macro is taken
+to be one of those from TS 18661 that rounds the result to a narrower
+type; if there is a function for which @var{t} is @var{u}, it is
+called, and otherwise the first function, if any, for which @var{t}
+has at least the range and precision of @var{u} is called, and it is
+an error if there is no such function.
+
+@end deftypefn
+
 @deftypefn {Built-in Function} @var{type} __builtin_complex (@var{real}, @var{imag})
 
 The built-in function @code{__builtin_complex} is provided for use in
index be3f5be9df149aeae053ae757286eca65623ebec..97968ad2302b89bcebb51cfbaf5c486cf00d20b8 100644 (file)
@@ -38,68 +38,24 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
    __TGMATH_CPLX*, __TGMATH_REAL*, and __TGMATH_CPLX_ONLY.  _CPLX
    means the generic argument(s) may be real or complex, _REAL means
    real only, _CPLX means complex only.  If there is no suffix, we are
-   defining a function of one generic argument.  If the suffix is _n
-   it is a function of n generic arguments.  If the suffix is _m_n it
-   is a function of n arguments, the first m of which are generic.  We
-   only define these macros for values of n and/or m that are needed. */
-
-/* The general rules for generic macros are given in 7.22 paragraphs 1 and 2.
-   If any generic parameter is complex, we use a complex version.  Otherwise
-   we use a real version.  If the real part of any generic parameter is long
-   double, we use the long double version.  Otherwise if the real part of any
-   generic parameter is double or of integer type, we use the double version.
-   Otherwise we use the float version. */
-
-#define __tg_cplx(expr) \
-  __builtin_classify_type(expr) == 9
-
-#define __tg_ldbl(expr) \
-  __builtin_types_compatible_p(__typeof__(expr), long double)
-
-#define __tg_dbl(expr)                                       \
-  (__builtin_types_compatible_p(__typeof__(expr), double)    \
-   || __builtin_classify_type(expr) == 1)
-
-#define __tg_choose(x,f,d,l)                                  \
-  __builtin_choose_expr(__tg_ldbl(x), l,                      \
-                        __builtin_choose_expr(__tg_dbl(x), d, \
-                                              f))
-
-#define __tg_choose_2(x,y,f,d,l)                                             \
-  __builtin_choose_expr(__tg_ldbl(x) || __tg_ldbl(y), l,                     \
-                        __builtin_choose_expr(__tg_dbl(x) || __tg_dbl(y), d, \
-                                              f))
-
-#define __tg_choose_3(x,y,z,f,d,l)                                        \
-   __builtin_choose_expr(__tg_ldbl(x) || __tg_ldbl(y) || __tg_ldbl(z), l, \
-                        __builtin_choose_expr(__tg_dbl(x) || __tg_dbl(y)  \
-                                              || __tg_dbl(z), d,          \
-                                              f))
-
-#define __TGMATH_CPLX(z,R,C)                                                  \
-  __builtin_choose_expr (__tg_cplx(z),                                        \
-                         __tg_choose (__real__(z), C##f(z), (C)(z), C##l(z)), \
-                         __tg_choose (z, R##f(z), (R)(z), R##l(z)))
-
-#define __TGMATH_CPLX_2(z1,z2,R,C)                                             \
-  __builtin_choose_expr (__tg_cplx(z1) || __tg_cplx(z2),                       \
-                         __tg_choose_2 (__real__(z1), __real__(z2),            \
-                                        C##f(z1,z2), (C)(z1,z2), C##l(z1,z2)), \
-                         __tg_choose_2 (z1, z2,                                \
-                                        R##f(z1,z2), (R)(z1,z2), R##l(z1,z2)))
+   defining a function of one argument.  If the suffix is _n
+   it is a function of n arguments.  We only define these macros for
+   values of n that are needed. */
+
+#define __TGMATH_CPLX(z,R,C)                           \
+  __builtin_tgmath (R##f, R, R##l, C##f, C, C##l, (z))
+
+#define __TGMATH_CPLX_2(z1,z2,R,C)                             \
+  __builtin_tgmath (R##f, R, R##l, C##f, C, C##l, (z1), (z2))
 
 #define __TGMATH_REAL(x,R) \
-  __tg_choose (x, R##f(x), (R)(x), R##l(x))
+  __builtin_tgmath (R##f, R, R##l, (x))
 #define __TGMATH_REAL_2(x,y,R) \
-  __tg_choose_2 (x, y, R##f(x,y), (R)(x,y), R##l(x,y))
+  __builtin_tgmath (R##f, R, R##l, (x), (y))
 #define __TGMATH_REAL_3(x,y,z,R) \
-  __tg_choose_3 (x, y, z, R##f(x,y,z), (R)(x,y,z), R##l(x,y,z))
-#define __TGMATH_REAL_1_2(x,y,R) \
-  __tg_choose (x, R##f(x,y), (R)(x,y), R##l(x,y))
-#define __TGMATH_REAL_2_3(x,y,z,R) \
-  __tg_choose_2 (x, y, R##f(x,y,z), (R)(x,y,z), R##l(x,y,z))
+  __builtin_tgmath (R##f, R, R##l, (x), (y), (z))
 #define __TGMATH_CPLX_ONLY(z,C) \
-  __tg_choose (__real__(z), C##f(z), (C)(z), C##l(z))
+  __builtin_tgmath (C##f, C, C##l, (z))
 
 /* Functions defined in both <math.h> and <complex.h> (7.22p4) */
 #define acos(z)          __TGMATH_CPLX(z, acos, cacos)
@@ -135,10 +91,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #define fmax(x,y)        __TGMATH_REAL_2(x, y, fmax)
 #define fmin(x,y)        __TGMATH_REAL_2(x, y, fmin)
 #define fmod(x,y)        __TGMATH_REAL_2(x, y, fmod)
-#define frexp(x,y)       __TGMATH_REAL_1_2(x, y, frexp)
+#define frexp(x,y)       __TGMATH_REAL_2(x, y, frexp)
 #define hypot(x,y)       __TGMATH_REAL_2(x, y, hypot)
 #define ilogb(x)         __TGMATH_REAL(x, ilogb)
-#define ldexp(x,y)       __TGMATH_REAL_1_2(x, y, ldexp)
+#define ldexp(x,y)       __TGMATH_REAL_2(x, y, ldexp)
 #define lgamma(x)        __TGMATH_REAL(x, lgamma)
 #define llrint(x)        __TGMATH_REAL(x, llrint)
 #define llround(x)       __TGMATH_REAL(x, llround)
@@ -150,13 +106,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #define lround(x)        __TGMATH_REAL(x, lround)
 #define nearbyint(x)     __TGMATH_REAL(x, nearbyint)
 #define nextafter(x,y)   __TGMATH_REAL_2(x, y, nextafter)
-#define nexttoward(x,y)  __TGMATH_REAL_1_2(x, y, nexttoward)
+#define nexttoward(x,y)  __TGMATH_REAL_2(x, y, nexttoward)
 #define remainder(x,y)   __TGMATH_REAL_2(x, y, remainder)
-#define remquo(x,y,z)    __TGMATH_REAL_2_3(x, y, z, remquo)
+#define remquo(x,y,z)    __TGMATH_REAL_3(x, y, z, remquo)
 #define rint(x)          __TGMATH_REAL(x, rint)
 #define round(x)         __TGMATH_REAL(x, round)
-#define scalbn(x,y)      __TGMATH_REAL_1_2(x, y, scalbn)
-#define scalbln(x,y)     __TGMATH_REAL_1_2(x, y, scalbln)
+#define scalbn(x,y)      __TGMATH_REAL_2(x, y, scalbn)
+#define scalbln(x,y)     __TGMATH_REAL_2(x, y, scalbln)
 #define tgamma(x)        __TGMATH_REAL(x, tgamma)
 #define trunc(x)         __TGMATH_REAL(x, trunc)
 
index ec43496dfd3c86dfdd4552a1a92e6cd7788e9637..1cd0070c82edd99e5cae25d1e3114166175b1734 100644 (file)
@@ -1,3 +1,11 @@
+2017-11-15  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/81156
+       * gcc.dg/builtin-tgmath-1.c, gcc.dg/builtin-tgmath-2.c,
+       gcc.dg/builtin-tgmath-err-1.c, gcc.dg/builtin-tgmath-err-2.c,
+       gcc.dg/dfp/builtin-tgmath-dfp-err.c,
+       gcc.dg/dfp/builtin-tgmath-dfp.c: New tests.
+
 2017-11-14  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        * gcc.target/powerpc/float128-hw4.c: New test.
diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-1.c b/gcc/testsuite/gcc.dg/builtin-tgmath-1.c
new file mode 100644 (file)
index 0000000..ff87ace
--- /dev/null
@@ -0,0 +1,322 @@
+/* Test __builtin_tgmath: valid uses, standard floating-point types.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+
+extern void abort (void);
+extern void exit (int);
+
+#define CHECK_CALL(C, E, V)                    \
+  do                                           \
+    {                                          \
+      if ((C) != (E))                          \
+       abort ();                               \
+      extern __typeof (C) V;                   \
+    }                                          \
+  while (0)
+
+extern float var_f;
+extern double var_d;
+extern long double var_ld;
+extern _Complex float var_cf;
+extern _Complex double var_cd;
+extern _Complex long double var_cld;
+extern int var_i;
+
+typedef float float_type;
+typedef double double_type;
+
+/* Test simple case, real arguments and return type.  */
+
+float_type t1f (float x) { return x + 1; }
+double t1d (double_type x) { return x + 2; }
+long double t1l (volatile long double x) { return x + 3; }
+
+#define t1v(x) __builtin_tgmath (t1f, t1d, t1l, x)
+#define t1vr(x) __builtin_tgmath (t1l, t1d, t1f, x)
+
+static void
+test_1 (void)
+{
+  float_type f = 1;
+  volatile float vf = 2;
+  double d = 3;
+  long double ld = 4;
+  int i = 5;
+  long long ll = 6;
+  CHECK_CALL (t1v (f), 2, var_f);
+  CHECK_CALL (t1v (vf), 3, var_f);
+  CHECK_CALL (t1v (d), 5, var_d);
+  CHECK_CALL (t1v (ld), 7, var_ld);
+  CHECK_CALL (t1v (i), 7, var_d);
+  CHECK_CALL (t1v (ll), 8, var_d);
+  CHECK_CALL (t1vr (f), 2, var_f);
+  CHECK_CALL (t1vr (vf), 3, var_f);
+  CHECK_CALL (t1vr (d), 5, var_d);
+  CHECK_CALL (t1vr (ld), 7, var_ld);
+  CHECK_CALL (t1vr (i), 7, var_d);
+  CHECK_CALL (t1vr (ll), 8, var_d);
+}
+
+/* Test first argument not type-generic.  */
+
+float t2f (int a, float x) { return a * x + 1; }
+double t2d (int a, double x) { return a * x + 2; }
+long double t2l (int a, long double x) { return a * x + 3; }
+
+#define t2v(a, x) __builtin_tgmath (t2f, t2d, t2l, a, x)
+
+static void
+test_2 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  int i = 4;
+  unsigned long long ll = 5;
+  CHECK_CALL (t2v (1, f), 2, var_f);
+  CHECK_CALL (t2v (2, d), 6, var_d);
+  CHECK_CALL (t2v (3, ld), 12, var_ld);
+  CHECK_CALL (t2v (4, i), 18, var_d);
+  CHECK_CALL (t2v (5, ll), 27, var_d);
+}
+
+/* Test return type not type-generic.  */
+
+int t3f (float x) { return x + 1; }
+int t3d (double x) { return x + 2; }
+int t3l (long double x) { return x + 3; }
+
+#define t3v(x) __builtin_tgmath (t3f, t3d, t3l, x)
+
+static void
+test_3 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  short s = 4;
+  CHECK_CALL (t3v (f), 2, var_i);
+  CHECK_CALL (t3v (d), 4, var_i);
+  CHECK_CALL (t3v (ld), 6, var_i);
+  CHECK_CALL (t3v (s), 6, var_i);
+}
+
+/* Test multiple type-generic arguments.  */
+
+float t4f (float x, float y) { return 10 * x + y; }
+double t4d (double x, double y) { return 100 * x + y; }
+long double t4l (long double x, long double y) { return 1000 * x + y; }
+
+#define t4v(x, y) __builtin_tgmath (t4f, t4d, t4l, x, y)
+
+static void
+test_4 (void)
+{
+  float f1 = 1;
+  float f2 = 2;
+  double d1 = 3;
+  double d2 = 4;
+  long double ld = 5;
+  long int l = 6;
+  CHECK_CALL (t4v (f1, f2), 12, var_f);
+  CHECK_CALL (t4v (f2, f1), 21, var_f);
+  CHECK_CALL (t4v (f1, d1), 103, var_d);
+  CHECK_CALL (t4v (d2, f2), 402, var_d);
+  CHECK_CALL (t4v (f1, l), 106, var_d);
+  CHECK_CALL (t4v (ld, f1), 5001, var_ld);
+  CHECK_CALL (t4v (l, l), 606, var_d);
+  CHECK_CALL (t4v (l, ld), 6005, var_ld);
+}
+
+/* Test complex argument, real return type.  */
+
+float t5f (_Complex float x) { return 1 + __real__ x + 3 * __imag__ x; }
+double t5d (_Complex double x) { return 2 + __real__ x + 4 * __imag__ x; }
+long double t5l (_Complex long double x) { return 3 + __real__ x + 5 * __imag__ x; }
+
+#define t5v(x) __builtin_tgmath (t5f, t5d, t5l, x)
+
+static void
+test_5 (void)
+{
+  float f = 1;
+  _Complex float cf = 2 + 3i;
+  double d = 4;
+  _Complex double cd = 5 + 6i;
+  long double ld = 7;
+  _Complex long double cld = 8 + 9i;
+  int i = 10;
+  _Complex int ci = 11 + 12i;
+  CHECK_CALL (t5v (f), 2, var_f);
+  CHECK_CALL (t5v (cf), 12, var_f);
+  CHECK_CALL (t5v (d), 6, var_d);
+  CHECK_CALL (t5v (cd), 31, var_d);
+  CHECK_CALL (t5v (ld), 10, var_ld);
+  CHECK_CALL (t5v (cld), 56, var_ld);
+  CHECK_CALL (t5v (i), 12, var_d);
+  CHECK_CALL (t5v (ci), 61, var_d);
+}
+
+/* Test complex argument, complex return type.  */
+
+_Complex float t6f (_Complex float x) { return 1 + x; }
+_Complex double t6d (_Complex double x) { return 2 + x; }
+_Complex long double t6l (_Complex long double x) { return 3 + x; }
+
+#define t6v(x) __builtin_tgmath (t6f, t6d, t6l, x)
+
+static void
+test_6 (void)
+{
+  float f = 1;
+  _Complex float cf = 2 + 3i;
+  double d = 4;
+  _Complex double cd = 5 + 6i;
+  long double ld = 7;
+  _Complex long double cld = 8 + 9i;
+  int i = 10;
+  _Complex int ci = 11 + 12i;
+  CHECK_CALL (t6v (f), 2, var_cf);
+  CHECK_CALL (t6v (cf), 3 + 3i, var_cf);
+  CHECK_CALL (t6v (d), 6, var_cd);
+  CHECK_CALL (t6v (cd), 7 + 6i, var_cd);
+  CHECK_CALL (t6v (ld), 10, var_cld);
+  CHECK_CALL (t6v (cld), 11 + 9i, var_cld);
+  CHECK_CALL (t6v (i), 12, var_cd);
+  CHECK_CALL (t6v (ci), 13 + 12i, var_cd);
+}
+
+/* Test real and complex argument, real return type.  */
+
+float t7f (float x) { return 1 + x; }
+float t7cf (_Complex float x) { return 2 + __real__ x; }
+double t7d (double x) { return 3 + x; }
+double t7cd (_Complex double x) { return 4 + __real__ x; }
+long double t7l (long double x) { return 5 + x; }
+long double t7cl (_Complex long double x) { return 6 + __real__ x; }
+
+#define t7v(x) __builtin_tgmath (t7f, t7d, t7l, t7cf, t7cd, t7cl, x)
+
+static void
+test_7 (void)
+{
+  float f = 1;
+  _Complex float cf = 2 + 3i;
+  double d = 4;
+  _Complex double cd = 5 + 6i;
+  long double ld = 7;
+  _Complex long double cld = 8 + 9i;
+  int i = 10;
+  _Complex int ci = 11 + 12i;
+  CHECK_CALL (t7v (f), 2, var_f);
+  CHECK_CALL (t7v (cf), 4, var_f);
+  CHECK_CALL (t7v (d), 7, var_d);
+  CHECK_CALL (t7v (cd), 9, var_d);
+  CHECK_CALL (t7v (ld), 12, var_ld);
+  CHECK_CALL (t7v (cld), 14, var_ld);
+  CHECK_CALL (t7v (i), 13, var_d);
+  CHECK_CALL (t7v (ci), 15, var_d);
+}
+
+/* Test real and complex argument, real and complex return type.  */
+
+float t8f (float x) { return 1 + x; }
+_Complex float t8cf (_Complex float x) { return 2 + x; }
+double t8d (double x) { return 3 + x; }
+_Complex double t8cd (_Complex double x) { return 4 + x; }
+long double t8l (long double x) { return 5 + x; }
+_Complex long double t8cl (_Complex long double x) { return 6 + x; }
+
+#define t8v(x) __builtin_tgmath (t8f, t8d, t8l, t8cf, t8cd, t8cl, x)
+
+static void
+test_8 (void)
+{
+  float f = 1;
+  _Complex float cf = 2 + 3i;
+  double d = 4;
+  _Complex double cd = 5 + 6i;
+  long double ld = 7;
+  _Complex long double cld = 8 + 9i;
+  int i = 10;
+  _Complex int ci = 11 + 12i;
+  CHECK_CALL (t8v (f), 2, var_f);
+  CHECK_CALL (t8v (cf), 4 + 3i, var_cf);
+  CHECK_CALL (t8v (d), 7, var_d);
+  CHECK_CALL (t8v (cd), 9 + 6i, var_cd);
+  CHECK_CALL (t8v (ld), 12, var_ld);
+  CHECK_CALL (t8v (cld), 14 + 9i, var_cld);
+  CHECK_CALL (t8v (i), 13, var_d);
+  CHECK_CALL (t8v (ci), 15 + 12i, var_cd);
+}
+
+/* Test multiple type-generic arguments, real and complex.  */
+
+float t9f (float x, float y) { return x + 10 * y; }
+_Complex float t9cf (_Complex float x, _Complex float y) { return x + 100 * y; }
+double t9d (double x, double y) { return x + 1000 * y; }
+_Complex double t9cd (_Complex double x, _Complex double y) { return x + 10000 * y; }
+long double t9l (long double x, long double y) { return x + 100000 * y; }
+_Complex long double t9cl (_Complex long double x, _Complex long double y) { return x + 1000000 * y; }
+
+#define t9v(x, y) __builtin_tgmath (t9f, t9d, t9l, t9cf, t9cd, t9cl, x, y)
+
+static void
+test_9 (void)
+{
+  float f = 1;
+  _Complex float cf = 2 + 3i;
+  double d = 4;
+  _Complex double cd = 5 + 6i;
+  long double ld = 7;
+  _Complex long double cld = 8 + 9i;
+  int i = 10;
+  _Complex int ci = 11 + 12i;
+  CHECK_CALL (t9v (f, f), 11, var_f);
+  CHECK_CALL (t9v (f, cf), 201 + 300i, var_cf);
+  CHECK_CALL (t9v (cf, f), 102 + 3i, var_cf);
+  CHECK_CALL (t9v (f, i), 10001, var_d);
+  CHECK_CALL (t9v (i, f), 1010, var_d);
+  CHECK_CALL (t9v (d, d), 4004, var_d);
+  CHECK_CALL (t9v (d, cd), 50004 + 60000i, var_cd);
+  CHECK_CALL (t9v (ld, i), 1000007, var_ld);
+  CHECK_CALL (t9v (cf, cld), 8000002 + 9000003i, var_cld);
+  CHECK_CALL (t9v (i, i), 10010, var_d);
+  CHECK_CALL (t9v (ci, i), 100011 + 12i, var_cd);
+}
+
+/* Test functions rounding result to narrower type.  */
+
+float t10d (double x) { return 1 + x; }
+float t10l (long double x) { return 2 + x; }
+
+#define t10v(x) __builtin_tgmath (t10d, t10l, x)
+
+static void
+test_10 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  short s = 4;
+  CHECK_CALL (t10v (f), 2, var_f);
+  CHECK_CALL (t10v (d), 3, var_f);
+  CHECK_CALL (t10v (ld), 5, var_f);
+  CHECK_CALL (t10v (s), 5, var_f);
+}
+
+int
+main (void)
+{
+  test_1 ();
+  test_2 ();
+  test_3 ();
+  test_4 ();
+  test_5 ();
+  test_6 ();
+  test_7 ();
+  test_8 ();
+  test_9 ();
+  test_10 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-2.c b/gcc/testsuite/gcc.dg/builtin-tgmath-2.c
new file mode 100644 (file)
index 0000000..c4140cc
--- /dev/null
@@ -0,0 +1,51 @@
+/* Test __builtin_tgmath: valid uses, _FloatN types.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float32 } */
+/* { dg-require-effective-target float32_runtime } */
+
+extern void abort (void);
+extern void exit (int);
+
+#define CHECK_CALL(C, E, V)                    \
+  do                                           \
+    {                                          \
+      if ((C) != (E))                          \
+       abort ();                               \
+      extern __typeof (C) V;                   \
+    }                                          \
+  while (0)
+
+extern float var_f;
+extern double var_d;
+extern long double var_ld;
+extern _Float32 var_f32;
+
+float t1f (float x) { return x + 1; }
+double t1d (double x) { return x + 2; }
+long double t1l (long double x) { return x + 3; }
+_Float32 t1f32 (_Float32 x) { return x + 4; }
+
+#define t1v(x) __builtin_tgmath (t1f, t1d, t1l, t1f32, x)
+
+static void
+test_1 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  _Float32 f32 = 4;
+  int i = 5;
+  CHECK_CALL (t1v (f), 2, var_f);
+  CHECK_CALL (t1v (d), 4, var_d);
+  CHECK_CALL (t1v (ld), 6, var_ld);
+  CHECK_CALL (t1v (f32), 8, var_f32);
+  CHECK_CALL (t1v (i), 7, var_d);
+}
+
+int
+main (void)
+{
+  test_1 ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c b/gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c
new file mode 100644 (file)
index 0000000..9016ec7
--- /dev/null
@@ -0,0 +1,76 @@
+/* Test __builtin_tgmath: errors that indicate a bad definition of a
+   type-generic macro rather than bad arguments in a call to it.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void *p;
+double d;
+double unprototyped_d ();
+long double unprototyped_ld ();
+double variadic_d (double, ...);
+long double variadic_ld (long double, ...);
+double no_arguments_d (void);
+long double no_arguments_ld (void);
+double f_d (double);
+long double f_ld (long double);
+double many_args (double, double, double, double);
+int f_i_d (double);
+_Complex int f_ci_d (double);
+void * f_p_d (double);
+double f_d_i (int);
+double f_d_ci (_Complex int);
+double f_d_p (void *);
+long double f_ld_d (double);
+_Complex double f_cd_d (double);
+double f_d_f (float);
+double f_d_dd (double, double);
+long double f_ld_ldld (long double, long double);
+float f_f_fd (float, double);
+
+void
+test (void)
+{
+  /* Arguments individually invalid or no consistent number of
+     arguments followed by those arguments.  */
+  __builtin_tgmath (); /* { dg-error "too few arguments" } */
+  __builtin_tgmath (f_d); /* { dg-error "too few arguments" } */
+  __builtin_tgmath (f_d, f_ld); /* { dg-error "too few arguments" } */
+  __builtin_tgmath (many_args, many_args, many_args); /* { dg-error "too few arguments" } */
+  __builtin_tgmath (many_args, d, d, d, d); /* { dg-error "too few arguments" } */
+  __builtin_tgmath (f_ld, many_args, d); /* { dg-error "has wrong number of arguments" } */
+  __builtin_tgmath (unprototyped_d, unprototyped_ld, d); /* { dg-error "is unprototyped" } */
+  __builtin_tgmath (f_d, unprototyped_ld, d); /* { dg-error "is unprototyped" } */
+  __builtin_tgmath (variadic_d, variadic_ld, d); /* { dg-error "variable arguments" } */
+  __builtin_tgmath (f_d, variadic_ld, d); /* { dg-error "variable arguments" } */
+  __builtin_tgmath (p, p, p); /* { dg-error "is not a function pointer" } */
+  __builtin_tgmath (f_d, p, p); /* { dg-error "is not a function pointer" } */
+  __builtin_tgmath (no_arguments_d, no_arguments_d, no_arguments_ld); /* { dg-error "has no arguments" } */
+  __builtin_tgmath (f_d, no_arguments_d, no_arguments_ld); /* { dg-error "has no arguments" } */
+
+  /* Invalid varying types of arguments.  */
+  __builtin_tgmath (f_i_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_ci_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_p_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_ld, f_i_d, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_ld, f_ci_d, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_ld, f_p_d, 0); /* { dg-error "invalid type-generic return type" } */
+  __builtin_tgmath (f_d_i, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */
+  __builtin_tgmath (f_d_ci, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */
+  __builtin_tgmath (f_d_p, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */
+  __builtin_tgmath (f_ld, f_d_i, 0); /* { dg-error "invalid type-generic type for argument" } */
+  __builtin_tgmath (f_ld, f_d_ci, 0); /* { dg-error "invalid type-generic type for argument" } */
+  __builtin_tgmath (f_ld, f_d_p, 0); /* { dg-error "invalid type-generic type for argument" } */
+
+  /* Arguments same type.  */
+  __builtin_tgmath (f_d, f_d, 0); /* { dg-error "all have the same type" } */
+
+  /* Missing or invalid type-generic parameter.  */
+  __builtin_tgmath (f_d, f_ld_d, 0); /* { dg-error "lack type-generic parameter" } */
+  __builtin_tgmath (f_d, f_ld, f_cd_d, 0); /* { dg-error "lack type-generic parameter" } */
+  __builtin_tgmath (f_d, f_ld, f_d, 0); /* { dg-error "duplicate type-generic parameter type" } */
+
+  /* Variation not consistent with the identified type-generic
+     parameter.  */
+  __builtin_tgmath (f_d, f_ld, f_d_f, 0); /* { dg-error "bad return type for function argument" } */
+  __builtin_tgmath (f_d_dd, f_ld_ldld, f_f_fd, 0, 0); /* { dg-error "bad type for argument" } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c b/gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c
new file mode 100644 (file)
index 0000000..df5655e
--- /dev/null
@@ -0,0 +1,19 @@
+/* Test __builtin_tgmath: errors that indicate bad arguments in a call
+   to a type-generic macro, non-DFP.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+float f_f (float);
+double f_d (double);
+long double f_ld (long double);
+void *p;
+long double ld;
+_Complex float cf;
+
+void
+test (void)
+{
+  __builtin_tgmath (f_f, f_d, f_ld, p); /* { dg-error "invalid type of argument" } */
+  __builtin_tgmath (f_f, f_d, ld); /* { dg-error "no matching function for type-generic call" } */
+  __builtin_tgmath (f_f, f_d, cf); /* { dg-error "no matching function for type-generic call" } */
+}
diff --git a/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c
new file mode 100644 (file)
index 0000000..b94c760
--- /dev/null
@@ -0,0 +1,33 @@
+/* Test __builtin_tgmath: errors that indicate bad arguments in a call
+   to a type-generic macro, DFP involved.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+float f_f (float);
+double f_d (double);
+long double f_ld (long double);
+_Complex float f_cf (_Complex float);
+_Complex double f_cd (_Complex double);
+_Complex long double f_cld (_Complex long double);
+_Decimal32 f_d32 (_Decimal32);
+_Decimal64 f_d64 (_Decimal64);
+_Decimal128 f_d128 (_Decimal128);
+float f_ff (float, float);
+_Complex float f_cfcf (_Complex float, _Complex float);
+_Decimal32 f_d32d32 (_Decimal32, _Decimal32);
+_Complex float cf;
+float f;
+_Decimal32 d32;
+
+void
+test (void)
+{
+  __builtin_tgmath (f_cf, f_cd, f_cld, d32); /* { dg-error "decimal floating-point argument 1 to complex-only type-generic function" } */
+  __builtin_tgmath (f_f, f_d, f_ld, d32); /* { dg-error "decimal floating-point argument 1 to binary-only type-generic function" } */
+  __builtin_tgmath (f_cfcf, f_d32d32, cf, d32); /* { dg-error "both complex and decimal floating-point arguments to type-generic function" } */
+  __builtin_tgmath (f_ff, f_d32d32, f, d32); /* { dg-error "both binary and decimal floating-point arguments to type-generic function" } */
+  __builtin_tgmath (f_d32, f_d64, f_d128, cf); /* { dg-error "complex argument 1 to decimal-only type-generic function" } */
+  __builtin_tgmath (f_d32, f_d64, f_d128, f); /* { dg-error "binary argument 1 to decimal-only type-generic function" } */
+  __builtin_tgmath (f_cfcf, f_d32d32, d32, cf); /* { dg-error "both complex and decimal floating-point arguments to type-generic function" } */
+  __builtin_tgmath (f_ff, f_d32d32, d32, f); /* { dg-error "both binary and decimal floating-point arguments to type-generic function" } */
+}
diff --git a/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c
new file mode 100644 (file)
index 0000000..256a71e
--- /dev/null
@@ -0,0 +1,263 @@
+/* Test __builtin_tgmath: valid uses, decimal floating-point types.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+
+extern void abort (void);
+extern void exit (int);
+
+#define CHECK_CALL(C, E, V)                    \
+  do                                           \
+    {                                          \
+      if ((C) != (E))                          \
+       abort ();                               \
+      extern __typeof (C) V;                   \
+    }                                          \
+  while (0)
+
+extern float var_f;
+extern double var_d;
+extern long double var_ld;
+extern _Complex float var_cf;
+extern _Complex double var_cd;
+extern _Complex long double var_cld;
+extern _Decimal32 var_d32;
+extern _Decimal64 var_d64;
+extern _Decimal128 var_d128;
+extern int var_i;
+
+/* Test decimal-only function, single argument.  */
+
+_Decimal32 t1d32 (_Decimal32 x) { return x + 1; }
+_Decimal64 t1d64 (_Decimal64 x) { return x + 2; }
+_Decimal128 t1d128 (_Decimal128 x) { return x + 3; }
+
+#define t1v(x) __builtin_tgmath (t1d32, t1d64, t1d128, x)
+
+static void
+test_1 (void)
+{
+  _Decimal32 d32 = 32;
+  _Decimal64 d64 = 64;
+  _Decimal128 d128 = 128;
+  int i = 256;
+  CHECK_CALL (t1v (d32), 33, var_d32);
+  CHECK_CALL (t1v (d64), 66, var_d64);
+  CHECK_CALL (t1v (d128), 131, var_d128);
+  CHECK_CALL (t1v (i), 258, var_d64);
+}
+
+/* Test decimal-only function, two arguments.  */
+
+_Decimal32 t2d32 (_Decimal32 x, _Decimal32 y) { return 10 * x + y; }
+_Decimal64 t2d64 (_Decimal64 x, _Decimal64 y) { return 100 * x + y;; }
+_Decimal128 t2d128 (_Decimal128 x, _Decimal128 y) { return 1000 * x + y; }
+
+#define t2v(x, y) __builtin_tgmath (t2d32, t2d64, t2d128, x, y)
+
+static void
+test_2 (void)
+{
+  _Decimal32 d32 = 1;
+  _Decimal64 d64 = 2;
+  _Decimal128 d128 = 3;
+  int i = 4;
+  CHECK_CALL (t2v (d32, d32), 11, var_d32);
+  CHECK_CALL (t2v (d64, d64), 202, var_d64);
+  CHECK_CALL (t2v (d32, d64), 102, var_d64);
+  CHECK_CALL (t2v (d128, d64), 3002, var_d128);
+  CHECK_CALL (t2v (d128, i), 3004, var_d128);
+  CHECK_CALL (t2v (i, i), 404, var_d64);
+  CHECK_CALL (t2v (i, d32), 401, var_d64);
+}
+
+/* Test real-only function, single argument.  */
+
+float t3f (float x) { return x + 1; }
+double t3d (double x) { return x + 2; }
+long double t3l (long double x) { return x + 3; }
+_Decimal32 t3d32 (_Decimal32 x) { return x + 4; }
+_Decimal64 t3d64 (_Decimal64 x) { return x + 5; }
+_Decimal128 t3d128 (_Decimal128 x) { return x + 6; }
+
+#define t3v(x) __builtin_tgmath (t3f, t3d, t3l, t3d32, t3d64, t3d128, x)
+
+static void
+test_3 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  int i = 4;
+  _Decimal32 d32 = 5;
+  _Decimal64 d64 = 6;
+  _Decimal128 d128 = 7;
+  CHECK_CALL (t3v (f), 2, var_f);
+  CHECK_CALL (t3v (d), 4, var_d);
+  CHECK_CALL (t3v (ld), 6, var_ld);
+  CHECK_CALL (t3v (i), 6, var_d);
+  CHECK_CALL (t3v (d32), 9, var_d32);
+  CHECK_CALL (t3v (d64), 11, var_d64);
+  CHECK_CALL (t3v (d128), 13, var_d128);
+}
+
+/* Test real-and-complex function, single argument.  */
+
+float t4f (float x) { return x + 1; }
+double t4d (double x) { return x + 2; }
+long double t4l (long double x) { return x + 3; }
+_Complex float t4cf (_Complex float x) { return x + 4; }
+_Complex double t4cd (_Complex double x) { return x + 5; }
+_Complex long double t4cl (_Complex long double x) { return x + 6; }
+_Decimal32 t4d32 (_Decimal32 x) { return x + 7; }
+_Decimal64 t4d64 (_Decimal64 x) { return x + 8; }
+_Decimal128 t4d128 (_Decimal128 x) { return x + 9; }
+
+#define t4v(x) __builtin_tgmath (t4f, t4d, t4l, t4cf, t4cd, t4cl, t4d32, t4d64, t4d128, x)
+
+static void
+test_4 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  int i = 4;
+  _Complex float cf = 5;
+  _Complex double cd = 6;
+  _Complex long double cld = 7;
+  _Complex int ci = 8;
+  _Decimal32 d32 = 9;
+  _Decimal64 d64 = 10;
+  _Decimal128 d128 = 11;
+  CHECK_CALL (t4v (f), 2, var_f);
+  CHECK_CALL (t4v (d), 4, var_d);
+  CHECK_CALL (t4v (ld), 6, var_ld);
+  CHECK_CALL (t4v (i), 6, var_d);
+  CHECK_CALL (t4v (cf), 9, var_cf);
+  CHECK_CALL (t4v (cd), 11, var_cd);
+  CHECK_CALL (t4v (cld), 13, var_cld);
+  CHECK_CALL (t4v (ci), 13, var_cd);
+  CHECK_CALL (t4v (d32), 16, var_d32);
+  CHECK_CALL (t4v (d64), 18, var_d64);
+  CHECK_CALL (t4v (d128), 20, var_d128);
+}
+
+/* Test real-and-complex function, real return type, single argument.  */
+
+float t5f (float x) { return x + 1; }
+double t5d (double x) { return x + 2; }
+long double t5l (long double x) { return x + 3; }
+float t5cf (_Complex float x) { return __real__ x + 4; }
+double t5cd (_Complex double x) { return __real__ x + 5; }
+long double t5cl (_Complex long double x) { return __real__ x + 6; }
+_Decimal32 t5d32 (_Decimal32 x) { return x + 7; }
+_Decimal64 t5d64 (_Decimal64 x) { return x + 8; }
+_Decimal128 t5d128 (_Decimal128 x) { return x + 9; }
+
+#define t5v(x) __builtin_tgmath (t5f, t5d, t5l, t5cf, t5cd, t5cl, t5d32, t5d64, t5d128, x)
+
+static void
+test_5 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  int i = 4;
+  _Complex float cf = 5;
+  _Complex double cd = 6;
+  _Complex long double cld = 7;
+  _Complex int ci = 8;
+  _Decimal32 d32 = 9;
+  _Decimal64 d64 = 10;
+  _Decimal128 d128 = 11;
+  CHECK_CALL (t5v (f), 2, var_f);
+  CHECK_CALL (t5v (d), 4, var_d);
+  CHECK_CALL (t5v (ld), 6, var_ld);
+  CHECK_CALL (t5v (i), 6, var_d);
+  CHECK_CALL (t5v (cf), 9, var_f);
+  CHECK_CALL (t5v (cd), 11, var_d);
+  CHECK_CALL (t5v (cld), 13, var_ld);
+  CHECK_CALL (t5v (ci), 13, var_d);
+  CHECK_CALL (t5v (d32), 16, var_d32);
+  CHECK_CALL (t5v (d64), 18, var_d64);
+  CHECK_CALL (t5v (d128), 20, var_d128);
+}
+
+/* Test real-and-complex function, two arguments.  */
+
+float t6f (float x, float y) { return x * 10 + y; }
+double t6d (double x, double y) { return x * 100 + y; }
+long double t6l (long double x, long double y) { return x * 1000 + y; }
+_Complex float t6cf (_Complex float x, _Complex float y) { return x * 10000 + y; }
+_Complex double t6cd (_Complex double x, _Complex double y) { return x * 100000 + y; }
+_Complex long double t6cl (_Complex long double x, _Complex long double y) { return x * 1000000 + y; }
+_Decimal32 t6d32 (_Decimal32 x, _Decimal32 y) { return x * 50 + y; }
+_Decimal64 t6d64 (_Decimal64 x, _Decimal64 y) { return x * 500 + y; }
+_Decimal128 t6d128 (_Decimal128 x, _Decimal128 y) { return x * 5000 + y; }
+
+#define t6v(x, y) __builtin_tgmath (t6f, t6d, t6l, t6cf, t6cd, t6cl, t6d32, t6d64, t6d128, x, y)
+
+static void
+test_6 (void)
+{
+  float f = 1;
+  double d = 2;
+  long double ld = 3;
+  int i = 4;
+  _Complex float cf = 5;
+  _Complex double cd = 6;
+  _Complex long double cld = 7;
+  _Complex int ci = 8;
+  _Decimal32 d32 = 9;
+  _Decimal64 d64 = 10;
+  _Decimal128 d128 = 11;
+  CHECK_CALL (t6v (f, f), 11, var_f);
+  CHECK_CALL (t6v (d, f), 201, var_d);
+  CHECK_CALL (t6v (f, d), 102, var_d);
+  CHECK_CALL (t6v (f, i), 104, var_d);
+  CHECK_CALL (t6v (ld, f), 3001, var_ld);
+  CHECK_CALL (t6v (i, ld), 4003, var_ld);
+  CHECK_CALL (t6v (i, i), 404, var_d);
+  CHECK_CALL (t6v (cf, f), 50001, var_cf);
+  CHECK_CALL (t6v (cf, cf), 50005, var_cf);
+  CHECK_CALL (t6v (cd, cf), 600005, var_cd);
+  CHECK_CALL (t6v (d, cld), 2000007, var_cld);
+  CHECK_CALL (t6v (ci, ci), 800008, var_cd);
+  CHECK_CALL (t6v (ci, f), 800001, var_cd);
+  CHECK_CALL (t6v (d32, d32), 459, var_d32);
+  CHECK_CALL (t6v (d64, i), 5004, var_d64);
+  CHECK_CALL (t6v (i, d32), 2009, var_d64);
+  CHECK_CALL (t6v (d128, d32), 55009, var_d128);
+}
+
+/* Test decimal-only function rounding result to narrower type.  */
+
+_Decimal32 t7d64 (_Decimal64 x) { return 1 + x; }
+_Decimal32 t7d128 (_Decimal128 x) { return 2 + x; }
+
+#define t7v(x) __builtin_tgmath (t7d64, t7d128, x)
+
+static void
+test_7 (void)
+{
+  _Decimal32 d32 = 1;
+  _Decimal64 d64 = 2;
+  _Decimal128 d128 = 3;
+  short s = 4;
+  CHECK_CALL (t7v (d32), 2, var_d32);
+  CHECK_CALL (t7v (d64), 3, var_d32);
+  CHECK_CALL (t7v (d128), 5, var_d32);
+  CHECK_CALL (t7v (s), 5, var_d32);
+}
+
+int
+main (void)
+{
+  test_1 ();
+  test_2 ();
+  test_3 ();
+  test_4 ();
+  test_5 ();
+  test_6 ();
+  test_7 ();
+  exit (0);
+}