{
    Copyright (c) 2000-2009 by Florian Klaempfl and David Zhang

    Code generation for add nodes on the FVM32

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************
}
unit ncpuadd;

{$i fpcdefs.inc}

interface

uses
  node, ncgadd, cpubase, aasmbase, cgbase;

type

  { tmipsaddnode }

  tmipsaddnode = class(tcgaddnode)
  private
    procedure cmp64_lt(left_reg, right_reg: TRegister64;unsigned:boolean);
    procedure cmp64_le(left_reg, right_reg: TRegister64;unsigned:boolean);
    procedure second_generic_cmp32(unsigned,is_smallset: boolean);
    procedure second_mul64bit;
  protected
    procedure second_addfloat; override;
    procedure second_cmpfloat; override;
    procedure second_cmpboolean; override;
    procedure second_cmpsmallset; override;
    procedure second_add64bit; override;
    procedure second_cmp64bit; override;
    procedure second_cmpordinal; override;
    procedure second_addordinal; override;
  public
    function use_generic_mul32to64: boolean; override;
    function use_generic_mul64bit: boolean; override;
  end;

implementation

uses
  systems, globtype, globals,
  cutils, verbose,
  paramgr,
  aasmtai, aasmcpu, aasmdata,
  defutil,
  cpuinfo,
  {cgbase,} cgcpu, cgutils,
  cpupara,
  procinfo,
  symconst,symdef,
  ncon, nset, nadd,
  ncgutil, hlcgobj, cgobj;

{*****************************************************************************
                               tmipsaddnode
*****************************************************************************}

procedure tmipsaddnode.second_generic_cmp32(unsigned,is_smallset: boolean);
var
  cond: TOpCmp;
  allow_constant : boolean;
  dreg : tregister;
begin
  pass_left_right;
  allow_constant:=(not is_smallset) or not (nodetype in [lten,gten]);
  force_reg_left_right(True, allow_constant);
  location_reset(location,LOC_FLAGS,OS_NO);

  cond:=cmpnode2topcmp(unsigned);
  if nf_swapped in flags then
    cond:=swap_opcmp(cond);

  if is_smallset and (nodetype in [lten,gten]) then
    begin
      if ((nodetype=lten) and not (nf_swapped in flags)) or
         ((nodetype=gten) and (nf_swapped in flags)) then
        dreg:=right.location.register
      else
        dreg:=left.location.register;
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg_reg(A_AND,dreg,right.location.register,left.location.register));
      cond:=OC_EQ;
    end;
  location.resflags.cond:=cond;
  location.resflags.reg1:=left.location.register;
  location.resflags.use_const:=(right.location.loc=LOC_CONSTANT);
  if location.resflags.use_const then
    location.resflags.value:=right.location.value
  else
    location.resflags.reg2:=right.location.register;
end;


procedure tmipsaddnode.second_add64bit;
begin
  if (nodetype=muln) then
    second_mul64bit
  else
    inherited second_add64bit;
end;


const
  cmpops: array[boolean] of TOpCmp = (OC_LT,OC_B);

procedure tmipsaddnode.cmp64_lt(left_reg, right_reg: TRegister64;unsigned: boolean);
begin
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,cmpops[unsigned],right_reg.reghi,left_reg.reghi,location.truelabel);
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reghi,right_reg.reghi,location.falselabel);
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_B,right_reg.reglo,left_reg.reglo,location.truelabel);
  cg.a_jmp_always(current_asmdata.CurrAsmList,location.falselabel);
end;


procedure tmipsaddnode.cmp64_le(left_reg, right_reg: TRegister64;unsigned: boolean);
begin
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,cmpops[unsigned],left_reg.reghi,right_reg.reghi,location.falselabel);
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reghi,right_reg.reghi,location.truelabel);
  cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_B,left_reg.reglo,right_reg.reglo,location.falselabel);
  cg.a_jmp_always(current_asmdata.CurrAsmList,location.truelabel);
end;


procedure tmipsaddnode.second_cmp64bit;
var
  truelabel,
  falselabel: tasmlabel;
  unsigned: boolean;
  left_reg,right_reg: TRegister64;
