Browse Source

* use LLVM constrained fpext/fptrunc intrinsics when fastmath is not enabled
for accurate exception behaviour

git-svn-id: trunk@43820 -

Jonas Maebe 5 years ago
parent
commit
a6a17efa42
4 changed files with 92 additions and 5 deletions
  1. 51 3
      compiler/llvm/hlcgllvm.pas
  2. 19 0
      compiler/llvm/llvmdef.pas
  3. 3 2
      compiler/llvm/llvminfo.pas
  4. 19 0
      rtl/inc/llvmintr.inc

+ 51 - 3
compiler/llvm/hlcgllvm.pas

@@ -96,6 +96,9 @@ uses
       procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister); override;
       procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; reg: tregister); override;
       procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference); override;
       procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tdef; reg: tregister; const ref: treference); override;
       procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister); override;
       procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister); override;
+     protected
+      procedure gen_fpconstrained_intrinsic(list: TAsmList; const intrinsic: TIDString; fromsize, tosize: tdef; fromreg, toreg: tregister);
+     public
 
 
       procedure gen_proc_symbol(list: TAsmList); override;
       procedure gen_proc_symbol(list: TAsmList); override;
       procedure handle_external_proc(list: TAsmList; pd: tprocdef; const importname: TSymStr); override;
       procedure handle_external_proc(list: TAsmList; pd: tprocdef; const importname: TSymStr); override;
@@ -165,7 +168,7 @@ implementation
     verbose,cutils,globals,fmodule,constexp,systems,
     verbose,cutils,globals,fmodule,constexp,systems,
     defutil,llvmdef,llvmsym,
     defutil,llvmdef,llvmsym,
     aasmtai,aasmcpu,
     aasmtai,aasmcpu,
-    aasmllvm,llvmbase,llvminfo,tgllvm,
+    aasmllvm,aasmllvmmetadata,llvmbase,llvminfo,tgllvm,
     symtable,symllvm,
     symtable,symllvm,
     paramgr,
     paramgr,
     pass_2,procinfo,llvmpi,cpuinfo,cgobj,cgllvm,cghlcpu,
     pass_2,procinfo,llvmpi,cpuinfo,cgobj,cgllvm,cghlcpu,
@@ -1321,10 +1324,55 @@ implementation
   procedure thlcgllvm.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister);
   procedure thlcgllvm.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tdef; reg1, reg2: tregister);
     var
     var
       op: tllvmop;
       op: tllvmop;
+      intrinsic: TIDString;
     begin
     begin
       op:=llvmconvop(fromsize,tosize,true);
       op:=llvmconvop(fromsize,tosize,true);
-      { reg2 = bitcast fromllsize reg1 to tollsize }
-      list.concat(taillvm.op_reg_size_reg_size(op,reg2,fromsize,reg1,tosize));
+      if (cs_opt_fastmath in current_settings.optimizerswitches) or
+         not(llvmflag_constrained_fptrunc_fpext in llvmversion_properties[current_settings.llvmversion]) or
+         not(op in [la_fptrunc,la_fpext]) then
+        list.concat(taillvm.op_reg_size_reg_size(op,reg2,fromsize,reg1,tosize))
+      else
+        begin
+          if op=la_fptrunc then
+            intrinsic:='llvm_experimental_constrained_fptrunc'
+          else
+            intrinsic:='llvm_experimental_constrained_fpext';
+          gen_fpconstrained_intrinsic(list,
+            intrinsic+llvmfloatintrinsicsuffix(tfloatdef(tosize))+llvmfloatintrinsicsuffix(tfloatdef(fromsize)),
+            fromsize,tosize,reg1,reg2);
+        end;
+    end;
+
+
+  procedure thlcgllvm.gen_fpconstrained_intrinsic(list: TAsmList; const intrinsic: TIDString; fromsize, tosize: tdef; fromreg, toreg: tregister);
+    var
+      frompara, roundpara, exceptpara, respara: tcgpara;
+      tmploc: tlocation;
+      pd: tprocdef;
+    begin
+      frompara.init;
+      roundpara.init;
+      exceptpara.init;
+      pd:=search_system_proc(intrinsic);
+
+      paramanager.getcgtempparaloc(list,pd,1,frompara);
+      paramanager.getcgtempparaloc(list,pd,2,roundpara);
+      paramanager.getcgtempparaloc(list,pd,3,exceptpara);
+
+      location_reset(tmploc,frompara.location^.loc,def_cgsize(fromsize));
+      tmploc.register:=fromreg;
+      gen_load_loc_cgpara(list,fromsize,tmploc,frompara);
+      a_load_reg_cgpara(list,llvm_metadatatype,tllvmmetadata.getstringreg('round.dynamic'),roundpara);
+      a_load_reg_cgpara(list,llvm_metadatatype,tllvmmetadata.getstringreg('fpexcept.strict'),exceptpara);
+      respara:=g_call_system_proc(list,pd,[@frompara,@roundpara,@exceptpara],nil);
+
+      location_reset(tmploc,respara.location^.loc,def_cgsize(tosize));
+      tmploc.register:=toreg;
+      gen_load_cgpara_loc(list,tosize,respara,tmploc,false);
+      frompara.done;
+      roundpara.done;
+      exceptpara.done;
+      respara.resetiftemp;
     end;
     end;
 
 
 
 

+ 19 - 0
compiler/llvm/llvmdef.pas

@@ -109,6 +109,8 @@ interface
 
 
     function llvmasmsymname(const sym: TAsmSymbol): TSymStr;
     function llvmasmsymname(const sym: TAsmSymbol): TSymStr;
 
 
+    function llvmfloatintrinsicsuffix(def: tfloatdef): TIDString;
+
 
 
 implementation
 implementation
 
 
