From 29263eb3430f8ad2066b849ae6baff691bbe2a11 Mon Sep 17 00:00:00 2001
From: Jonas Maebe <jonas@freepascal.org>
Date: Sun, 21 Oct 2012 17:56:33 +0000
Subject: [PATCH]   + full support for overflow checking on the JVM target
 (note: significantly     increases code size, because not natively supported
 by the JVM)

git-svn-id: trunk@22808 -
---
 compiler/jvm/hlcgcpu.pas | 123 ++++++++++++++++++++++++++++++++++++++-
 compiler/jvm/njvmmat.pas |  22 ++++++-
 2 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/compiler/jvm/hlcgcpu.pas b/compiler/jvm/hlcgcpu.pas
index 65c4962ba3..629ef845d9 100644
--- a/compiler/jvm/hlcgcpu.pas
+++ b/compiler/jvm/hlcgcpu.pas
@@ -69,6 +69,8 @@ uses
       procedure a_op_ref_reg(list: TAsmList; Op: TOpCG; size: tdef; const ref: TReference; reg: TRegister); override;
       procedure a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister); override;
       procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: tdef; reg1, reg2: TRegister); override;
+      procedure a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; a: aint; src, dst: tregister;setflags : boolean;var ovloc : tlocation); override;
+      procedure a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister;setflags : boolean;var ovloc : tlocation); override;
 
       procedure a_cmp_const_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: aint; const ref: treference; l: tasmlabel); override;
       procedure a_cmp_const_reg_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: aint; reg: tregister; l: tasmlabel); override;
@@ -97,6 +99,9 @@ uses
       procedure g_initialize(list : TAsmList;t : tdef;const ref : treference);override;
       procedure g_finalize(list : TAsmList;t : tdef;const ref : treference);override;
 
+      procedure g_overflowcheck(list: TAsmList; const Loc: tlocation; def: tdef); override;
+      procedure g_overflowCheck_loc(List:TAsmList;const Loc:TLocation;def:TDef;var ovloc : tlocation); override;
+
       procedure location_get_data_ref(list:TAsmList;def: tdef; const l:tlocation;var ref:treference;loadref:boolean; alignment: longint);override;
       procedure g_copyvaluepara_openarray(list: TAsmList; const ref: treference; const lenloc: tlocation; arrdef: tarraydef; destreg: tregister); override;
       procedure g_releasevaluepara_openarray(list: TAsmList; arrdef: tarraydef; const l: tlocation); override;
@@ -170,7 +175,6 @@ uses
         slots used for parameters and the provided resultdef }
       procedure g_adjust_stack_after_call(list: TAsmList; pd: tabstractprocdef; paraheight: longint; forceresdef: tdef);
 
-
       property maxevalstackheight: longint read fmaxevalstackheight;
 
       procedure gen_initialize_fields_code(list:TAsmList);
@@ -1164,6 +1168,105 @@ implementation
       a_op_reg_reg_reg(list,op,size,reg1,reg2,reg2);
     end;
 
+  procedure thlcgjvm.a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; a: aint; src, dst: tregister; setflags: boolean; var ovloc: tlocation);
+    var
+      tmpreg: tregister;
+    begin
+      if not setflags then
+        begin
+          inherited;
+          exit;
+        end;
+      tmpreg:=getintregister(list,size);
+      a_load_const_reg(list,size,a,tmpreg);
+      a_op_reg_reg_reg_checkoverflow(list,op,size,tmpreg,src,dst,true,ovloc);
+    end;
+
+  procedure thlcgjvm.a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tdef; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation);
+    var
+      orgsrc1, orgsrc2: tregister;
+      docheck: boolean;
+      lab: tasmlabel;
+    begin
+      if not setflags then
+        begin
+          inherited;
+          exit;
+        end;
+      { anything else cannot overflow }
+      docheck:=size.size in [4,8];
+      if docheck then
+        begin
+          orgsrc1:=src1;
+          orgsrc2:=src2;
+          if src1=dst then
+            begin
+              orgsrc1:=getintregister(list,size);
+              a_load_reg_reg(list,size,size,src1,orgsrc1);
+            end;
+          if src2=dst then
+            begin
+              orgsrc2:=getintregister(list,size);
+              a_load_reg_reg(list,size,size,src2,orgsrc2);
+            end;
+        end;
+      a_op_reg_reg_reg(list,op,size,src1,src2,dst);
+      if docheck then
+        begin
+          { * signed overflow for addition iff
+             - src1 and src2 are negative and result is positive (excep in case of
+               subtraction, then sign of src1 has to be inverted)
+             - src1 and src2 are positive and result is negative
+              -> Simplified boolean equivalent (in terms of sign bits):
+                 not(src1 xor src2) and (src1 xor dst)
+
+             for subtraction, multiplication: invert src1 sign bit
+             for division: handle separately (div by zero, low(inttype) div -1),
+               not supported by this code
+
+            * unsigned overflow iff carry out, aka dst < src1 or dst < src2
+          }
+          location_reset(ovloc,LOC_REGISTER,OS_S32);
+          { not pasbool8, because then we'd still have to convert the integer to
+            a boolean via branches for Dalvik}
+          ovloc.register:=getintregister(list,s32inttype);
+          if not ((size.typ=pointerdef) or
+                 ((size.typ=orddef) and
+                  (torddef(size).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
+                                            pasbool8,pasbool16,pasbool32,pasbool64]))) then
+            begin
+              a_load_reg_stack(list,size,src1);
+              if op in [OP_SUB,OP_IMUL] then
+                a_op_stack(list,OP_NOT,size,false);
+              a_op_reg_stack(list,OP_XOR,size,src2);
+              a_op_stack(list,OP_NOT,size,false);
+              a_load_reg_stack(list,size,src1);
+              a_op_reg_stack(list,OP_XOR,size,dst);
+              a_op_stack(list,OP_AND,size,false);
+              a_op_const_stack(list,OP_SHR,size,(size.size*8)-1);
+              if size.size=8 then
+                begin
+                  list.concat(taicpu.op_none(a_l2i));
+                  decstack(list,1);
+                end;
+            end
+          else
+            begin
+              a_load_const_stack(list,s32inttype,0,R_INTREGISTER);
+              current_asmdata.getjumplabel(lab);
+              { can be optimized by removing duplicate xor'ing to convert dst from
+                signed to unsigned quadrant }
+              a_cmp_reg_reg_label(list,size,OC_B,dst,src1,lab);
+              a_cmp_reg_reg_label(list,size,OC_B,dst,src2,lab);
+              a_op_const_stack(list,OP_XOR,s32inttype,1);
+              a_label(list,lab);
+            end;
+          a_load_stack_reg(list,s32inttype,ovloc.register);
+        end
+      else
+        ovloc.loc:=LOC_VOID;
+    end;
+
   procedure thlcgjvm.a_cmp_const_ref_label(list: TAsmList; size: tdef; cmp_op: topcmp; a: aint; const ref: treference; l: tasmlabel);
     begin
       if ref.base<>NR_EVAL_STACK_BASE then
