https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126012

            Bug ID: 126012
           Summary: [powerpc64le] Support IEEE-128 long double on FreeBSD
                    (config.gcc / configure / libgcc float128)
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: pkubaj at anongoth dot pl
  Target Milestone: ---

FreeBSD switched powerpc64le's ABI to use IEEE binary128 for long doubles in
https://cgit.freebsd.org/src/commit/?id=255538cd906045095d0c2113ae6c4731ce36c0cf.
For GCC to be ABI-compatible, it must also allow setting long double to
IEEE-128 on FreeBSD. GCC supports this on Linux, but FreeBSD is not wired. This
report collects the summary of changes necessary. [1]-[3] are FreeBSD target
enablement changes that mirror the existing powerpc64le-linux options. [4] is a
general libgfortran fix for any IEEE-128 long double target.

A patch series has been posted to gcc-patches.

--------------------------------------------------------------------------------
[1] gcc/config.gcc — default long double to IEEE quad on powerpc*le
--------------------------------------------------------------------------------
Honour --with-long-double-format=ieee for powerpc*le by setting the IEEE-quad
target defines (as already done for the linux configuration):

     powerpc*le-*-*)
-        tm_file="${tm_file} rs6000/sysv4le.h" ;;
+        tm_file="${tm_file} rs6000/sysv4le.h"
+        if test x$with_long_double_format = xieee; then
+            tm_defines="${tm_defines} RS6000_DEFAULT_LONG_DOUBLE_SIZE=128
TARGET_IEEEQUAD_DEFAULT=1 TARGET_FLOAT128_ENABLE_TYPE=1"
+        fi ;;

Without this, a powerpc64le-freebsd compiler configured
--with-long-double-format=ieee
still defaults long double to the legacy format, mismatching the FreeBSD base
ABI.

--------------------------------------------------------------------------------
[2] gcc/configure.ac — accept --with-long-double-format on powerpc64le-freebsd
--------------------------------------------------------------------------------
configure rejects --with-long-double-format for target:format combinations it
does not whitelist.  Add the FreeBSD powerpc64le entries next to the existing
linux ones (configure regenerated from configure.ac):

   case "$target:$with_long_double_format" in
-  powerpc64le-*-linux*:ieee | powerpc64le-*-linux*:ibm)
+  powerpc64le-*-linux*:ieee | powerpc64le-*-linux*:ibm | \
+  powerpc64le-*-freebsd*:ieee | powerpc64le-*-freebsd*:ibm)
       :
       ;;

--------------------------------------------------------------------------------
[3] libgcc/config.host — build float128 / float128-hw support on FreeBSD ppc64
--------------------------------------------------------------------------------
IEEE-128 long double needs the KFmode soft-float (and optional P9 hardware)
support routines in libgcc.  Add the float128 tmake fragments for FreeBSD
powerpc64*, gated on the same libgcc_cv_powerpc_float128[_hw] checks the linux
configuration uses:

     powerpc64*)
       tmake_file="${tmake_file} rs6000/t-freebsd64"
       md_unwind_header=rs6000/freebsd-unwind.h
+      if test $libgcc_cv_powerpc_float128 = yes; then
+        tmake_file="${tmake_file} rs6000/t-float128"
+      fi
+      if test $libgcc_cv_powerpc_float128_hw = yes; then
+        tmake_file="${tmake_file} rs6000/t-float128-hw"
+      fi
       ;;

Without this, the __*kf* / IEEE-128 helpers are not built into libgcc on
FreeBSD and IEEE-128 long double code fails to link.

--------------------------------------------------------------------------------
[4] libgfortran/kinds-override.h — don't create a redundant REAL(KIND=17)
    when long double is already IEEE-128  (general bug, breaks the build)
--------------------------------------------------------------------------------
On powerpc64le, kinds-override.h unconditionally defines a second 128-bit real
kind, REAL(KIND=17) == _Float128, whenever long double is 16 bytes.  That
assumes long double is IBM double-double, so a *separate* IEEE-128 kind is
needed.  When long double already is IEEE-128 (__LONG_DOUBLE_IEEE128__,
__SIZEOF_LONG_DOUBLE__ == 16) KIND=17 is an exact duplicate of KIND=16, and
because USE_IEC_60559 is not set the generated *_r17.c files resolve their math
through the libquadmath *q entry points (jnq, ynq, fabsq, sqrtq, ...).  With
libquadmath not configured (HAVE_FLOAT128 undefined, so quadmath_weak.h is not
included), those names are undeclared and -- a hard error since GCC 14 --
libgfortran fails to build:

    bessel_r17.c:85:20: error: implicit declaration of function 'jnq'; did you
mean 'jnf'?
    bessel_r17.c:155:20: error: implicit declaration of function 'ynq'
    norm2_r17.c:167:31: error: implicit declaration of function 'fabsq'; did
you mean 'fabsl'?
    norm2_r17.c:181:39: error: implicit declaration of function 'sqrtq'; did
you mean 'sqrtl'?
    ...
    make[1]: *** [all-target-libgfortran] Error 2

(The generated *_r17.c include only "libgfortran.h" -> "kinds.h"; the
*generated*
kinds.h appends `#include "kinds-override.h"`, so the macro reaches the C
files;
visible with `gcc -H -E bessel_r17.c`.  On gcc 12/13 the typedef is the older
"typedef __float128 GFC_REAL_17; / _Complex float __attribute__((mode(KC)))"
form, but the offending #if condition is identical.)

REAL(KIND=17) exists to expose "the 128-bit format that long double is NOT" --
it
is meaningful only when long double is IBM double-double.  When long double is
IEEE-128 it is redundant with KIND=16 and only adds the unavailable *q path.
Fix: only define it when long double is not already IEEE-128 (kept one line for
the kinds.inc grep):

    -#if defined(__powerpc64__)  && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 
&& __SIZEOF_LONG_DOUBLE__ == 16
    +#if defined(__powerpc64__)  && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 
&& __SIZEOF_LONG_DOUBLE__ == 16 && !defined(__LONG_DOUBLE_IEEE128__)

The r17 .c/.F90 then compile empty; KIND=16 == long double == IEEE-128 services
all 128-bit reals via the system libm *l entry points.  The IBM-double-double
long double case (the Linux default) is unaffected.

Reply via email to