begin
  current_asmdata.getjumplabel(truelabel);
  current_asmdata.getjumplabel(falselabel);
  location_reset_jump(location,truelabel,falselabel);

  pass_left_right;
  force_reg_left_right(true,true);

  unsigned:=not(is_signed(left.resultdef)) or
            not(is_signed(right.resultdef));

  left_reg:=left.location.register64;
  if (right.location.loc=LOC_CONSTANT) then
    begin
      if lo(right.location.value64)=0 then
        right_reg.reglo:=NR_R0
      else
        begin
          right_reg.reglo:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
          cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,lo(right.location.value64),right_reg.reglo);
        end;
      if hi(right.location.value64)=0 then
        right_reg.reghi:=NR_R0
      else
        begin
          right_reg.reghi:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
          cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,hi(right.location.value64),right_reg.reghi);
        end;
    end
  else
    right_reg:=right.location.register64;

  case NodeType of
    equaln:
      begin
        cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reghi,right_reg.reghi,location.falselabel);
        cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reglo,right_reg.reglo,location.falselabel);
        cg.a_jmp_always(current_asmdata.CurrAsmList,location.truelabel);
      end;
    unequaln:
      begin
        cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reghi,right_reg.reghi,location.truelabel);
        cg.a_cmp_reg_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,left_reg.reglo,right_reg.reglo,location.truelabel);
        cg.a_jmp_always(current_asmdata.CurrAsmList,location.falselabel);
      end;
  else
    if nf_swapped in flags then
      case NodeType of
        ltn:
          cmp64_lt(right_reg, left_reg,unsigned);
        lten:
          cmp64_le(right_reg, left_reg,unsigned);
        gtn:
          cmp64_lt(left_reg, right_reg,unsigned);
        gten:
          cmp64_le(left_reg, right_reg,unsigned);
      end
    else
      case NodeType of
        ltn:
          cmp64_lt(left_reg, right_reg,unsigned);
        lten:
          cmp64_le(left_reg, right_reg,unsigned);
        gtn:
          cmp64_lt(right_reg, left_reg,unsigned);
        gten:
          cmp64_le(right_reg, left_reg,unsigned);
      end;
  end;
end;


procedure tmipsaddnode.second_addfloat;
var
  op: TAsmOp;
begin
  pass_left_right;
  if (nf_swapped in flags) then
    swapleftright;

        { force fpureg as location, left right doesn't matter
          as both will be in a fpureg }
  hlcg.location_force_fpureg(current_asmdata.CurrAsmList, left.location, left.resultdef, True);
  hlcg.location_force_fpureg(current_asmdata.CurrAsmList, right.location, right.resultdef, True);

  location_reset(location, LOC_FPUREGISTER, def_cgsize(resultdef));
  location.register:=cg.getfpuregister(current_asmdata.CurrAsmList,location.size);

  case nodetype of
    addn:
    begin
      if location.size = OS_F64 then
        op := A_ADD_D
      else
        op := A_ADD_S;
    end;
    muln:
    begin
      if location.size = OS_F64 then
        op := A_MUL_D
      else
        op := A_MUL_S;
    end;
    subn:
    begin
      if location.size = OS_F64 then
        op := A_SUB_D
      else
        op := A_SUB_S;
    end;
    slashn:
    begin
      if location.size = OS_F64 then
        op := A_DIV_D
      else
        op := A_DIV_S;
    end;
    else
      internalerror(200306014);
  end;
  current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op,
    location.Register, left.location.Register, right.location.Register));

end;


const
  ops_cmpfloat: array[boolean,ltn..unequaln] of TAsmOp = (
  // ltn       lten      gtn       gten      equaln    unequaln
    (A_C_LT_S, A_C_LE_S, A_C_LT_S, A_C_LE_S, A_C_EQ_S, A_C_EQ_S),
    (A_C_LT_D, A_C_LE_D, A_C_LT_D, A_C_LE_D, A_C_EQ_D, A_C_EQ_D)
  );

procedure tmipsaddnode.second_cmpfloat;
var
  op: tasmop;
  lreg,rreg: tregister;
begin
  pass_left_right;
  if nf_swapped in flags then
    swapleftright;

  hlcg.location_force_fpureg(current_asmdata.CurrAsmList, left.location, left.resultdef, True);
  hlcg.location_force_fpureg(current_asmdata.CurrAsmList, right.location, right.resultdef, True);
  location_reset(location, LOC_FLAGS, OS_NO);

  op:=ops_cmpfloat[left.location.size=OS_F64,nodetype];

  if (nodetype in [gtn,gten]) then
    begin
      lreg:=right.location.register;
      rreg:=left.location.register;
    end
  else
    begin
      lreg:=left.location.register;
      rreg:=right.location.register;
    end;

  current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op,lreg,rreg));
  location.resflags.reg1:=NR_FCC0;
  if (nodetype=unequaln) then
    location.resflags.cond:=OC_EQ
  else
    location.resflags.cond:=OC_NE;
end;


procedure tmipsaddnode.second_cmpboolean;
begin
  second_generic_cmp32(true,false);
end;


procedure tmipsaddnode.second_cmpsmallset;
begin
  second_generic_cmp32(true,true);
end;


procedure tmipsaddnode.second_cmpordinal;
var
  unsigned: boolean;
begin
  unsigned := not (is_signed(left.resultdef)) or not (is_signed(right.resultdef));
  second_generic_cmp32(unsigned,false);
end;


const
  multops: array[boolean] of TAsmOp = (A_MULT, A_MULTU);

procedure tmipsaddnode.second_addordinal;
var
  unsigned: boolean;
begin
  unsigned:=not(is_signed(left.resultdef)) or
            not(is_signed(right.resultdef));
  if (nodetype=muln) and is_64bit(resultdef) then
    begin
      pass_left_right;
      force_reg_left_right(true,false);
      location_reset(location,LOC_REGISTER,def_cgsize(resultdef));
      location.register64.reglo:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
      location.register64.reghi:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg_reg(multops[unsigned],left.location.register,right.location.register));
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg(A_MFLO,location.register64.reglo));
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg(A_MFHI,location.register64.reghi));
    end
  else
    inherited second_addordinal;
end;

procedure tmipsaddnode.second_mul64bit;
var
  list: TAsmList;
  hreg1,hreg2,tmpreg: TRegister;
begin
  list:=current_asmdata.CurrAsmList;
  pass_left_right;
  location_reset(location,LOC_REGISTER,def_cgsize(resultdef));
  hlcg.location_force_reg(list,left.location,left.resultdef,left.resultdef,true);
  { calculate 32-bit terms lo(right)*hi(left) and hi(left)*lo(right) }
  hreg1:=NR_NO;
  hreg2:=NR_NO;
  tmpreg:=NR_NO;
  if (right.location.loc=LOC_CONSTANT) then
    begin
      { Omit zero terms, if any }
      if hi(right.location.value64)<>0 then
        begin
          hreg2:=cg.getintregister(list,OS_INT);
          tmpreg:=cg.getintregister(list,OS_INT);
          cg.a_load_const_reg(list,OS_INT,longint(hi(right.location.value64)),tmpreg);
          list.concat(taicpu.op_reg_reg_reg(A_MUL,hreg2,tmpreg,left.location.register64.reglo));
        end;
      tmpreg:=NR_NO;
      if lo(right.location.value64)<>0 then
        begin
          hreg1:=cg.getintregister(list,OS_INT);
          tmpreg:=cg.getintregister(list,OS_INT);
          cg.a_load_const_reg(list,OS_INT,longint(lo(right.location.value64)),tmpreg);
          list.concat(taicpu.op_reg_reg_reg(A_MUL,hreg1,tmpreg,left.location.register64.reghi));
        end;
    end
  else
    begin
      hlcg.location_force_reg(list,right.location,right.resultdef,right.resultdef,true);
      tmpreg:=right.location.register64.reglo;
      hreg1:=cg.getintregister(list,OS_INT);
      hreg2:=cg.getintregister(list,OS_INT);
      list.concat(taicpu.op_reg_reg_reg(A_MUL,hreg1,right.location.register64.reglo,left.location.register64.reghi));
      list.concat(taicpu.op_reg_reg_reg(A_MUL,hreg2,right.location.register64.reghi,left.location.register64.reglo));
    end;

  { At this point, tmpreg is either lo(right) or NR_NO if lo(left)*lo(right) is zero }
  if (tmpreg=NR_NO) then
    begin
      if (hreg2<>NR_NO) and (hreg1<>NR_NO) then
        begin
          location.register64.reghi:=cg.getintregister(list,OS_INT);
          list.concat(taicpu.op_reg_reg_reg(A_ADDU,location.register64.reghi,hreg1,hreg2));
        end
      else if (hreg2<>NR_NO) then
        location.register64.reghi:=hreg2
      else if (hreg1<>NR_NO) then
        location.register64.reghi:=hreg1
      else
        InternalError(2014122701);
      location.register64.reglo:=NR_R0;
    end
  else
    begin
      list.concat(taicpu.op_reg_reg(A_MULTU,left.location.register64.reglo,tmpreg));
      location.register64.reghi:=cg.getintregister(list,OS_INT);
      location.register64.reglo:=cg.getintregister(list,OS_INT);
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg(A_MFLO,location.register64.reglo));
      current_asmdata.CurrAsmList.Concat(taicpu.op_reg(A_MFHI,location.register64.reghi));
      if (hreg2<>NR_NO) then
        list.concat(taicpu.op_reg_reg_reg(A_ADDU,location.register64.reghi,location.register64.reghi,hreg2));
      if (hreg1<>NR_NO) then
        list.concat(taicpu.op_reg_reg_reg(A_ADDU,location.register64.reghi,location.register64.reghi,hreg1));
    end;
end;

function tmipsaddnode.use_generic_mul32to64: boolean;
begin
  result:=false;
end;

function tmipsaddnode.use_generic_mul64bit: boolean;
begin
  result:=(cs_check_overflow in current_settings.localswitches) or
    (not (CPUMIPS_HAS_ISA32R2 in cpu_capabilities[current_settings.cputype]));
end;


begin
  caddnode := tmipsaddnode;
end.