@@ -1619,6 +1722,24 @@ implementation
       // do nothing
     end;
 
+  procedure thlcgjvm.g_overflowcheck(list: TAsmList; const Loc: tlocation; def: tdef);
+    begin
+      { not possible, need the original operands }
+      internalerror(2012102101);
+    end;
+
+  procedure thlcgjvm.g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; var ovloc: tlocation);
+    var
+      hl : tasmlabel;
+    begin
+      if not(cs_check_overflow in current_settings.localswitches) then
+        exit;
+      current_asmdata.getjumplabel(hl);
+      a_cmp_const_loc_label(list,s32inttype,OC_EQ,0,ovloc,hl);
+      g_call_system_proc(list,'fpc_overflow',nil);
+      a_label(list,hl);
+    end;
+
   procedure thlcgjvm.location_get_data_ref(list: TAsmList; def: tdef; const l: tlocation; var ref: treference; loadref: boolean; alignment: longint);
     var
       tmploc: tlocation;
diff --git a/compiler/jvm/njvmmat.pas b/compiler/jvm/njvmmat.pas
index ca1de0acdf..faeb4c3b9f 100644
--- a/compiler/jvm/njvmmat.pas
+++ b/compiler/jvm/njvmmat.pas
@@ -78,6 +78,9 @@ implementation
 
     procedure tjvmmoddivnode.pass_generate_code;
       var
+        tmpreg: tregister;
+        lab: tasmlabel;
+        ovloc: tlocation;
         op: topcg;
         isu32int: boolean;
       begin
@@ -89,7 +92,6 @@ implementation
 
         if nodetype=divn then
           begin
-            { TODO: overflow checking in case of high(longint) or high(int64) div -1 }
             thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
             if is_signed(resultdef) then
               op:=OP_IDIV
@@ -141,6 +143,24 @@ implementation
               thlcgjvm(hlcg).resize_stack_int_val(current_asmdata.CurrAsmList,s64inttype,u32inttype,false);
           end;
          thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,resultdef,location.register);
+         if (cs_check_overflow in current_settings.localswitches) and
+            is_signed(resultdef) then
+           begin
+             { the JVM raises an exception for integer div-iby-zero -> only
+               overflow in case left is low(inttype) and right is -1 ->
+               check by adding high(inttype) to left and and'ing with right
+               -> result is -1 only in case above conditions are fulfilled)
+             }
+             tmpreg:=hlcg.getintregister(current_asmdata.CurrAsmList,resultdef);
+             hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,resultdef,true);
+             hlcg.a_op_const_reg_reg(current_asmdata.CurrAsmList,OP_ADD,resultdef,torddef(resultdef).high,right.location.register,tmpreg);
+             hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,resultdef,true);
+             hlcg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_AND,resultdef,left.location.register,tmpreg);
+             current_asmdata.getjumplabel(lab);
+             hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,resultdef,OC_NE,-1,tmpreg,lab);
+             hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_overflow',nil);
+             hlcg.a_label(current_asmdata.CurrAsmList,lab);
+           end;
       end;