[PATCH] fortran, libgfortran: Avoid using libquadmath for glibc 2.26+

2022-06-23 Thread Jakub Jelinek via Fortran
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+

2022-06-23 Thread Harald Anlauf via Fortran

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+

2022-06-23 Thread Jakub Jelinek via Fortran
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