Browse Source

+ support for software floating point exception handling on AArch64 (-CE)

git-svn-id: trunk@42891 -
florian 6 years ago
parent
commit
078595be4c

+ 36 - 0
compiler/aarch64/cgcpu.pas

@@ -100,6 +100,7 @@ interface
         procedure g_concatcopy_move(list: TAsmList; const source, dest: treference; len: tcgint);
         procedure g_concatcopy_move(list: TAsmList; const source, dest: treference; len: tcgint);
         procedure g_concatcopy(list: TAsmList; const source, dest: treference; len: tcgint);override;
         procedure g_concatcopy(list: TAsmList; const source, dest: treference; len: tcgint);override;
         procedure g_adjust_self_value(list: TAsmList; procdef: tprocdef; ioffset: tcgint);override;
         procedure g_adjust_self_value(list: TAsmList; procdef: tprocdef; ioffset: tcgint);override;
+        procedure g_check_for_fpu_exception(list: TAsmList; force, clear: boolean);override;
        private
        private
         function save_regs(list: TAsmList; rt: tregistertype; lowsr, highsr: tsuperregister; sub: tsubregister): longint;
         function save_regs(list: TAsmList; rt: tregistertype; lowsr, highsr: tsuperregister; sub: tsubregister): longint;
         procedure load_regs(list: TAsmList; rt: tregistertype; lowsr, highsr: tsuperregister; sub: tsubregister);
         procedure load_regs(list: TAsmList; rt: tregistertype; lowsr, highsr: tsuperregister; sub: tsubregister);
@@ -1032,6 +1033,7 @@ implementation
             instr:=taicpu.op_reg_reg(A_FCVT,reg2,reg1);
             instr:=taicpu.op_reg_reg(A_FCVT,reg2,reg1);
           end;
           end;
         list.Concat(instr);
         list.Concat(instr);
+        maybe_check_for_fpu_exception(list);
       end;
       end;
 
 
 
 
@@ -2256,6 +2258,40 @@ implementation
       end;
       end;
 
 
 
 
+    procedure tcgaarch64.g_check_for_fpu_exception(list: TAsmList;force,clear : boolean);
+      var
+        r : TRegister;
+        ai: taicpu;
+        l1,l2: TAsmLabel;
+      begin
+        { so far, we assume all flavours of AArch64 need explicit floating point exception checking }
+        if ((cs_check_fpu_exceptions in current_settings.localswitches) and
+            (force or current_procinfo.FPUExceptionCheckNeeded)) then
+          begin
+            r:=getintregister(list,OS_INT);
+            list.concat(taicpu.op_reg_reg(A_MRS,r,NR_FPSR));
+            list.concat(taicpu.op_reg_const(A_TST,r,$1f));
+            current_asmdata.getjumplabel(l1);
+            current_asmdata.getjumplabel(l2);
+            ai:=taicpu.op_sym(A_B,l1);
+            ai.is_jmp:=true;
+            ai.condition:=C_NE;
+            list.concat(ai);
+            list.concat(taicpu.op_reg_const(A_TST,r,$80));
+            ai:=taicpu.op_sym(A_B,l2);
+            ai.is_jmp:=true;
+            ai.condition:=C_EQ;
+            list.concat(ai);
+            a_label(list,l1);
+            alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+            cg.a_call_name(list,'FPC_THROWFPUEXCEPTION',false);
+            dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+            a_label(list,l2);
+            if clear then
+              current_procinfo.FPUExceptionCheckNeeded:=false;
+          end;
+      end;
+
 
 
     procedure create_codegen;
     procedure create_codegen;
       begin
       begin

+ 2 - 0
compiler/aarch64/ncpuadd.pas

@@ -218,6 +218,7 @@ interface
 
 
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,
            location.register,left.location.register,right.location.register));
            location.register,left.location.register,right.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 
@@ -238,6 +239,7 @@ interface
         { signalling compare so we can get exceptions }
         { signalling compare so we can get exceptions }
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCMPE,
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCMPE,
              left.location.register,right.location.register));
              left.location.register,right.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 

+ 4 - 0
compiler/aarch64/ncpuinl.pas

