Float problen running i386 inary on amd64

Bruce Evans brde at optusnet.com.au
Fri Nov 16 15:56:28 PST 2007

On Sat, 17 Nov 2007, Peter Jeremy wrote:

> On Sat, Nov 17, 2007 at 04:53:22AM +1100, Bruce Evans wrote:
>> Behaviour like this should be expected on i386 but not on amd64.  It
>> gives the well-known property of the sin() function, that sin(x) != sin(x)
>> for almost all x (!).  It happens because expressions _may_ be evaluated
>> in extra precision (this is perfectly standard), so identical expressions
>> may sometimes be evaluated in different precisions even, or especially,
>> if they are on the same line.
> Thank you for your detailed analysis.  Hwever, I believe you missed
> the critical point (I may have removed too much reference to the
> actual problem that Pete French saw): I can take a program that was
> statically compiled on FreeBSD/i386, run it in legacy (i386) mode on
> FreeBSD-6.3/amd64 and get different results.
> Another (admittedly contrived) example:
> ...

Ah, that explains it.  This was also a longstanding bug in the Linux
emulator.  linux_setregs() wasn't fixed to use the Linux npx control
word until relatively recently (2005).  Linux libraries used to set
the control word in the C library (crt), which I think is the right
place to initialize it since the correct initialization may depend on
the language, so the bug wasn't so obvious at first.

> This is identical code being executed in supposedly equivalent
> environments giving different results.
> I believe the fix is to initialise the FPU using __INITIAL_NPXCW__ in
> ia32_setregs(), though I'm not sure how difficult this is in reality.

Yes, that is the right fix.  It is moderately difficult to do correctly.
linux_setregs() now just uses fldcw(&control) where control =
__LINUX_NPXCW__.  This depends on bugs to work, since direct accesses
to the FPU in the kernel are not supported.  They cause a DNA trap
which should be fatal.  amd64 is supposed to print a message about
this error, but it apparently doesn't else log files would be fuller.
i386 doesn't even print a message.  npxdna() and fpudna() check related
invariants but not this one.

Correct code would do something like {fpu,npx}xinit(control) to
initialize the control word.  setregs() in RELENG_[1-4] does exactly
that -- npxinit() hides the complications.  Now {fpu,npx}init() is
only called once or twice at boot time for each CPU, and the complications
are a little larger since most initialization is delayed until the DNA
trap ({fpu,npx}init() now mainly sets up a copy of the initial FPU
state in memory for the trap handler to load later, and it cannot set
up per-thread state since the copy in memory is a global default).

The complications for delayed initialization are mainly to optimize
switching of the FPU state for signal handling, but are also used for
exec.  Another complication here is that signal handlers should be
given the default control word.  This is much more broken than for
- there are sysent hooks for sendsig and sigreturn, but none for setting
   registers in sendsig.
- all FreeBSD sendsig's end up using the gobal default initial FPU state
   (if they support switching the FPU state at all).
- all Linux sendsig's are missing support for switching the FPU state.
- suppose that the initial FPU (or even CPU) state is language-dependent
   and this is implemented mainly in the language runtime startup.
   sendsig's would have a hard time determining the languages' defaults
   so as to set them.  The languages would need to set the defaults in
   signal trampolines.


More information about the freebsd-stable mailing list