Joseph Myers implemented C99 excess precision support for C in 4.5: https://gcc.gnu.org/legacy-ml/gcc-patches/2008-11/msg00105.html C++ has a similar rule in [expr.pre]p6: "The values of the floating-point operands and the results of floating-point expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby. [Footnote: The cast and assignment operators must still perform their specific conversions.]" Implementing this seems particularly important for users wanting to use 16-bit floating point types in C++.
Created attachment 53667 [details] gcc13-pr107097-wip.patch Untested WIP. ?: handling isn't there, neither call argument, will need to check assignments and whether I got all the cast spots that need tweaking.
I know it's just a WIP, but could we share the setting of may_need_excess_precision between C and C++?
The code is different though. C has that case EQ_EXPR: case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: /* Excess precision for implicit conversions of integers to floating point in C11 and later. */ may_need_excess_precision = (flag_isoc11 && (ANY_INTEGRAL_TYPE_P (type0) || ANY_INTEGRAL_TYPE_P (type1))); break; part in which is from PR82071 and I can't find in the C++ standard a similar change to what has changed from C99 to C11. If you mean to share just case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR: case EXACT_DIV_EXPR: then that sounds something not worth sharing to me.
Created attachment 53669 [details] gcc13-pr107097-wip.patch Updated patch that handles ?:. excess-precision-1.C now passes at runtime... Next problem is excess-precision-2.C, where we rely on volatile long double ld11f = 1.1f; actually being the same as volatile long double ld11f = 1.1L;
Seems in that case we loose the precision in: #0 fold_convert_loc (loc=0, type=<real_type 0x7fffea1582a0 float>, arg=<real_cst 0x7fffea2af480>) at ../../gcc/fold-const.cc:2436 #1 0x000000000049c414 in cxx_eval_constant_expression (ctx=0x7fffffffcc90, t=<excess_precision_expr 0x7fffea290960>, lval=vc_prvalue, non_constant_p=0x7fffffffcdaf, overflow_p=0x7fffffffcdae, jump_target=0x0) at ../../gcc/cp/constexpr.cc:7541 #2 0x000000000049e566 in cxx_eval_outermost_constant_expr (t=<excess_precision_expr 0x7fffea290960>, allow_non_constant=true, strict=true, manifestly_const_eval=false, constexpr_dtor=false, object=<tree 0x0>) at ../../gcc/cp/constexpr.cc:7970 #3 0x000000000049f3b3 in maybe_constant_value (t=<excess_precision_expr 0x7fffea290960>, decl=<tree 0x0>, manifestly_const_eval=false) at ../../gcc/cp/constexpr.cc:8240 #4 0x00000000004e0a81 in cp_fully_fold (x=<excess_precision_expr 0x7fffea290960>) at ../../gcc/cp/cp-gimplify.cc:2367 #5 0x00000000004ef3ad in cp_convert_and_check (type=<real_type 0x7fffea1583f0 long double>, expr=<excess_precision_expr 0x7fffea290960>, complain=3) at ../../gcc/cp/cvt.cc:666 #6 0x000000000042b4e1 in convert_like_internal (convs=0x3b516a0, expr=<excess_precision_expr 0x7fffea290960>, fn=<tree 0x0>, argnum=0, issue_conversion_warnings=true, c_cast_p=false, complain=3) at ../../gcc/cp/call.cc:8549 #7 0x000000000042b765 in convert_like (convs=0x3b516a0, expr=<excess_precision_expr 0x7fffea290960>, fn=<tree 0x0>, argnum=0, issue_conversion_warnings=true, c_cast_p=false, complain=3) at ../../gcc/cp/call.cc:8604 #8 0x000000000042b7d8 in convert_like (convs=0x3b516a0, expr=<excess_precision_expr 0x7fffea290960>, complain=3) at ../../gcc/cp/call.cc:8616 #9 0x000000000043da51 in perform_implicit_conversion_flags (type=<real_type 0x7fffea1583f0 long double>, expr=<excess_precision_expr 0x7fffea290960>, complain=3, flags=262149) at ../../gcc/cp/call.cc:12999 #10 0x0000000000845343 in convert_for_assignment (type=<real_type 0x7fffea29abd0 long double>, rhs=<excess_precision_expr 0x7fffea290960>, errtype=ICR_INIT, fndecl=<tree 0x0>, parmnum=0, complain=3, flags=262149) at ../../gcc/cp/typeck.cc:10332 #11 0x00000000008459f4 in convert_for_initialization (exp=<tree 0x0>, type=<real_type 0x7fffea29abd0 long double>, rhs=<excess_precision_expr 0x7fffea290960>, flags=262149, errtype=ICR_INIT, fndecl=<tree 0x0>, parmnum=0, complain=3) at ../../gcc/cp/typeck.cc:10423 #12 0x000000000085075d in digest_init_r (type=<real_type 0x7fffea29abd0 long double>, init=<excess_precision_expr 0x7fffea290960>, nested=0, flags=262149, complain=3) at ../../gcc/cp/typeck2.cc:1276 #13 0x0000000000850e84 in digest_init_flags (type=<real_type 0x7fffea29abd0 long double>, init=<excess_precision_expr 0x7fffea290960>, flags=262149, complain=3) at ../../gcc/cp/typeck2.cc:1380 #14 0x000000000084eaff in store_init_value (decl=<var_decl 0x7fffea13be10 ld11f>, init=<excess_precision_expr 0x7fffea290960>, cleanups=0x7fffffffd668, flags=262149) at ../../gcc/cp/typeck2.cc:829 #15 0x00000000005270c3 in check_initializer (decl=<var_decl 0x7fffea13be10 ld11f>, init=<excess_precision_expr 0x7fffea290960>, flags=5, cleanups=0x7fffffffd668) at ../../gcc/cp/decl.cc:7466 #16 0x000000000052d248 in cp_finish_decl (decl=<var_decl 0x7fffea13be10 ld11f>, init=<excess_precision_expr 0x7fffea290960>, init_const_expr_p=true, asmspec_tree=<tree 0x0>, flags=5) at ../../gcc/cp/decl.cc:8468 Supposedly we should somewhere (temporarily) strip away the EXCESS_PRECISION_EXPR and readd it after conversion, but it is unclear to me where and under what conditions. Somewhere where we know it is an implicit conversion (because explicit conversion should round to semantic type)? And only when converting (implicitly) to some other REAL_TYPE/COMPLEX_TYPE? I mean, if we say try to initialize a class from some floating point value, we should determine that conversion from the semantic type. On the other side, e.g. for implicit conversion to bool, shouldn't we do the != 0 comparison in excess precision? The C patch strips EXCESS_PRECISION_EXPR unconditionally at the start of the function. So perhaps strip it in convert_for_assignment or perform_implicit_conversion_flags if type is arithmetic type (dunno about enums) only?
Perhaps try to look up the implicit conversion using the semantic type (i.e. with EXCESS_PRECISION_EXPR not stripped) and then if it is a standard conversion (which exact?) from EXCESS_PRECISION to arithmetic/enumeral type or so, strip away the excess precision and actually convert from the excess precision?
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:98e341130f87984af07c884fea773c0bb3cc8821 commit r13-3290-g98e341130f87984af07c884fea773c0bb3cc8821 Author: Jakub Jelinek <jakub@redhat.com> Date: Fri Oct 14 09:28:57 2022 +0200 c++: Implement excess precision support for C++ [PR107097, PR323] The following patch implements excess precision support for C++. Like for C, it uses EXCESS_PRECISION_EXPR tree to say that its operand is evaluated in excess precision and what the semantic type of the expression is. In most places I've followed what the C FE does in similar spots, so e.g. for binary ops if one or both operands are already EXCESS_PRECISION_EXPR, strip those away or for operations that might need excess precision (+, -, *, /) check if the operands should use excess precision and convert to that type and at the end wrap into EXCESS_PRECISION_EXPR with the common semantic type. This patch follows the C99 handling where it differs from C11 handling. There are some cases which needed to be handled differently, the C FE can just strip EXCESS_PRECISION_EXPR (replace it with its operand) when handling explicit cast, but that IMHO isn't right for C++ - the discovery what exact conversion should be used (e.g. if user conversion or standard or their sequence) should be decided based on the semantic type (i.e. type of EXCESS_PRECISION_EXPR), and that decision continues in convert_like* where we pick the right user conversion, again, if say some class has ctor from double and long double and we are on ia32 with standard excess precision promoting float/double to long double, then we should pick the ctor from double. Or when some other class has ctor from just double, and EXCESS_PRECISION_EXPR semantic type is float, we should choose the user ctor from double, but actually just convert the long double excess precision to double and not to float first. We need to make sure even identity conversion converts from excess precision to the semantic one though, but if identity is chained with other conversions, we don't want the identity next_conversion to drop to semantic precision only to widen afterwards. The existing testcases tweaks were for cases on i686-linux where excess precision breaks those tests, e.g. if we have double d = 4.2; if (d == 4.2) then it does the expected thing only with -fexcess-precision=fast, because with -fexcess-precision=standard it is actually double d = 4.2; if ((long double) d == 4.2L) where 4.2L is different from 4.2. I've added -fexcess-precision=fast to some tests and changed other tests to use constants that are exactly representable and don't suffer from these excess precision issues. There is one exception, pr68180.C looks like a bug in the patch which is also present in the C FE (so I'd like to get it resolved incrementally in both). Reduced testcase: typedef float __attribute__((vector_size (16))) float32x4_t; float32x4_t foo(float32x4_t x, float y) { return x + y; } with -m32 -std=c11 -Wno-psabi or -m32 -std=c++17 -Wno-psabi it is rejected with: pr68180.c:2:52: error: conversion of scalar âlong doubleâ to vector âfloat32x4_tâ {aka â__vector(4) floatâ} involves truncation but without excess precision (say just -std=c11 -Wno-psabi or -std=c++17 -Wno-psabi) it is accepted. Perhaps we should pass down the semantic type to scalar_to_vector and use the semantic type rather than excess precision type in the diagnostics. 2022-10-14 Jakub Jelinek <jakub@redhat.com> PR middle-end/323 PR c++/107097 gcc/ * doc/invoke.texi (-fexcess-precision=standard): Mention that the option now also works in C++. gcc/c-family/ * c-common.def (EXCESS_PRECISION_EXPR): Remove comment part about the tree being specific to C/ObjC. * c-opts.cc (c_common_post_options): Handle flag_excess_precision in C++ the same as in C. * c-lex.cc (interpret_float): Set const_type to excess_precision () even for C++. gcc/cp/ * parser.cc (cp_parser_primary_expression): Handle EXCESS_PRECISION_EXPR with REAL_CST operand the same as REAL_CST. * cvt.cc (cp_ep_convert_and_check): New function. * call.cc (build_conditional_expr): Add excess precision support. When type_after_usual_arithmetic_conversions returns error_mark_node, use gcc_checking_assert that it is because of uncomparable floating point ranks instead of checking all those conditions and make it work also with complex types. (convert_like_internal): Likewise. Add NESTED_P argument, pass true to recursive calls to convert_like. (convert_like): Add NESTED_P argument, pass it through to convert_like_internal. For other overload pass false to it. (convert_like_with_context): Pass false to NESTED_P. (convert_arg_to_ellipsis): Add excess precision support. (magic_varargs_p): For __builtin_is{finite,inf,inf_sign,nan,normal} and __builtin_fpclassify return 2 instead of 1, document what it means. (build_over_call): Don't handle former magic 2 which is no longer used, instead for magic 1 remove EXCESS_PRECISION_EXPR. (perform_direct_initialization_if_possible): Pass false to NESTED_P convert_like argument. * constexpr.cc (cxx_eval_constant_expression): Handle EXCESS_PRECISION_EXPR. (potential_constant_expression_1): Likewise. * pt.cc (tsubst_copy, tsubst_copy_and_build): Likewise. * cp-tree.h (cp_ep_convert_and_check): Declare. * cp-gimplify.cc (cp_fold): Handle EXCESS_PRECISION_EXPR. * typeck.cc (cp_common_type): For COMPLEX_TYPEs, return error_mark_node if recursive call returned it. (convert_arguments): For magic 1 remove EXCESS_PRECISION_EXPR. (cp_build_binary_op): Add excess precision support. When cp_common_type returns error_mark_node, use gcc_checking_assert that it is because of uncomparable floating point ranks instead of checking all those conditions and make it work also with complex types. (cp_build_unary_op): Likewise. (cp_build_compound_expr): Likewise. (build_static_cast_1): Remove EXCESS_PRECISION_EXPR. gcc/testsuite/ * gcc.target/i386/excess-precision-1.c: For C++ wrap abort and exit declarations into extern "C" block. * gcc.target/i386/excess-precision-2.c: Likewise. * gcc.target/i386/excess-precision-3.c: Likewise. Remove check_float_nonproto and check_double_nonproto tests for C++. * gcc.target/i386/excess-precision-7.c: For C++ wrap abort and exit declarations into extern "C" block. * gcc.target/i386/excess-precision-9.c: Likewise. * g++.target/i386/excess-precision-1.C: New test. * g++.target/i386/excess-precision-2.C: New test. * g++.target/i386/excess-precision-3.C: New test. * g++.target/i386/excess-precision-4.C: New test. * g++.target/i386/excess-precision-5.C: New test. * g++.target/i386/excess-precision-6.C: New test. * g++.target/i386/excess-precision-7.C: New test. * g++.target/i386/excess-precision-9.C: New test. * g++.target/i386/excess-precision-11.C: New test. * c-c++-common/dfp/convert-bfp-10.c: Add -fexcess-precision=fast as dg-additional-options. * c-c++-common/dfp/compare-eq-const.c: Likewise. * g++.dg/cpp1z/constexpr-96862.C: Likewise. * g++.dg/cpp1z/decomp12.C (main): Use 2.25 instead of 2.3 to avoid excess precision differences. * g++.dg/other/thunk1.C: Add -fexcess-precision=fast as dg-additional-options. * g++.dg/vect/pr64410.cc: Likewise. * g++.dg/cpp1y/pr68180.C: Likewise. * g++.dg/vect/pr89653.cc: Likewise. * g++.dg/cpp0x/variadic-tuple.C: Likewise. * g++.dg/cpp0x/nsdmi-union1.C: Use 4.25 instead of 4.2 to avoid excess precision differences. * g++.old-deja/g++.brendan/copy9.C: Add -fexcess-precision=fast as dg-additional-options. * g++.old-deja/g++.brendan/overload7.C: Likewise.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:16ec267063c8ce60769888d4097bcd158410adc8 commit r13-3291-g16ec267063c8ce60769888d4097bcd158410adc8 Author: Jakub Jelinek <jakub@redhat.com> Date: Fri Oct 14 09:33:23 2022 +0200 c++: Excess precision for ? int : float or int == float [PR107097, PR82071, PR87390] The following incremental patch implements the C11 behavior (for all C++ versions) for cond ? int : float cond ? float : int int cmp float float cmp int where int is any integral type, float any floating point type with excess precision and cmp ==, !=, >, <, >=, <= and <=>. 2022-10-14 Jakub Jelinek <jakub@redhat.com> PR c/82071 PR c/87390 PR c++/107097 gcc/cp/ * cp-tree.h (cp_ep_convert_and_check): Remove. * cvt.cc (cp_ep_convert_and_check): Remove. * call.cc (build_conditional_expr): Use excess precision for ?: with one arm floating and another integral. Don't convert first to semantic result type from integral types. (convert_like_internal): Don't call cp_ep_convert_and_check, instead just strip EXCESS_PRECISION_EXPR before calling cp_convert_and_check or cp_convert. * typeck.cc (cp_build_binary_op): Set may_need_excess_precision for comparisons or SPACESHIP_EXPR with at least one operand integral. Don't compute semantic_result_type if build_type is non-NULL. Call cp_convert_and_check instead of cp_ep_convert_and_check. gcc/testsuite/ * gcc.target/i386/excess-precision-8.c: For C++ wrap abort and exit declarations into extern "C" block. * gcc.target/i386/excess-precision-10.c: Likewise. * g++.target/i386/excess-precision-7.C: Remove. * g++.target/i386/excess-precision-8.C: New test. * g++.target/i386/excess-precision-9.C: Remove. * g++.target/i386/excess-precision-10.C: New test. * g++.target/i386/excess-precision-12.C: New test.
Now implemented.