Browse Source

* improved software floating point exception handling in the rtl

git-svn-id: trunk@43163 -
florian 5 years ago
parent
commit
c189af0e3d
5 changed files with 60 additions and 12 deletions
  1. 41 2
      rtl/arm/arm.inc
  2. 7 1
      rtl/arm/mathu.inc
  3. 1 0
      rtl/arm/thumb.inc
  4. 10 9
      rtl/inc/genmath.inc
  5. 1 0
      rtl/linux/arm/sighnd.inc

+ 41 - 2
rtl/arm/arm.inc

@@ -83,9 +83,9 @@ const
   FPSCR_UFC = 1 shl 3;
   FPSCR_UFC = 1 shl 3;
   FPSCR_IXC = 1 shl 4;
   FPSCR_IXC = 1 shl 4;
   FPSCR_IDC = 1 shl 7;
   FPSCR_IDC = 1 shl 7;
+  FPSCR_EXCEPTIONS = FPSCR_IOC or FPSCR_DZC or FPSCR_OFC or FPSCR_UFC or FPSCR_IXC or FPSCR_IDC;
 
 
-
-procedure fpc_throwfpuexception;[public,alias:'FPC_THROWFPUEXCEPTION'];
+procedure RaisePendingExceptions;
   var
   var
     fpscr : longint;
     fpscr : longint;
     f: TFPUException;
     f: TFPUException;
@@ -112,6 +112,34 @@ procedure fpc_throwfpuexception;[public,alias:'FPC_THROWFPUEXCEPTION'];
   end;
   end;
 
 
 
 
+procedure fpc_throwfpuexception;[public,alias:'FPC_THROWFPUEXCEPTION'];
+  var
+    fpscr : dword;
+    f: TFPUException;
+  begin
+    { at this point, we know already, that an exception will be risen }
+    fpscr:=getfpscr;
+
+    { check, if the exception is masked, as ARM without hardware exceptions have no masking functionality,
+      we use the software mask }
+    if ((fpscr and FPSCR_DZC) <> 0) and (exZeroDivide in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_DZC);
+    if ((fpscr and FPSCR_OFC) <> 0) and (exOverflow in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_OFC);
+    if ((fpscr and FPSCR_UFC) <> 0) and (exUnderflow in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_UFC);
+    if ((fpscr and FPSCR_IOC) <> 0) and (exInvalidOp in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_IOC);
+    if ((fpscr and FPSCR_IXC) <> 0) and (exPrecision in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_IXC);
+    if ((fpscr and FPSCR_IDC) <> 0) and (exDenormalized in softfloat_exception_mask) then
+      fpscr:=fpscr and not(FPSCR_IDC);
+    setfpscr(fpscr);
+    if (fpscr and FPSCR_EXCEPTIONS)<>0 then
+      RaisePendingExceptions;
+  end;
+
+
 Procedure SysInitFPU;{$ifdef SYSTEMINLINE}inline;{$endif}
 Procedure SysInitFPU;{$ifdef SYSTEMINLINE}inline;{$endif}
 begin
 begin
   { Enable FPU exceptions, but disable INEXACT, UNDERFLOW, DENORMAL }
   { Enable FPU exceptions, but disable INEXACT, UNDERFLOW, DENORMAL }
@@ -133,7 +161,18 @@ begin
 {$endif}
 {$endif}
     fmxr fpscr,r0
     fmxr fpscr,r0
   end;
   end;
+  softfloat_exception_mask:=[float_flag_underflow,float_flag_inexact,float_flag_denormal];
+  softfloat_exception_flags:=[];
 end;
 end;
+
+{$define FPC_SYSTEM_HAS_SYSRESETFPU}
+Procedure SysResetFPU;{$ifdef SYSTEMINLINE}inline;{$endif}
+begin
+  softfloat_exception_flags:=[];
+  setfpscr(getfpscr and not(FPSCR_EXCEPTIONS));
+end;
+
+
 {$endif}
 {$endif}
 {$endif}
 {$endif}
 
 

+ 7 - 1
rtl/arm/mathu.inc

