mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-01 07:33:40 +02:00
352 lines
12 KiB
ObjectPascal
352 lines
12 KiB
ObjectPascal
{
|
|
David Zhang 2007/01/15
|
|
$Id: ncpumat.pas,v 1.23 2005/02/14 17:13:10 peter Exp $
|
|
Copyright (c) 1998-2002 by Florian Klaempfl
|
|
|
|
Generate MIPSel assembler for math nodes
|
|
|
|
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 ncpumat;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
node, nmat, ncgmat, cgbase;
|
|
|
|
type
|
|
tMIPSELmoddivnode = class(tmoddivnode)
|
|
procedure pass_generate_code;override;
|
|
end;
|
|
|
|
tMIPSELshlshrnode = class(tshlshrnode)
|
|
procedure pass_generate_code;override;
|
|
{ everything will be handled in pass_2 }
|
|
function first_shlshr64bitint: tnode; override;
|
|
end;
|
|
|
|
tMIPSELnotnode = class(tcgnotnode)
|
|
procedure second_boolean; override;
|
|
end;
|
|
|
|
TMIPSunaryminusnode = class(tcgunaryminusnode)
|
|
procedure emit_float_sign_change(r: tregister; _size : tcgsize);override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
globtype, systems,
|
|
cutils, verbose, globals,
|
|
symconst, symdef,
|
|
aasmbase, aasmcpu, aasmtai, aasmdata,
|
|
defutil,
|
|
procinfo,
|
|
cgobj, hlcgobj, pass_2,
|
|
ncon,
|
|
cpubase,
|
|
ncgutil, cgcpu, cgutils;
|
|
|
|
{*****************************************************************************
|
|
TMipselMODDIVNODE
|
|
*****************************************************************************}
|
|
|
|
procedure tMIPSELmoddivnode.pass_generate_code;
|
|
var
|
|
power: longint;
|
|
tmpreg, numerator, divider, resultreg: tregister;
|
|
begin
|
|
secondpass(left);
|
|
secondpass(right);
|
|
location_copy(location, left.location);
|
|
|
|
{ put numerator in register }
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, left.location, left.resultdef, left.resultdef, True);
|
|
location_copy(location, left.location);
|
|
numerator := location.Register;
|
|
|
|
if (nodetype = modn) then
|
|
resultreg := cg.GetIntRegister(current_asmdata.CurrAsmList, OS_INT)
|
|
else
|
|
begin
|
|
if (location.loc = LOC_CREGISTER) then
|
|
begin
|
|
location.loc := LOC_REGISTER;
|
|
location.Register := cg.GetIntRegister(current_asmdata.CurrAsmList, OS_INT);
|
|
end;
|
|
resultreg := location.Register;
|
|
end;
|
|
|
|
if (nodetype = divn) and
|
|
(right.nodetype = ordconstn) and
|
|
ispowerof2(tordconstnode(right).Value.svalue, power) then
|
|
begin
|
|
tmpreg := cg.GetIntRegister(current_asmdata.CurrAsmList, OS_INT);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SAR, OS_INT, 31, numerator, tmpreg);
|
|
{ if signed, tmpreg=right value-1, otherwise 0 }
|
|
cg.a_op_const_reg(current_asmdata.CurrAsmList, OP_AND, OS_INT, tordconstnode(right).Value.svalue - 1, tmpreg);
|
|
{ add to the left value }
|
|
cg.a_op_reg_reg(current_asmdata.CurrAsmList, OP_ADD, OS_INT, tmpreg, numerator);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SAR, OS_INT, aword(power), numerator, resultreg);
|
|
end
|
|
else
|
|
begin
|
|
{ load divider in a register if necessary }
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, right.location,
|
|
right.resultdef, right.resultdef, True);
|
|
divider := right.location.Register;
|
|
|
|
|
|
if (nodetype = modn) then
|
|
begin
|
|
if is_signed(right.resultdef) then
|
|
begin
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_REM, resultreg, numerator, divider));
|
|
end
|
|
else
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_REMU, resultreg, numerator, divider));
|
|
end
|
|
else
|
|
begin
|
|
if is_signed({left.resultdef}right.resultdef) then
|
|
begin
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_DIV, resultreg, numerator, divider));
|
|
end
|
|
else
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_DIVU, resultreg, numerator, divider));
|
|
end;
|
|
end;
|
|
{ set result location }
|
|
location.loc := LOC_REGISTER;
|
|
location.Register := resultreg;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TMIPSelSHLRSHRNODE
|
|
*****************************************************************************}
|
|
|
|
function TMIPSELShlShrNode.first_shlshr64bitint: TNode;
|
|
begin
|
|
{ 64bit without constants need a helper }
|
|
if is_64bit(left.resultdef) and
|
|
(right.nodetype <> ordconstn) then
|
|
begin
|
|
Result := inherited first_shlshr64bitint;
|
|
exit;
|
|
end;
|
|
|
|
Result := nil;
|
|
end;
|
|
|
|
|
|
procedure tMIPSELshlshrnode.pass_generate_code;
|
|
var
|
|
hregister, resultreg, hregister1, hreg64hi, hreg64lo: tregister;
|
|
op: topcg;
|
|
shiftval: aword;
|
|
begin
|
|
{ 64bit without constants need a helper, and is
|
|
already replaced in pass1 }
|
|
if is_64bit(left.resultdef) and
|
|
(right.nodetype <> ordconstn) then
|
|
internalerror(200405301);
|
|
|
|
secondpass(left);
|
|
secondpass(right);
|
|
if is_64bit(left.resultdef) then
|
|
begin
|
|
location_reset(location, LOC_REGISTER, OS_64);
|
|
|
|
{ load left operator in a register }
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, left.location, left.resultdef, u64inttype, False);
|
|
|
|
|
|
hreg64hi := left.location.register64.reghi;
|
|
hreg64lo := left.location.register64.reglo;
|
|
|
|
shiftval := tordconstnode(right).Value.svalue and 63;
|
|
if shiftval > 31 then
|
|
begin
|
|
if nodetype = shln then
|
|
begin
|
|
cg.a_load_const_reg(current_asmdata.CurrAsmList, OS_32, 0, hreg64hi);
|
|
if (shiftval and 31) <> 0 then
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHL, OS_32, shiftval and 31, hreg64lo, hreg64lo);
|
|
end
|
|
else
|
|
begin
|
|
cg.a_load_const_reg(current_asmdata.CurrAsmList, OS_32, 0, hreg64lo);
|
|
if (shiftval and 31) <> 0 then
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHR, OS_32, shiftval and 31, hreg64hi, hreg64hi);
|
|
end;
|
|
location.register64.reglo := hreg64hi;
|
|
location.register64.reghi := hreg64lo;
|
|
end
|
|
else
|
|
begin
|
|
if shiftval <> 0 then
|
|
begin
|
|
hregister := cg.getintregister(current_asmdata.CurrAsmList, OS_32);
|
|
if nodetype = shln then
|
|
begin
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHR, OS_32, 32 - shiftval, hreg64lo, hregister);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHL, OS_32, shiftval, hreg64hi, hreg64hi);
|
|
cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList, OP_OR, OS_32, hregister, hreg64hi, hreg64hi);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHL, OS_32, shiftval, hreg64lo, hreg64lo);
|
|
end
|
|
else
|
|
begin
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHL, OS_32, 32 - shiftval, hreg64hi, hregister);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHR, OS_32, shiftval, hreg64lo, hreg64lo);
|
|
cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList, OP_OR, OS_32, hregister, hreg64lo, hreg64lo);
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SHR, OS_32, shiftval, hreg64hi, hreg64hi);
|
|
end;
|
|
end;
|
|
location.register64.reghi := hreg64hi;
|
|
location.register64.reglo := hreg64lo;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ load left operators in a register }
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, left.location, left.resultdef, left.resultdef, True);
|
|
location_copy(location, left.location);
|
|
resultreg := location.Register;
|
|
hregister1 := location.Register;
|
|
if (location.loc = LOC_CREGISTER) then
|
|
begin
|
|
location.loc := LOC_REGISTER;
|
|
resultreg := cg.GetIntRegister(current_asmdata.CurrAsmList, OS_INT);
|
|
location.Register := resultreg;
|
|
end;
|
|
{ determine operator }
|
|
if nodetype = shln then
|
|
op := OP_SHL
|
|
else
|
|
op := OP_SHR;
|
|
{ shifting by a constant directly coded: }
|
|
if (right.nodetype = ordconstn) then
|
|
begin
|
|
if tordconstnode(right).Value.svalue and 31 <> 0 then
|
|
cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, op, OS_32, tordconstnode(right).Value.svalue and 31, hregister1, resultreg);
|
|
end
|
|
else
|
|
begin
|
|
{ load shift count in a register if necessary }
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, right.location, right.resultdef, right.resultdef, True);
|
|
cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList, op, OS_32, right.location.Register, hregister1, resultreg);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TMIPSelNOTNODE
|
|
*****************************************************************************}
|
|
|
|
procedure tMIPSELnotnode.second_boolean;
|
|
var
|
|
hl: tasmlabel;
|
|
tmpreg : TRegister;
|
|
r64: TRegister64;
|
|
begin
|
|
{ if the location is LOC_JUMP, we do the secondpass after the
|
|
labels are allocated
|
|
}
|
|
if left.expectloc = LOC_JUMP then
|
|
begin
|
|
hl := current_procinfo.CurrTrueLabel;
|
|
current_procinfo.CurrTrueLabel := current_procinfo.CurrFalseLabel;
|
|
current_procinfo.CurrFalseLabel := hl;
|
|
secondpass(left);
|
|
maketojumpbool(current_asmdata.CurrAsmList, left, lr_load_regvars);
|
|
hl := current_procinfo.CurrTrueLabel;
|
|
current_procinfo.CurrTrueLabel := current_procinfo.CurrFalseLabel;
|
|
current_procinfo.CurrFalseLabel := hl;
|
|
location.loc := LOC_JUMP;
|
|
end
|
|
else
|
|
begin
|
|
secondpass(left);
|
|
case left.location.loc of
|
|
LOC_REGISTER, LOC_CREGISTER, LOC_REFERENCE, LOC_CREFERENCE,
|
|
LOC_SUBSETREG,LOC_CSUBSETREG,LOC_SUBSETREF,LOC_CSUBSETREF:
|
|
begin
|
|
hlcg.location_force_reg(current_asmdata.CurrAsmList, left.location, left.resultdef, left.resultdef, True);
|
|
if is_64bit(resultdef) then
|
|
begin
|
|
r64.reglo:=cg.GetIntRegister(current_asmdata.CurrAsmList,OS_INT);
|
|
r64.reghi:=cg.GetIntRegister(current_asmdata.CurrAsmList,OS_INT);
|
|
{ OR low and high parts together }
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_OR,r64.reglo,left.location.register64.reglo,left.location.register64.reghi));
|
|
{ x=0 <=> unsigned(x)<1 }
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const(A_SLTIU,r64.reglo,r64.reglo,1));
|
|
if is_cbool(resultdef) then
|
|
begin
|
|
cg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_NEG,OS_S32,r64.reglo,r64.reglo);
|
|
cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_32,OS_32,r64.reglo,r64.reghi);
|
|
end
|
|
else
|
|
cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_32,0,r64.reghi);
|
|
location_reset(location,LOC_REGISTER,OS_64);
|
|
location.Register64:=r64;
|
|
end
|
|
else
|
|
begin
|
|
tmpreg := cg.GetIntRegister(current_asmdata.CurrAsmList, OS_INT);
|
|
{ x=0 <=> unsigned(x)<1 }
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const(A_SLTIU, tmpreg, left.location.Register, 1));
|
|
if is_cbool(resultdef) then
|
|
cg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_NEG,OS_S32,tmpreg,tmpreg);
|
|
location_reset(location, LOC_REGISTER, OS_INT);
|
|
location.Register := tmpreg;
|
|
end;
|
|
end;
|
|
else
|
|
internalerror(2003042401);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TMIPSunaryminusnode
|
|
*****************************************************************************}
|
|
|
|
procedure TMIPSunaryminusnode.emit_float_sign_change(r: tregister; _size : tcgsize);
|
|
begin
|
|
case _size of
|
|
OS_F32:
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_NEG_s,r,r));
|
|
OS_F64:
|
|
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_NEG_d,r,r));
|
|
else
|
|
internalerror(2013030501);
|
|
end;
|
|
end;
|
|
|
|
|
|
begin
|
|
cmoddivnode := tMIPSELmoddivnode;
|
|
cshlshrnode := tMIPSELshlshrnode;
|
|
cnotnode := tMIPSELnotnode;
|
|
cunaryminusnode := TMIPSunaryminusnode;
|
|
end.
|