diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 288e16347d5..22431e52f45 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2017-11-15 Joseph Myers + + PR c/81156 + * doc/extend.texi (Other Builtins): Document __builtin_tgmath. + * ginclude/tgmath.h (__tg_cplx, __tg_ldbl, __tg_dbl, __tg_choose) + (__tg_choose_2, __tg_choose_3, __TGMATH_REAL_1_2) + (__TGMATH_REAL_2_3): Remove macros. + (__TGMATH_CPLX, __TGMATH_CPLX_2, __TGMATH_REAL, __TGMATH_REAL_2) + (__TGMATH_REAL_3, __TGMATH_CPLX_ONLY): Define using + __builtin_tgmath. + (frexp, ldexp, nexttoward, scalbn, scalbln): Define using + __TGMATH_REAL_2. + (remquo): Define using __TGMATH_REAL_3. + 2017-11-14 Jeff Law * vr-values.c: New file with contents extracted from tree-vrp.c. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 7e47cb8cf49..3127635056c 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2017-11-15 Joseph Myers + + PR c/81156 + * c-common.c (c_common_reswords): Add __builtin_tgmath. + * c-common.h (enum rid): Add RID_BUILTIN_TGMATH. + 2017-11-10 Martin Sebor PR c/81117 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index a76fae7f8fc..65d37c60c48 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -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 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 7e1877e8d16..5bb86191d2b 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -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. */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 60feeea9022..5622c8ae787 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2017-11-15 Joseph Myers + + PR c/81156 + * c-parser.c (check_tgmath_function): New function. + (enum tgmath_parm_kind): New enum. + (c_parser_postfix_expression): Handle __builtin_tgmath. + 2017-10-31 David Malcolm * c-decl.c (implicit_decl_warning): Update for renaming of diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 7bca5f1a2a7..3d90e28caad 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -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 *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 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 functions. */ + /* The first entry in each of these vectors is for the + return type, subsequent entries for parameter + types. */ + auto_vec parm_kind (nargs + 1); + auto_vec parm_first (nargs + 1); + auto_vec parm_complex (nargs + 1); + auto_vec 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 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 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 + macro to select between. Identify the + matching function, if any. First, the argument types + must be combined following 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 *args; + vec_alloc (args, nargs); + vec *origtypes; + vec_alloc (origtypes, nargs); + auto_vec 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 *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); } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c6bdb86a9ea..711264c132a 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -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{} 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{} 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{}. 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{} 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 diff --git a/gcc/ginclude/tgmath.h b/gcc/ginclude/tgmath.h index be3f5be9df1..97968ad2302 100644 --- a/gcc/ginclude/tgmath.h +++ b/gcc/ginclude/tgmath.h @@ -38,68 +38,24 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see __TGMATH_CPLX*, __TGMATH_REAL*, and __TGMATH_CPLX_ONLY. _CPLX means the generic argument(s) may be real or complex, _REAL means real only, _CPLX means complex only. If there is no suffix, we are - defining a function of one generic argument. If the suffix is _n - it is a function of n generic arguments. If the suffix is _m_n it - is a function of n arguments, the first m of which are generic. We - only define these macros for values of n and/or m that are needed. */ + 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 __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_CPLX_2(z1,z2,R,C) \ + __builtin_tgmath (R##f, R, R##l, C##f, C, C##l, (z1), (z2)) #define __TGMATH_REAL(x,R) \ - __tg_choose (x, R##f(x), (R)(x), R##l(x)) + __builtin_tgmath (R##f, R, R##l, (x)) #define __TGMATH_REAL_2(x,y,R) \ - __tg_choose_2 (x, y, R##f(x,y), (R)(x,y), R##l(x,y)) + __builtin_tgmath (R##f, R, R##l, (x), (y)) #define __TGMATH_REAL_3(x,y,z,R) \ - __tg_choose_3 (x, y, z, R##f(x,y,z), (R)(x,y,z), R##l(x,y,z)) -#define __TGMATH_REAL_1_2(x,y,R) \ - __tg_choose (x, R##f(x,y), (R)(x,y), R##l(x,y)) -#define __TGMATH_REAL_2_3(x,y,z,R) \ - __tg_choose_2 (x, y, R##f(x,y,z), (R)(x,y,z), R##l(x,y,z)) + __builtin_tgmath (R##f, R, R##l, (x), (y), (z)) #define __TGMATH_CPLX_ONLY(z,C) \ - __tg_choose (__real__(z), C##f(z), (C)(z), C##l(z)) + __builtin_tgmath (C##f, C, C##l, (z)) /* Functions defined in both and (7.22p4) */ #define acos(z) __TGMATH_CPLX(z, acos, cacos) @@ -135,10 +91,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define fmax(x,y) __TGMATH_REAL_2(x, y, fmax) #define fmin(x,y) __TGMATH_REAL_2(x, y, fmin) #define fmod(x,y) __TGMATH_REAL_2(x, y, fmod) -#define frexp(x,y) __TGMATH_REAL_1_2(x, y, frexp) +#define frexp(x,y) __TGMATH_REAL_2(x, y, frexp) #define hypot(x,y) __TGMATH_REAL_2(x, y, hypot) #define ilogb(x) __TGMATH_REAL(x, ilogb) -#define ldexp(x,y) __TGMATH_REAL_1_2(x, y, ldexp) +#define ldexp(x,y) __TGMATH_REAL_2(x, y, ldexp) #define lgamma(x) __TGMATH_REAL(x, lgamma) #define llrint(x) __TGMATH_REAL(x, llrint) #define llround(x) __TGMATH_REAL(x, llround) @@ -150,13 +106,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define lround(x) __TGMATH_REAL(x, lround) #define nearbyint(x) __TGMATH_REAL(x, nearbyint) #define nextafter(x,y) __TGMATH_REAL_2(x, y, nextafter) -#define nexttoward(x,y) __TGMATH_REAL_1_2(x, y, nexttoward) +#define nexttoward(x,y) __TGMATH_REAL_2(x, y, nexttoward) #define remainder(x,y) __TGMATH_REAL_2(x, y, remainder) -#define remquo(x,y,z) __TGMATH_REAL_2_3(x, y, z, remquo) +#define remquo(x,y,z) __TGMATH_REAL_3(x, y, z, remquo) #define rint(x) __TGMATH_REAL(x, rint) #define round(x) __TGMATH_REAL(x, round) -#define scalbn(x,y) __TGMATH_REAL_1_2(x, y, scalbn) -#define scalbln(x,y) __TGMATH_REAL_1_2(x, y, scalbln) +#define scalbn(x,y) __TGMATH_REAL_2(x, y, scalbn) +#define scalbln(x,y) __TGMATH_REAL_2(x, y, scalbln) #define tgamma(x) __TGMATH_REAL(x, tgamma) #define trunc(x) __TGMATH_REAL(x, trunc) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ec43496dfd3..1cd0070c82e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2017-11-15 Joseph Myers + + PR c/81156 + * gcc.dg/builtin-tgmath-1.c, gcc.dg/builtin-tgmath-2.c, + gcc.dg/builtin-tgmath-err-1.c, gcc.dg/builtin-tgmath-err-2.c, + gcc.dg/dfp/builtin-tgmath-dfp-err.c, + gcc.dg/dfp/builtin-tgmath-dfp.c: New tests. + 2017-11-14 Michael Meissner * gcc.target/powerpc/float128-hw4.c: New test. diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-1.c b/gcc/testsuite/gcc.dg/builtin-tgmath-1.c new file mode 100644 index 00000000000..ff87ace42fd --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-tgmath-1.c @@ -0,0 +1,322 @@ +/* Test __builtin_tgmath: valid uses, standard floating-point types. */ +/* { dg-do run } */ +/* { dg-options "" } */ + +extern void abort (void); +extern void exit (int); + +#define CHECK_CALL(C, E, V) \ + do \ + { \ + if ((C) != (E)) \ + abort (); \ + extern __typeof (C) V; \ + } \ + while (0) + +extern float var_f; +extern double var_d; +extern long double var_ld; +extern _Complex float var_cf; +extern _Complex double var_cd; +extern _Complex long double var_cld; +extern int var_i; + +typedef float float_type; +typedef double double_type; + +/* Test simple case, real arguments and return type. */ + +float_type t1f (float x) { return x + 1; } +double t1d (double_type x) { return x + 2; } +long double t1l (volatile long double x) { return x + 3; } + +#define t1v(x) __builtin_tgmath (t1f, t1d, t1l, x) +#define t1vr(x) __builtin_tgmath (t1l, t1d, t1f, x) + +static void +test_1 (void) +{ + float_type f = 1; + volatile float vf = 2; + double d = 3; + long double ld = 4; + int i = 5; + long long ll = 6; + CHECK_CALL (t1v (f), 2, var_f); + CHECK_CALL (t1v (vf), 3, var_f); + CHECK_CALL (t1v (d), 5, var_d); + CHECK_CALL (t1v (ld), 7, var_ld); + CHECK_CALL (t1v (i), 7, var_d); + CHECK_CALL (t1v (ll), 8, var_d); + CHECK_CALL (t1vr (f), 2, var_f); + CHECK_CALL (t1vr (vf), 3, var_f); + CHECK_CALL (t1vr (d), 5, var_d); + CHECK_CALL (t1vr (ld), 7, var_ld); + CHECK_CALL (t1vr (i), 7, var_d); + CHECK_CALL (t1vr (ll), 8, var_d); +} + +/* Test first argument not type-generic. */ + +float t2f (int a, float x) { return a * x + 1; } +double t2d (int a, double x) { return a * x + 2; } +long double t2l (int a, long double x) { return a * x + 3; } + +#define t2v(a, x) __builtin_tgmath (t2f, t2d, t2l, a, x) + +static void +test_2 (void) +{ + float f = 1; + double d = 2; + long double ld = 3; + int i = 4; + unsigned long long ll = 5; + CHECK_CALL (t2v (1, f), 2, var_f); + CHECK_CALL (t2v (2, d), 6, var_d); + CHECK_CALL (t2v (3, ld), 12, var_ld); + CHECK_CALL (t2v (4, i), 18, var_d); + CHECK_CALL (t2v (5, ll), 27, var_d); +} + +/* Test return type not type-generic. */ + +int t3f (float x) { return x + 1; } +int t3d (double x) { return x + 2; } +int t3l (long double x) { return x + 3; } + +#define t3v(x) __builtin_tgmath (t3f, t3d, t3l, x) + +static void +test_3 (void) +{ + float f = 1; + double d = 2; + long double ld = 3; + short s = 4; + CHECK_CALL (t3v (f), 2, var_i); + CHECK_CALL (t3v (d), 4, var_i); + CHECK_CALL (t3v (ld), 6, var_i); + CHECK_CALL (t3v (s), 6, var_i); +} + +/* Test multiple type-generic arguments. */ + +float t4f (float x, float y) { return 10 * x + y; } +double t4d (double x, double y) { return 100 * x + y; } +long double t4l (long double x, long double y) { return 1000 * x + y; } + +#define t4v(x, y) __builtin_tgmath (t4f, t4d, t4l, x, y) + +static void +test_4 (void) +{ + float f1 = 1; + float f2 = 2; + double d1 = 3; + double d2 = 4; + long double ld = 5; + long int l = 6; + CHECK_CALL (t4v (f1, f2), 12, var_f); + CHECK_CALL (t4v (f2, f1), 21, var_f); + CHECK_CALL (t4v (f1, d1), 103, var_d); + CHECK_CALL (t4v (d2, f2), 402, var_d); + CHECK_CALL (t4v (f1, l), 106, var_d); + CHECK_CALL (t4v (ld, f1), 5001, var_ld); + CHECK_CALL (t4v (l, l), 606, var_d); + CHECK_CALL (t4v (l, ld), 6005, var_ld); +} + +/* Test complex argument, real return type. */ + +float t5f (_Complex float x) { return 1 + __real__ x + 3 * __imag__ x; } +double t5d (_Complex double x) { return 2 + __real__ x + 4 * __imag__ x; } +long double t5l (_Complex long double x) { return 3 + __real__ x + 5 * __imag__ x; } + +#define t5v(x) __builtin_tgmath (t5f, t5d, t5l, x) + +static void +test_5 (void) +{ + float f = 1; + _Complex float cf = 2 + 3i; + double d = 4; + _Complex double cd = 5 + 6i; + long double ld = 7; + _Complex long double cld = 8 + 9i; + int i = 10; + _Complex int ci = 11 + 12i; + CHECK_CALL (t5v (f), 2, var_f); + CHECK_CALL (t5v (cf), 12, var_f); + CHECK_CALL (t5v (d), 6, var_d); + CHECK_CALL (t5v (cd), 31, var_d); + CHECK_CALL (t5v (ld), 10, var_ld); + CHECK_CALL (t5v (cld), 56, var_ld); + CHECK_CALL (t5v (i), 12, var_d); + CHECK_CALL (t5v (ci), 61, var_d); +} + +/* Test complex argument, complex return type. */ + +_Complex float t6f (_Complex float x) { return 1 + x; } +_Complex double t6d (_Complex double x) { return 2 + x; } +_Complex long double t6l (_Complex long double x) { return 3 + x; } + +#define t6v(x) __builtin_tgmath (t6f, t6d, t6l, x) + +static void +test_6 (void) +{ + float f = 1; + _Complex float cf = 2 + 3i; + double d = 4; + _Complex double cd = 5 + 6i; + long double ld = 7; + _Complex long double cld = 8 + 9i; + int i = 10; + _Complex int ci = 11 + 12i; + CHECK_CALL (t6v (f), 2, var_cf); + CHECK_CALL (t6v (cf), 3 + 3i, var_cf); + CHECK_CALL (t6v (d), 6, var_cd); + CHECK_CALL (t6v (cd), 7 + 6i, var_cd); + CHECK_CALL (t6v (ld), 10, var_cld); + CHECK_CALL (t6v (cld), 11 + 9i, var_cld); + CHECK_CALL (t6v (i), 12, var_cd); + CHECK_CALL (t6v (ci), 13 + 12i, var_cd); +} + +/* Test real and complex argument, real return type. */ + +float t7f (float x) { return 1 + x; } +float t7cf (_Complex float x) { return 2 + __real__ x; } +double t7d (double x) { return 3 + x; } +double t7cd (_Complex double x) { return 4 + __real__ x; } +long double t7l (long double x) { return 5 + x; } +long double t7cl (_Complex long double x) { return 6 + __real__ x; } + +#define t7v(x) __builtin_tgmath (t7f, t7d, t7l, t7cf, t7cd, t7cl, x) + +static void +test_7 (void) +{ + float f = 1; + _Complex float cf = 2 + 3i; + double d = 4; + _Complex double cd = 5 + 6i; + long double ld = 7; + _Complex long double cld = 8 + 9i; + int i = 10; + _Complex int ci = 11 + 12i; + CHECK_CALL (t7v (f), 2, var_f); + CHECK_CALL (t7v (cf), 4, var_f); + CHECK_CALL (t7v (d), 7, var_d); + CHECK_CALL (t7v (cd), 9, var_d); + CHECK_CALL (t7v (ld), 12, var_ld); + CHECK_CALL (t7v (cld), 14, var_ld); + CHECK_CALL (t7v (i), 13, var_d); + CHECK_CALL (t7v (ci), 15, var_d); +} + +/* Test real and complex argument, real and complex return type. */ + +float t8f (float x) { return 1 + x; } +_Complex float t8cf (_Complex float x) { return 2 + x; } +double t8d (double x) { return 3 + x; } +_Complex double t8cd (_Complex double x) { return 4 + x; } +long double t8l (long double x) { return 5 + x; } +_Complex long double t8cl (_Complex long double x) { return 6 + x; } + +#define t8v(x) __builtin_tgmath (t8f, t8d, t8l, t8cf, t8cd, t8cl, x) + +static void +test_8 (void) +{ + float f = 1; + _Complex float cf = 2 + 3i; + double d = 4; + _Complex double cd = 5 + 6i; + long double ld = 7; + _Complex long double cld = 8 + 9i; + int i = 10; + _Complex int ci = 11 + 12i; + CHECK_CALL (t8v (f), 2, var_f); + CHECK_CALL (t8v (cf), 4 + 3i, var_cf); + CHECK_CALL (t8v (d), 7, var_d); + CHECK_CALL (t8v (cd), 9 + 6i, var_cd); + CHECK_CALL (t8v (ld), 12, var_ld); + CHECK_CALL (t8v (cld), 14 + 9i, var_cld); + CHECK_CALL (t8v (i), 13, var_d); + CHECK_CALL (t8v (ci), 15 + 12i, var_cd); +} + +/* Test multiple type-generic arguments, real and complex. */ + +float t9f (float x, float y) { return x + 10 * y; } +_Complex float t9cf (_Complex float x, _Complex float y) { return x + 100 * y; } +double t9d (double x, double y) { return x + 1000 * y; } +_Complex double t9cd (_Complex double x, _Complex double y) { return x + 10000 * y; } +long double t9l (long double x, long double y) { return x + 100000 * y; } +_Complex long double t9cl (_Complex long double x, _Complex long double y) { return x + 1000000 * y; } + +#define t9v(x, y) __builtin_tgmath (t9f, t9d, t9l, t9cf, t9cd, t9cl, x, y) + +static void +test_9 (void) +{ + float f = 1; + _Complex float cf = 2 + 3i; + double d = 4; + _Complex double cd = 5 + 6i; + long double ld = 7; + _Complex long double cld = 8 + 9i; + int i = 10; + _Complex int ci = 11 + 12i; + CHECK_CALL (t9v (f, f), 11, var_f); + CHECK_CALL (t9v (f, cf), 201 + 300i, var_cf); + CHECK_CALL (t9v (cf, f), 102 + 3i, var_cf); + CHECK_CALL (t9v (f, i), 10001, var_d); + CHECK_CALL (t9v (i, f), 1010, var_d); + CHECK_CALL (t9v (d, d), 4004, var_d); + CHECK_CALL (t9v (d, cd), 50004 + 60000i, var_cd); + CHECK_CALL (t9v (ld, i), 1000007, var_ld); + CHECK_CALL (t9v (cf, cld), 8000002 + 9000003i, var_cld); + CHECK_CALL (t9v (i, i), 10010, var_d); + CHECK_CALL (t9v (ci, i), 100011 + 12i, var_cd); +} + +/* Test functions rounding result to narrower type. */ + +float t10d (double x) { return 1 + x; } +float t10l (long double x) { return 2 + x; } + +#define t10v(x) __builtin_tgmath (t10d, t10l, x) + +static void +test_10 (void) +{ + float f = 1; + double d = 2; + long double ld = 3; + short s = 4; + CHECK_CALL (t10v (f), 2, var_f); + CHECK_CALL (t10v (d), 3, var_f); + CHECK_CALL (t10v (ld), 5, var_f); + CHECK_CALL (t10v (s), 5, var_f); +} + +int +main (void) +{ + test_1 (); + test_2 (); + test_3 (); + test_4 (); + test_5 (); + test_6 (); + test_7 (); + test_8 (); + test_9 (); + test_10 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-2.c b/gcc/testsuite/gcc.dg/builtin-tgmath-2.c new file mode 100644 index 00000000000..c4140cc2bd5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-tgmath-2.c @@ -0,0 +1,51 @@ +/* Test __builtin_tgmath: valid uses, _FloatN types. */ +/* { dg-do run } */ +/* { dg-options "" } */ +/* { dg-add-options float32 } */ +/* { dg-require-effective-target float32_runtime } */ + +extern void abort (void); +extern void exit (int); + +#define CHECK_CALL(C, E, V) \ + do \ + { \ + if ((C) != (E)) \ + abort (); \ + extern __typeof (C) V; \ + } \ + while (0) + +extern float var_f; +extern double var_d; +extern long double var_ld; +extern _Float32 var_f32; + +float t1f (float x) { return x + 1; } +double t1d (double x) { return x + 2; } +long double t1l (long double x) { return x + 3; } +_Float32 t1f32 (_Float32 x) { return x + 4; } + +#define t1v(x) __builtin_tgmath (t1f, t1d, t1l, t1f32, x) + +static void +test_1 (void) +{ + float f = 1; + double d = 2; + long double ld = 3; + _Float32 f32 = 4; + int i = 5; + CHECK_CALL (t1v (f), 2, var_f); + CHECK_CALL (t1v (d), 4, var_d); + CHECK_CALL (t1v (ld), 6, var_ld); + CHECK_CALL (t1v (f32), 8, var_f32); + CHECK_CALL (t1v (i), 7, var_d); +} + +int +main (void) +{ + test_1 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c b/gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c new file mode 100644 index 00000000000..9016ec742be --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-tgmath-err-1.c @@ -0,0 +1,76 @@ +/* Test __builtin_tgmath: errors that indicate a bad definition of a + type-generic macro rather than bad arguments in a call to it. */ +/* { dg-do compile } */ +/* { dg-options "" } */ + +void *p; +double d; +double unprototyped_d (); +long double unprototyped_ld (); +double variadic_d (double, ...); +long double variadic_ld (long double, ...); +double no_arguments_d (void); +long double no_arguments_ld (void); +double f_d (double); +long double f_ld (long double); +double many_args (double, double, double, double); +int f_i_d (double); +_Complex int f_ci_d (double); +void * f_p_d (double); +double f_d_i (int); +double f_d_ci (_Complex int); +double f_d_p (void *); +long double f_ld_d (double); +_Complex double f_cd_d (double); +double f_d_f (float); +double f_d_dd (double, double); +long double f_ld_ldld (long double, long double); +float f_f_fd (float, double); + +void +test (void) +{ + /* Arguments individually invalid or no consistent number of + arguments followed by those arguments. */ + __builtin_tgmath (); /* { dg-error "too few arguments" } */ + __builtin_tgmath (f_d); /* { dg-error "too few arguments" } */ + __builtin_tgmath (f_d, f_ld); /* { dg-error "too few arguments" } */ + __builtin_tgmath (many_args, many_args, many_args); /* { dg-error "too few arguments" } */ + __builtin_tgmath (many_args, d, d, d, d); /* { dg-error "too few arguments" } */ + __builtin_tgmath (f_ld, many_args, d); /* { dg-error "has wrong number of arguments" } */ + __builtin_tgmath (unprototyped_d, unprototyped_ld, d); /* { dg-error "is unprototyped" } */ + __builtin_tgmath (f_d, unprototyped_ld, d); /* { dg-error "is unprototyped" } */ + __builtin_tgmath (variadic_d, variadic_ld, d); /* { dg-error "variable arguments" } */ + __builtin_tgmath (f_d, variadic_ld, d); /* { dg-error "variable arguments" } */ + __builtin_tgmath (p, p, p); /* { dg-error "is not a function pointer" } */ + __builtin_tgmath (f_d, p, p); /* { dg-error "is not a function pointer" } */ + __builtin_tgmath (no_arguments_d, no_arguments_d, no_arguments_ld); /* { dg-error "has no arguments" } */ + __builtin_tgmath (f_d, no_arguments_d, no_arguments_ld); /* { dg-error "has no arguments" } */ + + /* Invalid varying types of arguments. */ + __builtin_tgmath (f_i_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_ci_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_p_d, f_ld, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_ld, f_i_d, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_ld, f_ci_d, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_ld, f_p_d, 0); /* { dg-error "invalid type-generic return type" } */ + __builtin_tgmath (f_d_i, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */ + __builtin_tgmath (f_d_ci, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */ + __builtin_tgmath (f_d_p, f_ld, 0); /* { dg-error "invalid type-generic type for argument" } */ + __builtin_tgmath (f_ld, f_d_i, 0); /* { dg-error "invalid type-generic type for argument" } */ + __builtin_tgmath (f_ld, f_d_ci, 0); /* { dg-error "invalid type-generic type for argument" } */ + __builtin_tgmath (f_ld, f_d_p, 0); /* { dg-error "invalid type-generic type for argument" } */ + + /* Arguments same type. */ + __builtin_tgmath (f_d, f_d, 0); /* { dg-error "all have the same type" } */ + + /* Missing or invalid type-generic parameter. */ + __builtin_tgmath (f_d, f_ld_d, 0); /* { dg-error "lack type-generic parameter" } */ + __builtin_tgmath (f_d, f_ld, f_cd_d, 0); /* { dg-error "lack type-generic parameter" } */ + __builtin_tgmath (f_d, f_ld, f_d, 0); /* { dg-error "duplicate type-generic parameter type" } */ + + /* Variation not consistent with the identified type-generic + parameter. */ + __builtin_tgmath (f_d, f_ld, f_d_f, 0); /* { dg-error "bad return type for function argument" } */ + __builtin_tgmath (f_d_dd, f_ld_ldld, f_f_fd, 0, 0); /* { dg-error "bad type for argument" } */ +} diff --git a/gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c b/gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c new file mode 100644 index 00000000000..df5655ef402 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-tgmath-err-2.c @@ -0,0 +1,19 @@ +/* Test __builtin_tgmath: errors that indicate bad arguments in a call + to a type-generic macro, non-DFP. */ +/* { dg-do compile } */ +/* { dg-options "" } */ + +float f_f (float); +double f_d (double); +long double f_ld (long double); +void *p; +long double ld; +_Complex float cf; + +void +test (void) +{ + __builtin_tgmath (f_f, f_d, f_ld, p); /* { dg-error "invalid type of argument" } */ + __builtin_tgmath (f_f, f_d, ld); /* { dg-error "no matching function for type-generic call" } */ + __builtin_tgmath (f_f, f_d, cf); /* { dg-error "no matching function for type-generic call" } */ +} diff --git a/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c new file mode 100644 index 00000000000..b94c760c3ec --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp-err.c @@ -0,0 +1,33 @@ +/* Test __builtin_tgmath: errors that indicate bad arguments in a call + to a type-generic macro, DFP involved. */ +/* { dg-do compile } */ +/* { dg-options "" } */ + +float f_f (float); +double f_d (double); +long double f_ld (long double); +_Complex float f_cf (_Complex float); +_Complex double f_cd (_Complex double); +_Complex long double f_cld (_Complex long double); +_Decimal32 f_d32 (_Decimal32); +_Decimal64 f_d64 (_Decimal64); +_Decimal128 f_d128 (_Decimal128); +float f_ff (float, float); +_Complex float f_cfcf (_Complex float, _Complex float); +_Decimal32 f_d32d32 (_Decimal32, _Decimal32); +_Complex float cf; +float f; +_Decimal32 d32; + +void +test (void) +{ + __builtin_tgmath (f_cf, f_cd, f_cld, d32); /* { dg-error "decimal floating-point argument 1 to complex-only type-generic function" } */ + __builtin_tgmath (f_f, f_d, f_ld, d32); /* { dg-error "decimal floating-point argument 1 to binary-only type-generic function" } */ + __builtin_tgmath (f_cfcf, f_d32d32, cf, d32); /* { dg-error "both complex and decimal floating-point arguments to type-generic function" } */ + __builtin_tgmath (f_ff, f_d32d32, f, d32); /* { dg-error "both binary and decimal floating-point arguments to type-generic function" } */ + __builtin_tgmath (f_d32, f_d64, f_d128, cf); /* { dg-error "complex argument 1 to decimal-only type-generic function" } */ + __builtin_tgmath (f_d32, f_d64, f_d128, f); /* { dg-error "binary argument 1 to decimal-only type-generic function" } */ + __builtin_tgmath (f_cfcf, f_d32d32, d32, cf); /* { dg-error "both complex and decimal floating-point arguments to type-generic function" } */ + __builtin_tgmath (f_ff, f_d32d32, d32, f); /* { dg-error "both binary and decimal floating-point arguments to type-generic function" } */ +} diff --git a/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c new file mode 100644 index 00000000000..256a71e68be --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/builtin-tgmath-dfp.c @@ -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); +}