[PATCH] fortran, libgfortran: Avoid using libquadmath for glibc 2.26+
Hi! As mentioned by Joseph in PR105101, glibc 2.26 or later has on x86 (both -m32/-m64), powerpc64le, ia64 and mips support for *f128 math/complex APIs plus strtof128 and strfromf128, and these APIs allow us to avoid libquadmath for Fortran purposes on these architectures, replace *q math/complex APIs, strtof128 instead of strtoflt128 and, while strfromf128 unfortunately isn't a perfect replacement to quadmath_snprintf, it can be made to work. The advantage of this is that when configured against such glibcs (2.26 is now almost 5 years old), we can avoid linking against an extra shared library and the math support in glibc is maintained better than libquadmath. We need both a compiler change (so that for glibc 2.26+ it uses *f128 APIs instead of *q) and library change. The above mentioned problem with strfromf128 is that the strfrom* functions are severely restricted versions of snprintf. In libgfortran, we handle !isfinite differently and just use snprintf/quadmath_snprintf for %+-#.*{L,Q}{f,e} printing. strfrom* doesn't allow +, -, # modifiers and it only supports .34 or similar precision, not .* . The L/Q etc. letters are omitted. The + is there to force + sign at the start if it is positive. Workaround in the patch is to add the + at the start manually for !signbit (val). The - (left alignment instead of right) I don't understand why we need it, when minimum field width isn't specified (for strfrom* can't be specified), no padding is ever added anywhere I believe. The # is to force adding . - workaround is to search for first . or e or '\0' character, if it is '\0', just append ., if it is e, insert . before e and memmove the rest (which is just a few bytes, e, +/- and at most a few digits) one byte later. The .* case is handled by creating the format string for strfrom* by snprintf into a temporary buffer. So far lightly tested on x86_64-linux with glibc 2.35 (removed libgfortran dirs, rebuilt stage3 f951 and make all-target-libgfortran + make check-gfortran), ok for trunk if it passes full testing? On powerpc64le-linux, guess we'll need to test 3 configurations, glibc < 2.26, glibc 2.26 to 2.31 and glibc 2.32 and later. 2022-06-23 Jakub Jelinek gcc/fortran/ * gfortran.h (gfc_real_inf0: Add c__Float128 bitfield. * trans-types.h (gfc_real16_is_float128): Update comment. (gfc_real16_is__Float128): Declare. * trans-types.cc (gfc_real16_is__Float128): Define. (gfc_init_kinds): When building powerpc64le-linux libgfortran on glibc 2.26 to 2.31, set gfc_real16_is__Float128 and c__Float128 instead of gfc_real16_is_float128 and c_float128. (gfc_build_real_type): Don't set c_long_double if c__Float128. Set gfc_real16_is__Float128 and c__Float128 instead of gfc_real16_is_float128 and c_float128 on glibc 2.26 or later. (gfc_init_types): Handle c__Float128 like c_float128. * trans-intrinsic.cc (builtin_decl_for_precision): Handle gfc_real16_is__Float128 like gfc_real16_is_float128. (gfc_builtin_decl_for_float_kind): Handle c__Float128 like c_float128. (gfc_build_intrinsic_lib_fndecls): For gfc_real16_is__Float128 use *f128 APIs rather than *q APIs used for gfc_real16_is_float128. (gfc_get_intrinsic_lib_fndecl): Likewise. * trans-expr.cc (gfc_conv_power_op): Handle gfc_real16_is__Float128 like gfc_real16_is_float128. libgfortran/ * configure.ac: Check for strtof128 and strfromf128. Check for math and complex *f128 functions. Set have__Float128_libc_support to yes if *f128 support is around, for --enable-libquadmath-support default to no rather than yes if have__Float128_libc_support is yes. * acinclude.m4 (LIBGFOR_CHECK_FLOAT128): If libquadmath support isn't enabled and have__Float128_libc_support is yes, test _Float128/_Complex _Float128 support and define and subst HAVE__FLOAT128. * Makefile.am (kinds.h): Pass @HAVE__FLOAT128@ as an extra mk-kinds-h.sh argument. * mk-kinds-h.sh: Accept 4th have__float128 argument, if it is yes, use _Float128/_Complex _Float128 types insted of __float128 and _Complex float __attribute__((mode(TC))), use f128 suffix instead of q and define GFC_REAL_16_IS__FLOAT128 instead of GFC_REAL_16_IS_FLOAT128. * kinds-override.h: Add consistency check for GFC_REAL_16_IS__FLOAT128. * libgfortran.h (GFC_REAL_16_INFINITY, GFC_REAL_16_QUIET_NAN): Define for GFC_REAL_16_IS__FLOAT128. * caf/single.c (convert_type): Use _Float128/_Complex _Float128 for GFC_REAL_16_IS__FLOAT128. For HAVE_GFC_REAL_10 when HAVE_GFC_REAL_16 isn't defined use _Complex long double instead of long double. * ieee/issignaling_fallback.h (__issignalingf128): Handle GFC_REAL_16_IS__FLOAT128. (issignaling): Likewise. * ieee/ieee_helper.c: Handle GFC_REAL_16_I
Re: [PATCH] fortran, libgfortran: Avoid using libquadmath for glibc 2.26+
Hi Jakub, Am 23.06.22 um 14:04 schrieb Jakub Jelinek via Gcc-patches: Hi! As mentioned by Joseph in PR105101, glibc 2.26 or later has on x86 (both -m32/-m64), powerpc64le, ia64 and mips support for *f128 math/complex APIs plus strtof128 and strfromf128, and these APIs allow us to avoid libquadmath for Fortran purposes on these architectures, replace *q math/complex APIs, strtof128 instead of strtoflt128 and, while strfromf128 unfortunately isn't a perfect replacement to quadmath_snprintf, it can be made to work. The advantage of this is that when configured against such glibcs (2.26 is now almost 5 years old), we can avoid linking against an extra shared library and the math support in glibc is maintained better than libquadmath. We need both a compiler change (so that for glibc 2.26+ it uses *f128 APIs instead of *q) and library change. this is quite an important change in the gfortran ABI, as it will require recompilation of (library) code using quad precision. Not that I am particularly affected, but this should be highlighted for users. Am I right in assuming that this also effectively fixes PR46539? (Add -static-libquadmath). So far lightly tested on x86_64-linux with glibc 2.35 (removed libgfortran dirs, rebuilt stage3 f951 and make all-target-libgfortran + make check-gfortran), ok for trunk if it passes full testing? I did not look into all details, but noticed the following: --- gcc/fortran/trans-intrinsic.cc.jj 2022-05-16 11:14:57.587427707 +0200 +++ gcc/fortran/trans-intrinsic.cc 2022-06-23 11:42:03.355899271 +0200 @@ -155,7 +155,7 @@ builtin_decl_for_precision (enum built_i else if (precision == TYPE_PRECISION (double_type_node)) i = m->double_built_in; else if (precision == TYPE_PRECISION (long_double_type_node) - && (!gfc_real16_is_float128 + && ((!gfc_real16_is_float128 & !gfc_real16_is__Float128) Shouldn't this be && instead of & ? || long_double_type_node != gfc_float128_type_node)) i = m->long_double_built_in; else if (precision == TYPE_PRECISION (gfc_float128_type_node)) @@ -175,7 +175,7 @@ gfc_builtin_decl_for_float_kind (enum bu { int i = gfc_validate_kind (BT_REAL, kind, false); - if (gfc_real_kinds[i].c_float128) + if (gfc_real_kinds[i].c_float128 || gfc_real_kinds[i].c__Float128) { /* For _Float128, the story is a bit different, because we return a decl to a library function rather than a built-in. */ @@ -688,7 +688,7 @@ gfc_build_intrinsic_lib_fndecls (void) gfc_intrinsic_map_t *m; tree quad_decls[END_BUILTINS + 1]; - if (gfc_real16_is_float128) + if (gfc_real16_is_float128 || gfc_real16_is__Float128) { /* If we have soft-float types, we create the decls for their C99-like library functions. For now, we only handle _Float128 @@ -739,7 +739,10 @@ gfc_build_intrinsic_lib_fndecls (void) builtin_decl_for_float_type(). The others are all constructed by gfc_get_intrinsic_lib_fndecl(). */ #define OTHER_BUILTIN(ID, NAME, TYPE, CONST) \ - quad_decls[BUILT_IN_ ## ID] = define_quad_builtin (NAME "q", func_ ## TYPE, CONST); +quad_decls[BUILT_IN_ ## ID] \ + = define_quad_builtin (gfc_real16_is__Float128 \ +? NAME "f128" : NAME "q", func_ ## TYPE, \ +CONST); #include "mathbuiltins.def" @@ -751,8 +754,9 @@ gfc_build_intrinsic_lib_fndecls (void) /* There is one built-in we defined manually, because it gets called with builtin_decl_for_precision() or builtin_decl_for_float_type() even though it is not an OTHER_BUILTIN: it is SQRT. */ -quad_decls[BUILT_IN_SQRT] = define_quad_builtin ("sqrtq", func_1, true); - +quad_decls[BUILT_IN_SQRT] + = define_quad_builtin (gfc_real16_is__Float128 +? "sqrtf128" : "sqrtq", func_1, true); } /* Add GCC builtin functions. */ @@ -775,7 +779,7 @@ gfc_build_intrinsic_lib_fndecls (void) m->complex10_decl = builtin_decl_explicit (m->complex_long_double_built_in); - if (!gfc_real16_is_float128) + if (!gfc_real16_is_float128 && !gfc_real16_is__Float128) { if (m->long_double_built_in != END_BUILTINS) m->real16_decl = builtin_decl_explicit (m->long_double_built_in); @@ -876,6 +880,9 @@ gfc_get_intrinsic_lib_fndecl (gfc_intrin else if (gfc_real_kinds[n].c_float128) snprintf (name, sizeof (name), "%s%s%s", ts->type == BT_COMPLEX ? "c" : "", m->name, "q"); + else if (gfc_real_kinds[n].c__Float128) + snprintf (name, sizeof (name), "%s%s%s", + ts->type == BT_COMPLEX ? "c" : "", m->name, "f128"); else gcc_unreachable (); } --- gcc/fortran/trans-expr.cc.jj2022-04-23 10:10:51.146097072 +0200 +++ gcc/fortran/trans-expr.cc 2022-06-23 11:49:31.191964727 +0200
Re: [PATCH] fortran, libgfortran: Avoid using libquadmath for glibc 2.26+
On Thu, Jun 23, 2022 at 10:49:55PM +0200, Harald Anlauf wrote: > > We need both a compiler change (so that for glibc 2.26+ it uses *f128 APIs > > instead of *q) and library change. > > this is quite an important change in the gfortran ABI, as it will > require recompilation of (library) code using quad precision. > Not that I am particularly affected, but this should be highlighted > for users. We currently use %rename lib liborig *lib: %{static-libgfortran:--as-needed} -lquadmath %{static-libgfortran:--no-as-needed} -lm %(libgcc) %(liborig) in libgfortran.spec (on targets where we do configure in libquadmath). So, I believe the patch as is is an ABI change for *.o files if they use real(kind=16) math functions (one needs to recompile them), but not for linked shared libraries or executables, because the above aranges for them to be linked with -lquadmath or for -static-libgfortran with --as-needed -lquadmath --no-as-needed. The former adds libquadmath.so.0 automatically to anything gfortran links, the latter to anything that actually needs it (i.e. has undefined math/complex *q symbols). Note, libgfortran.so.5 itself is ABI compatible, just no longer has DT_NEEDED libquadmath.so.0 and uses the *f128 APIs + str{to,from}f128 instead of *q APIs + strtoflt128 and quadmath_snprintf. Now, what we could do if we'd want to also preserve *.o file compatibility, would be for gcc configured on glibc 2.26+ change libgfortran.spec to *lib: --as-needed -lquadmath --no-as-needed -lm %(libgcc) %(liborig) so that we even without -static-libgfortran link in libquadmath.so.0 only if it is needed. So, if there will be any gcc <= 12 compiled *.o files or *.o files compiled by gcc 13 if it was configured against glibc 2.25 or older, we'd link -lquadmath in, but if there are just *.o files referencing *f128 symbols, we wouldn't. > Am I right in assuming that this also effectively fixes PR46539? > (Add -static-libquadmath). That was one of the intents of the patch. But sure, it doesn't introduce -static-libquadmath, nor arranges for -static-libgfortran to link libquadmath statically, just in some cases (gcc configured on glibc 2.26 or later) and when everything that calls real(kind=16) math functions has been recompiled arranges for libquadmath not to be used at all. > > --- gcc/fortran/trans-intrinsic.cc.jj 2022-05-16 11:14:57.587427707 > > +0200 > > +++ gcc/fortran/trans-intrinsic.cc 2022-06-23 11:42:03.355899271 +0200 > > @@ -155,7 +155,7 @@ builtin_decl_for_precision (enum built_i > > else if (precision == TYPE_PRECISION (double_type_node)) > > i = m->double_built_in; > > else if (precision == TYPE_PRECISION (long_double_type_node) > > - && (!gfc_real16_is_float128 > > + && ((!gfc_real16_is_float128 & !gfc_real16_is__Float128) > > Shouldn't this be && instead of & ? You're right, will fix. Jakub