On Wed, Nov 15, 2017 at 2:54 AM, Joseph Myers <jos...@codesourcery.com> wrote:
> 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.  Applied to
> mainline.

Thanks - I suppose we can't avoid the repeated expansion by sth like

#define exp(Val) ({ __typeof__ Val tem = Val; __TGMATH_UNARY_REAL_IMAG
(tem, exp, cexp); })

?

Richard.

> gcc:
> 2017-11-15  Joseph Myers  <jos...@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.
>
> gcc/c:
> 2017-11-15  Joseph Myers  <jos...@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.
>
> gcc/c-family:
> 2017-11-15  Joseph Myers  <jos...@codesourcery.com>
>
>         PR c/81156
>         * c-common.c (c_common_reswords): Add __builtin_tgmath.
>         * c-common.h (enum rid): Add RID_BUILTIN_TGMATH.
>
> gcc/testsuite:
> 2017-11-15  Joseph Myers  <jos...@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.
>
> Index: gcc/c/c-parser.c
> ===================================================================
> --- gcc/c/c-parser.c    (revision 254726)
> +++ gcc/c/c-parser.c    (working copy)
> @@ -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: gcc/c-family/c-common.c
> ===================================================================
> --- gcc/c-family/c-common.c     (revision 254726)
> +++ gcc/c-family/c-common.c     (working copy)
> @@ -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: gcc/c-family/c-common.h
> ===================================================================
> --- gcc/c-family/c-common.h     (revision 254726)
> +++ gcc/c-family/c-common.h     (working copy)
> @@ -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: gcc/doc/extend.texi
> ===================================================================
> --- gcc/doc/extend.texi (revision 254726)
> +++ gcc/doc/extend.texi (working copy)
> @@ -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: gcc/ginclude/tgmath.h
> ===================================================================
> --- gcc/ginclude/tgmath.h       (revision 254726)
> +++ gcc/ginclude/tgmath.h       (working copy)
> @@ -38,68 +38,24 @@ see the files COPYING3 and COPYING.RUNTIME respect
>     __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. */
> +   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. */
>
> -/* 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 __TGMATH_CPLX(z,R,C)                           \
> +  __builtin_tgmath (R##f, R, R##l, C##f, C, C##l, (z))
>
> -#define __tg_cplx(expr) \
> -  __builtin_classify_type(expr) == 9
> +#define __TGMATH_CPLX_2(z1,z2,R,C)                             \
> +  __builtin_tgmath (R##f, R, R##l, C##f, C, C##l, (z1), (z2))
>
> -#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)))
> -
>  #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 respect
>  #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 respect
>  #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: gcc/testsuite/gcc.dg/builtin-tgmath-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/builtin-tgmath-1.c     (nonexistent)
> +++ gcc/testsuite/gcc.dg/builtin-tgmath-1.c     (working copy)
> @@ -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);
> +}
> Index: gcc/testsuite/gcc.dg/builtin-tgmath-2.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/builtin-tgmath-2.c     (nonexistent)
> +++ gcc/testsuite/gcc.dg/builtin-tgmath-2.c     (working copy)
> @@ -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);
> +}
> Index: gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c (nonexistent)
> +++ gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c (working copy)
> @@ -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" } */
> +}
> Index: gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c (nonexistent)
> +++ gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c (working copy)
> @@ -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" } */
> +}
> Index: gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c   (nonexistent)
> +++ gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c   (working copy)
> @@ -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" } */
> +}
> Index: gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c       (nonexistent)
> +++ gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c       (working copy)
> @@ -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);
> +}
>
> --
> Joseph S. Myers
> jos...@codesourcery.com

Reply via email to