On Wed, Nov 15, 2017 at 2:54 AM, Joseph Myers <[email protected]> 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 <[email protected]>
>
> 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 <[email protected]>
>
> 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 <[email protected]>
>
> 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 <[email protected]>
>
> 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
> [email protected]