This is the mail archive of the
cygwin
mailing list for the Cygwin project.
x86_64: floating-point environment (i.e. fenv.h). BUG.
- From: Houder <houder at xs4all dot nl>
- To: cygwin at cygwin dot com
- Date: Wed, 01 Aug 2018 12:22:39 +0200
- Subject: x86_64: floating-point environment (i.e. fenv.h). BUG.
Hi Corinna,
Short version of my report (as there is more to say about the
implementation of
"fenv") in Cygwin; this time I restrict myself to a bug in fegetenv() ).
(Note to myself: attach STC)
I am reporting a bug in fegetenv() in winsup/cygwin/fenv.cc. There is no
hurry
in repairing this bug, as "fenv" is hardly ever (never?) used by anyone.
fegetenv() should be modified as follows:
from:
__asm__ volatile ("fnstenv %0" : "=m" (envp->_fpu) : );
if (use_sse)
__asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
return 0
to:
// Henri: copying glibc ...
__asm__ volatile ("fnstenv %0\n"
"fldenv %0" : "=m" (envp->_fpu) : );
if (use_sse)
__asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
return 0;
Yes, you can verify my modification here:
https://sourceware.org/git/?p=glibc.git
(sysdeps/x86_64/fpu/fegetenv.c)
The fnstenv statement "copies" the state of the x87 FPU to memory, at
the same
time MASKING all exceptions ...
However, masking the exceptions is NOT what we desire at this point. For
this
reason fnstenv must be followed by fldenv, which copies "what has been
copied
from the FPU" back to the FPU.
The stc is as follows:
feenableexcept()
...
fegetenv(&fpenv) // save state of fenv (the "control and status
register")
//fesetenv(FE_DFL_ENV) // set default environment: would mask all
exceptions
provoke exception
fesetenv(&fpenv) // restore the previous state of fenv
Note: provoke exception, using
- either feraiseexcept()
- or double d = 1.0; long l = d + 0.4;
Using feraiseexcept() should trigger an exception; it does not.
Regards,
Henri
// gcc -Wall -o STC-FENV STC-FENV.c
// Linux: gcc -Wall -o STC-FENV STC-FENV.c -lm
/*
x86_64:
This stc does not terminate w/ an exception if one is provoked using feraiseexcept(). This is due to a bug in
fegetenv() in winsup/cygwin/fenv.cc.
At first glance, this is weird, as using "double d = 1.0; long l = d + 0.4;" to provoke an exception, results
in triggering the exception.
Floating-point in the Intel processor may either use "SSE" or the x87 FPU. On x86_64, SSE is used (mostly?).
In feraiseexcept() the exception is provoked using the x87 FPU ...
However, in fegetenv(), as result of the bug, all exceptions will have been masked after calling fegetenv().
WoW (x86):
... an entirely different story (x87 FPU and SSE behave differently when exceptions are enabled again after
an exception has been provoked while exceptions WERE being masked; moreover the triggering of the exception
is "delayed" (deferred) in case of the x87 FPU).
*/
/*
Program:
feenableexcept()
...
fegetenv(&fpenv) // save state of fenv (the "control and status register")
//fesetenv(FE_DFL_ENV) set default environment: would mask all exceptions
provoke exception
fesetenv(&fpenv) // restore the previous state of fenv
Note: provoke exception, using either feraiseexcept() or double d = 1.0; long l = d + 0.4;
*/
#if !defined __CYGWIN__
#define _GNU_SOURCE
#endif
#include <fenv.h>
#include <time.h>
#include <stdio.h>
const int xxx = 0x3d;
int main()
{
fenv_t fpenv;
if (feenableexcept(FE_ALL_EXCEPT) == -1) printf("\tfeenableexcept failed.\n");
printf("\tExceptions ENABLED!\n");
printf(">: fegetexcept() = %2x enabled\n", fegetexcept() );
printf("0: fegetexcept() = %2x mask\n", (~fegetexcept() & xxx) );
printf("Getenv(&fpenv) ...\n");
fegetenv(&fpenv);
#if 0
printf("Setenv(FE_DFL_ENV) ...\n"); fesetenv(FE_DFL_ENV); // duck
#else
printf("NO Setenv(FE_DFL_ENV) ...\n");
#endif
printf(">: fegetexcept() = %2x enabled\n", fegetexcept() );
printf("1: fegetexcept() = %2x mask\n", (~fegetexcept() & xxx) );
printf("Raise Exception!\n");
//double d = 1.0; long l = d + 0.4; l = l;
if (feraiseexcept(FE_INEXACT) == -1) printf("\tfeRAISErexcept() failed.\n");
printf("*: fetestexcept() = %2x status flags\n", fetestexcept(FE_ALL_EXCEPT) );
printf(">: fegetexcept() = %2x enabled\n", fegetexcept() );
printf("2: fegetexcept() = %2x mask\n", (~fegetexcept() & xxx) );
printf("Setenv(&fpenv) ...\n");
fesetenv(&fpenv);
printf("*: fetestexcept() = %2x status flags\n", fetestexcept(FE_ALL_EXCEPT) );
printf(">: fegetexcept() = %2x enabled\n", fegetexcept() );
printf("3: fegetexcept() = %2x mask\n", (~fegetexcept() & xxx) );
}
/* Results with the "generic" fenv.cc
Cygwin: ... using feraiseexcept()
64-@@ ./stc-fenv2 <==== Wrong! NO exception! ... Ditto x86 (NO exception!): wrong!
>: fegetexcept() = 3f enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
NO Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled <==== Wrong! See above
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3f enabled
3: fegetexcept() = 0 mask
64-@@ ./stc-fenv2
>: fegetexcept() = 3f enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3f enabled
3: fegetexcept() = 0 mask
Linux: ... using feraiseexcept()
@@ ./stc-fenv2
>: fegetexcept() = 3d enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
NO Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 3d enabled <==== OK
1: fegetexcept() = 0 mask
Raise Exception!
Floating point exception (core dumped)
@@ ./stc-fenv2
>: fegetexcept() = 3d enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3d enabled
3: fegetexcept() = 0 mask
=====
Cygwin: ... using double d = 1.0; long l = d + 0.4
64-@@ ./stc-fenv2
>: fegetexcept() = 3f enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
NO Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled <==== Wrong! See above
1: fegetexcept() = 3d mask
Raise Exception!
Floating point exception (core dumped)
@@ ./stc-fenv2
>: fegetexcept() = 3f enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
NO Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled <==== Wrong! See above
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3f enabled
3: fegetexcept() = 0 mask
64-@@ ./stc-fenv2
>: fegetexcept() = 3f enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3f enabled
3: fegetexcept() = 0 mask
Linux: ... using double d = 1.0; long l = d + 0.4
@@ ./stc-fenv2
>: fegetexcept() = 3d enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
NO Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 3d enabled <==== OK
1: fegetexcept() = 0 mask
Raise Exception!
Floating point exception (core dumped)
@@ ./stc-fenv2
>: fegetexcept() = 3d enabled
0: fegetexcept() = 0 mask
Getenv(&fpenv) ...
Setenv(FE_DFL_ENV) ...
>: fegetexcept() = 0 enabled
1: fegetexcept() = 3d mask
Raise Exception!
*: fetestexcept() = 20 status flags
>: fegetexcept() = 0 enabled
2: fegetexcept() = 3d mask
Setenv(&fpenv) ...
*: fetestexcept() = 0 status flags
>: fegetexcept() = 3d enabled
3: fegetexcept() = 0 mask
*/
--
Problem reports: http://cygwin.com/problems.html
FAQ: http://cygwin.com/faq/
Documentation: http://cygwin.com/docs.html
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple