Browse Source

- Fix overflow checking for multiplication operations on AVR.

git-svn-id: trunk@42492 -
Jeppe Johansen 6 years ago
parent
commit
030cf78ac5
1 changed files with 283 additions and 111 deletions
  1. 283 111
      compiler/avr/cgcpu.pas

+ 283 - 111
compiler/avr/cgcpu.pas

@@ -60,9 +60,11 @@ unit cgcpu;
 
 
         procedure a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister); override;
         procedure a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister); override;
         procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: TCGSize; src, dst : TRegister); override;
         procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: TCGSize; src, dst : TRegister); override;
-        procedure a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister); override;
         procedure a_op_const_reg_reg(list : TAsmList;op : TOpCg;size : tcgsize; a : tcgint;src,dst : tregister); override;
         procedure a_op_const_reg_reg(list : TAsmList;op : TOpCg;size : tcgsize; a : tcgint;src,dst : tregister); override;
 
 
+        procedure a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
+        procedure a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
+
         { move instructions }
         { move instructions }
         procedure a_load_const_reg(list : TAsmList; size: tcgsize; a : tcgint;reg : tregister);override;
         procedure a_load_const_reg(list : TAsmList; size: tcgsize; a : tcgint;reg : tregister);override;
         procedure a_load_reg_ref(list : TAsmList; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);override;
         procedure a_load_reg_ref(list : TAsmList; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);override;
@@ -94,6 +96,7 @@ unit cgcpu;
         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_overflowcheck(list: TAsmList; const l: tlocation; def: tdef); override;
         procedure g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef); override;
+        procedure g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; ovloc: tlocation); override;
 
 
         procedure g_save_registers(list : TAsmList);override;
         procedure g_save_registers(list : TAsmList);override;
         procedure g_restore_registers(list : TAsmList);override;
         procedure g_restore_registers(list : TAsmList);override;
@@ -109,6 +112,8 @@ unit cgcpu;
         function GetLoad(const ref : treference) : tasmop;
         function GetLoad(const ref : treference) : tasmop;
         function GetStore(const ref: treference): tasmop;
         function GetStore(const ref: treference): tasmop;
 
 
+        procedure gen_multiply(list: TAsmList; op: topcg; size: TCgSize; src2, src1, dst: tregister; check_overflow: boolean; var ovloc: tlocation);
+
       protected
       protected
         procedure a_op_reg_reg_internal(list: TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
         procedure a_op_reg_reg_internal(list: TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
         procedure a_op_const_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg, reghi: TRegister);
         procedure a_op_const_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg, reghi: TRegister);
@@ -432,29 +437,6 @@ unit cgcpu;
        end;
        end;
 
 
 
 
-     procedure tcgavr.a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister);
-       begin
-         if (op in [OP_MUL,OP_IMUL]) and (size in [OS_16,OS_S16]) and
-            (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) then
-           begin
-             getcpuregister(list,NR_R0);
-             getcpuregister(list,NR_R1);
-             list.concat(taicpu.op_reg_reg(A_MUL,src1,src2));
-             emit_mov(list,dst,NR_R0);
-             emit_mov(list,GetNextReg(dst),NR_R1);
-             list.concat(taicpu.op_reg_reg(A_MUL,GetNextReg(src1),src2));
-             list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
-             list.concat(taicpu.op_reg_reg(A_MUL,src1,GetNextReg(src2)));
-             list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
-             ungetcpuregister(list,NR_R0);
-             list.concat(taicpu.op_reg(A_CLR,NR_R1));
-             ungetcpuregister(list,NR_R1);
-           end
-         else
-          inherited a_op_reg_reg_reg(list,op,size,src1,src2,dst);
-       end;
-
-
      procedure tcgavr.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister);
      procedure tcgavr.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister);
        begin
        begin
          if (op in [OP_MUL,OP_IMUL]) and (size in [OS_16,OS_S16]) and (a in [2,4,8]) then
          if (op in [OP_MUL,OP_IMUL]) and (size in [OS_16,OS_S16]) and (a in [2,4,8]) then
