Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 30 Jan 2010 14:01:57 -0500
From:      David Schultz <das@freebsd.org>
To:        Kostik Belousov <kostikbel@gmail.com>
Cc:        gavin@freebsd.org, freebsd-standards@freebsd.org, freebsd-amd64@freebsd.org
Subject:   Re: standards/143358: nearbyint raises spurious inexact exception
Message-ID:  <20100130190157.GB63954@zim.MIT.EDU>
In-Reply-To: <20100130181145.GA3877@deviant.kiev.zoral.com.ua>
References:  <201001301251.o0UCpSEP077706@freefall.freebsd.org> <20100131014341.J78095@delplex.bde.org> <20100130181145.GA3877@deviant.kiev.zoral.com.ua>

next in thread | previous in thread | raw e-mail | index | archive | help
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@freebsd.org 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.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20100130190157.GB63954>