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