@@ -473,6 +455,36 @@ unit cgcpu;
            inherited a_op_const_reg_reg(list,op,size,a,src,dst);
            inherited a_op_const_reg_reg(list,op,size,a,src,dst);
        end;
        end;
 
 
+     procedure tcgavr.a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation);
+       var
+         tmpreg: TRegister;
+       begin
+         if (op in [OP_MUL,OP_IMUL]) and
+            setflags then
+           begin
+             tmpreg:=getintregister(list,size);
+             a_load_const_reg(list,size,a,tmpreg);
+             a_op_reg_reg_reg_checkoverflow(list,op,size,tmpreg,src,dst,setflags,ovloc);
+           end
+         else
+           begin
+             inherited a_op_const_reg_reg_checkoverflow(list, op, size, a, src, dst, setflags, ovloc);
+             ovloc.loc:=LOC_FLAGS;
+           end;
+       end;
+
+     procedure tcgavr.a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation);
+       begin
+         if (op in [OP_MUL,OP_IMUL]) and
+            setflags then
+           gen_multiply(list,op,size,src1,src2,dst,setflags,ovloc)
+         else
+           begin
+             inherited a_op_reg_reg_reg_checkoverflow(list, op, size, src1, src2, dst, setflags, ovloc);
+             ovloc.loc:=LOC_FLAGS;
+           end;
+       end;
+
 
 
      procedure tcgavr.a_op_reg_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
      procedure tcgavr.a_op_reg_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
        var
        var
@@ -483,6 +495,7 @@ unit cgcpu;
          paraloc1,paraloc2 : TCGPara;
          paraloc1,paraloc2 : TCGPara;
          l1,l2 : tasmlabel;
          l1,l2 : tasmlabel;
          pd : tprocdef;
          pd : tprocdef;
+         hovloc: tlocation;
 
 
       { NextRegDst* is sometimes called before the register usage and sometimes afterwards }
       { NextRegDst* is sometimes called before the register usage and sometimes afterwards }
        procedure NextSrcDstPreInc;
        procedure NextSrcDstPreInc;
@@ -598,93 +611,13 @@ unit cgcpu;
 
 
            OP_MUL,OP_IMUL:
            OP_MUL,OP_IMUL:
              begin
              begin
-               if size in [OS_8,OS_S8] then
-                 begin
-                   if CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype] then
-                     begin
-                       cg.a_reg_alloc(list,NR_R0);
-                       cg.a_reg_alloc(list,NR_R1);
-                       list.concat(taicpu.op_reg_reg(topcg2asmop[op],dst,src));
-                       list.concat(taicpu.op_reg(A_CLR,NR_R1));
-                       cg.a_reg_dealloc(list,NR_R1);
-                       list.concat(taicpu.op_reg_reg(A_MOV,dst,NR_R0));
-                       cg.a_reg_dealloc(list,NR_R0);
-                     end
-                   else
-                     begin
-                       if size=OS_8 then
-                         pd:=search_system_proc('fpc_mul_byte')
-                       else
-                          pd:=search_system_proc('fpc_mul_shortint');
-                       paraloc1.init;
-                       paraloc2.init;
-                       paramanager.getintparaloc(list,pd,1,paraloc1);
-                       paramanager.getintparaloc(list,pd,2,paraloc2);
-                       a_load_reg_cgpara(list,OS_8,src,paraloc2);
-                       a_load_reg_cgpara(list,OS_8,dst,paraloc1);
-                       paramanager.freecgpara(list,paraloc2);
-                       paramanager.freecgpara(list,paraloc1);
-                       alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
-                       if size=OS_8 then
-                         a_call_name(list,'FPC_MUL_BYTE',false)
-                       else
-                         a_call_name(list,'FPC_MUL_SHORTINT',false);
-                       dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
-                       cg.a_reg_alloc(list,NR_R24);
-                       cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
-                       cg.a_reg_dealloc(list,NR_R24);
-                       paraloc2.done;
-                       paraloc1.done;
-                     end;
-                 end
-               else if size in [OS_16,OS_S16] then
+               tmpreg:=dst;
+               if size in [OS_16,OS_S16] then
                  begin
                  begin
-                   if CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype] then
-                     begin
-                       tmpreg:=getintregister(list,OS_16);
-                       emit_mov(list,tmpreg,dst);
-                       emit_mov(list,GetNextReg(tmpreg),GetNextReg(dst));
-                       list.concat(taicpu.op_reg_reg(A_MUL,tmpreg,src));
-                       emit_mov(list,dst,NR_R0);
-                       emit_mov(list,GetNextReg(dst),NR_R1);
-                       list.concat(taicpu.op_reg_reg(A_MUL,GetNextReg(tmpreg),src));
-                       list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
-                       list.concat(taicpu.op_reg_reg(A_MUL,tmpreg,GetNextReg(src)));
-                       list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
-                       list.concat(taicpu.op_reg(A_CLR,NR_R1));
-                     end
-                   else
-                     begin
-                       if size=OS_16 then
-                         pd:=search_system_proc('fpc_mul_word')
-                       else
-                          pd:=search_system_proc('fpc_mul_integer');
-                       paraloc1.init;
-                       paraloc2.init;
-                       paramanager.getintparaloc(list,pd,1,paraloc1);
-                       paramanager.getintparaloc(list,pd,2,paraloc2);
-                       a_load_reg_cgpara(list,OS_16,src,paraloc2);
-                       a_load_reg_cgpara(list,OS_16,dst,paraloc1);
-                       paramanager.freecgpara(list,paraloc2);
-                       paramanager.freecgpara(list,paraloc1);
-                       alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
-                       if size=OS_16 then
-                         a_call_name(list,'FPC_MUL_WORD',false)
-                       else
-                         a_call_name(list,'FPC_MUL_INTEGER',false);
-                       dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
-                       cg.a_reg_alloc(list,NR_R24);
-                       cg.a_reg_alloc(list,NR_R25);
-                       cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
-                       cg.a_reg_dealloc(list,NR_R24);
-                       cg.a_load_reg_reg(list,OS_8,OS_8,NR_R25,GetNextReg(dst));
-                       cg.a_reg_dealloc(list,NR_R25);
-                       paraloc2.done;
-                       paraloc1.done;
-                     end;
-                 end
-               else
-                 internalerror(2011022002);
+                   tmpreg:=getintregister(list,size);
+                   a_load_reg_reg(list,size,size,dst,tmpreg);
+                 end;
+               gen_multiply(list,op,size,src,tmpreg,dst,false,hovloc);
              end;
              end;
 
 
            OP_DIV,OP_IDIV:
            OP_DIV,OP_IDIV:
@@ -1900,6 +1833,213 @@ unit cgcpu;
           result:=A_ST;
           result:=A_ST;
       end;
       end;
 
 
