diff options
author | Jakub Jelinek <jakub@redhat.com> | 2022-09-27 08:04:06 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2022-09-27 08:18:00 +0200 |
commit | b04208895fed34171eac6bafb60c90048eb1cb0c (patch) | |
tree | dcb54efa0d9259cd794dd7d0b1935c53d564e1f9 /gcc/cp | |
parent | 8be65640e12371571a88100864abd78733a7f7eb (diff) | |
download | gcc-b04208895fed34171eac6bafb60c90048eb1cb0c.zip gcc-b04208895fed34171eac6bafb60c90048eb1cb0c.tar.gz gcc-b04208895fed34171eac6bafb60c90048eb1cb0c.tar.bz2 |
c++: Implement P1467R9 - Extended floating-point types and standard names compiler part except for bfloat16 [PR106652]
The following patch implements the compiler part of C++23
P1467R9 - Extended floating-point types and standard names compiler part
by introducing _Float{16,32,64,128} as keywords and builtin types
like they are implemented for C already since GCC 7, with DF{16,32,64,128}_
mangling.
It also introduces _Float{32,64,128}x for C++ with the
https://github.com/itanium-cxx-abi/cxx-abi/pull/147
proposed mangling of DF{32,64,128}x.
The patch doesn't add anything for bfloat16_t support, as right now
__bf16 type refuses all conversions and arithmetic operations.
The patch wants to keep backwards compatibility with how __float128 has
been handled in C++ before, both for mangling and behavior in binary
operations, overload resolution etc. So, there are some backend changes
where for C __float128 and _Float128 are the same type (float128_type_node
and float128t_type_node are the same pointer), but for C++ they are distinct
types which mangle differently and _Float128 is treated as extended
floating-point type while __float128 is treated as non-standard floating
point type. The various C++23 changes about how floating-point types
are changed are actually implemented as written in the spec only if at least
one of the types involved is _Float{16,32,64,128,32x,64x,128x} (_FloatNx are
also treated as extended floating-point types) and kept previous behavior
otherwise. For float/double/long double the rules are actually written that
they behave the same as before.
There is some backwards incompatibility at least on x86 regarding _Float16,
because that type was already used by that name and with the DF16_ mangling
(but only since GCC 12 and I think it isn't that widely used in the wild
yet). E.g. config/i386/avx512fp16intrin.h shows the issues, where
in C or in GCC 12 in C++ one could pass 0.0f to a builtin taking _Float16
argument, but with the changes that is not possible anymore, one needs
to either use 0.0f16 or (_Float16) 0.0f.
We have also a problem with glibc headers, where since glibc 2.27
math.h and complex.h aren't compilable with these changes. One gets
errors like:
In file included from /usr/include/math.h:43,
from abc.c:1:
/usr/include/bits/floatn.h:86:9: error: multiple types in one declaration
86 | typedef __float128 _Float128;
| ^~~~~~~~~~
/usr/include/bits/floatn.h:86:20: error: declaration does not declare anything [-fpermissive]
86 | typedef __float128 _Float128;
| ^~~~~~~~~
In file included from /usr/include/bits/floatn.h:119:
/usr/include/bits/floatn-common.h:214:9: error: multiple types in one declaration
214 | typedef float _Float32;
| ^~~~~
/usr/include/bits/floatn-common.h:214:15: error: declaration does not declare anything [-fpermissive]
214 | typedef float _Float32;
| ^~~~~~~~
/usr/include/bits/floatn-common.h:251:9: error: multiple types in one declaration
251 | typedef double _Float64;
| ^~~~~~
/usr/include/bits/floatn-common.h:251:16: error: declaration does not declare anything [-fpermissive]
251 | typedef double _Float64;
| ^~~~~~~~
This is from snippets like:
/* The remaining of this file provides support for older compilers. */
# if __HAVE_FLOAT128
/* The type _Float128 exists only since GCC 7.0. */
# if !__GNUC_PREREQ (7, 0) || defined __cplusplus
typedef __float128 _Float128;
# endif
where it hardcodes that C++ doesn't have _Float{16,32,64,128,32x,64x,128x} support nor
{f,F}{16,32,64,128}{,x} literal suffixes nor _Complex _Float{16,32,64,128,32x,64x,128x}.
The patch fixincludes this for now and hopefully if this is committed, then
glibc can change those. The patch changes those
# if !__GNUC_PREREQ (7, 0) || defined __cplusplus
conditions to
# if !__GNUC_PREREQ (7, 0) || (defined __cplusplus && !__GNUC_PREREQ (13, 0))
Another thing is mangling, as said above, Itanium C++ ABI specifies
DF <number> _ as _Float{16,32,64,128} mangling, but GCC was implementing
a mangling incompatible with that starting with DF for fixed point types.
Fixed point was never supported in C++ though, I believe the reason why
the mangling has been added was that due to a bug it would leak into the
C++ FE through decltype (0.0r) etc. But that has been shortly after the
mangling was added fixed (I think in the same GCC release cycle), so we
now reject 0.0r etc. in C++. If we ever need the fixed point mangling,
I think it can be readded but better with a different prefix so that it
doesn't conflict with the published standard manglings. So, this patch
also kills the fixed point mangling and implements the DF <number> _
demangling.
The patch predefines __STDCPP_FLOAT{16,32,64,128}_T__ macros when
those types are available, but only for C++23, while the underlying types
are available in C++98 and later including the {f,F}{16,32,64,128} literal
suffixes (but those with a pedwarn for C++20 and earlier). My understanding
is that it needs to be predefined by the compiler, on the other side
predefining even for older modes when <stdfloat> is a new C++23 header
would be weird. One can find out if _Float{16,32,64,128,32x,64x,128x} is
supported in C++ by
__GNUC__ >= 13 && defined(__FLT{16,32,64,128,32X,64X,128X}_MANT_DIG__)
(but that doesn't work well with older G++ 13 snapshots).
As for std::bfloat16_t, three targets (aarch64, arm and x86) apparently
"support" __bf16 type which has the bfloat16 format, but isn't really
usable, e.g. {aarch64,arm,ix86}_invalid_conversion disallow any conversions
from or to type with BFmode, {aarch64,arm,ix86}_invalid_unary_op disallows
any unary operations on those except for ADDR_EXPR and
{aarch64,arm,ix86}_invalid_binary_op disallows any binary operation on
those. So, I think we satisfy:
"If the implementation supports an extended floating-point type with the
properties, as specified by ISO/IEC/IEEE 60559, of radix (b) of 2, storage
width in bits (k) of 16, precision in bits (p) of 8, maximum exponent (emax)
of 127, and exponent field width in bits (w) of 8, then the typedef-name
std::bfloat16_t is defined in the header <stdfloat> and names such a type,
the macro __STDCPP_BFLOAT16_T__ is defined, and the floating-point literal
suffixes bf16 and BF16 are supported."
because we don't really support those right now.
2022-09-27 Jakub Jelinek <jakub@redhat.com>
PR c++/106652
PR c++/85518
gcc/
* tree-core.h (enum tree_index): Add TI_FLOAT128T_TYPE
enumerator.
* tree.h (float128t_type_node): Define.
* tree.cc (build_common_tree_nodes): Initialize float128t_type_node.
* builtins.def (DEF_FLOATN_BUILTIN): Adjust comment now that
_Float<N> is supported in C++ too.
* config/i386/i386.cc (ix86_mangle_type): Only mangle as "g"
float128t_type_node.
* config/i386/i386-builtins.cc (ix86_init_builtin_types): Use
float128t_type_node for __float128 instead of float128_type_node
and create it if NULL.
* config/i386/avx512fp16intrin.h (_mm_setzero_ph, _mm256_setzero_ph,
_mm512_setzero_ph, _mm_set_sh, _mm_load_sh): Use 0.0f16 instead of
0.0f.
* config/ia64/ia64.cc (ia64_init_builtins): Use
float128t_type_node for __float128 instead of float128_type_node
and create it if NULL.
* config/rs6000/rs6000-c.cc (is_float128_p): Also return true
for float128t_type_node if non-NULL.
* config/rs6000/rs6000.cc (rs6000_mangle_type): Don't mangle
float128_type_node as "u9__ieee128".
* config/rs6000/rs6000-builtin.cc (rs6000_init_builtins): Use
float128t_type_node for __float128 instead of float128_type_node
and create it if NULL.
gcc/c-family/
* c-common.cc (c_common_reswords): Change _Float{16,32,64,128} and
_Float{32,64,128}x flags from D_CONLY to 0.
(shorten_binary_op): Punt if common_type returns error_mark_node.
(shorten_compare): Likewise.
(c_common_nodes_and_builtins): For C++ record _Float{16,32,64,128}
and _Float{32,64,128}x builtin types if available. For C++
clear float128t_type_node.
* c-cppbuiltin.cc (c_cpp_builtins): Predefine
__STDCPP_FLOAT{16,32,64,128}_T__ for C++23 if supported.
* c-lex.cc (interpret_float): For q/Q suffixes prefer
float128t_type_node over float128_type_node. Allow
{f,F}{16,32,64,128} suffixes for C++ if supported with pedwarn
for C++20 and older. Allow {f,F}{32,64,128}x suffixes for C++
with pedwarn. Don't call excess_precision_type for C++.
gcc/cp/
* cp-tree.h (cp_compare_floating_point_conversion_ranks): Implement
P1467R9 - Extended floating-point types and standard names except
for std::bfloat16_t for now. Declare.
(extended_float_type_p): New inline function.
* mangle.cc (write_builtin_type): Mangle float{16,32,64,128}_type_node
as DF{16,32,64,128}_. Mangle float{32,64,128}x_type_node as
DF{32,64,128}x. Remove FIXED_POINT_TYPE mangling that conflicts
with that.
* typeck2.cc (check_narrowing): If one of ftype or type is extended
floating-point type, compare floating-point conversion ranks.
* parser.cc (cp_keyword_starts_decl_specifier_p): Handle
CASE_RID_FLOATN_NX.
(cp_parser_simple_type_specifier): Likewise and diagnose missing
_Float<N> or _Float<N>x support if not supported by target.
* typeck.cc (cp_compare_floating_point_conversion_ranks): New function.
(cp_common_type): If both types are REAL_TYPE and one or both are
extended floating-point types, select common type based on comparison
of floating-point conversion ranks and subranks.
(cp_build_binary_op): Diagnose operation with floating point arguments
with unordered conversion ranks.
* call.cc (standard_conversion): For floating-point conversion, if
either from or to are extended floating-point types, set conv->bad_p
for implicit conversion from larger to smaller conversion rank or
with unordered conversion ranks.
(convert_like_internal): Emit a pedwarn on such conversions.
(build_conditional_expr): Diagnose operation with floating point
arguments with unordered conversion ranks.
(convert_arg_to_ellipsis): Don't promote extended floating-point types
narrower than double to double.
(compare_ics): Implement P1467R9 [over.ics.rank]/4 changes.
gcc/testsuite/
* g++.dg/cpp23/ext-floating1.C: New test.
* g++.dg/cpp23/ext-floating2.C: New test.
* g++.dg/cpp23/ext-floating3.C: New test.
* g++.dg/cpp23/ext-floating4.C: New test.
* g++.dg/cpp23/ext-floating5.C: New test.
* g++.dg/cpp23/ext-floating6.C: New test.
* g++.dg/cpp23/ext-floating7.C: New test.
* g++.dg/cpp23/ext-floating8.C: New test.
* g++.dg/cpp23/ext-floating9.C: New test.
* g++.dg/cpp23/ext-floating10.C: New test.
* g++.dg/cpp23/ext-floating.h: New file.
* g++.target/i386/float16-1.C: Adjust expected diagnostics.
libcpp/
* expr.cc (interpret_float_suffix): Allow {f,F}{16,32,64,128} and
{f,F}{32,64,128}x suffixes for C++.
include/
* demangle.h (enum demangle_component_type): Add
DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE.
(struct demangle_component): Add u.s_extended_builtin member.
libiberty/
* cp-demangle.c (d_dump): Handle
DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE. Don't handle
DEMANGLE_COMPONENT_FIXED_TYPE.
(d_make_extended_builtin_type): New function.
(cplus_demangle_builtin_types): Add _Float entry.
(cplus_demangle_type): For DF demangle it as _Float<N> or
_Float<N>x rather than fixed point which conflicts with it.
(d_count_templates_scopes): Handle
DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE. Just break; for
DEMANGLE_COMPONENT_FIXED_TYPE.
(d_find_pack): Handle DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE.
Don't handle DEMANGLE_COMPONENT_FIXED_TYPE.
(d_print_comp_inner): Likewise.
* cp-demangle.h (D_BUILTIN_TYPE_COUNT): Bump.
* testsuite/demangle-expected: Replace _Z3xxxDFyuVb test
with _Z3xxxDF16_DF32_DF64_DF128_CDF16_Vb. Add
_Z3xxxDF32xDF64xDF128xCDF32xVb test.
fixincludes/
* inclhack.def (glibc_cxx_floatn_1, glibc_cxx_floatn_2,
glibc_cxx_floatn_3): New fixes.
* tests/base/bits/floatn.h: New file.
* fixincl.x: Regenerated.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/call.cc | 130 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 13 | ||||
-rw-r--r-- | gcc/cp/mangle.cc | 67 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 9 | ||||
-rw-r--r-- | gcc/cp/typeck.cc | 172 | ||||
-rw-r--r-- | gcc/cp/typeck2.cc | 25 |
6 files changed, 355 insertions, 61 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 7e9289f..3b7e527 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -1541,6 +1541,22 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, || (underlying_type && same_type_p (to, underlying_type))) && next_conversion (conv)->rank <= cr_promotion) conv->rank = cr_promotion; + + /* A prvalue of floating-point type can be converted to a prvalue of + another floating-point type with a greater or equal conversion + rank ([conv.rank]). A prvalue of standard floating-point type can + be converted to a prvalue of another standard floating-point type. + For backwards compatibility with handling __float128 and other + non-standard floating point types, allow all implicit floating + point conversions if neither type is extended floating-point + type and if at least one of them is, fail if they have unordered + conversion rank or from has higher conversion rank. */ + if (fcode == REAL_TYPE + && tcode == REAL_TYPE + && (extended_float_type_p (from) + || extended_float_type_p (to)) + && cp_compare_floating_point_conversion_ranks (from, to) >= 2) + conv->bad_p = true; } else if (fcode == VECTOR_TYPE && tcode == VECTOR_TYPE && vector_types_convertible_p (from, to, false)) @@ -5842,6 +5858,21 @@ build_conditional_expr (const op_location_t &loc, /* In this case, there is always a common type. */ result_type = type_after_usual_arithmetic_conversions (arg2_type, arg3_type); + if (result_type == error_mark_node + && TREE_CODE (arg2_type) == REAL_TYPE + && TREE_CODE (arg3_type) == REAL_TYPE + && (extended_float_type_p (arg2_type) + || extended_float_type_p (arg3_type)) + && cp_compare_floating_point_conversion_ranks (arg2_type, + arg3_type) == 3) + { + if (complain & tf_error) + error_at (loc, "operands to %<?:%> of types %qT and %qT " + "have unordered conversion rank", + arg2_type, arg3_type); + return error_mark_node; + } + if (complain & tf_warning) do_warn_double_promotion (result_type, arg2_type, arg3_type, "implicit conversion from %qH to %qI to " @@ -7906,6 +7937,27 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, "direct-initialization", totype, TREE_TYPE (expr)); + if (TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE + && TREE_CODE (totype) == REAL_TYPE + && (extended_float_type_p (TREE_TYPE (expr)) + || extended_float_type_p (totype))) + switch (cp_compare_floating_point_conversion_ranks (TREE_TYPE (expr), + totype)) + { + case 2: + pedwarn (loc, 0, "converting to %qH from %qI with greater " + "conversion rank", totype, TREE_TYPE (expr)); + complained = true; + break; + case 3: + pedwarn (loc, 0, "converting to %qH from %qI with unordered " + "conversion ranks", totype, TREE_TYPE (expr)); + complained = true; + break; + default: + break; + } + for (; t ; t = next_conversion (t)) { if (t->kind == ck_user && t->cand->reason) @@ -8531,7 +8583,8 @@ convert_arg_to_ellipsis (tree arg, tsubst_flags_t complain) if (TREE_CODE (arg_type) == REAL_TYPE && (TYPE_PRECISION (arg_type) < TYPE_PRECISION (double_type_node)) - && !DECIMAL_FLOAT_MODE_P (TYPE_MODE (arg_type))) + && !DECIMAL_FLOAT_MODE_P (TYPE_MODE (arg_type)) + && !extended_float_type_p (arg_type)) { if ((complain & tf_warning) && warn_double_promotion && !c_inhibit_evaluation_warnings) @@ -11719,6 +11772,81 @@ compare_ics (conversion *ics1, conversion *ics2) return 1; } + { + /* A conversion in either direction between floating-point type FP1 and + floating-point type FP2 is better than a conversion in the same + direction between FP1 and arithmetic type T3 if + - the floating-point conversion rank of FP1 is equal to the rank of + FP2, and + - T3 is not a floating-point type, or T3 is a floating-point type + whose rank is not equal to the rank of FP1, or the floating-point + conversion subrank of FP2 is greater than the subrank of T3. */ + tree fp1 = from_type1; + tree fp2 = to_type1; + tree fp3 = from_type2; + tree t3 = to_type2; + int ret = 1; + if (TYPE_MAIN_VARIANT (fp2) == TYPE_MAIN_VARIANT (t3)) + { + std::swap (fp1, fp2); + std::swap (fp3, t3); + } + if (TYPE_MAIN_VARIANT (fp1) == TYPE_MAIN_VARIANT (fp3) + && TREE_CODE (fp1) == REAL_TYPE + /* Only apply this rule if at least one of the 3 types is + extended floating-point type, otherwise keep them as + before for compatibility reasons with types like __float128. + float, double and long double alone have different conversion + ranks and so when just those 3 types are involved, this + rule doesn't trigger. */ + && (extended_float_type_p (fp1) + || (TREE_CODE (fp2) == REAL_TYPE && extended_float_type_p (fp2)) + || (TREE_CODE (t3) == REAL_TYPE && extended_float_type_p (t3)))) + { + if (TREE_CODE (fp2) != REAL_TYPE) + { + ret = -ret; + std::swap (fp2, t3); + } + if (TREE_CODE (fp2) == REAL_TYPE) + { + /* cp_compare_floating_point_conversion_ranks returns -1, 0 or 1 + if the conversion rank is equal (-1 or 1 if the subrank is + different). */ + if (IN_RANGE (cp_compare_floating_point_conversion_ranks (fp1, + fp2), + -1, 1)) + { + /* Conversion ranks of FP1 and FP2 are equal. */ + if (TREE_CODE (t3) != REAL_TYPE + || !IN_RANGE (cp_compare_floating_point_conversion_ranks + (fp1, t3), + -1, 1)) + /* FP1 <-> FP2 conversion is better. */ + return ret; + int c = cp_compare_floating_point_conversion_ranks (fp2, t3); + gcc_assert (IN_RANGE (c, -1, 1)); + if (c == 1) + /* Conversion subrank of FP2 is greater than subrank of T3. + FP1 <-> FP2 conversion is better. */ + return ret; + else if (c == -1) + /* Conversion subrank of FP2 is less than subrank of T3. + FP1 <-> T3 conversion is better. */ + return -ret; + } + else if (TREE_CODE (t3) == REAL_TYPE + && IN_RANGE (cp_compare_floating_point_conversion_ranks + (fp1, t3), + -1, 1)) + /* Conversion ranks of FP1 and FP2 are not equal, conversion + ranks of FP1 and T3 are equal. + FP1 <-> T3 conversion is better. */ + return -ret; + } + } + } + if (TYPE_PTR_P (from_type1) && TYPE_PTR_P (from_type2) && TYPE_PTR_P (to_type1) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e4d8920..8779538 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7946,6 +7946,7 @@ extern tree require_complete_type (tree, extern tree complete_type (tree); extern tree complete_type_or_else (tree, tree); extern tree complete_type_or_maybe_complain (tree, tree, tsubst_flags_t); +extern int cp_compare_floating_point_conversion_ranks (tree, tree); inline bool type_unknown_p (const_tree); enum { ce_derived, ce_type, ce_normal, ce_exact }; extern bool comp_except_specs (const_tree, const_tree, int); @@ -8688,6 +8689,18 @@ struct push_access_scope_guard } }; +/* True if TYPE is an extended floating-point type. */ + +inline bool +extended_float_type_p (tree type) +{ + type = TYPE_MAIN_VARIANT (type); + for (int i = 0; i < NUM_FLOATN_NX_TYPES; ++i) + if (type == FLOATN_TYPE_NODE (i)) + return true; + return false; +} + #if CHECKING_P namespace selftest { extern void run_cp_tests (void); diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 75388e9..00d283f 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -2648,63 +2648,24 @@ write_builtin_type (tree type) write_string ("Dd"); else if (type == dfloat128_type_node || type == fallback_dfloat128_type) write_string ("De"); + else if (type == float16_type_node) + write_string ("DF16_"); + else if (type == float32_type_node) + write_string ("DF32_"); + else if (type == float64_type_node) + write_string ("DF64_"); + else if (type == float128_type_node) + write_string ("DF128_"); + else if (type == float32x_type_node) + write_string ("DF32x"); + else if (type == float64x_type_node) + write_string ("DF64x"); + else if (type == float128x_type_node) + write_string ("DF128x"); else gcc_unreachable (); break; - case FIXED_POINT_TYPE: - write_string ("DF"); - if (GET_MODE_IBIT (TYPE_MODE (type)) > 0) - write_unsigned_number (GET_MODE_IBIT (TYPE_MODE (type))); - if (type == fract_type_node - || type == sat_fract_type_node - || type == accum_type_node - || type == sat_accum_type_node) - write_char ('i'); - else if (type == unsigned_fract_type_node - || type == sat_unsigned_fract_type_node - || type == unsigned_accum_type_node - || type == sat_unsigned_accum_type_node) - write_char ('j'); - else if (type == short_fract_type_node - || type == sat_short_fract_type_node - || type == short_accum_type_node - || type == sat_short_accum_type_node) - write_char ('s'); - else if (type == unsigned_short_fract_type_node - || type == sat_unsigned_short_fract_type_node - || type == unsigned_short_accum_type_node - || type == sat_unsigned_short_accum_type_node) - write_char ('t'); - else if (type == long_fract_type_node - || type == sat_long_fract_type_node - || type == long_accum_type_node - || type == sat_long_accum_type_node) - write_char ('l'); - else if (type == unsigned_long_fract_type_node - || type == sat_unsigned_long_fract_type_node - || type == unsigned_long_accum_type_node - || type == sat_unsigned_long_accum_type_node) - write_char ('m'); - else if (type == long_long_fract_type_node - || type == sat_long_long_fract_type_node - || type == long_long_accum_type_node - || type == sat_long_long_accum_type_node) - write_char ('x'); - else if (type == unsigned_long_long_fract_type_node - || type == sat_unsigned_long_long_fract_type_node - || type == unsigned_long_long_accum_type_node - || type == sat_unsigned_long_long_accum_type_node) - write_char ('y'); - else - sorry ("mangling unknown fixed point type"); - write_unsigned_number (GET_MODE_FBIT (TYPE_MODE (type))); - if (TYPE_SATURATING (type)) - write_char ('s'); - else - write_char ('n'); - break; - default: gcc_unreachable (); } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index bb83d1c..3dedd14 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -1129,6 +1129,7 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword) case RID_UNSIGNED: case RID_FLOAT: case RID_DOUBLE: + CASE_RID_FLOATN_NX: case RID_VOID: /* CV qualifiers. */ case RID_CONST: @@ -19716,6 +19717,14 @@ cp_parser_simple_type_specifier (cp_parser* parser, case RID_DOUBLE: type = double_type_node; break; + CASE_RID_FLOATN_NX: + type = FLOATN_NX_TYPE_NODE (token->keyword - RID_FLOATN_NX_FIRST); + if (type == NULL_TREE) + error ("%<_Float%d%s%> is not supported on this target", + floatn_nx_types[token->keyword - RID_FLOATN_NX_FIRST].n, + floatn_nx_types[token->keyword - RID_FLOATN_NX_FIRST].extended + ? "x" : ""); + break; case RID_VOID: type = void_type_node; break; diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 22d834d..4854b98 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -267,6 +267,133 @@ merge_type_attributes_from (tree type, tree other_type) return cp_build_type_attribute_variant (type, attrs); } +/* Compare floating point conversion ranks and subranks of T1 and T2 + types. If T1 and T2 have unordered conversion ranks, return 3. + If T1 has greater conversion rank than T2, return 2. + If T2 has greater conversion rank than T1, return -2. + If T1 has equal conversion rank as T2, return -1, 0 or 1 depending + on if T1 has smaller, equal or greater conversion subrank than + T2. */ + +int +cp_compare_floating_point_conversion_ranks (tree t1, tree t2) +{ + tree mv1 = TYPE_MAIN_VARIANT (t1); + tree mv2 = TYPE_MAIN_VARIANT (t2); + int extended1 = 0; + int extended2 = 0; + + if (mv1 == mv2) + return 0; + + for (int i = 0; i < NUM_FLOATN_NX_TYPES; ++i) + { + if (mv1 == FLOATN_NX_TYPE_NODE (i)) + extended1 = i + 1; + if (mv2 == FLOATN_NX_TYPE_NODE (i)) + extended2 = i + 1; + } + if (extended2 && !extended1) + { + int ret = cp_compare_floating_point_conversion_ranks (t2, t1); + return ret == 3 ? 3 : -ret; + } + + const struct real_format *fmt1 = REAL_MODE_FORMAT (TYPE_MODE (t1)); + const struct real_format *fmt2 = REAL_MODE_FORMAT (TYPE_MODE (t2)); + gcc_assert (fmt1->b == 2 && fmt2->b == 2); + /* For {ibm,mips}_extended_format formats, the type has variable + precision up to ~2150 bits when the first double is around maximum + representable double and second double is subnormal minimum. + So, e.g. for __ibm128 vs. std::float128_t, they have unordered + ranks. */ + int p1 = (MODE_COMPOSITE_P (TYPE_MODE (t1)) + ? fmt1->emax - fmt1->emin + fmt1->p - 1 : fmt1->p); + int p2 = (MODE_COMPOSITE_P (TYPE_MODE (t2)) + ? fmt2->emax - fmt2->emin + fmt2->p - 1 : fmt2->p); + /* The rank of a floating point type T is greater than the rank of + any floating-point type whose set of values is a proper subset + of the set of values of T. */ + if ((p1 > p2 && fmt1->emax >= fmt2->emax) + || (p1 == p2 && fmt1->emax > fmt2->emax)) + return 2; + if ((p1 < p2 && fmt1->emax <= fmt2->emax) + || (p1 == p2 && fmt1->emax < fmt2->emax)) + return -2; + if ((p1 > p2 && fmt1->emax < fmt2->emax) + || (p1 < p2 && fmt1->emax > fmt2->emax)) + return 3; + if (!extended1 && !extended2) + { + /* The rank of long double is greater than the rank of double, which + is greater than the rank of float. */ + if (t1 == long_double_type_node) + return 2; + else if (t2 == long_double_type_node) + return -2; + if (t1 == double_type_node) + return 2; + else if (t2 == double_type_node) + return -2; + if (t1 == float_type_node) + return 2; + else if (t2 == float_type_node) + return -2; + return 0; + } + /* Two extended floating-point types with the same set of values have equal + ranks. */ + if (extended1 && extended2) + { + if ((extended1 <= NUM_FLOATN_TYPES) == (extended2 <= NUM_FLOATN_TYPES)) + { + /* Prefer higher extendedN value. */ + if (extended1 > extended2) + return 1; + else if (extended1 < extended2) + return -1; + else + return 0; + } + else if (extended1 <= NUM_FLOATN_TYPES) + /* Prefer _FloatN type over _FloatMx type. */ + return 1; + else if (extended2 <= NUM_FLOATN_TYPES) + return -1; + else + return 0; + } + + /* gcc_assert (extended1 && !extended2); */ + tree *p; + int cnt = 0; + for (p = &float_type_node; p <= &long_double_type_node; ++p) + { + const struct real_format *fmt3 = REAL_MODE_FORMAT (TYPE_MODE (*p)); + gcc_assert (fmt3->b == 2); + int p3 = (MODE_COMPOSITE_P (TYPE_MODE (*p)) + ? fmt3->emax - fmt3->emin + fmt3->p - 1 : fmt3->p); + if (p1 == p3 && fmt1->emax == fmt3->emax) + ++cnt; + } + /* An extended floating-point type with the same set of values + as exactly one cv-unqualified standard floating-point type + has a rank equal to the rank of that standard floating-point + type. + + An extended floating-point type with the same set of values + as more than one cv-unqualified standard floating-point type + has a rank equal to the rank of double. + + Thus, if the latter is true and t2 is long double, t2 + has higher rank. */ + if (cnt > 1 && mv2 == long_double_type_node) + return -2; + /* Otherwise, they have equal rank, but extended types + (other than std::bfloat16_t) have higher subrank. */ + return 1; +} + /* Return the common type for two arithmetic types T1 and T2 under the usual arithmetic conversions. The default conversions have already been applied, and enumerated types converted to their compatible @@ -337,6 +464,23 @@ cp_common_type (tree t1, tree t2) if (code2 == REAL_TYPE && code1 != REAL_TYPE) return build_type_attribute_variant (t2, attributes); + if (code1 == REAL_TYPE + && (extended_float_type_p (t1) || extended_float_type_p (t2))) + { + tree mv1 = TYPE_MAIN_VARIANT (t1); + tree mv2 = TYPE_MAIN_VARIANT (t2); + if (mv1 == mv2) + return build_type_attribute_variant (t1, attributes); + + int cmpret = cp_compare_floating_point_conversion_ranks (mv1, mv2); + if (cmpret == 3) + return error_mark_node; + else if (cmpret >= 0) + return build_type_attribute_variant (t1, attributes); + else + return build_type_attribute_variant (t2, attributes); + } + /* Both real or both integers; use the one with greater precision. */ if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2)) return build_type_attribute_variant (t1, attributes); @@ -5037,7 +5181,20 @@ cp_build_binary_op (const op_location_t &location, = targetm.invalid_binary_op (code, type0, type1))) { if (complain & tf_error) - error (invalid_op_diag); + { + if (code0 == REAL_TYPE + && code1 == REAL_TYPE + && (extended_float_type_p (type0) + || extended_float_type_p (type1)) + && cp_compare_floating_point_conversion_ranks (type0, + type1) == 3) + { + rich_location richloc (line_table, location); + binary_op_error (&richloc, code, type0, type1); + } + else + error (invalid_op_diag); + } return error_mark_node; } @@ -5907,6 +6064,19 @@ cp_build_binary_op (const op_location_t &location, && (shorten || common || short_compare)) { result_type = cp_common_type (type0, type1); + if (result_type == error_mark_node + && code0 == REAL_TYPE + && code1 == REAL_TYPE + && (extended_float_type_p (type0) || extended_float_type_p (type1)) + && cp_compare_floating_point_conversion_ranks (type0, type1) == 3) + { + if (complain & tf_error) + { + rich_location richloc (line_table, location); + binary_op_error (&richloc, code, type0, type1); + } + return error_mark_node; + } if (complain & tf_warning) { do_warn_double_promotion (result_type, type0, type1, diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index 739097a..d5236d1 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -997,12 +997,25 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain, else if (TREE_CODE (ftype) == REAL_TYPE && TREE_CODE (type) == REAL_TYPE) { - if ((same_type_p (ftype, long_double_type_node) - && (same_type_p (type, double_type_node) - || same_type_p (type, float_type_node))) - || (same_type_p (ftype, double_type_node) - && same_type_p (type, float_type_node)) - || (TYPE_PRECISION (type) < TYPE_PRECISION (ftype))) + if ((extended_float_type_p (ftype) || extended_float_type_p (type)) + ? /* "from a floating-point type T to another floating-point type + whose floating-point conversion rank is neither greater than + nor equal to that of T". + So, it is ok if + cp_compare_floating_point_conversion_ranks (ftype, type) + returns -2 (type has greater conversion rank than ftype) + or [-1..1] (type has equal conversion rank as ftype, possibly + different subrank. Only do this if at least one of the + types is extended floating-point type, otherwise keep doing + what we did before (for the sake of non-standard + backend types). */ + cp_compare_floating_point_conversion_ranks (ftype, type) >= 2 + : ((same_type_p (ftype, long_double_type_node) + && (same_type_p (type, double_type_node) + || same_type_p (type, float_type_node))) + || (same_type_p (ftype, double_type_node) + && same_type_p (type, float_type_node)) + || (TYPE_PRECISION (type) < TYPE_PRECISION (ftype)))) { if (TREE_CODE (init) == REAL_CST) { |