[fpc-pascal] EControlC instead of EZeroDivide when using SSE instructions

OBones obones at free.fr
Wed Aug 22 13:47:58 CEST 2012


Jonas Maebe wrote:
>
> OBones wrote on Wed, 22 Aug 2012:
>
>> Olivier SANNIER wrote:
>>> Does any of you have any suggestion as to explain this behavior, and 
>>> best of
>>> all, how to fix it?
>> I went further and changed the code this so that I can get better 
>> details as to where the exception is coming from:
>
> EControlC is the translation of run time error 217. The Win32 
> exception handler also sets error code 217 in case of an unknown 
> exception  (the Win64 exception handler does the same, for that 
> matter). Apparently Win32 is not reporting that division-by-zero as 
> either STATUS_INTEGER_DIVIDE_BY_ZERO or STATUS_FLOAT_DIVIDE_BY_ZERO, 
> while Win64 does do this.
>
> You can try debugging syswin32_i386_exception_handler in 
> rtl/win32/system.pp to see what exception Win32 reports instead.
Thanks for the confirmation as I was heading that way when I posted the 
message, without much hope.
But now that I have put that idea to full length, it became clear that 
the problem is not (entirely) with Win32 but because the exception 
handler does not handle the STATUS_FLOAT_MULTIPLE_FAULTS and 
STATUS_FLOAT_MULTIPLE_TRAPS cases.
And as you said, when it does not know what to do, it gives 217 that 
gets later translated to Control C, which to me is a bit misleading. I'd 
rather have a 202, External Exception.
Anyway, I installed my own exception handler using 
SetUnhandledExceptionFilter, saving the return value and I wrote my own 
"overloaded" version of the exception handler that does this:

function GetMXCSR: Cardinal;
asm
   stmxcsr Result
end;

function MyUnhandledExceptionFilter(excep : PExceptionPointers) : 
Longint;stdcall;
const
   MXCSR_IE = $01;   // invalid operation
   MXCSR_DE = $02;   // denormal
   MXCSR_ZE = $04;   // divide by zero
   MXCSR_OE = $08;   // overflow
   MXCSR_UE = $10;   // underflow
   MXCSR_PE = $20;   // precision
var
   MXCSR: Cardinal;
begin
   WriteLn('MyUnhandledExceptionFilter: ' + IntToHex(GetMXCSR, 2));
   WriteLn(IntToHex(excep^.ExceptionRecord^.ExceptionCode, 8));
   WriteLn(IntToHex(EXCEPTION_FLT_DIVIDE_BY_ZERO, 8));
   if Assigned(PreviousUnhandledExceptionFilter) then
   begin
     if (excep^.ExceptionRecord^.ExceptionCode = 
STATUS_FLOAT_MULTIPLE_FAULTS) or
       (excep^.ExceptionRecord^.ExceptionCode = 
STATUS_FLOAT_MULTIPLE_TRAPS) then
     begin
       MXCSR := GetMXCSR;

       if MXCSR and MXCSR_IE = MXCSR_IE then
         excep^.ExceptionRecord^.ExceptionCode := 
STATUS_FLOAT_INVALID_OPERATION
       else if MXCSR and MXCSR_DE = MXCSR_DE then
         excep^.ExceptionRecord^.ExceptionCode := 
STATUS_FLOAT_DENORMAL_OPERAND
       else if MXCSR and MXCSR_ZE = MXCSR_ZE then
         excep^.ExceptionRecord^.ExceptionCode := 
STATUS_FLOAT_DIVIDE_BY_ZERO
       else if MXCSR and MXCSR_OE = MXCSR_OE then
         excep^.ExceptionRecord^.ExceptionCode := STATUS_FLOAT_OVERFLOW
       else if MXCSR and MXCSR_UE = MXCSR_UE then
         excep^.ExceptionRecord^.ExceptionCode := STATUS_FLOAT_UNDERFLOW
       else if MXCSR and MXCSR_PE = MXCSR_PE then
         excep^.ExceptionRecord^.ExceptionCode := 
STATUS_FLOAT_INEXACT_RESULT;
     end;

     Result := PreviousUnhandledExceptionFilter(excep);
   end;
end;

I know that I hide the case where there might be two errors at once, but 
strangely enough, the MXCSR register only contained one exception bit 
set in my case, so I don't understand why Win32 is giving a "multiple 
traps" error.

So right, now, using this I'm getting the appropriate exception, I'm 
just left wondering if this code (or a similar one) should be added to 
FreePascal so that other people are not having the same issue as I'm having.

Note to others: this is only ever useful in Win32 because Win64 does not 
use the handlers set by SetUnhandledExceptionFilter

Regards
Olivier



More information about the fpc-pascal mailing list