@@ -250,7 +250,12 @@ function VFPCw2ExceptionMask(cw: dword): TFPUExceptionMask;
 
 
 function GetExceptionMask: TFPUExceptionMask;
 function GetExceptionMask: TFPUExceptionMask;
   begin
   begin
-    Result:=VFPCw2ExceptionMask(VFP_GetCW);
+    { some ARM CPUs ignore writing to the hardware mask and just return 0, so we need to return
+      the softfloat mask which should be in sync with the hard one }
+    if VFP_GetCW=0 then
+      Result:=softfloat_exception_mask
+    else
+      Result:=VFPCw2ExceptionMask(VFP_GetCW);
   end;
   end;
 
 
 
 
@@ -290,6 +295,7 @@ procedure ClearExceptions(RaisePending: Boolean =true);
   begin
   begin
     { RaisePending has no effect on ARM, it always raises them at the correct location }
     { RaisePending has no effect on ARM, it always raises them at the correct location }
     VFP_SetCW(VFP_GetCW and (not _VFP_EXCEPTIONS_PENDING_MASK));
     VFP_SetCW(VFP_GetCW and (not _VFP_EXCEPTIONS_PENDING_MASK));
+    softfloat_exception_flags:=[];
   end;
   end;
 
 
 {$else FPUARM_HAS_VFP_EXTENSION}
 {$else FPUARM_HAS_VFP_EXTENSION}

+ 1 - 0
rtl/arm/thumb.inc

@@ -19,6 +19,7 @@
 Procedure SysInitFPU;{$ifdef SYSTEMINLINE}inline;{$endif}
 Procedure SysInitFPU;{$ifdef SYSTEMINLINE}inline;{$endif}
 begin
 begin
   softfloat_exception_mask:=[float_flag_underflow,float_flag_inexact,float_flag_denormal];
   softfloat_exception_mask:=[float_flag_underflow,float_flag_inexact,float_flag_denormal];
+  softfloat_exception_flags:=[];
 end;
 end;
 
 
 
 

+ 10 - 9
rtl/inc/genmath.inc

@@ -134,20 +134,21 @@ Begin
   pflags := @softfloat_exception_flags;
   pflags := @softfloat_exception_flags;
   pflags^:=pflags^ + i;
   pflags^:=pflags^ + i;
   unmasked_flags := pflags^ - softfloat_exception_mask;
   unmasked_flags := pflags^ - softfloat_exception_mask;
+  { before we raise the exception, we mark it as handled,
+    this behaviour is similiar to the hardware handler in SignalToRunerror }
+  SysResetFPU;
   if (float_flag_invalid in unmasked_flags) then
   if (float_flag_invalid in unmasked_flags) then
      HandleError(207)
      HandleError(207)
-  else
-  if (float_flag_divbyzero in unmasked_flags) then
+  else if (float_flag_divbyzero in unmasked_flags) then
      HandleError(200)
      HandleError(200)
-  else
-  if (float_flag_overflow in unmasked_flags) then
+  else if (float_flag_overflow in unmasked_flags) then
      HandleError(205)
      HandleError(205)
-  else
-  if (float_flag_underflow in unmasked_flags) then
+  else if (float_flag_underflow in unmasked_flags) then
      HandleError(206)
      HandleError(206)
-  else
-  if (float_flag_inexact in unmasked_flags) then
-     HandleError(207);
+  else if (float_flag_inexact in unmasked_flags) then
+     HandleError(207)
+  else if (float_flag_denormal in unmasked_flags) then
+     HandleError(216);
 end;
 end;
 
 
 
 

+ 1 - 0
rtl/linux/arm/sighnd.inc

@@ -70,6 +70,7 @@ begin
         begin
         begin
           { don't know how to find the different causes, maybe via xer? }
           { don't know how to find the different causes, maybe via xer? }
           res := 207;
           res := 207;
+          SysResetFPU;
         end;
         end;
     SIGILL:
     SIGILL:
 {$ifdef FPC_SYSTEM_FPC_MOVE}
 {$ifdef FPC_SYSTEM_FPC_MOVE}