How to reset FPU in exception handler
Posted on 2000-03-14
Platform: Win32, MS VC++ 6 SP3.
By default in my app, floating point exceptions are masked and handled in hardware by the FPU. However, in certain situations the exception mask is reset by third party code - which subsequently causes software exceptions to be raised (e.g. when a NaN or Infinite value is generated).
This was causing the app to crash (unhandled exception), so I used SetUnhandledExceptionFilter() to set a top level exception handler.
In the case of a floating point exception (e.g. EXCEPTION_FLT_INVALID_OPERATION), the handler resets the FPU and returns EXCEPTION_CONTINUE_EXECUTION to re-execute the failed instruction.
Here's the handler code:
LONG __stdcall ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
LONG lRet = EXCEPTION_CONTINUE_SEARCH;
// By default, most floating point exceptions are handled in hardware.
// However, some third party printer drivers fiddle with the floating point
// control word which unmasks certain exceptions. This causes software
// exceptions to be generated instead - which would crash the program.
// So trap these exceptions, reset the FP package and re-execute the
// failed instruction.
if (pExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_FLT_DENORMAL_OPERAND ||
// Calling _fpreset() (or controlfp()) and returning EXCEPTION_CONTINUE_EXECUTION
// WILL NOT WORK - after returning, the OS will restore the state of the FPU, including
// the control and status words. This will overwrite the changes made by _fpreset()
// so the exception will immediately be thrown again - putting us right back here.
// Can you say 'stack overflow'?
// Call this just in case there is some internal state (i.e. state not stored in
// the FPU) to the floating point package.
// Reset the FPU.
// Where did I get these magic numbers? I wrote a small program that
// just called _fpreset() and then raised an exception using RaiseException().
// I put a break point in the exception handler and wrote down the values.
// Pretty cruddy eh? Well, it seems to work - I just hope it doesn't
// have any side effects.
pExceptionInfo->ContextRecord->FloatSave.ControlWord = 0xffff027f;
pExceptionInfo->ContextRecord->FloatSave.StatusWord = 0xffff0000;
pExceptionInfo->ContextRecord->FloatSave.TagWord = 0xffffffff;
pExceptionInfo->ContextRecord->FloatSave.ErrorOffset = (DWORD)0;
pExceptionInfo->ContextRecord->FloatSave.ErrorSelector = (DWORD)0;
pExceptionInfo->ContextRecord->FloatSave.DataOffset = (DWORD)0;
pExceptionInfo->ContextRecord->FloatSave.DataSelector = 0xffff0000;
lRet = EXCEPTION_CONTINUE_EXECUTION;
if ( s_gpPrevEFilter )
lRet = s_gpPrevEFilter( pExceptionInfo );
My question is: is there a better method to do this?