@@ -122,6 +122,7 @@ implementation
       begin
       begin
         load_fpu_location;
         load_fpu_location;
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FABS,location.register,left.location.register));
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FABS,location.register,left.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 
@@ -129,6 +130,7 @@ implementation
       begin
       begin
         load_fpu_location;
         load_fpu_location;
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_FMUL,location.register,left.location.register,left.location.register));
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_FMUL,location.register,left.location.register,left.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 
@@ -136,6 +138,7 @@ implementation
       begin
       begin
         load_fpu_location;
         load_fpu_location;
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FSQRT,location.register,left.location.register));
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FSQRT,location.register,left.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 
@@ -168,6 +171,7 @@ implementation
         { convert to signed integer rounding towards zero (there's no "round to
         { convert to signed integer rounding towards zero (there's no "round to
           integer using current rounding mode") }
           integer using current rounding mode") }
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCVTZS,location.register,hreg));
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCVTZS,location.register,hreg));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 
 

+ 1 - 0
compiler/aarch64/ncpumat.pas

@@ -187,6 +187,7 @@ implementation
         location_reset(location,LOC_MMREGISTER,def_cgsize(resultdef));
         location_reset(location,LOC_MMREGISTER,def_cgsize(resultdef));
         location.register:=cg.getmmregister(current_asmdata.CurrAsmList,location.size);
         location.register:=cg.getmmregister(current_asmdata.CurrAsmList,location.size);
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FNEG,location.register,left.location.register));
         current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FNEG,location.register,left.location.register));
+        cg.maybe_check_for_fpu_exception(current_asmdata.CurrAsmList);
       end;
       end;
 
 
 begin
 begin

+ 67 - 1
rtl/aarch64/aarch64.inc

@@ -57,14 +57,80 @@ procedure setfpsr(val: dword); nostackframe; assembler;
   end;
   end;
 
 
 
 
+const
+  FPSR_IOC = 1;
+  FPSR_DZC = 1 shl 1;
+  FPSR_OFC = 1 shl 2;
+  FPSR_UFC = 1 shl 3;
+  FPSR_IXC = 1 shl 4;
+  FPSR_IDC = 1 shl 7;
+  FPSR_EXCEPTIONS = FPSR_IOC or FPSR_DZC or FPSR_OFC or FPSR_UFC or FPSR_IXC or FPSR_IDC;
+
+
+procedure RaisePendingExceptions;
+  var
+    fpsr : dword;
+    f: TFPUException;
+  begin
+    fpsr:=getfpsr;
+    if (fpsr and FPSR_DZC) <> 0 then
+      float_raise(exZeroDivide);
+    if (fpsr and FPSR_OFC) <> 0 then
+      float_raise(exOverflow);
+    if (fpsr and FPSR_UFC) <> 0 then
+      float_raise(exUnderflow);
+    if (fpsr and FPSR_IOC) <> 0 then
+      float_raise(exInvalidOp);
+    if (fpsr and FPSR_IXC) <> 0 then
+      float_raise(exPrecision);
+    if (fpsr and FPSR_IDC) <> 0 then
+      float_raise(exDenormalized);
+    { now the soft float exceptions }
+    for f in softfloat_exception_flags do
+      float_raise(f);
+  end;
+
+
+{ as so far no AArch64 flavour which supports hard floating point exceptions, we use solely
+  the softfloat_exception_mask for masking as the masking flags are RAZ and WI if floating point
+  exceptions are not supported }
+procedure fpc_throwfpuexception;[public,alias:'FPC_THROWFPUEXCEPTION'];
+  var
+    fpsr : dword;
+    f: TFPUException;
+  begin
+    { at this point, we know already, that an exception will be risen }
+    fpsr:=getfpsr;
+
+    { check, if the exception is masked }
+    if ((fpsr and FPSR_DZC) <> 0) and (exZeroDivide in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_DZC);
+    if ((fpsr and FPSR_OFC) <> 0) and (exOverflow in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_OFC);
+    if ((fpsr and FPSR_UFC) <> 0) and (exUnderflow in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_UFC);
+    if ((fpsr and FPSR_IOC) <> 0) and (exInvalidOp in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_IOC);
+    if ((fpsr and FPSR_IXC) <> 0) and (exPrecision in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_IXC);
+    if ((fpsr and FPSR_IDC) <> 0) and (exDenormalized in softfloat_exception_mask) then
+      fpsr:=fpsr and not(FPSR_IDC);
+    setfpsr(fpsr);
+    if (fpsr and FPSR_EXCEPTIONS)<>0 then
+      RaisePendingExceptions;
+  end;
+
+
 procedure fpc_enable_fpu_exceptions;
 procedure fpc_enable_fpu_exceptions;
   begin
   begin
     { clear all "exception happened" flags we care about}
     { clear all "exception happened" flags we care about}
     setfpsr(getfpsr and not(fpu_exception_mask shr fpu_exception_mask_to_status_mask_shift));
     setfpsr(getfpsr and not(fpu_exception_mask shr fpu_exception_mask_to_status_mask_shift));
     { enable invalid operations and division by zero exceptions. }
     { enable invalid operations and division by zero exceptions. }
