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.