+    procedure tcgavr.gen_multiply(list: TAsmList; op: topcg; size: TCgSize; src2, src1, dst: tregister; check_overflow: boolean; var ovloc: tlocation);
+
+      procedure perform_r1_check;
+        var
+          hl: TAsmLabel;
+          ai: taicpu;
+        begin
+          if check_overflow then
+            begin
+              current_asmdata.getjumplabel(hl);
+              list.concat(taicpu.op_reg_reg(A_AND,NR_R1,NR_R1));
+
+              ai:=Taicpu.Op_Sym(A_BRxx,hl);
+              ai.SetCondition(C_EQ);
+              ai.is_jmp:=true;
+              list.concat(ai);
+
+              list.concat(taicpu.op_none(A_SET));
+
+              a_label(list,hl);
+            end;
+        end;
+
+      procedure perform_ovf_check;
+        var
+          ai: taicpu;
+          hl: TAsmLabel;
+        begin
+          if check_overflow then
+            begin
+              current_asmdata.getjumplabel(hl);
+
+              ai:=Taicpu.Op_Sym(A_BRxx,hl);
+              ai.SetCondition(C_CC);
+              ai.is_jmp:=true;
+              list.concat(ai);
+
+              list.concat(taicpu.op_none(A_SET));
+
+              a_label(list,hl);
+            end;
+        end;
+
+      var
+        pd: tprocdef;
+        paraloc1, paraloc2: tcgpara;
+        ai: taicpu;
+        hl: TAsmLabel;
+        name: String;
+      begin
+        ovloc.loc:=LOC_VOID;
+        if size in [OS_8,OS_S8] then
+          begin
+            if (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) and
+               ((not check_overflow) or
+                (size=OS_8)) then
+              begin
+                cg.a_reg_alloc(list,NR_R0);
+                cg.a_reg_alloc(list,NR_R1);
+                list.concat(taicpu.op_reg_reg(topcg2asmop[op],src1,src2));
+                // Check overflow
+                if check_overflow then
+                  begin
+                    current_asmdata.getjumplabel(hl);
+                    list.concat(taicpu.op_reg_reg(A_AND,NR_R1,NR_R1));
+
+                    ai:=Taicpu.Op_Sym(A_BRxx,hl);
+                    ai.SetCondition(C_EQ);
+                    ai.is_jmp:=true;
+                    list.concat(ai);
+
+                    list.concat(taicpu.op_reg(A_CLR,NR_R1));
+                    if op=OP_MUL then
+                      list.concat(taicpu.op_none(A_SEC))
+                    else
+                      list.concat(taicpu.op_none(A_SEV));
+
+                    a_label(list,hl);
+
+                    ovloc.loc:=LOC_FLAGS;
+                  end
+                else
+                  list.concat(taicpu.op_reg(A_CLR,NR_R1));
+                cg.a_reg_dealloc(list,NR_R1);
+
+                list.concat(taicpu.op_reg_reg(A_MOV,dst,NR_R0));
+                cg.a_reg_dealloc(list,NR_R0);
+              end
+            else
+              begin
+                if size=OS_8 then
+                  name:='fpc_mul_byte'
+                else
+                  name:='fpc_mul_shortint';
+
+                if check_overflow then
+                  name:=name+'_checkoverflow';
+
+                pd:=search_system_proc(name);
+                paraloc1.init;
+                paraloc2.init;
+                paramanager.getintparaloc(list,pd,1,paraloc1);
+                paramanager.getintparaloc(list,pd,2,paraloc2);
+                a_load_reg_cgpara(list,OS_8,src1,paraloc2);
+                a_load_reg_cgpara(list,OS_8,src2,paraloc1);
+                paramanager.freecgpara(list,paraloc2);
+                paramanager.freecgpara(list,paraloc1);
+                alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+                a_call_name(list,upper(name),false);
+                dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+                cg.a_reg_alloc(list,NR_R24);
+                cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
+                cg.a_reg_dealloc(list,NR_R24);
+                paraloc2.done;
+                paraloc1.done;
+              end;
+          end
+        else if size in [OS_16,OS_S16] then
+          begin
+            if (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) and
+               ((not check_overflow) or
+                (size=OS_16)) then
+              begin
+                if check_overflow then
+                  list.concat(taicpu.op_none(A_CLT));
+                cg.a_reg_alloc(list,NR_R0);
+                cg.a_reg_alloc(list,NR_R1);
+                list.concat(taicpu.op_reg_reg(A_MUL,src2,src1));
+                emit_mov(list,dst,NR_R0);
+                emit_mov(list,GetNextReg(dst),NR_R1);
+                list.concat(taicpu.op_reg_reg(A_MUL,GetNextReg(src1),src2));
+                perform_r1_check();
+                list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
+                perform_ovf_check();
+                list.concat(taicpu.op_reg_reg(A_MUL,src1,GetNextReg(src2)));
+                perform_r1_check();
+                list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
+                perform_ovf_check();
+                cg.a_reg_dealloc(list,NR_R0);
+                list.concat(taicpu.op_reg(A_CLR,NR_R1));
+                cg.a_reg_dealloc(list,NR_R1);
+
+                if check_overflow then
+                  begin
+                    {
+                      CLC
+                      CLV
+                      BRTC .L1
+                      SEV/SEC
+                    .L1:
+                    }
+                    list.concat(taicpu.op_none(A_CLC));
+                    list.concat(taicpu.op_none(A_CLV));
+
+                    current_asmdata.getjumplabel(hl);
+
+                    ai:=Taicpu.Op_Sym(A_BRxx,hl);
+                    ai.SetCondition(C_TC);
+                    ai.is_jmp:=true;
+                    list.concat(ai);
+
+                    if op=OP_MUL then
+                      list.concat(taicpu.op_none(A_SEC))
+                    else
+                      list.concat(taicpu.op_none(A_SEV));
+
+                    a_label(list,hl);
+
+                    ovloc.loc:=LOC_FLAGS;
+                  end;
+              end
+            else
+              begin
+                if size=OS_16 then
+                  name:='fpc_mul_word'
+                else
+                  name:='fpc_mul_integer';
+
+                if check_overflow then
+                  name:=name+'_checkoverflow';
+
+                pd:=search_system_proc(name);
+                paraloc1.init;
+                paraloc2.init;
+                paramanager.getintparaloc(list,pd,1,paraloc1);
+                paramanager.getintparaloc(list,pd,2,paraloc2);
+                a_load_reg_cgpara(list,OS_16,src1,paraloc2);
+                a_load_reg_cgpara(list,OS_16,src2,paraloc1);
+                paramanager.freecgpara(list,paraloc2);
+                paramanager.freecgpara(list,paraloc1);
+                alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+                a_call_name(list,upper(name),false);
+                dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
+                cg.a_reg_alloc(list,NR_R24);
+                cg.a_reg_alloc(list,NR_R25);
+                cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
+                cg.a_reg_dealloc(list,NR_R24);
+                cg.a_load_reg_reg(list,OS_8,OS_8,NR_R25,GetNextReg(dst));
+                cg.a_reg_dealloc(list,NR_R25);
+                paraloc2.done;
+                paraloc1.done;
+              end;
+          end
+        else
+          internalerror(2011022002);
+      end;
+
 
 
     procedure tcgavr.g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean);
     procedure tcgavr.g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean);
       var
       var
@@ -2374,7 +2514,7 @@ unit cgcpu;
         end;
         end;
 
 
 
 
-    procedure tcgavr.g_overflowCheck(list : TAsmList;const l : tlocation;def : tdef);
+    procedure tcgavr.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);
       var
       var
         hl : tasmlabel;
         hl : tasmlabel;
         ai : taicpu;
         ai : taicpu;
@@ -2400,6 +2540,38 @@ unit cgcpu;
       end;
       end;
 
 
 
 
+    procedure tcgavr.g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; ovloc: tlocation);
+      var
+        hl : tasmlabel;
+        ai : taicpu;
+        cond : TAsmCond;
+      begin
+        if not(cs_check_overflow in current_settings.localswitches) then
+         exit;
+
+        case ovloc.loc of
+          LOC_FLAGS:
+            begin
+              current_asmdata.getjumplabel(hl);
+              if not ((def.typ=pointerdef) or
+                     ((def.typ=orddef) and
+                      (torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
+                                                pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then
+                cond:=C_VC
+              else
+                cond:=C_CC;
+              ai:=Taicpu.Op_Sym(A_BRxx,hl);
+              ai.SetCondition(cond);
+              ai.is_jmp:=true;
+              list.concat(ai);
+
+              a_call_name(list,'FPC_OVERFLOW',false);
+              a_label(list,hl);
+            end;
+        end;
+      end;
+
+
     procedure tcgavr.g_save_registers(list: TAsmList);
     procedure tcgavr.g_save_registers(list: TAsmList);
       begin
       begin
         { this is done by the entry code }
         { this is done by the entry code }