This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: RFC Migrating PowerPC to IEEE 128-bit Floating Point
- From: Joseph Myers <joseph at codesourcery dot com>
- To: Steven Munroe <munroesj at linux dot vnet dot ibm dot com>
- Cc: Carlos O'Donell <carlos at redhat dot com>, <libc-alpha at sourceware dot org>, Tulio Magno Quites Machado Filho <tuliom at linux dot vnet dot ibm dot com>, Ulrich Weigand <Ulrich dot Weigand at de dot ibm dot com>, Michael Meissner <meissner at linux dot vnet dot ibm dot com>, David Edelsohn <dje dot gcc at gmail dot com>, <jakub at redhat dot com>
- Date: Tue, 13 Oct 2015 23:11:21 +0000
- Subject: Re: RFC Migrating PowerPC to IEEE 128-bit Floating Point
- Authentication-results: sourceware.org; auth=none
- References: <1443640701 dot 8741 dot 27 dot camel at oc7878010663> <alpine dot DEB dot 2 dot 10 dot 1509302017260 dot 21553 at digraph dot polyomino dot org dot uk> <1444759582 dot 31347 dot 25 dot camel at oc7878010663>
On Tue, 13 Oct 2015, Steven Munroe wrote:
> This is complicated. I thank Uli Weigand for developing this break-down
> of long double usage.
>
> Looking at what various targets use today, it seems that all platforms
> supported by glibc use IEEE float and double. As to long double, the
> following options are in use:
> (A) "long double" is 80-bit IEEE extended (no variants): i386, x86_64,
> ia64, m68k -- sysdeps directories: ieee754/ldbl-96
(Actually, there are two variants - m68k has one additional normal
exponent (values with exponent representation 0 are interpreted as if it
were a normal exponent, but with the explicit MSB of the mantissa allowed
to be 0), resulting in the least normal value and the least subnormal
value being half what they are in the Intel variant. This is handled by
e.g. gen-auto-libm-tests, but I strongly suspect that some of the code in
sysdeps/ieee754/ldbl-96 does not handle the m68k variant fully correctly.)
> (B) "long double" is 128-bit IEEE quad (no variants): aarch64, mips64,
> sparc64 -- sysdeps directories: ieee754/ldbl-128
(There's the MIPS variation in the quiet / signaling NaN convention, but
very little needs to care about that.)
Neither of those variations I noted is of any direct relevance to the
addition of __float128 support, except insofar as it probably involves
updating testcases that deal with such variations.
> I suggest we start by creating a new source directory
> (./ieee574/ldbl-f128) specifically for introducing __float128 support.
> This allows us to begin work without impacting to the existing
> platforms.
When it's being used for a type that's not long double, it's not clear
that the name should start ldbl-. But that's a side issue. The main
thing is that before adding any ABIs we should think very carefully about
just what ABIs are appropriate, as well as about the implementation
design. And the same applies to APIs. I think there is a large amount of
work to do before any directories are added.
> In parallel we can begin the work of preparing ldbl-128ibm to move out
> of the ieee-*l name space and prepare for the release where they will
> become of the old version of long double for the power platform.
I don't really see the benefit in such a move. As in, it's not
technically complicated to change a directory name, but when all the other
main implementations for particular floating-point formats are in
sysdeps/ieee754, does it really make sense for one format to have its
implementation sitting off on the side somewhere? The name isn't exactly
accurate, but is the move worth the disruption any more than removing the
useless "sysv/" directory component from sysdeps/unix/sysv/linux/ would
be?
> Finally once the ldbl-f128 target is fully implemented and tested,
> interested platforms can migrate to ldbl-f128 or decide to merge
> ldbl-128 and ldbl-f128 in to a single implementation supporting both
> names/types.
You need separate sysdeps directories for the concepts of "long double is
binary128" and "__float128 functions where that is not long double". The
former is ldbl-128. The case of "*f128 as aliases for *l when long double
is binary128" could probably be done through ldbl-128 as well.
> > I'd be wary of trusting that the set of functions with existing support
> > for variable long double types is in fact the complete set that need
> > fixing for such a change. In particular, as far as I can see, none of
> > that compatibility support is present for printf-like functions in argp.h,
> > err.h and error.h. Unless there's some reason I'm missing why no
> > compatibility support was needed for those functions in the original
> > 2004-6 changes (Jakub?), there's an ABI bug there that we can't now do
> > anything about for the original transition (because old and new binaries
> > use the same symbol versions for those functions, with old binaries
> > expecting long double = double and new binaries expecting distinct long
> > double), but that we can avoid for a future transition. I don't know if
> > any other affected functions are missing such compatibility support.
> >
> Ok we will investigate this. I don't seeing what the issues are with
> argp.h and err.h but looks like the error() and error_at_line() format
> strings could be a problem.
argp_error and argp_failure take a format string and variable arguments.
Thus, they are affected by the ABI for long double - users may
legitimately pass long double arguments to those functions. The same
applies to v?warnx? and v?errx?.
> > On point 5.2 (uses of long double in double functions): the correct fix is
> > to remove that use, along with the multiple-precision code that can make
> > the functions very slow, after doing sufficient error analysis of the
> > initial code in the functions to verify that by itself it yields results
> > with better than 1ulp accuracy, and so well within glibc's accuracy goals.
> > That will mean that long double performance has no effect on performance
> > of double libm functions.
>
> I only found one example of this (./sysdeps/ieee754/dbl-64/slowpow.c)
> and that usage was wrapped in a conditional (#ifdef
> USE_LONG_DOUBLE_FOR_MP).
slowexp.c also has such uses. I think those are the only cases.
> > Note that much of the glibc work could be done in the context of
> > supporting explicit __float128 functions for x86_64 - all GCC versions
> > supported for building glibc already support __float128, and differences
> > between __float128 and TS 18661-4 _Float128 should be easy to work around
> > when building with older GCC even if proper GCC _Float128 support would be
> > a preferred stating point (it allows you e.g. to write _Complex _Float128,
> > which isn't possible with __float128). That wouldn't include anything
> > related to changing the type of long double, and would still have a large
> > amount of work on design, obtaining consensus and implementation, but
> > would allow a large proportion of the work to go in even before the
> > minimum GCC version for building powerpc glibc is increased and so
> > __float128 support can be added for powerpc glibc.
> >
> A I suggested about, creating ./ieee754/ldbl-f128 would be the simplest
> and safest way to start this effort.
I don't think you'll get consensus to add such a directory without
substantial design work first. I can think of several more indirectly
related incremental changes that would be much more likely candidates for
"simplest and safest", once things get to the point of such source code
changes being appropriate.
As one example, a refactoring of the type-generics used (both in installed
headers and internally) for macros such as signbit. Because glibc would
be supporting a set of floating-point types depending on the architecture,
those macros would need to go via macros with architecture-specific
definitions. And because it will no longer be possible to distinguish all
such types using sizeof, it will be necessary to use other features such
as __builtin_types_compatible_p or _Generic. __builtin_types_compatible_p
is GCC-specific, but supported by more GCC versions than C11 _Generic,
which also runs into issues with different handling of qualified types in
different implementations (see DR#423) that would need allowing for. So
even such a small piece involves significant design and analysis work to
determine and justify the design chosen - but is of much lower risk than
things involving adding new functions or sysdeps directories.
(The type-generics in <tgmath.h> are much more complicated than that, but
are one of the many things that would need addressing eventually. See my
recent observations on the WG14 reflector
<http://www.open-std.org/jtc1/sc22/wg14/13831>.)
As another example, going through testcases that test for one or more of
float / double / long double and making those testcases type-generic as
far as possible so that (a) they may test for existing types not currently
covered and (b) it's easy to extend them to __float128 in future and we
have a good understanding of what tests to extend.
As a third example, refactoring the implementations of <complex.h>
functions to make them type-generic using macros. This would mean less
mechanical work in future to update multiple versions of a function when
fixing a bug, as well as making it easier to add __float128 versions of
those functions in future.
I'm not saying that one of those should necessarily be the starting point;
they're simply illustrative examples of some of the simpler pieces that
might appear fairly early given a more detailed design for the issues that
need to be addressed and the rough patch sequence involved in addressing
them. Even if you prototype changes involving new directories before
filling in such pieces, I think such pieces would need to appear early in
the long patch series (and personally I think prefetching implementation
and mainlining work on such pieces is generally beneficial).
Now, here are some examples of issues to consider in the design and
consensus-building process. Where I suggest answers here, I am *not*
asserting that only a particular answer is acceptable - what is or is not
required is determined by the community through a consensus process, and
generally these are cases where if making such changes myself I'd still
want to allow for the community to reach consensus first rather than
acting as libm maintainer to treat something as having consensus in the
absence of discussion. It's only the two principles I stated in
<https://sourceware.org/ml/libc-alpha/2015-09/msg00751.html> about ABI
compatibility requirements that I'm confident asserting as definite
requirements here, that are well-enough established no further discussion
of them is required to understand their application to this case.
* We need to agree that the prior rejection of __float128 support in
<https://sourceware.org/ml/libc-alpha/2004-03/msg00326.html> and
<https://sourceware.org/ml/libc-alpha/2004-05/msg00055.html> no longer
stands, in the light of the existence of standard C bindings in TS
18661-3. I don't think there should be any controversy about superseding
such an old decision, but it should still be clear that we are doing so.
* We need to agree on the appropriateness of APIs from TS 18661 - that is,
to get consensus on a whole set of APIs being appropriate for glibc, so
that doesn't need redebating every time an individual new API is proposed.
The APIs from parts 1, 3 and 4, excluding those relating to decimal
floating point, seem a reasonable set to consider in such a discussion
(with a clear expectation that such APIs could then be added piecemeal,
with individual patches still needing consensus on the merits of the
implementation). (If controversial, the minimal set for present purposes
would be the _Float128 APIs from part 3 that correspond to APIs already
present in glibc for other floating-point types, but I hope it would be
possible to obtain consensus on a wider set of APIs.)
* We need to agree on rules for ABIs corresponding to such APIs. My
suggestion is: where an API meets the ISO C rules for being usable without
including a header, the function needs an exported symbol with the same
name (for example, if we support sinf128 on a platform where long double
is binary128, there does actually need to be a sinf128 exported symbol,
not just a header redirecting calls to use sinl). But if it doesn't meet
such rules, the number of exported symbols should be kept to a minimum
(for example, functions that exist only for use by type-generic macros
should not have such aliases). Functions would go in libc or libm
according to what header has the declaration, unless consistency with
existing functions dictates otherwise (e.g. __isinff128 might go in libc
because other __isinf* functions are there).
* We need to consider what to do about functions not included in TS
18661-3. Some possible cases: (a) function aliases such as drem or gamma
or pow10 shouldn't have *f128 versions. (b) Obsolescent functions such as
scalb shouldn't either (though they'll still need long double = binary128
versions, but not the *f128 APIs). (c) Where TS 18661 has clearly chosen
a different direction, do not add *f128 APIs for the old functions (so no
nexttoward or printf variants - maybe add nextdown, nextup, strfrom
functions instead as preparatory patches - again, long double = binary128
verisons will still be needed). (d) In some cases, *f128 functions as GNU
APIs are clearly reasonable by analogy - for example, strtof128_l,
wcstof128, lgammaf128_r. (e) As a community, we should think especially
carefully about what to do in cases where the above might miss some
functionality, e.g. support for grouping when reading strings to
__float128, or strfmon formatting of __float128 values (the answer might
well end up being that this functionality can only be accessed in the case
where long double = __float128). (f) M_* constants in math.h (needed in
the implementations in any case). (Part of the design process is to get a
*complete* list of such cases for consideration.)
* Would it be best to avoid the new interfaces supporting matherr /
_LIB_VERSION? It's been discussed before that we'd like to deprecate that
functionality; proper deprecation could mean new symbol versions for
affected functions that avoid using the _LIB_VERSION global at all. It
would be unfortunate to add multiple versions of *f128 in quick
succession, first with _LIB_VERSION support and then without. This seems
desirable to discuss even if we end up concluding it's best not to make
this a dependency.
* Many more specific questions regarding details of the design and
implementation approach in different areas.
* While working on it, it's important to pay attention to how the work
relates to the details of TS 18661 and to ongoing discussions in WG14 and
to track cases where possible issues or ambiguities in TS 18661 are found
- and to pass such information back to WG14 for consideration when it's
considered whether to include parts of TS 18661 in the next revision of
the C standard (actually having an implementation of parts of part 3 might
make it more likely to be included in the next revision).
--
Joseph S. Myers
joseph@codesourcery.com