@@ -290,6 +292,23 @@ implementation
         result:='label %'+sym.name;
         result:='label %'+sym.name;
     end;
     end;
 
 
+  function llvmfloatintrinsicsuffix(def: tfloatdef): TIDString;
+    begin
+      case def.floattype of
+        s32real:
+          result:='_f32';
+        s64real:
+          result:='_f64';
+        s80real,sc80real:
+          result:='_f80';
+        s128real:
+          result:='_f128';
+        else
+          { comp/currency need to be converted to s(c)80real first }
+          internalerror(2019122902);
+      end;
+    end;
+
 
 
   function llvmbyvalparaloc(paraloc: pcgparalocation): boolean;
   function llvmbyvalparaloc(paraloc: pcgparalocation): boolean;
     begin
     begin

+ 3 - 2
compiler/llvm/llvminfo.pas

@@ -47,7 +47,8 @@ Type
 type
 type
    tllvmversionflag = (
    tllvmversionflag = (
      llvmflag_memcpy_indiv_align,  { memcpy intrinsic supports separate alignment for source and dest }
      llvmflag_memcpy_indiv_align,  { memcpy intrinsic supports separate alignment for source and dest }
-     llvmflag_null_pointer_valid   { supports "llvmflag_null_pointer_valid" attribute, which indicates access to nil should not be optimized as undefined behaviour }
+     llvmflag_null_pointer_valid,  { supports "llvmflag_null_pointer_valid" attribute, which indicates access to nil should not be optimized as undefined behaviour }
+     llvmflag_constrained_fptrunc_fpext { supports constrained fptrunc and fpext intrinsics }
    );
    );
    tllvmversionflags = set of tllvmversionflag;
    tllvmversionflags = set of tllvmversionflag;
 
 
@@ -70,7 +71,7 @@ Const
        { llvmver_7_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
        { llvmver_7_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
        { llvmver_7_1     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
        { llvmver_7_1     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
        { llvmver_8_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
        { llvmver_8_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid],
-       { llvmver_9_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid]
+       { llvmver_9_0     } [llvmflag_memcpy_indiv_align,llvmflag_null_pointer_valid,llvmflag_constrained_fptrunc_fpext]
      );
      );
 
 
    { Supported optimizations, only used for information }
    { Supported optimizations, only used for information }

+ 19 - 0
rtl/inc/llvmintr.inc

@@ -86,3 +86,22 @@ function llvm_experimental_constrained_fsub(a, b: float128; rounding, exceptions
 function llvm_experimental_constrained_fmul(a, b: float128; rounding, exceptions: LLVMMetadata): float128; external name 'llvm.experimental.constrained.fmul.f128';
 function llvm_experimental_constrained_fmul(a, b: float128; rounding, exceptions: LLVMMetadata): float128; external name 'llvm.experimental.constrained.fmul.f128';
 function llvm_experimental_constrained_fdiv(a, b: float128; rounding, exceptions: LLVMMetadata): float128; external name 'llvm.experimental.constrained.fdiv.f128';
 function llvm_experimental_constrained_fdiv(a, b: float128; rounding, exceptions: LLVMMetadata): float128; external name 'llvm.experimental.constrained.fdiv.f128';
 {$endif}
 {$endif}
+
+function llvm_experimental_constrained_fptrunc_f32_f64(a: double; rounding, exceptions: LLVMMetadata): single; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f32.f64';
+function llvm_experimental_constrained_fpext_f64_f32(a: single; rounding, exceptions: LLVMMetadata): double; compilerproc; external name 'llvm.experimental.constrained.fpext.f64.f32';
+{$ifdef SUPPORT_EXTENDED}
+function llvm_experimental_constrained_fptrunc_f32_f80(a: extended; rounding, exceptions: LLVMMetadata): single; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f32.x86_fp80';
+function llvm_experimental_constrained_fptrunc_f64_f80(a: extended; rounding, exceptions: LLVMMetadata): double; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f64.x86_fp80';
+function llvm_experimental_constrained_fpext_f80_f32(a: single; rounding, exceptions: LLVMMetadata): extended; compilerproc; external name 'llvm.experimental.constrained.fpext.x86_fp80.f32';
+function llvm_experimental_constrained_fpext_f80_f64(a: double; rounding, exceptions: LLVMMetadata): extended; compilerproc; external name 'llvm.experimental.constrained.fpext.x86_fp80.f64';
+{$ifdef SUPPORT_FLOAT128}
+function llvm_experimental_constrained_fptrunc_f128_f80(a: extended; rounding, exceptions: LLVMMetadata): float128; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f128.x86_fp80';
+function llvm_experimental_constrained_fpext_f80_f32(a: float128; rounding, exceptions: LLVMMetadata): extended; compilerproc; external name 'llvm.experimental.constrained.fpext.x86_fp80.f128';
+{$endif}
+{$endif}
+{$ifdef SUPPORT_FLOAT128}
+function llvm_experimental_constrained_fptrunc_f32_f128(a: float128; rounding, exceptions: LLVMMetadata): single; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f32.f128';
+function llvm_experimental_constrained_fptrunc_f64_f128(a: float128; rounding, exceptions: LLVMMetadata): double; compilerproc; external name 'llvm.experimental.constrained.fptrunc.f64.f128';
+function llvm_experimental_constrained_fpext_f128_f32(a: single; rounding, exceptions: LLVMMetadata): float128; compilerproc; external name 'llvm.experimental.constrained.fpext.f128.f32';
+function llvm_experimental_constrained_fpext_f128_f64(a: double; rounding, exceptions: LLVMMetadata): float128; compilerproc; external name 'llvm.experimental.constrained.fpext.f128.f64';
+{$endif}