mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-02 13:53:48 +02:00
516 lines
18 KiB
ObjectPascal
516 lines
18 KiB
ObjectPascal
{*****************************************************************************}
|
|
{ File : naddcpu.pas }
|
|
{ Author : Mazen NEIFER }
|
|
{ Project : Free Pascal Compiler (FPC) }
|
|
{ Creation date : 2002\07\14 }
|
|
{ Last modification date : 2002\07\26 }
|
|
{ Licence : GPL }
|
|
{ Bug report : mazen.neifer.01@supaero.org }
|
|
{*****************************************************************************}
|
|
{
|
|
$Id$
|
|
Copyright (c) 2000-2002 by Florian Klaempfl
|
|
|
|
Code generation for add nodes on the i386
|
|
|
|
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 naddcpu;
|
|
{$INCLUDE fpcdefs.inc}
|
|
INTERFACE
|
|
USES
|
|
node,nadd,cpubase,cginfo;
|
|
TYPE
|
|
TSparcAddNode=CLASS(TAddNode)
|
|
PROCEDURE pass_2;OVERRIDE;
|
|
PRIVATE
|
|
FUNCTION GetResFlags(unsigned:Boolean):TResFlags;
|
|
PROCEDURE left_must_be_reg(OpSize:TOpSize;NoSwap:Boolean);
|
|
PROCEDURE emit_generic_code(op:TAsmOp;OpSize:TOpSize;unsigned,extra_not,mboverflow:Boolean);
|
|
PROCEDURE emit_op_right_left(op:TAsmOp;OpSize:TOpsize);
|
|
PROCEDURE set_result_location(cmpOp,unsigned:Boolean);
|
|
END;
|
|
IMPLEMENTATION
|
|
USES
|
|
globtype,systems,
|
|
cutils,verbose,globals,
|
|
symconst,symdef,paramgr,
|
|
aasmbase,aasmtai,aasmcpu,defbase,htypechk,
|
|
cgbase,pass_2,regvars,
|
|
cpupara,
|
|
ncon,nset,
|
|
cga,ncgutil,tgobj,rgobj,rgcpu,cgobj,cg64f32;
|
|
CONST
|
|
opsize_2_cgSize:ARRAY[S_B..S_L]OF TCgSize=(OS_8,OS_16,OS_32);
|
|
FUNCTION TSparcAddNode.GetResFlags(unsigned:Boolean):TResFlags;
|
|
BEGIN
|
|
CASE NodeType OF
|
|
equaln:
|
|
GetResFlags:=F_E;
|
|
unequaln:
|
|
GetResFlags:=F_NE;
|
|
ELSE
|
|
IF NOT(unsigned)
|
|
THEN
|
|
BEGIN
|
|
IF nf_swaped IN flags
|
|
THEN
|
|
CASE NodeType OF
|
|
ltn:
|
|
GetResFlags:=F_G;
|
|
lten:
|
|
GetResFlags:=F_GE;
|
|
gtn:
|
|
GetResFlags:=F_L;
|
|
gten:
|
|
GetResFlags:=F_LE;
|
|
END
|
|
ELSE
|
|
CASE NodeType OF
|
|
ltn:
|
|
GetResFlags:=F_L;
|
|
lten:
|
|
GetResFlags:=F_LE;
|
|
gtn:
|
|
GetResFlags:=F_G;
|
|
gten:
|
|
GetResFlags:=F_GE;
|
|
END;
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
IF nf_swaped IN Flags
|
|
THEN
|
|
CASE NodeType OF
|
|
ltn:
|
|
GetResFlags:=F_A;
|
|
lten:
|
|
GetResFlags:=F_AE;
|
|
gtn:
|
|
GetResFlags:=F_B;
|
|
gten:
|
|
GetResFlags:=F_BE;
|
|
END
|
|
ELSE
|
|
CASE NodeType OF
|
|
ltn:
|
|
GetResFlags:=F_B;
|
|
lten:
|
|
GetResFlags:=F_BE;
|
|
gtn:
|
|
GetResFlags:=F_A;
|
|
gten:
|
|
GetResFlags:=F_AE;
|
|
END;
|
|
END;
|
|
END;
|
|
END;
|
|
PROCEDURE TSparcAddNode.left_must_be_reg(OpSize:TOpSize;NoSwap:Boolean);
|
|
BEGIN
|
|
IF(left.location.loc<>LOC_REGISTER)
|
|
THEN{left location is not a register}
|
|
BEGIN
|
|
IF(NOT NoSwap)AND(right.location.loc=LOC_REGISTER)
|
|
THEN{right is register so we can swap the locations}
|
|
BEGIN
|
|
location_swap(left.location,right.location);
|
|
toggleflag(nf_swaped);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
{maybe we can reuse a constant register when the operation is a comparison that
|
|
doesn't change the value of the register}
|
|
location_force_reg(exprasmlist,left.location,opsize_2_cgsize[opsize],(nodetype IN [ltn,lten,gtn,gten,equaln,unequaln]));
|
|
END;
|
|
END;
|
|
END;
|
|
PROCEDURE TSparcAddNode.emit_generic_code(op:TAsmOp;OpSize:TOpSize;unsigned,extra_not,mboverflow:Boolean);
|
|
VAR
|
|
power:LongInt;
|
|
hl4:TAsmLabel;
|
|
BEGIN
|
|
{ at this point, left.location.loc should be LOC_REGISTER }
|
|
IF right.location.loc=LOC_REGISTER
|
|
THEN
|
|
BEGIN
|
|
{ right.location is a LOC_REGISTER }
|
|
{ when swapped another result register }
|
|
IF(nodetype=subn)AND(nf_swaped IN flags)
|
|
THEN
|
|
BEGIN
|
|
IF extra_not
|
|
THEN
|
|
emit_reg(A_NOT,S_L,left.location.register);
|
|
emit_reg_reg(op,opsize,left.location.register,right.location.register);
|
|
{ newly swapped also set swapped flag }
|
|
location_swap(left.location,right.location);
|
|
toggleflag(nf_swaped);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
IF extra_not
|
|
THEN
|
|
emit_reg(A_NOT,S_L,right.location.register);
|
|
emit_reg_reg(op,opsize,right.location.register,left.location.register);
|
|
END;
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
{ right.location is not a LOC_REGISTER }
|
|
IF(nodetype=subn)AND(nf_swaped IN flags)
|
|
THEN
|
|
BEGIN
|
|
IF extra_not
|
|
THEN
|
|
emit_reg(A_NOT,opsize,left.location.register);
|
|
// rg.getexplicitregisterint(exprasmlist,R_EDI);
|
|
// cg.a_load_loc_reg(exprasmlist,right.location,R_EDI);
|
|
// emit_reg_reg(op,opsize,left.location.register,R_EDI);
|
|
// emit_reg_reg(A_MOV,opsize,R_EDI,left.location.register);
|
|
// rg.ungetregisterint(exprasmlist,R_EDI);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
{ Optimizations when right.location is a constant value }
|
|
IF(op=A_CMP)AND(nodetype IN [equaln,unequaln])AND(right.location.loc=LOC_CONSTANT)AND(right.location.value=0)
|
|
THEN
|
|
BEGIN
|
|
// emit_reg_reg(A_TEST,opsize,left.location.register,left.location.register);
|
|
END
|
|
ELSE IF(op=A_ADD)AND(right.location.loc=LOC_CONSTANT)AND(right.location.value=1)AND NOT(cs_check_overflow in aktlocalswitches)
|
|
THEN
|
|
BEGIN
|
|
emit_reg(A_INC,opsize,left.location.register);
|
|
END
|
|
ELSE IF(op=A_SUB)AND(right.location.loc=LOC_CONSTANT)AND(right.location.value=1)AND NOT(cs_check_overflow in aktlocalswitches)
|
|
THEN
|
|
BEGIN
|
|
emit_reg(A_DEC,opsize,left.location.register);
|
|
END
|
|
ELSE IF(op=A_SMUL)AND(right.location.loc=LOC_CONSTANT)AND(ispowerof2(right.location.value,power))AND NOT(cs_check_overflow in aktlocalswitches)
|
|
THEN
|
|
BEGIN
|
|
emit_const_reg(A_SLL,opsize,power,left.location.register);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
IF extra_not
|
|
THEN
|
|
BEGIN
|
|
// rg.getexplicitregisterint(exprasmlist,R_EDI);
|
|
// cg.a_load_loc_reg(exprasmlist,right.location,R_EDI);
|
|
// emit_reg(A_NOT,S_L,R_EDI);
|
|
// emit_reg_reg(A_AND,S_L,R_EDI,left.location.register);
|
|
// rg.ungetregisterint(exprasmlist,R_EDI);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
emit_op_right_left(op,opsize);
|
|
END;
|
|
END;
|
|
END;
|
|
END;
|
|
{ only in case of overflow operations }
|
|
{ produce overflow code }
|
|
{ we must put it here directly, because sign of operation }
|
|
{ is in unsigned VAR!! }
|
|
IF mboverflow
|
|
THEN
|
|
BEGIN
|
|
IF cs_check_overflow IN aktlocalswitches
|
|
THEN
|
|
BEGIN
|
|
// getlabel(hl4);
|
|
IF unsigned
|
|
THEN
|
|
emitjmp(C_NB,hl4)
|
|
ELSE
|
|
emitjmp(C_NO,hl4);
|
|
cg.a_call_name(exprasmlist,'FPC_OVERFLOW');
|
|
cg.a_label(exprasmlist,hl4);
|
|
END;
|
|
END;
|
|
END;
|
|
PROCEDURE TSparcAddNode.emit_op_right_left(op:TAsmOp;OpSize:TOpsize);
|
|
BEGIN
|
|
{left must be a register}
|
|
CASE right.location.loc OF
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
exprasmlist.concat(taicpu.op_reg_reg(op,opsize,right.location.register,left.location.register));
|
|
LOC_REFERENCE,LOC_CREFERENCE :
|
|
exprasmlist.concat(taicpu.op_ref_reg(op,opsize,right.location.reference,left.location.register));
|
|
LOC_CONSTANT:
|
|
exprasmlist.concat(taicpu.op_const_reg(op,opsize,right.location.value,left.location.register));
|
|
ELSE
|
|
InternalError(200203232);
|
|
END;
|
|
END;
|
|
PROCEDURE TSparcAddNode.set_result_location(cmpOp,unsigned:Boolean);
|
|
BEGIN
|
|
IF cmpOp
|
|
THEN
|
|
BEGIN
|
|
location_reset(location,LOC_FLAGS,OS_NO);
|
|
location.resflags:=GetResFlags(unsigned);
|
|
END
|
|
ELSE
|
|
location_copy(location,left.location);
|
|
END;
|
|
PROCEDURE TSparcAddNode.pass_2;
|
|
{is also being used for "xor", and "mul", "sub", or and comparative operators}
|
|
VAR
|
|
popeax,popedx,pushedfpu,mboverflow,cmpop:Boolean;
|
|
op:TAsmOp;
|
|
power:LongInt;
|
|
OpSize:TOpSize;
|
|
unsigned:Boolean;{true, if unsigned types are compared}
|
|
{ is_in_dest if the result is put directly into }
|
|
{ the resulting refernce or varregister }
|
|
{is_in_dest : boolean;}
|
|
{ true, if for sets subtractions the extra not should generated }
|
|
extra_not:Boolean;
|
|
BEGIN
|
|
{to make it more readable, string and set (not smallset!) have their own
|
|
procedures }
|
|
CASE left.resulttype.def.deftype OF
|
|
orddef:
|
|
BEGIN
|
|
IF is_boolean(left.resulttype.def)AND is_boolean(right.resulttype.def)
|
|
THEN{handling boolean expressions}
|
|
BEGIN
|
|
InternalError(20020726);//second_addboolean;
|
|
exit;
|
|
END
|
|
ELSE IF is_64bitint(left.resulttype.def)
|
|
THEN{64bit operations}
|
|
BEGIN
|
|
InternalError(20020726);//second_add64bit;
|
|
exit;
|
|
END;
|
|
END;
|
|
stringdef:
|
|
BEGIN
|
|
InternalError(20020726);//second_addstring;
|
|
exit;
|
|
END;
|
|
setdef:
|
|
BEGIN
|
|
{normalsets are already handled in pass1}
|
|
IF(tsetdef(left.resulttype.def).settype<>smallset)
|
|
THEN
|
|
internalerror(200109041);
|
|
InternalError(20020726);//second_addsmallset;
|
|
exit;
|
|
END;
|
|
arraydef :
|
|
BEGIN
|
|
{$ifdef SUPPORT_MMX}
|
|
if is_mmx_able_array(left.resulttype.def) then
|
|
begin
|
|
InternalError(20020726);//second_addmmx;
|
|
exit;
|
|
end;
|
|
{$endif SUPPORT_MMX}
|
|
END;
|
|
floatdef :
|
|
BEGIN
|
|
InternalError(20020726);//second_addfloat;
|
|
exit;
|
|
END;
|
|
END;
|
|
{defaults}
|
|
{is_in_dest:=false;}
|
|
extra_not:=false;
|
|
mboverflow:=false;
|
|
cmpop:=false;
|
|
unsigned:=not(is_signed(left.resulttype.def))or not(is_signed(right.resulttype.def));
|
|
opsize:=def_opsize(left.resulttype.def);
|
|
//pass_left_and_right(pushedfpu);
|
|
IF(left.resulttype.def.deftype=pointerdef)OR
|
|
(right.resulttype.def.deftype=pointerdef) or
|
|
(is_class_or_interface(right.resulttype.def) and is_class_or_interface(left.resulttype.def)) or
|
|
(left.resulttype.def.deftype=classrefdef) or
|
|
(left.resulttype.def.deftype=procvardef) or
|
|
((left.resulttype.def.deftype=enumdef)and(left.resulttype.def.size=4)) or
|
|
((left.resulttype.def.deftype=orddef)and(torddef(left.resulttype.def).typ in [s32bit,u32bit])) or
|
|
((right.resulttype.def.deftype=orddef)and(torddef(right.resulttype.def).typ in [s32bit,u32bit]))
|
|
THEN
|
|
BEGIN
|
|
CASE NodeType OF
|
|
addn:
|
|
BEGIN
|
|
op:=A_ADD;
|
|
mboverflow:=true;
|
|
END;
|
|
muln:
|
|
BEGIN
|
|
IF unsigned
|
|
THEN
|
|
op:=A_UMUL
|
|
ELSE
|
|
op:=A_SMUL;
|
|
mboverflow:=true;
|
|
END;
|
|
subn:
|
|
BEGIN
|
|
op:=A_SUB;
|
|
mboverflow:=true;
|
|
END;
|
|
ltn,lten,
|
|
gtn,gten,
|
|
equaln,unequaln:
|
|
BEGIN
|
|
op:=A_CMP;
|
|
cmpop:=true;
|
|
END;
|
|
xorn:
|
|
op:=A_XOR;
|
|
orn:
|
|
op:=A_OR;
|
|
andn:
|
|
op:=A_AND;
|
|
ELSE
|
|
CGMessage(type_e_mismatch);
|
|
END;
|
|
{ filter MUL, which requires special handling }
|
|
IF op=A_UMUL
|
|
THEN
|
|
BEGIN
|
|
popeax:=false;
|
|
popedx:=false;
|
|
{ here you need to free the symbol first }
|
|
{ left.location and right.location must }
|
|
{ only be freed when they are really released, }
|
|
{ because the optimizer NEEDS correct regalloc }
|
|
{ info!!! (JM) }
|
|
{ the location.register will be filled in later (JM) }
|
|
location_reset(location,LOC_REGISTER,OS_INT);
|
|
{$IfNDef NoShlMul}
|
|
IF right.nodetype=ordconstn
|
|
THEN
|
|
swapleftright;
|
|
IF(left.nodetype=ordconstn)and
|
|
ispowerof2(tordconstnode(left).value, power)and
|
|
not(cs_check_overflow in aktlocalswitches)
|
|
THEN
|
|
BEGIN
|
|
{ This release will be moved after the next }
|
|
{ instruction by the optimizer. No need to }
|
|
{ release left.location, since it's a }
|
|
{ constant (JM) }
|
|
location_release(exprasmlist,right.location);
|
|
location.register:=rg.getregisterint(exprasmlist);
|
|
cg.a_load_loc_reg(exprasmlist,right.location,location.register);
|
|
cg.a_op_const_reg(exprasmlist,OP_SHL,power,location.register);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
{$EndIf NoShlMul}
|
|
{In SPARC there is no push/pop mechanism. There is a windowing mechanism using
|
|
SAVE and RESTORE instructions.}
|
|
//regstopush:=all_registers;
|
|
//remove_non_regvars_from_loc(right.location,regstopush);
|
|
//remove_non_regvars_from_loc(left.location,regstopush);
|
|
{left.location can be R_EAX !!!}
|
|
// rg.GetExplicitRegisterInt(exprasmlist,R_EDI);
|
|
{load the left value}
|
|
// cg.a_load_loc_reg(exprasmlist,left.location,R_EDI);
|
|
// location_release(exprasmlist,left.location);
|
|
{ allocate EAX }
|
|
// if R_EAX in rg.unusedregsint then
|
|
// exprasmList.concat(tai_regalloc.Alloc(R_EAX));
|
|
{ load he right value }
|
|
// cg.a_load_loc_reg(exprasmlist,right.location,R_EAX);
|
|
// location_release(exprasmlist,right.location);
|
|
{ allocate EAX if it isn't yet allocated (JM) }
|
|
// if (R_EAX in rg.unusedregsint) then
|
|
// exprasmList.concat(tai_regalloc.Alloc(R_EAX));
|
|
{ also allocate EDX, since it is also modified by }
|
|
{ a mul (JM) }
|
|
{ if R_EDX in rg.unusedregsint then
|
|
exprasmList.concat(tai_regalloc.Alloc(R_EDX));
|
|
emit_reg(A_MUL,S_L,R_EDI);
|
|
rg.ungetregisterint(exprasmlist,R_EDI);
|
|
if R_EDX in rg.unusedregsint then
|
|
exprasmList.concat(tai_regalloc.DeAlloc(R_EDX));
|
|
if R_EAX in rg.unusedregsint then
|
|
exprasmList.concat(tai_regalloc.DeAlloc(R_EAX));
|
|
location.register:=rg.getregisterint(exprasmlist);
|
|
emit_reg_reg(A_MOV,S_L,R_EAX,location.register);
|
|
if popedx then
|
|
emit_reg(A_POP,S_L,R_EDX);
|
|
if popeax then
|
|
emit_reg(A_POP,S_L,R_EAX);}
|
|
{$IfNDef NoShlMul}
|
|
End;
|
|
{$endif NoShlMul}
|
|
location_freetemp(exprasmlist,left.location);
|
|
location_freetemp(exprasmlist,right.location);
|
|
exit;
|
|
end;
|
|
|
|
{ Convert flags to register first }
|
|
if (left.location.loc=LOC_FLAGS) then
|
|
location_force_reg(exprasmlist,left.location,opsize_2_cgsize[opsize],false);
|
|
if (right.location.loc=LOC_FLAGS) then
|
|
location_force_reg(exprasmlist,right.location,opsize_2_cgsize[opsize],false);
|
|
|
|
left_must_be_reg(OpSize,false);
|
|
emit_generic_code(op,opsize,unsigned,extra_not,mboverflow);
|
|
location_freetemp(exprasmlist,right.location);
|
|
location_release(exprasmlist,right.location);
|
|
if cmpop and
|
|
(left.location.loc<>LOC_CREGISTER) then
|
|
begin
|
|
location_freetemp(exprasmlist,left.location);
|
|
location_release(exprasmlist,left.location);
|
|
end;
|
|
set_result_location(cmpop,unsigned);
|
|
end
|
|
|
|
{ 8/16 bit enum,char,wchar types }
|
|
{ else
|
|
if ((left.resulttype.def.deftype=orddef) and
|
|
(torddef(left.resulttype.def).typ in [uchar,uwidechar])) or
|
|
((left.resulttype.def.deftype=enumdef) and
|
|
((left.resulttype.def.size=1) or
|
|
(left.resulttype.def.size=2))) then
|
|
begin
|
|
case nodetype of
|
|
ltn,lten,gtn,gten,
|
|
equaln,unequaln :
|
|
cmpop:=true;
|
|
else
|
|
CGMessage(type_e_mismatch);
|
|
end;
|
|
left_must_be_reg(opsize,false);
|
|
emit_op_right_left(A_CMP,opsize);
|
|
location_freetemp(exprasmlist,right.location);
|
|
location_release(exprasmlist,right.location);
|
|
if left.location.loc<>LOC_CREGISTER then
|
|
begin
|
|
location_freetemp(exprasmlist,left.location);
|
|
location_release(exprasmlist,left.location);
|
|
end;
|
|
set_result_location(true,true);
|
|
end
|
|
else
|
|
CGMessage(type_e_mismatch);}
|
|
end;
|
|
BEGIN
|
|
cAddNode:=TSparcAddNode;
|
|
END.
|