Subject: standards/143358: nearbyint raises spurious
inexact exception



On Sat, Jan 30, 2010, Kostik Belousov wrote:
> On Sun, Jan 31, 2010 at 02:20:30AM +1100, Bruce Evans wrote:
> > On Sat, 30 Jan 2010 gavin@xxxxxxxxxxx wrote:
> >
> > >Synopsis: nearbyint raises spurious inexact exception
> >
> > >Note that a comment in the function itself in lib/msun/src/s_nearbyint.c
> > >does also suggest that this PR is valid, "We save and restore the
> > >floating-point environment to avoid raising an inexact exception." I've
> > >also verified Solaris 10, Linux and FreeBSD 7.2 show the expected
> > >behaviour.
> >
> > This seems to be a bug in gcc-4.2, or perhaps a bug in the inline asms.
> > gcc-4.2 still works with -O0, but with -O it removes the entire fesetenv()
> > in nearbyint(). The fegetenv() isn't removed though its result is never
> > used. I didn't get anywhere good trying to fix this, but I got to the
> > following worse place of interest: after replacing the fesetenv(&env) by
> > fesetenv(&xenv) where xenv is an invalidly initialized global variable
> > (all 0's), the fegetenv(&env) corrupted the current environment (looks
> > a bit like xenv was used to initialize the current environment).
> >
> > Removing fesetenv() in other contexts would give larger bugs.
>
> While looking at this, I tried to run the example on custom-build
> gcc 4.4.3/binutils 2.20 toolchain, and bumped into the issue fixed
> by
> http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/3eac7944b4112632ad6fb257c7ce31deda72d89f...



Hmm, we should fix that, but I think it's orthogonal to the issue
in the PR.

Here's the code I'm getting with -O2:

nearbyint:
.LFB9:
subq $56, %rsp
.LCFI1:
movsd %xmm0, 8(%rsp)
leaq 16(%rsp), %rdi
call fegetenv
#APP
fldenv 16(%rsp)
ldmxcsr 44(%rsp)
#NO_APP
movsd 8(%rsp), %xmm0
call rint
addq $56, %rsp
ret

gcc isn't removing the fesetenv(); it's actually moving the
fesetenv() to before the rint() call. I believe the bug is
that gcc is using incorrect builtin knowledge that the rint()
function is pure. FreeBSD doesn't declare it this way because
that would be wrong; rint() depends upon and modifies the
floating point environment. When I compile with -fno-builtins,
the resulting code is correct.

I can't think of any good workarounds except using -fno-builtin or
making a _rint() alias for rint() to trick gcc.

This is related to a long-standing and thorny issue with gcc,
namely, that it doesn't understand how to cope with code that
modifies or examines the floating point environment (rounding
modes, exception flags, etc.) Basically, it assumes that all
floating point arithmetic is performed in the default environment,
and has no side effects. Implementing C99's FENV_ACCESS pragma to
address this has been on their todo list for years, but it's a big
task.



Privacy