[PATCH 3/4] Use libc_fe* macros in ldbl-128/e_expl.c.

Adhemerval Zanella adhemerval.zanella@linaro.org
Mon Mar 30 18:12:47 GMT 2020



On 27/03/2020 11:23, Stefan Liebler via Libc-alpha wrote:
> On 3/26/20 3:53 PM, Adhemerval Zanella via Libc-alpha wrote:
>>
>>
>> On 26/03/2020 06:08, Stefan Liebler via Libc-alpha wrote:
>>> On 3/25/20 4:42 PM, Adhemerval Zanella via Libc-alpha wrote:
>>>>
>>>>
>>>> On 25/03/2020 12:07, Adhemerval Zanella wrote:
>>>>>
>>>>>
>>>>> On 25/03/2020 12:00, Adhemerval Zanella wrote:
>>>>>>
>>>>>>
>>>>>> On 25/03/2020 07:13, Stefan Liebler via Libc-alpha wrote:
>>>>>>> Unfortunately, this patch is responsible for testfails on x86_64:
>>>>>>>
>>>>>>> math/test-float128-exp.out:
>>>>>>> Failure: exp (-0x1p-10000): Exception "Underflow" set
>>>>>>> Failure: exp (-0x2p-16384): Exception "Underflow" set
>>>>>>> ...
>>>>>>>
>>>>>>> math/test-float128-cexp.out:
>>>>>>> Failure: Real part of: cexp (0x2p-16384 - 0x4p-1076 i): Exception "Underflow" set
>>>>>>> Failure: Real part of: cexp (0x2p-16384 - 0x8p-152 i): Exception "Underflow" set
>>>>>>
>>>>>> The sysdeps/x86/fpu/fenv_private.h states:
>>>>>>
>>>>>> 296 #ifdef __x86_64__
>>>>>> 297 /* The SSE rounding mode is used by soft-fp (libgcc and glibc) on
>>>>>> 298    x86_64, so that must be set for float128 computations.  */
>>>>>> 299 # define SET_RESTORE_ROUNDF128(RM) \
>>>>>> 300   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_sse, libc_feresetround_sse)
>>>>>>
>>>>>> So
>>>>>>
>>>>>>>> diff --git a/sysdeps/ieee754/ldbl-128/e_expl.c b/sysdeps/ieee754/ldbl-128/e_expl.c
>>>>>>>> index 37c1538c08..104ace1690 100644
>>>>>>>> --- a/sysdeps/ieee754/ldbl-128/e_expl.c
>>>>>>>> +++ b/sysdeps/ieee754/ldbl-128/e_expl.c
>>>>>>>> @@ -66,6 +66,7 @@
>>>>>>>>     #include <inttypes.h>
>>>>>>>>     #include <math-barriers.h>
>>>>>>>>     #include <math_private.h>
>>>>>>>> +#include <fenv_private.h>
>>>>>>>>     #include <math-underflow.h>
>>>>>>>>     #include <stdlib.h>
>>>>>>>>     #include "t_expl.h"
>>>>>>>> @@ -146,9 +147,10 @@ __ieee754_expl (_Float128 x)
>>>>>>>>           union ieee854_long_double ex2_u, scale_u;
>>>>>>>>           fenv_t oldenv;
>>>>>>>>     -      feholdexcept (&oldenv);
>>>>>>>>     #ifdef FE_TONEAREST
>>>>>>>> -      fesetround (FE_TONEAREST);
>>>>>>>> +      libc_feholdexcept_setroundl (&oldenv, FE_TONEAREST);
>>>>>>
>>>>>> Should be libc_feholdexcept_setroundf128.
>>>>>
>>>>> But it does not see to help here, so I don't know what is failing as well.
>>>>
>>>> Ok, so what is happening __sfp_handle_exceptions always use 387 exception
>>>> mode for FP_EX_OVERFLOW and FP_EX_UNDERFLOW:
>>>>
>>>> config/i386/sfp-exceptions.c
>>>>
>>>>    79   if (_fex & FP_EX_OVERFLOW)
>>>>    80     {
>>>>    81       struct fenv temp;
>>>>    82       asm volatile ("fnstenv\t%0" : "=m" (temp));
>>>>    83       temp.__status_word |= FP_EX_OVERFLOW;
>>>>    84       asm volatile ("fldenv\t%0" : : "m" (temp));
>>>>    85       asm volatile ("fwait");
>>>>    86     }
>>>>    87   if (_fex & FP_EX_UNDERFLOW)
>>>>    88     {
>>>>    89       struct fenv temp;
>>>>    90       asm volatile ("fnstenv\t%0" : "=m" (temp));
>>>>    91       temp.__status_word |= FP_EX_UNDERFLOW;
>>>>    92       asm volatile ("fldenv\t%0" : : "m" (temp));
>>>>    93       asm volatile ("fwait");
>>>>    94     }
>>>> Yes this looks like the mentioned disassembly.
>>>> Different that FP_EX_INEXACT, for instance, where __SSE_MATH__ sets
>>>> whether SSE is used or not.
>>>>
>>>> So I think it is not safe to use the SSE variants for libc_*_testf128,
>>>> as for i387 we should use the default_* instead.
>>>>
>>> I've just switched to default_* in sysdeps/x86/fpu/fenv_private.h:
>>> -#ifdef __x86_64__
>>> +#if 0
>>>   /* The SSE rounding mode is used by soft-fp (libgcc and glibc) on
>>>      x86_64, so that must be set for float128 computations.  */
>>>   # define SET_RESTORE_ROUNDF128(RM) \
>>>
>>> But now there are 7 testfails. For some of them, the max.ulp goes up (over 10), but there are even worse cases. Here are shortened excerpts of the out files:
>>> - math/test-float128-clog.out:
>>> Failure: Test: Real part of: clog_towardzero (0x2.82b795e420b281a934c6dd315cb2p-4 + 0xf.cd42a15bf9a361243a89663e81e8p-4 i)
>>>   ulp       :  162259276829213363391578010288127.0000
>>>   max.ulp   :  3.0000
>>> Failure: Test: Real part of: clog_upward (0x2.82b795e420b281a934c6dd315cb2p-4 + 0xf.cd42a15bf9a361243a89663e81e8p-4 i)
>>>   ulp       :  162259276829213363391578010288128.0000
>>>   max.ulp   :  4.0000
>>>
>>> - math/test-float128-clog10.out:
>>> Failure: Test: Real part of: clog10_downward (0x3.bea2bd62e35p-4 + 0xf.8e3d619a8d11bfd30b038eep-4 i)
>>>   ulp       :  4.0000
>>>   max.ulp   :  3.0000
>>> Failure: Test: Real part of: clog10_towardzero (0x2.82b795e420b281a934c6dd315cb2p-4 + 0xf.cd42a15bf9a361243a89663e81e8p-4 i)
>>>   ulp       :  140936617129079063283494433422698.0000
>>>   max.ulp   :  4.0000
>>> Failure: Test: Real part of: clog10_upward (0x2.82b795e420b281a934c6dd315cb2p-4 + 0xf.cd42a15bf9a361243a89663e81e8p-4 i)
>>>   ulp       :  140936617129079063283494433422698.0000
>>>   max.ulp   :  4.0000
>>>
>>> - math/test-float128-jn.out
>>> - math/test-float128-lgamma.out
>>> - math/test-float128-tgamma.out:
>>> something like:
>>>   ulp       :  12.0000
>>>   max.ulp   :  4.0000
>>>
>>> Failure: tgamma_upward (-0x6.ec00000000000008p+8): errno set to 0, expected 34 (ERANGE)
>>>
>>> - math/test-float128-y1.out:
>>> Failure: Test: y1_downward (0x2p+0)
>>>   ulp       :  13.0000
>>>   max.ulp   :  4.0000
>>> Failure: Test: y1_towardzero (0x2p+0)
>>>   ulp       :  6.0000
>>>   max.ulp   :  2.0000
>>> Failure: Test: y1_upward (0x2p+0)
>>>   ulp       :  10.0000
>>>   max.ulp   :  5.0000
>>>
>>> - math/test-float128-yn.out
>>>
>>
>> So it seems the issue is the mix on how libm fenv function, the internal
>> libc_fe*, and libgcc handles the exception register.  The exported fenv
>> operates on both i387 and SSE (since it should work on long double as well),
>> and the internal libc_fe* will set either SSE for float, double, and float128
>> and i387 for long double (as expected).
>>
>> The libgcc, however, will set either SEE or i387 depending of the exception.
>> This broke the assumption of libc_fe* for float128 where either SSE or i387
>> will be used.
>>
>> One option might be to force libgcc to not use its __sfp_handle_exceptions
>> on x86_64 and provide one that uses only SEE operations since libgcc does
>> not use 'long double' on float128 operations.  The patch below does it
>> and applied on top your patches shows no regressions.
> 
> Great news. Thanks Adhemerval.
> I've also successfully build and run the testsuite with your patch on top of mine and with only your patch without mine.
> 
> As e.g. __multf3 or __addtf3 is used in various f128 functions, can you please first commit your patch? Then I will add a reference to this commit id in the commit-message.

I will send a RFC for this patch, we need to check with x86 maintainers
if this the desirable direction and if I got everything right. 

> 
> One other question: Why are the soft-fp functions (for add / multiply) called at all. Are the corresponding hardware instructions not available on all x86_64 machines? Or do we miss a compiler flag?

The float128 on gcc/x86_64 is implemented by soft-fp library in libgcc [1]
and its ABI passes arguments through SSE register [2].

[1] https://stackoverflow.com/questions/26639477/what-exactly-is-a-float128-if-im-using-gcc-4-9-on-x86-64
[2] https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI


More information about the Libc-alpha mailing list