https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=289232
Bug ID: 289232
Summary: i386 (x87) signal incorrectly reports FPE_FLTRES over
FPE_FLTUND when both are present
Product: Base System
Version: 13.5-RELEASE
Hardware: i386
OS: Any
Status: New
Severity: Affects Many People
Priority: ---
Component: kern
Assignee: [email protected]
Reporter: [email protected]
When an x87 floating-point operation underflows, the result is often also
inexact; so both the #U and #P flags are set in the x87 status register.
IEEE 754 prioritizes FP exception reporting with a hierarchy. When both
UNDERFLOW and INEXACT are present, UNDERFLOW should be reported (as UNDERFLOW
implies INEXACT.)
Unfortunately, the signal handling code appears to prefer INEXACT in this case,
so that the 'si_code' handed to the signal handler indicates FPE_FLTRES instead
of FPE_FLTUND.
This makes detecting underflow in a signal handler impossible when FE_INEXACT
is allowed in conjunction with FE_UNDERFLOW. If you set the floating-pt mask
with fpeenableexcept() to be feenableexcept(FE_UNDERFLOW | FE_INEXACT); the x87
floating-pt status register reported in the machine context only seems to
have #P set (something has cleared out #U). But, if you set it to only
feenableexcept(FE_UNDERFLOW), the same operation correctly reports FPE_FLTUND
in si_code, and the x87 status register only has #U.
I've tried several FreeBSD versions - the problem appears to be a very old one
all the back to FreeBSD 4.5 a least.
Interestingly, when using SSE floating-point, the FPE_FLTUND is properly
reported.
Here's an example program - compile this with -m64 (using SSE regs) and the
proper FPE_FLTUND is reported, compile it with -m32 (which uses x87 operations)
and the incorrect FPE_FLTRES is reported.
#include <fenv.h>
#include <stdio.h>
#include <signal.h>
#include <math.h>
#include <unistd.h>
void sigfpe_handler(int signum, siginfo_t *info, void *context) {
ucontext_t *uc = (ucontext_t *)context;
int si_code = info->si_code;
printf("Caught SIGFPE (Floating-Point Exception)!\n");
printf("Signal info (si_code): %d", info->si_code);
if(si_code == FPE_FLTUND) printf(": FPE_FLTUND\n");
else if (si_code == FPE_FLTRES) printf(": FPE_FLTRES\n");
else printf("\n");
/* The FP state pointer is within the machine-specific context */
/* This is the structure containing both x87 and SSE state */
struct _fpstate *fpregs = (struct _fpstate *)uc->uc_mcontext.mc_fpstate;
if (fpregs) {
// The x87 status word is at the beginning of the _fpstate structure
// The fxsave format places the FPU status word (fsw) at offset 2
//
// On x86-64, the mc_fpstate points to the FXSAVE area, where the
// x87 FPU Status Word is at offset 2.
unsigned short x87_status_word = *(unsigned short *)((char *)fpregs +
2);
printf("x87 FPU Status Word (FSW): 0x%04x\n", x87_status_word);
if(x87_status_word & 0x01) printf(" INVALID OP\n");
if(x87_status_word & 0x02) printf(" DENORMAL OPERAND\n");
if(x87_status_word & 0x04) printf(" ZERO DIVIDE\n");
if(x87_status_word & 0x08) printf(" OVERFLOW\n");
if(x87_status_word & 0x10) printf(" UNDERFLOW\n");
if(x87_status_word & 0x20) printf(" INEXACT\n");
}
_exit(1);
}
int main() {
struct sigaction sa;
sa.sa_sigaction = sigfpe_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGFPE, &sa, NULL);
// Enable traps for floating-point exceptions
// The following code is architecture-specific.
// It enables traps for underflow and inexact exceptions.
// The underflow trap should have priority over the inexact trap.
fedisableexcept(FE_INEXACT);
feenableexcept(FE_UNDERFLOW );
feenableexcept(FE_UNDERFLOW | FE_INEXACT);
double small_num = 1.0E-300;
double large_num = 1.0E+300;
// This operation will cause both an underflow and an inexact result.
double result = small_num / large_num;
printf("Result is: %e\n", result);
return 0;
}
--
You are receiving this mail because:
You are the assignee for the bug.