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
setregs:
- 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.

Bruce
_______________________________________________
freebsd-stable@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-stable
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to