Web lists-archives.com

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