-    setfpcr(getfpcr or fpu_exception_mask);
+    setfpcr((getfpcr and not(fpu_exception_mask)));
+    softfloat_exception_mask:=[exPrecision,exUnderflow,exInvalidOp];
   end;
   end;
 
 
+
 procedure fpc_cpuinit;
 procedure fpc_cpuinit;
   begin
   begin
     { don't let libraries influence the FPU cw set by the host program }
     { don't let libraries influence the FPU cw set by the host program }

+ 1 - 1
rtl/aarch64/math.inc

@@ -1,5 +1,5 @@
 {
 {
-    Implementation of mathematical routines for x86_64
+    Implementation of mathematical routines for AArch64
 
 
     This file is part of the Free Pascal run time library.
     This file is part of the Free Pascal run time library.
     Copyright (c) 1999-2005 by the Free Pascal development team
     Copyright (c) 1999-2005 by the Free Pascal development team

+ 18 - 3
rtl/aarch64/mathu.inc

@@ -80,9 +80,14 @@ const
 
 
 
 
 function GetExceptionMask: TFPUExceptionMask;
 function GetExceptionMask: TFPUExceptionMask;
+  {
   var
   var
     fpcr: dword;
     fpcr: dword;
+  }
   begin
   begin
+    { as I am not aware of any hardware exception supporting AArch64 implementation,
+      and else the trapping enable flags are RAZ, return the softfloat exception mask (FK)
+
     fpcr:=getfpcr;
     fpcr:=getfpcr;
     result:=[];
     result:=[];
     if ((fpcr and fpu_ioe)=0) then
     if ((fpcr and fpu_ioe)=0) then
@@ -97,14 +102,22 @@ function GetExceptionMask: TFPUExceptionMask;
       result := result+[exPrecision];
       result := result+[exPrecision];
     if ((fpcr and fpu_ide)=0) then
     if ((fpcr and fpu_ide)=0) then
       result := result+[exDenormalized];
       result := result+[exDenormalized];
+    }
+    result:=softfloat_exception_mask;
   end;
   end;
 
 
 
 
 function SetExceptionMask(const Mask: TFPUExceptionMask): TFPUExceptionMask;
 function SetExceptionMask(const Mask: TFPUExceptionMask): TFPUExceptionMask;
+  {
   var
   var
     newfpcr: dword;
     newfpcr: dword;
+  }
   begin
   begin
+    { as I am not aware of any hardware exception supporting AArch64 implementation,
+      and else the trapping enable flags are RAZ, work solely with softfloat_exception_mask (FK)
+    }
     softfloat_exception_mask:=mask;
     softfloat_exception_mask:=mask;
+    {
     newfpcr:=fpu_exception_mask;
     newfpcr:=fpu_exception_mask;
     if exInvalidOp in Mask then
     if exInvalidOp in Mask then
       newfpcr:=newfpcr and not(fpu_ioe);
       newfpcr:=newfpcr and not(fpu_ioe);
@@ -118,13 +131,15 @@ function SetExceptionMask(const Mask: TFPUExceptionMask): TFPUExceptionMask;
       newfpcr:=newfpcr and not(fpu_ixe);
       newfpcr:=newfpcr and not(fpu_ixe);
     if exDenormalized in Mask then
     if exDenormalized in Mask then
       newfpcr:=newfpcr and not(fpu_ide);
       newfpcr:=newfpcr and not(fpu_ide);
+    }
     { clear "exception happened" flags }
     { clear "exception happened" flags }
     ClearExceptions(false);
     ClearExceptions(false);
     { set new exception mask }
     { set new exception mask }
-    setfpcr((getfpcr and not(fpu_exception_mask)) or newfpcr);
+//    setfpcr((getfpcr and not(fpu_exception_mask)) or newfpcr);
     { unsupported mask bits will remain 0 -> read exception mask again }
     { unsupported mask bits will remain 0 -> read exception mask again }
-    result:=GetExceptionMask;
-    softfloat_exception_mask:=result;
+//    result:=GetExceptionMask;
+//    softfloat_exception_mask:=result;
+    result:=softfloat_exception_mask;
   end;
   end;